ARM32与ARM64内联汇编破坏描述差异原因咨询
为什么ARM32和ARM64内联汇编的破坏寄存器指定有差异?
这个问题的核心其实是ARM32和ARM64架构下**调用约定(Calling Convention)**以及寄存器分类规则的不同,咱们一步步拆解清楚:
1. 先搞懂两个架构的寄存器使用规则
这是差异的根本原因,不同架构的标准调用约定对寄存器的用途划分完全不一样:
ARM32(遵循AAPCS标准)
r0-r3是**调用者保存(caller-saved)**的临时寄存器,同时负责传递函数的前4个参数r4-r11是**被调用者保存(callee-saved)**寄存器,函数使用前必须保存原值,退出时恢复- 在你的
asm_test函数里,printf的第一个参数(字符串指针)会用r0传递,第二个参数y会用r1传递。你测试时指定破坏r0可行,是因为编译器会避开把y的目标寄存器分配到r0,避免影响后续printf的参数传递;而破坏r1会直接覆盖printf需要的y值,导致结果错误。
ARM64(遵循AArch64 AAPCS标准)
x0-x7是调用者保存的临时寄存器,负责传递前8个函数参数x8专门用于返回值的指针传递,x19-x28是被调用者保存寄存器- 这里
printf的第一个参数用x0传递,第二个参数y用x1传递。如果你声明破坏x0,会直接搞乱printf的字符串参数传递;而破坏x1时,编译器会重新调整y的寄存器分配,确保printf能正确拿到值,所以结果正常。
2. 你的代码里藏着一个关键错误
你写的mov指令顺序搞反了!ARM架构的mov指令格式是mov <目标寄存器>, <源寄存器>,也就是先写目标,再写源。你的代码里:
- ARM32:
mov %[in], %[out]→ 实际是把y的值(初始0)写到x的寄存器里 - ARM64:
mov %w[in], %w[out]→ 同样是反向赋值
你之所以能得到正确结果,完全是因为破坏寄存器的声明让编译器调整了寄存器分配,间接“修正”了这个错误。
3. 正确的写法(不需要显式破坏寄存器)
只要把mov指令的顺序改对,你根本不需要显式声明破坏寄存器——内联汇编的约束已经告诉编译器:[out] "=r"(y)是输出寄存器,[in] "r"(x)是输入寄存器,编译器会自动处理寄存器的使用,不会产生冲突。修正后的代码如下:
#include <arm_neon.h> #include <stdio.h> void asm_test() { int x = 10; int y = 0; #ifdef __aarch64__ asm volatile( "mov %w[out], %w[in]" // 目标在前,源在后 : [out] "=r"(y) : [in] "r"(x) // 无需破坏寄存器声明 ); #else asm volatile( "mov %[out], %[in]" // 目标在前,源在后 : [out] "=r"(y) : [in] "r"(x) // 无需破坏寄存器声明 ); #endif printf("y is %d\n", y); } int main() { asm_test(); // 你原代码里的arm_test是笔误哦 return 0; }
4. 去哪里查官方规则?
你可以直接参考ARM官方的调用约定文档:
- ARM32:搜索「ARM AAPCS」,官方文档里详细定义了寄存器使用、参数传递、栈帧布局的所有规则
- ARM64:搜索「AArch64 AAPCS」,同样包含完整的寄存器分类和调用规范
另外,GCC的官方手册里「Using Assembly Language with GCC」章节,也有针对ARM架构内联汇编的约束、破坏寄存器的详细说明。
内容的提问来源于stack exchange,提问作者ChrisZZ
相关产品推荐
相关产品推荐

