Brian A. Henning
Brian A. Henning

Reputation: 1511

Does GCC guarantee size-matched accesses?

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:

Upvotes: 4

Views: 107

Answers (1)

Peter Cordes
Peter Cordes

Reputation: 365247

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.

Upvotes: 4

Related Questions