Page MenuHomePhabricator

Automatic variable initialization
ClosedPublic

Authored by jfb on Nov 15 2018, 2:52 PM.

Details

Summary

Add an option to initialize automatic variables with either a pattern or with
zeroes. The default is still that automatic variables are uninitialized. Also
add attributes to request uninitialized on a per-variable basis, mainly to disable
initialization of large stack arrays when deemed too expensive.

This isn't meant to change the semantics of C and C++. Rather, it's meant to be
a last-resort when programmers inadvertently have some undefined behavior in
their code. This patch aims to make undefined behavior hurt less, which
security-minded people will be very happy about. Notably, this means that
there's no inadvertent information leak when:

  • The compiler re-uses stack slots, and a value is used uninitialized.
  • The compiler re-uses a register, and a value is used uninitialized.
  • Stack structs / arrays / unions with padding are copied.

This patch only addresses stack and register information leaks. There's many
more infoleaks that we could address, and much more undefined behavior that
could be tamed. Let's keep this patch focused, and I'm happy to address related
issues elsewhere.

To keep the patch simple, only some undef is removed for now, see
replaceUndef. The padding-related infoleaks are therefore not all gone yet.
This will be addressed in a follow-up, mainly because addressing padding-related
leaks should be a stand-alone option which is implied by variable
initialization.

There are three options when it comes to automatic variable initialization:

0. Uninitialized

  This is C and C++'s default. It's not changing. Depending on code
  generation, a programmer who runs into undefined behavior by using an
  uninialized automatic variable may observe any previous value (including
  program secrets), or any value which the compiler saw fit to materialize on
  the stack or in a register (this could be to synthesize an immediate, to
  refer to code or data locations, to generate cookies, etc).

1. Pattern initialization

  This is the recommended initialization approach. Pattern initialization's
  goal is to initialize automatic variables with values which will likely
  transform logic bugs into crashes down the line, are easily recognizable in
  a crash dump, without being values which programmers can rely on for useful
  program semantics. At the same time, pattern initialization tries to
  generate code which will optimize well. You'll find the following details in
  `patternFor`:

  - Integers are initialized with repeated 0xAA bytes (infinite scream).
  - Vectors of integers are also initialized with infinite scream.
  - Pointers are initialized with infinite scream on 64-bit platforms because
    it's an unmappable pointer value on architectures I'm aware of. Pointers
    are initialize to 0x000000AA (small scream) on 32-bit platforms because
    32-bit platforms don't consistently offer unmappable pages. When they do
    it's usually the zero page. As people try this out, I expect that we'll
    want to allow different platforms to customize this, let's do so later.
  - Vectors of pointers are initialized the same way pointers are.
  - Floating point values and vectors are initialized with a negative quiet NaN
    with repeated 0xFF payload (e.g. 0xffffffff and 0xffffffffffffffff). NaNs are nice
    (here, anways) because they propagate on arithmetic, making it more likely
    that entire computations become NaN when a single uninitialized value
    sneaks in.
  - Arrays are initialized to their homogeneous elements' initialization
    value, repeated. Stack-based Variable-Length Arrays (VLAs) are
    runtime-initialized to the allocated size (no effort is made for negative
    size, but zero-sized VLAs are untouched even if technically undefined).
  - Structs are initialized to their heterogeneous element's initialization
    values. Zero-size structs are initialized as 0xAA since they're allocated
    a single byte.
  - Unions are initialized using the initialization for the largest member of
    the union.

  Expect the values used for pattern initialization to change over time, as we
  refine heuristics (both for performance and security). The goal is truly to
  avoid injecting semantics into undefined behavior, and we should be
  comfortable changing these values when there's a worthwhile point in doing
  so.

  Why so much infinite scream? Repeated byte patterns tend to be easy to
  synthesize on most architectures, and otherwise memset is usually very
  efficient. For values which aren't entirely repeated byte patterns, LLVM
  will often generate code which does memset + a few stores.

2. Zero initialization

  Zero initialize all values. This has the unfortunate side-effect of
  providing semantics to otherwise undefined behavior, programs therefore
  might start to rely on this behavior, and that's sad. However, some
  programmers believe that pattern initialization is too expensive for them,
  and data might show that they're right. The only way to make these
  programmers wrong is to offer zero-initialization as an option, figure out
  where they are right, and optimize the compiler into submission. Until the
  compiler provides acceptable performance for all security-minded code, zero
  initialization is a useful (if blunt) tool.

I've been asked for a fourth initialization option: user-provided byte value.
This might be useful, and can easily be added later.

Why is an out-of band initialization mecanism desired? We could instead use
-Wuninitialized! Indeed we could, but then we're forcing the programmer to
provide semantics for something which doesn't actually have any (it's
uninitialized!). It's then unclear whether int derp = 0; lends meaning to 0,
or whether it's just there to shut that warning up. It's also way easier to use
a compiler flag than it is to manually and intelligently initialize all values
in a program.

Why not just rely on static analysis? Because it cannot reason about all dynamic
code paths effectively, and it has false positives. It's a great tool, could get
even better, but it's simply incapable of catching all uses of uninitialized
values.

Why not just rely on memory sanitizer? Because it's not universally available,
has a 3x performance cost, and shouldn't be deployed in production. Again, it's
a great tool, it'll find the dynamic uses of uninitialized variables that your
test coverage hits, but it won't find the ones that you encounter in production.

What's the performance like? Not too bad! Previous publications [0] have cited
2.7 to 4.5% averages. We've commmitted a few patches over the last few months to
address specific regressions, both in code size and performance. In all cases,
the optimizations are generally useful, but variable initialization benefits
from them a lot more than regular code does. We've got a handful of other
optimizations in mind, but the code is in good enough shape and has found enough
latent issues that it's a good time to get the change reviewed, checked in, and
have others kick the tires. We'll continue reducing overheads as we try this out
on diverse codebases.

Is it a good idea? Security-minded folks think so, and apparently so does the
Microsoft Visual Studio team [1] who say "Between 2017 and mid 2018, this
feature would have killed 49 MSRC cases that involved uninitialized struct data
leaking across a trust boundary. It would have also mitigated a number of bugs
involving uninitialized struct data being used directly.". They seem to use pure
zero initialization, and claim to have taken the overheads down to within noise.
Don't just trust Microsoft though, here's another relevant person asking for
this [2]. It's been proposed for GCC [3] and LLVM [4] before.

What are the caveats? A few!

  • Variables declared in unreachable code, and used later, aren't initialized. This goto, Duff's device, other objectionable uses of switch. This should instead be a hard-error in any serious codebase.
  • Volatile stack variables are still weird. That's pre-existing, it's really the language's fault and this patch keeps it weird. We should deprecate volatile [5].
  • As noted above, padding isn't fully handled yet.

I don't think these caveats make the patch untenable because they can be
addressed separately.

Should this be on by default? Maybe, in some circumstances. It's a conversation
we can have when we've tried it out sufficiently, and we're confident that we've
eliminated enough of the overheads that most codebases would want to opt-in.
Let's keep our precious undefined behavior until that point in time.

How do I use it:

  1. On the command-line:

    -ftrivial-auto-var-init=uninitialized (the default) -ftrivial-auto-var-init=pattern -ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang
  2. Using an attribute:

    int dont_initialize_me __attribute((uninitialized));

    [0]: https://users.elis.ugent.be/~jsartor/researchDocs/OOPSLA2011Zero-submit.pdf [1]: https://twitter.com/JosephBialek/status/1062774315098112001 [2]: https://outflux.net/slides/2018/lss/danger.pdf [3]: https://gcc.gnu.org/ml/gcc-patches/2014-06/msg00615.html [4]: https://github.com/AndroidHardeningArchive/platform_external_clang/commit/776a0955ef6686d23a82d2e6a3cbd4a6a882c31c [5]: http://wg21.link/p1152

rdar://problem/39131435

I've also posted an RFC to cfe-dev: http://lists.llvm.org/pipermail/cfe-dev/2018-November/060172.html

Diff Detail

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
kcc added a comment.Nov 15 2018, 4:35 PM

exciting. adding more folks FYI and to help review

jfb added a comment.Nov 15 2018, 4:43 PM

This isn't meant to change the semantics of C and C++.

As I said in the cfe-dev thread, that's exactly what it does. Specifically I think this is essentially defining a new dialect of C++, which I have massive concerns about. Additionally, as much as we might claim it's a transitional measure, we all know that's not how it'll be used in practice.

(cross-posting from cfe-dev)

I suspected that you’d disagree with zero-initialization. :-)

I tried to outline why I think it’s important:

  • Security-minded people think the need it. They might be right.
  • We need data to prove that they don’t need it.

Forgive my use of silly rhetoric: I’m sure you agree that more data is good! Here’s what I propose, since you cared enough to voice your opinion: let’s worth together to narrow the performance gap. I’ve got a handful of optimization opportunities, and you know the backend better than I do. Once we’ve addressed the issues we know about, I’m sure other types of codebases will surface other performance issues, let’s address those too.

Once people who think they need zero initialization are proven wrong though compiler optimizations, we can deprecate (and eventually remove / repurpose) the flag. If they’re not proven wrong… we’ll have learned something.

Sounds fair?

jfb edited the summary of this revision. (Show Details)Nov 15 2018, 4:43 PM
alex added a subscriber: alex.Nov 15 2018, 8:38 PM

There is also D54473 [sanitizers] Initial implementation for -fsanitize=init-locals.

kcc added a comment.Nov 16 2018, 3:51 PM

Would it be possible to extend test/Sema/uninit-variables.c to verify that the new option
doesn't break -Wuninitialized?

jfb updated this revision to Diff 174471.Nov 16 2018, 3:54 PM
  • Make sure uninit-variables.c doesn't break.
jfb added a comment.Nov 16 2018, 3:55 PM
In D54604#1301807, @kcc wrote:

Would it be possible to extend test/Sema/uninit-variables.c to verify that the new option
doesn't break -Wuninitialized?

Done.

emaste added a subscriber: emaste.Nov 20 2018, 1:29 PM
emaste added a subscriber: markj.
dmajor added a subscriber: dmajor.Nov 21 2018, 11:02 AM
pcc added inline comments.Dec 3 2018, 10:03 PM
include/clang/Driver/ToolChain.h
355

I wouldn't introduce this function because nothing is overriding it (for now, at least).

lib/CodeGen/CGDecl.cpp
968

*repeated

977

Do you have test coverage for vectors?

978

Is it necessary to qualify cast with llvm::? It doesn't seem to be qualified elsewhere in this file. (Same below.)

1159

Maybe use User::operands() to enumerate operands here?

1641

Is it possible to rewrite this function using early returns to avoid all the indentation below for the VLA case? I'd also put the code for non-VLAs first to make this function easier to understand (on my first reading I somehow got the impression that this function is *only* handling VLAs). I'm thinking maybe something like:

if (trivialAutoVarInit == LangOptions::TrivialAutoVarInitKind::Uninitialized)
  return;

if (!getContext().getTypeSizeInChars(type).isZero()) {
  // handle non-VLAs
}

auto *VlaType = dyn_cast_or_null<VariableArrayType>(getContext().getAsArrayType(type));
if (!VlaType)
  return;

// handle VLAs
1683

I think we need to check that the size is non-zero here. Otherwise we're going to clobber whatever comes after the VLA if its size is zero (I know that C forbids zero-sized VLAs, but I believe we support them as a GNU extension).

test/CodeGenCXX/trivial-auto-var-init.cpp
6

Since this is your only test involving structs and arrays, you probably want to test them beyond the fact that they don't contain undef. I also believe that this is testing for the absence of undef before the first label and not the total absence of undef in the output.

jfb updated this revision to Diff 177262.Dec 7 2018, 11:33 AM
jfb marked 10 inline comments as done.
  • Make sure uninit-variables.c doesn't break.
  • Address Peter's comments, improve tests.
jfb added a comment.Dec 7 2018, 11:35 AM

I've addressed @pcc's comments. I'll now update the patch to mandate a -Xclang option for zero-init (otherwise it'll warn and pattern-init instead), and remove documentation for zero-init. I'm also thinking of changing float init to negative NaN with infinite scream payload.

include/clang/Driver/ToolChain.h
355

I expect to do so for particular targets, so it's only a question of time, and for now it makes internal testing easier (I can build my compiler and override it for testing, before sending that upstream). I'd rather do it now, since it reduces merge conflicts as we kick the tires internally.

lib/CodeGen/CGDecl.cpp
977

test/CodeGenCXX/auto-var-init.cpp has those tests. I was lazy with this patch and hadn't updated the tests yet, in case people asked me to change the values. I've done so in this update.

1683

Indeed! Thanks for catching that. I thought I'd done it, and it turns out on O0 it wasn't doing anything bad, and neither was it in O2 because the GEPs were marked inbounds so the codegen magically turned into a memset based on size!

test/CodeGenCXX/trivial-auto-var-init.cpp
6

I was lazy with testing in case the design changed significantly. The updated test explains what I was going for here :)

jfb updated this revision to Diff 177328.Dec 7 2018, 2:46 PM
  • Add an ugly option to enable zero init
jfb added a comment.Dec 7 2018, 2:48 PM

I added an option that's required when using clang directly (*not* -cc1!) If you use -ftrivial-auto-var-init=zero without -enable-trivial-auto-var-init-zero-knowning-it-will-be-removed-from-clang in clang it'll generate a warning and change initialization to pattern initialization instead. That's roughly along the lines people were going for on cfe-dev.

jfb updated this revision to Diff 177331.Dec 7 2018, 2:53 PM
  • Update warning-flags.c
dexonsmith added inline comments.Dec 7 2018, 3:25 PM
include/clang/Basic/DiagnosticDriverKinds.td
412

s/knowning/knowing/ gives -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang

jfb updated this revision to Diff 177341.Dec 7 2018, 3:42 PM
  • Fix typo
jfb updated this revision to Diff 177600.Dec 10 2018, 2:55 PM
  • Make sure uninit-variables.c doesn't break.
  • Address Peter's comments, improve tests.
  • Add an ugly option to enable zero init
  • Update warning-flags.c
  • Fix typo
  • Use negative NaN with repeated 0xFF payload for all floating-point types. This is potentially faster to initialize than pure positive no-payload NaNs.
jfb edited the summary of this revision. (Show Details)Dec 10 2018, 3:00 PM

For the record: I'm OK with this direction. (I somewhat prefer removing the -enable-long-wordy-thing option and instead automatically disabling the zero option for clang release builds, but I'm OK with either approach.)

include/clang/Basic/Attr.td
3134–3142

I'm unconvinced that we should add this attribute; it seems insufficiently motivated to me, and harmful to portability. I think we should have some way of saying "leave this uninitialized", but zero- and pattern-initialization really seem like things that should simply be written in the source code in the normal way.

I think it would be reasonable to have a __uninitialized__ keyword or similar that can be used as the initializer of an explicitly-uninitialized variable. If you want an attribute for this (for compatibility with other compilers), [[clang::uninitialized]] also seems reasonable. But absent a strong motivation, I do not think we should have an attribute that specifies a particular initial value.

include/clang/Basic/AttrDocs.td
3710–3714

If we keep the attribute, we should not document the zero mode. We don't need to make it discoverable.

include/clang/Basic/DiagnosticDriverKinds.td
409–412

I think this should be an error instead of a warning. We shouldn't silently do something different from what was requested.

jfb updated this revision to Diff 178129.Dec 13 2018, 1:46 PM
jfb marked 3 inline comments as done.
  • Don't document 'zero'.
  • Make drv_trivial_auto_var_init_zero_disabled an error instead of a warning.
  • Change the parameter: we only have [[clang::uninitialized]] now.
jfb added a comment.Dec 13 2018, 1:50 PM

For the record: I'm OK with this direction. (I somewhat prefer removing the -enable-long-wordy-thing option and instead automatically disabling the zero option for clang release builds, but I'm OK with either approach.)

I'm inclined to go with what @kcc wants, because I want the data he'll gather. For my own use, I don't really care. One thing I could do: check __DATE__ and generate a warning if it's above a certain value and zero is used? i.e. check the date at which the compiler was compiled (not when it's used!) and error out if it was compiled after a certain point in time? I'm throwing out options that could makes folks more comfortable with zero...

include/clang/Basic/Attr.td
3134–3142

I definitely think that we want *some* attribute: the usecase is primarily to turn on automatic initialization for entire projects, and then turning it off manually where you measure unacceptable runtime costs (until you can do something better, or until the compiler fixes its perf problem). For example, large stack arrays would be something that you'd want to slap this attribute on.

That being said, I agree that __uninitialized__ or [[clang::uninitialized]] would serve this purpose. I figured that mirroring the command-line options would be more desirable, but I'm not sure anymore. I've updated the patch to have [[clang::uninitialized]].

I don't think we want user code to write it in the source: that's forcing user code to add semantics which their code otherwise doesn't have. It's existing practice, but IMO it's bad.

jfb edited the summary of this revision. (Show Details)Dec 13 2018, 1:51 PM
jfb marked an inline comment as done.Dec 13 2018, 1:54 PM
pcc added a comment.Dec 14 2018, 2:37 PM

Mostly looking good, a few style nits.

include/clang/Basic/DiagnosticDriverKinds.td
409

s/Warning/Error/ here? Then I think you won't need to adjust the list of warnings without warning flags.

lib/CodeGen/CGDecl.cpp
993

No else after return (same below)

1037

Remove braces

1183

No else after return, no braces.

test/CodeGenCXX/trivial-auto-var-init.cpp
180

PATTERN:

196

PATTERN:

jfb updated this revision to Diff 178301.Dec 14 2018, 2:55 PM
jfb marked 6 inline comments as done.
  • Address @pcc's comments.
jfb added a comment.Dec 14 2018, 2:55 PM
In D54604#1331711, @pcc wrote:

Mostly looking good, a few style nits.

Thanks! I think I addressed all of your comments.

jfb updated this revision to Diff 178304.Dec 14 2018, 3:14 PM
  • Rebase

Thanks, you've addressed my comments to my satisfaction. (Not approving as I've not done a detailed review of the implementation, only of the approach, but it looks like your review with @pcc is nearly complete.)

In D54604#1330356, @jfb wrote:

For the record: I'm OK with this direction. (I somewhat prefer removing the -enable-long-wordy-thing option and instead automatically disabling the zero option for clang release builds, but I'm OK with either approach.)

I'm inclined to go with what @kcc wants, because I want the data he'll gather. For my own use, I don't really care. One thing I could do: check __DATE__ and generate a warning if it's above a certain value and zero is used? i.e. check the date at which the compiler was compiled (not when it's used!) and error out if it was compiled after a certain point in time? I'm throwing out options that could makes folks more comfortable with zero...

If you want to go there, I'd be OK with that. I don't personally think it's necessary.

pcc accepted this revision.Dec 17 2018, 5:09 PM

LGTM

test/CodeGenCXX/trivial-auto-var-init-attribute.cpp
14

This test should be updated with the new attribute spelling, and should also test initialization. The tests below can be removed as well.

This revision is now accepted and ready to land.Dec 17 2018, 5:09 PM
jfb updated this revision to Diff 178566.Dec 17 2018, 5:14 PM
  • Update attribute test.
jfb marked an inline comment as done.Dec 17 2018, 5:14 PM
Closed by commit rL349442: Automatic variable initialization (authored by jfb, committed by ). · Explain WhyDec 17 2018, 9:15 PM
This revision was automatically updated to reflect the committed changes.
pcc added inline comments.Jan 16 2019, 3:39 PM
cfe/trunk/include/clang/Driver/Options.td
1657 ↗(On Diff #178590)

I noticed that this introduces a parsing ambiguity on the command line: the flag -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang can be interpreted either as this flag or as -e nable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang, meaning "tell the linker to use that long symbol name as the program's entry point". Worth fixing (e.g. by renaming to --enable-...)? It's probably a good idea to rename it every so often just to keep people on their toes anyway.

jfb marked an inline comment as done.Jan 16 2019, 4:39 PM
jfb added inline comments.
cfe/trunk/include/clang/Driver/Options.td
1657 ↗(On Diff #178590)

Don't we have the same issue with:

include/clang/Driver/CC1Options.td:def emit_html : Flag<["-"], "emit-html">,
include/clang/Driver/CC1Options.td:def emit_module : Flag<["-"], "emit-module">,
include/clang/Driver/CC1Options.td:def emit_module_interface : Flag<["-"], "emit-module-interface">,
include/clang/Driver/CC1Options.td:def emit_header_module : Flag<["-"], "emit-header-module">,
include/clang/Driver/CC1Options.td:def emit_pch : Flag<["-"], "emit-pch">,
include/clang/Driver/CC1Options.td:def emit_llvm_bc : Flag<["-"], "emit-llvm-bc">,
include/clang/Driver/CC1Options.td:def emit_llvm_only : Flag<["-"], "emit-llvm-only">,
include/clang/Driver/CC1Options.td:def emit_codegen_only : Flag<["-"], "emit-codegen-only">,
include/clang/Driver/CC1Options.td:def emit_obj : Flag<["-"], "emit-obj">,
include/clang/Driver/CC1Options.td:def emit_llvm_uselists : Flag<["-"], "emit-llvm-uselists">,
include/clang/Driver/CC1Options.td:def enable_split_dwarf : Flag<["-"], "enable-split-dwarf">,
include/clang/Driver/CC1Options.td:def enable_split_dwarf_EQ : Joined<["-"], "enable-split-dwarf=">,
include/clang/Driver/CC1Options.td:def error_on_deserialized_pch_decl : Separate<["-"], "error-on-deserialized-decl">,
include/clang/Driver/CC1Options.td:def error_on_deserialized_pch_decl_EQ : Joined<["-"], "error-on-deserialized-decl=">,
include/clang/Driver/Options.td:def emit_ast : Flag<["-"], "emit-ast">,
include/clang/Driver/Options.td:def emit_llvm : Flag<["-"], "emit-llvm">, Flags<[CC1Option]>, Group<Action_Group>,
include/clang/Driver/Options.td:def exported__symbols__list : Separate<["-"], "exported_symbols_list">;
include/clang/Driver/Options.td:def e : JoinedOrSeparate<["-"], "e">, Group<Link_Group>;
include/clang/Driver/Options.td:def enable_trivial_var_init_zero : Joined<["-"], "enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang">,

?

pcc added inline comments.Jan 16 2019, 4:57 PM
cfe/trunk/include/clang/Driver/Options.td
1657 ↗(On Diff #178590)

Just the ones in Options.td, I believe (since this is a driver-only flag).

Not sure if a similar problem was mentioned already or not, but the following program:

void alloc_sock(int *err);
int try_send();

int send(int len) {
  int err;
  if (len) {
    err = -1; 
    alloc_sock(&err);
    err = try_send();
  }
  return -1; 
}

produces a redundant store of 0xaaaaaaaa to err:

0000000000000000 <send>:
void alloc_sock(int *err);
int try_send();

int send(int len) {
   0:   50                      push   %rax
  int err;
   1:   c7 44 24 04 aa aa aa    movl   $0xaaaaaaaa,0x4(%rsp)
   8:   aa 
  if (len) {
   9:   85 ff                   test   %edi,%edi
   b:   74 1d                   je     2a <send+0x2a>
    err = -1;
   d:   c7 44 24 04 ff ff ff    movl   $0xffffffff,0x4(%rsp)
  14:   ff 
  15:   48 8d 7c 24 04          lea    0x4(%rsp),%rdi
    alloc_sock(&err);
  1a:   e8 00 00 00 00          callq  1f <send+0x1f>
                        1b: R_X86_64_PLT32      alloc_sock-0x4
    err = try_send();
  1f:   31 c0                   xor    %eax,%eax
  21:   e8 00 00 00 00          callq  26 <send+0x26>
                        22: R_X86_64_PLT32      try_send-0x4
  26:   89 44 24 04             mov    %eax,0x4(%rsp)
  }
  return -1;
  2a:   b8 ff ff ff ff          mov    $0xffffffff,%eax
  2f:   59                      pop    %rcx
  30:   c3                      retq

Not sure whether this isn't a bug in DSE though.

(Overall, since this is an "unsupported" feature, is it ok to file Bugzilla bugs for it?)

I think it's absolutely fair game to file bugs for this.

jfb added a comment.Jan 29 2019, 9:18 AM

Not sure if a similar problem was mentioned already or not, but the following program:

Yes please file a bug. Seems like the optimizer should sink the store enough that it appears only once on each path.

glider added a comment.Feb 5 2019, 5:48 AM

I think doing separate stores of 0xAA into struct members instead of copying part of .rodata over it will let us do a much better job in optimizing away redundant stores.
I've opened https://bugs.llvm.org/show_bug.cgi?id=40605 to track that.

Herald added a project: Restricted Project. · View Herald TranscriptFeb 5 2019, 5:48 AM