2010-02-20 9 views
5

Se ho il seguente codice C++ per confrontare due interi senza segno a 128 bit, con linea amd-64 asm:In estensione asm in stile GCC, è possibile generare un valore booleano "virtualizzato", ad es. la bandiera del carry?

struct uint128_t { 
    uint64_t lo, hi; 
}; 
inline bool operator< (const uint128_t &a, const uint128_t &b) 
{ 
    uint64_t temp; 
    bool result; 
    __asm__(
     "cmpq %3, %2;" 
     "sbbq %4, %1;" 
     "setc %0;" 
     : // outputs: 
     /*0*/"=r,1,2"(result), 
     /*1*/"=r,r,r"(temp) 
     : // inputs: 
     /*2*/"r,r,r"(a.lo), 
     /*3*/"emr,emr,emr"(b.lo), 
     /*4*/"emr,emr,emr"(b.hi), 
     "1"(a.hi)); 
    return result; 
} 

Poi sarà inline molto efficiente, ma con un difetto. Il valore di ritorno viene eseguito tramite l'"interfaccia" di un registro generale con un valore di 0 o 1. Questo aggiunge due o tre istruzioni aggiuntive non necessarie e riduce l'operazione di confronto che altrimenti sarebbe completamente ottimizzata. Il codice generato sarà simile a questo:

mov r10, [r14] 
    mov r11, [r14+8] 
    cmp r10, [r15] 
    sbb r11, [r15+8] 
    setc al 
    movzx eax, al 
    test eax, eax 
    jnz is_lessthan 

Se uso "SBB% 0,% 0" con un valore di ritorno "int" invece di "SETC% 0" con un valore di ritorno "bool", c'è ancora due ulteriori istruzioni:

mov r10, [r14] 
    mov r11, [r14+8] 
    cmp r10, [r15] 
    sbb r11, [r15+8] 
    sbb eax, eax 
    test eax, eax 
    jnz is_lessthan 

Quello che voglio è questo:

mov r10, [r14] 
    mov r11, [r14+8] 
    cmp r10, [r15] 
    sbb r11, [r15+8] 
    jc  is_lessthan 

GCC in linea esteso asm è meraviglioso, in caso contrario. Ma voglio che sia buono come una funzione intrinseca, in ogni modo. Voglio essere in grado di restituire direttamente un valore booleano sotto forma dello stato di un flag o flag di CPU, senza doverlo "renderizzare" in un registro generale.

E 'possibile, o GCC (e il compilatore Intel C++, che consente anche di utilizzare questa forma di asm in linea) deve essere modificato o anche refactored per renderlo possibile?

Inoltre, mentre ci sono - c'è un altro modo in cui la mia formulazione dell'operatore di confronto potrebbe essere migliorata?

+0

A partire dal 2013, non sembra ancora possibile fare direttamente. Ma ecco un bug report del 2011 che parla della desiderabilità di una tale funzionalità: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49611. Si collega a un thread del kernel Linux del 2001 che desidera anche una cosa del genere: http: //lkml.indiana.edu/hypermail/linux/kernel/0.111,2/0256.html. –

risposta

3

Qui siamo quasi 7 anni più tardi, e sì, gcc, infine, aggiunto il supporto per "bandiere output di" (aggiunto in 6.1.0, rilasciato ~ aprile 2016). La documentazione dettagliate sono here, ma in breve, sembra che questo:

/* Test if bit 0 is set in 'value' */ 
char a; 

asm("bt $0, %1" 
    : "[email protected]" (a) 
    : "r" (value)); 

if (a) 
    blah; 

Per comprendere [email protected]: Il vincolo di uscita (che richiede =) è di tipo @cc seguita dal codice condizione da utilizzare (in questo caso c per fare riferimento alla bandiera di trasporto).

Ok, questo potrebbe non essere più un problema per il tuo caso specifico (dal momento che gcc ora supporta il confronto di tipi di dati a 128 bit direttamente), ma (al momento) 1.326 persone hanno consultato questa domanda. Apparentemente c'è un certo interesse in questa funzione.

Ora personalmente favorisco la scuola di pensiero che dice don't use inline asm at all. Ma se tu devi, sì puoi (ora) i flag di 'output'.

FWIW.

4

Non conosco un modo per farlo. Si può o non può prendere in considerazione questo un miglioramento:

inline bool operator< (const uint128_t &a, const uint128_t &b) 
{ 
    register uint64_t temp = a.hi; 
    __asm__(
     "cmpq %2, %1;" 
     "sbbq $0, %0;" 
     : // outputs: 
     /*0*/"=r"(temp) 
     : // inputs: 
     /*1*/"r"(a.lo), 
     /*2*/"mr"(b.lo), 
     "0"(temp)); 

    return temp < b.hi; 
} 

Produce qualcosa di simile:

mov rdx, [r14] 
mov rax, [r14+8] 
cmp rdx, [r15] 
sbb rax, 0 
cmp rax, [r15+8] 
jc is_lessthan 
+0

+1 Miglioramento eccellente. Questo ha un benchmark migliore (per fare un'operazione di ordinamento multi-gigabyte) rispetto a quello che ho postato nella domanda. Continuo a sperare che ci possa essere un modo per restituire direttamente la bandiera di trasporto, quindi non la contrassegnerò come "risposta accettata", ma, ben fatto. – Deadcode

Problemi correlati