为什么GCCARM Cortex-M0在应该知道数据已经是uint8的情况下生成UXTB指令

问题描述

我正在使用 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
  • 加载 readb1 变量的 R2 地址
  • UXTB 扩展了 uint8 值???但是旋转参数是 0,所以基本上对 uint8 没有任何作用!
  • 将来自 R3 的数据存储为 BYTE 到 R2 的地址(我的变量)

为什么会这样?

首先要知道R3中的数据只有一个BYTE含义(它已经正确生成了LDRB)。其次,STRB 已经可以修剪 7..0 LSB 那么为什么要使用 UXTB 呢?

感谢您的澄清,

编辑: 编译器版本:

gcc version 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599] (GNU Tools for Arm Embedded Processors 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,#12]   ; (14 <fun+0x14>)
   8:   781b        ldrb    r3,[r3,#0]
   a:   7013        strb    r3,#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,#12]   ; (10 <fun+0x10>)
   2:   211a        movs    r1,#26
   4:   4b03        ldr     r3,#12]   ; (14 <fun+0x14>)
   6:   7011        strb    r1,#0]
   8:   781b        ldrb    r3,#0]
   a:   b2db        uxtb    r3,r3
   c:   7013        strb    r3,#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,r8)
  10:   00000000    .word   0x00000000
  14:   01234000    .word   0x01234000

用于 armv4t

00000000 <fun>:
   0:   231a        movs    r3,r8)
  10:   00000000    .word   0x00000000
  14:   01234000    .word   0x01234000

utxb 不见了。

我认为这只是一个错过的优化,窥视孔或其他。

正如已经回答的那样,当您使用非 gpr 大小的变量时,您可以预期和/或容忍编译器转换为寄存器大小。因编译器和目标而异,它们是在传入还是传出(读取变量时或在写入或使用之前)。

对于 x86,您可以分别访问寄存器的各个部分(或使用基于内存的操作数),您会看到它们不会这样做(在 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,#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 的优化问题。

,

首先要知道R3中的数据只有一个BYTE含义

寄存器只有 32 位。它们没有任何其他“意义”。该寄存器必须包含与加载字节相同的值 - 即 UXTB。之后的任何其他操作(例如添加某些内容需要整个寄存器包含正确的值。

一般来说,使用比 32 位更短的类型通常会增加一些开销,因为 Cortex-Mx 处理器不会对寄存器的“部分”进行操作。