Intel x86 Assembly& Microarchitecture 32位cdecl —处理浮点

示例

作为参数(浮动,双精度)

浮点数为32位,它们自然地在堆栈上传递。
双精度数为64位,它们遵循Little Endian约定1在栈中传递,首先推入高32位,然后推低32位。

//被调用者的C原型
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宽,而在堆栈上可以用两个32位压入和一个16位压入存储TBYTE(对于4 + 4 + 2 = 10),以使堆栈对准4个字节,它结束占用12个字节,因此使用了三个32位压入。
遵循Little Endian约定,首先将位79-64推入3,然后将位63-32推入,然后是位31-0。

//被调用者的C原型
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。

3使用带有任何扩展名的全角推送,不使用较高的WORD。

4 TBYE宽,请注意,与整数相反,FP总是以要求的精度返回。