LTO оптимизации негативные эффекты и найти лучшее решение

У меня MCU с флеш-памятью разбит на разделы (как обычно).
Линкер размещает разделы .struct_init, .struct_init_const, .struct_not_init по адресам, принадлежащим разделу 20 флэш-памяти. Это жестко закодировано в скрипте компоновщика.

Рассмотрим следующий тестовый код:
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
}
}

Я хочу быть уверен, что инициализация переменных param1_ приведет к извлечению данных из флэш-памяти. Потому что данные во флэш-разделе 20 могут быть изменены с помощью загрузчика (в момент, когда основная прошивка не запущена).

Вопрос в следующем: может ли LTO (и другие оптимизации) выбрасывать выборки из флэш-памяти и просто заменять известные значения, потому что они известны во время соединения из-за инициализации.
Какой подход лучше?
Если LTO может заменить значения — тогда следует избегать инициализации?
Я знаю, что изменчивый может помочь, но действительно ли он необходим в этой ситуации?

Код экзамена показывает различные подходы к доступу и инициализации данных.
Версия not_init кажется лучшей, потому что компилятор не может ничего заменить. Но было бы неплохо иметь некоторые параметры по умолчанию, поэтому я бы предпочел версию init, если она может быть использована.

Какой подход следует выбрать?

В настоящее время я использую GCC 4.9.3, но это общий вопрос о любом компиляторе C / C ++.

3

Решение

C и C ++ обе функции extern переменные, которые позволяют вам определять константы без немедленной передачи их значений:

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

Обычно вы определяете их в (одном) исходном файле, который скрывает их от всего не в этом исходном файле. Конечно, это не является устойчивым к LTO, но если вы можете отключить LTO, это достаточно простая стратегия.

Если отключение LTO не является вариантом, другое решение состоит в том, чтобы не определять их, позволить LTO создать двоичный файл, а затем использовать сценарий для объединения определений в создаваемом двоичном файле в правом разделе (тот, который может быть прошит).

Если значение не доступно во время LTO, вам гарантировано, что оно не будет подставлено.


Что касается представленных вами решений, то пока volatile действительно является стандартным совместимым решением, оно подразумевает, что значение не является постоянным, что предотвращает его кэширование во время выполнения. Является ли это приемлемым или нет, вам нужно знать, просто имейте в виду, что это может повлиять на производительность, чего, как я предполагал, вы хотели бы избежать, поскольку вы используете LTO.

3

Другие решения