This patch set is the draft of the code that accompanies our RFC located here: http://lists.llvm.org/pipermail/cfe-dev/2019-March/061607.html
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
- Build Status
Buildable 29037 Build 29036: arc lint + arc unit
Event Timeline
clang/lib/AST/DeclBase.cpp | ||
---|---|---|
1262 | Apologies that this is included. I've left a comment on clang/lib/AST/RecordFieldReorganizer.cpp describing our issues with this code. I also mention experimenting with fixing it in that comment as well, and in those experiments we've been able to remove this change from BuildDeclChain. We're just waiting for further advice before we change the code too rapidly. | |
clang/lib/AST/RecordFieldReorganizer.cpp | ||
58 | This part of the code where we rebuild the new DeclChain with the new order of fields is resulting in a Clang compiler that segfaults when building the Linux kernel. Any advice with how we can safely manage the DeclContext field chain is greatly appreciated here. In our experimentation (which isn't present in this diff here) we thought our best bet to was to save the LastDecl's next pointer before BuildDeclChain is called and then we set the NEW LastDecl's next pointer to it in an effort to preserve whatever context chaining is happening. Truthfully, we don't fully understand the machinery of the DeclContext or why our assumptions about this linked list of fields are incorrect. More troubleshooting information regarding this particular instance is on our github issue: https://github.com/clang-randstruct/llvm-project/issues/42 |
This needs a test under test/CodeGen at least.
clang/include/clang/AST/RecordFieldReorganizer.h | ||
---|---|---|
54 | I don't think we can use default_random_engine for this because the behaviour would need to be consistent between C++ standard library implementations, and the behaviour of default_random_engine is implementation defined. Similarly, I don't think that we can use std::shuffle (see note in https://en.cppreference.com/w/cpp/algorithm/random_shuffle ). |
clang/include/clang/AST/RecordFieldReorganizer.h | ||
---|---|---|
54 | Sure thing, we'll begin investigating alternatives. |
clang/include/clang/AST/RecordFieldReorganizer.h | ||
---|---|---|
54 | In that case the random numbers would be deterministic; however, std::shuffle would still vary by implementation (as mentioned in the note Peter linked to.) A quick search didn't reveal a deterministic shuffle in the LLVM code so you may have to implement one. |
I find it easier to understand the code by looking at the tests. When you add tests, please make sure you test for:
- Alignment attribute
- Packed attribute
- No unique address attribute
- Bit-fields
- Zero-width bit-field
- Zero or unsized array at end of struct
- C++ inheritance with vtables
- C++ virtual inheritance
- Anonymous unions (and the anonymous struct extension)
- Types with common initial sequence
I assume you only change field location, not their constructor's call order? i.e. class S { std::string a, b; }; always constructs a before b, but might reorder them.
There are cases where a developer could observe the difference in field order. For example, bit_cast or memcpy of a type which got reordered. What are the gotchas? Can you diagnose them?
clang/include/clang/AST/RandstructSeed.h | ||
---|---|---|
1 | This file needs the standard license comment. | |
6 | Returning a string is weird, and so it extern stuff. Is randomness a property of something in particular? Seems like Module or something similar might be the right place. | |
clang/include/clang/AST/RecordFieldReorganizer.h | ||
54 | Module::createRNG was created to support randomization of things. You should look at the discussion that led to its addition: in parts it tries to offer stable output when it can, and the idea was that you could inject a seed if you wanted to. Unfortunately the other randomization patches weren't committed, but they're also on Phabricator. | |
clang/include/clang/Basic/AttrDocs.td | ||
4161 | I'm not sure you need to document what happens when both are present. If people do it the message they get should be sufficient. | |
clang/lib/AST/RecordFieldReorganizer.cpp | ||
95 | https://en.cppreference.com/w/cpp/thread/hardware_destructive_interference_size will provide this. I have an RFC on how to implement it, haven't gotten to it. Just leave a FIXME. 64 is the right value, we all know ;-) |
Do you intend on moving this forward? It seems like a Good Thing overall, and I'd love to help review some more.
Yes. I'm sorry it's been in limbo so long. I haven't touched base with the rest of my team so I can't speak for them, but I'm interested in moving this along. I've been busy settling into work and post-university life lately.
From your initial review it sounds like there's quite a bit to be done by way of providing sufficient test coverage for this and some code cleanup. A few other kind contributors have left us advice on our github repo for some blocking issues with designated initializers.
I'll be reviewing all the advice we've been given here and on our repo and figuring out the best way to start making progress on this again. There's definitely a good basis here.
That said, I'm still interested in and would appreciate any advice or pointers to resources. I don't suppose there's an LLVM/Clang mentorship type program?
I think the essential functionality of this patch should be in LLVM and not Clang, so that all front-ends can benefit. Too many generally useful things are in Clang when they should be in LLVM (e.g. C ABI for ARM and x86; ranged switch statements). I opened an upstream bug to discuss this. https://github.com/clang-randstruct/llvm-project/issues/52
C and C++ layout decisions definitely belong in clang, not in LLVM. There might be opportunity to opt certain structures into re-layout through attributes, but generally speaking most of it needs to stay in clang.
My point is that most languages these days that intend to be compiled to machine code want compatibility with the C ABI, and randstruct will be part of that (and can be made compatible between languages by sharing the seed). LLVM knows what a struct is.
Basic stuff, sure LLVM can know about and move fields around, especially if clang slaps an attribute on the struct saying it's fine.
However, struct layout is deeply ingrained in code generation. Too deeply to just move it to LLVM. Consider this simple example:
template <int Size> struct Arr { int arr[Size]; }; struct A { int a, b, c; char d, e, f, g; }; Arr<__builtin_offsetof(A, d)> arr;
There's way more complex stuff that falls out of how C and C++ specify the language. That complexity really shouldn't be in scope for this patch.
Hi JF,
Could you elaborate on what I should be testing for here regarding the zero-width bit-field? I'm not sure zero-width bit-fields are a concern since I think they're illegal. I just tried compiling a "hello world" program with a struct that has a zero width bit-field as one of its members and the compiler emitted an error pointing at it.
error: named bit-field 'z' has zero width int z : 0; ^ 1 error generated.
For regular bit-fields the current implementation will keep adjacent bit-fields together and will preserve their original order but the overall group of adjacent bit-fields may relocate. My next revision will have a unit test describing and testing this behavior.
Still under development here: https://github.com/connorkuehl/llvm-project/tree/randstruct
Hi @connorkuehl,
I was asked by @kees to take a look at implementing this, with the view to support it in the Linux kernel. There doesn't seem to have been a lot of work on this recently. Is it effectively in limbo at the moment?
@void Cool! Yeah, I haven't done anything with this. The git tree in the quoted comment (https://github.com/connorkuehl/llvm-project/tree/randstruct) does have a lot more progress compared to this Phabricator submission in terms of some bug fixes and unit test coverage, though there's still room for improvement in terms of performance (and possibly correctness, iirc compiling Linux segfaulted Clang with those patches). Furthermore, the patchset is quite old, so I would be entirely shocked if it applied without much fuss on a recent LLVM tree. As you can see, the latest commit was in late 2019.
Best of luck! I hope at least some of it is helpful to you.
Hi @void, @connorkuehl
Reminding that I also participated in working on this, and back then in 2019 I did manage to compile and boot a working Linux kernel with this feature enabled in Clang. It was a fully built Fedora Linux 5.3 kernel. I remember though that @connorkuehl made various cleanups and refactoring following from my branch, but work stopped at some point and I don't remember at what state we left it it exactly.. :(
This is the Fedora Copr repo with the kernel RPM and all the LLVM/Clang package sources that used to build it: https://copr.fedorainfracloud.org/coprs/alonid/llvm-experimental
As for LLVM source, the git branch which was used back then is randstruct-demo-fc30 in my Github fork - https://github.com/da-x/llvm-project/
Thanks, @Dan and @connorkuehl!
I cherry-picked Connor's changes to top-of-tree LLVM. It wasn't too bad. I'll see how it works with the current Linux kernel. @jfb mentioned some really good issues having to do with C++. Because of how horrible it would be to support C++, I propose restricting this feature to C (probably a decision that's already been made).
This file needs the standard license comment.