Skip to content

Commit c54afe5

Browse files
Fix quadratic slowdown in AST matcher parent map generation (#87824)
Avoids the need to linearly re-scan all seen parent nodes to check for duplicates, which previously caused a slowdown for ancestry checks in Clang AST matchers. Fixes: #86881
1 parent 04bf1a4 commit c54afe5

File tree

2 files changed

+25
-3
lines changed

2 files changed

+25
-3
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,9 @@ Fixed Point Support in Clang
646646
AST Matchers
647647
------------
648648

649+
- Fixes a long-standing performance issue in parent map generation for
650+
ancestry-based matchers such as ``hasParent`` and ``hasAncestor``, making
651+
them significantly faster.
649652
- ``isInStdNamespace`` now supports Decl declared with ``extern "C++"``.
650653
- Add ``isExplicitObjectMemberFunction``.
651654
- Fixed ``forEachArgumentWithParam`` and ``forEachArgumentWithParamType`` to

clang/lib/AST/ParentMapContext.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,26 @@ class ParentMapContext::ParentMap {
6161
template <typename, typename...> friend struct ::MatchParents;
6262

6363
/// Contains parents of a node.
64-
using ParentVector = llvm::SmallVector<DynTypedNode, 2>;
64+
class ParentVector {
65+
public:
66+
ParentVector() = default;
67+
explicit ParentVector(size_t N, const DynTypedNode &Value) {
68+
Items.reserve(N);
69+
for (; N > 0; --N)
70+
push_back(Value);
71+
}
72+
bool contains(const DynTypedNode &Value) {
73+
return Seen.contains(Value);
74+
}
75+
void push_back(const DynTypedNode &Value) {
76+
if (!Value.getMemoizationData() || Seen.insert(Value).second)
77+
Items.push_back(Value);
78+
}
79+
llvm::ArrayRef<DynTypedNode> view() const { return Items; }
80+
private:
81+
llvm::SmallVector<DynTypedNode, 2> Items;
82+
llvm::SmallDenseSet<DynTypedNode, 2> Seen;
83+
};
6584

6685
/// Maps from a node to its parents. This is used for nodes that have
6786
/// pointer identity only, which are more common and we can save space by
@@ -99,7 +118,7 @@ class ParentMapContext::ParentMap {
99118
return llvm::ArrayRef<DynTypedNode>();
100119
}
101120
if (const auto *V = I->second.template dyn_cast<ParentVector *>()) {
102-
return llvm::ArrayRef(*V);
121+
return V->view();
103122
}
104123
return getSingleDynTypedNodeFromParentMap(I->second);
105124
}
@@ -252,7 +271,7 @@ class ParentMapContext::ParentMap {
252271
const auto *S = It->second.dyn_cast<const Stmt *>();
253272
if (!S) {
254273
if (auto *Vec = It->second.dyn_cast<ParentVector *>())
255-
return llvm::ArrayRef(*Vec);
274+
return Vec->view();
256275
return getSingleDynTypedNodeFromParentMap(It->second);
257276
}
258277
const auto *P = dyn_cast<Expr>(S);

0 commit comments

Comments
 (0)