ARM GCC内嵌汇编

ARM 142浏览

http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Extended-Asm

http://hi.baidu.com/wypnewbie/blog/item/563916ed2fe0c736269791ba.html

关于这篇文档

对于基于ARM的RISC处理器,GNU C编译器提供了在C代码中内嵌汇编的功能。这种非常酷的特性提供了C代码没有的功能,比如手动优化软件关键部分的代码、使用相关的处理器指令。

这里设想了读者是熟练编写ARM汇编程序读者,因为该片文档不是ARM汇编手册。同样也不是C语言手册。

这篇文档假设使用的是GCC 4 的版本,但是对于早期的版本也有效。

GCC asm 声明

让我们以一个简单的例子开始。就像C中的声明一样,下面的声明代码可能出现在你的代码中。

/* NOP 例子 */
asm("mov r0,r0");

该语句的作用是将r0移动到r0中。换句话讲他并不干任何事。典型的就是NOP指令,作用就是短时的延时。

请接着阅读和学习这篇文档,因为该声明并不像你想象的和其他的C语句一样。内嵌汇编使用汇编指令就像在纯汇编程序中使用的方法一样。可以在一个asm声明中写多个汇编指令。但是为了增加程序的可读性,最好将每一个汇编指令单独放一行。

asm(
"mov r0, r0nt"
"mov r0, r0nt"
"mov r0, r0nt"
"mov r0, r0"
);

换行符和制表符的使用可以使得指令列表看起来变得美观。你第一次看起来可能有点怪异,但是当C编译器编译C语句的是候,它就是按照上面(换行和制表)生成汇编的。到目前为止,汇编指令和你写的纯汇编程序中的代码没什么区别。但是对比其它的C声明,asm的常量和寄存器的处理是不一样的。通用的内嵌汇编模版是这样的。

asm(code : output operand list : input operand list : clobber list);

汇编和C语句这间的联系是通过上面asm声明中可选的output operand listinput
operand list
Clobber list后面再讲。

下面是将C语言的一个整型变量传递给汇编,逻辑左移一位后在传递给C语言的另外一个整型变量。

/* Rotating bits example */
asm("mov %[result], %[value], ror #1" : [result] "=r" (y) : [value] "r" (x));

每一个asm语句被冒号(:)分成了四个部分。

l 汇编指令放在第一部分中的“”中间。

"mov %[result], %[value], ror #1"

l 接下来是冒号后的可选择的output operand list,每一个条目是由一对[](方括号)和被他包括的符号名组成,它后面跟着限制性字符串,再后面是圆括号和它括着的C变量。这个例子中只有一个条目。

[result] "=r" (y)

l 接着冒号后面是输入操作符列表,它的语法和输入操作列表一样

[value] "r" (x)

l 破坏符列表,在本例中没有使用

就像上面的NOP例子,asm声明的4个部分中,只要最尾部没有使用的部分都可以省略。但是有有一点要注意的是,上面的4个部分中只要后面的还要使用,前面的部分没有使用也不能省略,必须空但是保留冒号。下面的一个例子就是设置ARM
Soc
CPSR寄存器,它有input但是没有output operand

asm("msr cpsr,%[ps]" : : [ps]"r"(status))

即使汇编代码没有使用,代码部分也要保留空字符串。下面的例子使用了一个特别的破坏符,目的就是告诉编译器内存被修改过了。这里的破坏符在下面的优化部分在讲解。

asm("":::"memory");

为了增加代码的可读性,你可以使用换行,空格,还有C风格的注释。

asm("mov %[result], %[value], ror #1"

: [result]"=r" (y) /* Rotation result. */
: [value]"r" (x) /* Rotated value. */
: /* No clobbers */
);

在代码部分%后面跟着的是后面两个部分方括号中的符号,它指的是相同符号操作列表中的一个条目。

%[result]表示第二部分的C变量y%[value]表示三部分的C变量x

符号操作符的名字使用了独立的命名空间。这就意味着它使用的是其他的符号表。简单一点就是说你不必关心使用的符号名在C代码中已经使用了。在早期的C代码中,循环移位的例子必须要这么写:

asm("mov %0, %1, ror #1" : "=r" (result) : "r" (value))

在汇编代码中操作数的引用使用的是%后面跟一个数字,%1代表第一个操作数,%2代码第二个操作数,往后的类推。这个方法目前最新的编译器还是支持的。但是它不便于维护代码。试想一下,你写了大量的汇编指令的代码,要是你想插入一个操作数,那么你就不得不从新修改操作数编号。

http://hi.baidu.com/yeyingxian/blog/item/57fb553427b4614e241f14c7.html

几个简单的例子
{
uint32_t __hi;
uint32_t __lo;
uint32_t __result;
asm("smull %0, %1, %3, %4nt"
"movs %0, %0, lsr %5nt"
"adc %2, %0, %1, lsl %6"
: "=&r" (__lo), "=&r" (__hi), "=r" (__result)
: "%r" (x), "r" (y),
"M" (SCALEBITS), "M" (32 - (SCALEBITS))
: "cc");
}

static INLINE real_t _MulHigh(real_t x, real_t y)
{
uint32_t __lo;
uint32_t __hi;
asm("smullt%0, %1, %2, %3"
: "=&r"(__lo),"=&r"(__hi)
: "%r"(x),"r"(y)
: "cc");
return __hi;
}

static __inline__ int MULSHIFT32(int x, int y)
{
int zlow;
__asm__ volatile ("smull %0,%1,%2,%3" : "=&r" (zlow), "=r" (y) : "r" (x),
"1" (y) : "cc");
return y;
}

1、asm不是ANSI C的标准关键字,应改为__asm__

2、asm后面可以加volatile或者__volatile__,告诉编译器不要优化asm块中的内容。默认情况下,当输出结果不使用时GCC会删除asm中的内容,或者会打乱指令顺序进行优化。

3、带有C/C++表达式的内联汇编格式为:
__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);

4、约束符含意
`r'
A register operand is allowed provided that it is in a general
register.
`0', `1', `2', ... `9'
An operand that matches the specified operand number is allowed.
If a digit is used together with letters within the same
alternative, the digit should come last.
`='
Means that this operand is write-only for this instruction: the
previous value is discarded and replaced by output data.
`+'
Means that this operand is both read and written by the
instruction.
`&'
Means (in a particular alternative) that this operand is an
"earlyclobber" operand, which is modified before the instruction is
finished using the input operands. Therefore, this operand may
not lie in a register that is used as an input operand or as part
of any memory address.
`%'
Declares the instruction to be commutative for this operand and the
following operand. This means that the compiler may interchange
the two operands if that is the cheapest way to make all operands
fit the constraints. GCC can only handle one commutative pair in
an asm; if you use more, the compiler may fail.
`M'
Integer in the range 0 to 32

5、 If your assembler instruction can alter the condition code register,
add `cc' to the list of clobbered registers. GCC on some machines
represents the condition codes as a specific hardware register; `cc'
serves to name this register. On other machines, the condition code is
handled differently, and specifying `cc' has no effect. But it is
valid no matter what the machine.