C语言中的内联汇编
内容提要
文章介绍GCC内联汇编:基本asm可直接嵌入汇编指令,但无法读写C变量;扩展asm通过输出、输入约束和clobber列表实现汇编与C变量交互。约束“r”表示使用通用寄存器,“=r”表示只写寄存器;若汇编修改其他寄存器,须在clobber列表声明,避免GCC错误优化。
延伸解读
基本asm的局限:无法与C变量交互
文章指出,基本asm语句虽然能直接嵌入汇编指令,但GCC不会解析这些指令,也不了解其含义或有效性。因此,基本asm无法读取或写入C变量,例如不能将C变量中的整数放入寄存器。所有基本asm块隐式带有volatile属性,但若需要与C代码交换数据,必须使用扩展asm。
扩展asm的约束:r与=r的含义
扩展asm通过输出、输入约束和clobber列表实现与C变量的交互。约束“r”表示操作数可存放在通用寄存器中,常用于输入;“=r”表示只写寄存器,常用于输出。例如,asm volatile("msr pmintenset_el1, %0" : : "r" ((u64)(0 << 31)))将C表达式的结果放入通用寄存器并填入%0;而asm("mrs %0, PMCR_EL0" : "=r" (f))将系统寄存器的值读入变量f。
clobber列表:告知GCC被修改的寄存器
当汇编代码修改了其他寄存器时,必须在clobber列表中显式声明,否则GCC可能错误地假设这些寄存器未被改动,从而进行不安全的优化。文章举例,在x86内联汇编中调用_foo函数前,将eax和ecx标记为clobber,这样GCC就不会依赖这两个寄存器来维护其他值。正确使用clobber列表是避免隐蔽错误的关键。
Q&A
GCC内联汇编中基本asm和扩展asm有什么区别?
基本asm可直接嵌入汇编指令,但无法读写C变量;扩展asm通过输出、输入约束和clobber列表实现汇编与C变量交互。
在扩展asm中,约束“r”和“=r”分别代表什么?
“r”表示使用通用寄存器,常用于输入约束;“=r”表示只写寄存器,常用于输出约束。
为什么在扩展asm中需要使用clobber列表?
因为汇编代码可能修改其他寄存器,而GCC无法解析汇编指令,不知道这些副作用,所以必须显式列出受影响的寄存器,避免GCC错误优化。
基本asm语句默认是volatile的吗?
是的,所有基本asm块都隐式是volatile的。
能否用基本asm将C变量中的整数放入寄存器?
不能,基本asm无法读写C变量,需要使用扩展asm。
扩展asm的语法结构是怎样的?
扩展asm的语法为:asm qualifiers (AssemblerTemplate : output_constraint (C lvalue) : input_constraint (C expression) : Clobbers)。