繁体   English   中英

为什么使用 push/pop 而不是 sub 和 mov?

[英]Why use push/pop instead of sub and mov?

当我在https://godbolt.org上使用不同的编译器时,我注意到编译器生成这样的代码是很常见的:

push    rax
push    rbx
push    rcx
call    rdx
pop     rcx
pop     rbx
pop     rax

我知道每次pushpop做两件事:

  1. 将操作数移入/移出堆栈空间
  2. 递增/递减堆栈指针 (rsp)

所以在我们上面的例子中,我假设 CPU 实际上在做 12 次操作(6 次移动,6 次加/减),不包括call 组合添加/订阅不是更有效吗? 例如:

sub rsp, 24
mov [rsp-24], rax
mov [rsp-16], rbx
mov [rsp-8], rcx
call    rdx
mov rcx, [rsp-8]
mov rbx, [rsp-16]
mov rax, [rsp-24]
add rsp, 24

现在只有 8 次操作(6 次移动,2 次加/减),不包括call 为什么编译器不使用这种方法?

如果您使用-mtune=pentium3或比-mtune=pentium-m更早的东西进行编译,GCC像您想象的那样进行代码生成,因为在那些旧 CPU 上,push/pop 确实会解码为堆栈指针上的单独 ALU 操作,如以及加载/存储。 (您必须使用-m32-march=nocona (64 位 P4 Prescott),因为那些旧 CPU 也不支持 x86-64)。 为什么 gcc 使用 movl 而不是 push 来传递函数 args?

但是 Pentium-M 在前端引入了一个“堆栈引擎”,它消除了堆栈操作的堆栈调整部分,如 push/call/ret/pop。 它有效地以零延迟重命名堆栈指针。 请参阅Agner Fog 的微架构指南Sandybridge 微架构中的堆栈引擎是什么?

作为一种普遍趋势,在现有二进制文件中广泛使用的任何指令都会激励 CPU 设计人员使其更快。 例如,奔腾 4 试图让大家停止使用 INC/DEC; 那没有用; 当前的 CPU 比以往更好地执行部分标志重命名 现代 x86 晶体管和功率预算可以支持这种复杂性,至少对于大核 CPU(不是 Atom / Silvermont)。 不幸的是,我认为对于sqrtsscvtsi2ss类的指令的错误依赖(在目标上)没有任何希望。


在像add rsp, 8这样的指令中显式使用堆栈指针add rsp, 8要求 Intel CPU 中的堆栈引擎插入同步 uop 以更新寄存器的乱序后端值。 如果内部偏移量太大,则相同。

事实上add esp,4在现代 CPU 上, pop dummy_registeradd rsp, 8 pop dummy_register add rsp, 8add esp,4有效,因此编译器通常会使用它来弹出一个堆栈槽并使用默认调整,例如使用-march=sandybridge 为什么这个函数将 RAX 压入堆栈作为第一个操作?

另请参阅哪些 C/C++ 编译器可以使用 push pop 指令来创建局部变量,而不是仅仅增加一次 esp? 回复:使用push来初始化堆栈上的局部变量,而不是sub rsp, n / mov 在某些情况下,这可能是一种胜利,特别是对于具有小值的代码大小,但编译器不会这样做。


另外,不,GCC / clang 不会制作与您展示的完全一样的代码。

如果他们需要在函数调用周围保存寄存器,他们通常会使用mov to memory 来做到这一点。 mov到呼叫保持寄存器,它们保存在函数的顶部,并将于年底恢复。

除了传递堆栈参数之外,我从未见过 GCC 或 clang 在函数调用之前推送多个调用破坏的寄存器。 并且绝对不会在之后多次弹出以恢复到相同(或不同)的寄存器。 函数内部的溢出/重新加载通常使用 mov。 这避免了在循环内推送/弹出的可能性(除了将堆栈参数传递给call ),并允许编译器进行分支而不必担心匹配推送与弹出。 此外,它还降低了堆栈展开元数据的复杂性,因为每个移动 RSP 的指令都必须有一个条目。 (使用 RBP 作为传统帧指针时,指令计数与元数据和代码大小之间的有趣折衷。)

可以通过调用保留寄存器 + 一些 reg-reg 移动在一个小函数中看到类似于您的代码生成的东西,该函数只是调用另一个函数,然后返回一个__int128 ,它是寄存器中的函数 arg。 因此需要保存传入的 RSI:RDI,以返回 RDX:RAX。

或者,如果在非内联函数调用后存储到全局或通过指针,编译器还需要保存函数 args 直到调用之后。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM