有条件地将零移入寄存器?

Posted

技术标签:

【中文标题】有条件地将零移入寄存器?【英文标题】:Conditional move zero into register? 【发布时间】:2020-11-30 18:16:05 【问题描述】:

有没有办法在汇编中有条件地将零移动到寄存器中?我正在努力做

cmpb %r9b, %r8b #compare r9 and r8
cmovgq $0, %rcx #If r8>r9, move zero to rcx

但编译器抱怨“cmovg 的操作数类型不匹配”,因为第一个操作数是立即数。我考虑过有条件地跳转到 mov 但我不确定是否还有其他可能更好的操作码。如何在不占用另一个寄存器的情况下有条件地将这个寄存器归零?

【问题讨论】:

您可以在内存中存储一​​个零并使用内存操作数,但它可能比条件跳转慢。否则,您将需要另一个寄存器。 请注意,您可以在文本段附近的内存中使用一个常量零,并使用相对于 RIP 的地址来访问它。这可能比难以预测的分支更快,但比临时使用另一个寄存器要慢。 【参考方案1】:

由于cmovgq 指令不接受立即值操作数,不使用其他寄存器的方法可能是将零值添加到堆栈中,使用其对应地址而不是立即值,然后恢复堆栈指针。

pushq   $0            # push 0 onto the stack
cmpb    %r9b,   %r8b  # compare r9b and r8b
cmovgq  (%rsp), %rcx  # if r8b > r9b, move zero to rcx
addq    $8,     %rsp  # restore stack pointer

或者,与其将零压入堆栈然后恢复堆栈指针,零值可以存储在内存中的其他位置。

  cmpb    %r9b, %r8b  # compare r9b and r8b
  cmovgq  zero, %rcx  # if r8b > r9b, move zero to rcx
  ...

  .section .rodata
zero:
  .quad 0

条件跳转不是使用cmovgq,而是一种替代方法,它不会使用另一个寄存器,并且会导致相同的行为(即,如果r8b > r9brcx 有条件地设置为 0)。

  cmpb    %r9b, %r8b   # compare r9 and r8
  jle     destination  # if r8b <= r9b, skip the next line
  movq    $0, %rcx     # if r8b > r9b, move zero to rcx
destination:
  ...

【讨论】:

人们出于性能原因使用cmov,而这些建议对性能不利。正常的方法是在 cmp 之前使用一个额外的寄存器,例如 xor %eax,%eax,然后使用 cmov,而不是从内存中加载。尤其是不要在它周围推送/添加 rsp。 godbolt.org/z/ncba87 另见 (What is the best way to set a register to zero in x86 assembly: xor, mov or and? - xor not mov)

以上是关于有条件地将零移入寄存器?的主要内容,如果未能解决你的问题,请参考以下文章

如何将浮点常量移入 FP 寄存器?

如何将浮点常量值移动到xmm寄存器中?

有效地将 CPU 寄存器中的所有位设置为 1

有效地将 YMM 寄存器的低 64 位设置为常数

在单臂霓虹灯寄存器中有效地将 8 位数字扩展到 12 位

如何有效地将 zmm 寄存器的低 64 位保存到内存中?