#include <stdio.h>
int main() {
int i = 1;
while(i) {
printf("in loop\n");
i++;
if(i == 10) {
break;
}
}
return 0;
}
用gcc编译(Ubuntu / Linaro 4.7.2-2ubuntu1)4.7.2,它反汇编为:
0x000000000040051c <+0>: push %rbp
0x000000000040051d <+1>: mov %rsp,%rbp
0x0000000000400520 <+4>: sub q4312078qx10,%rsp
0x0000000000400524 <+8>: movl q4312078qx1,-0x4(%rbp)
0x000000000040052b <+15>: jmp 0x400541 <main+37>
0x000000000040052d <+17>: mov q4312078qx400604,%edi
0x0000000000400532 <+22>: callq 0x4003f0 <puts@plt>
0x0000000000400537 <+27>: addl q4312078qx1,-0x4(%rbp)
0x000000000040053b <+31>: cmpl q4312078qxa,-0x4(%rbp)
0x000000000040053f <+35>: je 0x400549 <main+45>
0x0000000000400541 <+37>: cmpl q4312078qx0,-0x4(%rbp)
0x0000000000400545 <+41>: jne 0x40052d <main+17>
0x0000000000400547 <+43>: jmp 0x40054a <main+46>
0x0000000000400549 <+45>: nop
0x000000000040054a <+46>: mov q4312078qx0,%eax
0x000000000040054f <+51>: leaveq
0x0000000000400550 <+52>: retq
为什么+45上出现
nop
?为什么+35上的je
不能直接跳到+46?#1 楼
它可能用于功能对齐。现在,它在0x400550
上返回,可以除以8。如果在0x40054f
上返回,则未对齐。不过只是个推测。#2 楼
大多数微处理器都以对齐的16字节或32字节块为单位获取代码。
如果重要的子例程条目或跳转标签恰好在16字节块的
端附近,则微处理器提取该代码块时,只会得到一些有用的代码字节。它可能还必须
获取下一个16个字节,然后才能解码标签之后的前一个
指令。可以通过将重要的子例程条目和循环条目对齐16来避免此问题。对齐8
可以确保至少可以通过首个
指令读取加载8个字节的代码,这可能会如果指令很小,就足够了。
通过Agner Fog使用汇编语言优化子例程。 PDF
评论
您能否指出OP拆卸中的这种更好的对齐方式?我不明白为什么在打no的地方会有所改善。
–杂件
13-10-19在0:47
#3 楼
NOP插入的另一个原因是由于管道调度。如果分支预测需要一个周期来确定它是否正确(以及是否不冲洗管道),那么在将结果提交给寄存器之前,您需要一个周期延迟。在跳跃等于NOP的特定示例中,在我看来,处理器需要一个周期来确定其是否获得正确的答案,并根据需要调整管道。
深入研究代码并了解正在发生的事情。 :)
评论
我用Google搜索函数对齐方式,并在SO上找到了这篇文章,它似乎在更详细地回答了我的问题。感谢您的帮助。
– pyCtrl_
13-10-4在15:32
抱歉,我不认为这是函数对齐。这仅适用于功能开始,而不适用于结束。它也不是-falign-labels(按照falign-labels的描述插入了nop,但是不使用调整后的地址)。我宁愿认为这是由于编译器为更长的操作码表示保留了一些字节而没有清理掉。
–杂件
13年10月13日在14:07
是的,这似乎是未优化的代码。尝试将-O3或-Os生成的代码与-O0生成的代码进行比较。
– microtherion
13年10月13日在16:44