簡體   English   中英

為什么GCC(ARM Cortex-M0)應該知道數據已經是uint8時生成UXTB指令

[英]Why GCC (ARM Cortex-M0) generates UXTB instruction when it should know that data is already uint8

我正在使用 NXP (LPC845) 的 Cortex-M0 MCU,我試圖弄清楚 GCC 想要做什么:)

基本上,C代碼(偽)如下:

volatile uint8_t readb1 = 0x1a; // dummy
readb1 = GpioPadB(GPIO_PIN);

我寫的宏是

(*((volatile uint8_t*)(SOME_GPIO_ADDRESS)))

現在代碼正在運行,但它產生了一些我不明白的額外 UXTB 指令

00000378:   ldrb    r3, [r3, #0]
0000037a:   ldr     r2, [pc, #200]  ; (0x444 <AppInit+272>)
0000037c:   uxtb    r3, r3
0000037e:   strb    r3, [r2, #0]
105         asm("nop");

我的解釋如下:

  • 從 R3 中指定的地址加載 BYTE,將結果放入 R3 <-- 這是從 GPIO 寄存器加載為 BYTE
  • 加載 readb1 變量的 R2 地址
  • UXTB 擴展了 uint8 值??? 但是 rotate 參數是 0,所以基本上對 uint8 沒有任何作用!
  • 從 R3 存儲為 BYTE 到 R2 的地址(我的變量)數據

為什么會這樣?

首先,它應該知道 R3 中的數據只有一個 BYTE 的含義(它已經正確生成了 LDRB)。 其次,STRB 已經可以修整 7..0 LSB 那么為什么要使用 UXTB 呢?

感謝您的澄清,

編輯:編譯器版本:

gcc 版本 9.2.1 20191025(發布)[ARM/arm-9-branch 修訂版 277599](用於 Arm 嵌入式處理器的 GNU 工具 9-2019-q4-major)

我用-O3

看起來像是編譯器留下的額外指令和/或 cortex-m 或更新的內核有一些細微差別(很想知道細微差別是什么)。

#define GpioPadB(x) (*((volatile unsigned char *)(x)))
volatile unsigned char readb1;
void fun ( void )
{
    readb1 = 0x1A;
    readb1 = GpioPadB(0x1234000);
}

一個apt得到gcc

arm-none-eabi-gcc --version
arm-none-eabi-gcc (15:4.9.3+svn231177-1) 4.9.3 20150529 (prerelease)
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

arm-none-eabi-gcc -O2 -c -mthumb so.c -o so.o
arm-none-eabi-objdump -d so.o


00000000 <fun>:
   0:   231a        movs    r3, #26
   2:   4a03        ldr     r2, [pc, #12]   ; (10 <fun+0x10>)
   4:   7013        strb    r3, [r2, #0]
   6:   4b03        ldr     r3, [pc, #12]   ; (14 <fun+0x14>)
   8:   781b        ldrb    r3, [r3, #0]
   a:   7013        strb    r3, [r2, #0]
   c:   4770        bx      lr
   e:   46c0        nop         ; (mov r8, r8)
  10:   00000000    .word   0x00000000
  14:   01234000    .word   0x01234000

正如人們所期望的那樣。

arm-none-eabi-gcc -O2 -c -mthumb -march=armv7-m so.c -o so.o
arm-none-eabi-objdump -d so.o
so.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <fun>:
   0:   4a03        ldr     r2, [pc, #12]   ; (10 <fun+0x10>)
   2:   211a        movs    r1, #26
   4:   4b03        ldr     r3, [pc, #12]   ; (14 <fun+0x14>)
   6:   7011        strb    r1, [r2, #0]
   8:   781b        ldrb    r3, [r3, #0]
   a:   b2db        uxtb    r3, r3
   c:   7013        strb    r3, [r2, #0]
   e:   4770        bx  lr
  10:   00000000    .word   0x00000000
  14:   01234000    .word   0x01234000

里面有額外的 utxb 指令

有點新的東西

arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 10.2.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

對於 armv6m 和 armv7m

00000000 <fun>:
   0:   231a        movs    r3, #26
   2:   4a03        ldr     r2, [pc, #12]   ; (10 <fun+0x10>)
   4:   7013        strb    r3, [r2, #0]
   6:   4b03        ldr     r3, [pc, #12]   ; (14 <fun+0x14>)
   8:   781b        ldrb    r3, [r3, #0]
   a:   7013        strb    r3, [r2, #0]
   c:   4770        bx      lr
   e:   46c0        nop         ; (mov r8, r8)
  10:   00000000    .word   0x00000000
  14:   01234000    .word   0x01234000

對於 armv4t

00000000 <fun>:
   0:   231a        movs    r3, #26
   2:   4a03        ldr     r2, [pc, #12]   ; (10 <fun+0x10>)
   4:   7013        strb    r3, [r2, #0]
   6:   4b03        ldr     r3, [pc, #12]   ; (14 <fun+0x14>)
   8:   781b        ldrb    r3, [r3, #0]
   a:   7013        strb    r3, [r2, #0]
   c:   4770        bx      lr
   e:   46c0        nop         ; (mov r8, r8)
  10:   00000000    .word   0x00000000
  14:   01234000    .word   0x01234000

並且 utxb 不見了。

我認為這只是一個錯過的優化,窺視孔或其他。

正如已經回答的那樣,當您使用非 gpr 大小的變量時,您可以期望和/或容忍編譯器轉換為寄存器大小。 根據編譯器和目標的不同,它們是在輸入還是輸出時執行(讀取變量時或在寫入或使用之前)。

對於 x86 ,您可以分別訪問寄存器的各個部分(或使用基於 memory 的操作數),您會看到它們不會這樣做(在 gcc 中),即使在明顯需要符號擴展或填充的情況下也是如此。 並在使用該值時將其整理出來。

您可以在 gcc 源中搜索 utxb 並可能查看問題或評論。

編輯

請注意,clang 采用不同的路徑,它會燒掉生成地址的時鍾,但不進行擴展

00000000 <fun>:
   0:   f240 0000   movw    r0, #0
   4:   f2c0 0000   movt    r0, #0
   8:   211a        movs    r1, #26
   a:   7001        strb    r1, [r0, #0]
   c:   f244 0100   movw    r1, #16384  ; 0x4000
  10:   f2c0 1123   movt    r1, #291    ; 0x123
  14:   7809        ldrb    r1, [r1, #0]
  16:   7001        strb    r1, [r0, #0]
  18:   4770        bx  lr

clang --version
clang version 11.1.0 (https://github.com/llvm/llvm-project.git 1fdec59bffc11ae37eb51a1b9869f0696bfd5312)
Target: armv7m-none-unknown-eabi
Thread model: posix
InstalledDir: /opt/llvm11armv7m/bin

我認為這只是 gcc/gnu 的優化問題。

“volatile”修飾符是罪魁禍首。 它在編寫時不會調用類型擴展,因為它沒有意義。 但是在閱讀時,它總是調用擴展。 因為現在數據存儲在寄存器中,並且必須准備好在可見性限制的整個范圍內進行任何操作。 放棄“volatile”會刪除對數據的任何額外操作,但它也可以刪除使用變量的事實。

https://godbolt.org/z/cGvc8r6se

首先要知道R3中的data只有一個BYTE的意思

寄存器只有 32 位。 它們沒有任何其他“意義”。 寄存器必須包含與加載字節相同的值 - 因此是 UXTB。 以后的任何其他操作(例如添加一些東西需要整個寄存器包含正確的值。

一般來說,使用比 32 位更短的類型通常會增加一些開銷,因為 Cortex-Mx 處理器不對寄存器的“部分”進行操作。

要解決此問題,您需要在https://gcc.gnu.org/bugzilla/ 提交錯誤。 但是有兩種困難的情況。

  1. 有很多與“volatile”相關的bug,而且都沒有關閉,甚至大部分都沒有確認。 據我了解,開發者已經厭倦了與風車戰斗,甚至沒有反應。
  2. 要成功解決問題——你需要找到極端,那個寫下邪惡根源的那個。 作者身份等等。 你不會被允許進入別人的分支,只有最高級的才能進入 master。 但即使在這一刻之前,您也需要找到這種行為的原因,而這里又出現了問題。 GCC 代碼很大,你可以無限搜索。

我的個人意見: GCC 將 ARM kernel 寄存器視為快速 ZCD69B49357F06CD8E298D7 的一部分。 這個 memory 可以通過物理地址訪問,這只會增加問題。 那么,如果這個是memory,並且尺寸不匹配,那么,根據GCC,需要添加擴展命令。 為什么GCC在簡單訪問時使用正確的命令? - 好吧,他從 memory 到 memory 讀取。 強調 - “從記憶中”。 無論接下來發生什么,您都需要立即閱讀。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM