- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我被困在 K&R 书中用华氏到摄氏度的例子学习汇编语言的基础知识。这是我所指的 C 代码:
#include <stdio.h>
main()
{
int fahr, celsius;
int lower, upper, step;
lower = 0;
upper = 300;
step = 20;
fahr = lower;
while (fahr <= upper) {
celsius = 5 * (fahr-32) / 9;
printf("%d\t%d\n", fahr, celsius);
fahr = fahr + step;
}
}
$ gcc -O0 -g -ansi -pedantic l1-2a.c
$ gdb -q a.out
(gdb) disas /m main
(gdb) disas /m main
Dump of assembler code for function main:
6 {
0x00000000004004c4 <+0>: push %rbp
0x00000000004004c5 <+1>: mov %rsp,%rbp
0x00000000004004c8 <+4>: sub $0x20,%rsp
7 int fahr, celsius;
8 int lower, upper, step;
9
10 lower = 0;
0x00000000004004cc <+8>: movl $0x0,-0xc(%rbp)
11 upper = 300;
0x00000000004004d3 <+15>: movl $0x12c,-0x8(%rbp)
12 step = 20;
0x00000000004004da <+22>: movl $0x14,-0x4(%rbp)
13
14 fahr = lower;
0x00000000004004e1 <+29>: mov -0xc(%rbp),%eax
0x00000000004004e4 <+32>: mov %eax,-0x14(%rbp)
15 while (fahr <= upper) {
0x00000000004004e7 <+35>: jmp 0x400532 <main+110>
0x0000000000400532 <+110>: mov -0x14(%rbp),%eax
0x0000000000400535 <+113>: cmp -0x8(%rbp),%eax
0x0000000000400538 <+116>: jle 0x4004e9 <main+37>
16 celsius = 5 * (fahr-32) / 9;
0x00000000004004e9 <+37>: mov -0x14(%rbp),%edx
0x00000000004004ec <+40>: mov %edx,%eax
0x00000000004004ee <+42>: shl $0x2,%eax
0x00000000004004f1 <+45>: add %edx,%eax
0x00000000004004f3 <+47>: lea -0xa0(%rax),%ecx
0x00000000004004f9 <+53>: mov $0x38e38e39,%edx
0x00000000004004fe <+58>: mov %ecx,%eax
0x0000000000400500 <+60>: imul %edx
0x0000000000400502 <+62>: sar %edx
0x0000000000400504 <+64>: mov %ecx,%eax
0x0000000000400506 <+66>: sar $0x1f,%eax
0x0000000000400509 <+69>: mov %edx,%ecx
0x000000000040050b <+71>: sub %eax,%ecx
0x000000000040050d <+73>: mov %ecx,%eax
0x000000000040050f <+75>: mov %eax,-0x10(%rbp)
17 printf("%d\t%d\n", fahr, celsius);
0x0000000000400512 <+78>: mov $0x400638,%eax
0x0000000000400517 <+83>: mov -0x10(%rbp),%edx
0x000000000040051a <+86>: mov -0x14(%rbp),%ecx
0x000000000040051d <+89>: mov %ecx,%esi
0x000000000040051f <+91>: mov %rax,%rdi
0x0000000000400522 <+94>: mov $0x0,%eax
0x0000000000400527 <+99>: callq 0x4003b8 <printf@plt>
18 fahr = fahr + step;
0x000000000040052c <+104>: mov -0x4(%rbp),%eax
0x000000000040052f <+107>: add %eax,-0x14(%rbp)
19 }
20 }
0x000000000040053a <+118>: leaveq
0x000000000040053b <+119>: retq
End of assembler dump.
16 celsius = 5 * (fahr-32) / 9;
0x00000000004004e9 <+37>: mov -0x14(%rbp),%edx
0x00000000004004ec <+40>: mov %edx,%eax
0x00000000004004ee <+42>: shl $0x2,%eax
0x00000000004004f1 <+45>: add %edx,%eax
0x00000000004004f3 <+47>: lea -0xa0(%rax),%ecx
0x00000000004004f9 <+53>: mov $0x38e38e39,%edx
0x00000000004004fe <+58>: mov %ecx,%eax
0x0000000000400500 <+60>: imul %edx
0x0000000000400502 <+62>: sar %edx
0x0000000000400504 <+64>: mov %ecx,%eax
0x0000000000400506 <+66>: sar $0x1f,%eax
0x0000000000400509 <+69>: mov %edx,%ecx
0x000000000040050b <+71>: sub %eax,%ecx
0x000000000040050d <+73>: mov %ecx,%eax
0x000000000040050f <+75>: mov %eax,-0x10(%rbp)
lea -0xa0(%rax),%ecx
160
来自
%eax
注册,持有
5*fahr
, 作为:
5 * (fahr-32) / 9 <=> (5*fahr - 5*32) / 9 <=> (5*fahr - 160) / 9
%ecx
之后(以及完整的
%rcx
)商店
5*fahr - 160
.但是我不知道它是如何除以 9 的。为了避免除法,这似乎是一些技巧,例如“乘以倒数”,但我不明白它是如何工作的。
最佳答案
总结评论中的内容:0x38e38e39
是 954437177
十进制,正好是 (2^33 + 1) / 9
.所以,汇编代码是这样工作的(为了清楚起见,我用 (5 * fahr - 160)
替换了 X
):
mov $0x38e38e39,%edx /* edx is now 0x38e38e39 == (2^33 + 1) / 9 */
mov %ecx,%eax /* eax is now X */
imul %edx /* edx:eax is now eax * edx == X * ((2^33 + 1) / 9) */
edx:eax
代表 1 操作数
imul
首先填充其操作数(在本例中为
edx
)的 32 位,然后将剩余的低位放入
eax
.
edx
是
(X * ((2^33 + 1) / 9)) >> 32
的 32 个最低有效位.
eax
是
(X * ((2^33 + 1) / 9)) % 2^32
(但这很快就会被丢弃)
sar %edx /* edx is now edx >> 1 == (X * ((2^33 + 1) / 9)) >> 33 */
mov %ecx,%eax /* eax is now X again */
sar $0x1f,%eax /* eax is now X >> 0x1f == X >> 31 */
mov %edx,%ecx /* ecx is now (X * ((2^33 + 1) / 9)) >> 33 */
ecx
是
(X * ((2^33 + 1) / 9)) >> 33
的 32 个最低有效位和
eax
是
X >> 31
,即
X
的 32 个“符号位”-s (这是一个有符号的 32 位整数),等于
0
如果
X
是非负的并且到
-1
如果
X
是否定的。
X
ecx
的重要部分是它实际上是
X * ((2^33 + 1) / 9)
的32个最高有效位.
1
到它。当我们添加
1
,我们将 lsb 反转为
1
如果是
0
,否则我们将它和它后面的所有位取反,直到我们找到第一个
0
然后也将其反转。
(X * ((2^33 + 1) / 9))
时会发生什么? (或者,等效地,如果我们使用
-X
执行计算,我们会得到什么,考虑到
X
在这个例子中是正的)?当然,首先我们反转它的所有位,然后我们添加
1
到它。但是对于后者(添加
1
)来影响数字的 32 个最重要的位,32 个最低有效位必须等于
0xFFFFFFFF
.并且(相信我)没有 32 位整数,当乘以
0x38e38e39
时,给出这样的结果。
(-X * ((2^33 + 1) / 9)) == -(X * ((2^33 + 1) / 9))
,它与 32 个最高有效位不同:
((-X * ((2^33 + 1) / 9)) >> 33) & 0xFFFFFFFF != -(((X * ((2^33 + 1) / 9)) >> 33) & 0xFFFFFFFF)
.
(-X * ((2^33 + 1) / 9))
的 32 个最高有效位等于
(X * ((2^33 + 1) / 9))
的 32 个最高有效位的按位求反:
((-X * ((2^33 + 1) / 9)) >> 33) & 0xFFFFFFFF != ~(((X * ((2^33 + 1) / 9)) >> 33) & 0xFFFFFFFF)
.
X
案例:
ecx
的值为
-X
将等于
ecx
的值的按位求反为
X
.我们不想那样。因此,要获得
X
的负值的正确结果,我们必须添加
1
至
ecx
(或者,等效地,减去
-1
):
sub %eax,%ecx /* ecx is now X / 9 */
mov %ecx,%eax /* eax is now X / 9 */
mov %eax,-0x10(%rbp) /* Aaand mov the result into the variable "cels" */
imul
也只需要一个周期)。考虑
idiv
可能占用几乎两倍的周期(Hans Passant 在
this 对类似问题的回答中提到的从 11 到 18),这种方法可以带来巨大的性能优势。
关于c - 需要解释 K&R fahr-to-cels 示例的组装说明,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27260524/
我在一个项目中工作,该项目需要 SQL 结果的最佳性能,并且希望优化查询,但经过反复试验后,我在 IN 方面遇到了一些问题。 -- THIS RETURNS NO RESULTS AT ALL. SE
在尝试创建一个实际上非常简单的 SQL 语句时,我发现自己迷失了方向。 我有一个包含 3 个表的数据库: 食谱 - 存储一些用于 cooking 的食谱名称 配料食谱 - 将配料与食谱链接 成分 -
我正在尝试理解 PHP 中的 Hebrev 函数。 https://php.net/manual/en/function.hebrevc.php 它说:“将逻辑希伯来语文本转换为视觉文本”。但我不明白
嗨,我在 Grid view 的 android 文档中发现了一段代码对于以下代码。 gridview.setOnItemClickListener(new OnItemClickListener()
谁能解释一下 InfiniBand 是什么?与以太网相比的主要区别是什么,这些差异如何使其比以太网更快? 在官方description从 mellanox 写到 Introduce InfiniBan
这个问题已经有答案了: How are java increment statements evaluated in complex expressions (1 个回答) 已关闭 8 年前。 我知道
我正在阅读 MySQL 教程,我遇到了这个: SELECT /*! SQL_NO_CACHE */ user FROM users; 为什么优化提示 SQL_NO_CACHE 包含在: /*!
我无法理解$(this),我做了一个剪刀石头布的版本,并应用了 jQuery 让用户在计算机上选择按钮选项。我希望有人能解释一下 $(this) 指的是什么,它是 btn-primary 吗?该函数在
我不是很确定 while(choice == 1 || choice ==2);谁能解释一下。我明白这一点 if(choice ==1) displayMonthly(rainfall); e
let flyRight = CABasicAnimation(keyPath: "position.x") flyRight.toValue = view.bounds.size.width/2 f
目录 解释:int型默认值为0 但我们尝试发现并不能通过: 原因: int的默认值为0,而Integer的默认值为null
我正在处理一个查询,自从一个 SSRS 服务器传输到另一个服务器后,它似乎没有按预期执行,并且 where 语句的一部分中出现了以下行 找出不同之处,或者至少从我能找到的地方来看。 where COA
我正在制作一个退回检测程序,读取退回邮件。我们的设置是发送电子邮件,在发送的邮件中添加一个 noreply@domain.tl。一些收件人不再存在,因此我们想要读取退回邮件,并检测它发送给谁。我已经崩
我有一个关于公式通过控制点弯曲的问题。 如您所知,HTML Canvas 有 quadraticCurveTo(x1, y1, x2, y2)与 x1 and x2作为控制点。 但是,当您尝试使用它绘
我有一个 Emakefile看起来像: %% -- %% %% -- {'/Users/user/projects/custom_test/trunk/*', [debug_info, {out
我有一个非常简单的问题。这不仅适用于 spray-json,而且我已经阅读了 argonaut 和 circe 的类似声明。所以请赐教。 在 spray-json 中,我遇到了 There is no
我正在为视频添加水印。我试图让水印与视频尺寸成比例。我已经使用 scale2ref 看到了十几个不同的答案,但没有解释实际发生了什么,所以我发现很难知道如何实现/更改配置以适应我的情况。 当前覆盖命令
因为我正在学习语言,所以我在玩 Haskell,我只是发现了一些我不理解的东西,我找不到解释。如果我尝试运行此代码: map (`div` 0) [1,2,3,4] 我得到一个除以 0 的异常,这是预
我正在寻找解决错误对象引用未设置到对象实例的步骤/指南。以及问题发生原因的解释。 我正在寻找更一般的解释,所以如果我收到错误,我应该采取什么步骤来查找问题。我经常看到有人提供特定代码段的帖子,而其他人
我最近想升级我的知识React ,所以我从组件生命周期方法开始。让我好奇的第一件事是这个componentWillReceiveProps .所以,文档说当组件接收新的(不一定是更新的) Prop 时
我是一名优秀的程序员,十分优秀!