Sto provando a produrre codice (attualmente usando clang ++ - 3.8) che aggiunge due numeri composti da più parole macchina. Per semplificare le cose per il momento sto solo aggiungendo numeri a 128 bit, ma mi piacerebbe essere in grado di generalizzare questo.Produrre bene aggiungere con codice di trasporto da clang
Prima alcune typedefs:
typedef unsigned long long unsigned_word;
typedef __uint128_t unsigned_128;
E un tipo "risultato":
struct Result
{
unsigned_word lo;
unsigned_word hi;
};
La prima funzione, f
prende due coppie di parole senza segno e restituisce un risultato, da come intermedio passo mettendo entrambe queste parole a 64 bit in una parola a 128 bit prima di aggiungerle, in questo modo:
Result f (unsigned_word lo1, unsigned_word hi1, unsigned_word lo2, unsigned_word hi2)
{
Result x;
unsigned_128 n1 = lo1 + (static_cast<unsigned_128>(hi1) << 64);
unsigned_128 n2 = lo2 + (static_cast<unsigned_128>(hi2) << 64);
unsigned_128 r1 = n1 + n2;
x.lo = r1 & ((static_cast<unsigned_128>(1) << 64) - 1);
x.hi = r1 >> 64;
return x;
}
Questo in realtà viene inline piuttosto esattamente in questo modo:
movq 8(%rsp), %rsi
movq (%rsp), %rbx
addq 24(%rsp), %rsi
adcq 16(%rsp), %rbx
Ora, invece ho scritto una funzione più semplice utilizzando il clangore primitive multi-precisione, come di seguito:
static Result g (unsigned_word lo1, unsigned_word hi1, unsigned_word lo2, unsigned_word hi2)
{
Result x;
unsigned_word carryout;
x.lo = __builtin_addcll(lo1, lo2, 0, &carryout);
x.hi = __builtin_addcll(hi1, hi2, carryout, &x.carry);
return x;
}
Questo produce il seguente assemblea:
movq 24(%rsp), %rsi
movq (%rsp), %rbx
addq 16(%rsp), %rbx
addq 8(%rsp), %rsi
adcq $0, %rbx
In questo caso, c'è un extra. Invece di fare un ordinario add
sulle lo-words, quindi un adc
sulle hi-words, è solo add
s le parole-chiave, quindi add
s le lo-words, quindi fa un adc
sull'hi-word di nuovo con un argomento di zero.
Questo può non sembrare troppo male, ma quando si tenta questo con parole più grandi (diciamo 192bit, 256bit) a presto ottiene un pasticcio di or
s e altre istruzioni riguardanti la porta a monte della catena, invece di una semplice catena di add
, adc
, adc
, ... adc
.
I primitivi multi-precisione sembrano fare un lavoro terribile esattamente a quello che sono destinati a fare.
Quindi quello che sto cercando è un codice che potrei generalizzare a qualsiasi lunghezza (non c'è bisogno di farlo, quanto basta per capire come farlo), che clang produce addizioni in un modo con è efficiente come ciò che fa è costruito a 128 bit (che sfortunatamente non riesco facilmente a generalizzare). Presumo che questo dovrebbe solo una catena di adc
s, ma sono benvenuto in argomenti e codice che dovrebbe essere qualcos'altro.
Questo è uno di quei casi angolari che i compilatori attualmente succhiano. Se ti interessa davvero tanto, dovrai utilizzare l'assemblaggio in linea. GMP fa molto di questo materiale di propagazione del carico ed è tutto in assemblea. – Mysticial
Ho già fatto una domanda di taglia su questo. http://stackoverflow.com/questions/29029572/multi-word-addition-using-the-carry-flag Sospetto che troverai la stessa risposta (o la sua mancanza) che ho fatto. –