【问题标题】:x86 assembly: ADD/SUB/INC/DEC without overflow and branchingx86 程序集:无溢出和分支的 ADD/SUB/INC/DEC
【发布时间】:2015-07-11 09:06:04
【问题描述】:

在 x86 ASM 中,是否可以在不进行分支的加减运算时禁止溢出?例如,当从 0x01 中减去 0x02 时,它将设置为 0x00 而不是 0xFF。

我预计这可能是不可能的,所以我也有兴趣回答这个问题的更受限制的形式,其中只添加/减去 0x01。我有一个这样的想法(OF 是溢出标志):

dec eax
add eax,OF

我不知道其他架构,但是对于 i386,我找不到操作码,因为显然标志不能解释为整数并用于算术运算。我找到了一个可行的解决方案,但仅适用于未使用较高字节时的最低字节:

dec ax
sub al,ah
xor ah,ah

有没有更好的方法来做到这一点,也许也适用于更一般的情况?

【问题讨论】:

    标签: assembly integer-overflow


    【解决方案1】:

    在递增 1 的情况下(使用add #1,而不是inc)你可以在之后只使用sbc #0 来实现饱和。同样减 1:使用sub #1,后跟adc #0

    或者考虑使用 SSE,它支持在单个指令中进行饱和整数算术运算。

    【讨论】:

    • "在递增 1 的情况下..." 只要不使用 INC 完成递增,因为 INC 不会影响携带旗帜。
    【解决方案2】:

    您可以在较新的处理器上使用cmov (conditional MOV) 系列指令来避免在许多情况下出现分支,包括您描述的示例。如果标志值设置得当,这些指令的行为就像普通的mov,否则什么都不做。

    http://www.jaist.ac.jp/iscenter-new/mpc/altix/altixdata/opt/intel/vtune/doc/users_guide/mergedProjects/analyzer_ec/mergedProjects/reference_olh/mergedProjects/instructions/instruct32_hh/vc35.htm

    但是:

    我有一个想法是这样的(OF 是溢出标志)

    这不是你想要的溢出标志;这表明该值溢出了使用 2 的补码表示的边界(因此对于 16 位字值,如果它变得小于 -32768 或大于 32767)。您似乎正在使用无符号数字(并希望钳制为 0),所以您想要的是 carry 标志。

    例如,如果存在进位(如果结果从 0 包装到 0xFF),您可以使用 CMOVC 加载新值。

    sub ax, 1
    xor bx, bx
    cmovc ax, bx
    

    这在减去任何值时都有效。然而,事实证明,如果你只是想要一个减量,有一个更简单的方法。您可以减去 1,然后使用执行此操作的指令添加 0 + 进位:

    sub ax, 1
    adc ax, 0
    

    请注意,您不能使用 dec 指令代替 sub,因为 dec 不会影响进位标志。

    如果要另辟蹊径(加法),则使用sbb 代替adc(当然还有add 代替sub)。对于一般情况,您可以执行以下操作:

    add ax, 1
    mov bx, 0FFFFh
    cmovc ax, bx
    

    【讨论】:

    • 我不认为只用三个指令就可以解决一般情况。很好的答案,谢谢。
    猜你喜欢
    • 2011-04-24
    • 1970-01-01
    • 1970-01-01
    • 2012-11-03
    • 2011-08-24
    • 2011-05-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多