rjmccall added inline comments.
================ Comment at: clang/test/CodeGenCXX/attr-loader-uninitialized.cpp:23 +// CHECK: @nominally_value_init = global i32 undef +int nominally_value_init [[clang::loader_uninitialized]] = 4; + ---------------- JonChesterfield wrote: > rjmccall wrote: > > Quuxplusone wrote: > > > rjmccall wrote: > > > > JonChesterfield wrote: > > > > > Quuxplusone wrote: > > > > > > This test case is identical to line 36 of > > > > > > clang/test/Sema/attr-loader-uninitialized.cpp, where you say you > > > > > > don't want it to compile at all. > > > > > > > > > > > > I think you need a clearer idea of how this interacts with > > > > > > initializers. Is it merely supposed to eliminate the > > > > > > //zero-initialization// that happens before the user-specified > > > > > > construction/initialization, or is it supposed to compete with the > > > > > > user-specified construction/initialization? > > > > > > > > > > > > That is, for > > > > > > > > > > > > nontrivial unt [[clang::loader_uninitialized]]; > > > > > > > > > > > > is it merely supposed to call `unt::unt()` on a chunk of undef > > > > > > memory (instead of the usual chunk of zeroed memory), or is it > > > > > > supposed to skip the constructor entirely? And for > > > > > > > > > > > > int x [[clang::loader_uninitialized]] = foo(); > > > > > > > > > > > > is it merely supposed to call `foo()` and assign the result to a > > > > > > chunk of undef memory (instead of the usual chunk of zeroed > > > > > > memory), or is it supposed to skip the initialization entirely? > > > > > I think you commented while the first working piece of sema landed. > > > > > My thinking is relatively clear but my understanding of clang's > > > > > semantic analysis is a work in progress! > > > > > > > > > > Initializers (`= foo()`) are straightforward. Error on the basis that > > > > > the attribute effectively means `= undef`, and one should not have > > > > > two initializers. A test case is now added for that (and now passes). > > > > > > > > > > The codegen I want for a default constructed global marked with this > > > > > variable is: > > > > > - global variable allocated, with undef as the original value > > > > > - default constructor call synthesized > > > > > - said default constructor set up for invocation from crt, before > > > > > main, writing over the undef value > > > > > > > > > > Where the default constructor can be optimized as usual, e.g. if it > > > > > always writes a constant, we can init with that constant instead of > > > > > the undef and elide the constructor. > > > > > > > > > > I don't have that actually working yet - the constructor call is not > > > > > being emitted, so we just have the undef global. > > > > > > > > > > I think it's important to distinguish between the values of the bits > > > > > when the program is loaded and whether constructor/destructors are > > > > > called, as one could want any combination of the two. > > > > I think Arthur is suggesting that it would be useful to allow the > > > > attribute to be used in conjunction with an initializer in C++, since > > > > if the initializer has to be run dynamically, we can still meaningfully > > > > suppress the static zero-initialization. That is, we've accepted that > > > > it's useful to do this when *default-initializing* a global, but it's > > > > actually useful when doing *any* kind of dynamic initialization. > > > > > > > > Maybe we can leave it as an error in C++ when the initializer is a > > > > constant expression. Although that might be unnecessarily brittle if > > > > e.g. the constructor is `constexpr` in one library version but not > > > > another. > > > No, that's exctly what I mean. You seem to be holding two contradictory > > > ideas simultaneously: > > > > > > [[loader_uninitialized]] X x = X{}; // two initializers, therefore > > > error > > > > > > [[loader_uninitialized]] X x {}; // one initializer plus one > > > constructor, therefore fine > > > > > > In C++, these two declarations have identical semantics. It doesn't make > > > sense to say that one of them "calls a constructor" and the other one > > > "has an initializer." They're literally the same thing. > > > > > > Similarly in both C99 and C++ with plain old ints: > > > > > > [[loader_uninitialized]] int x = foo(); > > > > > > This means "call foo and dynamically initialize x with the result," just > > > as surely as > > > > > > [[loader_uninitialized]] X x = X(); > > > > > > means "call X::X and dynamically initialize x with the result." Having > > > one rule for dynamic initializers of primitive types and a separate rule > > > for dynamic initializers of class types doesn't work. > > > > > > Furthermore, "dynamic initialization" can be promoted to compile-time: > > > > > > [[loader_uninitialized]] int x = 42; > > > [[loader_uninitialized]] std::string_view x = "hello world"; > > > > > > It wouldn't make semantic sense to say that one of these has "two > > > initializers" and the other has "one initializer," because both of the > > > initializations end up happening at compile time and getting put into > > > .data. > > > Similarly in both C99 and C++ with plain old ints: > > > > C does not allow non-constant initialization of global variables. > > > > Let me try to explain what we're thinking here. If the variable provides > > both an initializer and this attribute, and the compiler can successfully > > perform constant initialization (as it's required to do in C, and as it > > often does in C++), then the attribute has probably become meaningless > > because we're not going to suppress that constant initialization. The > > compiler strongly prefers to diagnose meaningless attributes in some way, > > because they often indirectly indicate a bug. For example, maybe the > > initializer isn't supposed to be there but it's hidden behind a huge mess > > of macros; or maybe somebody added a constant initializer because they > > changed the variable schema to make that possible, and so the programmer > > should now remove the attribute and the dynamic initialization code that > > went along with it. Compared to that, the C++ use case of "allow a dynamic > > initializer but suppress static zero-initialization" is pretty marginal; > > that doesn't mean we absolutely shouldn't support it, but it does suggest > > that we shouldn't prioritize it over other aspects of the feature, like > > catching that possible bug. > I'm under the impression that the constructs are: > ``` > X x = X{}; // default construct an X and then call the copy constructor at x > X x {}; // default construct an X at the location of x > X x; // default construct an X at the location of x > ``` > > The C++ initialisation rules are very complicated so I won't be shocked to > have got that wrong. > > If your toolchain actually runs global constructors then there isn't much use > to marking C++ objects with this attribute, unless said constructor does > nothing and would thus leave the bytes still undef. > > Accepting default constructors (or, perhaps only accepting trivial types?) > would allow people to use this attribute with the approximation of C++ > available on systems that don't actually run the global constructors. > > Equally, if this seems too complicated, I'm happy to restrict this to C as > that's still an improvement on the asm and/or linker scripts I have available > today. > If your toolchain actually runs global constructors then there isn't much use > to marking C++ objects with this attribute, unless said constructor does > nothing and would thus leave the bytes still undef. That's not totally true. C++ still requires globals to be zero-initialized before it runs dynamic initializers, and the dynamic initializer is very likely to make that zero-initialization totally redundant. Arthur is right that there's no inherent reason that that only applies to dynamic *default* initialization, though, as opposed to any non-constant initialization. I'd prefer to avoid making this C-specific; Clang takes a lot of pride in trying to make feature cross-language as much as possible. We could just make the error about providing both the attribute and an initializer C-specific, though. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D74361/new/ https://reviews.llvm.org/D74361 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits