【问题标题】:Delphi 64 bits asm compiling errorDelphi 64位asm编译错误
【发布时间】:2014-01-12 21:17:25
【问题描述】:

以下函数不能用 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;

【问题讨论】:

  • FLD instruction x64 bit 的可能重复项
  • 更正,正如大卫在他的回答中提到的那样,Math.ArcTan2 应该是。根本不要使用 asm。
  • 在 64 位 Delphi XE2 中存在使用 x87 FPU 的第三方单元。它被写入重新启用 10 字节(扩展)数据类型,用于 8 字节(双)浮点类型不够用的计算。 OTOH,它的运行速度甚至比 x86 DCC 本机 x87 还要慢 - 因为编译器外单元无法分析代码流以进行优化,并且必须在每个语句后插入 FWAIT 操作码。
  • @Arioch Delphi 编译器在浮点代码生成中没有优化,并且在每条语句之后插入 FWAIT。 x87 代码在 x64 下运行缓慢还有其他原因。但是 80 位精度是在 x64 下考虑 x87 操作码的唯一原因。

标签: delphi assembly delphi-xe2 32bit-64bit fpu


【解决方案1】:

此代码移植到 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 下执行浮点运算的访问者的警告。

【讨论】:

  • 好吧,你是对的,我忘记将结果放入 xmm0 寄存器。 :)
  • @GJ。相反,Math.ArcTan2 和 Math.ArcSin 适用于所有 Delphi 编译器,而不仅仅是两个 Windows 编译器。我的回答中的 asm 代码演示了 做什么。我认为这句话说得很清楚。
【解决方案2】:

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;

【讨论】:

  • 在x64下运行时x87单元的控制字是什么状态?
  • “它被禁用”是什么意思?
  • @GJ.,我认为在 asm 中这样做没有任何意义。使用 RTL 函数支持所有平台。在这里使用 asm 几乎没有(如果没有的话),只有混乱,这个答案似乎增加了一点。
  • AFAICS 问题中只有一个问题:“你能帮我让它在 32 位和 64 位上都工作吗?”
  • @GJ。 - 抱歉干预,该评论不是来自大卫。我想指出,问题不在于为什么会出现编译错误。
猜你喜欢
  • 2013-08-25
  • 2018-04-20
  • 1970-01-01
  • 2016-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多