Clang:x86 FPU调用约定
Clang: x86 FPU calling convention
我需要支持32位平台(x86(的对象文件的动态库和静态链接:Win32、Linux32和MacOS32。传递FPU参数(浮点和双精度(时会出现问题。默认情况下,它们在SSE寄存器中传递,而不是在堆栈中传递。我并不反对SSE,但我需要通过堆栈和FPU标准地传递参数和结果。
我尝试(godbolt(设置-mno-sse选项,这产生了所需的结果。但我不想完全放弃SSE,有时我会使用内部函数和/或MMX/SSE优化。
__attribute__((stdcall))
long double test(int* num, float f, double d)
{
*num = sizeof(long double);
return f * d;
}
/*-target i386-windows-gnu -c -O3*/
push ebp
mov ebp, esp
and esp, -8
sub esp, 8
movss xmm0, dword ptr [ebp + 12] # xmm0 = mem[0],zero,zero,zero
mov eax, dword ptr [ebp + 8]
cvtss2sd xmm0, xmm0
mov dword ptr [eax], 12
mulsd xmm0, qword ptr [ebp + 16]
movsd qword ptr [esp], xmm0
fld qword ptr [esp]
mov esp, ebp
pop ebp
ret 16
/*-target i386-windows-gnu -mno-sse -c -O3*/
mov eax, dword ptr [esp + 4]
mov dword ptr [eax], 12
fld dword ptr [esp + 8]
fmul qword ptr [esp + 12]
ret 16
函数的两个版本都使用相同的调用约定
默认情况下,它们在SSE寄存器中传递,而不是在堆栈中传递。
这不是asm输出显示的内容,也不是发生的内容。请注意,您的第一个函数将其dwordfloat
arg从堆栈加载到xmm0中,然后将mulsd
与qworddouble
arg一起使用movss xmm0, dword ptr [ebp + 12]
是一个破坏XMM0旧内容的负载;XMM0不是此函数的输入。
然后,为了按照您正在使用的顽固的旧32位调用约定在x87st0
中返回retval,它使用movsd
存储到堆栈和fld
x87加载。
*
运算符将float
提升为double
以匹配其他操作数,从而产生double
乘法,而不是long double
。在返回临时double
结果之前,不会发生从double
到long double
的升级。
看起来clang默认为gcc调用的-mfpmath=sse
(如果可用(。这通常是好的,除了x87返回值调用约定会阻碍的小函数。(还要注意,x87从浮点和双精度"免费"升级为长双精度,这是fld dword
和qword
工作方式的一部分。(Clang并没有检查在一个小函数中使用SSE数学会花费多少开销;在这里,使用x87进行一次乘法显然会更有效。
但无论如何,-mno-sse
并没有改变ABI;仔细阅读你的asm如果是的话,生成的asm会吸得更少!
在Windows上,如果你一直在编写32位代码,vectorcall
应该是一种更好的传递/返回FP变量的方法:它可以使用XMM寄存器来传递/返回。显然,任何固定的ABI(如现有库(都需要正确声明,以便编译器正确地调用它们/接收它们的返回值。
您当前拥有的是stdcall
,堆栈上有FP args,并在st0
中返回
BTW,第一个函数中的许多代码都是从clang对齐堆栈到溢出/重新加载临时double
;Windows ABI仅保证4字节堆栈对齐。为了避免缓存线分割的风险,这是一项艰巨的工作。这几乎肯定是不值得的。尤其是当它本可以将其double d
堆栈arg作为暂存空间销毁,并希望调用方已经将其对齐时。优化已启用,只需设置一个帧指针即可and esp
而不会丢失旧的ESP。
您可以使用return f * (long double)d;
它编译为与-mno-sse
版本相同的asm。https://godbolt.org/z/LK0s_5
SSE2不支持80位x87类型,因此clang被迫使用fmul
。它最终根本没有扰乱SSE,结果是它需要它作为返回值。
- 为什么在 x64 中忽略__stdcall调用约定?
- 窗口调用约定
- 我应该提到纯虚函数中的调用约定吗?
- 如何使用gcc指定stdcall调用约定
- Clang:x86 FPU调用约定
- 该标准是否说明了例外和不同调用约定的共存
- Clang++的InterlockedExchange的32位调用约定错误,但MSVC可以
- std::sort & comp - 调用约定?
- 如何为类 /块 /范围选择C 调用约定
- DLL 的"good"调用约定是什么?
- 结构中重载运算符的调用约定
- 'ShowSUM':__declspec(dllexport)不能应用于具有__clrcall调用约定的函数
- x86-64 调用约定中的返回值
- 内联功能和调用约定
- 正在直接显示筛选器中调用约定
- 何时使用`__fastcall`调用约定
- 为C++结构定义显式析构函数如何影响调用约定
- 由于使用 MFC 时"__cdecl"和"__thiscall"调用约定不匹配而导致的链接器错误?
- Microsoft Visual C++是否未使用带有浮点的C/C++调用约定
- 在Visual Studio中调用约定