Учитывая двоичный файл с 32-разрядными малоконечными полями, которые мне нужно проанализировать, я хочу написать код разбора, который правильно компилируется независимо от конечности машины, выполняющей этот код. В настоящее время я использую
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];
}
это, однако, порождает неоптимальную сборку. На моей машине g++ -O3 -S
выдается:
_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
почему это происходит? Как я могу убедить его в создании оптимального кода при компиляции на небольших конечных машинах:
_Z17fromLittleEndian2PKc:
.LFB5:
.cfi_startproc
movl (%rdi), %eax
ret
.cfi_endproc
который я получил путем компиляции:
uint32_t fromLittleEndian2(const char* data){
return *reinterpret_cast<const uint32_t*>(data);
}
Так как я знаю, что моя машина малозначительная, я знаю, что над сборкой оптимальна, но она не сработает, если скомпилирована на машине большого конца. Он также нарушает правила строгого сглаживания, поэтому, если он встроен, он может создавать UB даже на маленьких конечных машинах. Есть ли допустимый код, который будет скомпилирован для оптимальной сборки , если возможно?
Так как я ожидаю, что моя функция будет встроена в много, любой тип определения endian endtime не может быть и речи. Единственной альтернативой написанию оптимального кода C/С++ является использование определения времени компиляции и использование template
или #define
, чтобы вернуться к неэффективному коду, если конечный endian не является малоподвижным. Это, однако, представляется довольно трудным сделать портативно.