【问题标题】:Alignment issue in x64 code, Free Pascalx64 代码中的对齐问题,Free Pascal
【发布时间】:2015-02-06 13:31:24
【问题描述】:

如果符合 32 位(使用适用的寄存器重命名),下面的代码可以正常工作。但它在执行时会引发错误(并且“警告:对象文件“project1.o”包含 32 位绝对重定位到符号“.data.n_tc_p$project1_orbitkeyheader64$int64$longint$$int64_shufidx”。 " 编译时)。

function SwapBytes64(const Val: Int64): Int64;
{$A 16}
const
  SHUFIDX : array [0..1] of Int64 = ($0001020304050607, 0);
begin
asm
  movq          xmm0, rcx
  pshufb        xmm0, SHUFIDX    // throws
  movq          rax, xmm0
end;
end;

我该如何纠正这个问题(最好是对齐常量)。

编辑 我也尝试过使用 movdqu。

回答 这是@Jester 回答的结果:

function SwapBytes64(const Val: Int64): Int64;
const
  SHUFIDX : array [0..1] of Int64 = ($0001020304050607, 0);
begin
asm
  movq          xmm0, rcx
  movdqu        xmm1, [rip+SHUFIDX]
  pshufb        xmm0, xmm1
  movq          rax, xmm0
end;
end;

这也有效,但没有明显的速度优势:

function SwapBytes64(const Val: Int64): Int64;
const
  SHUFIDX : array [0..1] of Int64 = ($0001020304050607, 0);
begin
asm
  movq          xmm0, rcx
  pshufb        xmm0, [rip+SHUFIDX]
  movq          rax, xmm0
end;
end;

【问题讨论】:

  • 既然是局部变量,难道你不应该像[rbp-8]那样处理局部变量吗?
  • 64 位模式不喜欢这个常量。 32位就可以了。这是一个定义对齐问题。我不需要像 rbp-8 这样的东西,因为我可以直接引用常量。
  • 什么平台?我记得当符号实际上没有在任何地方定义时,我在 Windows 上看到过类似的错误。
  • 我正在使用针对 Win64 / Athlon64 的 Lazarus / Free Pascal 进行编译。 Lazarus 在 32 位 VM 中,我从 C# 调用代码。在 32 位模式下,我可以在本地对其进行测试,并且可以正常工作(也没有编译器警告)。

标签: assembly lazarus freepascal


【解决方案1】:

这可能根本不是对齐问题。编译器已警告您对 SHUFIDX 的绝对引用将被截断为 32 位。如果地址不在前 4GiB 内,则会导致错误的内存引用。您应该在调试器中检查这一点。

作为一种解决方法,您应该使用 rip-relative 或间接寻址。前者可能看起来像 movdqu xmm1, [rip+SHUFIDX]movdqu xmm1, rel SHUFIDX 或类似的东西。请查阅您的编译器手册。

【讨论】:

  • @IanC 为了其他人的利益,您能告诉我们编译器接受了哪种语法吗?
  • 根据@TheRaven 的回答,它是[RIP + var]。哦,OP还在问题的答案中进行了编辑。 ://
【解决方案2】:

与您的实际问题无关:您的代码不安全。除非你编写一个纯汇编函数(“assembler; asm .. end;”,或者——在 Delphi 模式下——只包含一个“asm .. end;”块而没有围绕“begin .. end;”,编译器可以插入汇编块之前和之后的代码。特别是,它可能会在汇编块执行完毕后覆盖 rax 的值。

要解决此问题,请将您的函数设为纯汇编函数,或在末尾添加“movq @result, rax”。

【讨论】:

  • 请记住,此类 cmets 应在 cmets 部分进行,此处应仅发布相关答案。
【解决方案3】:

RIP + Var name 解决了我的问题,即相关变量被截断为 32 位内存分配。我什至将变量的空间解释为 Int64,但没有成功。使用值加载 RAX,然后将其分配给变量是可行的,但需要额外的编码,使 32 位代码块大小加倍。

MOV qword[var], RBX 会抛出错误

这可行,但会使代码膨胀:

MOV RAX, RBX
MOV qword[var], RAX

...虽然这可以按预期使用更少的 MOV 指令:

MOV qword[RIP + var], RBX

【讨论】:

  • 我非常怀疑MOV qword[var], RAX 是否有效,而MOV qword[var], RBX 无效,但我赞成为该汇编程序显示 RIP 相对寻址模式的正确语法 ([RIP + var])。
  • 担心它确实可以与 FAsm 和 NAsm 一起使用,但是 FreePascal 中用于 64 位输出的内联汇编程序会由于 64 位可执行文件中的内存空间截断到 32 位编译器错误而引发寻址错误。感谢您的支持 - 是的,当内存分配明确设置为四字时,RBX 到 var 的移动应该最有效,我同意你的观点 Peter Cordes。保重。
  • 但这两条指令的唯一区别是使用不同的 64 位寄存器(rax 而不是rbx)。这有什么不同?
  • 如前所述,FreePascal 编译器(本次讨论的内容)正在截断内存(从 64 位到 32 位),而 FPC 唯一支持的直接通用寄存器(内存大小)是 RAX;还有其他寄存器,但仍然很痛苦。所以我使用 rip + 内存地址(通过命名引用)来补偿。这是一个 RIP 问题,在 FPC 中是已知的 - 我仍然不明白为什么它没有得到修复,因为这个问题已经持续了 2 年。
  • 我还想知道为什么编译器在 64 位系统中将内存分配截断为 32 位,默认 malloc 大小将为 64 位,除非明确声明必须为 32 位,这似乎是一个逻辑错误(设计)在 FPC 中促进该线程解决的实际错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-08
相关资源
最近更新 更多