程序集64位:如何返回双精度值?

时间:2016-06-09 15:40:51

标签: gcc assembly nasm

如何在Assembler中返回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;
   }

大会:

      section .bss
      x: resq 1

      section .text 

      global func

      func:

      movq qword[x],xmm0
      fld qword [x]
      fld qword [x]
      fadd
      movq xmm0,qword[x]

      ret

它应该返回20.0但是它总是10.0 我错了什么?

2 个答案:

答案 0 :(得分:2)

@Michael Petch指出,使用以下代码,整个函数可以更多更高效:

addsd xmm0, xmm0   ; Add input parameter to itself
ret                ; Done!  (return values go in xmm0)

x86-64在XMM寄存器中传递/返回double,而不是内存或x87堆栈。(适用于x86-64 System V ABI /调用约定和Windows x64 。请参阅the x86 tag wiki

中的链接

发布的代码没有评论。评论它会对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在执行最终fadd之前忘记将movq的结果存储到内存中,因此该函数只返回其输入,如
double foo(double x) { return x; }但是在x87堆栈上留下了垃圾。

@Michael Petch继续注意到原始代码在浮点堆栈上留下了大量的“碎片” - 没有尝试用各种pop版本的指令清除它({{ 1}},或fstp代替faddp)。这为下一个浮点函数留下了更少的空间 - 直到最后导致浮点堆栈溢出,导致意外的NaN!

答案 1 :(得分:2)

您无法混淆FPU和XMM计算。 当你在FPU上计算某些东西时,你必须将它(如@Elderbug所说)存储在内存中然后你必须将它加载到XMM寄存器以在Win OS上的x64上的64位proc上返回它。 在64位系统上使用FPU仍然有一个优势,因为FPU的内部精度可以是80位(如果使用正确的FPU控制字:位8,9 float32(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架构软件开发人员手册: http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html 但是,如果您运行的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。见:https://future2048.blogspot.com