簡體   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