3

I have MCU with flash memory breaked in sections(as usual). Linker places .struct_init, .struct_init_const, .struct_not_init sections to addresses belongs to flash memory section20. It is hardcoded in linker script.

Consider following test code: test.h

typedef struct
{
    int val1;
    int val2;
} mystruct_t;

test.cpp

#include "test.h"

// each variable is placed in dedicated section
// sections are placed in flash section20
// linker exports symbols with address of eaach section
__attribute__((section(".struct_init")))
mystruct_t struct_init = {
    .val1 = 1,.val2 = 2};

__attribute__((section(".struct_init_const")))
extern const mystruct_t struct_init_const = {
    .val1 = 1, .val2 = 2};

__attribute__((section(".struct_not_init")))
mystruct_t struct_not_init;

main.cpp

#include <stdint.h>

// This symbols exported by linker
// contains addresses of corresponding sections
extern uintptr_t LNK_STRUCT_INIT_ADDR;
extern uintptr_t LNK_STRUCT_INIT_CONST_ADDR;
extern uintptr_t LNK_STRUCT_NOT_INIT_ADDR;

// Pointers for indirect access to data
mystruct_t* struct_init_ptr = (mystruct_t*)LNK_STRUCT_INIT_ADDR;
const mystruct_t* struct_init_const_ptr = (const mystruct_t*)LNK_STRUCT_INIT_CONST_ADDR;
mystruct_t* struct_not_init_ptr = (mystruct_t*)LNK_STRUCT_NOT_INIT_ADDR;

// Extern variables declarations for DIRECT access data
extern mystruct_t struct_init;
extern const mystruct_t struct_init_const;
extern mystruct_t struct_not_init;

// This is some variables representing config values
// They can be more complex objects(classes) with internal state and logic..
int param1_direct;
int param1_init_const_direct;
int param1_not_init_direct;

int param1_indirect;
int param2_init_const_indirect;
int param1_not_init_indirect;

int main(void)
{
    // local variables init with direct access
    int param1_direct_local = struct_init.val1;
    int param1_init_const_direct_local = struct_init_const.val1;
    int param1_not_init_direct_local = struct_not_init.val1;

    // local variables init with indirect access
    int param1_indirect_local = struct_init_ptr->val1;
    int param2_init_const_indirect_local = struct_init_const_ptr->val1;
    int param1_not_init_indirect_local = struct_not_init_ptr->val1;

    //global variables init direct
    param1_direct = struct_init.val1;
    param1_init_const_direct = struct_init_const.val1;
    param1_not_init_direct = struct_not_init.val1;
    //global variables init indirect
    param1_indirect = struct_init_ptr->val1;
    param2_init_const_indirect = struct_init_const_ptr->val1;
    param1_not_init_indirect = struct_not_init_ptr->val1;

    while(1){
        // use all variables we init above
        // usage of variables may also occure in some functions or methods
        // directly or indirectly called from this loop
    }
}

I wanna be sure that initialization of param1_ variables will lead to fetch data from flash. Because data in flash section20 can be changed using bootloader(at the moment when main firmware is not running).

The question is: Can LTO(and other optimizations) throw away fetches from flash and just substitute known values because they are known at link time because of initialization. What approach is better? If LTO can substitute values - then initialization should be avoided? I know volatile can help, but is it really needed in this situation?

Code exampe shows different approaches of accessing and initializing data. not_init version seems to be the best, because compiler can't substitute anything. But it will be a good idea to have some default parameters, so i'd prefer init version if it can be used.

What approach should be chosen?

Currently i am using GCC 4.9.3 but this is general question about any C/C++ compiler.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
sigmaN
  • 187
  • 9
  • There is no language "C/C++". Please pick one. Expecially with `const`, there are differences between them. – too honest for this site Jul 10 '15 at 19:35
  • Is there a reason you do not use the proper types? – too honest for this site Jul 10 '15 at 19:37
  • Language is C++. What do you mean ? "Is there a reason you do not use the proper types". Code example just illustrate different methods of accessing data in flash. Direct and indirect. With const and without... – sigmaN Jul 10 '15 at 19:40
  • Just wonder why you cast that much and do not use the correct types? – too honest for this site Jul 10 '15 at 19:45
  • Linker exports int with address of section. I need pointer from int. This is just example of one possible solution. It is definetly better to use & operator. Idea of int initialized pointer comes from this thread http://stackoverflow.com/questions/31322646/embedded-c11-code-do-i-need-volatile – sigmaN Jul 10 '15 at 19:57
  • Hmm.. Normally, it is sufficient sufficient to place the structs in the correct section (and have the linker script set up correctly, of course). As you already have done that, I just do not understand what all that fiddling is for. Imho, it just makes the code less readable/maintainable. Sorry, no offense, I just had to rework enough code with "tricky" behaviour, so I'm a bit sensitive. – too honest for this site Jul 10 '15 at 20:03
  • Totally agree about less readable code. All this mess started from reading some articles about LTO and other optimizations. Then i thought hmm, if it is statically initialized at compile time, then why compiler should fetch it from memory all he time? So i am i doubt now is it really sufficient just place variable into section? It seems to be its not and static initialization is potentially dangerous in this case. About ugly code: now i see, there is no reason to use it because direct or indirect access - does not makes any difference, LTO still can substitute values if they are known. – sigmaN Jul 11 '15 at 00:45
  • Well, I use such things for a large ARM project. However, I had to remove LTO as we are using 4.7.x and there seem to be some problems with it. Actually, I do not miss it, as we are still far below the memory limit (and for RAM, LTO gains zero to none actually) and some timing tests did not show that much improvement. But for other projects the mileage might vary. – too honest for this site Jul 11 '15 at 02:41
  • Yes, LTO is not a magic and i can see only about 6% smaller binary size and ~2% performance gain. RAM usage is not affected at all. But anyway LTO is actively developing feature and we should be ready to use it and know how to avoid potential problems in future. I use latest 4.9.3 and it has much more improved LTO implementation( compared with 4.7.x). I believe someday LTO will be enabled by default :) – sigmaN Jul 11 '15 at 08:07
  • I agree about its potential in general, But for now, I will not change the toolchain mid-project. As the system is highly dynamic and descriptor-based (it is OO-C, not C++, btw.), I also see not much potential in general in LOT. Most functions being worth inlining are defined - well - `inline` already. And LTO definitively complicates (at best) debugging. It's benefits are likely less on bare-metal embedded than on larger irons. Oh, remember to use the same options for linking and compiling; that was at least one of the pits I fell in with LTO at first. – too honest for this site Jul 11 '15 at 12:55

1 Answers1

3

C and C++ both feature extern variables, which lets you define constants without immediately giving away their values:

// .h
extern int const param1;
extern char const* const param2;
// ...

In general you would define them in a (single) source file, which would hide them away from anything not in this source file. This is not LTO resilient, of course, but if you can disable LTO it is an easy enough strategy.

If disabling LTO is not an option, another solution is to not define them, let LTO produce a binary, and then use a script to splice the definitions in the produced binary in the right section (the one that can be flashed).

With the value not available at LTO time, you are guaranteed that it will not be substituted.


As for the solutions you presented, while volatile is indeed a standard compliant solution, it implies that the value is not constant, which prevents caching it during run-time. Whether this is acceptable or not is for you to know, just be aware it might have a performance impact, which as you are using LTO I surmised you would like to avoid.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Thanks a lot! I will probably use not initialized version with binary modification before flashing. – sigmaN Jul 10 '15 at 19:21