6

以下函数不能用 64 位 Delphi XE2 编译器编译。(错误都与fld说明有关。)

[dcc64 Error] Project1.dpr(12): E2116 Invalid combination of opcode and operands 
[dcc64 Error] Project1.dpr(13): E2116 Invalid combination of opcode and operands
[dcc64 Error] Project1.dpr(20): E2116 Invalid combination of opcode and operands

第 12 和 13 行:

fld Y
fld X

第 20 行:

fld X

不幸的是,我没有组装技能,我正在使用需要移植到 64 位的第三方代码。你能帮我让它在 32 位和 64 位上都工作吗?

function PartArcTan(Y, X: Extended): Extended;
asm
  fld Y              // st(0) = Y
  fld X              // st(0) = X
  fpatan             // st(0) = ArcTan(Y, X)
  fwait
end;

function ArcSin(X: Extended): Extended; // -1 <= X <= 1
asm
  fld X               // st(0) = X
  fld st(0)           // st(1) = X
  fmul st(0), st(0)   // st(0) = Sqr(X)
  fld1                // st(0) = 1
  fsubrp st(1), st(0) // st(0) = 1 - Sqr(X)
  fsqrt               // st(0) = Sqrt(1 - Sqr(X))
  fpatan              // st(0) = ArcTan(X, Sqrt(1 - X*X))
  fwait
end;
4

2 回答 2

8

这段代码移植到 x64 的主要问题是它使用了错误的浮点单元。在 x64 上,浮点是在 SSE 单元上完成的。

是的,x87 单元仍然存在,但相比之下速度较慢。另一个问题是 x64 ABI 假定您将使用 SSE 单元。参数到达 SSE 寄存器。浮点值在 SSE 寄存器中返回。在 SSE 和 x87 单元之间传输值是没有意义的(更不用说相当辛苦和耗时的)。此外,浮点控制、异常掩码已为 SSE 单元初始化,但您确定它们将为 SSE 单元正确设置。

因此,鉴于这一切,我强烈建议您确保所有浮点代码都在 x64 下的 SSE 单元上执行。我认为唯一可以使用 x87 寄存器的情况是针对需要 x87 支持但不支持 SSE 的 10 字节扩展类型的算法。这里情况不同。

现在,移植到 SSE 单元并不像将操作码转换为 SSE 等效项那么简单。这是因为 SSE 浮动单元的内置功能要少得多。例如,SSE 操作码中不包含三角函数。

所以,解决这个问题的正确方法是改用 Pascal 代码。这些函数可以分别替换为Math.ArcTan2Math.ArcSin


为了详细说明这一点,让我们看看在 x64 下的 x87 单元上进行计算所涉及的内容。的代码ArcSin是这样的:

function ArcSin(X: Double): Double;
// to be 100% clear, do **not** use this code
asm
  movq [rsp-8], xmm0     // X arrives in xmm0, move it to stack memory
  fld qword ptr [rsp-8]  // now load X into the x87 unit
  fld st(0)              // calculation code exactly as before
  fmul st(0), st(0)
  fld1
  fsubrp st(1), st(0)
  fsqrt
  fpatan
  fwait
  fstp qword ptr [rsp-8] // but now we need to move the return value
  movq xmm0, [rsp-8]     // back into xmm0, again via the stack
end;

注意事项:

  1. x64 ABI 表示输入参数到达 xmm0。我们不能将它直接加载到 x87 单元中。所以我们必须从 xmm0 转移到堆栈上的暂存内存,然后从那里加载到 x87 单元。
  2. 返回值时我们必须做类似的事情。该值在 xmm0 中返回,由 ABI 指定。所以我们需要移出 x87 单元,来暂存堆栈内存,然后加载到 xmm0 中。
  3. 我们完全忽略了浮点控制字:异常屏蔽、精度和舍入控制等。如果你要这样做,你需要建立一个机制来确保 x87 单元的控制字以理智的方式处理。

因此,也许这可以作为对希望使用 x87 在 x64 下执行浮点运算的未来访问者的警告。

于 2013-12-24T17:20:02.997 回答
3

x64 仍然支持经典的浮点单元,但您需要调整代码以遵循不同的 ABI。

x32/x64 示例:

function PartArcTan(X: double): double;
asm
{$IFDEF CPUX64}
        movq [rsp-8], xmm0
        fld    qword ptr [rsp-8]
{$ELSE}
        fld    qword ptr X
{$ENDIF}
        fld1
        fpatan
        fwait
{$IFDEF CPUX64}
        fstp   qword ptr [rsp-8]
        movq   xmm0, [rsp-8]
{$ENDIF}
end;
于 2013-12-24T17:42:42.520 回答