Android Studio 2024年12月版内联汇编报错:invalid operand for inline asm constraint 'i'
问题:内联汇编
bfi指令编译报错:invalid operand for inline asm constraint 'i' 数年前编写的内联汇编函数曾可正常运行,但在2024年12月最新版本的Android Studio中编译时出现如下报错:
error: invalid operand for inline asm constraint 'i'
涉及的bfi内联函数代码如下:
static __attribute__((always_inline)) uint32_t bfi(uint32_t dst, uint32_t src, int lsb, int n) { __asm ( "bfi %w[dst], %w[src], %[lsb], %[n]" : [dst]"+r"(dst) : [src]"r"(src), [lsb]"i"(lsb), [n]"i"(n) : ); return dst; }
调用时传入的是编译时常量,示例代码:
static __attribute__((always_inline)) uint16_t procPixel_ia(uint32_t p) { p = bfi(p, p, 3, 16); p = bfi(p, p, 2, 11); p >>= 8; return (uint16_t) p; }
更换旧版NDK也无法解决,需指出问题所在或相关变更。
问题原因与解决办法
核心问题
GCC/Clang对i约束的检查变得更严格:虽然调用时传入的是常量,但函数参数lsb和n的类型为int,编译器无法在函数定义层面确认它们是编译时常量,因此拒绝将其作为i约束的操作数(i约束要求操作数必须是立即数/编译期可确定的常量)。
解决办法
标记参数为编译期常量
如果使用C++,可以将函数声明为constexpr,让编译器明确参数是编译期常量:static constexpr __attribute__((always_inline)) uint32_t bfi(uint32_t dst, uint32_t src, int lsb, int n) { __asm ( "bfi %w[dst], %w[src], %[lsb], %[n]" : [dst]"+r"(dst) : [src]"r"(src), [lsb]"i"(lsb), [n]"i"(n) : ); return dst; }如果用C语言,可改用宏定义替代函数,直接在调用处展开常量:
#define bfi(dst, src, lsb, n) ({ \ uint32_t _d = (dst); \ __asm__ ("bfi %w0, %w1, %2, %3" : "+r"(_d) : "r"(src), "i"(lsb), "i"(n)); \ _d; \ })使用ARM架构特定约束
ARM的bfi指令对lsb(0-31)和n(1-32)有固定范围限制,可改用ARM专属约束:lsb用I(匹配0-31立即数),n用J(匹配1-32立即数),编译器会接受符合范围的常量参数,无需显式标记参数为常量:static __attribute__((always_inline)) uint32_t bfi(uint32_t dst, uint32_t src, int lsb, int n) { __asm ( "bfi %w[dst], %w[src], %[lsb], %[n]" : [dst]"+r"(dst) : [src]"r"(src), [lsb]"I"(lsb), [n]"J"(n) : ); return dst; }
相关变更背景
近年GCC和Clang版本更新中,对内联汇编约束的类型检查更加严格,尤其是针对i约束,不再允许编译器无法在函数定义阶段确认的常量参数。即使调用时传入的是常量,只要函数参数本身未被标记为常量类型,编译器会默认参数可能是变量,从而触发报错。
内容的提问来源于stack exchange,提问作者Jake 'Alquimista' LEE
相关产品推荐
相关产品推荐

