GCC 是否保证大小匹配的访问?

问题描述 投票:0回答:1

我不太确定如何简洁地提出这个问题,所以如果已经提出并回答了这个问题,我深表歉意。

我正在开发一个 ARM 平台,该平台具有对外设 FIFO 的 32 位伪寄存器访问权限。根据读取或写入的大小,可变数量的字节被删除或添加到 FIFO 中。

我编写了以下 C 代码来执行 16 位(半字)读取:

a_u16_var = *(uint16_t*)&FIFO_REG;

并确认它确实在目标文件中产生了

ldrh
操作码。

这有保证吗?如果我对

uint32_t
uint8_t
遵循相同的模式,我可以依赖编译器分别生成
ldr
/
str
ldrb
/
strb
吗?如果没有,确保正确大小的访问的最佳方法是什么?内联汇编?

编辑:更多平台细节:

  • 核心:ARM Cortex-M33(具体来说是STM32H563ZI)
  • 编译器:GNU arm-none-eabi-* 版本 11.3.1(STM32 11.3 的 GNU 工具)
  • 操作系统:FreeRTOS,如果重要的话
c assembly gcc arm
1个回答
0
投票

对于

volatile
,是的,GCC 将尝试仅使用与易失性访问类型匹配的宽度的单个访问,如果它是机器本机可以执行的大小。

我不记得在哪里读过这篇文章;它不在 https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html 中(这主要是关于什么算作易失性访问,以及当您执行像

x = volatile_var = y;
这样棘手的事情时是否有单独的读取)

否则,零保证,并且您可以构造测试用例来诱使 GCC 进行单个更广泛的访问,或者仅访问低字节或高字节,例如,IIRC 类似

var &= 0xFF
之类的东西将使其执行零扩展字节加载而不是半字和单独的零扩展。 64 位计算机上的哪些类型在 gnu C 和 gnu C++ 中自然是原子的? -- 意味着它们具有原子读取,原子写入还有另一个反例,它可以使用一对 32 位半体的
stp
进行非易失性,但不能保证是单个 64 位ARMv8.4 之前的原子访问。使用
volatile uint64_t
使其成为安全的单一访问,例如Linux 内核推出自己的原子。


对于 MMIO 无论如何你绝对想要

volatile
!!但是您要转换为指向普通 uint16 的指针,而不是指向易失性的指针,所以这很糟糕。

a_u16_var = *(volatile uint16_t*)&FIFO_REG;
// should be safe in GNU C for what you're looking for

围绕

volatile
访问的规则应该足够了,您不需要
-fno-strict-aliasing
(在评论中提到过)。易失性访问不能与其他易失性访问一起重新排序(在编译时),并且编译器不会假设有关加载或存储的值的任何信息。只要您对 MMIO 寄存器的所有访问都是
volatile
,GCC 就不会做出任何假设。

© www.soinside.com 2019 - 2024. All rights reserved.