6

Consider the following code:

typedef struct {
  uint32_t a : 1;
  uint32_t b : 1;
  uint32_t c : 30;
} Foo1;

void Fun1(void) {
  volatile Foo1 foo = (Foo1) {.a = 1, .b = 1, .c = 1};
}

This general pattern comes up quite a bit when using bit fields to punch registers in embedded applications. Using a recent ARM gcc compiler (e.g. gcc 8.2 or gcc 7.3) with -O3 and -std=c11, I get the following assembly:

  sub sp, sp, #8
  movs r3, #7
  str r3, [sp, #4]
  add sp, sp, #8
  bx lr

This is pretty much exactly what you want and expect; Foo is not volatile, so the initialization of each bit can be combined together into the literal 0x7 before finally being stored to the volatile variable (register) foo.

However, it's convenient to be able to manipulate the raw contents of the entire register which gives rise to an anonymous implementation of the bit field:

typedef union {
  struct {
    uint32_t a : 1;
    uint32_t b : 1;
    uint32_t c : 30;
  };
  uint32_t raw;
} Foo2;

void Fun2(void) {
  volatile Foo2 foo = (Foo2) {.a = 1, .b = 1, .c = 1};
}

Unfortunately, the resulting assembly is not so optimized:

  sub sp, sp, #8
  ldr r3, [sp, #4]
  orr r3, r3, #1
  str r3, [sp, #4]
  ldr r3, [sp, #4]
  orr r3, r3, #2
  str r3, [sp, #4]
  ldr r3, [sp, #4]
  and r3, r3, #7
  orr r3, r3, #4
  str r3, [sp, #4]
  add sp, sp, #8
  bx lr

For a densely packed register, a read-modify-write of each bit can get... expensive.

What's special about the union / anonymous struct that prevents gcc from optimizing the initialization like the pure struct?

timrau
  • 22,578
  • 4
  • 51
  • 64
user2465116
  • 388
  • 2
  • 10
  • 1
    Look at answer toward the bottom here [C global anonymous struct / union](https://stackoverflow.com/questions/32906637/c-global-anonymous-struct-union). In the union, it must provide offsets for both the struct and `uint32_t` and then bitfield address to the anonymous struct on top of that. The named struct simply provides bitfield access. The jist I get is that gcc can optimize named structs more easily than anonymous structs. – David C. Rankin Jul 28 '19 at 02:41
  • Does this answer your question? [C global anonymous struct / union](https://stackoverflow.com/questions/32906637/c-global-anonymous-struct-union) – user26742873 Feb 16 '21 at 03:06

1 Answers1

0

I hope I could answer your question. The problem is GCC compiler has some predefined rules about C to Assembly conversions, and when you make an union of one struct and one uint32_t it doesn't have a predefined pattern and that's why the resulting assembly it's not optimized as well as the first example.

I suggest you to use casting to solve the problem.

typedef struct {
  uint32_t a : 1;
  uint32_t b : 1;
  uint32_t c : 30;
} Foo1;

void Fun1(void) {
  volatile Foo1 foo = (Foo1) {.a = 1, .b = 1, .c = 1};
  volatile uint32_t rawA = *((uint32_t *) &foo);
  volatile uint32_t rawB = *((uint32_t *) &foo + sizeof(uint32_t);
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Angel
  • 1
  • 3