I would like use bit-fields to access low level memory. I'm aware of the non-portability of bitfields but they do appear to be consistently implemented on my platform (cortex-m4) on both clang and gcc. However, I found that assignment of the entire bitfield at once generates significantly more assembly instructions on clang than gcc. A simplified example of my goal is shown below:
struct B {
struct {
volatile int b:3;
volatile int c:3;
volatile int d:26;
} a;
} bmem;
constexpr B * const b = &bmem;
void fun() {
b->a = { .b = 1, .c = 2 };
}
When switching between armv7-a clang 21.1.0 and arm gcc 15.2.0, one can see that the gcc compiler optimizes the struct initialization to 3 instructions, while clang takes 16. It appears that clang treats the rhs values as volatile while gcc does not. I tried various permutations to reduce the instructions that clang generates, but I could not find a reliable way to make it work. The closest that I could do without a lot of extra code is marking the struct pointer b as pointer to volatile rather than the members of struct B. And then making it a union and using illegal (due to undefined behavior) union type punning. However I don't like this solution. Is there a straightforward way to indicate to the compiler that I would like to set this memory value without treating the initialization value as volatile?
Here is the union type punning version:
struct B {
union {
int u;
struct {
int b:3;
int c:3;
int d:26;
} a;
};
} bmem;
constexpr volatile B * const b = &bmem;
void fun() {
b->u = B { .a = { .b = 1, .c = 2 } }.u;
}
Additional information
Thank you for the comments below. For additional reference I'm looking to access to memory mapped hardware registers. I should have made it clear that I'm not trying to do thread communication although I can imagine that the solutions for thread communication could provide insight into how to do the memory mapped registers. Generally the status quo for this platform is use manufacture provided header files. These header files can take two different forms but both are based on volatile. The first form is volatile uint32_t registers plus a series of #define bit masks. The second form is a bit-field union like code example #2 above, except that the union and struct members are defined volatile. I've been using the first form extensively and I am experimenting with the second form to help reduce bit logic mistakes and have better type and range checking.
union type punning
Per the comments I looked into union type punning and found that both clang and gcc currently admit that it works with certain caveats. Per their caveats I'm a little concerned that I would make a mistake but using pointers incorrectly. I also prefer the type safety that code example #1 gives.
atomic builtin functions
I now tried these and found that void __atomic_store (type *ptr, type *val, int memorder) with memorder __ATOMIC_RELAXED gives me a working system. However I found it also doesn't seem to have type safety, allowing me to set any val to any ptr type for some reason. It also lacks the ability to use set the bitfield directly such as b->a.c = 3 without using an atomic_load and atomic_store. Where the volatile version does behave as I would like in that case. I also found that if I compile with -flto that I would have to mark the b instance with __attribute__((used)).
Profiling
On this platform I'm very aware of the cost of register setting due to keeping track of registers set during interrupts. In practice these bit-fields may be more than three values and the number of instructions scales with the number of bit-field members on clang. On this platform the pipeline is relatively straightforward such that the number of cycles is generally pretty proportional to the number of instructions in this case.