gcc 内联汇编格式以及详解

用汇编编写的程序虽然运行速度快,但开发速度非常慢,效率也很低。如果只是想对关 键代码段进行优化,或许更好的办法是将汇编指令嵌入到C 语言程序中,从而充分利用
高级语言和汇编语言各自的特点。但一般来讲,在 C 代码中嵌入汇编语句要比 “纯粹” 的汇编语言代码复杂得多,因为需要解决如何分配寄存器,以及如何与C 代码中的变量 相结合等问题。
GCC 提供了很好的内联汇编支持,最基本的格式是:

__asm__("asm statements");

例如:

__asm__("nop"); 
如果需要同时执行多条汇编语句,则应该用"\\n\\t"将各个语句分隔开,例如: 
__asm__( "pushl %%eax \\n\\t"
"movl $0, %%eax \\n\\t"
"popl %eax");

通常嵌入到 C 代码中的汇编语句很难做到与其它部分没有任何关系, 因此更多时候要需
用到完整的内联汇编格式:

__asm__("asm statements" : outputs : inputs : registers-modified);

插入到 C 代码中的汇编语句是以":"分隔的四个部分,其中第一部分就是汇编代码本身, 通常称为指令部,其格式和在汇编语言中使用的格式基本相同。指令部分是必须的,而 其它部分则可以根据实际情况而省略。
在将汇编语句嵌入到C代码中时, 操作数如何与C代码中的变量相结合是个很大的问题。 GCC 采用如下方法来解决这个问题:程序员提供具体的指令,而对寄存器的使用则只需 给出"样板"和约束条件就可以了, 具体如何将寄存器与变量结合起来完全由GCC 和 GAS 来负责。
在 GCC 内联汇编语句的指令部中,加上前缀’%'的数字(如%0,%1)表示的就是需要使用 寄存器的"样板"操作数。

指令部中使用了几个样板操作数,就表明有几个变量需要与寄 存器相结合,这样 GCC 和 GAS 在编译和汇编时会根据后面给定的约束条件进行恰当的 处理。由于样板操作数也使用’%‘作为前缀,因此在涉及到具体的寄存器时,寄存器名前 面应该加上两个’%’,以免产生混淆。

紧跟在指令部后面的是输出部,是规定输出变量如何与样板操作数进行结合的条件,每 个条件称为一个 “约束”,必要时可以包含多个约束,相互之间用逗号分隔开就可以了。

每个输出约束都以’='号开始,然后紧跟一个对操作数类型进行说明的字后,最后是如何 与变量相结合的约束。凡是与输出部中说明的操作数相结合的寄存器或操作数本身,在 执行完嵌入的汇编代码后均不保留执行之前的内容, 这是GCC 在调度寄存器时所使用的 依据。

输出部后面是输入部,输入约束的格式和输出约束相似,但不带’='号。如果一个输入约 束要求使用寄存器, 则GCC 在预处理时就会为之分配一个寄存器, 并插入必要的指令将 操作数装入该寄存器。与输入部中说明的操作数结合的寄存器或操作数本身,在执行完 嵌入的汇编代码后也不保留执行之前的内容。
有时在进行某些操作时,除了要用到进行数据输入和输出的寄存器外,还要使用多个寄 存器来保存中间计算结果, 这样就难免会破坏原有寄存器的内容。 在GCC 内联汇编格式 中的最后一个部分中, 可以对将产生副作用的寄存器进行说明, 以便GCC 能够采用相应 的措施。
下面是一个内联汇编的简单例子: 例4.内联汇编

/* inline.c */
int main()
{
int a = 10, b = 0;
__asm__ __volatile__("movl %1, %%eax; \\n\\r" 
"movl %%eax, %0;"
:"=r"(b) /* 输出 */ 
:"r"(a) /* 输入 */
:"%eax"); /* 不受影响的寄存器 */
printf("Result: %d, %d\\n", a, b); }

上面的程序完成将变量 a 的值赋予变量 b,有几点需要说明:
• 变量 b 是输出操作数,通过%0来引用,而变量a 是输入操作数,通过%1来引用。
• 输入操作数和输出操作数都使用 r 进行约束,表示将变量 a 和变量 b 存储在寄存 器中。输入约束和输出约束的不同点在于输出约束多一个约束修饰符’=’。
• 在内联汇编语句中使用寄存器 eax 时, 寄存器名前应该加两个’%’,即%%eax。内 联汇编中使用%0、%1等来标识变量, 任何只带一个’%‘的标识符都看成是操作数,而 不是寄存器。
• 内联汇编语句的最后一个部分告诉 GCC 它将改变寄存器 eax 中的值,GCC 在处 理时不应使用该寄存器来存储任何其它的值。
• 由于变量 b 被指定成输出操作数,当内联汇编语句执行完毕后,它所保存的值将 被更新。
在内联汇编中用到的操作数从输出部的第一个约束开始编号, 序号从0开始, 每个约束记 数一次,指令部要引用这些操作数时,只需在序号前加上’%'作为前缀就可以了。需要注 意的是,内联汇编语句的指令部在引用一个操作数时总是将其作为32位的长字使用,但 实际情况可能需要的是字或字节,因此应该在约束中指明正确的限定符:
限定符
意义
“m”、“v”、“o”
内存单元
“r”
任何寄存器
“q”
寄存器 eax、ebx、ecx、edx 之一
“i”、“h”
直接操作数
“E"和"F”
浮点数
“g”
任意
“a”、“b”、“c”、“d”
分别表示寄存器 eax、ebx、ecx 和 edx
“S"和"D”
寄存器 esi、edi
“I”
常数(0至31)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值