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
risposta
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
ah ok, non conoscevo i comandi jc, jnc, adc. Thx – hans
Warnin, GCC utilizza la sintassi AT & T. Questa è la sintassi Intel ... –
@Fififox, grazie. Ho modificato la mia risposta. –
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.
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%.
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. –
@ R .. Non c'è dubbio: ti sbagli! Controlla il test superiore! –
@R .. Sì, SETcc è vecchia istruzione, ma è molto più veloce di ADC o utilizza salti di tipo cncidico come JC o JNC. –
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.
È 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 –
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? –
@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
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 –
all'interno di un blocco di asm è sufficiente. distribuirlo non dovrebbe essere così difficile. – hans