[clang] [clang][dataflow] Cache getModeledFields (PR #180878)

Jan Voung via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 10 19:34:23 PST 2026


https://github.com/jvoung created https://github.com/llvm/llvm-project/pull/180878

Cache getModeledFields at the DataflowAnalysisContext level (since each
of those could have different ModeledFields). Otherwise, the
underlying getFieldsFromClassHierarchy is repeated many times and can end up
taking 4-10% of the time (compared to 40% for parsing, and 5.3% for
querySolver across several benchmarks).

Also change the return type to a reference, now that it is not fresh Set
each time (though that copy is minor).


>From eff6f2b00f5018e4f9eecc1d7ba03eee30cd9f8f Mon Sep 17 00:00:00 2001
From: Jan Voung <jvoung at gmail.com>
Date: Wed, 11 Feb 2026 03:29:12 +0000
Subject: [PATCH] [clang][dataflow] Cache getModeledFields

Cache getModeledFields at the DataflowAnalysisContext level (since each
of those could have different ModeledFields). Otherwise, the
underlying getFieldsFromClassHierarchy is repeated many times and can end up
taking 4-10% of the time (compared to 40% for parsing, and 5.3% for
querySolver across several benchmarks).

Also change the return type to a reference, now that it is not fresh Set
each time (though that copy is minor).
---
 .../FlowSensitive/DataflowAnalysisContext.h       | 15 ++++++++++++---
 .../FlowSensitive/DataflowAnalysisContext.cpp     | 12 +++++++++++-
 clang/lib/Analysis/FlowSensitive/RecordOps.cpp    |  4 ++--
 3 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
index 11042e865c4e6..ebce88a9d9aa3 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -17,6 +17,7 @@
 
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/Type.h"
 #include "clang/AST/TypeOrdering.h"
 #include "clang/Analysis/FlowSensitive/ASTOps.h"
 #include "clang/Analysis/FlowSensitive/AdornedCFG.h"
@@ -207,8 +208,9 @@ class DataflowAnalysisContext {
   Solver::Result querySolver(llvm::SetVector<const Formula *> Constraints);
 
   /// Returns the fields of `Type`, limited to the set of fields modeled by this
-  /// context.
-  FieldSet getModeledFields(QualType Type);
+  /// context. The returned reference is valid for the lifetime of the context,
+  /// or until `addModeledFields()` is called.
+  const FieldSet &getModeledFields(QualType Type);
 
   /// Returns the names and types of the synthetic fields for the given record
   /// type.
@@ -262,7 +264,11 @@ class DataflowAnalysisContext {
   /// `Tokens` in the dependency graph.
   llvm::DenseSet<Atom> collectDependencies(llvm::DenseSet<Atom> Tokens) const;
 
-  // Extends the set of modeled field declarations.
+  /// Computes and returns the fields of `Type`, limited to the set of fields
+  /// modeled by this context.
+  FieldSet computeModeledFields(QualType Type);
+
+  /// Extends the set of modeled field declarations.
   void addModeledFields(const FieldSet &Fields);
 
   /// Adds all constraints of the flow condition identified by `Token` and all
@@ -329,6 +335,9 @@ class DataflowAnalysisContext {
   // Fields modeled by environments covered by this context.
   FieldSet ModeledFields;
 
+  // Cache of modeled fields for each type, covered by this context.
+  llvm::DenseMap<QualType, std::unique_ptr<FieldSet>> CachedModeledFields;
+
   std::unique_ptr<Logger> LogOwner; // If created via flags.
 
   std::function<llvm::StringMap<QualType>(QualType)> SyntheticFieldCallback;
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
index 6e3a270e6bed6..42b3dafdb9ffa 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
+#include "clang/AST/Type.h"
 #include "clang/Analysis/FlowSensitive/ASTOps.h"
 #include "clang/Analysis/FlowSensitive/Formula.h"
 #include "clang/Analysis/FlowSensitive/Logger.h"
@@ -43,7 +44,7 @@ static llvm::cl::opt<std::string> DataflowLog(
 namespace clang {
 namespace dataflow {
 
-FieldSet DataflowAnalysisContext::getModeledFields(QualType Type) {
+FieldSet DataflowAnalysisContext::computeModeledFields(QualType Type) {
   // During context-sensitive analysis, a struct may be allocated in one
   // function, but its field accessed in a function lower in the stack than
   // the allocation. Since we only collect fields used in the function where
@@ -57,8 +58,17 @@ FieldSet DataflowAnalysisContext::getModeledFields(QualType Type) {
   return llvm::set_intersection(getObjectFields(Type), ModeledFields);
 }
 
+const FieldSet &DataflowAnalysisContext::getModeledFields(QualType Type) {
+  std::unique_ptr<FieldSet> &Fields =
+      CachedModeledFields[Type.getCanonicalType().getUnqualifiedType()];
+  if (Fields == nullptr)
+    Fields = std::make_unique<FieldSet>(computeModeledFields(Type));
+  return *Fields;
+}
+
 void DataflowAnalysisContext::addModeledFields(const FieldSet &Fields) {
   ModeledFields.set_union(Fields);
+  CachedModeledFields.clear();
 }
 
 StorageLocation &DataflowAnalysisContext::createStorageLocation(QualType Type) {
diff --git a/clang/lib/Analysis/FlowSensitive/RecordOps.cpp b/clang/lib/Analysis/FlowSensitive/RecordOps.cpp
index 03d6ed8020a0a..767521334b0a2 100644
--- a/clang/lib/Analysis/FlowSensitive/RecordOps.cpp
+++ b/clang/lib/Analysis/FlowSensitive/RecordOps.cpp
@@ -85,7 +85,7 @@ void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst,
     // Dst may have children modeled from other derived types than SrcType, e.g.
     // after casts of Dst to other types derived from DstType. Only copy the
     // children and synthetic fields present in both Dst and SrcType.
-    const FieldSet FieldsInSrcType =
+    const FieldSet &FieldsInSrcType =
         Env.getDataflowAnalysisContext().getModeledFields(SrcType);
     for (auto [Field, DstFieldLoc] : Dst.children())
       if (const auto *FieldAsFieldDecl = dyn_cast<FieldDecl>(Field);
@@ -103,7 +103,7 @@ void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst,
     // after other casts of Src to those types (likely in different branches,
     // but without flow-condition-dependent field modeling). Only copy the
     // children and synthetic fields of Src that are present in DstType.
-    const FieldSet FieldsInDstType =
+    const FieldSet &FieldsInDstType =
         Env.getDataflowAnalysisContext().getModeledFields(DstType);
     for (auto [Field, SrcFieldLoc] : Src.children()) {
       if (const auto *FieldAsFieldDecl = dyn_cast<FieldDecl>(Field);



More information about the cfe-commits mailing list