我正在使用 arm-none-eabi-gcc 6.3.1 为 stm32 构建我们的固件。
如果我启用链接时优化,它仍然可以编译和启动,并且比没有 -ftlo 的情况小约 10kiB,但有一些细微的损坏。
我该如何调试呢?
有没有办法让 gcc 告诉我它在链接时优化期间(错误地)丢弃了什么?
时间问题
优化代码应该并且将会使其运行得更快,这可能会导致硬件出现问题,预计它会变慢一点。
一个例子:
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
但不是的变量的访问,即使该访问被封装在另一个模块的函数调用中。
让我们看一个简单的例子。
主循环.c:
while(1) {
if(button_pressed()) {
do_stuff();
}
}
按钮.c:
int button_flag;
void button_interrupt_handler() {
button_flag = GPIOx->IDR & SOME_BIT;
}
void button_pressed() {
return button_flag;
}
如果没有-lto
,调用另一个模块中的函数会被视为具有可能副作用的黑盒,始终会生成调用,并且始终会评估结果。换句话说,对另一个模块的每个函数调用都充当隐式内存屏障。随着-lto
障碍不再存在,编译器可以有效地内联或以其他方式优化其他模块中的函数。