First off I am developing for a microcontroller so RAM and ROM usage are priorities.
I realise this may read as a bug report or not specific enough. If I don't get any answers here I will file it as such.
I like using static const
structs to initialise stack structures to defaults. In most cases the default struct is all zeros. I prefer to do this with static const
structs rather than a memset
(memset or struct assignment, static const assignment)
My current toolchain is arm-none-eabi-gcc-4_7_3
, compiling for a Cortex M4 target with optimisation -Os
.
I have noticed the following; GCC produces different code if I explicitly initialise my static const
struct to zero than if I do not (static const struct foo;
vs static const struct foo = {0};
). In particular, it allocates the uninitialised static const
structs to memory and performs copy operations.
Here is a code sample:
struct foo {int foo; int bar;};
struct bar {int bar[20];};
static const struct foo foo1_init, foo2_init = {0};
static const struct bar bar1_init, bar2_init = {0};
extern struct foo foo1, foo2;
extern struct bar bar1, bar2;
void init_foo1(void)
{
foo1 = foo1_init;
}
void init_foo2(void)
{
foo2 = foo2_init;
}
void init_bar1(void)
{
bar1 = bar1_init;
}
void init_bar2(void)
{
bar2 = bar2_init;
}
Compiled, this produces the following assembler listing (rearranged and trimmed for brevity):
396 .section .bss.foo1_init,"aw",%nobits
397 .align 2
398 .set .LANCHOR0,. + 0
401 foo1_init:
402 0000 00000000 .space 8
402 00000000
40 .L2:
41 0010 00000000 .word .LANCHOR0
42 0014 00000000 .word foo1
55: **** foo1 = foo1_init;
32 .loc 1 55 0
33 0000 034A ldr r2, .L2
34 0002 044B ldr r3, .L2+4
35 0004 92E80300 ldmia r2, {r0, r1}
36 0008 83E80300 stmia r3, {r0, r1}
67 .L5:
68 000c 00000000 .word foo2
60: **** foo2 = foo2_init;
60 0000 024B ldr r3, .L5
61 0002 0022 movs r2, #0
62 0004 1A60 str r2, [r3, #0]
63 0006 5A60 str r2, [r3, #4]
389 .section .bss.bar1_init,"aw",%nobits
390 .align 2
391 .set .LANCHOR1,. + 0
394 bar1_init:
395 0000 00000000 .space 80
395 00000000
395 00000000
395 00000000
395 00000000
98 .L8:
99 0010 00000000 .word .LANCHOR1
100 0014 00000000 .word bar1
65: **** bar1 = bar1_init;
89 .loc 1 65 0
90 0002 0349 ldr r1, .L8
91 0004 0348 ldr r0, .L8+4
92 0006 5022 movs r2, #80
93 0008 FFF7FEFF bl memcpy
130 .L11:
131 0010 00000000 .word bar2
70: **** bar2 = bar2_init;
121 .loc 1 70 0
122 0002 0021 movs r1, #0
123 0004 5022 movs r2, #80
124 0006 0248 ldr r0, .L11
125 0008 FFF7FEFF bl memset
We can see that for foo2 = init_foo2
and bar2 = init_bar2
the compiler has optimised the copies down to storing zeros to foo2
directly or calling memset
for bar2
.
We can see that for foo1 = init_foo1
and bar1 = init_bar1
the compiler is performing explicit copies, loading to and saving from registers for foo1
and calling memcpy
for foo2
.
I have a few questions:
- Is this expected GCC operation? I would expect the uninitialised
static const
structs to follow the same path inside GCC as the initialisedstatic const
structs and so produce the same output. - Does this happen for other versions of ARM GCC? I do not have other versions to hand, and all online
C
to assembly compilers are in factC++
compilers. - Does this happen for other target architectures of GCC? Again, I do not have other versions to hand.