[clang] [Clang] Cache DeclContext-to-Decl conversions (NFC) (PR #223042)

via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 11 13:02:34 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Mehdi Amini (joker-eph)

<details>
<summary>Changes</summary>

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): 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

---
Full diff: https://github.com/llvm/llvm-project/pull/223042.diff


2 Files Affected:

- (modified) clang/include/clang/AST/DeclBase.h (+24-6) 
- (modified) clang/lib/AST/DeclBase.cpp (+8-1) 


``````````diff
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) {

``````````

</details>


https://github.com/llvm/llvm-project/pull/223042


More information about the cfe-commits mailing list