https://github.com/joker-eph updated https://github.com/llvm/llvm-project/pull/223042
>From 2a109893ce3f5970a9c618b8845eb4d158b3c6e6 Mon Sep 17 00:00:00 2001 From: Mehdi Amini <[email protected]> Date: Thu, 10 Sep 2026 05:32:04 -0700 Subject: [PATCH 1/2] Cache DeclContext-to-Decl conversions Cache the corresponding Decl pointer after the first DeclContext conversion and route parent traversal and generic casts through the cache. Use relaxed atomic access so concurrent read-only AST traversal remains race-free. CTMark O0 (3 samples, CPU 6): 29.439800 s -> 29.303500 s (-0.463%). Impact on significant TUs in MLIR build time: - `mlir/lib/RegisterAllDialects.cpp`: 2.5437% fewer retired instructions. - `mlir/lib/Dialect/LLVMIR/IR/NVVMDialect.cpp`: 1.3250% fewer retired instructions. Assisted-by: Codex --- clang/include/clang/AST/DeclBase.h | 30 ++++++++++++++++++++++++------ clang/lib/AST/DeclBase.cpp | 9 ++++++++- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 9d233be282dbb..8d0fcd76dd5ca 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -33,6 +33,7 @@ #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/VersionTuple.h" #include <algorithm> +#include <atomic> #include <cassert> #include <cstddef> #include <iterator> @@ -1464,6 +1465,8 @@ enum class LinkageSpecLanguageIDs; /// BlockDecl /// CapturedDecl class DeclContext { + friend class Decl; + /// For makeDeclVisibleInContextImpl friend class ASTDeclReader; /// For checking the new bits in the Serialization part. @@ -2101,6 +2104,12 @@ class DeclContext { /// another pointer. mutable Decl *LastDecl = nullptr; + /// The corresponding declaration, cached after the first conversion. + /// This correspondence is immutable. Relaxed atomics allow concurrent + /// read-only AST traversals to populate the cache without synchronizing + /// mutations to the AST itself. + mutable std::atomic<Decl *> CachedDecl = nullptr; + /// Build up a chain of declarations. /// /// \returns the first/last pair of declarations. @@ -2122,10 +2131,19 @@ class DeclContext { const char *getDeclKindName() const; - /// getParent - Returns the containing DeclContext. - DeclContext *getParent() { - return cast<Decl>(this)->getDeclContext(); + /// Return the declaration containing this context. + Decl *getAsDecl() { + if (Decl *Cached = CachedDecl.load(std::memory_order_relaxed)) + return Cached; + return Decl::castFromDeclContext(this); } + + const Decl *getAsDecl() const { + return const_cast<DeclContext *>(this)->getAsDecl(); + } + + /// getParent - Returns the containing DeclContext. + DeclContext *getParent() { return getAsDecl()->getDeclContext(); } const DeclContext *getParent() const { return const_cast<DeclContext*>(this)->getParent(); } @@ -2140,7 +2158,7 @@ class DeclContext { /// // getLexicalParent() == translation unit /// DeclContext *getLexicalParent() { - return cast<Decl>(this)->getLexicalDeclContext(); + return getAsDecl()->getLexicalDeclContext(); } const DeclContext *getLexicalParent() const { return const_cast<DeclContext*>(this)->getLexicalParent(); @@ -2830,11 +2848,11 @@ template <class ToTy, bool IsKnownSubtype = ::std::is_base_of<DeclContext, ToTy>::value> struct cast_convert_decl_context { static const ToTy *doit(const DeclContext *Val) { - return static_cast<const ToTy*>(Decl::castFromDeclContext(Val)); + return static_cast<const ToTy *>(Val->getAsDecl()); } static ToTy *doit(DeclContext *Val) { - return static_cast<ToTy*>(Decl::castFromDeclContext(Val)); + return static_cast<ToTy *>(Val->getAsDecl()); } }; diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 70f61fa57a682..e0903a19a8c5a 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -1080,16 +1080,23 @@ const AttrVec &Decl::getAttrs() const { } Decl *Decl::castFromDeclContext (const DeclContext *D) { + if (Decl *Cached = D->CachedDecl.load(std::memory_order_relaxed)) + return Cached; + Decl::Kind DK = D->getDeclKind(); + Decl *Result = nullptr; switch (DK) { #define DECL(NAME, BASE) #define DECL_CONTEXT(NAME) \ case Decl::NAME: \ - return static_cast<NAME##Decl *>(const_cast<DeclContext *>(D)); + Result = static_cast<NAME##Decl *>(const_cast<DeclContext *>(D)); \ + break; #include "clang/AST/DeclNodes.inc" default: llvm_unreachable("a decl that inherits DeclContext isn't handled"); } + D->CachedDecl.store(Result, std::memory_order_relaxed); + return Result; } DeclContext *Decl::castToDeclContext(const Decl *D) { >From c5eae44e7ad12116d9bc1f62406240712cf24678 Mon Sep 17 00:00:00 2001 From: Mehdi Amini <[email protected]> Date: Thu, 10 Sep 2026 05:32:04 -0700 Subject: [PATCH 2/2] Cache DeclContext-to-Decl conversions Store the owning Decl pointer when constructing each DeclContext and route parent traversal and generic casts through it. This avoids a lazy lookup and does not imply support for concurrent AST traversal. CTMark O0 (three alternating matched-build samples, CPU 6): 28.990700 s -> 28.702600 s (-0.9938%). Peak build RSS: 258016 -> 259276 KiB (+0.4883%). All 632 normalized objects matched in each pair. MLIR build-time medians (three alternating matched-build samples, CPU 6): - `mlir/lib/RegisterAllDialects.cpp`: 2.8115% fewer retired instructions, 0.7353% less user CPU, 0.1972% less wall time, and 7144 KiB (+0.5257%) peak RSS. - `mlir/lib/Dialect/LLVMIR/IR/NVVMDialect.cpp`: 1.5841% fewer retired instructions, 1.1120% less user CPU, 0.9893% less wall time, and 912 KiB (+0.0795%) peak RSS. All MLIR outputs matched after removing only `.comment`; each concurrency guard log was empty. Assisted-by: Codex --- clang/include/clang/AST/Decl.h | 9 +++++---- clang/include/clang/AST/DeclBase.h | 19 +++++-------------- clang/include/clang/AST/DeclCXX.h | 3 ++- clang/include/clang/AST/DeclOpenMP.h | 2 +- clang/lib/AST/Decl.cpp | 18 ++++++++++-------- clang/lib/AST/DeclBase.cpp | 22 +++------------------- clang/lib/AST/DeclCXX.cpp | 4 ++-- clang/lib/AST/DeclObjC.cpp | 6 +++--- clang/lib/AST/DeclOpenMP.cpp | 2 +- clang/lib/AST/DeclTemplate.cpp | 2 +- 10 files changed, 33 insertions(+), 54 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index eafeeecac7794..dba6f60397bba 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -247,8 +247,8 @@ class PragmaDetectMismatchDecl final /// lookup in the extern "C" context. class ExternCContextDecl : public Decl, public DeclContext { explicit ExternCContextDecl(TranslationUnitDecl *TU) - : Decl(ExternCContext, TU, SourceLocation()), - DeclContext(ExternCContext) {} + : Decl(ExternCContext, TU, SourceLocation()), + DeclContext(ExternCContext, this) {} virtual void anchor(); @@ -4775,7 +4775,8 @@ class TopLevelStmtDecl : public Decl, public DeclContext { bool IsSemiMissing = false; TopLevelStmtDecl(DeclContext *DC, SourceLocation L, Stmt *S) - : Decl(TopLevelStmt, DC, L), DeclContext(TopLevelStmt), Statement(S) {} + : Decl(TopLevelStmt, DC, L), DeclContext(TopLevelStmt, this), + Statement(S) {} virtual void anchor(); @@ -5274,7 +5275,7 @@ class ExportDecl final : public Decl, public DeclContext { SourceLocation RBraceLoc; ExportDecl(DeclContext *DC, SourceLocation ExportLoc) - : Decl(Export, DC, ExportLoc), DeclContext(Export), + : Decl(Export, DC, ExportLoc), DeclContext(Export, this), RBraceLoc(SourceLocation()) {} public: diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 8d0fcd76dd5ca..d40fe642a4a2d 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -2104,11 +2104,8 @@ class DeclContext { /// another pointer. mutable Decl *LastDecl = nullptr; - /// The corresponding declaration, cached after the first conversion. - /// This correspondence is immutable. Relaxed atomics allow concurrent - /// read-only AST traversals to populate the cache without synchronizing - /// mutations to the AST itself. - mutable std::atomic<Decl *> CachedDecl = nullptr; + /// The declaration corresponding to this context. + Decl *const CorrespondingDecl; /// Build up a chain of declarations. /// @@ -2116,7 +2113,7 @@ class DeclContext { static std::pair<Decl *, Decl *> BuildDeclChain(ArrayRef<Decl*> Decls, bool FieldsAlreadyLoaded); - DeclContext(Decl::Kind K); + DeclContext(Decl::Kind K, Decl *D); public: ~DeclContext(); @@ -2132,15 +2129,9 @@ class DeclContext { const char *getDeclKindName() const; /// Return the declaration containing this context. - Decl *getAsDecl() { - if (Decl *Cached = CachedDecl.load(std::memory_order_relaxed)) - return Cached; - return Decl::castFromDeclContext(this); - } + Decl *getAsDecl() { return CorrespondingDecl; } - const Decl *getAsDecl() const { - return const_cast<DeclContext *>(this)->getAsDecl(); - } + const Decl *getAsDecl() const { return CorrespondingDecl; } /// getParent - Returns the containing DeclContext. DeclContext *getParent() { return getAsDecl()->getDeclContext(); } diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index afe46fae1bceb..0c6549cc6ef90 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -2117,7 +2117,8 @@ class CXXDeductionGuideDecl : public FunctionDecl { /// template argument list imposed by the compound requirement. class RequiresExprBodyDecl : public Decl, public DeclContext { RequiresExprBodyDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc) - : Decl(RequiresExprBody, DC, StartLoc), DeclContext(RequiresExprBody) {} + : Decl(RequiresExprBody, DC, StartLoc), + DeclContext(RequiresExprBody, this) {} public: friend class ASTDeclReader; diff --git a/clang/include/clang/AST/DeclOpenMP.h b/clang/include/clang/AST/DeclOpenMP.h index 06414cef6baf3..c76f590fec2c1 100644 --- a/clang/include/clang/AST/DeclOpenMP.h +++ b/clang/include/clang/AST/DeclOpenMP.h @@ -365,7 +365,7 @@ class OMPDeclareMapperDecl final : public OMPDeclarativeDirective<ValueDecl>, QualType Ty, DeclarationName VarName, OMPDeclareMapperDecl *PrevDeclInScope) : OMPDeclarativeDirective<ValueDecl>(OMPDeclareMapper, DC, L, Name, Ty), - DeclContext(OMPDeclareMapper), VarName(VarName), + DeclContext(OMPDeclareMapper, this), VarName(VarName), PrevDeclInScope(PrevDeclInScope) {} void setPrevDeclInScope(OMPDeclareMapperDecl *Prev) { diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index d1d296dd60d14..f3443479a8f9a 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -103,7 +103,7 @@ bool Decl::isOutOfLine() const { TranslationUnitDecl::TranslationUnitDecl(ASTContext &ctx) : Decl(TranslationUnit, nullptr, SourceLocation()), - DeclContext(TranslationUnit), redeclarable_base(ctx), Ctx(ctx) {} + DeclContext(TranslationUnit, this), redeclarable_base(ctx), Ctx(ctx) {} //===----------------------------------------------------------------------===// // NamedDecl Implementation @@ -3073,7 +3073,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, const AssociatedConstraint &TrailingRequiresClause) : DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo, StartLoc), - DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0), + DeclContext(DK, this), redeclarable_base(C), Body(), ODRHash(0), EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) { assert(T.isNull() || T->isFunctionType()); FunctionDeclBits.SClass = S; @@ -4939,7 +4939,8 @@ const FieldDecl *FieldDecl::findCountedByField() const { TagDecl::TagDecl(Kind DK, TagKind TK, const ASTContext &C, DeclContext *DC, SourceLocation L, IdentifierInfo *Id, TagDecl *PrevDecl, SourceLocation StartL) - : TypeDecl(DK, DC, L, Id, StartL), DeclContext(DK), redeclarable_base(C), + : TypeDecl(DK, DC, L, Id, StartL), DeclContext(DK, this), + redeclarable_base(C), TypedefNameDeclOrQualifier((TypedefNameDecl *)nullptr) { assert((DK != Enum || TK == TagTypeKind::Enum) && "EnumDecl not matched with TagTypeKind::Enum"); @@ -5501,7 +5502,7 @@ unsigned RecordDecl::getODRHash() { //===----------------------------------------------------------------------===// BlockDecl::BlockDecl(DeclContext *DC, SourceLocation CaretLoc) - : Decl(Block, DC, CaretLoc), DeclContext(Block) { + : Decl(Block, DC, CaretLoc), DeclContext(Block, this) { setIsVariadic(false); setCapturesCXXThis(false); setBlockMissingReturnType(true); @@ -5724,7 +5725,7 @@ BlockDecl *BlockDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { OutlinedFunctionDecl::OutlinedFunctionDecl(DeclContext *DC, unsigned NumParams) : Decl(OutlinedFunction, DC, SourceLocation()), - DeclContext(OutlinedFunction), NumParams(NumParams), + DeclContext(OutlinedFunction, this), NumParams(NumParams), BodyAndNothrow(nullptr, false) {} OutlinedFunctionDecl *OutlinedFunctionDecl::Create(ASTContext &C, @@ -5752,7 +5753,7 @@ void OutlinedFunctionDecl::setNothrow(bool Nothrow) { } CapturedDecl::CapturedDecl(DeclContext *DC, unsigned NumParams) - : Decl(Captured, DC, SourceLocation()), DeclContext(Captured), + : Decl(Captured, DC, SourceLocation()), DeclContext(Captured, this), NumParams(NumParams), ContextParam(0), BodyAndNothrow(nullptr, false) {} CapturedDecl *CapturedDecl::Create(ASTContext &C, DeclContext *DC, @@ -5971,8 +5972,9 @@ HLSLBufferDecl::HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc, IdentifierInfo *ID, SourceLocation IDLoc, SourceLocation LBrace) : NamedDecl(Decl::Kind::HLSLBuffer, DC, IDLoc, DeclarationName(ID)), - DeclContext(Decl::Kind::HLSLBuffer), LBraceLoc(LBrace), KwLoc(KwLoc), - IsCBuffer(CBuffer), HasValidPackoffset(false), LayoutStruct(nullptr) {} + DeclContext(Decl::Kind::HLSLBuffer, this), LBraceLoc(LBrace), + KwLoc(KwLoc), IsCBuffer(CBuffer), HasValidPackoffset(false), + LayoutStruct(nullptr) {} HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C, DeclContext *LexicalParent, bool CBuffer, diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index e0903a19a8c5a..3d4be8154d447 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -1079,24 +1079,8 @@ const AttrVec &Decl::getAttrs() const { return getASTContext().getDeclAttrs(this); } -Decl *Decl::castFromDeclContext (const DeclContext *D) { - if (Decl *Cached = D->CachedDecl.load(std::memory_order_relaxed)) - return Cached; - - Decl::Kind DK = D->getDeclKind(); - Decl *Result = nullptr; - switch (DK) { -#define DECL(NAME, BASE) -#define DECL_CONTEXT(NAME) \ - case Decl::NAME: \ - Result = static_cast<NAME##Decl *>(const_cast<DeclContext *>(D)); \ - break; -#include "clang/AST/DeclNodes.inc" - default: - llvm_unreachable("a decl that inherits DeclContext isn't handled"); - } - D->CachedDecl.store(Result, std::memory_order_relaxed); - return Result; +Decl *Decl::castFromDeclContext(const DeclContext *D) { + return const_cast<Decl *>(D->getAsDecl()); } DeclContext *Decl::castToDeclContext(const Decl *D) { @@ -1315,7 +1299,7 @@ Decl *DeclContext::getNonClosureAncestor() { // DeclContext Implementation //===----------------------------------------------------------------------===// -DeclContext::DeclContext(Decl::Kind K) { +DeclContext::DeclContext(Decl::Kind K, Decl *D) : CorrespondingDecl(D) { DeclContextBits.DeclKind = K; setHasExternalLexicalStorage(false); setHasExternalVisibleStorage(false); diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index f0da56542ae7e..0b46ab1662d5d 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -3302,7 +3302,7 @@ bool CXXConversionDecl::isLambdaToBlockPointerConversion() const { LinkageSpecDecl::LinkageSpecDecl(DeclContext *DC, SourceLocation ExternLoc, SourceLocation LangLoc, LinkageSpecLanguageIDs lang, bool HasBraces) - : Decl(LinkageSpec, DC, LangLoc), DeclContext(LinkageSpec), + : Decl(LinkageSpec, DC, LangLoc), DeclContext(LinkageSpec, this), ExternLoc(ExternLoc), RBraceLoc(SourceLocation()) { setLanguage(lang); LinkageSpecDeclBits.HasBraces = HasBraces; @@ -3364,7 +3364,7 @@ NamespaceDecl::NamespaceDecl(ASTContext &C, DeclContext *DC, bool Inline, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, NamespaceDecl *PrevDecl, bool Nested) - : NamespaceBaseDecl(Namespace, DC, IdLoc, Id), DeclContext(Namespace), + : NamespaceBaseDecl(Namespace, DC, IdLoc, Id), DeclContext(Namespace, this), redeclarable_base(C), LocStart(StartLoc) { setInline(Inline); setNested(Nested); diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index 1adf6c9e048a9..06d02c64a14f2 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -66,7 +66,7 @@ ObjCContainerDecl::ObjCContainerDecl(Kind DK, DeclContext *DC, const IdentifierInfo *Id, SourceLocation nameLoc, SourceLocation atStartLoc) - : NamedDecl(DK, DC, nameLoc, Id), DeclContext(DK) { + : NamedDecl(DK, DC, nameLoc, Id), DeclContext(DK, this) { setAtStartLoc(atStartLoc); } @@ -823,8 +823,8 @@ ObjCMethodDecl::ObjCMethodDecl( bool isSynthesizedAccessorStub, bool isImplicitlyDeclared, bool isDefined, ObjCImplementationControl impControl, bool HasRelatedResultType) : NamedDecl(ObjCMethod, contextDecl, beginLoc, SelInfo), - DeclContext(ObjCMethod), MethodDeclType(T), ReturnTInfo(ReturnTInfo), - DeclEndLoc(endLoc) { + DeclContext(ObjCMethod, this), MethodDeclType(T), + ReturnTInfo(ReturnTInfo), DeclEndLoc(endLoc) { // Initialized the bits stored in DeclContext. ObjCMethodDeclBits.Family = diff --git a/clang/lib/AST/DeclOpenMP.cpp b/clang/lib/AST/DeclOpenMP.cpp index ef08a1c30042f..0a59b070023fe 100644 --- a/clang/lib/AST/DeclOpenMP.cpp +++ b/clang/lib/AST/DeclOpenMP.cpp @@ -132,7 +132,7 @@ OMPRequiresDecl *OMPRequiresDecl::CreateDeserialized(ASTContext &C, OMPDeclareReductionDecl::OMPDeclareReductionDecl( Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name, QualType Ty, OMPDeclareReductionDecl *PrevDeclInScope) - : ValueDecl(DK, DC, L, Name, Ty), DeclContext(DK), Combiner(nullptr), + : ValueDecl(DK, DC, L, Name, Ty), DeclContext(DK, this), Combiner(nullptr), PrevDeclInScope(PrevDeclInScope) { setInitializer(nullptr, OMPDeclareReductionInitKind::Call); } diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 64cb49b0aa53e..66ab9885f2907 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1955,7 +1955,7 @@ SourceRange ExplicitInstantiationDecl::getSourceRange() const { CXXExpansionStmtDecl::CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc, NonTypeTemplateParmDecl *NTTP) - : Decl(CXXExpansionStmt, DC, Loc), DeclContext(CXXExpansionStmt), + : Decl(CXXExpansionStmt, DC, Loc), DeclContext(CXXExpansionStmt, this), IndexNTTP(NTTP) {} CXXExpansionStmtDecl * _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
