Сроки
Оптимизация кода должна и сделает его более быстрым, что может вызвать проблемы с оборудованием, которое ожидает его немного медленнее.
Пример:
void GPIO_Test() {
GPIO_InitTypeDef GPIO_InitStruct;
RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6 | GPIO_PIN_7, GPIO_PIN_SET);
GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
Это работает без -lto
, но не может установить высокий уровень выходов, когда -lto
включен. Зачем? Поскольку в большинстве моделей STM32 требуется небольшая задержка между включением часов в RCC и использованием периферийных устройств (это упоминается в сообщениях об ошибках). Вызов функции обеспечит требуемую задержку, но с -lto
компилятор может встроить функции в другие модули, уменьшая задержку.
Отсутствует volatile
Распространенным источником проблем с -lto
является то, что он может оптимизировать доступ к переменным, которые должны были быть объявлены как volatile
, но это не так, даже если доступ инкапсулирован в вызов функции в другом модуле.
Давайте посмотрим на простой пример.
mainloop.c:
while(1) {
if(button_pressed()) {
do_stuff();
}
}
button.c:
int button_flag;
void button_interrupt_handler() {
button_flag = GPIOx->IDR & SOME_BIT;
}
void button_pressed() {
return button_flag;
}
Без -lto
вызов функции в другом модуле обрабатывается как черный ящик с возможными побочными эффектами, вызов всегда генерируется, а результат всегда оценивается. Другими словами, каждый вызов функции другого модуля действует как неявный барьер памяти. С -lto
барьер больше не существует, компилятор может эффективно встроить или иным образом оптимизировать функции в других модулях.