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
This commit is contained in:
higher-performance 2024-04-10 13:24:19 -04:00 committed by GitHub
parent 04bf1a4090
commit c54afe5c33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 25 additions and 3 deletions

View File

@ -646,6 +646,9 @@ Fixed Point Support in Clang
AST Matchers AST Matchers
------------ ------------
- Fixes a long-standing performance issue in parent map generation for
ancestry-based matchers such as ``hasParent`` and ``hasAncestor``, making
them significantly faster.
- ``isInStdNamespace`` now supports Decl declared with ``extern "C++"``. - ``isInStdNamespace`` now supports Decl declared with ``extern "C++"``.
- Add ``isExplicitObjectMemberFunction``. - Add ``isExplicitObjectMemberFunction``.
- Fixed ``forEachArgumentWithParam`` and ``forEachArgumentWithParamType`` to - Fixed ``forEachArgumentWithParam`` and ``forEachArgumentWithParamType`` to

View File

@ -61,7 +61,26 @@ class ParentMapContext::ParentMap {
template <typename, typename...> friend struct ::MatchParents; template <typename, typename...> friend struct ::MatchParents;
/// Contains parents of a node. /// Contains parents of a node.
using ParentVector = llvm::SmallVector<DynTypedNode, 2>; class ParentVector {
public:
ParentVector() = default;
explicit ParentVector(size_t N, const DynTypedNode &Value) {
Items.reserve(N);
for (; N > 0; --N)
push_back(Value);
}
bool contains(const DynTypedNode &Value) {
return Seen.contains(Value);
}
void push_back(const DynTypedNode &Value) {
if (!Value.getMemoizationData() || Seen.insert(Value).second)
Items.push_back(Value);
}
llvm::ArrayRef<DynTypedNode> view() const { return Items; }
private:
llvm::SmallVector<DynTypedNode, 2> Items;
llvm::SmallDenseSet<DynTypedNode, 2> Seen;
};
/// Maps from a node to its parents. This is used for nodes that have /// Maps from a node to its parents. This is used for nodes that have
/// pointer identity only, which are more common and we can save space by /// pointer identity only, which are more common and we can save space by
@ -99,7 +118,7 @@ class ParentMapContext::ParentMap {
return llvm::ArrayRef<DynTypedNode>(); return llvm::ArrayRef<DynTypedNode>();
} }
if (const auto *V = I->second.template dyn_cast<ParentVector *>()) { if (const auto *V = I->second.template dyn_cast<ParentVector *>()) {
return llvm::ArrayRef(*V); return V->view();
} }
return getSingleDynTypedNodeFromParentMap(I->second); return getSingleDynTypedNodeFromParentMap(I->second);
} }
@ -252,7 +271,7 @@ public:
const auto *S = It->second.dyn_cast<const Stmt *>(); const auto *S = It->second.dyn_cast<const Stmt *>();
if (!S) { if (!S) {
if (auto *Vec = It->second.dyn_cast<ParentVector *>()) if (auto *Vec = It->second.dyn_cast<ParentVector *>())
return llvm::ArrayRef(*Vec); return Vec->view();
return getSingleDynTypedNodeFromParentMap(It->second); return getSingleDynTypedNodeFromParentMap(It->second);
} }
const auto *P = dyn_cast<Expr>(S); const auto *P = dyn_cast<Expr>(S);