为什么VC++编译器MOV + PUSH而不仅仅是推动它们?86

Tyl*_*den 5 compiler-construction x86 assembly visual-c++

在VC++的这个反汇编中,正在进行函数调用.在推送寄存器之前,编译器将本地指针MOV转换为寄存器:

    memcpy( nodeNewLocation, pNode, sizeCurrentNode );
0041A5DA 8B 45 F8             mov         eax,dword ptr [ebp-8]  
0041A5DD 50                   push        eax  
0041A5DE 8B 4D 0C             mov         ecx,dword ptr [ebp+0Ch]  
0041A5E1 51                   push        ecx  
0041A5E2 8B 55 D4             mov         edx,dword ptr [ebp-2Ch]  
0041A5E5 52                   push        edx  
0041A5E6 E8 67 92 FF FF       call        00413852  
0041A5EB 83 C4 0C             add         esp,0Ch 
Run Code Online (Sandbox Code Playgroud)

为什么不直接推他们?即

push  dword ptr [ebp-8]
Run Code Online (Sandbox Code Playgroud)

此外,如果您要单独执行推送,为什么不手动执行此操作.换句话说,不要做上面的"推送eax",而是做

mov [esp], eax
Run Code Online (Sandbox Code Playgroud)

等等.这样做的好处是,在执行3个mov之后,您可以执行单个减法来设置新的堆栈指​​针,而不是通过推送隐式减去三次.

更新---发布版本

这与为发布而编译的代码相同:

; 741  :    memcpy( nodeNewLocation, pNode, sizeCurrentNode );

  00087 8b 45 f8     mov     eax, DWORD PTR _sizeCurrentNode$[ebp]
  0008a 8b 7b 04     mov     edi, DWORD PTR [ebx+4]
  0008d 50       push    eax
  0008e 56       push    esi
  0008f 57       push    edi
  00090 e8 00 00 00 00   call    _memcpy
  00095 83 c4 0c     add     esp, 12            ; 0000000cH
Run Code Online (Sandbox Code Playgroud)

绝对比调试版本更有效,但它仍然在做一个MOV/PUSH组合.

Han*_*ant 5

这是一个优化.英特尔处理器手册第4卷第12.3.3.6节明确提到了这一点:

在Intel Atom微体系结构中,使用PUSH/POP指令管理堆栈空间和函数调用/返回之间的地址调整将比使用ENTER/LEAVE替代方案更优化.这是因为PUSH/POP不需要MSROM流,而堆栈指针地址更新在AGU完成.当被调用函数需要返回给调用者时,被调用者可以发出POP指令来恢复数据并从EBP恢复堆栈指针.

汇编/编译器编码规则19.(MH影响,M一般性)对于Intel Atom处理器,请使用PUSH/POP的寄存器形式并避免使用LEAVE; 使用LEA调整ESP而不是ADD/SUB.

手册的其余部分并不清楚原因,但确实提到了隐式ESP调整可能的3周期AGU失速.


Tyl*_*den 1

我其实已经明白其中的原因了。它与 Pentium MMX 上的指令流水线方式有关。有两个管道,U 和 V,允许 MMX 处理器一次处理 2 条指令(如果它们可配对)。PUSH 不能相互配对,但可以与 MOV 配对。所以,如果你写:

mov eax, [indirect]
mov esi, [indirect]
push eax
push esi
Run Code Online (Sandbox Code Playgroud)

然后,发生的情况是指令 #1 和 #3 配对,#2 和 #4 配对,因此,实际上,这四个指令与单个 mov/push 运行相同数量的周期,并且单个 mov/push 是比两次推送[间接]更快。第 4.3 节第 14 页详细描述了这种具体情况。41,Agner Fog 的微架构优化指南的示例 4.11a 和 4.11b,可在互联网上广泛获取。

  • 它们实际上不会在这里配对,因为它们必须在 PMMX 中相邻。如果 MSVC 仍在针对 PMMX 进行优化,我也会感到非常惊讶。 (3认同)