Обе версии вашей функции , использующие одно и то же соглашение о вызовах
По умолчанию они передаются в регистры SSE, а не в стек.
Это не то, что показывает ваш вывод asm, а не то, что происходит. Обратите внимание, что ваша первая функция загружает свой dword float
arg из стека в xmm0, затем используя mulsd
с аргументом qword double
также из стека. movss xmm0, dword ptr [ebp + 12]
- это нагрузка, которая уничтожаетстарое содержимое XMM0;XMM0 не является входом для этой функции.
Затем, чтобы вернуть ответ в x87 st0
в соответствии с используемым вами твердым старым 32-разрядным соглашением о вызовах, он использует хранилище movsd
дляstack и fld
x87 load.
Оператор *
повышает float
до double
, чтобы соответствовать другому операнду, что приводит к умножению double
, а не long double
. Повышение с double
до long double
не произойдет до тех пор, пока не будет возвращен этот временный double
результат.
Похоже, что значения по умолчанию для clang будут называться -mfpmath=sse
, если доступно ,Обычно это хорошо, за исключением небольших функций, где мешает соглашение о вызове возвращаемого значения x87. (Также обратите внимание, что x87 имеет «бесплатное» продвижение от float и double до long double, как часть того, как работают fld dword
и qword
.) Clang не проверяет, сколько стоит затрат на использование математики SSEв небольшой функции;здесь, очевидно, было бы более эффективно использовать x87 для одного умножения.
Но в любом случае -mno-sse
не меняет ABI;читайте ваш ассм более внимательно. Если это так, сгенерированный ассм будет меньше отстой!
В Windows, если вы застряли при создании 32-битного кода вообще, vectorcall
должно бытьлучший способ передавать / возвращать переменные FP, когда это возможно: он может использовать регистры XMM для передачи / возврата. Очевидно, что любые ABI, которые установлены в камне (как для существующих библиотек), должны быть правильно объявлены, чтобы компилятор вызывал их / правильно возвращал значения из них.
То, что у вас в данный момент есть , является stdcall
с аргументами FP в стеке и возвращенными в st0
.
Кстати, большая часть кода в вашей первой функции от clang выравнивает стек до разлива /перезагрузите временную double
;Windows ABI гарантирует только 4-байтовое выравнивание стека. Это объем работы, чтобы избежать риска разделения строки кэша, почти наверняка того не стоит. Особенно, когда он мог просто уничтожить свой аргумент стека double d
как пустое место, и надеялся, что вызывающий объект выровнял это. Оптимизация включена, она просто устанавливает указатель кадра, чтобы он мог and esp
без потери старого ESP.
Вы можете использовать return f * (long double)d;
, который компилируется в идентичный ассемблер дляверсия -mno-sse
. https://godbolt.org/z/LK0s_5
SSE2 не поддерживает 80-битные типы x87, поэтому clang вынужден использовать fmul
. В итоге он вообще не возится с SSE, и в результате получается то, что ему нужно для возвращаемого значения.