x86 左移算术溢出控制

Loo*_*Per 3 x86 assembly integer-overflow bit-shift signed-overflow

您知道有什么方法可以有效地检查 x86 左移算术上是否发生溢出/下溢吗?

Nat*_*man 5

一个好的选择是在左移之后立即执行算术移位,看看是否得到相同的数字:

\n
    mov ebx, eax      ; keep a copy of the original\n    sal eax, cl         ;  TODO: also copy the shifted EAX somewhere\n    sar eax, cl\n    cmp ebx, eax      ;  (x << n) >> n == x\n    jne overflow\n ; result: not stored anywhere by this snippet.\n
Run Code Online (Sandbox Code Playgroud)\n

BMI2 3操作数移位可以节省一些mov指令:

\n
; input: x in EDI, shift count n in ESI\n    shlx  eax, edi, esi       ; there is no salx, it\'s the same operation\n    sarx  edx, eax, esi       ; (x << n) >> n\n    cmp   edx, eax\n    jne   overflow\n   ; else EAX = x<<n  without overflow\n
Run Code Online (Sandbox Code Playgroud)\n
\n

(这部分答案是基于对规范的误读。)

\n

如果您担心班次计数太大而导致换行,只需在移位之前检查班次计数即可。如果移位计数大于位数,则会发生溢出。(8 位和 16 位移位除外,如果需要,您可以移出所有位;对于 64 位以下的所有操作数大小,计数将被屏蔽为 5 位。)

\n

通常您会为此检查标志。但是,您不能真正依赖它们SHL(或者SAL这是相同的指令)。查看软件开发人员手册或HTML 摘录

\n
\n

受影响的标志

\n

CF 标志包含从目标操作数移出的最后一位的值;对于计数大于或等于目标操作数的大小(以位为单位)的 SHL 和 SHR 指令,它是未定义的。OF 标志仅对 1 位移位产生影响(请参阅上面的 \xe2\x80\x9cDescription\xe2\x80\x9d);否则,它是未定义的。根据结果​​设置 SF、ZF 和 PF 标志。如果计数为 0,则标志不受影响。对于非零计数,AF 标志未定义。

\n
\n

最好的方法是在移位之前确保字节操作的移位计数 <8,字操作的移位计数 <16,双字的移位计数 <32,四字的移位计数 <64。

\n
\n

使用 FLAGS 检测结果溢出:

\n

如果移位计数不大于目标操作数,则可以检查 CF 标志以查看移出的最后一位。如果一次执行一位移位,则可以在每次移位后测试 CF,看看是否有 1 在任何点移出,这表明发生了溢出。

\n

但这会检测到无符号溢出。要检测有符号-1溢出,当(0x...ff) 变为-2(0x...fe)时,这不是问题。但关键是符号位没有改变。根据实际有符号溢出对 OF 组进行 1 位移位,其中OF \xe2\x86\x90 MSB(DEST) XOR CF;

\n

这仅适用于一次移位 1 位;x86 甚至没有为 1 以外的移位计数定义 OF 值,不幸的是没有记录在此过程中是否发生任何符号翻转。

\n