C语言 检查进位标志是否设置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3139772/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 05:46:00  来源:igfitidea点击:

check if carry flag is set

cgccx86intel

提问by hans

Using inline assembler [gcc, intel, c], how to check if the carry flag is set after an operation?

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

采纳答案by Nick Dandoulakis

With conditional jumps jc(jump if carry) or jnc(jump if not carry).

使用条件跳转jc(如果进位则跳转)或jnc(如果不进位则跳转)。

Or you can store the carry flag,

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

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

回答by R.. GitHub STOP HELPING ICE

sbb %eax,%eaxwill store -1 in eax if the carry flag is set, 0 if it is clear. There's no need to pre-clear eax to 0; subtracting eax from itself does that for you. This technique can be very powerful since you can use the result as a bitmask to modify the results of computations in place of using conditional jumps.

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

You should be aware that it is only valid to test the carry flag if it was set by arithmetic performed INSIDE the inline asm block. You can't test carry of a computation that was performed in C code because there are all sorts of ways the compiler could optimize/reorder things that would clobber the carry flag.

您应该知道,如果进位标志是由内联汇编块内部执行的算术设置的,则只有测试进位标志才有效。您无法测试在 C 代码中执行的计算的进位,因为编译器可以通过多种方式优化/重新排序会破坏进位标志的东西。

回答by GJ.

However the x86 assembler hes dedicated fastALU flag test instructions named SETcc where the cc is desired ALU flag. So you can write:

然而,x86 汇编器使用名为 SETcc 的专用快速ALU 标志测试指令,其中 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

With SETccinstruction you can test flags like carry, zero, sign, overflow or parity, some SETccinstructions allow to test two flags at once.

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

EDIT:Added simple test made in Delphi to disappear a doubt about term fast

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

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;

The loop (1M iterations) wich using instruction setcis more than 5 times faster than loop with adcinstriuction.

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

EDIT: Added second test which test result stored in register AL comulative in register CL to be more realistic case.

编辑:添加了第二个测试,该测试结果存储在寄存器 AL 中的寄存器 CL 中,以便更真实。

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;

Rutine part with SETcc instruction is still faster for about 20%.

带有 SETcc 指令的 Rutine 部分仍然快了大约 20%。

回答by jww

The first function performs unsigned addition and then tests for overflow using the carry flag (CF). The volatile's must remain. Otherwise the optimizer will rearrange instructions, which pretty much ensures an incorrect result. I've seen the optimizer change the jncto a jae(which is also based on CF).

第一个函数执行无符号加法,然后使用进位标志 (CF) 测试溢出。挥发性的必须保留。否则优化器将重新排列指令,这几乎确保了不正确的结果。我已经看到优化器将 the 更改jnc为 a 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 
/* 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 
int r, a, b;
...

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

...
, %[xo] ;" "1: ;" : [xo] "=m" (no_overflow) ); if(r) *r = result; return no_overflow; }
, %[xc] ;" "1: ;" : [xc] "=m" (no_carry) ); if(r) *r = result; return no_carry; }

The next function is for the signed ints. Same use of volatile applies. Note that signed integer math jumps on OF flag via jno. I've seen the optimizer change this to a jnb(which is also based on OF).

下一个函数用于带符号的整数。volatile 的用法相同。请注意,有符号整数数学通过jno. 我已经看到优化器将其更改为 a jnb(也基于 OF)。

/* 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;
}

In the big picture, you might use the functions as follows. In the same big picture, many folks will probably reject the extra work and aesthetic non-beauty until pwn'd by an overflow/wrap/underflow

在大局中,您可能会使用以下函数。在同样的大局中,许多人可能会拒绝额外的工作和美学上的非美,直到被溢出/包裹/下溢

##代码##

The inline GCC assembly is available in GCC 3.1 and above. See Assembler Instructions with C Expression Operands, or search for 'GCC Extended Assembly'.

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

Finally, the same in Visual Studio would be as follows (not much difference in code generation), but syntax is much easier since MASM allows you to jump to a C label:

最后,Visual Studio 中的情况如下(代码生成没有太大区别),但语法要简单得多,因为 MASM 允许您跳转到 C 标签:

##代码##

On the bad side, the above MASM code is only applicable for x86 assembly. For x64 assembly, there is no inlining so you will have to code it up in assembly (in a separate file) and use use MASM64 to compile.

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