The value of padding when storing an aggregate into memory

Hello all,

LangRef isn’t clear about the value of padding when an aggregate value is stored into memory, and I’d like to suggest that storing an aggregate fills padding with undef.

Here are a few clues that supports this change:

  • According to C17, the value of padding bytes when storing values in structures or unions is unspecified.

  • IPSCCP ignores padding and directly stores a constant aggregate if possible: https://godbolt.org/z/ddWq9z
    Memcpyopt ignores padding when copying an aggregate or storing a constant: https://godbolt.org/z/hY6ndd / https://godbolt.org/z/3WMP5a

  • Alive2 (with store operation updated) did not find any problematic transformation from LLVM unit tests and while running translation validation on a few C programs.

The patch is here: https://reviews.llvm.org/D86189

Thanks,
Juneyoung

Interesting topic. Is any such optimization reachable from C?

Hello Alexander,

Interesting topic. Is any such optimization reachable from C?
Yes, I think so - both PassBuilder and PassManagerBuilder add MemCpyOpt & IPSCCP in the default pass pipeline.

Juneyoung

Hello Juneyoung,

Interesting topic. Is any such optimization reachable from C?

>

Yes, I think so - both PassBuilder and PassManagerBuilder add MemCpyOpt &
IPSCCP in the default pass pipeline.

In my experience, clang (unlike gcc) preserves the naive meaning of the source C code in dealing with padding in structs -- assignments of whole structs copy the padding while assignments to members of structs retain their old padding. The only exception I know is returning structs from functions, as was pointed by Greg Parker in https://twitter.com/gparker/status/1193996158768578561 .

I don't think this is required by any standard and it would be interesting to find out why exactly it's done this way (and whether the mentioned exception is a bug). This is especially wasteful in case of bit-fields.

OTOH it's not clear to me this has anything to do with the transformations of LLVM IR that you mentioned. Perhaps clang just doesn't emit any IR that could trigger those optimizations.

In my experience, clang (unlike gcc) preserves the naive meaning of the
source C code in dealing with padding in structs – assignments of whole
structs copy the padding while assignments to members of structs retain
their old padding. The only exception I know is returning structs from
functions, as was pointed by Greg Parker in
https://twitter.com/gparker/status/1193996158768578561 .

OTOH it’s not clear to me this has anything to do with the
transformations of LLVM IR that you mentioned. Perhaps clang just
doesn’t emit any IR that could trigger those optimizations.

Thank you for the info. I also tried to find an example written in C that leaves load/store of an aggregate, and I couldn’t. Rather than directly loading/storing aggregates, the lowered IR uses field-wise load/store or memcpy/memmove of an aggregate.
I skimmed through unit tests in clang, and using pointers to member functions might introduce load/store of aggregates since it uses a pair of i32 or i64.
But (1) it isn’t clear whether the member function pointer type has padding (2) if padding exists, a valid c++ program can store data to it.

Until I find a working example, I have to say that the change is for explaining the relevant optimizations in LLVM, even if the passes exist in the pipeline.
I think the change suggested in the patch is still valid because we need it to explain the optimizations.

Juneyoung