2010-06-29 6 views
13

Utilizzare l'assemblatore inline [gcc, intel, c], come verificare se il flag di trasporto viene impostato dopo un'operazione?controllare se il flag di trasporto è impostato

+0

Si desidera testare questo all'interno di un blocco di asm o si desidera passare lo stato del flag di riporto a qualcosa nel codice C in cui il vostro asm è in linea? Il test –

+0

all'interno di un blocco di asm è sufficiente. distribuirlo non dovrebbe essere così difficile. – hans

risposta

10

Con salti condizionali jc (jump se carry) o jnc (saltare se non trasportare).

Oppure si può memorizzare il carry flag,

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

ah ok, non conoscevo i comandi jc, jnc, adc. Thx – hans

+2

Warnin, GCC utilizza la sintassi AT & T. Questa è la sintassi Intel ... –

+1

@Fififox, grazie. Ho modificato la mia risposta. –

14

sbb %eax,%eax immagazzinerà -1 in eax se è impostato il flag di carry, 0 se è chiaro. Non è necessario pre-cancellare eax su 0; sottrarre EA da sé lo fa per te. Questa tecnica può essere molto potente in quanto è possibile utilizzare il risultato come maschera di bit per modificare i risultati dei calcoli anziché utilizzare i salti condizionali.

Si dovrebbe essere consapevoli che è valido solo per testare il flag carry se è stato impostato mediante aritmetica eseguita all'interno del blocco asm in linea. Non è possibile testare il carry di un calcolo che è stato eseguito nel codice C perché ci sono tutti i modi in cui il compilatore può ottimizzare/riordinare le cose che potrebbero contenere il flag di carry.

5

Tuttavia, l'assembler x86 ha dedicato veloce istruzioni di test ALU flag denominate SETcc dove il cc è il flag ALU desiderato. Così si può scrivere:

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 

Con SETcc di istruzioni è possibile testare le bandiere come carry, pari a zero, segno, overflow o di parità, alcuni SETcc istruzioni permettono di testare due bandiere in una sola volta.

EDIT: aggiunta semplice test fatto in Delphi per scomparire un dubbio sul termine veloce

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; 

Il loop (iterazioni 1M) wich utilizzando istruzioni SETC è più di 5 volte più veloce di loop con adc installazione.

MODIFICA: aggiunta seconda prova che il risultato del test memorizzato nel registro AL comulativo nel registro CL è più realistico.

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; 

parte Rutina con l'istruzione SETcc è ancora più veloce di circa il 20%.

+0

Hai una citazione per chiamarli velocemente? Non ho tenuto il passo con le ultime cpp delle ultime generazioni, ma per molto tempo sono state considerate opcode legacy lente. –

+0

@ R .. Non c'è dubbio: ti sbagli! Controlla il test superiore! –

+1

@R .. Sì, SETcc è vecchia istruzione, ma è molto più veloce di ADC o utilizza salti di tipo cncidico come JC o JNC. –

2

La prima funzione esegue l'aggiunta senza segno e quindi i test per l'overflow utilizzando il flag di trasporto (CF). Il volatile deve rimanere. In caso contrario, l'ottimizzatore riorganizzerà le istruzioni, il che garantisce praticamente un risultato errato. Ho visto l'ottimizzatore modificare lo in uno jae (che è anche basato su 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 funzione successiva è per gli allegati. Si applica lo stesso uso di volatile. Nota che il matematico intero con segno salta su OF flag tramite jno. Ho visto l'ottimizzatore cambiare questo in un jnb (che è anche basato su 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; 
} 

Nell'immagine grande, è possibile utilizzare le funzioni come segue.Nello stesso quadro generale, molte persone probabilmente rifiutare il lavoro supplementare ed estetico non-bellezza fino pwn'd da un overflow/involucro/underflow

int r, a, b; 
... 

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

... 

L'assemblea GCC in linea è disponibile in GCC 3.1 e superiore. Vedere Assembler Instructions with C Expression Operands o cercare 'GCC Extended Assembly'.

Infine, lo stesso in Visual Studio sarà il seguente (non molta differenza nella generazione di codice), ma la sintassi è molto più facile da quando MASM consente di passare ad un'etichetta 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; 
} 

Sulla cattiva lato, il codice MASM sopra è applicabile solo per l'assembly x86. Per l'assemblaggio di x64, non c'è alcuna inlining, quindi dovrai codificarlo in assembly (in un file separato) e usare use MASM64 per compilare.

+0

È possibile utilizzare l'estensione goto per passare a un'etichetta C utilizzando ad es. asm volatile goto ("ja% l [clabel]"::: "memoria": clabel) ;, dove clabel è l'etichetta C –

+0

Leggendo la risposta di @R .. sembra invalidare la tua funzione. 'Dovresti essere consapevole del fatto che è valido solo per testare il flag carry se è stato impostato mediante aritmetica eseguita all'interno del blocco asm in linea. Sei sicuro di questo? –

+0

@DrBeco - sì, per GCC, dipende. GCC garantirà la "consecutività" delle istruzioni nel blocco, ma potrebbe inserire/intercalare le proprie istruzioni. Se le istruzioni GCC non modificano CC, allora tutto sarà OK. L'assemblatore in linea di Microsoft non ha i limiti di GCC. – jww