32 位 cdecl 处理浮点
作为参数(float, double)
浮点数大小为 32 位,它们在堆栈中自然传递。
双打是 64 位大小,它们在堆栈上传递,遵循 Little Endian 约定 1 ,首先推送高 32 位而不是低位。
//C prototype of callee
double foo(double a, float b);
foo(3.1457, 0.241);
;Assembly call
;3.1457 is 0x40092A64C2F837B5ULL
;0.241 is 0x3e76c8b4
push DWORD 3e76c8b4h ;b, is 32 bits, nothing special here
push DWORD 0c2f837b5h ;a, is 64 bits, Higher part of 3.1457
push DWORD 40092a64h ;a, is 64 bits, Lower part of 3.1457
call foo
add esp, 0ch
;Call, using the FPU
;ST(0) = a, ST(1) = b
sub esp, 0ch
fstp QWORD PTR [esp] ;Storing a as a QWORD on the stack
fstp DWORD PTR [esp+08h] ;Storing b as a DWORD on the stack
call foo
add esp, 0ch
作为参数(长双)
长双精度是 80 位 2 宽,而在堆栈上 TBYTE 可以存储两个 32 位推送和一个 16 位推送(4 + 4 + 2 = 10),以保持堆栈对齐 4 个字节,它结束占用 12 个字节,因此使用三个 32 位推送。
尊重 Little Endian 约定,第 79-64 位先按 3 ,然后按 63-32 位,然后按 31-0 位。
//C prototype of the callee
void __attribute__((cdecl)) foo(long double a);
foo(3.1457);
;Call to foo in assembly
;3.1457 is 0x4000c9532617c1bda800
push DWORD 4000h ;Bits 79-64, as 32 bits push
push DWORD 0c9532617h ;Bits 63-32
push DWORD 0c1bda800h ;Bits 31-0
call foo
add esp, 0ch
;Call to foo, using the FPU
;ST(0) = a
sub esp, 0ch
fstp TBYTE PTR [esp] ;Store a as ten byte on the stack
call foo
add esp, 0ch
作为返回值
无论大小如何,浮点值都将返回到 ST(0)
4 中 。
//C
float one() { return 1; }
;Assembly
fld1 ;ST(0) = 1
ret
//C
double zero() { return 0; }
;Assembly
fldz ;ST(0) = 0
ret
//C
long double pi() { return PI; }
;Assembly
fldpi ;ST(0) = PI
ret
1 在较低地址处降低 DWORD。
2 被称为 TBYTE,来自 Ten Bytes。
3 使用任何延伸的全宽度推动,不使用更高的 WORD。
4 TBYE 宽,注意与整数相反,FP 总是以更高的精度返回。