算术恒等式和 EFLAGS

0 x86 assembly x86-64 eflags

由于 ?x = not(x)+1 则意味着 ab = a+not(b)+1,那么

sub rax, rcx
Run Code Online (Sandbox Code Playgroud)

相当于

mov temp, rcx
not temp
add rax, temp
add rax, 1
Run Code Online (Sandbox Code Playgroud)

temp 某些寄存器被认为是易失性的?

换句话说,后者是否以完全相同的方式影响 EFLAGS?如果不是,又怎么能强求呢?

Nat*_*dge 5

不,它们不是等价的。例如,如果rax = 1rcx = 3sub rax, rcx则将设置进位标志,因为您正在从较小的数字中减去较大的数字。但是在您的第二个指令序列中,以下add rax, temp,rax将包含-3(即0xfffffffffffffffd),并且添加1-3不会导致进位。所以在你的第二个指令序列之后,进位标志将被清除。

我不知道有什么简单的方法可以精确模拟将sub其对标志的影响包括在内的行为(除了使用cmp,但这是作弊,因为它实际上只是sub在幕后)。原则上,您可以编写一长串指令,手动执行与sub内部执行的所有相同测试(参考指令集手册中的精确描述),并在最后使用sahfpopf类似方法设置标志。

这将是很多工作,特别是如果您不打算使用cmp,并且我不打算通过它来回答这个问题。特别是因为我也想不出有什么理由需要这样做,除非是一项相当无意义的练习。


Pet*_*des 5

是的,这在 RAX 中得到了相同的整数结果。

\n
\n

换句话说,后者是否以完全相同的方式影响 EFLAGS?

\n
\n

当然不是。ZF、SF 和 PF 仅取决于整数结果,但CF 和 OF 1取决于如何获得结果。x86 的 CF 进位标志是减法的借位输出。(与某些 ISA(例如 ARM)不同,如果没有借位,则减法会设置进位标志。)

\n

您可以在头脑中检查一下简单的反例:
\n0 - 1sub集合 CF=1。但你的方法清除了CF。

\n
mov temp, rcx        # no effect on FLAGS\nnot temp             # no effect on FLAGS, unlike most other x86 ALU instructions\nadd rax, ~1 = 0xFF..FE     # 0 + anything  clears CF\nadd rax, 1                 # 0xFE + 1 = 0xFF..FF = -1.  clears CF\n
Run Code Online (Sandbox Code Playgroud)\n

(有趣的事实:not不影响 FLAGS,与大多数其他 ALU 指令不同,包括negneg设置与 相同的标志sub。x860历史的一个奇怪的怪癖。https ://www.felixcloutier.com/x86/not#flags-affected

\n

脚注 1:AF 也是如此,即低字节中从低半字节到高半字节的半进位标志(辅助)。您不能直接对其进行分支,x86-64 删除了aaa读取它的 BCD 指令,但它仍然存在于 RFLAGS 中,您可以使用pushf/pop rax例如读取它。

\n
\n

如果不是的话,怎么能强迫呢?

\n
\n

使用不同的指令。要获得 EFLAGS 所需的效果,最简单、最有效的方法是将其优化回sub rax, rcx. 这就是 x86 有subsbb指令的原因。如果这就是您想要的,请使用它。

\n
\n

如果你想要一个替代方案,你绝对需要避免像add rax,1最后一步这样的事情。仅当最终结果为零时才会设置 CF,从 ULONG_MAX = -1 开始回绕。

\n

x -= y在大多数情况下,这样做x += -y对 OF 是有效的。(但不是最负数y=LONG_MIN1UL<<63),否则neg rcx会溢出)。

\n

但 CF 告诉您 64 + 64 位加法或减法的 65 位完整结果。64 位求反是不够的:x += -y并不总是将 CF 设置为与x -= y预期相反的值。

\n

可能涉及neg/的东西sbb可能有用?但不,它将否定的进位视为 -0 / -1,而不是-(1<<64)

\n
# Broken attempt that fails for CF when rcx=0 at least, probably many more cases.\n# Also fails for OF for rcx=0x8000000000000000 = LONG_MIN\nmov temp, rcx        # no effect on FLAGS\nneg temp             # or NOT + INC  if you insist on avoiding sub-like operations\nadd rax, temp        # x += -y\ncmc                  # complement carry.  CF = !CF\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,我们在一个步骤中组合了 x 和 y。您add rax, 1最后会继续执行先前的 CF 结果,从而使 CF 更不可能/不可能成为您想要的结果。

\n

有符号溢出 (OF) 有一个特殊情况。对于大多数输入来说都是相同的,其中有符号算术运算对于x -= yor是相同的x += -y。但如果-y溢出后仍为负数(最负的 2 的补码没有逆数),则它会加上一个负数,而不是减去一个负数。

\n

例如,-LONG_MIN == LONG_MIN由于签名溢出。(C 表示法;有符号溢出在 ISO C 中是 UB,但在 asm 中它会换行)。

\n

CF 尝试的反例:

\n

-1 - 0不借位,所以 CF=0。\n -1 + -0=-1 + 0两者都不进位,然后 CMC 将 CF 翻转为 1

\n

但是-1( 0xff...ff) 加上任何其他数字都会进位,而-1减去任何数字则不会。

\n
\n

因此,准确模拟借用输出并不容易,而且可能也不是很有趣sub

\n

请注意,硬件 ALU 通常使用类似二进制加法器\xe2\x80\x93减法器之类的东西,以进位/借位感知方式A复用或作为~A全加器的输入来实现或使用正确的借位输出进行减法。A + BA - B

\n

应该可以在 asm 中使用stc/adc dst, inverted_src来复制类似硬件的实际功能:添加进位为 1 的逆数。不单独加1。

\n

(TODO:重写此答案的更多内容以显示使用not//而不是可能需要通过数字一路传播进位的多个操作)stcadc

\n

有关的:

\n\n