简体   繁体   English

算术恒等式和 EFLAGS

[英]Arithmetic identities and EFLAGS

Since −x = not(x)+1 which then implies ab = a+not(b)+1, would then因为 −x = not(x)+1 意味着 ab = a+not(b)+1,所以

sub rax, rcx

be equivalent to相当于

mov temp, rcx
not temp
add rax, temp
add rax, 1

where temp is some register considered to be volatile?某些寄存器在哪里 temp 被认为是易失性的?

In other words, does the latter affect EFLAGS in the exact same way?换句话说,后者是否以完全相同的方式影响 EFLAGS? If not, how can it be forced to?如果不是,怎么能被迫?

No, they're not equivalent.不,它们不相等。 For instance if rax = 1 and rcx = 3 , then sub rax, rcx will set the carry flag, because you are subtracting a larger number from a smaller one.例如,如果rax = 1rcx = 3 ,则sub rax, rcx将设置进位标志,因为您正在从较小的数字中减去较大的数字。 But in your second sequence of instructions, following add rax, temp , rax will contain -3 (ie 0xfffffffffffffffd ), and adding 1 to -3 does not cause a carry.但是在您的第二个指令序列中,在add rax, temp之后, rax将包含-3 (即0xfffffffffffffffd ),并且将1添加到-3不会导致进位。 So after your second sequence of instructions, the carry flag would be cleared.所以在你的第二个指令序列之后,进位标志将被清除。

I do not know of any simple way to exactly emulate the behavior of sub including its effect on flags (other than by using cmp , but that's cheating because it's really just sub under the hood).我不知道有任何简单的方法可以精确地模拟sub的行为,包括它对标志的影响(除了使用cmp ,但那是作弊,因为它实际上只是sub在引擎盖下)。 In principle, you could write a long sequence of instructions that manually did all the same tests that sub does internally (referring to its precise description in the instruction set manual), and sets the flags at the end using sahf or popf of the like.原则上,您可以编写一长串指令,手动执行sub内部执行的所有相同测试(参考指令集手册中的精确描述),并在末尾使用sahfpopf之类的设置标志。

This would be a lot of work, especially if you are not going to use cmp , and I am not going to go through it for this answer.这将是很多工作,特别是如果您不打算使用cmp ,并且我不会通过它 go 来回答这个问题。 Especially because I also can't think of any reason why one would need to do it, except as a fairly pointless exercise.尤其是因为我也想不出任何理由需要这样做,除了作为一项相当无意义的练习。

Yes, that gets the same integer result in RAX.是的,在 RAX 中得到相同的 integer 结果。

In other words, does the latter affect EFLAGS in the exact same way?换句话说,后者是否以完全相同的方式影响 EFLAGS?

Of course not.当然不是。 ZF, SF, and PF only depend on the integer result, but CF and OF 1 depend on how you get there . ZF、SF 和 PF 仅取决于 integer 结果,但CF 和 OF 1取决于您如何到达那里 x86's CF carry flag is a borrow output from subtraction. x86 的 CF 进位标志是从减法中借用 output。 (Unlike some ISAs such as ARM, where subtraction sets the carry flag if there was no borrow.) (与某些 ISA 不同,例如 ARM,如果没有借位,减法会设置进位标志。)

Trivial counterexample you could check in your head:您可以在脑海中检查一个简单的反例:
0 - 1 with sub sets CF=1. 0 - 1sub集 CF=1。 But your way clears CF.但是您的方式清除了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

(Fun fact: not doesn't affect FLAGS, unlike most other ALU instructions including neg . neg sets flags the same as sub from 0 . A strange quirk of x86 history. https://www.felixcloutier.com/x86/not#flags-affected ) (有趣的事实: not不会影响 FLAGS,不像大多数其他 ALU 指令,包括negneg设置标志与sub0相同。x86 历史的一个奇怪怪癖。https://www.felix#cloutier.com/x86/not标志影响

Footnote 1: so does AF, the half-carry flag (auxiliary) from the low to high nibble in the low byte.脚注 1:AF 也是如此,它是低字节中从低半字节到高半字节的半进位标志(辅助)。 You can't branch on it directly, and x86-64 removed the BCD instructions like aaa that read it, but it's still there in RFLAGS where you can read it with pushf / pop rax for example.您不能直接对其进行分支,并且 x86-64 删除了读取它的像aaa这样的 BCD 指令,但它仍然存在于 RFLAGS 中,您可以使用pushf / pop rax读取它。

If not, how can it be forced to?如果不是,怎么能被迫?

Use different instructions.使用不同的指令。 The easiest and most efficient way to get the desired effect on EFLAGS would be to optimize it back to sub rax, rcx .在 EFLAGS 上获得所需效果的最简单和最有效的方法是将其优化回sub rax, rcx That's why x86 has sub and sbb instructions.这就是为什么 x86 有subsbb指令的原因。 If that's what you want, use it.如果那是您想要的,请使用它。


If you want an alternative, you definitely need to avoid something like add rax,1 as the last step.如果你想要一个替代方案,你肯定需要避免像add rax,1作为最后一步。 That would set CF only if the final result is zero, wrapping from ULONG_MAX = -1.仅当最终结果为零时才会设置 CF,从 ULONG_MAX = -1 开始。

Doing x -= y as x += -y works for OF in most cases.在大多数情况下,将x -= y作为x += -y适用于 OF。 (But not the most-negative number y=LONG_MIN ( 1UL<<63 ), where neg rcx would overflow). (但不是最负数y=LONG_MIN ( 1UL<<63 ),其中neg rcx会溢出)。

But CF tells you about the 65-bit full result of 64 + 64-bit addition or subtraction.但是 CF 告诉你 64 + 64 位加法或减法的 65 位完整结果。 64-bit negation isn't sufficient: x += -y doesn't always set CF opposite of what x -= y would. 64 位否定是不够的: x += -y并不总是将 CF 设置为与x -= y的相反。

Possibly something involving neg / sbb could be useful?可能涉及neg / sbb的东西可能有用吗? But no, that treats carry-out from negation as -0 / -1, not -(1<<64) .但是不,这将否定的执行视为 -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

Notice that we combine x and y in a single step.请注意,我们在一个步骤中组合了 x 和 y。 Your add rax, 1 at the end steps on the earlier CF result, making it even less likely / possible for CF to be what you want.您在较早的 CF 结果的末尾add rax, 1 ,使 CF 成为您想要的结果的可能性甚至更低。

Signed-overflow (OF) has a corner case.有符号溢出 (OF) 有一个极端情况。 It would be the same for most inputs, where the signed arithmetic operation is the same for x -= y or x += -y .大多数输入都是相同的,其中带符号的算术运算对于x -= yx += -y是相同的。 But if -y overflows to still be negative ([the most-negative 2's complement number][1] has no inverse), it's adding a negative instead of subtracting a negative.但是如果-y溢出仍然是负数([最负的 2 的补数][1] 没有倒数),它是加一个负数而不是减去一个负数。

eg -LONG_MIN == LONG_MIN because of signed overflow.例如-LONG_MIN == LONG_MIN因为有符号溢出。 (C notation; signed overflow is UB in ISO C, but in asm it wraps). (C 表示法;有符号溢出是 ISO C 中的 UB,但在 asm 中它会换行)。

Counterexample for this attempt for CF: CF 尝试的反例:

-1 - 0 doesn't borrow, so CF=0. -1 - 0不借,所以 CF=0。 -1 + -0 = -1 + 0 doesn't carry either, and then CMC will flip CF to 1 -1 + -0 = -1 + 0也不进位,然后 CMC 会将 CF 翻转为 1

But -1 ( 0xff...ff ) plus any other number does carry-out, while -1 minus any number doesn't.但是-1 ( 0xff...ff ) 加上任何其他数字会执行,而-1减去任何数字不会。


So it's not easy, and probably not very interesting to emulate the borrow output of sub accurately.所以要准确模拟sub的借用output并不容易,而且可能不是很有趣。

Note that hardware ALUs often use something like a binary [Adder–subtractor][2] that muxes A or ~A as an input to full-adders in a carry/borrow aware way to implement A + B or A - B with a correct borrow output for subtraction.请注意,硬件 ALU 经常使用二进制 [加法器-减法器][2] 之类的东西,它以进位/借位感知方式A~A多路复用作为全加器的输入,以正确实现A + BA - B借用 output 做减法。

[1]: https://en.wikipedia.org/wiki/Two%27s_complement#:~:text=The%20two's%20complement%20of%20the%20most%20negative%20number%20representable%20(eg,%C2%A7%20Most%20negative%20number%20below. [2]: https://en.wikipedia.org/wiki/Adder%E2%80%93subtractor [1]: https://en.wikipedia.org/wiki/Two%27s_complement#:~:text=The%20two's%20complement%20of%20the%20most%20negative%20number%20representable%20(例如,%C2% A7%20Most%20negative%20number%20below. [2]: https://en.wikipedia.org/wiki/Adder%E2%80%93subtractor

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM