4.5.3.1 嵌入式汇编的基本格式
具有输入和输出参数的嵌入式汇编的基本格式为:
asm(“汇编语句”
: 输出寄存器
: 输入寄存器
: 会被修改的寄存器 );
这里给出一个例子,然后进行详细解说。
asm("cld\n\t" "rep\n\t" "stol"
: /* 没有输出寄存器 */
: "c"(count-1), "a"(fill_value), "D"(dest)
: "%ecx", "%edi");
我们一点一点来看。
asm("cld\n\t" "rep\n\t" "stosl"
这三句是通常的汇编语句,以清方向位,重复保存值。
: /* 没有输出寄存器 */
说明这段嵌入汇编程序没有用到输出寄存器。
: "c"(count-1), "a"(fill_value), "D"(dest)
这里我们将 count-1 的值加载到 ecx 寄存器中,fill_value 加载到 eax 中,dest 放到 edi 中。为什
么要让 gcc 编译程序去做这样的加载而不让我们自己做呢?因为 gcc 在它进行寄存器分配时可以进行某些
优化,例如 fill_value 值可能已经在 eax 中。如果是在一个循环语句中的话,gcc 就可能在整个循环操作
中保留 eax,这样就可以在每次循环中少用一个 movl 语句。
: "%ecx", "%edi");
这句告诉 gcc 这些寄存器中的值已经改变了。
很古怪吧?不过在 gcc 知道你拿这些寄存器做些什么后,这确实能够对 gcc 的优化操作有所帮助。以
下是一些你可能会用到的寄存器加载代码:
a
eax,
b
ebx,
c
ecx,
d
edx,
S
esi,
D
edi,
q,r 动态分配的积存器,
g
eax,ebx,ecx,edx 或内存变量,
A
eax 与 edx 联合(64 位),
内核代码 linux/kernel/
71
m
内存, o
内存并可加偏移值,
I
常数(0-31),
J
常数(0-63),
K
常数(0-255) L
常数(0-65535), M
常数(0-3),
N
常数(0-255)(1 字节常数),
O
常数(0-32) 。
下面的例子不是让你自己指定哪个变量使用哪个寄存器,而是让 gcc 为你选择。
asm("leal (%1, %1, 4), %0"
: "=r"(y)
: "0"(x));
这个例子可以非常快地将 x 乘 5。[ leal (r1, r2,4), r3 语句表示 r1+r2*4 ? r3 ]
其中"%0","%1"是指gcc自动分配的寄存器。 编号的顺序是从输出寄存器序列从左到右从上到下以"%0"
开始。所以这里"%1"代表输入值 x 要放入的寄存器,"%0"表示输出值寄存器。输出寄存器代码前一定要加
等于号。如果输入寄存器的代码是 0 或为空时,则说明使用与相应输出一样的寄存器。所以如果 gcc 将 r
指定为 eax 的话,那么上面汇编语句的含义即为:
"leal (eax,eax,4), eax"
注意:在执行时如果汇编语句不希望被 gcc 优化而挪动地方,就需要在 asm 符号后面添加 volatile
关键词:
asm volatile (……);
或者更详细的说明为:
__asm__ __volatile__ (……);
下面是几个简单例子。
#define disable() __asm__ __volatile__ ("cli");
#define enable() __asm__ __volatile__ ("sti");
#define times3(arg1, arg2) \
__asm__ ( \
"leal (%0, %0 2), %0" \
: "=r" (arg2) \
: "0" (arg1) );