【问题标题】:clang miss assembler error?铿锵小姐汇编错误?
【发布时间】:2016-04-27 09:31:56
【问题描述】:

在我看来,clang++ 错过了 g++ 拾取的汇编代码中的错误。还是我错过了一些clang的编译器标志?我是汇编代码的新手。

使用 clang++ 我编译并链接了我的应用程序错误和警告,但我遇到了令人讨厌的分段错误。切换到 g++,另一方面,我得到了这些错误:

GO_F_ImageColourConversion.cpp: Assembler messages:
GO_F_ImageColourConversion.cpp:4679: Error: `(%rsi,%edx,2)' is not a valid base/index expression 
GO_F_ImageColourConversion.cpp:4682: Error: `(%rcx,%edx,1)' is not a valid base/index expression

我正在使用这些编译器标志: -DLINUX -g -Wno-deprecated -D_GNU_SOURCE -D_REENTRANT -D__STDC_CONSTANT_MACROS -fPIC -fPIE

我有以下代码(省略不相关部分):

Ipp8u * pSrc;
Ipp8u * pDst;
int x, y;

                asm volatile
                    (
                     "movl      (%1, %0, 2), %%eax;\n"
                     "shlw      $8, %%ax;\n"
                     "shrl      $8, %%eax;\n"
                     "movw      %%ax, (%2, %0, 1);\n"

                    : /* no output */
                    : "r" (x), "r" (pSrc), "r" (pDst)
                    : "eax", "memory");
            }

通过查看answer on SO,我意识到我遇到了 32/64 位问题(我正在移植到 64 位)。Ipp8u* 是 8 位,但在我的机器上只有 4 位。

将 int 更改为 uintptr_t x, y; 似乎可以解决问题。为什么clang编译不报错?

【问题讨论】:

  • 大概是因为 clang 自动替换了正确的 64 位寄存器。您尚未显示为 clang 生成的程序集,但我认为它是有效的 (%rsi,%rdx,2)。 PS:这个不需要汇编,编译器肯定可以从C生成好的代码。而且这是一个特别糟糕的汇编代码。

标签: linux assembly clang x86-64 inline-assembly


【解决方案1】:

gcc 和 clang 都为我扼杀了你的代码:

6 : error: base register is 64-bit, but index register is not
"movl (%1, %0, 2), %%eax\n"
^
<inline asm>:1:13: note: instantiated into assembly here
movl (%rdi, %edx, 2), %eax

来自clang 3.8 on the godbolt compiler explorer,带有一个包裹着它的函数,所以它是可测试的,你没有在问题中提供。您确定您的 clang 正在构建 64 位代码吗? (-m64,不是-m32-mx32)。

在godbolt上提供一个链接到您的代码,并使用某些版本的clang默默地错误编译它,否则我只能对您的实际问题说“无法重现”。

是的,您的问题是xint,而您的问题是寻址模式下的混合寄存器大小。 (%rsi,%edx,2) 不可编码。


使用%q0 得到%rdx 并不能保证寄存器的高32 位中没有垃圾(尽管可能性极小)。相反,您可以使用 "r" ((int64_t)x)sign-extend x to 64bits

为什么需要内联汇编?您的 C 版本的编译器输出有多糟糕?

如果您确实想使用内联 asm,这要好得多

uint32_t asm_tmp = *(uint32_t *)(x*2 + (char*)pSrc);  // I think I've reproduced the same pointer math as the addressing mode you used.
asm ( "shlw      $8, %w[v]\n\t"    // e.g.  ax
      "shrl      $8, %k[v]\n\t"    // e.g. eax.  potential partial-register slowdown from reading eax after writing ax on older CPUs
      : [v] "+&r" (asm_tmp)
      );
*(uint16_t *)(x + (char*)pDst) = asm_tmp;  // store the low 16

这可以用 clang 很好地编译,但是 gcc is kinda braindead about generating the address。也许地址的表达方式不同?

您的代码以加载开始并以存储结束,从而违背了约束的目的。总是让编译器尽可能多地处理。如果没有内联汇编,您可能会从中获得更好的代码,并且编译器会理解它的作用,并且可能会自动矢量化或进行其他转换。消除对 asm 语句的需要 volatile"memory" clobber 对优化器来说已经是一个很大的改进:现在它是一个纯函数,编译器知道只转换一个寄存器。

另请参阅the end of this answer,了解更多编写不烂的内联 asm 的指南。

【讨论】:

  • 让我强调一下,我是内联 asm 的新手。我正在移植一些旧的 C++ 代码,其中有很多程序集。我宁愿把它全部用 C(++),但我需要先学会阅读 asm 才能重写它。正如@Jester 所指出的,我确信编译器可以生成好的代码。感谢您指出神螺栓。我将编译器更改为我使用的 clang 3.0,它编译成功link 虽然我不知道结果是否好...我想更新我的 clang 编译器对我来说是个好主意。跨度>
  • @PerM.:你为什么要使用这么古老的 clang 版本?新发布版本是 3.8,当前稳定版是 3.7。无论如何,clang3.0 的输出在寻址模式的两个部分都使用 32 位寄存器,因此,只要您在虚拟地址空间的低 2G 之外的地址上使用它,它就会中断。 (64 位代码中的 32 位寻址模式是符号扩展的)。在这种情况下,您应该使用我的代码。它使用 clang 编译为最佳代码,并且可以内联。对于您的其余代码,我认为反复试验不会很好地工作。你需要懂 asm 的人。
  • @PeterCordes:Clang 3.0 对我来说一点也不稀奇。它是 Ubuntu 12.04(长期支持)使用的版本。 Canonical 将在 2017 年的部分时间内支持 12.04。我有几个客户使用这个特定的 Linux 发行版,因为他们也为 Canonical 支持付费。
  • @MichaelPetch:我不建议使用带有硬壳旧 LTS 版本的编译器,尤其是。甚至不是当前的 LTS 版本。当然它会构建正确的代码,但如果你希望它为现代 CPU 自动矢量化,更新的 clang 有显着的改进。 Debian / Ubuntu 上有一个official (llvm.or) PPA for clang,有稳定版本和当前版本。我想我会推荐 clang 3.7 来运送二进制文件。 clang 3.8 在某些情况下做得更好,但它仍然很新并且偶尔会出现错误。
  • @PeterCordes :你不能为大公司做很多开发。许多人将在他们的企业中标准化一套工具。您可能会觉得它“生硬”,但在企业中,他们可能会认为它是“经过验证的”,如果他们使用该平台的官方支持版本,他们可以从 Canonical 获得直接的企业支持。出于同样的原因,Windows XP 仍然拥有庞大的安装基础。有时,大型企业的工作前提是“如果没有损坏就不要修复它”。
猜你喜欢
  • 2023-03-21
  • 1970-01-01
  • 2012-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多