GCC&x27;s asm的扩展版本
我从没想过我会发布一个集会问题。:-) 在中,有一个。此函数可以接受四个参数:汇编代码、输出列表、输入列表和覆盖列表 我的问题是,覆盖列表中的寄存器是否已清零?以前在那里的值会发生什么变化(来自其他正在执行的代码)GCC&x27;s asm的扩展版本,gcc,assembly,inline-assembly,Gcc,Assembly,Inline Assembly,我从没想过我会发布一个集会问题。:-) 在中,有一个。此函数可以接受四个参数:汇编代码、输出列表、输入列表和覆盖列表 我的问题是,覆盖列表中的寄存器是否已清零?以前在那里的值会发生什么变化(来自其他正在执行的代码) 更新:考虑到目前为止我的答案(谢谢!),我想补充一点,尽管clobber列表中列出了一个寄存器,但它(在我的实例中)正在pop(popl)命令中使用。没有其他参考。我怀疑覆盖列表只是给GCC一个提示,不要在ASM调用中在这些寄存器中存储任何有价值的内容;由于GCC不分析您给它的ASM
更新:考虑到目前为止我的答案(谢谢!),我想补充一点,尽管clobber列表中列出了一个寄存器,但它(在我的实例中)正在pop(
popl
)命令中使用。没有其他参考。我怀疑覆盖列表只是给GCC一个提示,不要在ASM调用中在这些寄存器中存储任何有价值的内容;由于GCC不分析您给它的ASM,并且某些指令有副作用,会触及代码中未明确命名的其他寄存器,因此这是告诉GCC的方法 如果“调零”的意思是“寄存器中的值被替换为0,以防止我知道其他函数在做什么”,那么不,在使用之前寄存器不会调零。但这并不重要,因为您告诉GCC您计划在那里存储信息,而不是希望读取当前存在的信息
将此信息提供给GCC,以便(阅读文档)在完成汇编代码时“无需猜测哪些寄存器或内存位置将包含要使用的数据”(例如,不必记住数据是否位于堆栈寄存器或其他寄存器)
GCC需要大量汇编代码帮助,因为“编译器……不解析汇编指令模板,也不知道它的意思,甚至不知道它是否是有效的汇编输入。扩展asm功能通常用于编译器本身不知道存在的机器指令。”
更新
GCC被设计为一个多通道编译器。许多通行证实际上是完全不同的程序。一组程序组成“编译器”将源从C、C++、艾达、java等翻译成汇编代码。然后一个单独的程序(gas
,对于GNU汇编程序)将该汇编代码转换为二进制代码(然后ld
和collect2
对二进制代码执行更多操作)。汇编块存在,直接将文本传递到<代码> Gub<代码>,并且存在“Culblist”(和输入列表),以便编译器可以在C、C++、艾达、java等方面和“<代码>气体< /代码>”方面传递所需的任何设置,并确保寄存器中当前的任何重要信息都可以通过在程序集块运行之前将其复制到内存中(并在运行之后从内存中复制回来)来保护它不受程序集块的影响
另一种方法是保存和恢复每个汇编代码块的每个寄存器。在具有大量寄存器的RISC机器上,可能会变得昂贵(例如,安腾有128个通用寄存器、128个浮点寄存器和64个1位寄存器)
我已经有一段时间没写汇编代码了。我使用GCC的命名寄存器特性的经验比使用特定寄存器的经验多得多。那么,看一个例子:
#include <stdio.h>
long foo(long l)
{
long result;
asm (
"movl %[l], %[reg];"
"incl %[reg];"
: [reg] "=r" (result)
: [l] "r" (l)
);
return result;
}
int main(int argc, char** argv)
{
printf("%ld\n", foo(5L));
}
由于GCC的编译器部分不检查汇编代码,因此它不会保护
%%ecx
中的数据。如果幸运的话,%%ecx
可能恰好是GCC决定用于%[reg]
或%[l]
的寄存器之一。如果我运气不好,我会“神秘地”更改程序其他部分中的值。不,它们没有归零。覆盖列表(通常称为clobber列表)的目的是通知GCC,由于asm指令,clobber列表中列出的寄存器将被修改,因此编译器应保留当前处于活动状态的任何寄存器
例如,在x86上,cpuid
指令使用四个固定寄存器返回四部分信息:%eax
、%ebx
、%ecx
和%edx
,基于%eax
的输入值。如果我们只对%eax
和%ebx
中的结果感兴趣,那么我们可能(天真地)写:
这将在C变量input_res1
和res2
中获得结果的第一部分和第二部分;但是,如果GCC使用%ecx
和%edx
保存其他数据;它们将被cpuid
指令覆盖,gcc不知道。防止这种情况;我们使用clobber列表:
正如我们告诉GCC的那样,%ecx
和%edx
将被此asm调用覆盖,它可以正确处理这种情况-要么不使用%ecx
或%edx
,要么在asm
函数之前将它们的值保存到堆栈中,然后在调用后恢复
更新:
关于您的第二个问题(为什么您会看到popl
指令的clobber列表中列出了一个寄存器)-假设您的asm
看起来像:
__asm__("popl %eax" : : : "%eax" );
然后,这里的代码从堆栈中弹出一个项目,但是它不关心实际值——它可能只是保持堆栈平衡,或者该值在该代码路径中不需要。以这种方式书写,而不是:
int trash // don't ever use this.
__asm__("popl %0" : "=r"(trash));
您不必显式地创建一个临时变量来保存不需要的值。诚然,在这种情况下,两者之间没有太大的区别,但使用clobber的版本清楚地表明,您并不关心堆栈中的值。谢谢您的回答。你的答案(以及其他答案)给了我我需要的信息。。。我更新了我的问题,希望你能考虑一下我的新信息。
int input_res1 = 0; // also used for first part of result
int res2;
__asm__("cpuid" : "+a"(input_res1), "=b"(res2)
: : "%ecx", "%edx" );
__asm__("popl %eax" : : : "%eax" );
int trash // don't ever use this.
__asm__("popl %0" : "=r"(trash));