【问题标题】:x86_64 assembly instructions changed in link stage of GCCx86_64 汇编指令在 GCC 的链接阶段更改
【发布时间】:2020-02-14 07:44:13
【问题描述】:

我正在使用 Linux(centos7_64) 中的 sqlite3 库编译程序。由于用户使用的是旧 CPU,我在 GCC 中设置了 -march=nehalem 标志(-march=nehalem -mtune=nehalem -m64 -O3)。我发现我不能将汇编指令限制为 nehalem,一些 BMI 操作仍然存在于最终的二进制文件中。

按照输出一步一步,我发现问题来自链接器(ld)。

libsqlite3.a:

   632c2:       66 41 83 4f 26 01       orw    $0x1,0x26(%r15)
   632c8:       0f b6 84 24 80 00 00    movzbl 0x80(%rsp),%eax
   632cf:       00
   632d0:       c1 e0 08                shl    $0x8,%eax
   632d3:       89 c2                   mov    %eax,%edx
   632d5:       0f b6 84 24 81 00 00    movzbl 0x81(%rsp),%eax
   632dc:       00
   632dd:       c1 e0 10                shl    $0x10,%eax
   632e0:       09 d0                   or     %edx,%eax
   632e2:       8d 90 00 fe ff ff       lea    -0x200(%rax),%edx
   632e8:       41 89 47 30             mov    %eax,0x30(%r15)
   632ec:       81 fa 00 fe 00 00       cmp    $0xfe00,%edx
   632f2:       0f 87 d1 05 00 00       ja     638c9 <sqlite3BtreeOpen+0xb29>
   632f8:       8d 50 ff                lea    -0x1(%rax),%edx
   632fb:       85 c2                   test   %eax,%edx
   632fd:       0f 85 c6 05 00 00       jne    638c9 <sqlite3BtreeOpen+0xb29>

但是,在最终的二进制文件中:

  9499f2:       66 41 83 4f 26 01       orw    $0x1,0x26(%r15)
  9499f8:       0f b6 84 24 80 00 00    movzbl 0x80(%rsp),%eax
  9499ff:       00
  949a00:       0f b6 94 24 81 00 00    movzbl 0x81(%rsp),%edx
  949a07:       00
  949a08:       c1 e0 08                shl    $0x8,%eax
  949a0b:       89 c1                   mov    %eax,%ecx
  949a0d:       89 d0                   mov    %edx,%eax
  949a0f:       c1 e0 10                shl    $0x10,%eax
  949a12:       09 c8                   or     %ecx,%eax
  949a14:       8d 90 00 fe ff ff       lea    -0x200(%rax),%edx
  949a1a:       41 89 47 30             mov    %eax,0x30(%r15)
  949a1e:       81 fa 00 fe 00 00       cmp    $0xfe00,%edx
  949a24:       0f 87 cf 05 00 00       ja     949ff9 <sqlite3BtreeOpen+0xb09>
  949a2a:       c4 e2 78 f3 c8          blsr   %eax,%eax
  949a2f:       85 c0                   test   %eax,%eax
  949a31:       0f 85 c2 05 00 00       jne    949ff9 <sqlite3BtreeOpen+0xb09>

注意最后几行,链接器将 lea 更改为 blsr,这是出乎意料的。

因此,为什么会发生这种情况。链接器(ld)会进一步优化代码吗?如何限制链接器使用的指令?

【问题讨论】:

  • 您确定没有使用-flto 启用链接时优化/代码生成吗?通常允许跨文件内联是一件好事,但您也需要为此设置正确的拱形和优化选项。显然,对于生成最终代码的任何内容,您都设置了基线以外的其他内容。 (或者你链接了一些不同的目标文件......)
  • 另外,我认为 GCC 正在为那些 2 字节负载编写非常低效的代码。看起来movzwl 0x80(%rsp), %eax / shl $8, %eax 会起作用。您是否有可以重现错过的优化的资源?还是只有某些旧的 GCC 版本才会出现这种情况?
  • 我真的不确定,但也许是因为一些放松。我的意思是想象一个架构,它有一个JUMP 指令,它只能跳转到256 字节之外。你调用了一个在这个范围内的foo 函数,所以会生成像JUMP foo 这样的东西。现在这在该编译单元中可能有效,但是当您将其与其他代码链接时,foo 可能会超出该256 字节范围,因此必须将其替换为能够进行更长跳转的类似指令。所以没有必要进行优化,可能只需要使代码有效。
  • 请创建一个minimal reproducible example,包括确切的编译器版本和您键入以生成错误二进制文件的确切命令。有多种可能的问题,并且至少不知道编译器和链接器是如何被调用的,很难说问题可能是什么。
  • 感谢所有有用的 cmets。我发现了问题。请参阅我自己发布的答案。请不要投票。很抱歉问了一个不完整且愚蠢的问题。

标签: gcc assembly optimization linker ld


【解决方案1】:

非常感谢 cmets。我发现了问题,正如 Peter Cordes 在评论中所说,我链接到另一组 sqlite 库。我安装了太多的GCC编译环境,每个编译器在默认库路径下都有自己的sqlite。我的项目是由 cmake 管理的,它记得所有以前的 GCC 设置...

发现步骤:

  1. 在 gcc 命令中添加 -v 标志。

  2. 复制 ld 命令,并添加标志“--print-map -Map=demo.map”,再次运行完整的 ld 命令。

  3. 在demo.map中搜索库名(这里是sqlite),我清楚地发现已经链接了另一组sqlite库。意识到我有多愚蠢......

更新:我有一个新问题:如果 library.a 是使用高级 CPU 指令编译的,如何在链接阶段降级它,似乎这些指令将被复制成二进制而不检查 GCC 中的 -march 标志。

【讨论】:

  • 提供library.a 就像提供一堆松散的.o 文件。如果它们是使用-mbmi 编译的,则无法将它们链接到可能在早期 CPU 上运行的可执行文件中。与gcc -march=whatever 链接不会过滤链接器输入,实际上目标文件甚至没有元数据来记录所需的ISA 扩展。 (虽然这将允许像您希望的那样进行自动检查,但有趣的想法......)
猜你喜欢
  • 1970-01-01
  • 2017-02-27
  • 2018-06-19
  • 2016-09-13
  • 2015-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多