avr-g++ stores temporary variables for function parameters in memory

45 views Asked by At

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.

0

There are 0 answers