2015-07-23 10 views
6

Questo è relativo a How to force const propagation through an inline function? Clang ha un assemblatore integrato; e non usa l'assemblatore del sistema (che è spesso GNU AS (GAS)). Non-Clang ha fatto presto i calcoli matematici, e tutto "ha funzionato".Forza Clang per "eseguire matematica precocemente" su valori costanti

Dico "in anticipo" perché @ n.m. obiettato a descriverlo come "la matematica eseguita dal preprocessore". Ma l'idea è che il valore sia noto al momento della compilazione e dovrebbe essere valutato in anticipo, come quando il preprocessore valuta uno #if (X % 32 == 0).

Sotto, Clang 3.6 si lamenta di aver violato un vincolo. Appare la costante non viene propagata:

$ export CXX=/usr/local/bin/clang++ 
$ $CXX --version 
clang version 3.6.0 (tags/RELEASE_360/final) 
Target: x86_64-apple-darwin12.6.0 
... 
$ make 
/usr/local/bin/clang++ -DNDEBUG -g2 -O3 -Wall -fPIC -arch i386 -arch x86_64 -pipe -Wno-tautological-compare -c integer.cpp 
In file included from integer.cpp:8: 
In file included from ./integer.h:7: 
In file included from ./secblock.h:7: 
./misc.h:941:44: error: constraint 'I' expects an integer constant expression 
     __asm__ ("rolb %1, %0" : "+mq" (x) : "I" ((unsigned char)(y%8))); 
                ^~~~~~~~~~~~~~~~~~~~ 
./misc.h:951:44: error: constraint 'I' expects an integer constant expression 
... 

Le funzioni specializzazioni modello sopra sono inlined:

template<> inline byte rotrFixed<byte>(byte x, unsigned int y) 
{ 
    // The I constraint ensures we use the immediate-8 variant of the 
    // shift amount y. However, y must be in [0, 31] inclusive. We 
    // rely on the preprocessor to propoagte the constant and perform 
    // the modular reduction so the assembler generates the instruction. 
    __asm__ ("rorb %1, %0" : "+mq" (x) : "I" ((unsigned char)(y%8))); 
    return x; 
} 

Essi vengono eseguiti con un valore const, quindi la quantità di rotazione è noto al momento della compilazione . Un tipico chiamante potrebbe essere simile:

unsigned int x1 = rotrFixed<byte>(1, 4); 
unsigned int x2 = rotrFixed<byte>(1, 32); 

Nessuno di questi [discutibili] trucchi sarebbe necessario se GCC o Clang fornito un intrinseco per eseguire le rotate in near constant time. Mi accontenterei persino di "eseguire la rotazione" poiché non hanno nemmeno quello.

Qual è il trucco necessario per far riprendere Clang eseguendo la pre-elaborazione del valore const?


lettori più attenti riconosceranno rotrFixed<byte>(1, 32) potrebbe essere un comportamento indefinito se si utilizza un tradizionale C/C++ ruotare. Quindi passiamo all'assemblaggio per evitare le limitazioni di C/C++ e godetevi l'1 speedup delle istruzioni.

Un curioso lettore potrebbe chiedersi perché lo faremmo. I crittografi chiamano le specifiche e, a volte, queste specifiche non sono in sintonia con l'hardware sottostante o con gli enti standard. Piuttosto che modificare le specifiche del cryptographer, tentiamo di fornirle testualmente per rendere più semplici i controlli.


Un bug è stato aperto per questo problema: LLVM Bug 24226 - Constant not propagated into inline assembly, results in "constraint 'I' expects an integer constant expression".

Non so cosa garantisce Clang, ma so che il compilatore e l'assemblatore integrato dichiarano di essere compatibili con l'assemblatore di GCC e GNU. E GCC e GAS forniscono la propagazione del valore costante.

+1

Continui a parlare dell'aritmetica del preprocessore, ma non ci sono costanti # define'd ovunque nel codice. –

+0

È possibile utilizzare l'assemblatore di sistema tramite '-no-integrated-as'. – Thomas

+2

Se 'y' è noto al momento di comoile, perché non renderlo un parametro template? –

risposta

2

Dal momento che ti sembra di essere fuori di fortuna cercando di forzare una valutazione costante a causa di progettare decisioni, la forma ror r/m8, cl potrebbe essere un buon compromesso:

__asm__ ("rorb %b1, %b0" : "+q,m" (x) : "c,c" (y) : "cc"); 

La sintassi vincolo alternativa multipla è quello di 'promuovere' registro utilizzare l'uso della memoria a causa di un problema con clang, coperto here. Non so se questo problema è stato risolto nelle versioni successive. gcc tende ad essere migliore nell'abbinamento dei vincoli e nell'evitare sversamenti.

Ciò richiede il caricamento di (y) nel registro rcx/ecx/cl, ma il compilatore può probabilmente nasconderlo dietro un'altra latenza. Inoltre, non ci sono problemi di intervallo per (y). rorb utilizza efficacemente (%cl % 8).Il clobber "cc" non è richiesto.


Se un'espressione è costante, sia gcc e clang possono utilizzare __builtin_constant_p:

if (__builtin_constant_p(y)) 
    __asm__("rorb %1, %b0" : "+q,m" (x) : "N,N" ((unsigned char) y) : "cc"); 
else 
    ... non-constant (y) ... 

o come accennato nella mailing list:

if (__builtin_constant_p(y)) 
{ 
    if ((y &= 0x7) != 0) 
     x = (x >> y) | (x << (8 - y)); /* gcc generates rotate. */ 
} 
+0

* "' rorb utilizza in modo efficace (% cl% 8) '" * - Sapevo che questo poteva essere sfruttato, ma non sapevo come esprimerlo ... Grazie. – jww

+1

@jww - BTW, per un immediato senza segno a 8 bit, potresti usare il vincolo '" N "', piuttosto che '" I "', e lasciare che 'ror' gestisca l'intervallo modulo 8. –

0

Se 'N' contraint per 8 bit, allora che ne dici di 16/32/64?