I am using avr-g++ 10.2.0 on Fedora 37, target is ATmega128. My project should be optimized for code size and RAM usage.
One 8-bit hardware register port is defined like this:
struct MyPort
{
uint8_t someBit:1;
uint8_t someOtherBit:1;
uint8_t bit_1:1;
uint8_t bit_2:1;
uint8_t bit_3:1;
uint8_t bit_4:1;
uint8_t bit_5:1;
uint8_t more_bit:1;
};
I have an initializer function like this:
constexpr void Initialize(const MyPort & value)
{
port = value;
}
where 'port' is the hardware register itself and I would like to use it this way:
Initialize( {0,0,1,0,1,1,0,0} );
The expected result looks like this:
ldi r24, 0x34
out 0x18, r24
But in some cases the corresponding value is stored in the initialized memory like this:
lds r24, 0x0280
out 0x18, r24
I found that it depends on the value: if that 8-bit value contains only one '1' bit, then it is a constant (e.g. 0x80, 0x10, etc), but with more '1' bits (e.g. 0xc0, 0x11, etc) it is stored in the memory. For example:
Initialize( {0,0,0,0,0,0,0,1} );
will result in
ldi r24, 0x80
out 0x18, r24
but calling this way:
Initialize( {0,0,0,1,0,0,0,1} );
will result in
lds r24, 0x0280
out 0x18, r24
where the value 0x88 is stored in the initialized memory segment at 0x280
I do not understand what is the reason of that difference. I do not want to store anything in the memory what is not necessary. The compiler is called with '-O s' optimization parameters.
A small example:
struct MyPort
{
unsigned char b0:1;
unsigned char b1:1;
unsigned char b2:1;
unsigned char b3:1;
unsigned char b4:1;
unsigned char b5:1;
unsigned char b6:1;
unsigned char b7:1;
};
constexpr void Init(unsigned char addr, const MyPort & data)
{
*reinterpret_cast<MyPort *>(addr) = data;
}
int main(int argc, char ** argv)
{
Init( 0x34, {0,0,0,1,0,0,0,0} );
Init( 0x35, {0,0,0,1,0,0,1,0} );
return 0;
}
Can be compiled this way:
avr-g++ -Os test.cpp
and the result:
avr-objdump -d a.out
I got this (displayed only the relevant part):
00000018 <main>:
18: 88 e0 ldi r24, 0x08
1a: 84 bb out 0x14, r24
1c: 80 91 60 00 lds r24, 0x0060
20: 85 bb out 0x15, r24
22: 90 e0 ldi r25, 0x00
24: 80 e0 ldi r24, 0x00
26: 08 95 ret
The first register is initialized by an immediate value, but the second is stored in the memory (at 0x60), however, only the value differs.
Is there any way to force the immediate value?
The result is the same for all optimization levels other than 0.