efriedma added inline comments.
================ Comment at: clang/lib/CodeGen/CGExprConstant.cpp:1324 // This is a string literal initializing an array in an initializer. - return CGM.GetConstantArrayFromStringLiteral(E); + return E->isLValue() ? + CGM.GetAddrOfConstantStringFromLiteral(E).getPointer() : ---------------- nickdesaulniers wrote: > efriedma wrote: > > nickdesaulniers wrote: > > > efriedma wrote: > > > > efriedma wrote: > > > > > efriedma wrote: > > > > > > nickdesaulniers wrote: > > > > > > > efriedma wrote: > > > > > > > > nickdesaulniers wrote: > > > > > > > > > nickdesaulniers wrote: > > > > > > > > > > efriedma wrote: > > > > > > > > > > > nickdesaulniers wrote: > > > > > > > > > > > > efriedma wrote: > > > > > > > > > > > > > Maybe we should have a separate > > > > > > > > > > > > > ConstExprEmitterLValue... trying to handle both > > > > > > > > > > > > > LValues and RValues on the same codepath has been > > > > > > > > > > > > > problematic in the past. It's very easy for code to > > > > > > > > > > > > > get confused what it's actually trying to emit. > > > > > > > > > > > > So we'd have a `ConstExprEmitterLValue` class with some > > > > > > > > > > > > visitor methods, and a `ConstExprEmitterRValue` with > > > > > > > > > > > > other methods implemented? > > > > > > > > > > > Something like that. > > > > > > > > > > > > > > > > > > > > > > Actually thinking about it a bit more, not sure you need > > > > > > > > > > > to actually implement ConstExprEmitterLValue for now. > > > > > > > > > > > You might just be able to ensure we don't ever call > > > > > > > > > > > ConstExprEmitter with an lvalue. The current > > > > > > > > > > > ConstExprEmitter doesn't expect lvalues, and shouldn't > > > > > > > > > > > call itself with lvalues. (We bail on explicit > > > > > > > > > > > LValueToRValue conversions.) And Evaluate() shouldn't > > > > > > > > > > > actually evaluate the contents of an lvalue if it isn't > > > > > > > > > > > dereferenced, so there hopefully aren't any performance > > > > > > > > > > > issues using that codepath. > > > > > > > > > > > > > > > > > > > > > > In terms of implementation, I guess that's basically > > > > > > > > > > > restoring the destType->isReferenceType() that got > > > > > > > > > > > removed? (I know I suggested it, but I wasn't really > > > > > > > > > > > thinking about it...) > > > > > > > > > > One thing I think we may need to add to `ConstExprEmitter` > > > > > > > > > > is the ability to evaluate `CallExpr`s based on certain > > > > > > > > > > test case failures...does that seem right? > > > > > > > > > See also the calls to `constexpr f()` in > > > > > > > > > clang/test/CodeGenCXX/const-init-cxx1y.cpp > > > > > > > > The only things I know of that Evaluate() can't handle are > > > > > > > > CK_ToUnion, CK_ReinterpretMemberPointer, and > > > > > > > > DesignatedInitUpdateExpr. DesignatedInitUpdateExpr is related > > > > > > > > to the failures in test/CodeGenCXX/designated-init.cpp; I don't > > > > > > > > think the others show up in any of the testcases you've > > > > > > > > mentioned. (CK_ToUnion can't appear in C++ code. > > > > > > > > CK_ReinterpretMemberPointer is a `reinterpret_cast<T>` where T > > > > > > > > is a member pointer type.) > > > > > > > > > > > > > > > > Given none of those constructs show up in const-init-cxx1y.cpp, > > > > > > > > the only reason for it to fail is if we aren't correctly > > > > > > > > falling back for a construct the current code doesn't know how > > > > > > > > to handle. You shouldn't need to implement any new constructs. > > > > > > > in clang/test/CodeGenCXX/designated-init.cpp we have: > > > > > > > ``` > > > > > > > >> 22 namespace ModifyStaticTemporary { > > > > > > > >> > > > > > > > 23 struct A { int &&temporary; int x; }; > > > > > > > > > > > > > > 24 constexpr int f(int &r) { r *= 9; return r - 12; } > > > > > > > > > > > > > > 25 A a = { 6, f(a.temporary) }; > > > > > > > ``` > > > > > > > In the AST, that looks like: > > > > > > > ``` > > > > > > > | |-VarDecl 0x562b77df39b0 <line:25:3, col:29> col:5 used a > > > > > > > 'A':'ModifyStaticTemporary::A' cinit > > > > > > > | | `-ExprWithCleanups 0x562b77df3c68 <col:9, col:29> > > > > > > > 'A':'ModifyStaticTemporary::A' > > > > > > > | | `-InitListExpr 0x562b77df3bb8 <col:9, col:29> > > > > > > > 'A':'ModifyStaticTemporary::A' > > > > > > > | | |-MaterializeTemporaryExpr 0x562b77df3c08 <col:11> 'int' > > > > > > > xvalue extended by Var 0x562b77df39b0 'a' > > > > > > > 'A':'ModifyStaticTemporary::A' > > > > > > > | | | `-IntegerLiteral 0x562b77df3a18 <col:11> 'int' 6 > > > > > > > | | `-CallExpr 0x562b77df3b30 <col:14, col:27> 'int' > > > > > > > | | |-ImplicitCastExpr 0x562b77df3b18 <col:14> 'int (*)(int > > > > > > > &)' <FunctionToPointerDecay> > > > > > > > | | | `-DeclRefExpr 0x562b77df3ad0 <col:14> 'int (int &)' > > > > > > > lvalue Function 0x562b77df37a0 'f' 'int (int &)' > > > > > > > | | `-MemberExpr 0x562b77df3aa0 <col:16, col:18> 'int' > > > > > > > lvalue .temporary 0x562b77df35c0 > > > > > > > | | `-DeclRefExpr 0x562b77df3a80 <col:16> > > > > > > > 'A':'ModifyStaticTemporary::A' lvalue Var 0x562b77df39b0 'a' > > > > > > > 'A':'ModifyStaticTemporary::A' > > > > > > > ``` > > > > > > > (So, indeed no `DesignatedInitUpdateExpr`) but the call to the > > > > > > > `constexpr` `f()` updates the reference (to `54`). If I remove > > > > > > > the visitor for `MaterializeTemporaryExpr`, we fail to evaluate > > > > > > > `f` and end up emitting `6` rather than `54`. Doesn't that mean > > > > > > > that the fast path (`ConstExprEmitter`) needs to be able to > > > > > > > evaluate `CallExpr`? > > > > > > > > > > > > > > Or should `VisitInitListExpr` bail if any of the inits > > > > > > > `isa<MaterializeTemporaryExpr>` (or perhaps `isa<CallExpr>`)? > > > > > > There are a few related cases here. > > > > > > > > > > > > Case number one is when you have something like `int z(); A a = { > > > > > > z(), z() };`. There's no constant evaluation going on: you just > > > > > > emit two zero-initialized variables, and the runtime init > > > > > > initializes both of them. > > > > > > > > > > > > Case number two is when everything is obviously constant: something > > > > > > like `A a = { 1, 2 };` > > > > > > > > > > > > Case number three is when there are simple side-effects, and the > > > > > > standard requires we evaluate them at compile-time. Something like > > > > > > `A a = { 1, ++a.temporary };`. In this case, we need to ensure > > > > > > that we use Evaluate() to compute the value of both the temporary > > > > > > and the variable. The literal "1" is not the correct value to use. > > > > > > CodeGenModule::GetAddrOfGlobalTemporary is supposed to ensure we > > > > > > use the value from the evaluation of the variable as a whole (see > > > > > > comment "If the initializer of the extending declaration"). > > > > > > > > > > > > Case number four is when we can't constant-evaluate a variable as a > > > > > > whole, but we do evaluate some of the temporaries involved. > > > > > > Something like `int z(); A a = { 1, a.temporary += z() };` In this > > > > > > case, we constant-evaluate the temporary using the initial value, > > > > > > then emit runtime initialization to finish computing the value of > > > > > > the variable as a whole. > > > > > > > > > > > > You example should fall under case three. Both the temporary and > > > > > > the variable should be evaluated by Evaluate(). > > > > > > > > > > > > I'm not sure how the code ends up emitting the value 6, but > > > > > > hopefully that helps? > > > > > Oh, I think I see what's happening; the code that looks for the > > > > > temporary in GetAddrOfGlobalTemporary isn't reliable if the whole > > > > > variable isn't evaluated first. It ends up pulling out the result of > > > > > a partial evaluation, or something like that. > > > > > > > > > > Making EmitArrayInitialization/EmitRecordInitialization bail if they > > > > > see a MaterializeTemporaryExpr should deal with the issue, I think? > > > > > Not sure if you'd need to recursively visit all the initializers (I > > > > > don't remember what constructs allow lifetime extension off the top > > > > > of my head). > > > > To be more precise, what happens is that calling EvaluateAsLValue on > > > > the MaterializeTemporaryExpr actually *corrupts* the computed value of > > > > the temporary: the complete variable is evaluated earlier for other > > > > reasons, then EvaluateAsLValue overwrites the correct value we computed > > > > earlier with the wrong value. > > > > In this case, we need to ensure that we use Evaluate() to compute the > > > > value of both the temporary and the variable. > > > > > > Just triple checking, `Evaluate` is the "slow path" (i.e. > > > `VarDecl::evaluateValue`, `Expr::EvaluateAsLValue`, and > > > `Expr::EvaluateAsRValue`? > > > > > > > To be more precise, what happens is that calling EvaluateAsLValue on > > > > the MaterializeTemporaryExpr actually *corrupts* the computed value of > > > > the temporary > > > > > > So the slow path gets it wrong? But prior to this patch, that's was used > > > first before ConstExprEmitter? (Maybe I should add more comments about > > > fast vs slow path) > > > > > > --- > > > > > > > Not sure if you'd need to recursively visit all the initializers (I > > > > don't remember what constructs allow lifetime extension off the top of > > > > my head). > > > > > > I think I would; the last test case currently failing is > > > clang/test/CodeGenCXX/atomicinit.cpp: > > > ``` > > > struct X { > > > constexpr X(int n) : n(n) {} > > > short n; > > > char c = 6; > > > }; > > > > > > struct Y { > > > _Atomic(X) a; > > > _Atomic(int) b; > > > }; > > > Y y = { X(4), 5 }; > > > ``` > > > The AST for `y` looks like: > > > ``` > > > `-VarDecl 0x562bba0cad00 <line:11:1, col:17> col:3 y 'Y':'Y' cinit > > > `-ExprWithCleanups 0x562bba0e9f28 <col:7, col:17> 'Y':'Y' > > > `-InitListExpr 0x562bba0e9c20 <col:7, col:17> 'Y':'Y' > > > |-ImplicitCastExpr 0x562bba0e9ef8 <col:9, col:12> '_Atomic(X)' > > > <NonAtomicToAtomic> > > > | `-ImplicitCastExpr 0x562bba0e9ee0 <col:9, col:12> 'X':'X' > > > <ConstructorConversion> > > > | `-CXXConstructExpr 0x562bba0e9eb0 <col:9, col:12> 'X':'X' 'void > > > (X &&) noexcept' elidable > > > | `-MaterializeTemporaryExpr 0x562bba0e9c70 <col:9, col:12> > > > 'X':'X' xvalue > > > | `-CXXFunctionalCastExpr 0x562bba0e9b88 <col:9, col:12> > > > 'X':'X' functional cast to X <ConstructorConversion> > > > | `-CXXConstructExpr 0x562bba0e9a10 <col:9, col:12> 'X':'X' > > > 'void (int)' > > > | `-IntegerLiteral 0x562bba0cadc0 <col:11> 'int' 4 > > > `-ImplicitCastExpr 0x562bba0e9f10 <col:15> '_Atomic(int)' > > > <NonAtomicToAtomic> > > > `-IntegerLiteral 0x562bba0e9bb0 <col:15> 'int' 5 > > > ``` > > > so we'd need to peek through the casts to find that there was a > > > `MaterializeTemporaryExpr` in there, then bail? > > > Just triple checking, Evaluate is the "slow path" (i.e. > > > VarDecl::evaluateValue, Expr::EvaluateAsLValue, and > > > Expr::EvaluateAsRValue? > > > > Yes. > > > > >> To be more precise, what happens is that calling EvaluateAsLValue on the > > >> MaterializeTemporaryExpr actually *corrupts* the computed value of the > > >> temporary > > > So the slow path gets it wrong? But prior to this patch, that's was used > > > first before ConstExprEmitter? (Maybe I should add more comments about > > > fast vs slow path) > > > > The EvaluateAsLValue bug only shows up if you EvaluateAsLValue pieces of > > the initializer. If you use the slow path first, we never EvaluateAsLValue > > pieces of the initializer; we just evaluate the whole variable initializer > > in one evaluation. (Depending on the construct, we may have to evaluate it > > during semantic analysis; if we do, the evaluation is cached.) > > > > -------- > > > > > I think I would; the last test case currently failing is > > > clang/test/CodeGenCXX/atomicinit.cpp: > > > > I don't think that's the same issue? There, the MaterializeTemporaryExpr > > is immediately passed to a CXXConstructExpr, which returns an rvalue, so > > the final IR shouldn't actually reference the temporary. It looks like the > > issue is that VisitCXXConstructExpr is broken; it tries to look through a > > trivial move constructor, but the the operand of a move constructor is an > > lvalue, so the recursive visit doesn't work correctly. The following > > crashes even without your patch: > > > > ``` > > struct X { > > constexpr X(int n) : n(n) {} > > short n; > > char c = 6; > > }; > > struct Y { > > _Atomic(X) a; > > int b; > > }; > > int z; > > Y y = { X(4), z }; > > ``` > > > > You can probably just kill off the VisitCXXConstructExpr codepath... or if > > you want to try to repair it, I guess you can teach it to specifically > > handle only trivial constructors where the operand is a > > MaterializeTemporaryExpr. > > You can probably just kill off the VisitCXXConstructExpr codepath.. > > If I delete `ConstExprEmitter::VisitCXXConstructExpr` (the "fast path") then > wont the slow path then fail in the same way as your example above that > crashes even without my patch? I can't parse the question. What do you think is causing the _Atomic case to crash? I tried looking a bit more; my example might not actually be related to the CXXConstructExpr. There's something weird going on with the NonAtomicToAtomic cast. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D151587/new/ https://reviews.llvm.org/D151587 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits