【问题标题】:How to pop a lesser-sized value into a register如何将较小的值弹出到寄存器中
【发布时间】:2020-08-29 21:09:23
【问题描述】:

我有一个 2 字节的值存储在堆栈中,我想将 pop 放入寄存器中。我可以通过使用其中的 16 位寄存器在其中一个通用寄存器上执行此操作,例如:

popw %cx

有没有办法将它放入另一个寄存器,例如r11,并用零填充?比如:

popzwq %r11   # making up the syntax

或者什么是正确的方法?

【问题讨论】:

  • 您真的只想将堆栈指针增加 2 吗?堆栈通常是 8 字节对齐的,因此增加 2 是不常见的。
  • pop %r11 movzwq %r11w, %r11
  • 设计师可以引入无穷无尽的复合指令,他们只会花时间和电路来做一些真正有用的事情。保持指令集整洁和原子也是设计者的原则。

标签: assembly x86 x86-64 cpu-registers


【解决方案1】:

通常你一开始就避免这种情况,从不使用 pushw,而是在推送/弹出时始终使用全宽(64 位)堆栈槽。这也使堆栈保持对齐。此外,通常您只会在进入/离开函数时使用 push/pop 来保存/恢复寄存器。 (没有调用约定保留低位而不保留高位字节,通常保存/恢复完整寄存器最有效。)

但如果您确实需要它,只需用 2 条指令模拟即可。

   movzwl  (%rsp), %ecx          # or %r11d.  implicitly zero-extends into the full 64-bit reg
   add     $2, %rsp              # or if you need to preserve FLAGS,
                                 # lea 2(%rsp), %rsp

pop 没有零扩展形式; 8086 只能弹出全宽(16 位)寄存器,因此没有要修改的现有操作码。并且 386 没有引入任何与 movzx 或 movsx 等效的零扩展 pop;几乎没有人会从中受益:普通代码在使用它时会使用全角推送/弹出。 (更常见的是当时常见的遗留堆栈参数调用约定,而不是前几个参数的现代高效寄存器参数。) 所以在晶体管和操作码编码空间上的成本是不值得的。


或者,如果您不想零扩展,pop %r11w。 (不确定您是否不知道 r8..r15 的低 8 / 低 16 / 低 32 寄存器的名称,或者为什么在第二个示例中不使用 RCX 来与 pop cx 进行对比)。

【讨论】:

  • add $2, %rsp。这让我害怕。为什么会有人这样做?
  • @W.Chang:因为他们在模仿popw。同意在极少数情况下做任何一件事情都是有意义的;这就是该答案第一段的重点。在 asm 中有很多可能但可怕的事情。例如在有限的情况下,您可以使用 ESP 或 RSP 作为暂存空间,保存一个非指针。 (例如,没有安装信号处理程序,并且您在用户空间中。通常仍然是一个坏主意,尤其是在 x86-64 上,您有 15 个其他 regs,加上 xmm0..15,而不是只有 7 个其他 32 位 GPR。 )
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-25
  • 1970-01-01
相关资源
最近更新 更多