AVR-GCC下ATtiny13饱和加法的优化技术问询
ATtiny13 8位饱和加法的AVR-GCC优化问题
我正在为ATtiny13编写程序,需要执行大量8位饱和加法操作。使用AVR-GCC 14.1.0并开启-O3优化级别时,发现编译器未能生成利用进位标志的最优汇编代码。以下是我尝试的多种实现方式及对应的编译结果:
版本1:基于溢出后数值比较
uint8_t saturating_add_1(uint8_t a, uint8_t b) { uint8_t temp = a + b; if(temp < a) return 0xFF; return temp; }
该版本在x86平台能被优化为利用标志位的代码,但AVR-GCC生成的汇编未利用进位标志:
saturating_add_1: .L__stack_usage = 0 mov r25,r24 add r24,r22 cp r24,r25 brlo .L1 ret .L1: ldi r24,lo8(-1) ret
版本2:基于预计算最大值判断
uint8_t saturating_add_2(uint8_t a, uint8_t b) { if(b > 255 - a) return 255; else return a + b; }
生成的汇编效率更低:
saturating_add_2: .L__stack_usage = 0 ldi r18,lo8(-1) ldi r19,0 sub r18,r24 sbc r19,__zero_reg__ cp r18,r22 cpc r19,__zero_reg__ brlt .L1 add r24,r22 ret .L1: ldi r24,lo8(-1) ret
版本3:使用内置溢出检测函数
uint8_t saturating_add_builtin(uint8_t a, uint8_t b) { if(__builtin_add_overflow(a, b, &a)) return 255; else return a; }
生成的汇编与版本1类似,仍未使用brcs/brcc指令。
版本4:直接读取SREG寄存器判断进位
uint8_t saturating_add_reg(uint8_t a, uint8_t b) { uint8_t temp = a + b; if(SREG & 1) return 255; return temp; }
指令数有所减少,但逻辑仍非最优:
saturating_add_reg: .L__stack_usage = 0 add r24,r22 in __tmp_reg__,__SREG__ sbrs __tmp_reg__,0 ret ldi r24,lo8(-1) ret
版本5:内联汇编实现(无常量优化)
uint8_t saturating_add_asm_1(uint8_t a, uint8_t b) { asm ( "add %[a], %[b]\n\t" "brcc no_overflow_%=\n\t" "ldi %[a], 255\n\t" "no_overflow_%=:" : [a] "+d" (a) : [b] "r" (b) : "cc" ); return a; }
该版本能生成最优的汇编逻辑,但无法针对常量输入进行优化(比如当b为0时,编译器无法直接返回a)。
版本6:依赖C加法后的进位标志的内联汇编
uint8_t saturating_add_asm_2(uint8_t a, uint8_t b) { uint8_t temp = a + b; asm ( "brcc no_overflow_%=\n\t" "ldi %[temp], 255\n\t" "no_overflow_%=:" : [temp] "+d" (temp) : : ); return temp; }
该版本存在代码重排风险,若添加volatile关键字则会禁用更多优化。
咨询问题
- 无需内联汇编的情况下,能否让AVR-GCC生成利用进位标志的最优饱和加法代码?
- 如何编写既支持常量优化、又能生成最优汇编的内联饱和加法实现?
内容的提问来源于stack exchange,提问作者Kryštof Vosyka
相关产品推荐
相关产品推荐

