【问题标题】:x86 assembly: Pop a value without storing itx86 程序集:弹出一个值而不存储它
【发布时间】:2018-07-20 04:48:06
【问题描述】:

在 x86 汇编中,是否可以从堆栈中删除一个值而不存储它?类似于pop word null 的东西?我显然可以使用add esp,4,但也许我缺少一个漂亮而干净的 cisc 助记符?

【问题讨论】:

标签: assembly x86 stack callstack stack-pointer


【解决方案1】:

add esp,4 / add rsp,8 正常/惯用/干净的方式。不需要 特殊 方式,因为堆栈并不神奇或特殊(至少在这方面不是);它只是寄存器中的一个指针,其中包含一些隐式使用它的指令。 (对于内核堆栈,中断会异步使用它,因此即使软件想要实现内核红区也无法实现......)

除此之外,在函数末尾清理整个堆栈帧的神奇 CISC 方法是 leave = mov esp, ebp / pop ebp(或 16 位或 64 位等效项)。与enter 不同,它在现代 CPU 上足够快,可以在实践中使用,但在 Intel CPU 上仍然是 3 uop 指令。 (http://agner.org/optimize/)。但是leave 仅在您首先花费额外的指令使用ebp / rbp 制作堆栈帧时才有效。 (通常您不会这样做,除非您需要保留可变数量的堆栈空间,例如在循环中使用 push 来创建一个数组,或者等效于 C99 VLA 或 alloca。或者对于初学者代码使访问本地人更容易,或者在 SP 不能用于寻址模式的 16 位模式下。)

清理堆栈参数的神奇 CISC 方法是让被调用者使用 ret imm16(花费 1 个额外的 uop)来弹出参数,创建一个调用约定,被调用者清理堆栈。在 caller-pops 调用约定中,无法使用这种形式的 ret,但您可以简单地保留堆栈偏移量并使用 mov 来存储 args 以供下一个函数调用而不是 push(如果函数根本需要任何堆栈参数;寄存器参数调用约定通常更有效。)

因此,神奇的 CISC 方式在现代 CPU 上没有性能优势,只有较小的代码大小。


您可以使用pop reg 代替add esp,4 的原因有两个:

  • 代码大小:pop r32/r64 是一个单字节指令,而 add esp,4 是 3 个字节,add rsp,8 是 4 个字节。
  • 性能:当您在堆栈指令 (push/pop/call/ret) 之后显式使用 esp / rsp 时,英特尔的堆栈引擎必须插入额外的堆栈同步微指令。因此,在call(返回ret)之后,它会在函数末尾的ret 之前保存一个uop 以使用pop 而不是add esp,4

    AMD 的堆栈引擎不需要额外的堆栈同步微指令,但仍会生成推送/弹出单微指令。与旧的 Intel/AMD CPU 不同,push/pop 的成本高于普通的mov 加载/存储,需要单独的 uop 来修改堆栈指针。并创建对堆栈指针的数据依赖。

有关性能的更多详细信息,请参阅Why does this function push RAX to the stack as the first operation?

如果您正在寻找美学,那么您可以很好地缩进、格式化和注释您的代码,但除此之外,如果美学胜过优化,那么您在选择 x86 asm 时选择了错误的语言


当然,如果您需要将堆栈调整超过 1 个寄存器宽度,如果您不需要 pop 将加载的数据,请务必使用 add。或者,如果您需要调整 +128 字节,请使用 sub esp, -128,因为 -128 可以编码为符号扩展的 imm8,但 +128 不是。

或者可以使用lea esp, [esp+4],就像gcc 使用-mtune=atom 一样。 (对于有序原子,而不是silvermont)。就像我说的,如果你想要干净,你不应该选择 x86 asm。


您几乎总能在pop 中找到一个死寄存器。如果您需要在弹出一些您实际想要弹出的寄存器之前将 E/RSP 调整一个堆栈槽,您始终可以弹出同一个寄存器两次。

在极少数情况下,7 (x86-32) 或 15 (x86-64) 非堆栈寄存器都不能用作 pop 目标,此优化不可用,您应该简单地使用传统的add不值得花费额外的指令来使pop成为可能;这将超过使用 pop 的小好处。

请注意,pop Sreg(段寄存器)仍然使用常规的“堆栈宽度”(32 位或 64 位,具体取决于模式),而不是 16 位寄存器仅消耗 16 位。 But only pop ds/es/ss are single-byte. pop fs/gs are 2 bytes each。因此,如果您正在优化代码大小,pop gsadd esp,4 小 1 个字节,但要慢得多。 (或比add rsp,8 小2 个字节)。

【讨论】:

  • 您可能希望在第一次迭代时编写“时尚”的 asm 版本,然后再进行基准测试并击败它,因此您至少拥有相当干净的基本版本。但是,当然,如果您不追求性能或尺寸,那么您应该远离 asm,所以我最初的评论处于错误的边缘,我可能会删除它,因为您回答得更好。
猜你喜欢
  • 2014-11-16
  • 2020-09-16
  • 1970-01-01
  • 2020-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多