【问题标题】:Sqrt in Assembly x86装配 x86 中的 Sqrt
【发布时间】:2016-03-02 12:53:21
【问题描述】:

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

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

代码以asm(...) 的形式插入到C 程序中。

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

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

我得到编译错误:

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

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

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

【问题讨论】:

  • 您不能从通用寄存器加载到 FPU。你需要通过记忆。请参阅指令集参考。 push ebx; fild dword [esp]; fsqrt; fistp dword [esp]; pop ebx。请注意,通常不需要汇编,而且 gcc 内联汇编是一个复杂的野兽。
  • 请使用minimal reproducible example 而不是单纯的sn-p。
  • @Jester 嘿,我在某个网站上看到过这个方案,但不明白我应该插入什么来代替 dword 和 esp?
  • esp 是堆栈指针,您不要插入任何“代替”它的东西。 dword 可能需要为dword ptr,这只是表示操作数的大小。
  • @Jester 你对 esp 的看法是对的 .. 我有点困惑(顺便说一句,我需要组装)

标签: assembly compiler-errors x86 mismatch


【解决方案1】:

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

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 内联 asm 是执行 asm 的最困难的方法,因为您必须真正了解所有内容才能获得正确的约束。弄错它们可能会导致其他周围的代码以微妙且难以调试的方式中断,而不仅仅是您对内联汇编所做的事情是错误的。有关这方面的更多详细信息,请参阅 x86 标签 wiki。

如果你想要int a = sqrt((int)b),那么把它写在你的代码中,让编译器为你生成这三个指令。一定要阅读并理解编译器的输出,但不要只是盲目地在其中插入一个序列asm("")

例如:

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

compiles to(没有 -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

我猜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 位整数,因为当 Intel 发布 SSE2 时 AMD64 仍处于设计阶段)。

【讨论】:

  • 我认为它比我预期的要复杂“一点”。谢谢!
猜你喜欢
  • 1970-01-01
  • 2015-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-19
  • 2015-11-21
  • 1970-01-01
  • 2021-09-09
相关资源
最近更新 更多