内联 x86 程序集的 Clang 错误 "expected register"(适用于 GCC)

Clang errors "expected register" with inline x86 assembly (works with GCC)

我用一些内联汇编编写了一个演示(展示了如何将内存数组右移一位),它在 GCC 中编译和运行良好。但是,对于 Clang,我不确定它是否生成了错误的代码或者是什么,但尽管有“rm”约束,我还是在使用内存,这让我很不高兴。

我已经通过 Godbolt 尝试了很多编译器和版本,虽然它适用于所有 x86/x86_64 版本的 GCC,但它无法用于所有版本的 Clang。我不确定问题是出在我的代码上还是我发现了编译器错误。

代码:

#include <stdio.h>

int main(int argc, char** argv)
{
  unsigned char bytes[16] =
  {
    0xFF, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0xF0,
  };

  __asm__ volatile("shrl , 12(%0); \n"
                   "rcrl ,  8(%0); \n"
                   "rcrl ,  4(%0); \n"
                   "rcrl ,  0(%0); \n"
                   : : "rm" (bytes));

  printf("bytes: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x \n",
         bytes[15], bytes[14], bytes[13], bytes[12], bytes[11], bytes[10], bytes[9], bytes[8],
         bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]);
  return 0;
}

编译输出:

.<source>:13:20: error: expected register here
.  __asm__ volatile("shrl , 12(%0); \n"
.                   ^
.<inline asm>:1:14: note: instantiated into assembly here
.        shrl , 12(-88(%rbp)); 
.                    ^
.<source>:14:21: error: expected register here
.                   "rcrl ,  8(%0); \n"
.                    ^
.<inline asm>:2:13: note: instantiated into assembly here
.rcrl ,  8(-88(%rbp)); 
.            ^
.<source>:15:21: error: expected register here
.                   "rcrl ,  4(%0); \n"
.                    ^
.<inline asm>:3:13: note: instantiated into assembly here
.rcrl ,  4(-88(%rbp)); 
.            ^
.<source>:16:21: error: expected register here
.                   "rcrl ,  0(%0); \n"
.                    ^
.<inline asm>:4:13: note: instantiated into assembly here
.rcrl ,  0(-88(%rbp)); 
.            ^

I'm unsure if the problem is my code or if I found a compiler bug.

问题出在您的代码上。在 GNU 汇编程序中,括号用于解引用,就像 C 中的一元 * 一样,您只能解引用寄存器,不能解引用内存。因此,当 %0 可能是内存错误时,在程序集中写入 12(%0) 是错误的。它恰好在 GCC 中工作,因为 GCC 选择在那里使用 "rm" 的寄存器,而 Clang 选择使用内存。您应该改用 "r" (bytes)

此外,您需要告诉编译器您的程序集将修改数组,可以使用 memory 破坏或添加 *(unsigned char (*)[16])bytes 作为输出。现在,允许优化您的 printf 以仅硬编码程序开头的值。

固定码:

#include <stdio.h>

int main(int argc, char** argv)
{
  unsigned char bytes[16] =
  {
    0xFF, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0x81,
    0x81, 0x81, 0x81, 0xF0,
  };

  __asm__ volatile("shrl , 12(%1); \n"
                   "rcrl ,  8(%1); \n"
                   "rcrl ,  4(%1); \n"
                   "rcrl ,  0(%1); \n"
                   : "+m" (*(unsigned char (*)[16])bytes) : "r" (bytes));

  printf("bytes: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x \n",
         bytes[15], bytes[14], bytes[13], bytes[12], bytes[11], bytes[10], bytes[9], bytes[8],
         bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]);
  return 0;
}