Quantcast
Channel: Active questions tagged gcc - Stack Overflow
Viewing all articles
Browse latest Browse all 22000

STM32 MCU GCC Compilation behavior

$
0
0

I have some misunderstanding about MCU GCC compilation behavior regarding function that return other things that 32bits value.

MCU: STM32 L0 Series (STM32L083)GCC : gcc version 7.3.1 20180622 (release) [ARM/embedded-7-branch revision 261907] (GNU Tools for Arm Embedded Processors 7-2018-q2-update)

My code is optimized for size (with option -Os ). In my understanding, this will allow the gcc to use implicit -fshort-enums in order to pack enums.

I have two enum var, 1-byte wide :

enum eRadioMode         radio_mode // (@ 0x20003200)enum eRadioFunction     radio_func // (@ 0x20003201)

And a function :

enum eRadioMode radio_get_mode(enum eRadioFunction _radio_func);

When i call this bunch of code :

radio_mode = radio_get_mode(radio_func);

It will produce this bunch of ASM at compile time:

; At this point :;   r4 value is 0x20003201 (Address of radio_func)7820        ldrb    r0, [r4, #0]            ; GCC treat correctly r4 as a pointer to 1 byte wide var, no problem heref7ff ffcd   bl  80098a8 <radio_get_mode>    ; Call to radio_get_mode()4d1e        ldr r5, [pc, #120]              ; r5 is loaded with 0x20003200 (Address of radio_mode)6028        str r0, [r5, #0]                ; Why GCC use 'str' and not 'strb' at this point ?

The last line here is the problem : The value of r0, return value of radio_get_mode(), is stored into address pointed by r5, as a 32bit value.Since radio_func is 1 byte after radio_mode, its value is overwritten by the second byte of r0 (that is always 0x00 since enum is only 1 byte wide).

As my function radio_get_mode is declared as returning 1 single byte, why GCC doesn't use instruction strb in order to save this single byte into the address pointed by r5 ?

I have tried :

  • radio_get_mode() as returning uint8_t : uint8_t radio_get_mode(enum eRadioFunction _radio_func);
  • Forcing cast to uint8_t : radio_mode = (uint8_t)radio_get_mode(radio_func);
  • Passing by a third var (but GCC cancel that useless move at compile - not so dumb) :
uint32_t r = radio_get_mode(radio_func);radio_mode = (uint8_t) r;

But none of these solutions work.

Since the size optimization (-Os) is needed in first sight to reduce rom usage (and not ram - at this time of my project -) I found that the workaround gcc option -fno-short-enums will let the compiler to use 4 bytes by enum, discarding by the way any overlapping memory in this case.

But, in my opinion, this is a dirty way to hide a real problem here :

  • Is GCC not able to correctly handle other return size than 32bit ?
  • There is a correct way to do that ?

Thanks in advance.

EDIT :

  • I did NOT use -f-short-enums at any moment.
  • I'm sure that these enum has no value greater than 0xFF
  • I have tried to declare radio_mode and radio_func as uint8_t (aka unsigned char) : The problem is the same.
  • When compiled with -Os, Output.map is as follow :
Common symbol       size              file...radio_mode          0x1               src/radio/radio.oradio_func          0x1               src/radio/radio.o.........Section         address               label                0x2000319c                radio_state                0x20003200                radio_mode                0x20003201                radio_func                0x20003202                radio_protocol...

The output of the mapfile show clearly that radio_mode and radio_func is 1 byte wide and at following address.

  • When compiled without -Os, Output.map show clearly that enums become 4 byte wide (with address padded to 4).
  • When compiled with -Os and -fno-short-enums, do the same things that without -Os for all enums (This is why I guess -Os implies implicit -f-short-enums)
  • I will try to provide minimal reproducible example
  • My analysis of the problem is that I'm pretty sure it is a compiler bug. For me, this is clearly a memory overlapping. My question is more about the best things to do in order to avoid this - in the "best practice" way.

EDIT 2

It is my bad, I have re-tester changing all signature to uint8_t (aka unsigned char) and it work well.

@Peter Cordes seems to found the problem here : When using it, -Os is partly enabling -fshort-enums, getting some parts of GCC to treat it as size 1 and other parts to treat it as size 4.

ASM code using only uint8_t is :

    ; Same position than before    7820        ldrb    r0, [r4, #0]    f7ff ffcd   bl  80098a8 <radio_get_mode>    4d1e        ldr r5, [pc, #120]      7028        strb    r0, [r5, #0]   ; Yes ! GCC use 'strb' and not 'str' like before !

To clarify :

  • It seems to have compiler bug when using -Os and enums. This is bad luck that two enum is at consecutive adresses that overlap.
  • Using -fno-short-enums in conjonction with -Os appear to be a good workaround IMO, since the problem is concerning only enum, and not all 1 byte var at all.

Thanks again.


Viewing all articles
Browse latest Browse all 22000

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>