算术恒等式和 EFLAGS
Arithmetic identities and EFLAGS
因为 −x = not(x)+1 那么意味着 a-b = a+not(b)+1,那么
sub rax, rcx
相当于
mov temp, rcx
not temp
add rax, temp
add rax, 1
哪里的 temp 寄存器被认为是易失性的?
换句话说,后者是否以完全相同的方式影响 EFLAGS?如果不是,又怎么能强制呢?
不,它们不等同。例如,如果 rax = 1
和 rcx = 3
,则 sub rax, rcx
将设置进位标志,因为您是从较小的数字中减去较大的数字。但是在您的第二个指令序列中,add rax, temp
、rax
将包含 -3
(即 0xfffffffffffffffd
),并且将 1
添加到 -3
不会引起进位。所以在你的第二个指令序列之后,进位标志将被清除。
我不知道有什么简单的方法可以准确地模拟 sub
的行为,包括它对标志的影响(除了使用 cmp
,但那是作弊,因为它实际上只是 sub
在引擎盖下)。原则上,您可以编写一长串指令,手动执行 sub
内部执行的所有相同测试(参考指令集手册中的精确描述),并在末尾使用 [=23 设置标志=] 或 popf
之类的。
这将是一项繁重的工作,特别是如果您不打算使用 cmp
,并且我不打算通过它来回答这个问题。特别是因为我也想不出任何需要这样做的理由,除非是一个毫无意义的练习。
是的,在 RAX 中得到相同的整数结果。
In other words, does the latter affect EFLAGS in the exact same way?
当然不是。 ZF、SF 和 PF 仅取决于整数结果,但 CF 和 OF1 取决于您如何到达那里 。 x86 的 CF 进位标志是减法的借位输出。 (不像一些 ISA,例如 ARM,如果没有借位,减法会设置进位标志。)
您可以在脑海中检查的简单反例:
0 - 1
和 sub
设置 CF=1。但是你的方式清除了CF。
mov temp, rcx # no effect on FLAGS
not temp # no effect on FLAGS, unlike most other x86 ALU instructions
add rax, ~1 = 0xFF..FE # 0 + anything clears CF
add rax, 1 # 0xFE + 1 = 0xFF..FF = -1. clears CF
(有趣的事实:not
不影响 FLAGS,不像大多数其他 ALU 指令,包括 neg
。neg
设置标志与 sub
来自 0
。x86 历史上的一个怪癖。https://www.felixcloutier.com/x86/not#flags-affected)
脚注1:AF也是,低字节中从低到高半字节的半进位标志(辅助)。你不能直接在它上面分支,x86-64 删除了读取它的 BCD 指令,比如 aaa
,但它仍然存在于 RFLAGS 中,你可以用 pushf
/ pop rax
读取它例如。
If not, how can it be forced to?
使用不同的指令。在 EFLAGS 上获得预期效果的最简单和最有效的方法是将其优化回 sub rax, rcx
。这就是为什么 x86 有 sub
和 sbb
指令。如果那是你想要的,使用它。
如果你想要一个替代方案,你肯定需要避免像 add rax,1
这样的事情作为最后一步。只有当最终结果为零时才会设置 CF,从 ULONG_MAX = -1.
换行
像x += -y
那样做x -= y
在大多数情况下都适用于OF。 (但不是最负数 y=LONG_MIN
(1UL<<63
),其中 neg rcx
会溢出)。
但是CF告诉你64+64位加法或减法的65位完整结果。 64 位否定是不够的:x += -y
并不总是将 CF 设置为与 x -= y
相反的设置。
可能涉及 neg
/ sbb
的内容可能有用?但是不,这将否定的结转视为 -0 / -1,而不是 -(1<<64)
.
# Broken attempt that fails for CF when rcx=0 at least, probably many more cases.
# Also fails for OF for rcx=0x8000000000000000 = LONG_MIN
mov temp, rcx # no effect on FLAGS
neg temp # or NOT + INC if you insist on avoiding sub-like operations
add rax, temp # x += -y
cmc # complement carry. CF = !CF
请注意,我们在一个步骤中合并了 x 和 y。您最后的 add rax, 1
步进了较早的 CF 结果,使得 CF 成为您想要的结果的可能性/可能性更低。
有符号溢出 (OF) 有一个极端情况。大多数输入都是一样的,其中带符号的算术运算对于 x -= y
或 x += -y
是相同的。但是如果 -y
溢出仍然是负数(the most-negative 2's complement number 没有倒数),它是在加一个负数而不是减去一个负数。
例如-LONG_MIN == LONG_MIN
因为有符号溢出。 (C 表示法;有符号溢出在 ISO C 中是 UB,但在 asm 中它换行)。
CF尝试的反例:
-1 - 0
不借,所以CF=0。
-1 + -0
= -1 + 0
也不进位,然后CMC将CF翻转为1
但是 -1
(0xff...ff
) 加上任何其他数字都会结转,而 -1
减去任何数字都不会。
所以准确地模拟 sub
的借用输出并不容易,而且可能不是很有趣。
请注意,硬件 ALU 通常使用二进制 Adder–subtractor 之类的东西,将 A
或 ~A
多路复用为 在 [=126] 中的全加器的输入=] 意识到 实现 A + B
或 A - B
并使用正确的减法借用输出。
应该可以在 asm 中使用 stc
/ adc dst, inverted_src
来复制类似硬件的实际功能:添加带进位的逆运算 -in of 1。不单独加1.
(TODO:重写此答案的更多内容以显示使用 not
/ stc
/ adc
而不是可能需要通过数字传播进位的多个操作) .
相关:
- When are the carry flags set by x86 negation (NEG) / subtraction (SUB)?
因为 −x = not(x)+1 那么意味着 a-b = a+not(b)+1,那么
sub rax, rcx
相当于
mov temp, rcx
not temp
add rax, temp
add rax, 1
哪里的 temp 寄存器被认为是易失性的?
换句话说,后者是否以完全相同的方式影响 EFLAGS?如果不是,又怎么能强制呢?
不,它们不等同。例如,如果 rax = 1
和 rcx = 3
,则 sub rax, rcx
将设置进位标志,因为您是从较小的数字中减去较大的数字。但是在您的第二个指令序列中,add rax, temp
、rax
将包含 -3
(即 0xfffffffffffffffd
),并且将 1
添加到 -3
不会引起进位。所以在你的第二个指令序列之后,进位标志将被清除。
我不知道有什么简单的方法可以准确地模拟 sub
的行为,包括它对标志的影响(除了使用 cmp
,但那是作弊,因为它实际上只是 sub
在引擎盖下)。原则上,您可以编写一长串指令,手动执行 sub
内部执行的所有相同测试(参考指令集手册中的精确描述),并在末尾使用 [=23 设置标志=] 或 popf
之类的。
这将是一项繁重的工作,特别是如果您不打算使用 cmp
,并且我不打算通过它来回答这个问题。特别是因为我也想不出任何需要这样做的理由,除非是一个毫无意义的练习。
是的,在 RAX 中得到相同的整数结果。
In other words, does the latter affect EFLAGS in the exact same way?
当然不是。 ZF、SF 和 PF 仅取决于整数结果,但 CF 和 OF1 取决于您如何到达那里 。 x86 的 CF 进位标志是减法的借位输出。 (不像一些 ISA,例如 ARM,如果没有借位,减法会设置进位标志。)
您可以在脑海中检查的简单反例:
0 - 1
和 sub
设置 CF=1。但是你的方式清除了CF。
mov temp, rcx # no effect on FLAGS
not temp # no effect on FLAGS, unlike most other x86 ALU instructions
add rax, ~1 = 0xFF..FE # 0 + anything clears CF
add rax, 1 # 0xFE + 1 = 0xFF..FF = -1. clears CF
(有趣的事实:not
不影响 FLAGS,不像大多数其他 ALU 指令,包括 neg
。neg
设置标志与 sub
来自 0
。x86 历史上的一个怪癖。https://www.felixcloutier.com/x86/not#flags-affected)
脚注1:AF也是,低字节中从低到高半字节的半进位标志(辅助)。你不能直接在它上面分支,x86-64 删除了读取它的 BCD 指令,比如 aaa
,但它仍然存在于 RFLAGS 中,你可以用 pushf
/ pop rax
读取它例如。
If not, how can it be forced to?
使用不同的指令。在 EFLAGS 上获得预期效果的最简单和最有效的方法是将其优化回 sub rax, rcx
。这就是为什么 x86 有 sub
和 sbb
指令。如果那是你想要的,使用它。
如果你想要一个替代方案,你肯定需要避免像 add rax,1
这样的事情作为最后一步。只有当最终结果为零时才会设置 CF,从 ULONG_MAX = -1.
像x += -y
那样做x -= y
在大多数情况下都适用于OF。 (但不是最负数 y=LONG_MIN
(1UL<<63
),其中 neg rcx
会溢出)。
但是CF告诉你64+64位加法或减法的65位完整结果。 64 位否定是不够的:x += -y
并不总是将 CF 设置为与 x -= y
相反的设置。
可能涉及 neg
/ sbb
的内容可能有用?但是不,这将否定的结转视为 -0 / -1,而不是 -(1<<64)
.
# Broken attempt that fails for CF when rcx=0 at least, probably many more cases.
# Also fails for OF for rcx=0x8000000000000000 = LONG_MIN
mov temp, rcx # no effect on FLAGS
neg temp # or NOT + INC if you insist on avoiding sub-like operations
add rax, temp # x += -y
cmc # complement carry. CF = !CF
请注意,我们在一个步骤中合并了 x 和 y。您最后的 add rax, 1
步进了较早的 CF 结果,使得 CF 成为您想要的结果的可能性/可能性更低。
有符号溢出 (OF) 有一个极端情况。大多数输入都是一样的,其中带符号的算术运算对于 x -= y
或 x += -y
是相同的。但是如果 -y
溢出仍然是负数(the most-negative 2's complement number 没有倒数),它是在加一个负数而不是减去一个负数。
例如-LONG_MIN == LONG_MIN
因为有符号溢出。 (C 表示法;有符号溢出在 ISO C 中是 UB,但在 asm 中它换行)。
CF尝试的反例:
-1 - 0
不借,所以CF=0。
-1 + -0
= -1 + 0
也不进位,然后CMC将CF翻转为1
但是 -1
(0xff...ff
) 加上任何其他数字都会结转,而 -1
减去任何数字都不会。
所以准确地模拟 sub
的借用输出并不容易,而且可能不是很有趣。
请注意,硬件 ALU 通常使用二进制 Adder–subtractor 之类的东西,将 A
或 ~A
多路复用为 在 [=126] 中的全加器的输入=] 意识到 实现 A + B
或 A - B
并使用正确的减法借用输出。
应该可以在 asm 中使用 stc
/ adc dst, inverted_src
来复制类似硬件的实际功能:添加带进位的逆运算 -in of 1。不单独加1.
(TODO:重写此答案的更多内容以显示使用 not
/ stc
/ adc
而不是可能需要通过数字传播进位的多个操作) .
相关:
- When are the carry flags set by x86 negation (NEG) / subtraction (SUB)?