一、内联汇编
在c语言中可以直接调用内联汇编,提高代码的运行效率,或者有时需要调用特殊的汇编指令。(比如ldrex/strex实现互斥访问),比如一些平台的特定指令在c中没有对应的操作等等,这些都需要内联汇编。
参考文章:
① GNU C扩展汇编
② ARM GCC 内嵌(inline)汇编手册
③ C内联汇编
- c语言实现的加法:
int add(int a, int b)
{
return a+b;
}
反汇编后,
00010404 <add>:
10404: b480 push {r7}
10406: b083 sub sp, #12
10408: af00 add r7, sp, #0
1040a: 6078 str r0, [r7, #4]
1040c: 6039 str r1, [r7, #0]
1040e: 687a ldr r2, [r7, #4]
10410: 683b ldr r3, [r7, #0]
10412: 4413 add r3, r2 //实现加法的只有这一条,但是需要做入栈,出栈的操作
10414: 4618 mov r0, r3
10416: 370c adds r7, #12
10418: 46bd mov sp, r7
1041a: f85d 7b04 ldr.w r7, [sp], #4
1041e: 4770 bx lr
- 使用汇编函数实现的加法:
.text // 放在代码段
.global add // 实现全局函数add
.thumb // 使用thumb指令, main.c默认使用thumb指令, 所以这里也使用thumb指令
add:
add r0, r0, r1
bx lr
根据ATPCS规则,main调用add,会把第一个参数存入r0,第二个r1,这样就没有入栈和出栈的操作了,比较高效。
其实还有另一种语法可以实现汇编,在c语言中调用内联汇编。
二、内联汇编语法
asm asm-qualifiers ( AssemblerTemplate
: OutputOperands
[ : InputOperands
[ : Clobbers ] ])
asm asm-qualifiers ( AssemblerTemplate
: OutputOperands
: InputOperands
: Clobbers
: GotoLabels)
- asm:也可以写作"__asm__",表示这是一段内联汇编
- asm-qualifiers:有3个取值,volatile、inline、goto。其中volatile是告诉编译器不要随意优化这段代码。
- AssemblerTemplate:汇编指令内容,用双引号包含起来,用\n来分开每一条指令
- OutputOperands:输出操作数,输出的结果保存在哪里。
[ [asmSymbolicName] ] constraint (cvariablename)
- asmSymbolicName是符号名,随便取,也可以省略。
- constraint表示约束
- cvariablename:c语言的表两名
constraint表示约束,有如下常用的值
constraint | 描述 |
---|---|
m | memory operand,表示要传入有效的地址,只要CPU能支持该地址,就可以传入 |
r | register operand,寄存器操作数,使用寄存器来保存这些操作数 |
i | imediate integer operand,表示可以传入一个立即数 |
constraint前还可以加上一些修饰符,比如"=r"、"+r"、"=&r"
constraint Modifier Characters | 描述 |
---|---|
= | 表示内联汇编会修改这个操作数,即:写 |
+ | 这个操作数即被读,也被写 |
& | 它是一个earlyclobber操作数 |
-
InputOperands:输入操作数,内联汇编执行前,输入的数据保存在哪里
[ [asmSymbolicName] ] constraint (cexpression)
- asmSymbolicName是符号名,随便取,也可以省略。
- constraint表示约束,跟OutputOperands类似。
- cexpression:c语言的表两名
-
Clobbers:对于OutputOperands所涉及的寄存器、内存,肯定做了修改,必须声明会被修改的寄存器
常用的cc、memory意义如下
Clobbers | 描述 |
---|---|
cc | 表示汇编代码会修改“flags register” |
memory | 表示汇编代码中,除了InputOperands和OutputOperands中指定的之外,还会读写更多的内存 |
三、内联函数实现的加法
int add(int a, int b)
{
int sum;
__asm__ volatile (
"add %0, %1, %2" //把第1、2个操作数相加,放入第0个操作数,就是a+b放入sum
:"=r"(sum)
:"r"(a), "r"(b)
:"cc"
);
return sum;
}
- earlyclobber
在OutpuOperands中经常有“&”符号,表示告诉编译器单独分配一个寄存器,不要和输入操作数用同一个寄存器。