树莓派4 64位Linux下C内嵌ARMV8汇编双精度浮点加法报错求解
报错根本原因
你遇到的编译错误是GCC内嵌汇编约束和ARMv8指令语法不匹配导致的,具体原因如下:
- 你使用的
g约束允许编译器将变量分配到通用寄存器、浮点寄存器、内存三个位置中的任意一个。第一段代码编译时,编译器给输出操作数%0(对应result变量)分配了通用寄存器x0,而ARMv8的str指令存储浮点寄存器到内存时,要求地址必须放在方括号内,正确语法是str d2, [x0],但直接替换%0生成的指令是str d2, x0,不符合语法规范,因此汇编器抛出寻址模式错误。 - 你的代码还存在两个隐藏隐患:
- 硬编码使用了
d1、d2浮点寄存器,但没有在内嵌汇编的破坏列表(clobber list)中声明,GCC不知道这两个寄存器被修改,如果上下文代码用它们存储其他值,会出现不可预期的运行错误。 ldr是内存加载指令,如果%1、%2被编译器分配到寄存器,生成的ldr d1, xn同样会触发寻址错误,你第二段代码能正常运行完全是巧合。
- 硬编码使用了
第二段代码正常运行的原因
第二段代码中你没有使用输出操作数%0,而是将计算结果写入了输入操作数%2对应的d2变量位置,编译时编译器刚好给%2分配了内存地址,生成的指令是符合语法的str d2, [地址寄存器],因此可以正常运行。但这个实现是不可靠的:优化等级变更、上下文代码变动都可能导致编译失败,同时你直接修改了输入变量d2的原始值,也不符合你最初把结果存入result的需求。
符合要求的最简实现
如果你希望自行选择寄存器存储操作数,推荐两种规范写法:
方案1:自行处理加载存储,用内存约束确保操作数在内存中
#include <stdio.h> int main() { double d1 = 0.34543; double d2 = 1.0; double result = 0; asm volatile( "ldr d0, %1\n\t" "ldr d1, %2\n\t" "fadd d0, d0, d1\n\t" "str d0, %0" : "=m" (result) // m约束:强制变量放在内存,编译器自动生成方括号 : "m" (d1), "m" (d2) : "d0", "d1" // 声明这两个寄存器被汇编代码修改 ); printf("%f + %f = %f", d1, d2, result); return 0; }
方案2:让GCC自动分配浮点寄存器,代码更简洁
如果你不需要手动控制加载存储流程,可以用浮点寄存器约束让GCC自动完成变量和寄存器的映射,代码更短且可靠性更高:
#include <stdio.h> int main() { double d1 = 0.34543; double d2 = 1.0; double result = 0; asm volatile("fadd %0, %1, %2" : "=w"(result) // w约束:分配AArch64浮点寄存器存储输出 : "w"(d1), "w"(d2) // 输入也用浮点寄存器存储 ); printf("%f + %f = %f", d1, d2, result); return 0; }
内容的提问来源于stack exchange,提问作者user8899928
相关产品推荐
相关产品推荐

