ABI System V x86-64 сообщает, что счетчик аргументов регистра FP передается в AL, и что старшие байты RAX могут содержать мусор . (То же, что и любое узкое целое число или аргумент FP. Но см. Также в этом вопросе и ответе о clang, предполагающем расширение нуля или знака для узких целочисленных аргументов до 32 бит. Это относится только к собственно аргументам функции, а не al
.)
Используйте movzx eax, al
для расширения нуля AL в RAX. (Запись EAX неявно расширяется в RAX, в отличие от записи 8- или 16-битного регистра.)
Если есть еще один целочисленный регистр, который вы можете замкнуть, используйте movzx ecx,al
, чтобы сработало исключение mov на процессорах Intel, обеспечивающее нулевую задержку и не требующий порт выполнения. Сбой mov-elission от Intel, когда src и dst являются частями одного и того же регистра.
Также нет нужды использовать 64-битный источник для преобразования в FP. cvtsi2sd xmm0, eax
на один байт (без префикса REX), и после расширения нуля в EAX вы знаете, что интерпретация дополнения EAX и RAX со знаком 2, используемая cvtsi2sd
, идентична.
На вашем Mac clang / LLVM решил оставить мусор в старших байтах RAX . Оптимизатор LLVM менее осторожен во избежании ложных зависимостей, чем gcc, поэтому он иногда будет писать частичные регистры. (Иногда даже когда это не сохраняет размер кода, но в этом случае это делает).
Исходя из ваших результатов, мы можем заключить, что вы использовали clang на Mac и gcc или ICC на Ubuntu.
Проще посмотреть на asm, генерируемый компилятором, из упрощенного примера (new
и std::cout::operator<<
приводят к большому количеству кода).
extern "C" double foo(int, ...);
int main() {
foo(123, 1.0, 2.0);
}
Компилируется в этот ассм в Исследователь компилятора Godbolt , с gcc и clang -O3:
### clang7.0 -O3
.section .rodata
.LCPI0_0:
.quad 4607182418800017408 # double 1
.LCPI0_1:
.quad 4611686018427387904 # double 2
.text
main: # @main
push rax # align the stack by 16 before a call
movsd xmm0, qword ptr [rip + .LCPI0_0] # xmm0 = mem[0],zero
movsd xmm1, qword ptr [rip + .LCPI0_1] # xmm1 = mem[0],zero
mov edi, 123
mov al, 2 # leave the rest of RAX unmodified
call foo
xor eax, eax # return 0
pop rcx
ret
GCC выдает в основном то же самое, но с
## gcc8.2 -O3
...
mov eax, 2 # AL = RAX = 2 FP args in regs
mov edi, 123
call foo
...
mov eax,2
вместо mov al,2
позволяет избежать ложной зависимости от старого значения RAX для процессоров, которые не переименовывают AL отдельно от остальной части RAX . (Это делают только семейство Intel P6 и Sandybridge, а не IvyBridge и более поздние версии. И ни какие-либо процессоры AMD, Pentium 4 или Silvermont.)
См. Как именно работают частичные регистры на Haswell / Skylake? Написание AL, похоже, ложно зависит от RAX, и AH не согласуется , чтобы больше узнать о том, как IvB и более поздние версии отличаются от Core2 / Nehalem.