C 表达式中的汇编指令
asm为gcc中的关键字,asm表达式为在C代码中嵌套汇编指令,该表达式只是单纯的替换出汇编代码,并不对汇编代码的含义进行解析。
asm表达式有两种形式,第二种asm-qualifiers包含了goto语句。
第一种形式为常见的用法,AssemblerTemplate和OutputOperands必须存在,其中Clobbers存在需要InputOperands也出现。
asmasm-qualifiers(AssemblerTemplate :OutputOperands [:InputOperands [:Clobbers]]) asmasm-qualifiers(AssemblerTemplate : :InputOperands :Clobbers :GotoLabels)
Qualifiers的类型
volatile,避免编译器的优化inline,内敛限定符,最小的体积goto,包含跳转指令
参数
AssemblerTemplate
-汇编指令模板是包含汇编器指令的文字字符串,编辑器替换引用输入,编译器不会解析该指令的含义。OutputOperands
-由AssemblerTemplate中的指令修改的C变量的逗号分隔列表,允许使用空列表。InputOperands
-由AssemblerTemplate中的指令读取的C变量的逗号分隔列表,允许使用空列表。Clobbers
-用逗号分隔的寄存器列表或由AssemblerTemplate修改的值,不能出现在OutputOperands和InputOperands中被提及,允许使用空列表。GotoLabels
-当使用asm的goto形式时,此部分包含AssemblerTemplate中的代码可能跳转到的所有C标签的列表。
AssemblerTemplate
汇编指令由一个字符串给出,多条汇编指令结合在一起使用的时候,中间以\r\t隔开,如
asm("inc%0\n\tinc%0":"=r"(res):"0"(res)); /APP #11"asm.c"1 inc%rax inc%rax #0""2 /NO_APPs
需要转义的字符:%,=,{,},|
故在ATT汇编中,对寄存器进行操作的需要双%%,如inc%%rax.
OutputOperands
操作数之间用逗号分隔。每个操作数具有以下格式:
[[asmSymbolicName]]constraint(cvariablename)
asmSymbolicName
-为操作数指定名称,格式为%[name]
c//res=numasm("movq%[num],%[res]":[res]"=r"(res):[num]"m"(num));
-如果未指定名称使用数字,从output域开始,第一个参数为%0,一次类推,这里的res为%0,num为%1
c//res=numasm("movq%1,%0":"=r"(res):"m"(num));constraint
-一个字符串常量,用于指定对操作数的存储的约束,需要以"="或"+"开头cvariablename
-指定一个C左值表达式来保存输出,通常是一个变量名。括号是语法的必需部分
第一个参数为增加可读性使用的,现在我们有代码如下
int64_tres; int64_tnum=1; asm("movq%[num],%[res]":[res]"=r"(res):[num]"m"(num)); asm("movq%1,%0":"=r"(res):"m"(num)); asm("movq%1,%0":"=m"(res):"m"(num)); asm("movq%1,%0":"=r"(res):"r"(num)); //对应的汇编代码,只保留asm表达式中的代码 #13"asm.c"1 movq-16(%rbp),%rax//asm-1 #0""2 /NO_APP /APP #15"asm.c"1 movq-16(%rbp),%rax//asm-2 #0""2 /NO_APP /APP #17"asm.c"1 movq-16(%rbp),-8(%rbp)//asm-3 #0""2 /NO_APP /APP #19"asm.c"1 movq%rax,%rax//asm-4 #0""2 /NO_APP
- 使用名称替换和数字替换效果一样,见asm-1和asm-2约束的用法,这里使用比较简单通用的的两种情况,r为通过寄存器寻址操作,m通过内存寻址操作,所以看到当
- 约束了r就对应寄存器的操作。
- 结果保存在res也就是cvariablename中
InputOperands
输入操作数使C变量和表达式中的值可用于汇编代码。
[[asmSymbolicName]]constraint(cexpression)
asmSymbolicName和输出列表的用法完全一致
constraint约束不能使用=和+.可以使用"0",这表明在输出约束列表中(从零开始)的条目,指定的输入必须与输出约束位于同一位置。
int64_tres=3; int64_tnum=1; asm("addq%1,%0":"=g"(res):"0"(num)); //输入输出位置相同 movq$3,-8(%rbp) movq$1,-16(%rbp) movq-16(%rbp),%rax /APP #32"asm.c"1 addq%rax,%rax #0""2 /NO_APP
- cexpression可以不为左值,作为汇编表达式的输入值即可
- Clobbers
破坏列表,主要用于指示编译器生成的汇编指令。
从asm表达式中看到输出操作数中列出条目的更改编译器是可以确定的,但内联汇编代码可能不仅对输出进行了修改。例如,计算可能需要其他寄存器,或者处理器可能会由于特定汇编程序指令而破坏寄存器的值。为了将这些更改通知编译器,在Clobber列表中列出这些会产生副作用的条目。破坏列表条目可以是寄存器名称,也可以是特殊的破坏列表项(在下面列出)。每个内容列表条目都是一个字符串常量,用双引号引起来并用逗号分隔。
寄存器
```c asmvolatile("movc3%0,%1,%2" :/*Nooutputs.*/ :"r"(from),"r"(to),"g"(count) :"%rbx","%rcx","%rdx","memory"); /APP #25"asm.c"1 movc3%rax,%r8,-72(%rbp) #0""2 /NO_APP ``` 可以看到使用到了rax寄存器,然后修改程序在Clobbers增加%rax,结果如下 ```c asmvolatile("movc3%0,%1,%2" :/*Nooutputs.*/ :"r"(from),"r"(to),"g"(count) :"%rax","%rbx","%rcx","%rdx","memory"); /APP #25"asm.c"1 movc3%r8,%r9,-72(%rbp) #0""2 /NO_APP ```
特殊破坏列表项
-"cc",表示汇编代码修改了标志寄存器
-"memory",为了确保内存中包含正确的值,编译器可能需要在执行asm之前将特定的寄存器值刷新到内存中
编译器为了破坏列表项的值受到破坏,当这些条目是寄存器时,不对其进行使用;为特殊参数时,重新刷新得到最新的值。
约束
一些基础的约束
约束名 | 说明 |
---|---|
whitespace | 空白字符被忽略 |
m | 允许使用内存操作数,以及机器通常支持的任何类型的地址 |
o | 允许使用内存操作数,但前提是地址是可偏移的 |
V | 允许使用内存操作数,不可偏移的内存地址,与"o'互斥 |
r | 允许在通用寄存器中使用的寄存器操作数,其中可以指定寄存器,如a(%rax),b(%rbx) |
i | 允许使用立即整数操作数 |
n | 允许使用具有已知数值的立即整数操作数,‘I',‘J',‘K',…‘P'更应该使用n |
F | 允许使用浮点立即数 |
g | 允许使用任何寄存器,内存或立即数整数操作数,但非通用寄存器除外 |
X | 允许任何操作数,‘0',‘1',‘2',…‘9' |
p | 允许使用有效内存地址的操作数 |
标识符约束
标识符 | 说明 |
---|---|
= | 表示此操作数是由该指令写入的:先前的值将被丢弃并由新数据替换 |
+ | 表示该操作数由指令读取和写入 |
& | 表示(在特定替代方法中)此操作数是早期指令操作数,它是在使用输入操作数完成指令之前写入的,故输入操作数部分不能分配与输出操作数相同的寄存器 |
% | 表示该操作数与后续操作数的可交换指令 |
内核示例
x86的内存屏障指令。
//避免编译器的优化,声明此处内存可能发生破坏 #definebarrier()asmvolatile("":::"memory") //在32位的CPU下,lock指令为锁总线,加上一条内存操作指令就达到了内存屏障的作用,64位的cpu已经有新增的*fence指令可以使用 //mb()执行一个内存屏障作用的指令,为指定CPU操作;破坏列表声明ccmemory指示避免编译器进行优化 #ifdefCONFIG_X86_32 #definemb()asmvolatile(ALTERNATIVE("lock;addl$0,-4(%%esp)","mfence",\ X86_FEATURE_XMM2):::"memory","cc") #definermb()asmvolatile(ALTERNATIVE("lock;addl$0,-4(%%esp)","lfence",\ X86_FEATURE_XMM2):::"memory","cc") #definewmb()asmvolatile(ALTERNATIVE("lock;addl$0,-4(%%esp)","sfence",\ X86_FEATURE_XMM2):::"memory","cc") #else #definemb()asmvolatile("mfence":::"memory") #definermb()asmvolatile("lfence":::"memory") #definewmb()asmvolatile("sfence":::"memory") #endif
x86下获取current的值
DECLARE_PER_CPU(structtask_struct*,current_task); #definethis_cpu_read_stable(var)percpu_stable_op("mov",var) static__always_inlinestructtask_struct*get_current(void) { returnthis_cpu_read_stable(current_task); } #definepercpu_stable_op(op,var)\ ({\ typeof(var)pfo_ret__;\ switch(sizeof(var)){\ case8:\ asm(op"q"__percpu_arg(P1)",%0"\ :"=r"(pfo_ret__)\ :"p"(&(var)));\ break;\ }\ pfo_ret__;\ })
current_task为一个structtask_struct类型的指针,追踪宏调用,在x86-64下命中了case8:的汇编代码,展开的代码为
asm("mov""q""%%""gs"":""%""P1"",%0":"=r"(pfo_ret__):"p"(&(current_task))); //变换一下为 asm("movq%%gs:%P1,%0":"=r"(pfo_ret__):"p"(&(current_task)));
这行代码的含义为将约束输入部分必须为有效的地址(p约束),将CPUid(通过段寄存器gs和偏移通过GDT得到,这里后文分析了)通过寄存器(r约束)赋值给pfo_ret__.
参考
GCC文档
C语言ASM汇编内嵌语法zz
总结
以上所述是小编给大家介绍的C表达式中的汇编指令,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。