tags:

views:

104

answers:

3

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

+3  A: 

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

Or you can store the carry flag,

;; Intel syntax
mov eax, 0
adc eax, 0 ; add with carry
Nick D
ah ok, didn't know the jc,jnc,adc commands. Thx
hans
Fififox
@Fififox, thanks. I edited my answer.
Nick D
+4  A: 

sbb %eax,%eax will 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.

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.

R..
+1  A: 

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

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 SETcc instruction you can test flags like carry, zero, sign, overflow or parity, some SETcc instructions allow to test two flags at once.

EDIT: Added simple test made in Delphi to disappear a doubt about term 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 setc is more than 5 times faster than loop with adc instriuction.

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

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%.

GJ
Do you have a citation for calling them fast? I haven't kept up with the latest cpus for the past few generations, but for a long time, these were considered slow legacy opcodes.
R..
@R.. There is no doubt: you are wrong! Check upper test!
GJ
@R.. Yes the SETcc is old instruction, but is much faster than ADC or using cinditional jumps like JC or JNC.
GJ
The above tests are meaningless. (1), the results are not even used so any latency will be masked. And (2), the non-SETcc version is using mov/adc rather than sbb which is something like 6x larger in bytes and double the opcode count. If you want to benchmark this you should try both versions with code that actually makes use of the result for a computation.
R..
@R.. Added second test! Rutine part with SETcc instruction is still faster for about 20%.
GJ
You're still using mov/adc instead of sbb. Try it with sbb then subtracting the result (which will be 0 or -1, and thus will add 0 or 1 when you substract it) from the "accumulator" (CL). Also make sure you're using the shortest-form instructions - I don't remember off-hand but I seem to remember sbb potentially being shorter depending on which register or which register size you use. And tighter loops may run faster due to cache issues.
R..
OK, in that case is faster. But normaly we test carry flag to return boolean result in AL when we exit function. For that purpose was SETxx instruction made! Remember instruction ADC is older than SETxx.
GJ