Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does GCC guarantee size-matched accesses?

Tags:

c

gcc

assembly

arm

I'm not really sure how to ask this question succinctly, so I apologize if this is already asked and answered.

I am working on an ARM platform with a 32-bit pseudo-register access to a peripheral FIFO. Depending on the size of the read or write, a variable number of bytes are removed or added to the FIFO.

I wrote the following C code to perform a 16-bit (halfword) read:

a_u16_var = *(uint16_t*)&FIFO_REG;

and confirmed that it indeed resulted in an ldrh opcode in the object file.

Is this guaranteed? If I follow the same pattern for uint32_t and uint8_t, can I depend on the compiler generating ldr/str and ldrb/strb, respectively? If not, what's the best way to ensure the right size access? Inline assembly?

EDIT: More platform specifics:

  • Core: ARM Cortex-M33 (STM32H563ZI to be really specific)
  • Compiler: GNU arm-none-eabi-* version 11.3.1 (GNU Tools for STM32 11.3)
  • OS: FreeRTOS, if that counts
like image 465
Brian A. Henning Avatar asked Sep 19 '25 11:09

Brian A. Henning


1 Answers

With volatile, yes, GCC will try to use only a single access of width matching the type of the volatile access, if it's a size the machine can do natively.

I don't remember where I read this; it's not in https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html (which is mostly about what counts as a volatile access, and whether there's a separate read when you do tricky stuff like x = volatile_var = y;)

Otherwise no, zero guarantee, and you can construct test cases that tempt GCC into doing a single wider access, or accessing just the low or high byte for example, IIRC something like var &= 0xFF will get it to do a zero-extending byte load instead of half-word and separate zero-extend. And Which types on a 64-bit computer are naturally atomic in gnu C and gnu C++? -- meaning they have atomic reads, and atomic writes has another counter-example where it can use stp of a pair of 32-bit halves for non-volative, which isn't guaranteed to be a single 64-bit atomic access before ARMv8.4. Using volatile uint64_t makes it a single access that's safe for e.g. the Linux kernel to roll its own atomics.


For MMIO you definitely want volatile anyway!! But you're casting to a pointer-to-plain-uint16, not pointer-to-volatile, so that's bad.

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

The rules surrounding volatile accesses should be sufficient that you don't need -fno-strict-aliasing (which was mentioned in comments). Volatile accesses can't reorder (at compile time) with other volatile accesses, and the compiler doesn't assume anything about the value loaded or stored. As long as all your accesses to the MMIO register are volatile, there aren't any assumptions GCC can be making.

like image 153
Peter Cordes Avatar answered Sep 21 '25 04:09

Peter Cordes