我想写一个简单的代码(或算法)来设置/清除溢出标志。对于设置OF,我知道我可以使用有符号值。但我怎么能清楚呢?
答案 0 :(得分:2)
提供:
CF
-旗帜说寄存器是al
。 ({setc
仅适用于字节寄存器r / 8)
; clear OF-Flag, preserve CF
setc al
sar al, 1
注意:这很好,因为它没有部分标志更新,这可能会导致停顿。 ({sar xx, 1
写入所有标志,不像inc
/ dec
一样保留所有标志)c.f. Intel Optimization Guide, 3.5.2.6: Partial Flag Register Stalls,但请注意,现代Intel CPU根本没有部分标志停顿或标志合并的内容:读取标志just read either or both of CF or the SPAZO group as 2 separate inputs的指令。 (这就是为什么cmovbe
在Broadwell及更高版本上仍然是2 oups:它需要CF和ZF。https://uops.info/)
来源:Intel Documentation SAR p.1234
说寄存器是al
。 (适用于r / 8,r / 16,r / 32,r / 64)
; set OF-Flag, preserve CF
mov al, 0x7F
inc al
; clear OF-Flag, preserve CF
mov al, 0x0
inc al
来源:Intel Documentation INC p.551
不同的方法,如果您可以假设:
adx
的处理器(您可以使用grep adx /proc/cpuinfo
检查cpu标志)说寄存器是eax
。 (需要r64 / r32)
; clear OF-Flag, preserve CF
mov eax, 0x0
adox eax, eax
; set OF-Flag, preserve CF
mov eax, 0xFFFFFFFF
adox eax, eax
注意:请勿尝试将mov
替换为xor
(或类似名称),因为这样会清除CF
答案 1 :(得分:1)
有许多可能的解决方案。
例如, test al, al
会清除OF
标志,而不会影响注册内容。
或者,如果您不想影响其他标志,您可以直接修改*FLAGS
寄存器。例如,在32位中,这看起来像:
pushfd ; Push EFLAGS onto the stack
and dword [esp], ~0x800 ; Clear bit 11 (OF)
popfd ; Pop the modified result back into EFLAGS
修改:根据Peter Cordes'将or al, al
更改为test al, al
建议。 (效果相同,但后者因性能原因更好)
答案 2 :(得分:0)
popf
is quite slow (like one per 20 cycles on Skylake);如果你需要清除或设置OF然后理想地将它作为ALU指令的副作用,特别是你将要使用的一个有用的计算,你知道不会或将溢出。 (一个会溢出的通常很难找到,不像CF那样你总是可以只用sub
而不是add
一个常量几乎一直包围所有输入,除了非常小的范围)
如果您因某种原因需要设置/清除 OF而不影响其他条件代码,那么是,pushf
/ popf
是可行的方法。 lahf
/ sahf
不会获得OF,因为OF在EFLAGS中位于第11位,低于8位。
test al,al
(或任何相同的,同一个寄存器)清除OF和CF ,就像comparing / subtracting zero一样。其他标志根据值有用地设置。
xor eax,eax
清除EAX,清除OF / SF / CF,设置ZF / PF 。无论如何你经常需要一个归零的寄存器,所以如果你需要OF清除(例如adox
extended-precision chain的开头),那么一石二鸟并安排你的代码,这样最后的标志设置指令就是xor-调零。
在x86-64中,您还可以相信在指针+长度上使用add
并不会跨越无符号虚拟地址空间的中间,从而清除OF
。但是这个假设可能会破坏未来拥有完全64位虚拟地址的CPU,因为那时就没有hole in virtual address space around the signed-wraparound boundary,所以一个连续的数组可以跨越它。这已经发生在32位代码中,在64位内核或32位内核下运行,不使用2G:2G内核:用户拆分虚拟地址空间。
xor eax, eax
/ cmp al, -128
设置OF,只需要4个字节的代码。它可能是最便宜的方式,与sub
或其他什么不同,它不会写任何部分寄存器(或任何完整的寄存器)。它仍然使EAX归零。
0 - -128
wraps to -128
, i.e. signed OF。 8位2的补码整数只能表示来自-128..+127
的值。最负数是特殊情况,没有正确的反转。它有自己的绝对值/负值,或者更恰当的是这些函数溢出。 (或者您可以将绝对值操作视为具有有符号输入和无符号输出,因此结果为+128,即0x80。x86没有整数abs指令(准备-x
,然后测试/ cmov),但是对于SSSE3,它确实有向量整数pabsb
)
对于除-1
以外的AL中的任何已知值,有一个cmp al, imm8
将设置OF。对于0..127的任何值,cmp al, -128
换行。对于-2 ..- 128的任何值,cmp al, +127
包装并因此设置OF。对于-1
,减去127只会将您带到-128。减去-128可以达到+127。不幸的是,我不认为在寄存器中没有已知值的情况下设置OF的单指令方式。
它 不是al
,而是cmp al,imm8
的2字节特殊编码。其他8位或32位寄存器可以使用正常的3字节编码。
没有破坏任何寄存器,也没有已知的常量,这是6个字节:
push rax
xor eax,eax
cmp al, -128
pop rax
这确实破坏了其他条件代码,但它比pushf
/ popf
更快。通常你可以破坏某些东西,否则你就无法破坏堆栈。
setno al # OF=0 -> AL=1 OF=1 -> AL=0
cmp al, -127 # 1 - -127 = 128 = -128 0 - -127 = +127