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

<[email protected]> Sat, 14 Feb 2026 22:44:41 -0500
Newsgroups gmane.comp.gcc.help
Message-ID <[email protected]>
Hello Johann and GNU team,

Thanks for taking a look at this, I confirm that when using
-fno-tree-dominator-opts, the compiler generates the SBIC and SBIS opcodes.

What is the recommended action now?
Do you consider this a bug in .uncprop1? (Since activating the optimization
causes less efficient code)

Regards,
Gabriel

-----Original Message-----
From: Georg-Johann Lay <[email protected]> 
Sent: Friday, February 13, 2026 12:37 PM
To: [email protected]; [email protected]
Subject: Re: AVR/GNU C Compiler 15 - Non efficient bit access

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