内联 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;
}
我用一些内联汇编编写了一个演示(展示了如何将内存数组右移一位),它在 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;
}