This is GCC Bugzilla
This is GCC Bugzilla Version 2.20+
View Bug Activity | Format For Printing | Clone This Bug
For setting individual bits of a register, the construct unsigned char BitLocation = Whatever; REG |= (1<<BitLocation); is commonly used. avr-gcc fails to realize that the target register is only 8 bits wide and performs an unnecessary 16-Bit shift of 1. Even if this code snippet is replaced by the following: unsigned char BitLocation = Whatever; const unsigned char one = 1; REG |= (one << BitLocation); The inefficient code will be generated, even with the -Os flag. I suppose this is because the << operator is not defined for 8-bit wide data types. Environment: System: Darwin variable.local 7.9.0 Darwin Kernel Version 7.9.0: Wed Mar 30 20:11:17 PST 2005; root:xnu/xnu-517.12.7.obj~1/RELEASE_PPC Power Macintosh powerpc host: powerpc-apple-darwin7.9.0 build: powerpc-apple-darwin7.9.0 target: avr-unknown-none configured with: ../configure --target=avr --prefix=/usr/local/atmel --enable-languages=c --disable-libssp --enable-__cxa_atexit --enable-clocale=gnu --disable-nls : (reconfigured) ../configure --target=avr --prefix=/usr/local/avr --enable-languages=c --disable-libssp --enable-__cxa_atexit --enable-clocale=gnu --disable-nls How-To-Repeat: Use the following test program, compile with -Os -S to see assembler code generated by avr-gcc: #include <avr/io.h> void setpin(unsigned char pin, unsigned char val) { if (val == 1) PORTC |= (1<<pin); else PORTC &= ~(1<<pin); } void setpin1(unsigned char pin, unsigned char val) { const unsigned char one = 1; if (val == 1) PORTC |= (one<<pin); else PORTC &= ~(one<<pin); } int main(void) { setpin(3, 1); setpin1(3, 1); return 0; }
Fix: Define shift operators for 8 bit wide data types, use if possible. I'm not a compiler expert but I know that the multiply operator detects the data types involved, so it should be doable with the shift operators as well.
Here's an excerpt of the assembly code obtained with -Os -S with some comments from me: ---------------------------------------------------- setpin: /* prologue: frame size=0 */ /* prologue end (size=0) */ mov r20,r24 clr r21 cpi r22,lo8(1) <--- The compare is done byte wide brne .L2 in r18,53-0x20 ldi r24,lo8(1) <--- Here we load 1 sixteen bit wide ldi r25,hi8(1) rjmp 2f 1: lsl r24 <--- Here we shift 1 sixteen bit wide rol r25 2: dec r20 brpl 1b or r18,r24 <--- r25 is not used. Why would we compute it ? rjmp .L6 .L2: in r18,53-0x20 ---------------------------------------------------- Cheers Rudi
Confirmed on 4.3.2.
The same occurs in 4.5 I think the bug is invalid The expression 1<< pin will be promoted. This produces a defined result if pin>7 and <15 The expression can not then be lower to 8 bit shift - since a shift by >7 is undefined. If you use pin &7, the shift is indeed 8 bit shift. Though it still loads a int (HIMODE) value of 1 that should have been reduced to QIMODE. setpinx: /* prologue: function */ /* frame size = 0 */ /* stack size = 0 */ .L__stack_usage = 0 mov r25,r24 andi r25,lo8(7) ldi r18,lo8(1) ldi r19,hi8(1) mov r24,r18 rjmp 2f 1: lsl r24 2: dec r25 brpl 1b cpi r22,lo8(1) brne .L7 in r25,53-0x20 or r25,r24 out 53-0x20,r25 ret .L7: in r25,53-0x20 com r24 and r24,r25 out 53-0x20,r24 ret