https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94600
--- Comment #3 from Richard Biener <rguenth at gcc dot gnu.org> --- Confirmed on arm. The odd thing is that the optimized GIMPLE for foo() is much nicer: foo () { <bb 2> [local count: 153437707]: MEM[(volatile struct t0 *)655404B] ={v} a0[0]; MEM[(volatile struct t0 *)655408B] ={v} a0[1]; MEM[(volatile struct t0 *)655412B] ={v} a0[2]; MEM[(volatile struct t0 *)655416B] ={v} a0[3]; MEM[(volatile struct t0 *)655420B] ={v} a0[4]; MEM[(volatile struct t0 *)655424B] ={v} a0[5]; return; while for bar() we have additional stmts to construct the stack objects though the actual stores are the same: <bb 2> [local count: 1073741824]: a01 = {}; a02 = {}; a03 = {}; a04 = {}; a05 = {}; MEM[(struct *)&a00] = 33556023; MEM[(volatile struct t0 *)655404B] ={v} a00; _38 = MEM[(struct *)&a01]; _39 = _38 & 33521664; _40 = _39 | 33558455; MEM[(struct *)&a01] = _40; MEM[(volatile struct t0 *)655408B] ={v} a01; _41 = MEM[(struct *)&a02]; _42 = _41 & 33521664; _43 = _42 | 167774200; MEM[(struct *)&a02] = _43; MEM[(volatile struct t0 *)655412B] ={v} a02; _44 = MEM[(struct *)&a03]; _45 = _44 & 33521664; _46 = _45 | 33554453; MEM[(struct *)&a03] = _46; MEM[(volatile struct t0 *)655416B] ={v} a03; _47 = MEM[(struct *)&a04]; _48 = _47 & 33521664; _49 = _48 | 33554453; MEM[(struct *)&a04] = _49; MEM[(volatile struct t0 *)655420B] ={v} a04; _50 = MEM[(struct *)&a05]; _51 = _50 & 33521664; _52 = _51 | 33554453; MEM[(struct *)&a05] = _52; MEM[(volatile struct t0 *)655424B] ={v} a05; a00 ={v} {CLOBBER}; a01 ={v} {CLOBBER}; a02 ={v} {CLOBBER}; a03 ={v} {CLOBBER}; a04 ={v} {CLOBBER}; a05 ={v} {CLOBBER}; return; I suspect that the RTL expansion for foo() applies "premature" optimization via constant folding the aggregate initializer and thus compiling this into partly initializing the destination which isn't valid for volatile qualified LHSs. For bar() there's nothing to optimize for it.