@typeof

内联汇编简介

内联汇编用于在C程序中直接嵌入汇编代码,可用于编写使用C/C++语句无法实现的功能(当然,最好先去看一下 GCC 文档,尤其是__builtin类函数)

GCC 默认使用 AT&T 语法。AT&T 语法有以下不同。

基本语法

内联汇编分为基本内联汇编和扩展内联汇编两种。
基本内联汇编:asm [限定符](汇编语句);
扩展内联汇编:`asm 限定符;

限定符常用于内联汇编。volatile指示编译器不要自动优化(经常在内联汇编上出问题)这段汇编。
基本内联汇编将汇编语句直接插入程序中(可能会被自动“优化”掉),而编译器会处理扩展内联汇编,自动分配使用的寄存器,将汇编的输入与输出和C语言变量绑定,避免汇编语句干扰变量和程序正常执行,并且可以生成更短、性能更好的代码。因此,除非编写裸函数(编译器不生成函数对应的调用与返回序列)等特殊用途,大部分情况下建议使用扩展内联汇编。

而扩展内联汇编中,汇编语句扩展为汇编模板,此时,编译器就需要分配寄存器,并在汇编语句两侧生成可能需要的额外指令,然后填上汇编模板中的格式串。

如果需要在汇编语句或模板中使用多个汇编指令,可以使用 \n 或者 ; 分隔。

约束模板

扩展内联汇编可指定输入与输出,而将汇编语句与C变量绑定,就需要使用约束模板。
约束模板格式:"[约束]"(值)
常用约束如下:

操作数的大小需要与所编写的汇编指令所需的大小相符,否则会导致编译失败。

此外,在不同架构上有不同的约束符以指定特定寄存器,可将值放入指定位置,或者从指定位置提取值。对于x86与amd64,下列约束可指定特定寄存器:

在汇编模板中,为了能正确地与C程序互操作,应避免直接使用寄存器名与已定义的符号(如变量名)。

修饰列表(Clobber)

当汇编模板中的汇编语句修改了输入输出约束以外的寄存器或内存,则需要在clobbered列表中指定,以告知编译器此段指令完成后,相应寄存器、内存值已被改写,不可使用。如果指定多个目标,则使用逗号分隔各个字符串。

什么时候需要clobber,clobber什么?
凡是被修改(无论显式或隐式)的寄存器,并且未出现于输入输出约束中,则需要添加此寄存器。
如果输入输出约束中的寄存器出现在 clobber 列表中,则编译器会报告“无法满足约束”的错误。
如果此段汇编完成后以某种形式修改了输入输出约束以外的内存,则需要添加"memory"至此列表中。如果改变了标志位寄存器,则为 cc

汇编模板

在基本汇编基础上,汇编模板通过将操作数模板化,并在编译时替换,增加了灵活性,有利于C语言与汇编的正确互操作。
常用模板:

例子

in指令

asm volatile("inb %1,%0":"=a"(ret):"Nd"(port):)

其中,N指示操作数为无符号8位整数。inout指令要求指定的端口为立即数或dx寄存器。

交换两个值(xchg指令)

asm volatile("xchg %1,%0":
"=r"(a):
"m"(b),"0"(a):
"memory"
);

注:x86与x86-64的所有指令均不支持两个操作数均为内存。
这里使用了引用约束,"0"(a[i])中,寄存器与前面的"=r"(a[i])使用的相同。

报错

gcc 对于有错误的内联汇编的报错能力相比于 C 语言本身差了很多,例如不能准确报告不能满足约束的原因。如果错误发生在汇编器阶段,那么汇编器多半只会给出一个含义难以理解、模棱两可的错误信息,需要通过仔细检查汇编(内联汇编多半不会很长)来确定可能的错误原因,包括操作数大小的匹配、操作数类型是否合法、约束的正确性等。

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »