Re: AVR/GNU C Compiler 15 - Non efficient bit access

Georg-Johann Lay via Gcc-help <[email protected]> Fri, 13 Feb 2026 18:37:02 +0100
Newsgroups gmane.comp.gcc.help
Message-ID <[email protected]>
Am 13.02.26 um 15:45 schrieb [email protected]:
> I'm working on an embedded project using an AVR microcontroller.
> 
> I've noticed that version 15 generates non-efficient access to registers
> that have single instruction opcodes available.
> 
> The AVR Instruction Set provides opcodes for I/O direct addressing:
> SBIC: Skip if Bit in I/O Register Cleared
> 
> SBIS: Skip if Bit in I/O Register Set
> 
> This is a minimal example that generates the problem:
> 
> typedef __UINT8_TYPE__ uint8_t;
> 
> #define SFR (*(volatile uint8_t*) (1 + __AVR_SFR_OFFSET__))
> 
> char File1 (char angle, char scale);
> 
> void File2 (void)
> {
>      if (!(SFR & (1 << 4)))
>          for (uint8_t i = 0; i < 3; ++i)
>              File1 (i, 1);
> }
> 
> Compile with:
> $ avr-gcc -mmcu=atmega8 -S -Os -dp x.c

The generated code is:

File2:
	push r28		 ;  40	[c=4 l=1]  pushqi1/0
	in r24,0x1	 ;  37	[c=4 l=1]  movqi_insn/3
	mov r28,r24	 ;  38	[c=4 l=2]  *andqi3/3
	andi r28,1<<4
	sbrc r24,4	 ;  39	[c=4 l=2]  *sbrx_branchqi
	rjmp .L1	
.L3:
	ldi r22,lo8(1)	 ;  32	[c=4 l=1]  movqi_insn/1
	mov r24,r28	 ;  33	[c=4 l=1]  movqi_insn/0
	call File1	 ;  12	[c=12 l=2]  call_value_insn/1
	subi r28,lo8(-(1))	 ;  34	[c=4 l=1]  *addqi3/1
	cpi r28,lo8(3)	 ;  35	[c=4 l=1]  cmpqi3/0
	brne .L3		 ;  36	[c=4 l=1]  branch
.L1:
	pop r28		 ;  43	[c=4 l=1]  popqi
	ret		 ;  44	[c=0 l=1]  return_from_epilogue

What happens is that in the calculation of SFR & (1 << 4) a value of
zero may be produced, and that value is then used to initialize the loop
variable i = 0.

Insn combine combines a SBRC insn 39 as expected but doesn't look
further, because is cannot combine the volatile load 37 as that load
would still be around after a combination to SBIC, breaking volatile
correctness.

The pass that introduces the reuse of the zero value is .uncprop1, and
indeed with -fno-tree-dominator-opts the code is as expected (that's 
just an observation and not a recommendation to use that option):

File2:
	push r28		 ;  39	[c=4 l=1]  pushqi1/0
	sbic 0x1,4	 ;  38	[c=4 l=2]  *sbix_branch
	rjmp .L1	
	ldi r28,0		 ;  37	[c=4 l=1]  movqi_insn/0
.L3:
	ldi r22,lo8(1)	 ;  32	[c=4 l=1]  movqi_insn/1
	mov r24,r28	 ;  33	[c=4 l=1]  movqi_insn/0
	call File1	 ;  13	[c=12 l=2]  call_value_insn/1
	subi r28,lo8(-(1))	 ;  34	[c=4 l=1]  *addqi3/1
	cpi r28,lo8(3)	 ;  35	[c=4 l=1]  cmpqi3/0
	brne .L3		 ;  36	[c=4 l=1]  branch
.L1:
	pop r28		 ;  42	[c=4 l=1]  popqi
	ret		 ;  43	[c=0 l=1]  return_from_epilogue

Now insn 38 is a SBIC on bit 4 of the SFR, and insn 37 just loads
i = 0 in reg 28.  The sequence is 2 instructions shorter.

Johann