GCC内联汇编
语法规则
asm volatile(
汇编指令列表
∶输出操作数 //非必需
∶输入操作数 //非必需
∶可能影响的寄存器或存储器 1/非必需
);
-
Explanation
asm
是GCC的关键字,表示内联汇编操作,注__asm__
是GCC中asm的宏定义,也可以使用volatile
或者__volatile__
是可选的,表示不进行任何优化,否则某些操作可能会被优化掉。- 汇编指令列表, 即要嵌入的汇编指令。每条指令应该被双引号括起来作为字符串,两条指令之前必须以
\n
或者:
作为分隔符,没有添加分隔符的两个字符串将会被合并成为一个字符串。 - 输出操作数,用来指定当前内联汇编程序的输出操作符列表
- 输入操作数,用来指定当前内联汇编语句的输入操作符列表
- 可能影响的寄存器或存储器,用于告知编译器当前内联汇编语句可能会对某些寄存器或内存进行修改,使得编译器在优化时将其因素考虑进去
-
Learn from example
__asm__ __volatile__( "Instruction_l;\ Instruction 2;\ ...\ Instruction_n;" :[out1]"=r"(valuel), [out2]"=r"(value2), ...[outn]"=r"(valuen) :[inl]"r"(valuel),[in2]"r"(value2),...[inn]"r"(valuen) :"r0","r1",..."rn" );
输入操作数与输出操作数
GCC 内联汇编语法的"输入操作数"和"输出操作数"部分用来指定当前内联汇编程序的输入和输出操作符列表
-
遵循的语法如下
-
每一个输入或者输出操作符都由以下3部分组成。
-
方括号[]中的符号名用于将内联汇编程序中使用的操作数(由
%[str]
指定)和此操作符(由[str]
指定)通过同名"字符"绑定起来。除了
%[str]
中明确的符号命名指定外,还可以使用%数字
的方式进行隐含指定。"数字"从0开始,依次表示输出操作数和输入操作数。假设包含"输出操作数"列表中有2个操作数,"输入操作数"列表中有2个操作数,则汇编程序中%0
表示第一个输出操作数,%1
表示第二个输出操作数,%2
表示第一个输入操作数,%3
表示第二个输入操作数。 -
引号中的限制字符串,用于约束此操作数变量的属性,常用的约束如下。
a)字母"r"表示使用编译器自动分配的寄存器来存储该操作数变量;字母"m"表示使用内存地址来存储该操作数变量。如果同时指明"rm",则编译器自动选择最优方案。
b)对于"输出操作数"而言,"="代表输出变量用作输出,原来的值会被新值替换;"+"代表输出变量不仅作为输出,而且作为输入。
**注意:**此约束对不适用于"输入操作数"。
-
圆括号()中的C/C+变量或者表达式。
常用的constraints有以下几个:
- m 内存操作数
- r 寄存器操作数
- i 立即数操作数(整数)
- f 浮点寄存器操作数
- F 立即数操作数(浮点)
-
-
输出操作符之间需使用逗号分隔。
-
可能影响的寄存器或存储器
如果内联汇编中的某个指令会更新某些寄存器的值,则必须在 asm中第三个冒号后的"可能影响的寄存器或存储器"中地指定出这些寄存器,通知GCC编译器不再假定之前存入这些寄存器中的值依然合法。
- 指定出这些寄存器由逗号分隔开,每个寄存器由引号包含住:
:"xl","x2"
- 注意:对于那些已经由"输入操作数"和"输出操作数"部分约束指定了的变量,由于编译器自动分配寄存器,因此编译器知道哪些寄存器会被更新,则无须指定这部分的寄存器。
Example
int sum;
int add1=100;
int add2=200;
//插入汇编代码调用add汇编指令进行加法操作
__asm__ __volatile__(
"add %[dest], %[srcl], %[src2]" //使用add指令,一个目标操作数(命名为dest),
//两个源操作数(分别命名为sre1和src2)
:[dest]"=r"(sum) //将add指令的目标操作数dest和C程序中的sum变量绑定。
:[srcl]"r"(add1),[src2]"r"(add2)//将add指令的源操作数srec1和C程序中的add1变量绑定
//将源操作数 arc2和add2变量绑定
//此内联汇编没有指定可能影响的寄存器或存储器,因此省略第三个冒号
);
汇编中调用C/C++函数
**二进制接口(Abstract Binary Interface,ABI):**ABI描述了应用程序和操作系统之间、应用和它的库之间,以及应用的组成部分之间的接口
- ABI涵盖了如下细节
- 数据类型的大小、布局和对齐。
- 函数调用约定:例如是所有的参数都通过栈传递,还是部分参数通过寄存器传递;哪个寄存器用于哪个函数参数;通过栈传递的第一个函数参数是最先还是最后推到栈上。
- 系统调用的编码和一个应用如何向操作系统进行系统调用。
对于RISC-V汇编程序而言,在汇编程序中调用C/C+语言函数,必须遵照ABI所定义的函数调用规则,即函数参数由寄存器a0 ~a7传递,函数返回由寄存器a0~a1指定.