C++ 叮当声:x86 FPU调用约定
我需要支持32位平台(x86)的对象文件的动态库和静态链接:Win32、Linux32和MacOS32。传递FPU参数(float和double)时会出现问题。默认情况下,它们在SSE寄存器中传递,而不是在堆栈中传递。我并不反对SSE,但我需要通过堆栈和FPU标准地传递参数和结果 设置-mno sse选项,这将产生所需的结果。但我不想完全放弃SSE,有时我想使用内部函数和/或使用MMX/SSE优化C++ 叮当声:x86 FPU调用约定,c++,x86,clang,sse,calling-convention,C++,X86,Clang,Sse,Calling Convention,我需要支持32位平台(x86)的对象文件的动态库和静态链接:Win32、Linux32和MacOS32。传递FPU参数(float和double)时会出现问题。默认情况下,它们在SSE寄存器中传递,而不是在堆栈中传递。我并不反对SSE,但我需要通过堆栈和FPU标准地传递参数和结果 设置-mno sse选项,这将产生所需的结果。但我不想完全放弃SSE,有时我想使用内部函数和/或使用MMX/SSE优化 \uuuuuuuuuuuuuuuuuuuu属性((stdcall)) 长双精度测试(int*num
\uuuuuuuuuuuuuuuuuuuu属性((stdcall))
长双精度测试(int*num,浮点f,双精度d)
{
*num=sizeof(长双精度);
返回f*d;
}
函数的两个版本使用相同的调用约定
默认情况下,它们在SSE寄存器中传递,而不是在堆栈中传递
这不是asm输出所显示的,也不是所发生的。请注意,您的第一个函数将其dwordfloat
arg从堆栈加载到xmm0中,然后使用mulsd
和qworddouble
arg也从堆栈中加载。movss xmm0,dword ptr[ebp+12]
是一个销毁xmm0旧内容的加载;XMM0不是此函数的输入
然后,为了按照您正在使用的陈旧的32位调用约定返回x87st0
中的retval,它使用movsd
存储到堆栈和fld
x87加载
*
运算符将浮点
提升为双精度
,以匹配另一个操作数,结果是双精度
相乘,而不是长双精度
。只有返回临时的double
结果,才会从double
升级到long double
它似乎默认为gcc所称的-mfpmath=sse
(如果可用)。这通常是好的,除了x87返回值调用约定会妨碍的小函数之外。(还请注意,x87具有从float和double到long double的“免费”升级,这是fld dword
和qword
工作原理的一部分。)Clang没有检查在一个小函数中使用SSE math会花费多少开销;在这里,使用x87进行一次乘法显然会更有效
但无论如何,-mno sse
不会改变ABI;更仔细地阅读你的asm。如果是,生成的asm就不会那么糟糕了
在Windows上,如果您一直无法编写32位代码,
vectorcall
应该是一种更好的传递/返回FP变量的方法,如果可能的话:它可以使用XMM寄存器传递/返回FP变量。显然,任何一个固定设置的ABI(如现有库)都需要正确声明,以便编译器正确地调用它们/接收它们的返回值
您当前拥有的是stdcall
,堆栈上有FP args,并在st0
中返回
顺便说一句,第一个函数中的很多代码都是从对齐堆栈的叮当声到溢出/重新加载临时
double
;Windows ABI仅保证4字节堆栈对齐。这是避免缓存线拆分风险的工作量,几乎肯定不值得。尤其是当它本可以销毁其双d
堆栈arg作为暂存空间时,并且希望调用方已将其对齐。优化已启用,只需为其设置一个帧指针即可和esp
,而不会丢失旧的esp
您可以使用
返回f*(长双精度)d代码>
编译为与-mno sse
版本相同的asm
SSE2不支持80位x87类型,因此clang被迫使用fmul
。它最终完全不会与SSE发生冲突,结果就是它需要它作为返回值。两个版本的函数都使用相同的调用约定
默认情况下,它们在SSE寄存器中传递,而不是在堆栈中传递
这不是asm输出所显示的,也不是所发生的。请注意,您的第一个函数将其dwordfloat
arg从堆栈加载到xmm0中,然后使用mulsd
和qworddouble
arg也从堆栈中加载。movss xmm0,dword ptr[ebp+12]
是一个销毁xmm0旧内容的加载;XMM0不是此函数的输入
然后,为了按照您正在使用的陈旧的32位调用约定返回x87st0
中的retval,它使用movsd
存储到堆栈和fld
x87加载
*
运算符将浮点
提升为双精度
,以匹配另一个操作数,结果是双精度
相乘,而不是长双精度
。只有返回临时的double
结果,才会从double
升级到long double
它似乎默认为gcc所称的-mfpmath=sse
(如果可用)。这通常是好的,除了x87返回值调用约定会妨碍的小函数之外。(还请注意,x87具有从float和double到long double的“免费”升级,这是fld dword
和qword
工作原理的一部分。)Clang没有检查在一个小函数中使用SSE math会花费多少开销;在这里,使用x87进行一次乘法显然会更有效
但无论如何,-mno sse
不会改变ABI;更仔细地阅读你的asm。如果是,生成的asm就不会那么糟糕了
在Windows上,如果您一直无法编写32位代码,vectorcall
应该是一种更好的传递/返回FP变量的方法,如果可能的话:它可以使用XMM寄存器传递/返回FP变量。显然,任何设置为
/*-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