内联汇编笔记
0x01. 内联汇编代码.
template<> template<typename T> inline
bool QBasicAtomicOps<4>::ref(T &q_value) Q_DECL_NOTHROW
{
T newValue;
int result;
asm volatile("0:\n"
"ldrex %[newValue], [%[q_value]]\n"
"add %[newValue], %[newValue], #1\n"
"strex %[result], %[newValue], [%[q_value]]\n"
"teq %[result], #0\n"
"bne 0b\n"
: [newValue] "=&r" (newValue),
[result] "=&r" (result),
"+m" (q_value)
: [q_value] "r" (&q_value)
: "cc", "memory");
return newValue != 0;
}
内联汇编代码如上所示。
上面是QT 源码中智能指针,累加引用计数值的具体实现源码。原理是通过LDREX与STREX 指令产生barrier,进而实现操作的原子操作(更加具体的实现需要深入ARM 体系结构进一步扩展阅读)。
上述代码功能简述:
a. 将q_value中的值加载到变量newValue(这里在实际的汇编模块里将替换为一个通用寄存器);
b. 将newValue中的值+1后存放在newValue中;
c. 将+1后的结果(存放在newValue中)传递给result(该变量将被另外一个通用寄存器所替换);
d. 检测result中的值是否等于0,如果不相等则跳转到ldrex
这句重新执行。
0x02. 内联汇编的语法.
asm ( assembler template
: output operands /* optional */
: input operands /* optional */
: list of clobbered registers /* optional */
);
-
assembler template
汇编语句的模板,模板可以由一条或多条汇编指令构成,每条指令由双引号包裹,并显式地将换行符(’\n’)添加到指令的末尾; -
: output operands
输出操作数;输出操作数列表中,各个操作数之间由‘,’分隔:
: [newValue] "=&r" (newValue), [result] "=&r" (result),
“+m” (_q_value)在上例中,一共有三个输出操作数:
[newValue] "=&r" (newValue)
和[result] "=&r" (result)
以及"+m" (_q_value)
。其中
[ ]
内指示汇编代码中出现的符号,( )
中对应C 中对应的变量或表达式。=&r
: 标识,当前参数仅通过通用寄存器方式输出。+m
: 标识,该操作数可读可写,是一个任意有效的变量(不影响任何寄存器值)。
-
: input operands
输入操作数; 输入操作数列表中,各个操作数之间由‘,’分隔:
: [_q_value] "r" (&_q_value)
在上例中,一共有一个输入操作数:
其中
[ ]
内指示汇编代码中出现的符号,( )
中对应C 中对应的变量或表达式。
"r"
: 标识,当前输入操作数将使用通用寄存器传递变量值。 -
:list of clobbered registers
操作寄存器的列表,以及特殊指示标识列表。用于指示语句内指定绑定的寄存器列表或者指示一些特殊的提示信息状态,提供给编译。: "cc", "memory"
“cc”:标识当前的汇编代码段的执行结果将对CPU状态寄存器产生影响(该段汇编中使用加法指令add,该指令将产生进位位等,进而影响到CPU状态寄存器。)。需要显式标识,告知编译器,当该段代码执行返回后,编译器需要负责清理并恢复该寄存器。(寄存器的具体名称根据不同CPU体系而变化。)
“memory”:用于指示编译器,当前句块内的操作将影响内存内容。比如输出表达式,
[newValue] "=&r" (newValue)
表示汇编语言中的名为newValue的变量中的值将通过一个通用寄存器返回给C模块中的变量newValue。而上述操作将修改对应堆栈上的变量内容,同时该操作也修改了所指定的通用寄存器的值。