[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