理解汇编:-O2 如果分支
我只是使用 if 语句尝试了一个简单的 C 程序并分析了它的汇编。然而,当使用 -O2 标志进行编译时,其行为有很大不同。
相同的 C 代码是:-
#include<stdio.h>
int main(int argc, char **argv) {
int a;
if(a<0) {
printf("A is less than 0\n");
}
}
相应的汇编是:-
main:
push %ebp
mov %ebp, %esp
sub %esp, 8
and %esp, -16
sub %esp, 16
test %eax, %eax
js .L4
leave
ret
.p2align 4,,15
.L4:
sub %esp, 12
push OFFSET FLAT:.LC0
call puts
add %esp, 16
leave
ret
.size main, .-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.4.6"
我读到 test
指令基本上只是执行两个操作数的逻辑与。我还了解到,当前一条指令中的符号发生变化时,js
指令会执行跳转。因此,用 eax 测试 eax 将给出 0 或 1,跳转将取决于此。
我无法理解它是如何在这里用于分支的。 有人可以解释这是如何工作的吗?
I just tried out a simple C program using an if statement and analyzed its assembly. However, its behavior differs a lot when -O2 flag is used for compilation.
The C code for the same is :-
#include<stdio.h>
int main(int argc, char **argv) {
int a;
if(a<0) {
printf("A is less than 0\n");
}
}
And the corresponding assembly is:-
main:
push %ebp
mov %ebp, %esp
sub %esp, 8
and %esp, -16
sub %esp, 16
test %eax, %eax
js .L4
leave
ret
.p2align 4,,15
.L4:
sub %esp, 12
push OFFSET FLAT:.LC0
call puts
add %esp, 16
leave
ret
.size main, .-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.4.6"
I read that the test
instruction basically just performs the logical AND of the two operands. I also read that the js
instruction performs a jump when there is a change in sign in the previous instruction. So, test
ing eax with eax would give 0
or 1
and the jump would depend on this.
I fail to understand how it is being used here for branching.
Could someone explain how this works?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
JS 当符号改变时不跳转,如果符号标志为 1,则跳转。
如果最后一次运算结果为负,则符号位打开(2 的补数中的负数具有 1 中的最高位) 。
因此,如果 AND 运算是在两个负整数(-1 和 -1)之间,最后一位将为 1(符号标志),因此进行跳转。如果数字为正,最后一位将为 0,则不会进行跳转。
JS doesn't jump when there is a change in sign, it jumps if the sign flag is 1.
The sign bit is on if the result of the last operation was negative(negative numbers in 2's compliment have the most significant bit in 1).
So if the AND operation was between two negative integers(-1 & -1) the last bit will be 1(sign flag), so the jump is taken. In case the numbers were positive the last bit would be 0, the jump won't be taken.
好吧,我不知道你期望什么,但你的程序有未定义的行为,因为
a
未初始化。所以汇编器的输出实际上可以是任何东西。Well I don't know what you expect, but your program has undefined behavior since
a
is not initialized. So the assembler output could be literally anything.英特尔手册对此很有帮助。这是 TEST 指令的文档:
SF 是符号标志,由 JS 操作码测试的标志。这里它被设置为 eax 的最高有效位,即符号位。因此,当 eax 包含负数时进行跳转。
The Intel manuals are good for this. This is what it documents for the TEST instruction:
SF is the sign flag, the one that's tested by the JS opcode. It is set to the most significant bit of eax here, the sign bit. The jump is thus taken when eax contains a negative number.
如果
eax
为负,则标志将指示在test
指令之后并且将进行跳转。这会将 PC 推送至执行打印的.L4
。否则,我们就离开。If
eax
is negative, the flags will indicate that after thetest
instruction and the jump will be taken. That pushes the PC to.L4
which does the printing. Otherwise, we leave.test eax, eax
如果 eax = 0 则将设置零标志js 指令将基本上检查符号标志(a<0)
test eax, eax
will set zero flag if eax = 0js instruction will check the sign flag basically (a<0)
看起来当您指定 -O2 时,编译器会将您的“int a”放入寄存器中以进行速度优化。
由于您从未初始化“int a”,因此程序集不会显示写入 eax 的任何内容,而是具有最后分配给它的值。
其他答案解释了测试如何作为分支机制工作。
Looks like when you specify -O2, the compiler is putting your "int a" into a register for speed optimization.
Since you never initialize 'int a', the assembly does not show anything written to eax and it instead has the value that was last assigned to it.
The other answers explain how test is working as a branching mechanism.