GCC does not optimise a struct copy of uninitialised static const
<p>First off I am developing for a microcontroller so RAM and ROM usage are priorities.</p>

<p>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.</p>

<p>I like using <code>static const</code> structs to initialise stack structures to defaults. In most cases the default struct is all zeros. I prefer to do this with <code>static const</code> structs rather than a <code>memset</code> (<a href="https://stackoverflow.com/questions/1998752/memset-or-value-initialization-to-zero-out-a-struct">memset or struct assignment</a>, <a href="https://stackoverflow.com/questions/6891720/initialize-reset-struct-to-zero-null">static const assignment</a>)</p>

<p>My current toolchain is <code>arm-none-eabi-gcc-4_7_3</code>, compiling for a Cortex M4 target with optimisation <code>-Os</code>.</p>

<p>I have noticed the following; GCC produces different code if I explicitly initialise my <code>static const</code> struct to zero than if I do not (<code>static const struct foo;</code> vs <code>static const struct foo = {0};</code>). In particular, it allocates the uninitialised <code>static const</code> structs to memory and performs copy operations.</p>

<p>Here is a code sample:</p>

<pre><code>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;
}
</code></pre>

<p>Compiled, this produces the following assembler listing (rearranged and trimmed for brevity):</p>

<pre><code> 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
</code></pre>

<p>We can see that for <code>foo2 = init_foo2</code> and <code>bar2 = init_bar2</code> the compiler has optimised the copies down to storing zeros to <code>foo2</code> directly or calling <code>memset</code> for <code>bar2</code>.</p>

<p>We can see that for <code>foo1 = init_foo1</code> and <code>bar1 = init_bar1</code> the compiler is performing explicit copies, loading to and saving from registers for <code>foo1</code> and calling <code>memcpy</code> for <code>foo2</code>.</p>

<p>I have a few questions:</p>

<ol>
<li>Is this expected GCC operation? I would expect the uninitialised <code>static const</code> structs to follow the same path inside GCC as the initialised <code>static const</code> structs and so produce the same output.</li>
<li>Does this happen for other versions of ARM GCC? I do not have other versions to hand, and all online <code>C</code> to assembly compilers are in fact <code>C++</code> compilers.</li>
<li>Does this happen for other target architectures of GCC? Again, I do not have other versions to hand.</li>
</ol>