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 总是以更高的精度返回。