我试图了解 gcc 在设置 -O3 标志时执行了什么样的优化。我很困惑这两行是什么,
xor %esi, %esi
lea 0x0(%esi), %esi
在我看来这是多余的。在这里使用 lea 指令有什么意义?
该指令用于填充空间以进行对齐。当循环从对齐的地址开始时会更快,因为处理器将内存以块的形式加载到解码器中。通过对齐循环和函数的开头,它们更有可能位于这些块之一的开头。这可以防止加载以前不会使用的指令,最大化未来指令的数量,并且可能最重要的是,确保第一条指令完全在第一个块中,因此执行它不需要两次加载.
编译器知道最好对齐循环,并且有两个选项可以这样做。它可以跳转到循环的开头,也可以用无操作来填补空白,让处理器通过它们。跳转指令会破坏指令流,并经常在现代处理器上造成周期浪费,因此不必要地添加它们是不可取的。对于这样的短距离,无操作更好。
x86 体系结构包含一条专门用于什么都不做的指令,nop
. 但是,这是一个字节长,因此对齐循环需要不止一个字节。解码每个指令并决定它什么都不做需要时间,因此简单地插入另一个没有副作用的更长指令会更快。因此,编译器插入了lea
您看到的指令。它绝对没有任何影响,并且由编译器选择以具有所需的确切长度。事实上,最近的处理器有标准的多字节无操作指令,所以这很可能在解码过程中被识别出来,甚至永远不会被执行。
正如 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 问题更详细地描述它 - NOPL 在 x86 系统中做了什么?
请注意,xor 本身不是 nop(它会更改 reg 的值),但执行起来也非常便宜,因为它是一个零习语 -将寄存器与自身进行异或的目的是什么?