程序集x86中的Sqrt

时间:2016-03-02 12:53:22

标签: assembly compiler-errors x86 mismatch

我在网上找到了一些建议。

我有类似的问题,但没有一个建议有帮助(或者我没有正确地弄清楚如何根据我的程序实现它们)。

代码作为asm(...)插入到C程序中。

使用-masm=intel进行编译后,使用:

asm ("FLD EBX \n" "FSQRT \n" "FST EBX \n").

我收到编译错误:

"错误:' fld'"的操作数类型不匹配 " ...' fst'"。

不匹配

EBX在这些命令之前保留一些整数正值。

那么获取ebx = sqrt(ebx)的正确方法是什么?

1 个答案:

答案 0 :(得分:4)

您应该在现代代码中使用SSE / SSE2作为sqrt,而不是x87。您可以使用一条指令直接将gp寄存器中的整数转换为xmm寄存器中的double。

cvtsi2sd  xmm0, ebx
sqrtsd    xmm0, xmm0     ; sd means scalar double, as opposed to SIMD packed double
cvttsd2si  ebx, xmm0     ; convert with truncation (C-style cast)

; cvtsd2si  ecx, xmm0    ; rounded to nearest integer (or whatever the current rounding mode is)

这也适用于64位整数(rbx),但请注意double只能精确表示最多约2 ^ 53(尾数大小)的整数。如果要检查整数是否为正方形,可以使用float sqrt然后对整数结果进行试验乘法。 ((a*a) == b

有关指南,教程和手册的链接,请参阅

请注意,将此代码插入C程序的中间是完全错误的方法。 GNU C inline asm是执行asm的最难的方法,因为你必须真正理解所有内容以使约束正确。让他们错误可能导致其他周围的代码以微妙和难以调试的方式破解,而不仅仅是你在内联错误中所做的事情。有关此内容的更多详细信息,请参阅x86标记wiki。

如果你想要int a = sqrt((int)b),那么在你的代码中写下它,让编译器为你生成这三条指令。通过各种方式阅读和理解编译器输出,但不要盲目地在asm("")的中间插入一个序列。

e.g:

#include <math.h>
int isqrt(int a) { return sqrt(a); }

compiles to(gcc 5.3不带-ffast-math):

    pxor    xmm0, xmm0      # D.2569
    cvtsi2sd        xmm0, edi       # D.2569, a
    sqrtsd  xmm1, xmm0  # tmp92, D.2569
    ucomisd xmm1, xmm1        # tmp92, tmp92
    jp      .L7 #,
    cvttsd2si       eax, xmm1     # D.2570, tmp92
    ret
.L7:
    sub     rsp, 8    #,
    call    sqrt    #
    add     rsp, 8    #,
    cvttsd2si       eax, xmm0     # D.2570, tmp92
    ret

我猜sqrt()必须在某些错误上设置errno。 :/

使用-fno-math-errno

    pxor    xmm0, xmm0      # D.2569
    cvtsi2sd        xmm0, edi       # D.2569, a
    sqrtsd  xmm0, xmm0  # tmp92, D.2569
    cvttsd2si       eax, xmm0     # D.2570, tmp92
    ret

pxor是为了打破对xmm0之前内容的错误依赖,因为cvtsi2sd做出了奇怪的设计决定,使dest向量reg的上半部分保持不变。只有在您希望将转换结果插入现有向量时,这才有用,但已经cvtdq2pd进行了压缩转换。 (并且它们可能没有考虑到64位整数,因为当英特尔发布SSE2时,AMD64仍然处于绘图板上。)