Gcc 汇编64位:如何返回双精度值?
如何在汇编程序中返回64位值? 我试过这个: C-方案:Gcc 汇编64位:如何返回双精度值?,gcc,assembly,nasm,Gcc,Assembly,Nasm,如何在汇编程序中返回64位值? 我试过这个: C-方案: #include <stdio.h> double result=0; double a = 10; extern double func(double a); int main() { result = func(a); printf("result: %f\n", result); return 0; } 它应该返回20.0,但始终是10.0 我做错了什么 @M
#include <stdio.h>
double result=0;
double a = 10;
extern double func(double a);
int main() {
result = func(a);
printf("result: %f\n", result);
return 0;
}
它应该返回20.0,但始终是10.0
我做错了什么 @Michael Petch指出,使用以下代码,整个函数的效率可能会更高:
addsd xmm0, xmm0 ; Add input parameter to itself
ret ; Done! (return values go in xmm0)
x86-64在XMM寄存器中传递/返回double
,而不是内存或x87堆栈。(适用于x86-64系统V ABI/调用约定和Windows x64。请参阅中的链接)
发布的代码没有注释。评论它会对OP有所帮助,所以
;; Buggy original version with comments
movq qword[x],xmm0 ; Store current value in memory [Why?]
fld qword [x] ; Load current value from memory [Why??]
fld qword [x] ; Load current value from memory again
fadd ; Add top two stack items
movq xmm0,qword[x] ; reload original value from memory, unmodified
@ElderBug指出,OP在执行最终的movq
之前忘记将fadd
的结果存储到内存中,因此此函数只返回其输入,如double foo(double x){return x;}
但在x87堆栈上留下垃圾
@Michael Petch接着指出,原始代码在浮点堆栈上留下了大量的“碎片”——没有人试图用各种
pop
版本的指令来清理它(fstp
,或faddp
,而不是fadd
)。这就减少了下一个浮点函数的空间——直到最后导致浮点堆栈溢出,导致意外的NaN @Michael Petch指出,使用以下代码,整个函数的效率可能会更高:
addsd xmm0, xmm0 ; Add input parameter to itself
ret ; Done! (return values go in xmm0)
x86-64在XMM寄存器中传递/返回double
,而不是内存或x87堆栈。(适用于x86-64系统V ABI/调用约定和Windows x64。请参阅中的链接)
发布的代码没有注释。评论它会对OP有所帮助,所以
;; Buggy original version with comments
movq qword[x],xmm0 ; Store current value in memory [Why?]
fld qword [x] ; Load current value from memory [Why??]
fld qword [x] ; Load current value from memory again
fadd ; Add top two stack items
movq xmm0,qword[x] ; reload original value from memory, unmodified
@ElderBug指出,OP在执行最终的movq
之前忘记将fadd
的结果存储到内存中,因此此函数只返回其输入,如double foo(double x){return x;}
但在x87堆栈上留下垃圾
@Michael Petch接着指出,原始代码在浮点堆栈上留下了大量的“碎片”——没有人试图用各种
pop
版本的指令来清理它(fstp
,或faddp
,而不是fadd
)。这就减少了下一个浮点函数的空间——直到最后导致浮点堆栈溢出,导致意外的NaN 不能混淆FPU和XMM计算。
当您在FPU上计算某些内容时,必须将其存储在内存中(正如@Elderbug所说),然后必须将其加载到XMM寄存器,以在Win OS上x64上的64位进程上返回。
在64位系统上使用FPU仍有优势,因为FPU的内部精度可以是80位(如果使用正确的FPU控制字:位8,9浮点32(24位尾数)=00b)
双浮点(53位尾数)=10b
扩展精度(64位尾数)=11b
如果要使用FPU:
fld QWORD PTR x ; laod var to FPU: into ST(0) (MASM Syntax)
fadd ST(0), ST(0) ; this adds [x]+[x]
fstp QWORD PTR x ; store result back in var
movsd xmm0, QWORD PTR x
注意:对于movsd,始终需要SSE2。(在SSE1机器上会发生GP故障!请参阅英特尔64和IA-32体系结构软件开发人员手册:
但是,如果您运行Windows8/8.1/10,这对您来说绝对不是问题,因为操作系统本身会根据系统要求请求SSE2
编辑:SSE2是x86-x64中的基线(如Peter Cordes在评论中所述),因此您可以始终在64位上使用它
如果要将SIMD与XMM寄存器一起使用:
movsd xmm0, QWORD PTR x
addsd xmm0, xmm0 ; this instruction also requires SSE2
; ok, retun xmm0
还要注意,您也不能混淆XMM和MMX寄存器!
(MOVQ2DQ和MOVDQ2Q指令可以将它们从一个转换为另一个,但其他指令不能)
如果您的函数使用参数并且应该在Windows操作系统上运行,则需要确保函数prolog/epilog有效。请参阅:您不能混淆FPU和XMM计算。 当您在FPU上计算某些内容时,必须将其存储在内存中(正如@Elderbug所说),然后必须将其加载到XMM寄存器,以在Win OS上x64上的64位进程上返回。 在64位系统上使用FPU仍有优势,因为FPU的内部精度可以是80位(如果使用正确的FPU控制字:位8,9浮点32(24位尾数)=00b) 双浮点(53位尾数)=10b 扩展精度(64位尾数)=11b 如果要使用FPU:
fld QWORD PTR x ; laod var to FPU: into ST(0) (MASM Syntax)
fadd ST(0), ST(0) ; this adds [x]+[x]
fstp QWORD PTR x ; store result back in var
movsd xmm0, QWORD PTR x
注意:对于movsd,始终需要SSE2。(在SSE1机器上会发生GP故障!请参阅英特尔64和IA-32体系结构软件开发人员手册:
但是,如果您运行Windows8/8.1/10,这对您来说绝对不是问题,因为操作系统本身会根据系统要求请求SSE2
编辑:SSE2是x86-x64中的基线(如Peter Cordes在评论中所述),因此您可以始终在64位上使用它
如果要将SIMD与XMM寄存器一起使用:
movsd xmm0, QWORD PTR x
addsd xmm0, xmm0 ; this instruction also requires SSE2
; ok, retun xmm0
还要注意,您也不能混淆XMM和MMX寄存器!
(MOVQ2DQ和MOVDQ2Q指令可以将它们从一个转换为另一个,但其他指令不能)
如果您的函数使用参数并且应该在Windows操作系统上运行,则需要确保函数prolog/epilog有效。请参阅:您从不存储结果。顺便说一句,使用SSE而不是x87可能更简单。Elderbug是正确的。
fadd
添加了st(0)和st(1)将值存储在st(0)中
。不使用堆栈顶部的值更新[x]中的值。也不在返回之前从FPU堆栈中弹出额外的值(如果调用此函数5次,将导致FPU堆栈溢出问题)。正如Elder指出的,除非给定的任务要求您这样做,否则您可以使用SSE而不是FPU。代码也可以简化,但我很好奇您是为32位还是64位代码编译的?我假设从您得到的结果来看,您的可执行文件是64位的。如果您可以使用SSE,您可以使用这样的指令section.text;全局函数;函数