繁体   English   中英

检查进位标志是否设置

[英]check if carry flag is set

使用内联汇编器 [gcc, intel, c],如何检查操作后是否设置了进位标志?

sbb %eax,%eax如果设置了进位标志sbb %eax,%eax将在eax中存储-1,如果清除则为0。 无需将eax预先清除为0。 从自身中减去eax就能为您做到这一点。 该技术非常强大,因为您可以将结果用作位掩码来代替使用条件跳转来修改计算结果。

您应该意识到,只有在内置asm块内通过算术运算设置了进位标志时,才对进位标志进行测试。 您无法测试用C代码执行的计算的进位,因为编译器可以通过各种方式优化/重新排序会破坏进位标志的内容。

有条件跳跃时jc (如果进位则跳跃)或jnc (如果不进位则跳跃)。

或者您可以存储进位标志,

;; Intel syntax
mov eax, 0
adc eax, 0 ; add with carry

但是,x86汇编器使用专用的快速 ALU标志测试指令SETSET,其中需要cc的ALU标志。 所以你可以这样写:

setc    AL                           //will set AL register to 1 or clear to 0 depend on carry flag

or

setc    byte ptr [edx]               //will set memory byte on location edx depend on carry flag

or even

setc    byte ptr [CarryFlagTestByte]  //will set memory variable on location CarryFlagTestByte depend on carry flag

使用SETcc指令,您可以测试进位,零,符号,溢出或奇偶校验等标志,某些SETcc指令允许一次测试两个标志。

编辑:添加了在Delphi中进行的简单测试,以消除对术语快速的疑问

procedure TfrmTest.ButtonTestClick(Sender: TObject);
  function GetCPUTimeStamp: int64;
  asm
    rdtsc
  end;
var
 ii, i: int64;
begin
  i := GetCPUTimeStamp;
  asm
    mov   ecx, 1000000
@repeat:
    mov   al, 0
    adc   al, 0
    mov   al, 0
    adc   al, 0
    mov   al, 0
    adc   al, 0
    mov   al, 0
    adc   al, 0
    loop  @repeat
  end;
  i := GetCPUTimeStamp - i;

  ii := GetCPUTimeStamp;
  asm
    mov   ecx, 1000000
@repeat:
    setc  al
    setc  al
    setc  al
    setc  al
    loop  @repeat
  end;
  ii := GetCPUTimeStamp - ii;
  caption := IntToStr(i) + '  ' +  IntToStr(ii));
end;

使用指令setc的循环(1M迭代)比使用adc指令的循环快5倍以上。

编辑:添加了第二项测试,测试结果更实际地存储在寄存器AL的可交换存储在寄存器AL中。

procedure TfrmTestOtlContainers.Button1Click(Sender: TObject);
  function GetCPUTimeStamp: int64;
  asm
    rdtsc
  end;

var
 ii, i: int64;
begin
  i := GetCPUTimeStamp;
  asm
    xor   ecx, ecx
    mov   edx, $AAAAAAAA

    shl   edx, 1
    mov   al, 0
    adc   al, 0
    add   cl, al

    shl   edx, 1
    mov   al, 0
    adc   al, 0
    add   cl, al

    shl   edx, 1
    mov   al, 0
    adc   al, 0
    add   cl, al

    shl   edx, 1
    mov   al, 0
    adc   al, 0
    add   cl, al

    shl   edx, 1
    mov   al, 0
    adc   al, 0
    add   cl, al

    shl   edx, 1
    mov   al, 0
    adc   al, 0
    add   cl, al

    shl   edx, 1
    mov   al, 0
    adc   al, 0
    add   cl, al

    shl   edx, 1
    mov   al, 0
    adc   al, 0
    add   cl, al

  end;
  i := GetCPUTimeStamp - i;

  ii := GetCPUTimeStamp;
  asm
    xor   ecx, ecx
    mov   edx, $AAAAAAAA

    shl   edx, 1
    setc  al
    add   cl, al

    shl   edx, 1
    setc  al
    add   cl, al

    shl   edx, 1
    setc  al
    add   cl, al

    shl   edx, 1
    setc  al
    add   cl, al

    shl   edx, 1
    setc  al
    add   cl, al

    shl   edx, 1
    setc  al
    add   cl, al

    shl   edx, 1
    setc  al
    add   cl, al

    shl   edx, 1
    setc  al
    add   cl, al

  end;
  ii := GetCPUTimeStamp - ii;
  caption := IntToStr(i) + '  ' +  IntToStr(ii);
end;

使用SETcc指令的常规部分的速度仍快约20%。

第一个函数执行无符号加法,然后使用进位标志(CF)测试溢出。 挥发物必须保留。 否则,优化程序将重新排列指令,这几乎可以确保结果不正确。 我已经看到优化器将jnc更改为jae (也基于CF)。

/* Performs r = a + b, returns 1 if the result is safe (no overflow), 0 otherwise */
int add_u32(uint32_t a, uint32_t b, uint32_t* r)
{
    volatile int no_carry = 1;
    volatile uint32_t result = a + b;

    asm volatile
    (
     "jnc 1f          ;"
     "movl $0, %[xc]  ;"
     "1:              ;"
     : [xc] "=m" (no_carry)
     );

    if(r)
        *r = result;

    return no_carry;
}

下一个函数用于带符号的整数。 同样使用volatile。 请注意,带符号整数数学通过jno跳至OF标志。 我已经看到优化器将其更改为jnb (这也基于OF)。

/* Performs r = a + b, returns 1 if the result is safe (no overflow), 0 otherwise */
int add_i32(int32_t a, int32_t b, int32_t* r)
{   
    volatile int no_overflow = 1;
    volatile int32_t result = a + b;

    asm volatile
    (
     "jno 1f          ;"
     "movl $0, %[xo]  ;"
     "1:              ;"
     : [xo] "=m" (no_overflow)
     );

    if(r)
        *r = result;

    return no_overflow;
}

在大局中,您可以使用以下功能。 在同一张大图上,许多人可能会拒绝多余的工作和美学上的非美,直到被上溢/包装/下溢所淹没

int r, a, b;
...

if(!add_i32(a, b, &r))
    abort(); // Integer overflow!!!

...

内联GCC组件在GCC 3.1及更高版本中可用。 请参见带有C表达式操作数的汇编程序指令 ,或搜索“ GCC扩展汇编程序”。

最后,Visual Studio中的内容如下(代码生成没有太大区别),但是语法非常容易,因为MASM允许您跳转到C标签:

/* Performs r = a + b, returns 1 if the result is safe (no overflow), 0 otherwise */
int add_i32(__int32 a, __int32 b, __int32* r)
{   
    volatile int no_overflow = 1;
    volatile __int32 result = a + b;

    __asm
    {
        jno NO_OVERFLOW;
        mov no_overflow, 0;
    NO_OVERFLOW:
    }

    if(r)
        *r = result;

    return no_overflow;
}

不利的一面是,上面的MASM代码仅适用于x86汇编。 对于x64汇编,没有内联,因此您将不得不在汇编中对其进行编码(在单独的文件中),并使用MASM64进行编译。

10 年后,但如果正确,这可能会提供一个想法或解决方案。 我一直在努力测试环绕,直到我发现了内嵌装配。 我尝试使用各种边缘值进行测试,似乎工作正常。 程序从 cmdln 获取输入并将其转换为整数并输出十六进制和二进制值。

gcc 版本 11.2.1 $> gcc -Wall -std=c99 -O2 -o uilt uilt.c

片段:

  size_t i = 0;
  int mul = 10;
  uint128_t sum = 0;
  int int_array[48] = {0};

  // fill arr. with ea. str val in argv[1] str. converted to int vals.
  while (i < strlen(argv[1])) {
     // chk they are digit chars, if not, skip iter
     if (isdigit(argv[1][i]) == 0) {
        i++;
        continue;
     }
     int_array[i] = (argv[1][i] - 48);
     sum = int_array[i] + (sum * mul);

     /* check carry flag */
     __asm__ goto("jc %l0"
                  : /* no outputs  */
                  : /* no inputs   */
                  : /* no clobbers */
                  : carry);

     /* no carry */
     goto its_good;

     carry:
        system("clear");
        printf("\n\n\tERROR!!!\
        \n\n\t!!!!!!! uilt has ABORTED !!!!!!\
        \n\tCmdln arg exceeds 2^127 bit limit\
        \n\twhen converted from string to 127\
        \n\tbit unsigned __int128.\n\n");
        exit(1);

     its_good:
        i++;
  }

一些输出:

[jim@nitroII uiltDev]$ ./uilt 1

十二月:1

十六进制:0x0001

垃圾箱:0x0001

[jim@nitroII uiltDev]$ ./uilt 255

十二月:255

十六进制:0x00ff

垃圾箱:0x0000 1111 1111

[jim@nitroII uiltDev]$ ./uilt 18446744073709551616

十二月:18446744073709551616

十六进制:0x0001 0000 0000 0000 0000

Bin:0x0001 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

[jim@nitroII uiltDev]$ ./uilt 340282366920938463463374607431768211455

十二月:340282366920938463463374607431768211455

十六进制:0x0000 ffff ffff ffff ffff ffff ffff ffff ffff

斌:0×0000 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111

十二月:340282366920938463463374607431768211456

    ERROR!!!            

    !!!!!!! uilt has ABORTED !!!!!!            
    Cmdln arg exceeds 2^127 bit limit            
    when converted from string to 127            
    bit unsigned __int128.

暂无
暂无

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

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