【问题标题】:x86 Assembly: INC and DEC instruction and overflow flagx86 汇编:INC 和 DEC 指令和溢出标志
【发布时间】:2011-04-24 22:36:41
【问题描述】:

在 x86 汇编中,当对有符号整数的add 或sub 操作溢出时设置溢出标志,并且当对无符号整数的操作溢出时设置进位标志。

但是,当涉及到inc 和dec 指令时,情况似乎有些不同。根据这个website,inc指令根本不影响进位标志。

但我找不到任何关于 inc 和 dec 如何影响溢出标志的信息。

当发生整数溢出时,inc 或 dec 是否设置溢出标志?有符号整数和无符号整数的这种行为是否相同?

==============================编辑 ========== ====================

好的,所以基本上这里的共识是 INC 和 DEC 在设置标志方面应该与 ADD 和 SUB 行为相同,但进位标志除外。这也是英特尔手册中所说的。

问题是,当涉及到无符号整数时,我实际上无法在实践中重现这种行为。

考虑以下汇编代码(使用 GCC 内联汇编更容易打印出结果。)

int8_t ovf = 0;

__asm__
(
    "movb $-128, %%bh;"
    "decb %%bh;"
    "seto %b0;"
    : "=g"(ovf)
    :
    : "%bh"
);

printf("Overflow flag: %d\n", ovf);

这里我们递减一个有符号的 8 位值 -128。由于 -128 是可能的最小值,溢出是不可避免的。正如预期的那样,打印出来的是:Overflow flag: 1

但是当我们对 unsigned 值做同样的事情时,行为并不像我预期的那样:

int8_t ovf = 0;

__asm__
(
    "movb $255, %%bh;"
    "incb %%bh;"
    "seto %b0;"
    : "=g"(ovf)
    :
    : "%bh"
);

printf("Overflow flag: %d\n", ovf);

这里我增加了一个无符号的 8 位值 255。由于 255 是可能的最大值,因此溢出是不可避免的。但是,打印出来的是:Overflow flag: 0。

嗯?在这种情况下为什么不设置溢出标志?

【问题讨论】:

标签: c assembly x86 x86-64 inline-assembly


【解决方案1】:

CPU/ALU 只能处理无符号二进制数,然后它使用 OF、CF、AF、SF、ZF 等,让您决定是否将其用作有符号数(OF),无符号数 (CF) 或 BCD 数 (AF)。


关于您的问题,请记住将二进制数本身视为无符号数。

**此外,溢出和 OF 需要 3 个数字:输入数字、在算术中使用的第二个数字和结果数字。

仅当第一个和第二个数字的符号位(最高有效位)具有相同的值并且结果具有不同的符号时,才会激活溢出。 如,加 2 个负数得到一个正数,或加 2 个正数得到一个负数:

if( (Sign_Num1==Sign_Num2) && (Sign_Result!=Sign_Num1) ) OF=1;
else OF=0;

对于您的第一个问题,您使用-128 作为第一个数字。第二个数字是隐含的-1,由 DEC 指令使用。所以我们真的有二进制数0x80 和0xFF。它们都将符号位设置为 1。结果是 0x7F,这是一个符号位设置为 0 的数字。我们得到了 2 个具有相同符号的初始数字,以及具有不同符号的结果,因此我们表示溢出。 -128-1 导致 127,因此设置了溢出标志以指示错误的签名结果。


对于第二个问题,您使用255 作为第一个数字。第二个数字是隐含的1,由 INC 指令使用。所以我们真的有二进制数0xFF 和0x01。它们都有不同的符号位,因此不可能发生溢出(只有在基本上添加相同符号的 2 个数字时才有可能溢出,但永远不可能用 2 个不同符号的数字溢出,因为它们永远不会导致超出可能的签名值)。 结果是0x00,它没有设置溢出标志,因为255+1,或者更准确地说,-1+1 给出了 0,这对于有符号算术显然是正确的。

请记住,要设置溢出标志,被加/减的2个数字需要具有相同值的符号位,然后结果必须具有与它们不同的值的符号位。

【讨论】:

    【解决方案2】:

    处理器所做的是为这些指令的结果(add、adc、dec、inc、sbb、sub)为有符号和无符号情况设置适当的标志,即每个操作都有两个不同的标志结果。另一种方法是有两组指令,其中一组设置有符号相关标志,另一组设置无符号相关标志。如果发布编译器在操作中使用无符号变量,它将测试进位和零(jc、jnc、jb、jbe 等),如果有符号,它将测试溢出、符号和零(jo、jno、jg、jng、jl、jle 等)。

    【讨论】:

      【解决方案3】:

      正如许多其他答案所指出的,INC 和 DEC 不会影响 CF,而 ADD 和 SUB 会。

      然而,尚未说明的是,这可能会产生性能差异。并不是说您通常会为此感到困扰,除非您试图从例程中优化地狱,但本质上不设置 CF 意味着 INC/DEC 只写入标志寄存器的一部分,这可能导致部分标志寄存器停止,请参阅Intel 64 and IA-32 Architectures Optimization Reference Manual 或Agner Fog's optimisation manuals。

      【讨论】:

      • 如果后面的指令读取 CF,它可以导致部分标志停止,例如在adc 循环中。见Problems with ADC/SBB and INC/DEC in tight loops on some CPUs。但与 Pentium 4 不同,这通常不是问题,除非您专门利用 inc/dec 让 CF 不被读取 CF 修改。 INC instruction vs ADD 1: Does it matter?(通常没有)。 Partial-flag stalls 只是 Intel P6 家族的东西; Sandybridge 做了相当有效的标志 merging。
      • 另请注意,最近的英特尔(Skylake 和可能更早的版本)根本没有标志合并。 CF 仍然单独重命名,但像 cmova 这样需要 CF 和 ZF 的指令只有 2 个单独的标志输入。 (这就是为什么cmova 花费 2 uop,因为它有 4 个输入,但大多数其他 cmov 条件只需要 CF 或 SPAZO 组中的一些标志,而不是两者)参见@BeeOnRope(Travis Downs')线程agner.org/optimize/blog/read.php?i=960跨度>
      【解决方案4】:

      尝试更改您的测试以传递数字而不是对其进行硬编码,然后有一个循环尝试所有 256 个数字,以找到影响标志的数字。或者让 asm 执行循环并在它碰到标志时退出,或者当它环绕到它开始的数字时(以 0x00、0x7f、0x80 或 0xFF 以外的数字开始)。

      编辑

      .globl 公司 公司: 移动 $33, %eax 最佳: 公司百分比 完成了 跳转顶部 完毕: ret .globl dec 十二月: 移动 $33, %eax 顶部: 十进制% 乔多克斯 跳转顶部 多德克斯: ret

      Inc 从 0x7F 变为 0x80 时溢出。 dec从0x80到0x7F时溢出,我怀疑问题出在您使用内联汇编程序的方式上。

      【讨论】:

      • 有符号和无符号整数通常是编程语言的一种表现形式,不会反映在硬件中。没有签名公司和未签名公司,它只是公司的一种类型,通用的。这就是该指令存在 OF 的原因。一些处理器通常不是那么好,并且不提供检测无符号和有符号结果的方法。我很惊讶地看到这个功能。
      • 嗯,有 mul 与 imul 和 div 与 idiv 与 x86 指令集
      • 好...通常加减法、inc、dec 等不是基于符号的,因为它没有意义。
      【解决方案5】:

      当操作将导致符号更改时设置溢出标志。您的代码非常接近。我能够使用以下 (VC++) 代码设置 OF 标志:

      char ovf = 0;
      
      _asm {
          mov bh, 127
          inc bh
          seto ovf
      }
      cout << "ovf: " << int(ovf) << endl;
      

      当 BH 增加时,MSB 从 0 变为 1,从而设置 OF。

      这也设置了OF:

      char ovf = 0;
      
      _asm {
          mov bh, 128
          dec bh
          seto ovf
      }
      cout << "ovf: " << int(ovf) << endl;
      

      请记住,处理器不区分有符号数和无符号数。当您使用 2 的补码算术时,您可以使用一组指令来处理两者。如果要测试无符号溢出,则需要使用进位标志。由于 INC/DEC 不影响进位标志,因此您需要针对这种情况使用 ADD/SUB。

      【讨论】:

        【解决方案6】:

        Intel® 64 and IA-32 Architectures Software Developer's Manuals

        查看相应的手册Instruction Set Reference, A-M。每条指令都被精确记录。

        这里是受影响标志的 INC 部分:

        CF 标志不受影响。 OF、SZ、ZF、AZ、PF标志根据结果设置。

        【讨论】:

        • 是的,这也是我的理解。然而,在实践中,这似乎并没有真正发生。有关演示这一点的汇编代码,请参阅我编辑的问题。
        【解决方案7】:

        除了进位标志 inc 设置标志的方式与添加操作数 1 的方式相同。

        inc 不影响进位标志这一事实非常重要。

        http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-2.html#HEADING2-117

        【讨论】:

        • 除了add 还会在发生整数溢出时设置溢出标志,但据我所知,inc 不会这样做。有关演示这一点的汇编代码,请参阅我编辑的问题。
        • @Channel72 你对标志的理解有误。 CF 表示 MSB(最高有效字节)的执行,如 1111 1111 + 1 = 1 | 0000 0000 CF 这里。 CF 表示无效(溢出)无符号操作。 (因为是 256,不适合byte)。但这是非常有效的有符号运算(-1 + 1 = 0)。 OF 是当进位到 MSB 时,如 0111 1111 + 1 = 1000 0000 这意味着有效的无符号和无效的有符号操作(127 + 8 = -128 哎呀,这是有符号的情况下溢出,因为范围是 -128; 127)
        猜你喜欢
        • 1970-01-01
        • 2012-11-06
        • 2016-06-17
        • 1970-01-01
        • 2013-10-18
        • 1970-01-01
        • 2023-03-30
        • 1970-01-01
        相关资源
        最近更新 更多