AArch64内联汇编函数移除inline后编译动态库失败的问题咨询
AArch64 GCC 7.3.0内联汇编编译动态库问题
在Linux AArch64架构下使用gcc 7.3.0编译器时遇到内联汇编相关问题:
原有带inline属性的内联函数vcopyq_laneq_u8_inner可正常编译链接为可执行程序,代码如下:
uint8x16_t vcopyq_laneq_u8_inner(uint8x16_t a, const int b, uint8x16_t c, const int d) { uint8x16_t res; __asm__ __volatile__( :"ins %[dst].B[%[dlane]], %[src].B[%[slane]] \n\t" :[dst] "=w"(res) :"0"(a), [dlane]"i"(b), [src]"w"(c), [slane]"i"(d) :); return res; }
现在需要将该函数编译为动态库,移除了inline关键字后编译失败,报错信息如下:
warning: asm operand 2 probably doesn't match constraints warning: asm operand 4 probably doesn't match constraints error: impossible constraint in 'asm'
推测错误原因是内联汇编的"i"约束要求操作数为立即数,但参数b、d是const int变量。目前想到的解决方法是通过256个if-else判断将b和d替换为立即数,但该方式效率极低。现咨询两个问题:
- 为何该函数带
inline属性时可编译为可执行程序,移除后编译动态库却失败? - 是否存在高效方法避免编写大量if-else语句?
问题1解答
当函数带inline属性时,编译器会将函数内联到调用它的代码中。此时如果调用该函数时传入的b和d是编译期可知的立即数,gcc会直接把这些立即数代入内联汇编的"i"约束,满足要求,因此可以正常编译。
而移除inline关键字后,函数作为独立的动态库函数存在,编译器无法预知调用时传入的b和d的值——它们可能是运行时才能确定的变量,而内联汇编的"i"约束强制要求操作数是编译期可见的立即数,此时编译器无法满足这个约束,因此报错。
问题2解答
有两种高效的解决方案,不需要写大量if-else:
方案1:使用ARM NEON内置函数替代内联汇编
直接用ARM官方提供的内置函数实现相同功能,完全避免手写内联汇编的麻烦。gcc会自动将这些内置函数优化为高效的ins指令,代码可读性和可移植性也更好:
#include <arm_neon.h> uint8x16_t vcopyq_laneq_u8_inner(uint8x16_t a, const int b, uint8x16_t c, const int d) { // 提取c的第d个lane的uint8_t值 uint8_t lane_val = vgetq_lane_u8(c, d); // 将该值写入a的第b个lane并返回 return vsetq_lane_u8(lane_val, a, b); }
方案2:编译期分支优化
利用gcc的__builtin_constant_p宏,在编译期判断参数是否为立即数,分别处理两种场景:
#include <arm_neon.h> uint8x16_t vcopyq_laneq_u8_inner(uint8x16_t a, const int b, uint8x16_t c, const int d) { if (__builtin_constant_p(b) && __builtin_constant_p(d)) { // 参数是编译期立即数时,用原内联汇编实现高效指令 uint8x16_t res; __asm__ __volatile__( "ins %[dst].B[%[dlane]], %[src].B[%[slane]] \n\t" : [dst] "=w"(res) : "0"(a), [dlane]"i"(b), [src]"w"(c), [slane]"i"(d) :); return res; } else { // 参数是运行时变量时,用内置函数处理 uint8_t lane_val = vgetq_lane_u8(c, d); return vsetq_lane_u8(lane_val, a, b); } }
这种方式既保留了立即数场景下的高效性,又兼容了运行时变量的情况,无需编写大量分支判断。
内容的提问来源于stack exchange,提问作者SnowDance1997
相关产品推荐
相关产品推荐

