繁体   English   中英

STM32 MCU GCC 编译行为

[英]STM32 MCU GCC Compilation behavior

我对 MCU GCC 关于 function 的编译行为有一些误解,它返回其他 32 位值的东西。

MCU: STM32 L0 Series (STM32L083)
GCC : gcc version 7.3.1 20180622 (release) [ARM/embedded-7-branch revision 261907] (GNU Tools for Arm Embedded Processors 7-2018-q2-update)

我的代码针对大小进行了优化(使用选项-Os )。 据我了解,这将允许 gcc 使用隐式-fshort-enums来打包枚举。

我有两个 1 字节宽的枚举变量:

enum eRadioMode         radio_mode // (@ 0x20003200)
enum eRadioFunction     radio_func // (@ 0x20003201)

还有一个 function:

enum eRadioMode radio_get_mode(enum eRadioFunction _radio_func);

当我调用这组代码时:

radio_mode = radio_get_mode(radio_func);

它将在编译时生成这堆 ASM:

; At this point :
;   r4 value is 0x20003201 (Address of radio_func)

7820        ldrb    r0, [r4, #0]            ; GCC treat correctly r4 as a pointer to 1 byte wide var, no problem here
f7ff ffcd   bl  80098a8 <radio_get_mode>    ; Call to radio_get_mode()
4d1e        ldr r5, [pc, #120]              ; r5 is loaded with 0x20003200 (Address of radio_mode)
6028        str r0, [r5, #0]                ; Why GCC use 'str' and not 'strb' at this point ?

最后一行是问题: r0的值, radio_get_mode()的返回值,作为 32 位值存储到r5指向的地址中。 由于radio_funcradio_mode之后是 1 个字节,它的值被r0的第二个字节覆盖(因为枚举只有 1 个字节宽,所以它总是 0x00)。

由于我的 function radio_get_mode被声明为返回 1 个单字节,为什么 GCC 不使用指令strb将这个单字节保存到r5指向的地址中?

我努力了:

  • radio_get_mode()作为返回uint8_t : uint8_t radio_get_mode(enum eRadioFunction _radio_func);
  • 强制转换为uint8_tradio_mode = (uint8_t)radio_get_mode(radio_func);
  • 通过第三个变量(但 GCC 在编译时取消了那个无用的动作 - 不是那么愚蠢):
uint32_t r = radio_get_mode(radio_func);
radio_mode = (uint8_t) r;

但是这些解决方案都不起作用。

由于首先需要大小优化(-Os)来减少 rom 使用(而不是 ram - 在我的项目的这个时候 -)我发现解决方法 gcc 选项-fno-short-enums将让编译器使用 4枚举字节,在这种情况下丢弃任何重叠的 memory。

但是,在我看来,这是一种隐藏真正问题的肮脏方式:

  • GCC 是否无法正确处理 32 位以外的其他返回大小?
  • 有正确的方法吗?

提前致谢。

编辑:

  • 我在任何时候都没有使用-f-short-enums
  • 我确定这些枚举没有大于 0xFF 的值
  • 我试图将radio_moderadio_func声明为uint8_t (又名unsigned char ):问题是一样的。
  • 使用-Os时, Output.map 如下:

Common symbol       size              file
...
radio_mode          0x1               src/radio/radio.o
radio_func          0x1               src/radio/radio.o
...
...
...
Section         address               label
                0x2000319c                radio_state
                0x20003200                radio_mode
                0x20003201                radio_func
                0x20003202                radio_protocol
...

mapfile 的 output 清楚地显示radio_moderadio_func为 1 字节宽,并位于以下地址。

  • 在没有-Os的情况下编译时, Output.map 清楚地显示枚举变为 4 字节宽(地址填充为 4)。
  • 当使用-Os-fno-short-enums编译时,对所有枚举执行与没有-Os相同的操作(这就是我猜-Os暗示隐含-f-short-enums原因)
  • 我将尝试提供最小的可重现示例
  • 我对问题的分析是我很确定这是一个编译器错误。 对我来说,这显然是一个 memory 重叠。 我的问题更多是关于避免这种情况的最佳做法——以“最佳实践”的方式。

编辑 2

这是我的错,我重新测试员将所有签名更改为uint8_t (又名unsigned char )并且效果很好。

@Peter Cordes 似乎在这里发现了问题:使用它时, -Os部分启用-fshort-enums ,让 GCC 的某些部分将其视为尺寸 1,而将其他部分视为尺寸 4。

仅使用uint8_t的 ASM 代码是:

    ; Same position than before
    7820        ldrb    r0, [r4, #0]
    f7ff ffcd   bl  80098a8 <radio_get_mode>
    4d1e        ldr r5, [pc, #120]  
    7028        strb    r0, [r5, #0]   ; Yes ! GCC use 'strb' and not 'str' like before !

澄清:

  • 使用-Os和枚举时似乎存在编译器错误。 两个枚举位于重叠的连续地址上,这很不幸。
  • -fno-short-enums-Os结合使用似乎是 IMO 的一个很好的解决方法,因为问题仅涉及枚举,而不是所有 1 字节 var。

再次感谢。

ARM 端口 abi 将 none-aebi 枚举定义为可变大小类型,将 linux-eabi 定义为标准固定类型。

这就是你观察到的行为的原因。 它与优化无关。

在此示例中,您可以看到它是如何工作的。 https://godbolt.org/z/-mY_WY

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM