如何在没有移位/旋转的情况下在 GPR 的特定位设置进位标志?

Max*_*ian 0 x86 assembly bit-manipulation carryflag eflags

我正在为 Intel 80386 处理器在 NASM 中编写程序,我需要在 GPR(通用寄存器)的特定位中设置进位标志的值,而不更改寄存器中的其他位。

是否有可能在不使用任何类型的移位/旋转的情况下这样做?

Tho*_*ger 5

这样做的一种无分支方法是用进位标志填充临时寄存器,掩码您想要的位,然后或用您的目标寄存器填充它。

使用EAX如划痕(以此为正被操纵的32位的值):

sbb eax, eax
and eax, 1 << 16  ; Adjust bitshift in constant for the desired bit. Multiple bits can be used.
Run Code Online (Sandbox Code Playgroud)

如果未设置进位标志,sbb将执行eax = eax - eax = 0. 如果设置了进位标志,sbb将会执行eax = eax - (eax + 1) = -1,所以所有位都被设置。然后将所需的位屏蔽。

之后,您需要在目标中设置适当的位。如果该位处于初始已知状态,则这可以简化。使用EBX为目标:

and ebx, ~(1 << 16)  ; Same value at before. This doesn't need to be a shifted bit, it could be a number.
or  ebx, eax
Run Code Online (Sandbox Code Playgroud)

根据之前发生在临时寄存器上的情况(EAX此处),可能值得查看一些优化信息,例如https://www.agner.org/optimize/。一些处理器会认识到新值EAX不依赖于旧值,一些处理器会认为它对旧值有(错误的)依赖。

看了之后,文档“Optimizing subroutines in assembly language”sbb在“用位操作指令替换条件跳转”一节中提到了上述技巧。

使用EAX(累加器)作为暂存寄存器将导致更小的代码大小。

  • 还可以考虑使用“cmovc”来缩短关键路径延迟。 (2认同)