2010-06-29 5 views
13

Utilisation de l'assembleur en ligne [gcc, intel, c], comment vérifier si l'indicateur de portage est défini après une opération?vérifier si l'indicateur de portage est défini

+0

Vous voulez tester cela dans un bloc d'asm ou vous voulez passer l'état de l'indicateur de report à quelque chose dans le code C dans lequel votre asm est en ligne? –

+0

tests dans un bloc de ASM est suffisant. le faire sortir ne devrait pas être si difficile. – hans

Répondre

10

Avec des sauts conditionnels jc (sauter si porter) ou jnc (sauter si ne pas porter).

Ou vous pouvez stocker le drapeau de transport,

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

ah ok, ne connaissait pas les commandes jc, jnc, adc. Thx – hans

+2

Warnin, GCC utilise la syntaxe AT & T. C'est la syntaxe Intel ... –

+1

@Fififox, merci. J'ai édité ma réponse. –

14

sbb %eax,%eax stockera -1 dans eax si le drapeau de transport est fixé, 0 s'il est clair. Il n'est pas nécessaire de pré-effacer eax à 0; soustraire EAX de lui-même fait cela pour vous. Cette technique peut être très puissante puisque vous pouvez utiliser le résultat comme un masque de bits pour modifier les résultats des calculs au lieu d'utiliser des sauts conditionnels.

Vous devez savoir qu'il est seulement valide de tester le drapeau de report s'il a été défini par l'arithmétique effectuée à l'intérieur du bloc asm en ligne. Vous ne pouvez pas tester le report d'un calcul qui a été effectué en code C, car il existe toutes sortes de façons pour le compilateur d'optimiser/de réorganiser les éléments susceptibles d'écraser le drapeau de report.

5

Cependant l'assembleur x86 est dédié rapide instructions d'essai d'indicateur ALU nommé SETcc où le cc est l'indicateur ALU désiré. Vous pouvez donc écrire:

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 

Avec setcc instruction, vous pouvez tester les drapeaux comme carry, zéro, signe, dépassement ou parité, quelques instructions setcc permettent de tester deux drapeaux à la fois.

EDIT: test simple Ajouté fait à Delphes Disparaître un doute sur terme rapide

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; 

La boucle (itérations 1M) wich en utilisant l'instruction SETC est plus de 5 fois plus rapide que boucle avec adc instriuction.

EDIT: Ajout du second test dont le résultat du test est enregistré dans le registre AL comulatif dans le registre CL pour être plus réaliste.

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; 

partie rutine avec l'instruction setcc est encore plus rapide pour environ 20%.

+0

Avez-vous une citation pour les appeler rapidement? Je n'ai pas suivi les derniers processeurs depuis quelques générations, mais pendant longtemps, ils ont été considérés comme des opcodes lents. –

+0

@R .. Il n'y a aucun doute: vous avez tort! Vérifiez le test supérieur! –

+1

@R .. Oui, SETcc est une ancienne instruction, mais elle est beaucoup plus rapide que l'ADC ou utilise des sauts cinditional comme JC ou JNC. –

2

La première fonction effectue une addition non signée, puis teste le dépassement en utilisant le drapeau de report (CF). Les volatiles doivent rester. Sinon, l'optimiseur réorganisera les instructions, ce qui garantit un résultat incorrect. J'ai vu l'optimiseur changer le jnc en jae (qui est également basé sur 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; 
} 

La fonction suivante est pour les entrées signées. La même utilisation de volatile s'applique. Notez que maths entiers signés saute sur le drapeau OF via jno. J'ai vu l'optimiseur changer cela en un jnb (qui est également basé sur 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; 
} 

Dans l'ensemble, vous pouvez utiliser les fonctions suivantes.Dans le même tableau d'ensemble, beaucoup de gens rejetteront probablement le travail supplémentaire et non-beauté esthétique jusqu'à pwn'd par un trop-plein/enveloppement/underflow

int r, a, b; 
... 

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

... 

L'ensemble de GCC en ligne est disponible dans GCC 3.1 et au-dessus. Voir Assembler Instructions with C Expression Operands ou recherchez 'GCC Extended Assembly'.

Enfin, même dans Visual Studio serait la suivante (pas beaucoup de différence dans la génération de code), mais la syntaxe est beaucoup plus facile depuis MASM vous permet de passer directement à une étiquette 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; 
} 

Sur le mauvais Côté, le code MASM ci-dessus est uniquement applicable pour l'assemblage x86. Pour l'assemblage x64, il n'y a pas d'inline, vous devrez donc le coder en assembly (dans un fichier séparé) et utiliser MASM64 pour compiler.

+0

Vous pouvez utiliser l'extension goto pour accéder à une étiquette C en utilisant par exemple. asm volatil goto ("ja% l [clabel]"::: "mémoire": clabel) ;, où clabel est libellé C –

+0

La lecture ci-dessous la réponse de @R .. semble invalider votre fonction. 'Vous devez savoir qu'il est seulement valide de tester le drapeau de report s'il a été défini par l'arithmétique effectuée à l'intérieur du bloc asm en ligne. Êtes-vous sûr de cela? –

+0

@DrBeco - oui, pour GCC, cela dépend. GCC assurera la "consécution" des instructions dans votre bloc, mais il pourra insérer/entrelacer ses propres instructions. Si les instructions de GCC ne modifient pas CC, tout ira bien. L'assembleur en ligne de Microsoft ne souffre pas des limitations de GCC. – jww

Questions connexes