I read a few of the previous discussions on integer promotion, and none of them mentioned differences between avr-gcc (5.4.0) in gnu11 and gnu++11 modes (the default for avr-gcc and avr-g++).
When compiled with -Os as a C program, it optimizes the right shift to a single lsr.
#include <avr/io.h> int main() { uint8_t x = PORTB; x >>= 1; PORTB = x; }
However as a C++ program (.cpp instead of .c), x gets promoted to signed int.
in r24, 0x18 ldi r25, 0x00 asr r25 ror r24 out 0x18, r24
I've tried various combinations of casting, anding with 0xFF, and with 0x7F before shifting, all to no avail. The only thing that worked was resorting to inline asm for the right shift.
asm ("lsr %0" : "+r"(x));
I thought both the C11 and C++11 standards specify the integer promotion, and how a compiler optimizes is implementation defined, but I was quite surprised that avr-gcc only does it the optimal way for C.
For portability and maintainability reasons I'd rather not use inline asm, so if anyone knows how to coerce avr-g++ to compile the left shift to "lsr", I'd like to know how.