Dato un file binario con campi little-endian a 32 bit che ho bisogno di analizzare, voglio scrivere il codice di analisi che compila correttamente indipendentemente da endianness della macchina che esegue quel codice. Attualmente io usoConversione ottimale e portatile di endian in c/C++
uint32_t fromLittleEndian(const char* data){
return uint32_t(data[3]) << (CHAR_BIT*3) |
uint32_t(data[2]) << (CHAR_BIT*2) |
uint32_t(data[1]) << CHAR_BIT |
data[0];
}
questo, tuttavia generare assembly inoptimal. Sulla mia macchina g++ -O3 -S
produce:
_Z16fromLittleEndianPKc:
.LFB4:
.cfi_startproc
movsbl 3(%rdi), %eax
sall $24, %eax
movl %eax, %edx
movsbl 2(%rdi), %eax
sall $16, %eax
orl %edx, %eax
movsbl (%rdi), %edx
orl %edx, %eax
movsbl 1(%rdi), %edx
sall $8, %edx
orl %edx, %eax
ret
.cfi_endproc
perché sta succedendo questo? Come potevo convincerlo a produrre codice ottimale quando compilato su macchine little endian:
_Z17fromLittleEndian2PKc:
.LFB5:
.cfi_startproc
movl (%rdi), %eax
ret
.cfi_endproc
che ho ottenuto dalla compilazione:
uint32_t fromLittleEndian2(const char* data){
return *reinterpret_cast<const uint32_t*>(data);
}
Dal momento che so che la mia macchina è little-endian, I sappiate che sopra l'assemblaggio è ottimale, ma fallirà se compilato su una macchina big-endian. Inoltre viola le regole di aliasing rigoroso, quindi se inline potrebbe produrre UB anche su macchine little endian. Esiste un codice valido che verrà compilato nell'assemblaggio ottimale se possibile?
Poiché mi aspetto che la mia funzione sia molto inclinata, qualsiasi tipo di rilevamento di endian runtime è fuori questione. L'unica alternativa alla scrittura di codice C/C++ ottimale consiste nell'utilizzare il rilevamento del tempo di compilazione endian e utilizzare template
s o #define
s per ricorrere al codice inefficiente se il target endian non è little-endian. Questo tuttavia sembra essere piuttosto difficile da fare portabilmente.
Non è possibile abbinare 'reinterpret_cast'. Non sta eseguendo alcun riordino dei byte. Se devi ballare l'endian byte shuffle, devi pagare la band. – user4581301
Il fatto è che se la mia piattaforma di destinazione di compilazione è little endian allora non ho bisogno di byte shuffle - anche il compilatore dovrebbe saperlo, ma produce comunque codice di shuffle byte. –
La cosa è che il compilatore non sa che stai sfogliando endian. Vede solo un mucchio di turni e or. Sarebbe un bel trucco da avere, però. Potrebbe giocare a livello makefile e compilare e collegare nella funzione corretta, ma questo ucciderà qualsiasi inlining. – user4581301