diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -257,6 +257,8 @@ - Fix handling of comments in function like macros so they are ignored in -CC mode. (`#60887 `_) +- Fix incorrect merging of lambdas across modules. + (`#60985 `_) Bug Fixes to Compiler Builtins diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -900,7 +900,7 @@ bool HasICEInit : 1; bool CheckedForICEInit : 1; - Stmt *Value; + LazyDeclStmtPtr Value; APValue Evaluated; EvaluatedStmt() diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -395,7 +395,7 @@ unsigned NumCaptures : 15; /// The number of explicit captures in this lambda. - unsigned NumExplicitCaptures : 13; + unsigned NumExplicitCaptures : 12; /// Has known `internal` linkage. unsigned HasKnownInternalLinkage : 1; @@ -404,6 +404,10 @@ /// mangling in the Itanium C++ ABI. unsigned ManglingNumber : 31; + /// The index of this lambda within its context declaration. This is not in + /// general the same as the mangling number. + unsigned IndexInContext; + /// The declaration that provides context for this lambda, if the /// actual DeclContext does not suffice. This is used for lambdas that /// occur within default arguments of function parameters within the class @@ -424,7 +428,7 @@ : DefinitionData(D), DependencyKind(DK), IsGenericLambda(IsGeneric), CaptureDefault(CaptureDefault), NumCaptures(0), NumExplicitCaptures(0), HasKnownInternalLinkage(0), ManglingNumber(0), - MethodTyInfo(Info) { + IndexInContext(0), MethodTyInfo(Info) { IsLambda = true; // C++1z [expr.prim.lambda]p4: @@ -1772,18 +1776,31 @@ /// the declaration context suffices. Decl *getLambdaContextDecl() const; - /// Set the mangling number and context declaration for a lambda - /// class. - void setLambdaMangling(unsigned ManglingNumber, Decl *ContextDecl, - bool HasKnownInternalLinkage = false) { + /// Retrieve the index of this lambda within the context declaration returned + /// by getLambdaContextDecl(). + unsigned getLambdaIndexInContext() const { assert(isLambda() && "Not a lambda closure type!"); - getLambdaData().ManglingNumber = ManglingNumber; - getLambdaData().ContextDecl = ContextDecl; - getLambdaData().HasKnownInternalLinkage = HasKnownInternalLinkage; + return getLambdaData().IndexInContext; } - /// Set the device side mangling number. - void setDeviceLambdaManglingNumber(unsigned Num) const; + /// Information about how a lambda is numbered within its context. + struct LambdaNumbering { + Decl *ContextDecl = nullptr; + unsigned IndexInContext = 0; + unsigned ManglingNumber = 0; + unsigned DeviceManglingNumber = 0; + bool HasKnownInternalLinkage = false; + }; + + /// Set the mangling numbers and context declaration for a lambda class. + void setLambdaNumbering(LambdaNumbering Numbering); + + // Get the mangling numbers and context declaration for a lambda class. + LambdaNumbering getLambdaNumbering() const { + return {getLambdaContextDecl(), getLambdaIndexInContext(), + getLambdaManglingNumber(), getDeviceLambdaManglingNumber(), + hasKnownLambdaInternalLinkage()}; + } /// Retrieve the device side mangling number. unsigned getDeviceLambdaManglingNumber() const; diff --git a/clang/include/clang/AST/ExternalASTSource.h b/clang/include/clang/AST/ExternalASTSource.h --- a/clang/include/clang/AST/ExternalASTSource.h +++ b/clang/include/clang/AST/ExternalASTSource.h @@ -371,7 +371,7 @@ /// \param Source the external AST source. /// /// \returns a pointer to the AST node. - T* get(ExternalASTSource *Source) const { + T *get(ExternalASTSource *Source) const { if (isOffset()) { assert(Source && "Cannot deserialize a lazy pointer without an AST source"); @@ -379,6 +379,14 @@ } return reinterpret_cast(Ptr); } + + /// Retrieve the address of the AST node pointer. Deserializes the pointee if + /// necessary. + T **getAddressOfPointer(ExternalASTSource *Source) const { + // Ensure the integer is in pointer form. + (void)get(Source); + return reinterpret_cast(&Ptr); + } }; /// A lazy value (of type T) that is within an AST node of type Owner, diff --git a/clang/include/clang/AST/MangleNumberingContext.h b/clang/include/clang/AST/MangleNumberingContext.h --- a/clang/include/clang/AST/MangleNumberingContext.h +++ b/clang/include/clang/AST/MangleNumberingContext.h @@ -27,6 +27,9 @@ /// Keeps track of the mangled names of lambda expressions and block /// literals within a particular context. class MangleNumberingContext { + // The index of the next lambda we encounter in this context. + unsigned LambdaIndex = 0; + public: virtual ~MangleNumberingContext() {} @@ -55,6 +58,11 @@ /// given call operator within the device context. No device number is /// assigned if there's no device numbering context is associated. virtual unsigned getDeviceManglingNumber(const CXXMethodDecl *) { return 0; } + + // Retrieve the index of the next lambda appearing in this context, which is + // used for deduplicating lambdas across modules. Note that this is a simple + // sequence number and is not ABI-dependent. + unsigned getNextLambdaIndex() { return LambdaIndex++; } }; } // end namespace clang diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -2092,6 +2092,11 @@ : nullptr; } + void setConditionVariableDeclStmt(DeclStmt *CondVar) { + assert(hasVarStorage()); + getTrailingObjects()[varOffset()] = CondVar; + } + Stmt *getInit() { return hasInitStorage() ? getTrailingObjects()[initOffset()] : nullptr; @@ -2324,6 +2329,11 @@ : nullptr; } + void setConditionVariableDeclStmt(DeclStmt *CondVar) { + assert(hasVarStorage()); + getTrailingObjects()[varOffset()] = CondVar; + } + SwitchCase *getSwitchCaseList() { return FirstCase; } const SwitchCase *getSwitchCaseList() const { return FirstCase; } void setSwitchCaseList(SwitchCase *SC) { FirstCase = SC; } @@ -2487,6 +2497,11 @@ : nullptr; } + void setConditionVariableDeclStmt(DeclStmt *CondVar) { + assert(hasVarStorage()); + getTrailingObjects()[varOffset()] = CondVar; + } + SourceLocation getWhileLoc() const { return WhileStmtBits.WhileLoc; } void setWhileLoc(SourceLocation L) { WhileStmtBits.WhileLoc = L; } @@ -2576,6 +2591,8 @@ /// the init/cond/inc parts of the ForStmt will be null if they were not /// specified in the source. class ForStmt : public Stmt { + friend class ASTStmtReader; + enum { INIT, CONDVAR, COND, INC, BODY, END_EXPR }; Stmt* SubExprs[END_EXPR]; // SubExprs[INIT] is an expression or declstmt. SourceLocation LParenLoc, RParenLoc; @@ -2603,10 +2620,18 @@ /// If this ForStmt has a condition variable, return the faux DeclStmt /// associated with the creation of that condition variable. + DeclStmt *getConditionVariableDeclStmt() { + return reinterpret_cast(SubExprs[CONDVAR]); + } + const DeclStmt *getConditionVariableDeclStmt() const { return reinterpret_cast(SubExprs[CONDVAR]); } + void setConditionVariableDeclStmt(DeclStmt *CondVar) { + SubExprs[CONDVAR] = CondVar; + } + Expr *getCond() { return reinterpret_cast(SubExprs[COND]); } Expr *getInc() { return reinterpret_cast(SubExprs[INC]); } Stmt *getBody() { return SubExprs[BODY]; } diff --git a/clang/include/clang/Sema/ExternalSemaSource.h b/clang/include/clang/Sema/ExternalSemaSource.h --- a/clang/include/clang/Sema/ExternalSemaSource.h +++ b/clang/include/clang/Sema/ExternalSemaSource.h @@ -230,6 +230,11 @@ return false; } + /// Notify the external source that a lambda was assigned a mangling number. + /// This enables the external source to track the correspondence between + /// lambdas and mangling numbers if necessary. + virtual void AssignedLambdaNumbering(const CXXRecordDecl *Lambda) {} + /// LLVM-style RTTI. /// \{ bool isA(const void *ClassID) const override { diff --git a/clang/include/clang/Sema/MultiplexExternalSemaSource.h b/clang/include/clang/Sema/MultiplexExternalSemaSource.h --- a/clang/include/clang/Sema/MultiplexExternalSemaSource.h +++ b/clang/include/clang/Sema/MultiplexExternalSemaSource.h @@ -360,6 +360,9 @@ bool MaybeDiagnoseMissingCompleteType(SourceLocation Loc, QualType T) override; + // Inform all attached sources that a mangling number was assigned. + void AssignedLambdaNumbering(const CXXRecordDecl *Lambda) override; + /// LLVM-style RTTI. /// \{ bool isA(const void *ClassID) const override { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7108,10 +7108,9 @@ Expr *TrailingRequiresClause); /// Number lambda for linkage purposes if necessary. - void handleLambdaNumbering( - CXXRecordDecl *Class, CXXMethodDecl *Method, - std::optional> Mangling = - std::nullopt); + void handleLambdaNumbering(CXXRecordDecl *Class, CXXMethodDecl *Method, + std::optional + NumberingOverride = std::nullopt); /// Endow the lambda scope info with the relevant properties. void buildLambdaScope(sema::LambdaScopeInfo *LSI, CXXMethodDecl *CallOperator, diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -560,6 +560,10 @@ llvm::DenseMap> AnonymousDeclarationsForMerging; + /// Map from numbering information for lambdas to the corresponding lambdas. + llvm::DenseMap, NamedDecl *> + LambdaDeclarationsForMerging; + /// Key used to identify LifetimeExtendedTemporaryDecl for merging, /// containing the lifetime-extending declaration and the mangling number. using LETemporaryKey = std::pair; @@ -1101,7 +1105,13 @@ /// they might contain a deduced return type that refers to a local type /// declared within the function. SmallVector, 16> - PendingFunctionTypes; + PendingDeducedFunctionTypes; + + /// The list of deduced variable types that we have not yet read, because + /// they might contain a deduced type that refers to a local type declared + /// within the variable. + SmallVector, 16> + PendingDeducedVarTypes; /// The list of redeclaration chains that still need to be /// reconstructed, and the local offset to the corresponding list @@ -1139,6 +1149,11 @@ 2> PendingObjCExtensionIvarRedeclarations; + /// Members that have been added to classes, for which the class has not yet + /// been notified. CXXRecordDecl::addedMember will be called for each of + /// these once recursive deserialization is complete. + SmallVector, 4> PendingAddedClassMembers; + /// The set of NamedDecls that have been loaded, but are members of a /// context that has been merged into another context where the corresponding /// declaration is either missing or has not yet been loaded. @@ -2082,6 +2097,8 @@ llvm::MapVector> &LPTMap) override; + void AssignedLambdaNumbering(const CXXRecordDecl *Lambda) override; + /// Load a selector from disk, registering its ID if it exists. void LoadSelector(Selector Sel); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -6622,6 +6622,10 @@ } bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const { + // Caution: this function is called by the AST reader during deserialization, + // so it cannot rely on AST invariants being met. Non-trivial accessors + // should be avoided, along with any traversal of redeclaration chains. + if (X == Y) return true; @@ -6757,6 +6761,11 @@ if (const auto *VarX = dyn_cast(X)) { const auto *VarY = cast(Y); if (VarX->getLinkageInternal() == VarY->getLinkageInternal()) { + // During deserialization, we might compare variables before we load + // their types. Assume the types will end up being the same. + if (VarX->getType().isNull() || VarY->getType().isNull()) + return true; + if (hasSameType(VarX->getType(), VarY->getType())) return true; diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -2919,13 +2919,12 @@ DC, *TInfoOrErr, Loc, DCXX->getLambdaDependencyKind(), DCXX->isGenericLambda(), DCXX->getLambdaCaptureDefault())) return D2CXX; - ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl()); + CXXRecordDecl::LambdaNumbering Numbering = DCXX->getLambdaNumbering(); + ExpectedDecl CDeclOrErr = import(Numbering.ContextDecl); if (!CDeclOrErr) return CDeclOrErr.takeError(); - D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr, - DCXX->hasKnownLambdaInternalLinkage()); - D2CXX->setDeviceLambdaManglingNumber( - DCXX->getDeviceLambdaManglingNumber()); + Numbering.ContextDecl = *CDeclOrErr; + D2CXX->setLambdaNumbering(Numbering); } else if (DCXX->isInjectedClassName()) { // We have to be careful to do a similar dance to the one in // Sema::ActOnStartCXXMemberDeclarations diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2356,12 +2356,15 @@ if (auto *S = Init.dyn_cast()) return cast(S); - return cast_or_null(Init.get()->Value); + auto *Eval = getEvaluatedStmt(); + return cast(Eval->Value.isOffset() + ? Eval->Value.get(getASTContext().getExternalSource()) + : Eval->Value.get(nullptr)); } Stmt **VarDecl::getInitAddress() { if (auto *ES = Init.dyn_cast()) - return &ES->Value; + return ES->Value.getAddressOfPointer(getASTContext().getExternalSource()); return Init.getAddrOfPtr1(); } @@ -2498,7 +2501,7 @@ bool IsConstantInitialization) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); - const auto *Init = cast(Eval->Value); + const auto *Init = getInit(); assert(!Init->isValueDependent()); // We only produce notes indicating why an initializer is non-constant the @@ -2582,7 +2585,7 @@ "already evaluated var value before checking for constant init"); assert(getASTContext().getLangOpts().CPlusPlus && "only meaningful in C++"); - assert(!cast(Eval->Value)->isValueDependent()); + assert(!getInit()->isValueDependent()); // Evaluate the initializer to check whether it's a constant expression. Eval->HasConstantInitialization = diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1646,10 +1646,15 @@ return getLambdaData().ContextDecl.get(Source); } -void CXXRecordDecl::setDeviceLambdaManglingNumber(unsigned Num) const { +void CXXRecordDecl::setLambdaNumbering(LambdaNumbering Numbering) { assert(isLambda() && "Not a lambda closure type!"); - if (Num) - getASTContext().DeviceLambdaManglingNumbers[this] = Num; + getLambdaData().ManglingNumber = Numbering.ManglingNumber; + if (Numbering.DeviceManglingNumber) + getASTContext().DeviceLambdaManglingNumbers[this] = + Numbering.DeviceManglingNumber; + getLambdaData().IndexInContext = Numbering.IndexInContext; + getLambdaData().ContextDecl = Numbering.ContextDecl; + getLambdaData().HasKnownInternalLinkage = Numbering.HasKnownInternalLinkage; } unsigned CXXRecordDecl::getDeviceLambdaManglingNumber() const { diff --git a/clang/lib/AST/ODRDiagsEmitter.cpp b/clang/lib/AST/ODRDiagsEmitter.cpp --- a/clang/lib/AST/ODRDiagsEmitter.cpp +++ b/clang/lib/AST/ODRDiagsEmitter.cpp @@ -1742,6 +1742,7 @@ return true; } + // Note, these calls can trigger deserialization. const Expr *FirstInit = FirstParam->getInit(); const Expr *SecondInit = SecondParam->getInit(); if ((FirstInit == nullptr) != (SecondInit == nullptr)) { diff --git a/clang/lib/Sema/MultiplexExternalSemaSource.cpp b/clang/lib/Sema/MultiplexExternalSemaSource.cpp --- a/clang/lib/Sema/MultiplexExternalSemaSource.cpp +++ b/clang/lib/Sema/MultiplexExternalSemaSource.cpp @@ -341,3 +341,9 @@ } return false; } + +void MultiplexExternalSemaSource::AssignedLambdaNumbering( + const CXXRecordDecl *Lambda) { + for (auto *Source : Sources) + Source->AssignedLambdaNumbering(Lambda); +} diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8232,12 +8232,12 @@ const VarDecl *DefVD = nullptr; // If there is no initializer - this can not be a constant expression. - if (!Var->getAnyInitializer(DefVD)) return true; + const Expr *Init = Var->getAnyInitializer(DefVD); + if (!Init) + return true; assert(DefVD); - if (DefVD->isWeak()) return false; - EvaluatedStmt *Eval = DefVD->ensureEvaluatedStmt(); - - Expr *Init = cast(Eval->Value); + if (DefVD->isWeak()) + return false; if (Var->getType()->isDependentType() || Init->isValueDependent()) { // FIXME: Teach the constant evaluator to deal with the non-dependent parts diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -389,22 +389,14 @@ void Sema::handleLambdaNumbering( CXXRecordDecl *Class, CXXMethodDecl *Method, - std::optional> Mangling) { - - ContextRAII ManglingContext(*this, Class->getDeclContext()); - - if (Mangling) { - bool HasKnownInternalLinkage; - unsigned ManglingNumber, DeviceManglingNumber; - Decl *ManglingContextDecl; - std::tie(HasKnownInternalLinkage, ManglingNumber, DeviceManglingNumber, - ManglingContextDecl) = *Mangling; - Class->setLambdaMangling(ManglingNumber, ManglingContextDecl, - HasKnownInternalLinkage); - Class->setDeviceLambdaManglingNumber(DeviceManglingNumber); + std::optional NumberingOverride) { + if (NumberingOverride) { + Class->setLambdaNumbering(*NumberingOverride); return; } + ContextRAII ManglingContext(*this, Class->getDeclContext()); + auto getMangleNumberingContext = [this](CXXRecordDecl *Class, Decl *ManglingContextDecl) -> MangleNumberingContext * { @@ -419,11 +411,10 @@ return &Context.getManglingNumberContext(DC); }; + CXXRecordDecl::LambdaNumbering Numbering; MangleNumberingContext *MCtx; - Decl *ManglingContextDecl; - std::tie(MCtx, ManglingContextDecl) = + std::tie(MCtx, Numbering.ContextDecl) = getCurrentMangleNumberContext(Class->getDeclContext()); - bool HasKnownInternalLinkage = false; if (!MCtx && (getLangOpts().CUDA || getLangOpts().SYCLIsDevice || getLangOpts().SYCLIsHost)) { // Force lambda numbering in CUDA/HIP as we need to name lambdas following @@ -433,15 +424,19 @@ // Also force for SYCL, since we need this for the // __builtin_sycl_unique_stable_name implementation, which depends on lambda // mangling. - MCtx = getMangleNumberingContext(Class, ManglingContextDecl); + MCtx = getMangleNumberingContext(Class, Numbering.ContextDecl); assert(MCtx && "Retrieving mangle numbering context failed!"); - HasKnownInternalLinkage = true; + Numbering.HasKnownInternalLinkage = true; } if (MCtx) { - unsigned ManglingNumber = MCtx->getManglingNumber(Method); - Class->setLambdaMangling(ManglingNumber, ManglingContextDecl, - HasKnownInternalLinkage); - Class->setDeviceLambdaManglingNumber(MCtx->getDeviceManglingNumber(Method)); + Numbering.IndexInContext = MCtx->getNextLambdaIndex(); + Numbering.ManglingNumber = MCtx->getManglingNumber(Method); + Numbering.DeviceManglingNumber = MCtx->getDeviceManglingNumber(Method); + Class->setLambdaNumbering(Numbering); + + if (auto *Source = + dyn_cast_or_null(Context.getExternalSource())) + Source->AssignedLambdaNumbering(Class); } } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -13316,13 +13316,6 @@ E->getCaptureDefault()); getDerived().transformedLocalDecl(OldClass, {Class}); - std::optional> Mangling; - if (getDerived().ReplacingOriginal()) - Mangling = std::make_tuple(OldClass->hasKnownLambdaInternalLinkage(), - OldClass->getLambdaManglingNumber(), - OldClass->getDeviceLambdaManglingNumber(), - OldClass->getLambdaContextDecl()); - CXXMethodDecl *NewCallOperator = getSema().CreateLambdaCallOperator(E->getIntroducerRange(), Class); NewCallOperator->setLexicalDeclContext(getSema().CurContext); @@ -13502,7 +13495,13 @@ { // Number the lambda for linkage purposes if necessary. Sema::ContextRAII ManglingContext(getSema(), Class->getDeclContext()); - getSema().handleLambdaNumbering(Class, NewCallOperator, Mangling); + + std::optional Numbering; + if (getDerived().ReplacingOriginal()) { + Numbering = OldClass->getLambdaNumbering(); + } + + getSema().handleLambdaNumbering(Class, NewCallOperator, Numbering); } // FIXME: Sema's lambda-building mechanism expects us to push an expression diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7367,6 +7367,7 @@ return nullptr; } ReadingKindTracker ReadingKind(Read_Decl, *this); + Deserializing D(this); Expected MaybeCode = Cursor.ReadCode(); if (!MaybeCode) { @@ -7401,6 +7402,7 @@ return nullptr; } ReadingKindTracker ReadingKind(Read_Decl, *this); + Deserializing D(this); Expected MaybeCode = Cursor.ReadCode(); if (!MaybeCode) { @@ -8568,6 +8570,17 @@ LateParsedTemplates.clear(); } +void ASTReader::AssignedLambdaNumbering(const CXXRecordDecl *Lambda) { + if (Lambda->getLambdaContextDecl()) { + // Keep track of this lambda so it can be merged with another lambda that + // is loaded later. + LambdaDeclarationsForMerging.insert( + {{Lambda->getLambdaContextDecl()->getCanonicalDecl(), + Lambda->getLambdaIndexInContext()}, + const_cast(Lambda)}); + } +} + void ASTReader::LoadSelector(Selector Sel) { // It would be complicated to avoid reading the methods anyway. So don't. ReadMethodPool(Sel); @@ -9278,11 +9291,12 @@ } void ASTReader::finishPendingActions() { - while (!PendingIdentifierInfos.empty() || !PendingFunctionTypes.empty() || - !PendingIncompleteDeclChains.empty() || !PendingDeclChains.empty() || - !PendingMacroIDs.empty() || !PendingDeclContextInfos.empty() || - !PendingUpdateRecords.empty() || - !PendingObjCExtensionIvarRedeclarations.empty()) { + while ( + !PendingIdentifierInfos.empty() || !PendingDeducedFunctionTypes.empty() || + !PendingDeducedVarTypes.empty() || !PendingIncompleteDeclChains.empty() || + !PendingDeclChains.empty() || !PendingMacroIDs.empty() || + !PendingDeclContextInfos.empty() || !PendingUpdateRecords.empty() || + !PendingObjCExtensionIvarRedeclarations.empty()) { // If any identifiers with corresponding top-level declarations have // been loaded, load those declarations now. using TopLevelDeclsMap = @@ -9300,9 +9314,9 @@ // Load each function type that we deferred loading because it was a // deduced type that might refer to a local type declared within itself. - for (unsigned I = 0; I != PendingFunctionTypes.size(); ++I) { - auto *FD = PendingFunctionTypes[I].first; - FD->setType(GetType(PendingFunctionTypes[I].second)); + for (unsigned I = 0; I != PendingDeducedFunctionTypes.size(); ++I) { + auto *FD = PendingDeducedFunctionTypes[I].first; + FD->setType(GetType(PendingDeducedFunctionTypes[I].second)); // If we gave a function a deduced return type, remember that we need to // propagate that along the redeclaration chain. @@ -9311,7 +9325,15 @@ PendingDeducedTypeUpdates.insert( {FD->getCanonicalDecl(), FD->getReturnType()}); } - PendingFunctionTypes.clear(); + PendingDeducedFunctionTypes.clear(); + + // Load each variable type that we deferred loading because it was a + // deduced type that might refer to a local type declared within itself. + for (unsigned I = 0; I != PendingDeducedVarTypes.size(); ++I) { + auto *VD = PendingDeducedVarTypes[I].first; + VD->setType(GetType(PendingDeducedVarTypes[I].second)); + } + PendingDeducedVarTypes.clear(); // For each decl chain that we wanted to complete while deserializing, mark // it as "still needs to be completed". @@ -9487,7 +9509,6 @@ continue; // FIXME: Check for =delete/=default? - // FIXME: Complain about ODR violations here? const FunctionDecl *Defn = nullptr; if (!getContext().getLangOpts().Modules || !FD->hasBody(Defn)) { FD->setLazyBody(PB->second); @@ -9518,6 +9539,12 @@ } PendingBodies.clear(); + // Inform any classes that had members added that they now have more members. + for (auto [RD, MD] : PendingAddedClassMembers) { + RD->addedMember(MD); + } + PendingAddedClassMembers.clear(); + // Do some cleanup. for (auto *ND : PendingMergedDefinitionsToDeduplicate) getContext().deduplicateMergedDefinitonsFor(ND); @@ -9694,9 +9721,6 @@ ObjCProtocolOdrMergeFailures.empty()) return; - // Ensure we don't accidentally recursively enter deserialization while - // we're producing our diagnostics. - Deserializing RecursionGuard(this); ODRDiagsEmitter DiagsEmitter(Diags, getContext(), getPreprocessor().getLangOpts()); diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -158,9 +158,12 @@ return Record.getSubmodule(readSubmoduleID()); } - void ReadCXXRecordDefinition(CXXRecordDecl *D, bool Update); + void ReadCXXRecordDefinition(CXXRecordDecl *D, bool Update, + Decl *LambdaContext = nullptr, + unsigned IndexInLambdaContext = 0); void ReadCXXDefinitionData(struct CXXRecordDecl::DefinitionData &Data, - const CXXRecordDecl *D); + const CXXRecordDecl *D, Decl *LambdaContext, + unsigned IndexInLambdaContext); void MergeDefinitionData(CXXRecordDecl *D, struct CXXRecordDecl::DefinitionData &&NewDD); void ReadObjCDefinitionData(struct ObjCInterfaceDecl::DefinitionData &Data); @@ -377,6 +380,7 @@ void VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D); void VisitIndirectFieldDecl(IndirectFieldDecl *FD); RedeclarableResult VisitVarDeclImpl(VarDecl *D); + void ReadVarDeclInit(VarDecl *VD); void VisitVarDecl(VarDecl *VD) { VisitVarDeclImpl(VD); } void VisitImplicitParamDecl(ImplicitParamDecl *PD); void VisitParmVarDecl(ParmVarDecl *PD); @@ -422,6 +426,9 @@ template void mergeRedeclarable(Redeclarable *D, RedeclarableResult &Redecl); + void mergeLambda(CXXRecordDecl *D, RedeclarableResult &Redecl, + Decl *Context, unsigned Number); + void mergeRedeclarableTemplate(RedeclarableTemplateDecl *D, RedeclarableResult &Redecl); @@ -564,6 +571,8 @@ // FIXME: Can we diagnose ODR violations somehow? if (Record.readInt()) ReadFunctionDefinition(FD); + } else if (auto *VD = dyn_cast(D)) { + ReadVarDeclInit(VD); } } @@ -861,10 +870,10 @@ void ASTDeclReader::VisitValueDecl(ValueDecl *VD) { VisitNamedDecl(VD); - // For function declarations, defer reading the type in case the function has - // a deduced return type that references an entity declared within the - // function. - if (isa(VD)) + // For function or variable declarations, defer reading the type in case the + // declaration has a deduced type that references an entity declared within + // the function definition or variable initializer. + if (isa(VD)) DeferredTypeID = Record.getGlobalTypeID(Record.readInt()); else VD->setType(Record.readType()); @@ -1026,7 +1035,7 @@ // We'll set up the real type in Visit, once we've finished loading the // function. FD->setType(FD->getTypeSourceInfo()->getType()); - Reader.PendingFunctionTypes.push_back({FD, DeferredTypeID}); + Reader.PendingDeducedFunctionTypes.push_back({FD, DeferredTypeID}); } else { FD->setType(Reader.GetType(DeferredTypeID)); } @@ -1571,6 +1580,7 @@ VD->VarDeclBits.TSCSpec = Record.readInt(); VD->VarDeclBits.InitStyle = Record.readInt(); VD->VarDeclBits.ARCPseudoStrong = Record.readInt(); + bool HasDeducedType = false; if (!isa(VD)) { VD->NonParmVarDeclBits.IsThisDeclarationADemotedDefinition = Record.readInt(); @@ -1585,7 +1595,18 @@ VD->NonParmVarDeclBits.PreviousDeclInSameBlockScope = Record.readInt(); VD->NonParmVarDeclBits.ImplicitParamKind = Record.readInt(); VD->NonParmVarDeclBits.EscapingByref = Record.readInt(); + HasDeducedType = Record.readInt(); } + + // If this variable has a deduced type, defer reading that type until we are + // done deserializing this variable, because the type might refer back to the + // variable. + if (HasDeducedType) + Reader.PendingDeducedVarTypes.push_back({VD, DeferredTypeID}); + else + VD->setType(Reader.GetType(DeferredTypeID)); + DeferredTypeID = 0; + auto VarLinkage = Linkage(Record.readInt()); VD->setCachedLinkage(VarLinkage); @@ -1594,22 +1615,13 @@ VD->getLexicalDeclContext()->isFunctionOrMethod()) VD->setLocalExternDecl(); - if (uint64_t Val = Record.readInt()) { - VD->setInit(Record.readExpr()); - if (Val != 1) { - EvaluatedStmt *Eval = VD->ensureEvaluatedStmt(); - Eval->HasConstantInitialization = (Val & 2) != 0; - Eval->HasConstantDestruction = (Val & 4) != 0; - } - } - - if (VD->hasAttr() && VD->getType()->getAsCXXRecordDecl()) { + if (VD->hasAttr()) { Expr *CopyExpr = Record.readExpr(); if (CopyExpr) Reader.getContext().setBlockVarCopyInit(VD, CopyExpr, Record.readInt()); } - if (VD->getStorageDuration() == SD_Static && Record.readInt()) { + if (Record.readInt()) { Reader.DefinitionSource[VD] = Loc.F->Kind == ModuleKind::MK_MainFile || Reader.getContext().getLangOpts().BuildingPCHWithObjectFile; @@ -1643,6 +1655,18 @@ return Redecl; } +void ASTDeclReader::ReadVarDeclInit(VarDecl *VD) { + if (uint64_t Val = Record.readInt()) { + EvaluatedStmt *Eval = VD->ensureEvaluatedStmt(); + Eval->HasConstantInitialization = (Val & 2) != 0; + Eval->HasConstantDestruction = (Val & 4) != 0; + // Store the offset of the initializer. Don't deserialize it yet: it might + // not be needed, and might refer back to the variable, for example if it + // contains a lambda. + Eval->Value = GetCurrentCursorOffset(); + } +} + void ASTDeclReader::VisitImplicitParamDecl(ImplicitParamDecl *PD) { VisitVarDecl(PD); } @@ -1894,10 +1918,10 @@ } void ASTDeclReader::ReadCXXDefinitionData( - struct CXXRecordDecl::DefinitionData &Data, const CXXRecordDecl *D) { - #define FIELD(Name, Width, Merge) \ - Data.Name = Record.readInt(); - #include "clang/AST/CXXRecordDeclDefinitionBits.def" + struct CXXRecordDecl::DefinitionData &Data, const CXXRecordDecl *D, + Decl *LambdaContext, unsigned IndexInLambdaContext) { +#define FIELD(Name, Width, Merge) Data.Name = Record.readInt(); +#include "clang/AST/CXXRecordDeclDefinitionBits.def" // Note: the caller has deserialized the IsLambda bit already. Data.ODRHash = Record.readInt(); @@ -1909,21 +1933,26 @@ Reader.getContext().getLangOpts().BuildingPCHWithObjectFile; } - Data.NumBases = Record.readInt(); - if (Data.NumBases) - Data.Bases = ReadGlobalOffset(); - Data.NumVBases = Record.readInt(); - if (Data.NumVBases) - Data.VBases = ReadGlobalOffset(); - Record.readUnresolvedSet(Data.Conversions); Data.ComputedVisibleConversions = Record.readInt(); if (Data.ComputedVisibleConversions) Record.readUnresolvedSet(Data.VisibleConversions); assert(Data.Definition && "Data.Definition should be already set!"); - Data.FirstFriend = readDeclID(); - if (Data.IsLambda) { + if (!Data.IsLambda) { + assert(!LambdaContext && !IndexInLambdaContext && + "given lambda context for non-lambda"); + + Data.NumBases = Record.readInt(); + if (Data.NumBases) + Data.Bases = ReadGlobalOffset(); + + Data.NumVBases = Record.readInt(); + if (Data.NumVBases) + Data.VBases = ReadGlobalOffset(); + + Data.FirstFriend = readDeclID(); + } else { using Capture = LambdaCapture; auto &Lambda = static_cast(Data); @@ -1934,8 +1963,10 @@ Lambda.NumExplicitCaptures = Record.readInt(); Lambda.HasKnownInternalLinkage = Record.readInt(); Lambda.ManglingNumber = Record.readInt(); - D->setDeviceLambdaManglingNumber(Record.readInt()); - Lambda.ContextDecl = readDeclID(); + if (unsigned DeviceManglingNumber = Record.readInt()) + Reader.getContext().DeviceLambdaManglingNumbers[D] = DeviceManglingNumber; + Lambda.IndexInContext = IndexInLambdaContext; + Lambda.ContextDecl = LambdaContext; Capture *ToCapture = nullptr; if (Lambda.NumCaptures) { ToCapture = (Capture *)Reader.getContext().Allocate(sizeof(Capture) * @@ -2056,13 +2087,17 @@ {MergeDD.Definition, &MergeDD}); } -void ASTDeclReader::ReadCXXRecordDefinition(CXXRecordDecl *D, bool Update) { +void ASTDeclReader::ReadCXXRecordDefinition(CXXRecordDecl *D, bool Update, + Decl *LambdaContext, + unsigned IndexInLambdaContext) { struct CXXRecordDecl::DefinitionData *DD; ASTContext &C = Reader.getContext(); // Determine whether this is a lambda closure type, so that we can // allocate the appropriate DefinitionData structure. bool IsLambda = Record.readInt(); + assert(!(IsLambda && Update) && + "lambda definition should not be added by update record"); if (IsLambda) DD = new (C) CXXRecordDecl::LambdaDefinitionData( D, nullptr, CXXRecordDecl::LDK_Unknown, false, LCD_None); @@ -2076,7 +2111,7 @@ if (!Canon->DefinitionData) Canon->DefinitionData = DD; D->DefinitionData = Canon->DefinitionData; - ReadCXXDefinitionData(*DD, D); + ReadCXXDefinitionData(*DD, D, LambdaContext, IndexInLambdaContext); // We might already have a different definition for this record. This can // happen either because we're reading an update record, or because we've @@ -2103,8 +2138,15 @@ ASTContext &C = Reader.getContext(); enum CXXRecKind { - CXXRecNotTemplate = 0, CXXRecTemplate, CXXRecMemberSpecialization + CXXRecNotTemplate = 0, + CXXRecTemplate, + CXXRecMemberSpecialization, + CXXLambda }; + + Decl *LambdaContext = nullptr; + unsigned IndexInLambdaContext = 0; + switch ((CXXRecKind)Record.readInt()) { case CXXRecNotTemplate: // Merged when we merge the folding set entry in the primary template. @@ -2136,11 +2178,19 @@ mergeRedeclarable(D, Redecl); break; } + case CXXLambda: { + LambdaContext = readDecl(); + if (LambdaContext) + IndexInLambdaContext = Record.readInt(); + mergeLambda(D, Redecl, LambdaContext, IndexInLambdaContext); + break; + } } bool WasDefinition = Record.readInt(); if (WasDefinition) - ReadCXXRecordDefinition(D, /*Update*/false); + ReadCXXRecordDefinition(D, /*Update=*/false, LambdaContext, + IndexInLambdaContext); else // Propagate DefinitionData pointer from the canonical declaration. D->DefinitionData = D->getCanonicalDecl()->DefinitionData; @@ -2742,6 +2792,41 @@ mergeRedeclarable(D, Existing, Redecl); } +/// Attempt to merge D with a previous declaration of the same lambda, which is +/// found by its index within its context declaration, if it has one. +/// +/// We can't look up lambdas in their enclosing lexical or semantic context in +/// general, because for lambdas in variables, both of those might be a +/// namespace or the translation unit. +void ASTDeclReader::mergeLambda(CXXRecordDecl *D, RedeclarableResult &Redecl, + Decl *Context, unsigned IndexInContext) { + // If we don't have a mangling context, treat this like any other + // declaration. + if (!Context) + return mergeRedeclarable(D, Redecl); + + // If modules are not available, there is no reason to perform this merge. + if (!Reader.getContext().getLangOpts().Modules) + return; + + // If we're not the canonical declaration, we don't need to merge. + if (!D->isFirstDecl()) + return; + + if (auto *Existing = Redecl.getKnownMergeTarget()) + // We already know of an existing declaration we should merge with. + mergeRedeclarable(D, cast(Existing), Redecl); + + // Look up this lambda to see if we've seen it before. If so, merge with the + // one we already loaded. + NamedDecl *&Slot = Reader.LambdaDeclarationsForMerging[{ + Context->getCanonicalDecl(), IndexInContext}]; + if (Slot) + mergeRedeclarable(D, cast(Slot), Redecl); + else + Slot = D; +} + void ASTDeclReader::mergeRedeclarableTemplate(RedeclarableTemplateDecl *D, RedeclarableResult &Redecl) { mergeRedeclarable(D, Redecl); @@ -4306,13 +4391,9 @@ switch ((DeclUpdateKind)Record.readInt()) { case UPD_CXX_ADDED_IMPLICIT_MEMBER: { auto *RD = cast(D); - // FIXME: If we also have an update record for instantiating the - // definition of D, we need that to happen before we get here. Decl *MD = Record.readDecl(); assert(MD && "couldn't read decl from update record"); - // FIXME: We should call addHiddenDecl instead, to add the member - // to its DeclContext. - RD->addedMember(MD); + Reader.PendingAddedClassMembers.push_back({RD, MD}); break; } @@ -4340,15 +4421,7 @@ auto *VD = cast(D); VD->NonParmVarDeclBits.IsInline = Record.readInt(); VD->NonParmVarDeclBits.IsInlineSpecified = Record.readInt(); - uint64_t Val = Record.readInt(); - if (Val && !VD->getInit()) { - VD->setInit(Record.readExpr()); - if (Val != 1) { - EvaluatedStmt *Eval = VD->ensureEvaluatedStmt(); - Eval->HasConstantInitialization = (Val & 2) != 0; - Eval->HasConstantDestruction = (Val & 4) != 0; - } - } + ReadVarDeclInit(VD); break; } diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -228,7 +228,7 @@ if (HasElse) S->setElse(Record.readSubStmt()); if (HasVar) - S->setConditionVariable(Record.getContext(), readDeclAs()); + S->setConditionVariableDeclStmt(cast(Record.readSubStmt())); if (HasInit) S->setInit(Record.readSubStmt()); @@ -253,7 +253,7 @@ if (HasInit) S->setInit(Record.readSubStmt()); if (HasVar) - S->setConditionVariable(Record.getContext(), readDeclAs()); + S->setConditionVariableDeclStmt(cast(Record.readSubStmt())); S->setSwitchLoc(readSourceLocation()); S->setLParenLoc(readSourceLocation()); @@ -279,7 +279,7 @@ S->setCond(Record.readSubExpr()); S->setBody(Record.readSubStmt()); if (HasVar) - S->setConditionVariable(Record.getContext(), readDeclAs()); + S->setConditionVariableDeclStmt(cast(Record.readSubStmt())); S->setWhileLoc(readSourceLocation()); S->setLParenLoc(readSourceLocation()); @@ -299,7 +299,7 @@ VisitStmt(S); S->setInit(Record.readSubStmt()); S->setCond(Record.readSubExpr()); - S->setConditionVariable(Record.getContext(), readDeclAs()); + S->setConditionVariableDeclStmt(cast_or_null(Record.readSubStmt())); S->setInc(Record.readSubExpr()); S->setBody(Record.readSubStmt()); S->setForLoc(readSourceLocation()); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5175,6 +5175,7 @@ const Decl *D = DeclUpdate.first; bool HasUpdatedBody = false; + bool HasAddedVarDefinition = false; RecordData RecordData; ASTRecordWriter Record(*this, RecordData); for (auto &Update : DeclUpdate.second) { @@ -5184,6 +5185,8 @@ // to skip over the lazy body to reach statements for other records. if (Kind == UPD_CXX_ADDED_FUNCTION_DEFINITION) HasUpdatedBody = true; + else if (Kind == UPD_CXX_ADDED_VAR_DEFINITION) + HasAddedVarDefinition = true; else Record.push_back(Kind); @@ -5196,6 +5199,7 @@ break; case UPD_CXX_ADDED_FUNCTION_DEFINITION: + case UPD_CXX_ADDED_VAR_DEFINITION: break; case UPD_CXX_POINT_OF_INSTANTIATION: @@ -5203,14 +5207,6 @@ Record.AddSourceLocation(Update.getLoc()); break; - case UPD_CXX_ADDED_VAR_DEFINITION: { - const VarDecl *VD = cast(D); - Record.push_back(VD->isInline()); - Record.push_back(VD->isInlineSpecified()); - Record.AddVarDeclInit(VD); - break; - } - case UPD_CXX_INSTANTIATED_DEFAULT_ARGUMENT: Record.AddStmt(const_cast( cast(Update.getDecl())->getDefaultArg())); @@ -5322,12 +5318,20 @@ } } + // Add a trailing update record, if any. These must go last because we + // lazily load their attached statement. if (HasUpdatedBody) { const auto *Def = cast(D); Record.push_back(UPD_CXX_ADDED_FUNCTION_DEFINITION); Record.push_back(Def->isInlined()); Record.AddSourceLocation(Def->getInnerLocStart()); Record.AddFunctionDefinition(Def); + } else if (HasAddedVarDefinition) { + const auto *VD = cast(D); + Record.push_back(UPD_CXX_ADDED_VAR_DEFINITION); + Record.push_back(VD->isInline()); + Record.push_back(VD->isInlineSpecified()); + Record.AddVarDeclInit(VD); } OffsetsRecord.push_back(GetDeclRef(D)); @@ -5917,6 +5921,7 @@ // getODRHash will compute the ODRHash if it has not been previously computed. Record->push_back(D->getODRHash()); + bool ModulesDebugInfo = Writer->Context->getLangOpts().ModulesDebugInfo && !D->isDependentType(); Record->push_back(ModulesDebugInfo); @@ -5925,24 +5930,24 @@ // IsLambda bit is already saved. - Record->push_back(Data.NumBases); - if (Data.NumBases > 0) - AddCXXBaseSpecifiers(Data.bases()); - - // FIXME: Make VBases lazily computed when needed to avoid storing them. - Record->push_back(Data.NumVBases); - if (Data.NumVBases > 0) - AddCXXBaseSpecifiers(Data.vbases()); - AddUnresolvedSet(Data.Conversions.get(*Writer->Context)); Record->push_back(Data.ComputedVisibleConversions); if (Data.ComputedVisibleConversions) AddUnresolvedSet(Data.VisibleConversions.get(*Writer->Context)); // Data.Definition is the owning decl, no need to write it. - AddDeclRef(D->getFirstFriend()); - // Add lambda-specific data. - if (Data.IsLambda) { + if (!Data.IsLambda) { + Record->push_back(Data.NumBases); + if (Data.NumBases > 0) + AddCXXBaseSpecifiers(Data.bases()); + + // FIXME: Make VBases lazily computed when needed to avoid storing them. + Record->push_back(Data.NumVBases); + if (Data.NumVBases > 0) + AddCXXBaseSpecifiers(Data.vbases()); + + AddDeclRef(D->getFirstFriend()); + } else { auto &Lambda = D->getLambdaData(); Record->push_back(Lambda.DependencyKind); Record->push_back(Lambda.IsGenericLambda); @@ -5952,7 +5957,8 @@ Record->push_back(Lambda.HasKnownInternalLinkage); Record->push_back(Lambda.ManglingNumber); Record->push_back(D->getDeviceLambdaManglingNumber()); - AddDeclRef(D->getLambdaContextDecl()); + // The lambda context declaration and index within the context are provided + // separately, so that they can be used for merging. AddTypeSourceInfo(Lambda.MethodTyInfo); for (unsigned I = 0, N = Lambda.NumCaptures; I != N; ++I) { const LambdaCapture &Capture = Lambda.Captures.front()[I]; diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -278,7 +278,7 @@ // Source locations require array (variable-length) abbreviations. The // abbreviation infrastructure requires that arrays are encoded last, so // we handle it here in the case of those classes derived from DeclaratorDecl - if (DeclaratorDecl *DD = dyn_cast(D)) { + if (auto *DD = dyn_cast(D)) { if (auto *TInfo = DD->getTypeSourceInfo()) Record.AddTypeLoc(TInfo->getTypeLoc()); } @@ -286,16 +286,24 @@ // Handle FunctionDecl's body here and write it after all other Stmts/Exprs // have been written. We want it last because we will not read it back when // retrieving it from the AST, we'll just lazily set the offset. - if (FunctionDecl *FD = dyn_cast(D)) { + if (auto *FD = dyn_cast(D)) { Record.push_back(FD->doesThisDeclarationHaveABody()); if (FD->doesThisDeclarationHaveABody()) Record.AddFunctionDefinition(FD); } + // Similar to FunctionDecls, handle VarDecl's initializer here and write it + // after all other Stmts/Exprs. We will not read the initializer until after + // we have finished recursive deserialization, because it can recursively + // refer back to the variable. + if (auto *VD = dyn_cast(D)) { + Record.AddVarDeclInit(VD); + } + // If this declaration is also a DeclContext, write blocks for the // declarations that lexically stored inside its context and those // declarations that are visible from its context. - if (DeclContext *DC = dyn_cast(D)) + if (auto *DC = dyn_cast(D)) VisitDeclContext(DC); } @@ -1037,6 +1045,7 @@ Record.push_back(D->getTSCSpec()); Record.push_back(D->getInitStyle()); Record.push_back(D->isARCPseudoStrong()); + bool HasDeducedType = false; if (!isa(D)) { Record.push_back(D->isThisDeclarationADemotedDefinition()); Record.push_back(D->isExceptionVariable()); @@ -1053,36 +1062,34 @@ else Record.push_back(0); Record.push_back(D->isEscapingByref()); + HasDeducedType = D->getType()->getContainedDeducedType(); + Record.push_back(HasDeducedType); } Record.push_back(D->getLinkageInternal()); - Record.AddVarDeclInit(D); - - if (D->hasAttr() && D->getType()->getAsCXXRecordDecl()) { + if (D->hasAttr()) { BlockVarCopyInit Init = Writer.Context->getBlockVarCopyInit(D); Record.AddStmt(Init.getCopyExpr()); if (Init.getCopyExpr()) Record.push_back(Init.canThrow()); } - if (D->getStorageDuration() == SD_Static) { - bool ModulesCodegen = false; - if (Writer.WritingModule && - !D->getDescribedVarTemplate()) { - // When building a C++20 module interface unit or a partition unit, a - // strong definition in the module interface is provided by the - // compilation of that unit, not by its users. (Inline variables are still - // emitted in module users.) - ModulesCodegen = - (Writer.WritingModule->isInterfaceOrPartition() || - (D->hasAttr() && - Writer.Context->getLangOpts().BuildingPCHWithObjectFile)) && - Writer.Context->GetGVALinkageForVariable(D) >= GVA_StrongExternal; - } - Record.push_back(ModulesCodegen); - if (ModulesCodegen) - Writer.ModularCodegenDecls.push_back(Writer.GetDeclRef(D)); + bool ModulesCodegen = false; + if (Writer.WritingModule && D->getStorageDuration() == SD_Static && + !D->getDescribedVarTemplate()) { + // When building a C++20 module interface unit or a partition unit, a + // strong definition in the module interface is provided by the + // compilation of that unit, not by its users. (Inline variables are still + // emitted in module users.) + ModulesCodegen = + (Writer.WritingModule->isInterfaceOrPartition() || + (D->hasAttr() && + Writer.Context->getLangOpts().BuildingPCHWithObjectFile)) && + Writer.Context->GetGVALinkageForVariable(D) >= GVA_StrongExternal; } + Record.push_back(ModulesCodegen); + if (ModulesCodegen) + Writer.ModularCodegenDecls.push_back(Writer.GetDeclRef(D)); enum { VarNotTemplate = 0, VarTemplate, StaticDataMemberSpecialization @@ -1118,8 +1125,9 @@ !D->isConstexpr() && !D->isInitCapture() && !D->isPreviousDeclInSameBlockScope() && - !(D->hasAttr() && D->getType()->getAsCXXRecordDecl()) && + !D->hasAttr() && !D->isEscapingByref() && + !HasDeducedType && D->getStorageDuration() != SD_Static && !D->getMemberSpecializationInfo()) AbbrevToUse = Writer.getDeclVarAbbrev(); @@ -1413,7 +1421,10 @@ VisitRecordDecl(D); enum { - CXXRecNotTemplate = 0, CXXRecTemplate, CXXRecMemberSpecialization + CXXRecNotTemplate = 0, + CXXRecTemplate, + CXXRecMemberSpecialization, + CXXLambda }; if (ClassTemplateDecl *TemplD = D->getDescribedClassTemplate()) { Record.push_back(CXXRecTemplate); @@ -1424,6 +1435,15 @@ Record.AddDeclRef(MSInfo->getInstantiatedFrom()); Record.push_back(MSInfo->getTemplateSpecializationKind()); Record.AddSourceLocation(MSInfo->getPointOfInstantiation()); + } else if (D->isLambda()) { + // For a lambda, we need some information early for merging. + Record.push_back(CXXLambda); + if (auto *Context = D->getLambdaContextDecl()) { + Record.AddDeclRef(Context); + Record.push_back(D->getLambdaIndexInContext()); + } else { + Record.push_back(0); + } } else { Record.push_back(CXXRecNotTemplate); } @@ -2301,6 +2321,7 @@ Abv->Add(BitCodeAbbrevOp(0)); // isPrevDeclInSameScope Abv->Add(BitCodeAbbrevOp(0)); // ImplicitParamKind Abv->Add(BitCodeAbbrevOp(0)); // EscapingByref + Abv->Add(BitCodeAbbrevOp(0)); // HasDeducedType Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // HasConstant* Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // VarKind (local enum) diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -150,7 +150,7 @@ if (HasElse) Record.AddStmt(S->getElse()); if (HasVar) - Record.AddDeclRef(S->getConditionVariable()); + Record.AddStmt(S->getConditionVariableDeclStmt()); if (HasInit) Record.AddStmt(S->getInit()); @@ -177,7 +177,7 @@ if (HasInit) Record.AddStmt(S->getInit()); if (HasVar) - Record.AddDeclRef(S->getConditionVariable()); + Record.AddStmt(S->getConditionVariableDeclStmt()); Record.AddSourceLocation(S->getSwitchLoc()); Record.AddSourceLocation(S->getLParenLoc()); @@ -198,7 +198,7 @@ Record.AddStmt(S->getCond()); Record.AddStmt(S->getBody()); if (HasVar) - Record.AddDeclRef(S->getConditionVariable()); + Record.AddStmt(S->getConditionVariableDeclStmt()); Record.AddSourceLocation(S->getWhileLoc()); Record.AddSourceLocation(S->getLParenLoc()); @@ -220,7 +220,7 @@ VisitStmt(S); Record.AddStmt(S->getInit()); Record.AddStmt(S->getCond()); - Record.AddDeclRef(S->getConditionVariable()); + Record.AddStmt(S->getConditionVariableDeclStmt()); Record.AddStmt(S->getInc()); Record.AddStmt(S->getBody()); Record.AddSourceLocation(S->getForLoc()); diff --git a/clang/test/Modules/lambda-in-variable.cpp b/clang/test/Modules/lambda-in-variable.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/lambda-in-variable.cpp @@ -0,0 +1,120 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 -fmodules -fmodules-cache-path=%t -fmodule-map-file=%t/module.modulemap %t/use.cpp -emit-llvm -o - -triple x86_64-linux-gnu | FileCheck %s +// RUN: %clang_cc1 -DDEFINE_LOCALLY -std=c++20 -fmodules -fmodules-cache-path=%t -fmodule-map-file=%t/module.modulemap %t/use.cpp -emit-llvm -o - -triple x86_64-linux-gnu | FileCheck %s + +//--- module.modulemap +module a { header "a.h" export * } +module b { header "b.h" export * } +module c { header "c.h" export * } + +//--- nonmodular.h +void not_constant(); + +template struct A { + template static inline T N = [] { not_constant(); return M; } (); +}; + +template inline T N = [] { not_constant(); return M; } (); + +template inline auto L = [] {}; + +template int Z; + +// These lambdas should not be merged, despite having the same context decl and +// mangling number (but different signatures). +inline auto MultipleLambdas = ((void)[](int*) { return 1; }, [] { return 2; }); + +//--- a.h +#include "nonmodular.h" + +//--- b.h +#include "a.h" + +int b1() { return A::N<1>; } +int b2() { return N; } + +inline auto x1 = L; +inline auto x2 = L; + +inline constexpr int *P = &Z; +inline constexpr int *xP = P; + +static_assert(!__is_same(decltype(x1), decltype(x2))); + +//--- c.h +#include "a.h" + +int c1() { return A::N<2>; } +int c2() { return N; } + +inline auto y2 = L; +inline auto y1 = L; + +inline constexpr int *P = &Z; +inline constexpr int *yP = P; + +//--- use.cpp +#ifdef DEFINE_LOCALLY +#include "nonmodular.h" + +inline constexpr int *P = &Z; +inline constexpr int *zP = P; + +auto z0 = L; +auto z2 = L; +auto z1 = L; +#endif + +#include "b.h" +#include "c.h" + +int b1v = b1(); +int b2v = b2(); +int c1v = c1(); +int c2v = c2(); + +// We should merge together matching lambdas. +static_assert(__is_same(decltype(x1), decltype(y1))); +static_assert(__is_same(decltype(x2), decltype(y2))); +static_assert(!__is_same(decltype(x1), decltype(x2))); +static_assert(!__is_same(decltype(y1), decltype(y2))); +static_assert(!__is_same(decltype(x1), decltype(y2))); +static_assert(!__is_same(decltype(x2), decltype(y1))); +static_assert(xP == yP); +#ifdef DEFINE_LOCALLY +static_assert(!__is_same(decltype(x1), decltype(z0))); +static_assert(!__is_same(decltype(x2), decltype(z0))); +static_assert(__is_same(decltype(x1), decltype(z1))); +static_assert(__is_same(decltype(x2), decltype(z2))); +static_assert(xP == zP); +#endif + +static_assert(MultipleLambdas() == 2); + +// We should not merge the instantiated lambdas from `b.h` and `c.h` together, +// even though they will both have anonymous declaration number #1 within +// A and within the TU, respectively. + +// CHECK-LABEL: define {{.*}}global_var_init{{.*}} comdat($_Z1NIiLi1EE) { +// CHECK: load i8, ptr @_ZGV1NIiLi1EE, align 8 +// CHECK: call {{.*}} i32 @_ZNK1NIiLi1EEMUlvE_clEv( +// CHECK: store i32 {{.*}}, ptr @_Z1NIiLi1EE + +// CHECK-LABEL: define {{.*}}global_var_init{{.*}} comdat($_ZN1AIiE1NILi1EEE) { +// CHECK: load i8, ptr @_ZGVN1AIiE1NILi1EEE, align 8 +// CHECK: call {{.*}} i32 @_ZNK1AIiE1NILi1EEMUlvE_clEv( +// CHECK: store i32 {{.*}}, ptr @_ZN1AIiE1NILi1EEE + +// CHECK-LABEL: define {{.*}}global_var_init{{.*}} comdat($_Z1NIiLi2EE) { +// CHECK: load i8, ptr @_ZGV1NIiLi2EE, align 8 +// CHECK: call {{.*}} i32 @_ZNK1NIiLi2EEMUlvE_clEv( +// CHECK: store i32 {{.*}}, ptr @_Z1NIiLi2EE + +// CHECK-LABEL: define {{.*}}global_var_init{{.*}} comdat($_ZN1AIiE1NILi2EEE) { +// CHECK: load i8, ptr @_ZGVN1AIiE1NILi2EEE, align 8 +// CHECK: call {{.*}} i32 @_ZNK1AIiE1NILi2EEMUlvE_clEv( +// CHECK: store i32 {{.*}}, ptr @_ZN1AIiE1NILi2EEE + diff --git a/clang/test/OpenMP/parallel_master_taskloop_simd_codegen.cpp b/clang/test/OpenMP/parallel_master_taskloop_simd_codegen.cpp --- a/clang/test/OpenMP/parallel_master_taskloop_simd_codegen.cpp +++ b/clang/test/OpenMP/parallel_master_taskloop_simd_codegen.cpp @@ -1457,11 +1457,31 @@ // CHECK2-NEXT: ret void // // -// CHECK2-LABEL: define {{[^@]+}}@_ZN1SC2Ei +// CHECK2-LABEL: define {{[^@]+}}@__cxx_global_var_init +// CHECK2-SAME: () #[[ATTR6]] section "__TEXT,__StaticInit,regular,pure_instructions" { +// CHECK2-NEXT: entry: +// CHECK2-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 4 dereferenceable(4) @s, i32 noundef 1) +// CHECK2-NEXT: ret void +// +// +// CHECK2-LABEL: define {{[^@]+}}@_ZN1SC1Ei // CHECK2-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[C:%.*]]) unnamed_addr #[[ATTR8:[0-9]+]] align 2 { // CHECK2-NEXT: entry: // CHECK2-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 // CHECK2-NEXT: [[C_ADDR:%.*]] = alloca i32, align 4 +// CHECK2-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 +// CHECK2-NEXT: store i32 [[C]], ptr [[C_ADDR]], align 4 +// CHECK2-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 +// CHECK2-NEXT: [[TMP0:%.*]] = load i32, ptr [[C_ADDR]], align 4 +// CHECK2-NEXT: call void @_ZN1SC2Ei(ptr noundef nonnull align 4 dereferenceable(4) [[THIS1]], i32 noundef [[TMP0]]) +// CHECK2-NEXT: ret void +// +// +// CHECK2-LABEL: define {{[^@]+}}@_ZN1SC2Ei +// CHECK2-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[C:%.*]]) unnamed_addr #[[ATTR8]] align 2 { +// CHECK2-NEXT: entry: +// CHECK2-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 +// CHECK2-NEXT: [[C_ADDR:%.*]] = alloca i32, align 4 // CHECK2-NEXT: [[DOTCAPTURE_EXPR_:%.*]] = alloca i8, align 1 // CHECK2-NEXT: [[DOTCAPTURE_EXPR__CASTED:%.*]] = alloca i64, align 8 // CHECK2-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 @@ -1650,26 +1670,6 @@ // CHECK2-NEXT: ret i32 0 // // -// CHECK2-LABEL: define {{[^@]+}}@_ZN1SC1Ei -// CHECK2-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[C:%.*]]) unnamed_addr #[[ATTR8]] align 2 { -// CHECK2-NEXT: entry: -// CHECK2-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 -// CHECK2-NEXT: [[C_ADDR:%.*]] = alloca i32, align 4 -// CHECK2-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 -// CHECK2-NEXT: store i32 [[C]], ptr [[C_ADDR]], align 4 -// CHECK2-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 -// CHECK2-NEXT: [[TMP0:%.*]] = load i32, ptr [[C_ADDR]], align 4 -// CHECK2-NEXT: call void @_ZN1SC2Ei(ptr noundef nonnull align 4 dereferenceable(4) [[THIS1]], i32 noundef [[TMP0]]) -// CHECK2-NEXT: ret void -// -// -// CHECK2-LABEL: define {{[^@]+}}@__cxx_global_var_init -// CHECK2-SAME: () #[[ATTR6]] section "__TEXT,__StaticInit,regular,pure_instructions" { -// CHECK2-NEXT: entry: -// CHECK2-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 4 dereferenceable(4) @s, i32 noundef 1) -// CHECK2-NEXT: ret void -// -// // CHECK2-LABEL: define {{[^@]+}}@_GLOBAL__sub_I_parallel_master_taskloop_simd_codegen.cpp // CHECK2-SAME: () #[[ATTR6]] section "__TEXT,__StaticInit,regular,pure_instructions" { // CHECK2-NEXT: entry: @@ -3111,8 +3111,28 @@ // CHECK6-NEXT: ret i32 [[TMP48]] // // +// CHECK6-LABEL: define {{[^@]+}}@__cxx_global_var_init +// CHECK6-SAME: () #[[ATTR2:[0-9]+]] section "__TEXT,__StaticInit,regular,pure_instructions" { +// CHECK6-NEXT: entry: +// CHECK6-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 4 dereferenceable(4) @s, i32 noundef 1) +// CHECK6-NEXT: ret void +// +// +// CHECK6-LABEL: define {{[^@]+}}@_ZN1SC1Ei +// CHECK6-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[C:%.*]]) unnamed_addr #[[ATTR3:[0-9]+]] align 2 { +// CHECK6-NEXT: entry: +// CHECK6-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 +// CHECK6-NEXT: [[C_ADDR:%.*]] = alloca i32, align 4 +// CHECK6-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 +// CHECK6-NEXT: store i32 [[C]], ptr [[C_ADDR]], align 4 +// CHECK6-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 +// CHECK6-NEXT: [[TMP0:%.*]] = load i32, ptr [[C_ADDR]], align 4 +// CHECK6-NEXT: call void @_ZN1SC2Ei(ptr noundef nonnull align 4 dereferenceable(4) [[THIS1]], i32 noundef [[TMP0]]) +// CHECK6-NEXT: ret void +// +// // CHECK6-LABEL: define {{[^@]+}}@_ZN1SC2Ei -// CHECK6-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[C:%.*]]) unnamed_addr #[[ATTR2:[0-9]+]] align 2 { +// CHECK6-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[C:%.*]]) unnamed_addr #[[ATTR3]] align 2 { // CHECK6-NEXT: entry: // CHECK6-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 // CHECK6-NEXT: [[C_ADDR:%.*]] = alloca i32, align 4 @@ -3195,28 +3215,8 @@ // CHECK6-NEXT: ret void // // -// CHECK6-LABEL: define {{[^@]+}}@_ZN1SC1Ei -// CHECK6-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[C:%.*]]) unnamed_addr #[[ATTR2]] align 2 { -// CHECK6-NEXT: entry: -// CHECK6-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 -// CHECK6-NEXT: [[C_ADDR:%.*]] = alloca i32, align 4 -// CHECK6-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 -// CHECK6-NEXT: store i32 [[C]], ptr [[C_ADDR]], align 4 -// CHECK6-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 -// CHECK6-NEXT: [[TMP0:%.*]] = load i32, ptr [[C_ADDR]], align 4 -// CHECK6-NEXT: call void @_ZN1SC2Ei(ptr noundef nonnull align 4 dereferenceable(4) [[THIS1]], i32 noundef [[TMP0]]) -// CHECK6-NEXT: ret void -// -// -// CHECK6-LABEL: define {{[^@]+}}@__cxx_global_var_init -// CHECK6-SAME: () #[[ATTR3:[0-9]+]] section "__TEXT,__StaticInit,regular,pure_instructions" { -// CHECK6-NEXT: entry: -// CHECK6-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 4 dereferenceable(4) @s, i32 noundef 1) -// CHECK6-NEXT: ret void -// -// // CHECK6-LABEL: define {{[^@]+}}@_GLOBAL__sub_I_parallel_master_taskloop_simd_codegen.cpp -// CHECK6-SAME: () #[[ATTR3]] section "__TEXT,__StaticInit,regular,pure_instructions" { +// CHECK6-SAME: () #[[ATTR2]] section "__TEXT,__StaticInit,regular,pure_instructions" { // CHECK6-NEXT: entry: // CHECK6-NEXT: call void @__cxx_global_var_init() // CHECK6-NEXT: ret void @@ -3919,8 +3919,28 @@ // CHECK8-NEXT: ret i32 [[TMP62]] // // +// CHECK8-LABEL: define {{[^@]+}}@__cxx_global_var_init +// CHECK8-SAME: () #[[ATTR2:[0-9]+]] section "__TEXT,__StaticInit,regular,pure_instructions" { +// CHECK8-NEXT: entry: +// CHECK8-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 4 dereferenceable(4) @s, i32 noundef 1) +// CHECK8-NEXT: ret void +// +// +// CHECK8-LABEL: define {{[^@]+}}@_ZN1SC1Ei +// CHECK8-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[C:%.*]]) unnamed_addr #[[ATTR3:[0-9]+]] align 2 { +// CHECK8-NEXT: entry: +// CHECK8-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 +// CHECK8-NEXT: [[C_ADDR:%.*]] = alloca i32, align 4 +// CHECK8-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 +// CHECK8-NEXT: store i32 [[C]], ptr [[C_ADDR]], align 4 +// CHECK8-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 +// CHECK8-NEXT: [[TMP0:%.*]] = load i32, ptr [[C_ADDR]], align 4 +// CHECK8-NEXT: call void @_ZN1SC2Ei(ptr noundef nonnull align 4 dereferenceable(4) [[THIS1]], i32 noundef [[TMP0]]) +// CHECK8-NEXT: ret void +// +// // CHECK8-LABEL: define {{[^@]+}}@_ZN1SC2Ei -// CHECK8-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[C:%.*]]) unnamed_addr #[[ATTR2:[0-9]+]] align 2 { +// CHECK8-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[C:%.*]]) unnamed_addr #[[ATTR3]] align 2 { // CHECK8-NEXT: entry: // CHECK8-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 // CHECK8-NEXT: [[C_ADDR:%.*]] = alloca i32, align 4 @@ -4003,28 +4023,8 @@ // CHECK8-NEXT: ret void // // -// CHECK8-LABEL: define {{[^@]+}}@_ZN1SC1Ei -// CHECK8-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[C:%.*]]) unnamed_addr #[[ATTR2]] align 2 { -// CHECK8-NEXT: entry: -// CHECK8-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 -// CHECK8-NEXT: [[C_ADDR:%.*]] = alloca i32, align 4 -// CHECK8-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 -// CHECK8-NEXT: store i32 [[C]], ptr [[C_ADDR]], align 4 -// CHECK8-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 -// CHECK8-NEXT: [[TMP0:%.*]] = load i32, ptr [[C_ADDR]], align 4 -// CHECK8-NEXT: call void @_ZN1SC2Ei(ptr noundef nonnull align 4 dereferenceable(4) [[THIS1]], i32 noundef [[TMP0]]) -// CHECK8-NEXT: ret void -// -// -// CHECK8-LABEL: define {{[^@]+}}@__cxx_global_var_init -// CHECK8-SAME: () #[[ATTR3:[0-9]+]] section "__TEXT,__StaticInit,regular,pure_instructions" { -// CHECK8-NEXT: entry: -// CHECK8-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 4 dereferenceable(4) @s, i32 noundef 1) -// CHECK8-NEXT: ret void -// -// // CHECK8-LABEL: define {{[^@]+}}@_GLOBAL__sub_I_parallel_master_taskloop_simd_codegen.cpp -// CHECK8-SAME: () #[[ATTR3]] section "__TEXT,__StaticInit,regular,pure_instructions" { +// CHECK8-SAME: () #[[ATTR2]] section "__TEXT,__StaticInit,regular,pure_instructions" { // CHECK8-NEXT: entry: // CHECK8-NEXT: call void @__cxx_global_var_init() // CHECK8-NEXT: ret void