RTC*_*222 2 assembly x86-64 nasm eflags
我想将比较结果设置的零标志(例如“cmp rax,rbx”)移动到寄存器中。我知道我可以使用 PUSHF/PUSHFD/PUSHFQ 之一将标志推入堆栈,但现在我只想将零标志从堆栈移动到寄存器。
根据https://www.felixcloutier.com/x86/pushf:pushfd:pushfq,它“将堆栈指针减 4(如果当前操作数大小属性为 32)并将 EFLAGS 寄存器的全部内容推送到堆。” 但这并没有告诉我它们被推送的顺序以及如何访问零标志。
根据英特尔软件开发人员手册(2023 年 6 月)第 7.3.13.2 节,零标志是从零开始的 #6。(还有维基百科)。因此我猜这样就mov al,[rsp+6]可以了。但我需要确定,因为它们都只是布尔值——我可能会获取错误的布尔值。
我处于 64 位模式,因此我将寻址 RFLAGS 寄存器。手册第 7.3.1.4 节说:“PUSHF 和 POPF 在 64 位模式下的行为与非 64 位模式下的行为相同。PUSHFD 始终将 64 位 RFLAGS 压入堆栈(RF 和 VM 标志读为清除) )。POPFD 总是从堆栈顶部弹出一个 64 位值,并将低 32 位加载到 RFLAGS。然后将 RFLAGS 的高位进行零扩展。
最后,PUSHFD 和 PUSHFQ 有什么区别?双字和四字,但在 64 位模式下应该使用哪个?
不要用于pushf此。正如评论中已经提到的,将 ZF 实现到寄存器中的最佳方法是条件设置指令setz。这正是它的用途。 setcc reg8根据条件代码cc是假还是真,将 8 位寄存器设置为 0 或 1。
事实上,它设置了 8 位部分寄存器,而 64 位寄存器的其他 56 位保持不变,这一点很尴尬。它很少有用,而且实际上对性能不利,因为它会引入对先前 64 位寄存器值的读取依赖性。为了缓解这种情况,您可以先将 64 位寄存器清零。例如:
xor ecx, ecx
cmp rax, rbx
setz cl
Run Code Online (Sandbox Code Playgroud)
请注意,xor ecx, ecx实际上将 全部归零rcx,并且比显而易见的更有效xor rcx, rcx;请参阅在 x86 汇编中将寄存器设置为零的最佳方法是什么:xor、mov 或 and?为什么32 位寄存器上的 x86-64 指令会将整个 64 位寄存器的上部清零?。并确保xor位于 之前cmp,而不是之后,因为xor更新了标志。
家庭pushf在这里不是一个好的选择;它本身是一条相当慢的指令(大约 5-10 个时钟周期延迟),涉及内存访问,然后需要更多的工作来操作结果的位。 setz另一方面通常只有一个时钟周期。如果由于某种原因你无法使用setz,那么你的下一个最佳选择将是条件跳转:
xor ecx, ecx
cmp rax, rbx
jnz onward
mov ecx, 1
onward:
;; more code
Run Code Online (Sandbox Code Playgroud)
但就你的实际问题而言pushf。
就“顺序”而言,pushfq在 64 位模式下,将整个 RFLAGS 寄存器作为单个 64 位值推送。RFLAGS 的布局可以在英特尔手册以及您已经链接的维基百科页面中找到。位编号从 0 开始,作为最低有效位。所以 ZF 是第6 位,实现 ZF 的有效但低效的方法是
pushfq
pop rax
shr eax, 6
and eax, 1
Run Code Online (Sandbox Code Playgroud)
您的建议mov al, [rsp+6]将加载al字节6,即位 48-55。32 以上的所有位都被保留,并且可能为 0,因此这只会在 中为您提供 0 al。同样,标志是单个 64 位四字的各个位;它们不会作为 64 个单独的字节推送。
pushfd和pushfq是相同的操作码;推32位还是64位的问题是由当前CPU模式决定的。如果您正确地告诉汇编器您正在为 64 位模式编写代码(例如使用bits 64nasm),那么它将拒绝汇编pushfd.
pushfnasm 实际上会在 64 位模式下处理助记符pushfq,因为这通常是您想要的。然而,可以在 64 位模式下使用操作数大小覆盖字节执行 16 位标志推送;Intel 的手册对此进行了称呼pushf,但 nasm 要求您将其编写为pushfw. 但这并没有什么用处,因为它会使堆栈错位。在 64 位模式下,您总是希望压入和弹出完整的 64 位(8 字节)单元,除非在非常不寻常的情况下。
| 归档时间: |
|
| 查看次数: |
463 次 |
| 最近记录: |