clang miss汇编错误?

时间:2016-04-27 09:31:56

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

在我看来,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位isssue(我正在移植到64位).Ipp8u *是8位但在我的机器上只有4位。

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

1 个答案:

答案 0 :(得分:2)

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

为什么你需要内联asm? 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 clobber删除asm语句为"memory"的需求已经是优化器的一个重大改进:现在它是一个纯函数,编译器知道它只是转换一个寄存器。

另请参阅the end of this answer以获取有关编写内联asm的更多指南。