LTO optimizes negative effects and finds the best solution

I have an MCU with flash memory partitioned (as usual). Linker objects. The .struct_init, .struct_init_const, .struct_not_init sections for addresses belong to the flash section20. It is hardcoded in the linker script.

Consider the 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 want to be sure that initializing the param1_ variables will fetch data from flash. Since the data in the flash20 section can be changed using the bootloader (at the moment when the main firmware version is not running).

The question is: can LTO (and other optimizations) discard samples from flash and just replace known values ​​because they are known at reference time due to initialization. Which approach is better? If LTO can replace values ​​- then should initialization be avoided? I know volatile can help, but is it really necessary in this situation?

The code example shows different approaches to accessing and initializing data. The not_init version seems to be the best, because the compiler cannot replace anything. But it would be nice to have some default options, so I would prefer the init version if it can be used.

Which approach should you take?

I am currently using GCC 4.9.3, but this is a general C / C ++ compiler question.

+3


source to share


1 answer


C and C ++ both have variables extern

that allow you to define constants without immediately highlighting their values:

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

      

In general, you define them in the (one) source file, which hides them from anything that is not in that source file. It's not LTO resilient of course, but if you can turn LTO off, it's a fairly straightforward strategy.

If turning LTO off is not an option, another solution should not define them, let LTO build a binary and then use a script to merge the definitions in the resulting binary on the right side (the one that might be flashed).



When a value is not available during LTO, you are guaranteed that it will not be replaced.


Regarding the solutions you presented, and volatile

is a really standard compatible solution, this means the value is not constant, which prevents it from being cached at runtime. Regardless of whether this is acceptable or not, you should be aware, just keep in mind that it can affect the performance that you believe LTO is using you would like to avoid.

+3


source







All Articles