GCC 无法从具有内联汇编的函数生成 32 位代码
Posted
技术标签:
【中文标题】GCC 无法从具有内联汇编的函数生成 32 位代码【英文标题】:GCC fails to generate 32-bit code from a function with inline assembly 【发布时间】:2013-10-22 19:29:39 【问题描述】:我尝试生成这样的 32 位代码: gcc -S -m32 BMPTransformer.c -o BMPTransformer.s
我使用的是 Ubuntu 13.04。有类似问题的人似乎通过安装 libc6-dev-i386 克服了困难。不过,它对我没有用。
编译器报错:
BMPTransformer.c:243:6:错误:在重新加载“asm”时找不到类“GENERAL_REGS”中的寄存器 BMPTransformer.c:243:6: error: ‘asm’ 操作数有不可能的约束
代码原样:
216 static void ASM_reverse_image(BMPImage *image)
217
218 asm (
219 "movl $0, %%eax\n"
220
221 "cmpl %%eax, %1\n"
222 "jl end\n"
223
224 "row:\n"
225 "movl (%0, %%eax, 4), %%edx\n"
226 "decl %1\n"
227 "movl (%0, %1, 4), %%esi\n"
228 "movl %%esi, (%0,%%eax, 4)\n"
229 "incl %%eax\n"
230 "movl %%edx, (%0, %1, 4)\n"
231 "cmpl %%eax, %1\n"
232 "jg row\n"
233
234 "end:\n"
235
236 : : "r"(image->pixel_data), "r"(image->header.height): "%eax", "%edx", "%esi"
237 );
238
使用 64 位 a、b、c 寄存器的代码运行良好。但我需要一个 32 位版本。
【问题讨论】:
尼特:clobber 列表错过了"memory"
和 "cc"
条目。此外,gcc 本身很可能会为这项工作创建性能更好的代码(来自等效的 C 源代码)
谢谢!我错过了这两个,因为我还不习惯 AT&T 语法。
【参考方案1】:
该错误通常表明编译器已用完寄存器。从您发布的小片段来看,情况并非如此,而且确实对我来说编译得很好。你可能没有告诉一些重要的细节。
无论如何,绝对没有理由以当前形式将其写成内联汇编。编译器可以轻松生成更好(和工作)的代码。最初的比较当然应该在 C 中。
旁注:当使用 gcc 内联汇编时,一般的想法是为编译器留下尽可能多的可能性。例如,您不需要任何寄存器,您可以使用通用约束。
【讨论】:
“重要细节”是函数声明为static
。 gcc 通常会尝试 inline 那些,至少在同一个编译单元(源文件)中,所以它取决于调用者 - 未显示。
我在另一台机器上运行它,它编译得很好。在安装生成 32 位代码所需的软件包时,我可能没有注意到错误。你一开始是对的。我还没有找到任何关于通用约束的信息。你能对这个更详细一点吗?有没有办法在 asm 中编写更高效的算法?【参考方案2】:
将其编码为纯 C:
static void ASM_reverse_image(BMPImage *image)
int *pixel_data = image->pixel_data;
int tmp;
size_t idx = 0, height = image->header.height;
for (idx = 0; idx < height; idx++)
tmp = pixel_data[idx];
pixel_data[idx] = pixel_data[height - idx];
pixel_data[height - idx] = tmp;
或者,如果您使用的是 C++,只需:
for (idx = 0; idx < height; idx++)
std::swap(pixel_data[idx], pixel_data[height - idx]);
编辑:对于组装练习,这样做:
int tmp;
asm("row:
mov (%0), %2
xchg %2, (%0, %1, 4)
lea 4(%0), %0
dec %1
jns row"
: : "r"(image->pixel_data), "r"(image->header.height), "r"(tmp)
: "memory", "cc");
但这不是好的代码 - 主要是因为这是一种“流”处理类型,应该通过向量单元完成。
在 gcc 内联汇编中避免请求特定的寄存器总是一个好主意。让编译器来选择。这可能意味着您必须将一个或多个“伪变量”声明为汇编寄存器操作数(以获得“注册保留”)。
【讨论】:
我的 C 代码看起来就像这样。正如您可能已经猜到的那样,我需要内联汇编版本只是为了练习。 我理解你编写的汇编代码,但我不明白为什么它不好。你能在这个上更精确一点吗? 内联汇编不仅效率低下(带有内存操作数的xchg
有一个隐含的lock
前缀,所以它非常慢),它甚至不安全。 asm 修改保存仅输入操作数的寄存器。不过,您是对的,最好让编译器为您分配寄存器。用mov
开始一个asm 块几乎总是一个糟糕的计划。 (除非它是像这里这样的循环的一部分,或者您也需要原始副本)。以上是关于GCC 无法从具有内联汇编的函数生成 32 位代码的主要内容,如果未能解决你的问题,请参考以下文章