https://github.com/zyn0217 created https://github.com/llvm/llvm-project/pull/230369
This reduces size of ConstraintSatisfaction and ASTConstraintSatisfaction, from 184 bytes to 32 bytes and from 8 bytes to 4 bytes respectively. As a result this has to convert SatisfactionCache from a ContextualFoldingSet to DenseMap to get rid of unused template argument list on stack. Hopefully this could improve our RSS consumption when deducing template arguments (we have to pay an extra ConstraintSatisfaction cost even no concepts are involved) >From 4c24d35b9592a815d31ed590deadaa36d0414396 Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Fri, 9 Oct 2026 12:32:41 +0800 Subject: [PATCH] [Clang] Improve ConstraintSatisfaction --- clang/include/clang/AST/ASTConcept.h | 49 +++++++++---------- clang/include/clang/AST/ExprConcepts.h | 4 +- clang/include/clang/Sema/Sema.h | 7 +-- clang/lib/AST/ASTConcept.cpp | 12 ++--- clang/lib/AST/ASTImporter.cpp | 4 +- clang/lib/AST/ComputeDependence.cpp | 2 +- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 2 +- clang/lib/Sema/Sema.cpp | 8 +-- clang/lib/Sema/SemaConcept.cpp | 12 ++--- clang/lib/Sema/SemaDeclCXX.cpp | 2 +- clang/lib/Sema/SemaExprCXX.cpp | 10 +--- clang/lib/Sema/TreeTransform.h | 4 +- clang/lib/Serialization/ASTWriterStmt.cpp | 8 +-- 13 files changed, 52 insertions(+), 72 deletions(-) diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h index 62fe2c0f653d57..71a16931d6b3d0 100644 --- a/clang/include/clang/AST/ASTConcept.h +++ b/clang/include/clang/AST/ASTConcept.h @@ -44,31 +44,15 @@ using UnsatisfiedConstraintRecord = /// The result of a constraint satisfaction check, containing the necessary /// information to diagnose an unsatisfied constraint. -class ConstraintSatisfaction : public llvm::FoldingSetNode { - // The template-like entity that 'owns' the constraint checked here (can be a - // constrained entity or a concept). - const NamedDecl *ConstraintOwner = nullptr; - llvm::SmallVector<TemplateArgument, 4> TemplateArgs; - +class ConstraintSatisfaction { public: - - ConstraintSatisfaction() = default; - - ConstraintSatisfaction(const NamedDecl *ConstraintOwner, - ArrayRef<TemplateArgument> TemplateArgs) - : ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs) {} - bool IsSatisfied = false; bool ContainsErrors = false; /// \brief The substituted constraint expr, if the template arguments could be /// substituted into them, or a diagnostic if substitution resulted in an /// invalid expression. - llvm::SmallVector<UnsatisfiedConstraintRecord, 4> Details; - - void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) { - Profile(ID, C, ConstraintOwner, TemplateArgs); - } + llvm::SmallVector<UnsatisfiedConstraintRecord, 1> Details; static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C, const NamedDecl *ConstraintOwner, @@ -86,13 +70,24 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode { /// necessary information to diagnose an unsatisfied constraint. /// /// This is safe to store in an AST node, as opposed to ConstraintSatisfaction. -struct ASTConstraintSatisfaction final : - llvm::TrailingObjects<ASTConstraintSatisfaction, - UnsatisfiedConstraintRecord> { - std::size_t NumRecords; +class ASTConstraintSatisfaction final + : llvm::TrailingObjects<ASTConstraintSatisfaction, + UnsatisfiedConstraintRecord> { + friend TrailingObjects; + friend class ASTStmtWriter; + friend class ASTStmtReader; + + unsigned NumRecords : 30; bool IsSatisfied : 1; bool ContainsErrors : 1; + ASTConstraintSatisfaction(const ASTContext &C, + const ConstraintSatisfaction &Satisfaction); + + ASTConstraintSatisfaction(const ASTContext &C, + const ASTConstraintSatisfaction &Satisfaction); + +public: const UnsatisfiedConstraintRecord *begin() const { return getTrailingObjects(); } @@ -105,13 +100,15 @@ struct ASTConstraintSatisfaction final : return {begin(), end()}; } - ASTConstraintSatisfaction(const ASTContext &C, - const ConstraintSatisfaction &Satisfaction); - ASTConstraintSatisfaction(const ASTContext &C, - const ASTConstraintSatisfaction &Satisfaction); + bool isSatisfied() const { return IsSatisfied; } + + bool containsErrors() const { return ContainsErrors; } + + unsigned record_size() const { return NumRecords; } static ASTConstraintSatisfaction * Create(const ASTContext &C, const ConstraintSatisfaction &Satisfaction); + static ASTConstraintSatisfaction * Rebuild(const ASTContext &C, const ASTConstraintSatisfaction &Satisfaction); }; diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h index 18ce5a94ebef3c..7d977069e80964 100644 --- a/clang/include/clang/AST/ExprConcepts.h +++ b/clang/include/clang/AST/ExprConcepts.h @@ -128,7 +128,7 @@ class ConceptSpecializationExpr final : public Expr { bool isSatisfied() const { assert(!isValueDependent() && "isSatisfied called on a dependent ConceptSpecializationExpr"); - return Satisfaction->IsSatisfied; + return Satisfaction->isSatisfied(); } /// \brief Get elaborated satisfaction info about the template arguments' @@ -463,7 +463,7 @@ class NestedRequirement : public Requirement { : Requirement(RK_Nested, /*IsDependent=*/false, /*ContainsUnexpandedParameterPack*/ false, - Satisfaction->IsSatisfied), + Satisfaction->isSatisfied()), Satisfaction(Satisfaction), HasInvalidConstraint(true), InvalidConstraintEntity(InvalidConstraintEntity) {} diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0864337a9374c6..e33d15c11cd757 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -115,7 +115,7 @@ struct InlineAsmIdentifierInfo; namespace clang { class ADLResult; class APValue; -struct ASTConstraintSatisfaction; +class ASTConstraintSatisfaction; class ASTConsumer; class ASTContext; class ASTDeclReader; @@ -8883,9 +8883,6 @@ class Sema final : public SemaBase { concepts::TypeRequirement *BuildTypeRequirement( concepts::Requirement::SubstitutionDiagnostic *SubstDiag); concepts::NestedRequirement *BuildNestedRequirement(Expr *E); - concepts::NestedRequirement * - BuildNestedRequirement(StringRef InvalidConstraintEntity, - const ASTConstraintSatisfaction &Satisfaction); ExprResult ActOnRequiresExpr(SourceLocation RequiresKWLoc, RequiresExprBodyDecl *Body, SourceLocation LParenLoc, @@ -15165,7 +15162,7 @@ class Sema final : public SemaBase { /// Cache whether the associated constraint of a declaration /// is satisfied. - llvm::ContextualFoldingSet<ConstraintSatisfaction, const ASTContext &> + llvm::DenseMap<llvm::FoldingSetNodeID, ConstraintSatisfaction *> SatisfactionCache; // The current stack of constraint satisfactions, so we can exit-early. diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp index 2b04821a157da4..ee8ef848cebe09 100644 --- a/clang/lib/AST/ASTConcept.cpp +++ b/clang/lib/AST/ASTConcept.cpp @@ -43,9 +43,9 @@ CreateUnsatisfiedConstraintRecord(const ASTContext &C, ASTConstraintSatisfaction::ASTConstraintSatisfaction( const ASTContext &C, const ConstraintSatisfaction &Satisfaction) - : NumRecords{Satisfaction.Details.size()}, - IsSatisfied{Satisfaction.IsSatisfied}, ContainsErrors{ - Satisfaction.ContainsErrors} { + : NumRecords(Satisfaction.Details.size()), + IsSatisfied(Satisfaction.IsSatisfied), + ContainsErrors(Satisfaction.ContainsErrors) { for (unsigned I = 0; I < NumRecords; ++I) CreateUnsatisfiedConstraintRecord(C, Satisfaction.Details[I], getTrailingObjects() + I); @@ -53,9 +53,9 @@ ASTConstraintSatisfaction::ASTConstraintSatisfaction( ASTConstraintSatisfaction::ASTConstraintSatisfaction( const ASTContext &C, const ASTConstraintSatisfaction &Satisfaction) - : NumRecords{Satisfaction.NumRecords}, - IsSatisfied{Satisfaction.IsSatisfied}, - ContainsErrors{Satisfaction.ContainsErrors} { + : NumRecords(Satisfaction.NumRecords), + IsSatisfied(Satisfaction.IsSatisfied), + ContainsErrors(Satisfaction.ContainsErrors) { for (unsigned I = 0; I < NumRecords; ++I) CreateUnsatisfiedConstraintRecord(C, *(Satisfaction.begin() + I), getTrailingObjects() + I); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 22c3af47f451f6..10ee9b75afec0f 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1075,8 +1075,8 @@ StringRef ASTNodeImporter::ImportASTStringRef(StringRef FromStr) { Error ASTNodeImporter::ImportConstraintSatisfaction( const ASTConstraintSatisfaction &FromSat, ConstraintSatisfaction &ToSat) { - ToSat.IsSatisfied = FromSat.IsSatisfied; - ToSat.ContainsErrors = FromSat.ContainsErrors; + ToSat.IsSatisfied = FromSat.isSatisfied(); + ToSat.ContainsErrors = FromSat.containsErrors(); if (!ToSat.IsSatisfied) { for (auto Record = FromSat.begin(); Record != FromSat.end(); ++Record) { if (const Expr *E = Record->dyn_cast<const Expr *>()) { diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index 7ea1c67ff77389..c4bf3681a79122 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -955,7 +955,7 @@ ExprDependence clang::computeDependence(ConceptSpecializationExpr *E, ExprDependence D = ValueDependent ? ExprDependence::Value : ExprDependence::None; auto Res = D | toExprDependence(TA); - if(!ValueDependent && E->getSatisfaction().ContainsErrors) + if (!ValueDependent && E->getSatisfaction().containsErrors()) Res |= ExprDependence::Error; return Res; } diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index e49b1cba7b1dde..54d5e3eba66af8 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -433,7 +433,7 @@ TemplateParameterListBuilder::constructConceptSpecializationExpr( // Constraint satisfaction is used to construct the // ConceptSpecailizationExpr, and represents the 2nd Template Argument, // located at the bottom of the sample AST above. - const ConstraintSatisfaction CS(CD, {ConceptTA}); + ConstraintSatisfaction CS; TemplateArgumentLoc TAL = S.getTrivialTemplateArgumentLoc(ConceptTA, QualType(), SourceLocation()); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 21f71d7f8b40e2..25ead1308c537a 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -328,7 +328,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, GlobalNewDeleteDeclared(false), DisableTypoCorrection(false), TyposCorrected(0), IsBuildingRecoveryCallExpr(false), CurrentInstantiationScope(nullptr), NonInstantiationEntries(0), - ArgPackSubstIndex(std::nullopt), SatisfactionCache(Context) { + ArgPackSubstIndex(std::nullopt) { assert(pp.TUKind == TUKind); TUScope = nullptr; @@ -626,11 +626,7 @@ Sema::~Sema() { ExternalSema->ForgetSema(); // Delete cached satisfactions. - std::vector<ConstraintSatisfaction *> Satisfactions; - Satisfactions.reserve(SatisfactionCache.size()); - for (auto &Node : SatisfactionCache) - Satisfactions.push_back(&Node); - for (auto *Node : Satisfactions) + for (auto [_, Node] : SatisfactionCache) delete Node; threadSafety::threadSafetyCleanup(ThreadSafetyDeclCache); diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index fb66bdd6d2bd04..15b6b9c0c8b396 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1323,14 +1323,12 @@ bool Sema::CheckConstraintSatisfaction( llvm::FoldingSetNodeID ID; ConstraintSatisfaction::Profile(ID, Context, Owner, FlattenedArgs); - llvm::FoldingSetInsertToken Token; - if (auto *Cached = SatisfactionCache.lookup(ID, Token)) { + if (auto *Cached = SatisfactionCache.lookup(ID)) { OutSatisfaction = *Cached; return false; } - auto Satisfaction = - std::make_unique<ConstraintSatisfaction>(Owner, FlattenedArgs); + auto Satisfaction = std::make_unique<ConstraintSatisfaction>(); if (::CheckConstraintSatisfaction( *this, Template, AssociatedConstraints, TemplateArgsLists, TemplateIDRange, *Satisfaction, ConvertedExpr, TopLevelConceptId)) { @@ -1338,7 +1336,7 @@ bool Sema::CheckConstraintSatisfaction( return true; } - if (auto *Cached = SatisfactionCache.lookup(ID, Token)) { + if (auto *Cached = SatisfactionCache.lookup(ID)) { // The evaluation of this constraint resulted in us trying to re-evaluate it // recursively. This isn't really possible, except we try to form a // RecoveryExpr as a part of the evaluation. If this is the case, just @@ -1354,7 +1352,7 @@ bool Sema::CheckConstraintSatisfaction( // Else we can simply add this satisfaction to the list. OutSatisfaction = *Satisfaction; // Note that entries of SatisfactionCache are deleted in Sema's destructor. - SatisfactionCache.insert(Satisfaction.release()); + SatisfactionCache.insert({ID, Satisfaction.release()}); return false; } @@ -2060,7 +2058,7 @@ void Sema::DiagnoseUnsatisfiedConstraint( const ASTConstraintSatisfaction &Satisfaction = ConstraintExpr->getSatisfaction(); - assert(!Satisfaction.IsSatisfied && + assert(!Satisfaction.isSatisfied() && "Attempted to diagnose a satisfied constraint"); ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.records(), diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index ea628f29d8a005..af8e53d4aea2ed 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18092,7 +18092,7 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, dyn_cast_or_null<ConceptSpecializationExpr>(InnerCond)) { const ASTConstraintSatisfaction &Satisfaction = ConceptIDExpr->getSatisfaction(); - if (!Satisfaction.ContainsErrors || Satisfaction.NumRecords) { + if (!Satisfaction.containsErrors() || Satisfaction.record_size()) { Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed) << !HasMessage << Msg.str() << AssertExpr->getSourceRange(); // Drill down into concept specialization expressions to see why they diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index deb7bdd09fdc7d..e65b8240255c96 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8121,7 +8121,7 @@ Sema::BuildExprRequirement( if (!HasError) { SubstitutedConstraintExpr = cast<ConceptSpecializationExpr>(Constraint.get()); - if (SubstitutedConstraintExpr->getSatisfaction().ContainsErrors) + if (SubstitutedConstraintExpr->getSatisfaction().containsErrors()) HasError = true; } if (HasError) { @@ -8191,14 +8191,6 @@ Sema::BuildNestedRequirement(Expr *Constraint) { Satisfaction); } -concepts::NestedRequirement * -Sema::BuildNestedRequirement(StringRef InvalidConstraintEntity, - const ASTConstraintSatisfaction &Satisfaction) { - return new (Context) concepts::NestedRequirement( - InvalidConstraintEntity, - ASTConstraintSatisfaction::Rebuild(Context, Satisfaction)); -} - RequiresExprBodyDecl * Sema::ActOnStartRequiresExpr(SourceLocation RequiresKWLoc, ArrayRef<ParmVarDecl *> LocalParameters, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index c8458fda58a88e..841228d9272050 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3872,8 +3872,8 @@ class TreeTransform { concepts::NestedRequirement * RebuildNestedRequirement(StringRef InvalidConstraintEntity, const ASTConstraintSatisfaction &Satisfaction) { - return SemaRef.BuildNestedRequirement(InvalidConstraintEntity, - Satisfaction); + return new (getSema().Context) + concepts::NestedRequirement(InvalidConstraintEntity, &Satisfaction); } concepts::NestedRequirement *RebuildNestedRequirement(Expr *Constraint) { diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 513c832c38ffd9..dea4d34fb73f80 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -490,10 +490,10 @@ void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) { static void addConstraintSatisfaction(ASTRecordWriter &Record, const ASTConstraintSatisfaction &Satisfaction) { - Record.push_back(Satisfaction.IsSatisfied); - Record.push_back(Satisfaction.ContainsErrors); - if (!Satisfaction.IsSatisfied) { - Record.push_back(Satisfaction.NumRecords); + Record.push_back(Satisfaction.isSatisfied()); + Record.push_back(Satisfaction.containsErrors()); + if (!Satisfaction.isSatisfied()) { + Record.push_back(Satisfaction.record_size()); for (const auto &DetailRecord : Satisfaction) { if (auto *Diag = dyn_cast<const ConstraintSubstitutionDiagnostic *>( DetailRecord)) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
