我试图了解在设置-O3标志时gcc执行了哪种优化。我很困惑这两行,
xor %esi, %esi
lea 0x0(%esi), %esi
在我看来多余。在这里使用lea指令有什么意义?
答案 0 :(得分:4)
该指令用于填充空间以进行对齐。当它们在对齐的地址上启动时,循环可以更快,因为处理器以块的形式将内存加载到解码器中。通过对齐循环和函数的开头,它们更有可能位于其中一个块的开头。这可以防止以前的指令不被加载,最大化未来指令的数量,并且可能最重要的是,确保第一条指令完全在第一块中,因此不需要两次加载来执行它
编译器知道最好对齐循环,并且有两个选项可以执行此操作。它可以跳转到循环的开头,也可以用无操作填充间隙,让处理器流过它们。跳转指令会破坏指令流并且经常导致现代处理器上的浪费周期,因此不必要地添加它们是不可取的。对于像这样的短距离,无操作会更好。
x86体系结构包含专门用于无所事事的指令nop
。但是,这是一个字节长,因此对齐循环需要不止一个。对每一个进行解码并确定它不需要花费时间,因此简单地插入另一个没有副作用的较长指令会更快。因此,编译器插入了您看到的lea
指令。它绝对没有任何影响,并且由编译器选择具有所需的确切长度。事实上,最近的处理器具有标准的多字节无操作指令,因此在解码过程中可能会识别这种情况,甚至从未执行过。
答案 1 :(得分:1)
正如ughoavgfhw所解释的那样 - 这些是用于更好代码对齐的填充。
您可以在以下链接中找到此lea
-
http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/2010-September/003881.html
报价:
1-byte: XCHG EAX, EAX
2-byte: 66 NOP
3-byte: LEA REG, 0 (REG) (8-bit displacement)
4-byte: NOP DWORD PTR [EAX + 0] (8-bit displacement)
5-byte: NOP DWORD PTR [EAX + EAX*1 + 0] (8-bit displacement)
**6-byte: LEA REG, 0 (REG) (32-bit displacement)**
7-byte: NOP DWORD PTR [EAX + 0] (32-bit displacement)
8-byte: NOP DWORD PTR [EAX + EAX*1 + 0] (32-bit displacement)
9-byte: NOP WORD PTR [EAX + EAX*1 + 0] (32-bit displacement)
另请注意此SO问题更详细地描述 - What does NOPL do in x86 system?
请注意,xor本身不是一个nop(它会改变reg的值),但它也很便宜,因为它是一个零惯用语 - What is the purpose of XORing a register with itself?