装配x86中的Sqrt

use*_*793 0 x86 assembly compiler-errors mismatch

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

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

该代码如asm(...)在C程序中那样插入。

-masm=intel使用编译后,使用时:

asm ("FLD EBX \n" "FSQRT \n" "FST EBX \n").
Run Code Online (Sandbox Code Playgroud)

我收到编译错误:

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

在这些命令之前,EBX持有一些整数正值。

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

Pet*_*des 6

您应该在现代代码中将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)
Run Code Online (Sandbox Code Playgroud)

这也适用于64位整数(rbx),但请注意,double它只能精确表示不超过2 ^ 53(尾数大小)的整数。如果要检查整数是否是理想的平方,可以使用float sqrt,然后对整数结果进行尝试乘法。((a*a) == b

请参阅,以获得指向指南,教程和手册的链接。


请注意,将此代码插入C程序的中间是完全错误的方法。GNU C内联汇编是执行汇编的最困难的方法,因为您必须真正了解所有内容才能正确地执行约束。弄错它们可能导致其他周围的代码以微妙且难以调试的方式破坏,而不仅仅是行内汇编错误的事情。有关更多信息,请参见x86标签Wiki。

如果需要int a = sqrt((int)b),则将其编写为代码,然后让编译器为您生成这三个指令。一定要阅读并理解编译器的输出,但不要只是使用盲目地将一个序列放入中间asm("")

例如:

#include <math.h>
int isqrt(int a) { return sqrt(a); }
Run Code Online (Sandbox Code Playgroud)

编译为(不带-ffast-math的gcc 5.3):

    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
Run Code Online (Sandbox Code Playgroud)

我想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
Run Code Online (Sandbox Code Playgroud)

这样做pxor是为了打破对xmm0先前内容的错误依赖,因为cvtsi2sd做出了奇怪的设计决定,即不修改dest向量reg的上半部分。仅在要将转换结果插入到现有向量中时才有用,但是已经cvtdq2pd进行了打包转换。(而且他们可能没有考虑64位整数,因为当Intel发布SSE2时AMD64仍处于起步阶段)。