2010-06-29 25 views
13

Mit Inline-Assembler [gcc, intel, c], prüfen, ob das Übertrags-Flag nach einer Operation gesetzt ist?Prüfen, ob Übertragsmerker gesetzt ist

+0

Sie möchten dies innerhalb eines asm-Blocks testen oder den Zustand des carry-Flags an etwas im C-Code übergeben, in dem Ihre ASM inline ist? –

+0

Testen in einem Block von Asm ist ausreichend. es auszugeben sollte nicht so schwer sein. – hans

Antwort

10

Mit bedingten Sprüngen jc (springen wenn tragen) oder jnc (springen, wenn nicht tragen).

Oder Sie können den Carry-Flag speichern,

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

ah ok, kannte die jc, jnc, adc Befehle nicht. Thx – hans

+2

Warnin, GCC verwendet AT & T-Syntax. Das ist Intel Syntax ... –

+1

@Fififox, danke. Ich habe meine Antwort bearbeitet. –

14

sbb %eax,%eax speichert -1 in EAX, wenn der Carry-Flag gesetzt ist, 0, wenn es klar ist. Es ist nicht notwendig, eax auf 0 zu setzen; das Subtrahieren von eax von sich selbst tut das für dich. Diese Technik kann sehr leistungsfähig sein, da Sie das Ergebnis als Bitmaske verwenden können, um die Ergebnisse von Berechnungen zu ändern, anstatt bedingte Sprünge zu verwenden.

Sie sollten beachten, dass es nur gültig ist, das Übertrags-Flag zu testen, wenn es durch arithmetische Berechnungen INSIDE des Inline-Asm-Blocks gesetzt wurde. Sie können den Übertrag einer Berechnung, die in C-Code ausgeführt wurde, nicht testen, da es verschiedene Möglichkeiten gibt, wie der Compiler Dinge optimieren oder neu anordnen kann, die das Übertrags-Flag überlagern würden.

5

Allerdings der x86 Assembler hes gewidmet schnell ALU Flag Test Anweisungen namens SETcc, wo die CC ist ALU Flag gewünscht. So können Sie schreiben:

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 

Mit SETcc Anweisung Sie Flags wie carry testen können, Null, Zeichen, Überlauf oder Parität, einige SETcc Anweisungen erlauben sofort zwei Flags zu testen.

EDIT: Hinzugefügt einfacher Test in Delphi zum Verschwinden gebracht Zweifel an Begriff schnell

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; 

Die Schleife (1M Iterationen) weicht mit Anweisung SETC mehr als 5-mal schneller als Schleife mit adc Instruuction.

BEARBEITEN: Zusätzlicher zweiter Test, welches Testergebnis im Register AL kumulativ im Register CL gespeichert wurde, um realistischer zu sein.

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 Teil mit SETcc Anweisung ist noch schneller für etwa 20%.

+0

Haben Sie ein Zitat, um sie schnell anzurufen? Ich habe in den letzten paar Generationen nicht mit den neuesten CPUs Schritt gehalten, aber lange Zeit wurden diese als langsame Legacy-Opcodes betrachtet. –

+0

@R .. Es besteht kein Zweifel: Sie liegen falsch! Überprüfen Sie den oberen Test! –

+1

@R .. Ja die SETcc ist alte Anweisung, aber ist viel schneller als ADC oder mit Cinditional Jumps wie JC oder JNC. –

2

Die erste Funktion führt eine vorzeichenlose Addition durch und testet dann mit dem Übertrags-Flag (CF) auf Überlauf. Die Volatilen müssen bleiben. Andernfalls wird der Optimierer Anweisungen neu anordnen, was ziemlich genau zu einem falschen Ergebnis führt. Ich habe gesehen, der Optimierer die jnc in eine jae (die auch auf CF basiert) zu ändern.

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

Die nächste Funktion ist für die signierten Ints. Gleicher Gebrauch von volatilem gilt. Beachten Sie, dass vorzeichenbehaftete Integer-Mathe über OF-Flag über jno springt. Ich habe gesehen, dass der Optimierer dies in eine jnb ändert (die ebenfalls auf OF basiert).

Im großen Bild könnten Sie die Funktionen wie folgt verwenden.Im gleichen großen Bild, werden viele Leute ablehnen wahrscheinlich die zusätzliche Arbeit und Ästhetik nicht-Schönheit bis pwn'd durch einen Überlauf/wrap/Unterlauf

int r, a, b; 
... 

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

... 

Die Inline-GCC Montag ist in GCC 3.1 und höher zur Verfügung. Siehe Assembler Instructions with C Expression Operands, oder suchen Sie nach 'GCC Extended Assembly'.

Schließlich wäre das gleiche in Visual Studio wie folgt (nicht viel Unterschied in der Code-Generierung), aber Syntax ist viel einfacher, da MASM Sie zu einem C-Label springen:

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

Auf dem schlechten Der obige MASM-Code gilt nur für die x86-Assembly. Für x64 Assembly gibt es keine Inlining, so dass Sie es in Assembly (in einer separaten Datei) codieren müssen und verwenden Sie MASM64 zum Kompilieren.

+0

Sie können die Erweiterung goto verwenden, um zu einer C-Marke zu springen, indem Sie z. asm flüchtige goto ("ja% l [cabel]"::: "speicher": cabel) ;, wo cabel ist C label –

+0

Lesen unter der Antwort von @R .. scheint Ihre Funktion ungültig zu machen. 'Sie sollten sich darüber im Klaren sein, dass es nur gültig ist, das Übertrags-Flag zu testen, wenn es durch arithmetische Berechnungen innerhalb des Inline-Asm-Blocks gesetzt wurde. Sind Sie sich dessen sicher? –

+0

@DrBeco - ja, für GCC, es kommt darauf an. GCC stellt die "Folgerichtigkeit" der Anweisungen in Ihrem Block sicher, aber es kann seine eigenen Anweisungen einfügen/verschachteln. Wenn GCC-Anweisungen CC nicht ändern, ist alles in Ordnung. Der Inline-Assembler von Microsoft unterliegt nicht den Einschränkungen von GCC. – jww