使用GCC扩展内联汇编的例子如下:
#define read_cr0() ({ \
unsigned int __dummy; \
__asm__( \
"movl %%cr0,%0\n\t" \
:"=r" (__dummy)); \
__dummy; \
})
它代表什么含义呢?这需要从其基本格式讲起。GCC扩展内联汇编的基本格式是:
asm [volatile] ( Assembler Template
: Output Operands
[ : Input Operands
[ : Clobbers ] ])
其中,__asm__ 表示汇编代码的开始,其后可以跟 __volatile__(这是可选项),其含义是避免 “asm” 指令被删除、移动或组合,在执行代码时,如果不希望汇编语句被 gcc 优化而改变位置,就需要在 asm 符号后添加 volatile 关键词:asm volatile(...);或者更详细地说明为:__asm__ __volatile__(...);然后就是小括弧,括弧中的内容是具体的内联汇编指令代码。 "
:"=r" (__dummy)
“=r”表示相应的目标操作数(指令部分的%0)可以使用任何一个通用寄存器,并且变量__dummy 存放在这个寄存器中,但如果是:
:“=m”(__dummy)
“=m”就表示相应的目标操作数是存放在内存单元__dummy中。表示约束条件的字母很多,下表给出几个主要的约束字母及其含义:
字母 | 含义 |
m, v, o | 内存单元 |
R | 任何通用寄存器 |
Q | 寄存器eax, ebx, ecx,edx之一 |
I, h | 直接操作数 |
E, F | 浮点数 |
G | 任意 |
a, b, c, d | 寄存器eax/ax/al, ebx/bx/bl, ecx/cx/cl或edx/dx/dl |
S, D | 寄存器esi或edi |
I | 常数(0~31) |
输入部分(input operand list):输入部分与输出部分相似,但没有“=”。如果输入部分一个操作数所要求使用的寄存器,与前面输出部分某个约束所要求的是同一个寄存器,那就把对应操作数的编号(如“1”,“2”等)放在约束条件中。在后面的例子中,可看到这种情况。修改部分(clobber list,也称 乱码列表):这部分常常以“memory”为约束条件,以表示操作完成后内存中的内容已有改变,如果原来某个寄存器的内容来自内存,那么现在内存中这个单元的内容已经改变。乱码列表通知编译器,有些寄存器或内存因内联汇编块造成乱码,可隐式地破坏了条件寄存器的某些位(字段)。 注意,指令部分为必选项,而输入部分、输出部分及修改部分为可选项,当输入部分存在,而输出部分不存在时,分号“:“要保留,当“memory”存在时,三个分号都要保留,例如
#define __cli() __asm__ __volatile__("cli": : :"memory")
下面是一个例子:
int count=1;
int value=1;
int buf[10];
void main()
{
asm(
"cld nt"
"rep nt"
"stosl"
:
: "c" (count), "a" (value) , "D" (buf[0])
: "%ecx","%edi"
);
}
得到的主要汇编代码为:
movl count,%ecx
movl value,%eax
movl buf,%edi
#APP
cld
rep
stosl
#NO_APP
cld,rep,stos这几条语句的功能是向buf中写上count个value值。冒号后的语句指明输入,输出和被改变的寄存器。通过冒号以后的语句,编译器就知道你的指令需要和改变哪些寄存器,从而可以优化寄存器的分配。其中符号"c"(count)指示要把count的值放入ecx寄存器。类似的还有:
a eax
b ebx
c ecx
d edx
S esi
D edi
I 常数值,(0 - 31)
q,r 动态分配的寄存器
g eax,ebx,ecx,edx或内存变量
A 把eax和edx合成一个64位的寄存器(use long longs)
也可以让gcc自己选择合适的寄存器。如下面的例子:
asm("leal (%1,%1,4),%0"
: "=r" (x)
: "0" (x)
);
这段代码到的主要汇编代码为:
movl x,%eax
#APP
leal (%eax,%eax,4),%eax
#NO_APP
movl %eax,x
几点说明:
asm("leal (%%ebx,%%ebx,4),%0"
: "=r" (x)
: "0" (x)
);
注意要使用两个%,因为一个%的语法已经被%n用掉了。
参考: