ARM64内联汇编基本写法为asm volatile("instr" : outputs : inputs : clobbers),必须用volatile防优化,约束符仅支持"r"、"w"、"I"等,系统寄存器名全大写且带下划线,多架构需用__aarch64__宏条件编译。

ARM64下用asm volatile写内联汇编的基本写法
ARM64(aarch64)和ARM32(armv7)的内联汇编语法不兼容,直接把x86或旧ARM代码搬过来会编译失败。GCC要求明确指定约束(constraint)、输入/输出操作数,并且必须用asm volatile防止被优化掉——哪怕你只是读一个寄存器状态。
最简可行例子:读取当前异常级别(EL):
unsigned int el;
asm volatile("mrs %0, CurrentEL" : "=r"(el));
注意三点:mrs是ARM64指令;"=r"(el)表示输出到通用寄存器再存入变量;CurrentEL是系统寄存器名,大小写敏感,不能写成currentel或CURRENT_EL。
常见错误:约束符写错、寄存器名拼错、漏写clobber
报错error: impossible constraint in 'asm'通常是因为用了x86风格的"a"(%rax)或"d"(%rdx)约束——ARM64只认"r"(任意通用寄存器)、"w"(64位宽寄存器)、"I"(立即数0–65535)等。用错就直接编译不过。
-
"r"适用于大多数整数运算,但不能保证是W0/W1等特定寄存器 - 想强制用X0–X30,得用
"=r"+"0"匹配约束,比如"=r"(out) : "0"(in) - 修改了特殊寄存器(如
SP、LR)或条件标志(cc),必须在clobber列表里声明:: : : "cc", "sp",否则后续代码可能行为异常 - 系统寄存器名必须全大写且带下划线,比如
MPIDR_EL1,不是mpidr_el1,也不是MPIDREL1
ARM32和ARM64混用时怎么避免编译崩溃
同一个C++项目如果要同时支持ARM32和ARM64,不能靠宏简单包裹asm块——因为指令集、寄存器名、约束规则都不同。GCC不会帮你做翻译。
推荐做法是分文件或用条件编译隔离:
#if defined(__aarch64__)
asm volatile("mrs %0, CNTFRQ_EL0" : "=r"(freq));
#elif defined(__arm__)
asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r"(freq));
#endif
关键点:
-
__aarch64__和__arm__是GCC预定义宏,比__ARM_ARCH_7A__更可靠 - ARM32的
mrc/mcrr指令参数顺序、协处理器编号(p15)必须严格匹配ARM ARM文档 - 别试图在ARM64上用
__arm__宏去编译ARM32汇编——链接阶段就会报undefined reference to `__aeabi_unwind_cpp_pr0'这类符号错误
volatile真的够用?什么时候必须加memory clobber
volatile只阻止编译器重排或删除这条asm,但不保证它和前后C++内存访问的顺序。比如你想测一段代码的精确周期,先读CNTPCT_EL0,执行目标代码,再读一次:
uint64_t t1, t2;
asm volatile("mrs %0, CNTPCT_EL0" : "=r"(t1));
// your code here
asm volatile("mrs %0, CNTPCT_EL0" : "=r"(t2));
这时候编译器可能把// your code here整个优化掉,或者把两次mrs合并——因为它们没数据依赖。解决办法是加"memory" clobber:
asm volatile("mrs %0, CNTPCT_EL0" : "=r"(t1) : : "memory");
这告诉编译器:“这条汇编可能读写任意内存”,从而禁止跨过它的内存访问重排。但代价是性能下降,所以只在真正需要顺序保证时加。
真实场景中容易忽略的是:系统寄存器访问本身不触发内存屏障,memory clobber也替代不了dsb ish这类指令。如果涉及多核同步,光靠asm volatile不够,还得手写dsb + isb。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











