Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/vim/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Visual c++ 为什么VC++;编译器MOV+;推参数而不是仅仅推它们?x86_Visual C++_Assembly_Compiler Construction_X86 - Fatal编程技术网

Visual c++ 为什么VC++;编译器MOV+;推参数而不是仅仅推它们?x86

Visual c++ 为什么VC++;编译器MOV+;推参数而不是仅仅推它们?x86,visual-c++,assembly,compiler-construction,x86,Visual C++,Assembly,Compiler Construction,X86,在这个从VC++反汇编的过程中,正在进行函数调用。编译器在推送本地指针之前将其移动到寄存器: memcpy( nodeNewLocation, pNode, sizeCurrentNode ); 0041A5DA 8B 45 F8 mov eax,dword ptr [ebp-8] 0041A5DD 50 push eax 0041A5DE 8B 4D 0C mov

在这个从VC++反汇编的过程中,正在进行函数调用。编译器在推送本地指针之前将其移动到寄存器:

    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 
为什么不直接推动他们呢?即

push  dword ptr [ebp-8]
另外,如果要单独推送,为什么不手动推送呢。换句话说,不要执行上面的“push eax”,而是执行

mov [esp], eax
这样做的好处是,在完成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

肯定比调试版本更高效,但它仍在执行MOV/推送组合。

我怀疑它只在调试构建中执行,或者可能在某些情况下执行,在这些情况下,它需要管道或其他考虑(例如,它可以将参数放入
esi
并在调用后使用)。我研究过一些二进制文件,MSVC肯定会使用这样的推送:

 push ebx          ; mthd
 push dword ptr [ebp+place+4]
 push dword ptr [ebp+place] ; pos
 push [ebp+filedes]   ; fh
 call __lseeki64_nolock
(来自CRT的代码)


至于第二个问题,寻址esp的指令比push长:
“push eax”
是一个字节,而
“mov[esp-8],eax”
是四个字节。事实上,这种方法(
mov
而不是
push
)是默认情况下由GCC使用的,因为在几个版本之前(选项
-maccumulate outing args
),它已经导致了。据推测,这会使代码更快,但我不相信。

这是一种优化。《英特尔处理器手册》第4卷第12.3.3.6节中明确提到:

在英特尔原子微体系结构中,使用PUSH/POP指令管理堆栈空间 函数调用/返回之间的地址调整将比 使用输入/离开选项。这是因为PUSH/POP不需要MSROM 流和堆栈指针地址更新在AGU完成。 当被调用函数需要返回给调用方时,被调用方可以发出POP指令 从EBP恢复数据和堆栈指针

汇编/编译器编码规则19。(MH影响,M一般性)适用于英特尔 Atom处理器,喜欢PUSH/POP的寄存器形式,避免使用LEAVE;使用LEA 调整ESP而不是添加/订阅


手册的其余部分并没有明确说明原因,但它确实提到了隐式ESP调整可能出现的3周期AGU失速。

我实际上已经找到了原因。这与奔腾MMX上指令的流水线方式有关。有两条管道,U和V,允许MMX处理器一次处理两条指令,如果它们是可配对的。推送不能彼此配对,但它们可以与MOV配对。所以,如果你写:

mov eax, [indirect]
mov esi, [indirect]
push eax
push esi

然后,发生的情况是指令1和3配对,指令2和指令4配对,因此,有效地,这四条指令的运行周期与单个mov/推送相同,单个mov/推送比两个推送[间接]快。第4.3节,p.详细描述了这一具体情况。41,Agner Fog的微体系结构优化指南的示例4.11a和4.11b,可在互联网上广泛获得。

这实际上是在发布模式下编译的吗?看起来很模糊debuggishIt是为调试而编译的。为什么在这种情况下会有不同?因为编译器在调试模式下不会关心这些事情。在最后一个示例中,通过延迟子进程使堆栈暂时不平衡是否安全?我知道在实模式下这是个坏消息(在不合适的时候中断“借用”堆栈的一部分),但在保护模式下我不确定。通过解耦指令,可以减少寄存器暂停的数量。我展示了发布构建ASM,它具有类似的MOV。在查看项目的代码时,编译器从不执行间接推送,只执行MOV。我怀疑这可能是为了支持一个非常旧的x86版本,比如286或386之类的东西。@Tylerduden:每个拥有32位寄存器的x86家族成员都有“推送内存”指令。但它们的执行速度通常比MOV-PUSH组合慢,因为现代微体系结构并不真正喜欢具有多个内存访问的指令。这不是真正的原子问题。我使用的是VS10,它的编译器是在Atom出现之前编写的。此外,Atom主要是笔记本电脑之类的低功耗架构。这是一个微架构问题,Atom只是实现它的众多芯片中的一个。它们实际上不会在这里配对,因为它们必须在PMMX中相邻。如果MSVC还在为PMMX进行优化,我也会非常惊讶。。