forked from OSchip/llvm-project
Filter the toplevel matchers by kind.
Summary: Filter the toplevel matchers by kind. Decl and Stmt matchers are tied to a specific node kind and trying to match incompatible nodes is a waste. Precalculate a filtered list of matchers that have a chance of matching the node and ignore the rest. Speeds up our clang-tidy benchmark by ~10% Reviewers: klimek Subscribers: klimek, cfe-commits Differential Revision: http://reviews.llvm.org/D6361 llvm-svn: 222688
This commit is contained in:
parent
6953a3a6e0
commit
074bbb698d
|
@ -23,6 +23,7 @@
|
||||||
#include "clang/AST/TemplateBase.h"
|
#include "clang/AST/TemplateBase.h"
|
||||||
#include "clang/AST/TypeLoc.h"
|
#include "clang/AST/TypeLoc.h"
|
||||||
#include "clang/Basic/LLVM.h"
|
#include "clang/Basic/LLVM.h"
|
||||||
|
#include "llvm/ADT/DenseMapInfo.h"
|
||||||
#include "llvm/Support/AlignOf.h"
|
#include "llvm/Support/AlignOf.h"
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
@ -90,6 +91,21 @@ public:
|
||||||
static ASTNodeKind getMostDerivedCommonAncestor(ASTNodeKind Kind1,
|
static ASTNodeKind getMostDerivedCommonAncestor(ASTNodeKind Kind1,
|
||||||
ASTNodeKind Kind2);
|
ASTNodeKind Kind2);
|
||||||
|
|
||||||
|
/// \brief Hooks for using ASTNodeKind as a key in a DenseMap.
|
||||||
|
struct DenseMapInfo {
|
||||||
|
// ASTNodeKind() is a good empty key because it is represented as a 0.
|
||||||
|
static inline ASTNodeKind getEmptyKey() { return ASTNodeKind(); }
|
||||||
|
// NKI_NumberOfKinds is not a valid value, so it is good for a
|
||||||
|
// tombstone key.
|
||||||
|
static inline ASTNodeKind getTombstoneKey() {
|
||||||
|
return ASTNodeKind(NKI_NumberOfKinds);
|
||||||
|
}
|
||||||
|
static unsigned getHashValue(const ASTNodeKind &Val) { return Val.KindId; }
|
||||||
|
static bool isEqual(const ASTNodeKind &LHS, const ASTNodeKind &RHS) {
|
||||||
|
return LHS.KindId == RHS.KindId;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// \brief Kind ids.
|
/// \brief Kind ids.
|
||||||
///
|
///
|
||||||
|
@ -375,4 +391,12 @@ template <typename T, typename EnablerT> struct DynTypedNode::BaseConverter {
|
||||||
} // end namespace ast_type_traits
|
} // end namespace ast_type_traits
|
||||||
} // end namespace clang
|
} // end namespace clang
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct DenseMapInfo<clang::ast_type_traits::ASTNodeKind>
|
||||||
|
: clang::ast_type_traits::ASTNodeKind::DenseMapInfo {};
|
||||||
|
|
||||||
|
} // end namespace llvm
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -199,9 +199,9 @@ public:
|
||||||
/// \brief For each \c Matcher<> a \c MatchCallback that will be called
|
/// \brief For each \c Matcher<> a \c MatchCallback that will be called
|
||||||
/// when it matches.
|
/// when it matches.
|
||||||
struct MatchersByType {
|
struct MatchersByType {
|
||||||
std::vector<std::pair<DeclarationMatcher, MatchCallback *>> Decl;
|
std::vector<std::pair<internal::DynTypedMatcher, MatchCallback *>>
|
||||||
|
DeclOrStmt;
|
||||||
std::vector<std::pair<TypeMatcher, MatchCallback *>> Type;
|
std::vector<std::pair<TypeMatcher, MatchCallback *>> Type;
|
||||||
std::vector<std::pair<StatementMatcher, MatchCallback *>> Stmt;
|
|
||||||
std::vector<std::pair<NestedNameSpecifierMatcher, MatchCallback *>>
|
std::vector<std::pair<NestedNameSpecifierMatcher, MatchCallback *>>
|
||||||
NestedNameSpecifier;
|
NestedNameSpecifier;
|
||||||
std::vector<std::pair<NestedNameSpecifierLocMatcher, MatchCallback *>>
|
std::vector<std::pair<NestedNameSpecifierLocMatcher, MatchCallback *>>
|
||||||
|
|
|
@ -289,6 +289,11 @@ public:
|
||||||
|
|
||||||
void setAllowBind(bool AB) { AllowBind = AB; }
|
void setAllowBind(bool AB) { AllowBind = AB; }
|
||||||
|
|
||||||
|
/// \brief Check whether this matcher could ever match a node of kind \p Kind.
|
||||||
|
/// \return \c false if this matcher will never match such a node. Otherwise,
|
||||||
|
/// return \c true.
|
||||||
|
bool canMatchNodesOfKind(ast_type_traits::ASTNodeKind Kind) const;
|
||||||
|
|
||||||
/// \brief Return a matcher that points to the same implementation, but
|
/// \brief Return a matcher that points to the same implementation, but
|
||||||
/// restricts the node types for \p Kind.
|
/// restricts the node types for \p Kind.
|
||||||
DynTypedMatcher dynCastTo(const ast_type_traits::ASTNodeKind Kind) const;
|
DynTypedMatcher dynCastTo(const ast_type_traits::ASTNodeKind Kind) const;
|
||||||
|
@ -297,6 +302,14 @@ public:
|
||||||
bool matches(const ast_type_traits::DynTypedNode &DynNode,
|
bool matches(const ast_type_traits::DynTypedNode &DynNode,
|
||||||
ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const;
|
ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const;
|
||||||
|
|
||||||
|
/// \brief Same as matches(), but skips the kind check.
|
||||||
|
///
|
||||||
|
/// It is faster, but the caller must ensure the node is valid for the
|
||||||
|
/// kind of this matcher.
|
||||||
|
bool matchesNoKindCheck(const ast_type_traits::DynTypedNode &DynNode,
|
||||||
|
ASTMatchFinder *Finder,
|
||||||
|
BoundNodesTreeBuilder *Builder) const;
|
||||||
|
|
||||||
/// \brief Bind the specified \p ID to the matcher.
|
/// \brief Bind the specified \p ID to the matcher.
|
||||||
/// \return A new matcher with the \p ID bound to it if this matcher supports
|
/// \return A new matcher with the \p ID bound to it if this matcher supports
|
||||||
/// binding. Otherwise, returns an empty \c Optional<>.
|
/// binding. Otherwise, returns an empty \c Optional<>.
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "clang/AST/ASTConsumer.h"
|
#include "clang/AST/ASTConsumer.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
#include "clang/AST/RecursiveASTVisitor.h"
|
#include "clang/AST/RecursiveASTVisitor.h"
|
||||||
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/StringMap.h"
|
#include "llvm/ADT/StringMap.h"
|
||||||
#include "llvm/Support/Timer.h"
|
#include "llvm/Support/Timer.h"
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
@ -523,7 +524,7 @@ private:
|
||||||
///
|
///
|
||||||
/// Used by \c matchDispatch() below.
|
/// Used by \c matchDispatch() below.
|
||||||
template <typename T, typename MC>
|
template <typename T, typename MC>
|
||||||
void matchImpl(const T &Node, const MC &Matchers) {
|
void matchWithoutFilter(const T &Node, const MC &Matchers) {
|
||||||
const bool EnableCheckProfiling = Options.CheckProfiling.hasValue();
|
const bool EnableCheckProfiling = Options.CheckProfiling.hasValue();
|
||||||
TimeBucketRegion Timer;
|
TimeBucketRegion Timer;
|
||||||
for (const auto &MP : Matchers) {
|
for (const auto &MP : Matchers) {
|
||||||
|
@ -537,22 +538,66 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void matchWithFilter(const ast_type_traits::DynTypedNode &DynNode) {
|
||||||
|
auto Kind = DynNode.getNodeKind();
|
||||||
|
auto it = MatcherFiltersMap.find(Kind);
|
||||||
|
const auto &Filter =
|
||||||
|
it != MatcherFiltersMap.end() ? it->second : getFilterForKind(Kind);
|
||||||
|
|
||||||
|
if (Filter.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool EnableCheckProfiling = Options.CheckProfiling.hasValue();
|
||||||
|
TimeBucketRegion Timer;
|
||||||
|
auto &Matchers = this->Matchers->DeclOrStmt;
|
||||||
|
for (unsigned short I : Filter) {
|
||||||
|
auto &MP = Matchers[I];
|
||||||
|
if (EnableCheckProfiling)
|
||||||
|
Timer.setBucket(&TimeByBucket[MP.second->getID()]);
|
||||||
|
BoundNodesTreeBuilder Builder;
|
||||||
|
if (MP.first.matchesNoKindCheck(DynNode, this, &Builder)) {
|
||||||
|
MatchVisitor Visitor(ActiveASTContext, MP.second);
|
||||||
|
Builder.visitMatches(&Visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<unsigned short> &
|
||||||
|
getFilterForKind(ast_type_traits::ASTNodeKind Kind) {
|
||||||
|
auto &Filter = MatcherFiltersMap[Kind];
|
||||||
|
auto &Matchers = this->Matchers->DeclOrStmt;
|
||||||
|
assert((Matchers.size() < USHRT_MAX) && "Too many matchers.");
|
||||||
|
for (unsigned I = 0, E = Matchers.size(); I != E; ++I) {
|
||||||
|
if (Matchers[I].first.canMatchNodesOfKind(Kind)) {
|
||||||
|
Filter.push_back(I);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Filter;
|
||||||
|
}
|
||||||
|
|
||||||
/// @{
|
/// @{
|
||||||
/// \brief Overloads to pair the different node types to their matchers.
|
/// \brief Overloads to pair the different node types to their matchers.
|
||||||
void matchDispatch(const Decl *Node) { matchImpl(*Node, Matchers->Decl); }
|
void matchDispatch(const Decl *Node) {
|
||||||
void matchDispatch(const Stmt *Node) { matchImpl(*Node, Matchers->Stmt); }
|
return matchWithFilter(ast_type_traits::DynTypedNode::create(*Node));
|
||||||
|
}
|
||||||
|
void matchDispatch(const Stmt *Node) {
|
||||||
|
return matchWithFilter(ast_type_traits::DynTypedNode::create(*Node));
|
||||||
|
}
|
||||||
|
|
||||||
void matchDispatch(const Type *Node) {
|
void matchDispatch(const Type *Node) {
|
||||||
matchImpl(QualType(Node, 0), Matchers->Type);
|
matchWithoutFilter(QualType(Node, 0), Matchers->Type);
|
||||||
}
|
}
|
||||||
void matchDispatch(const TypeLoc *Node) {
|
void matchDispatch(const TypeLoc *Node) {
|
||||||
matchImpl(*Node, Matchers->TypeLoc);
|
matchWithoutFilter(*Node, Matchers->TypeLoc);
|
||||||
|
}
|
||||||
|
void matchDispatch(const QualType *Node) {
|
||||||
|
matchWithoutFilter(*Node, Matchers->Type);
|
||||||
}
|
}
|
||||||
void matchDispatch(const QualType *Node) { matchImpl(*Node, Matchers->Type); }
|
|
||||||
void matchDispatch(const NestedNameSpecifier *Node) {
|
void matchDispatch(const NestedNameSpecifier *Node) {
|
||||||
matchImpl(*Node, Matchers->NestedNameSpecifier);
|
matchWithoutFilter(*Node, Matchers->NestedNameSpecifier);
|
||||||
}
|
}
|
||||||
void matchDispatch(const NestedNameSpecifierLoc *Node) {
|
void matchDispatch(const NestedNameSpecifierLoc *Node) {
|
||||||
matchImpl(*Node, Matchers->NestedNameSpecifierLoc);
|
matchWithoutFilter(*Node, Matchers->NestedNameSpecifierLoc);
|
||||||
}
|
}
|
||||||
void matchDispatch(const void *) { /* Do nothing. */ }
|
void matchDispatch(const void *) { /* Do nothing. */ }
|
||||||
/// @}
|
/// @}
|
||||||
|
@ -685,6 +730,18 @@ private:
|
||||||
llvm::StringMap<llvm::TimeRecord> TimeByBucket;
|
llvm::StringMap<llvm::TimeRecord> TimeByBucket;
|
||||||
|
|
||||||
const MatchFinder::MatchersByType *Matchers;
|
const MatchFinder::MatchersByType *Matchers;
|
||||||
|
|
||||||
|
/// \brief Filtered list of matcher indices for each matcher kind.
|
||||||
|
///
|
||||||
|
/// \c Decl and \c Stmt toplevel matchers usually apply to a specific node
|
||||||
|
/// kind (and derived kinds) so it is a waste to try every matcher on every
|
||||||
|
/// node.
|
||||||
|
/// We precalculate a list of matchers that pass the toplevel restrict check.
|
||||||
|
/// This also allows us to skip the restrict check at matching time. See
|
||||||
|
/// use \c matchesNoKindCheck() above.
|
||||||
|
llvm::DenseMap<ast_type_traits::ASTNodeKind, std::vector<unsigned short>>
|
||||||
|
MatcherFiltersMap;
|
||||||
|
|
||||||
const MatchFinder::MatchFinderOptions &Options;
|
const MatchFinder::MatchFinderOptions &Options;
|
||||||
ASTContext *ActiveASTContext;
|
ASTContext *ActiveASTContext;
|
||||||
|
|
||||||
|
@ -855,7 +912,7 @@ MatchFinder::~MatchFinder() {}
|
||||||
|
|
||||||
void MatchFinder::addMatcher(const DeclarationMatcher &NodeMatch,
|
void MatchFinder::addMatcher(const DeclarationMatcher &NodeMatch,
|
||||||
MatchCallback *Action) {
|
MatchCallback *Action) {
|
||||||
Matchers.Decl.push_back(std::make_pair(NodeMatch, Action));
|
Matchers.DeclOrStmt.push_back(std::make_pair(NodeMatch, Action));
|
||||||
Matchers.AllCallbacks.push_back(Action);
|
Matchers.AllCallbacks.push_back(Action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -867,7 +924,7 @@ void MatchFinder::addMatcher(const TypeMatcher &NodeMatch,
|
||||||
|
|
||||||
void MatchFinder::addMatcher(const StatementMatcher &NodeMatch,
|
void MatchFinder::addMatcher(const StatementMatcher &NodeMatch,
|
||||||
MatchCallback *Action) {
|
MatchCallback *Action) {
|
||||||
Matchers.Stmt.push_back(std::make_pair(NodeMatch, Action));
|
Matchers.DeclOrStmt.push_back(std::make_pair(NodeMatch, Action));
|
||||||
Matchers.AllCallbacks.push_back(Action);
|
Matchers.AllCallbacks.push_back(Action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,11 @@ DynTypedMatcher DynTypedMatcher::trueMatcher(
|
||||||
return DynTypedMatcher(NodeKind, NodeKind, &*TrueMatcherInstance);
|
return DynTypedMatcher(NodeKind, NodeKind, &*TrueMatcherInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DynTypedMatcher::canMatchNodesOfKind(
|
||||||
|
ast_type_traits::ASTNodeKind Kind) const {
|
||||||
|
return RestrictKind.isBaseOf(Kind);
|
||||||
|
}
|
||||||
|
|
||||||
DynTypedMatcher DynTypedMatcher::dynCastTo(
|
DynTypedMatcher DynTypedMatcher::dynCastTo(
|
||||||
const ast_type_traits::ASTNodeKind Kind) const {
|
const ast_type_traits::ASTNodeKind Kind) const {
|
||||||
auto Copy = *this;
|
auto Copy = *this;
|
||||||
|
@ -172,6 +177,20 @@ bool DynTypedMatcher::matches(const ast_type_traits::DynTypedNode &DynNode,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DynTypedMatcher::matchesNoKindCheck(
|
||||||
|
const ast_type_traits::DynTypedNode &DynNode, ASTMatchFinder *Finder,
|
||||||
|
BoundNodesTreeBuilder *Builder) const {
|
||||||
|
assert(RestrictKind.isBaseOf(DynNode.getNodeKind()));
|
||||||
|
if (Implementation->dynMatches(DynNode, Finder, Builder)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Delete all bindings when a matcher does not match.
|
||||||
|
// This prevents unexpected exposure of bound nodes in unmatches
|
||||||
|
// branches of the match tree.
|
||||||
|
Builder->removeBindings([](const BoundNodesMap &) { return true; });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
llvm::Optional<DynTypedMatcher> DynTypedMatcher::tryBind(StringRef ID) const {
|
llvm::Optional<DynTypedMatcher> DynTypedMatcher::tryBind(StringRef ID) const {
|
||||||
if (!AllowBind) return llvm::None;
|
if (!AllowBind) return llvm::None;
|
||||||
auto Result = *this;
|
auto Result = *this;
|
||||||
|
|
Loading…
Reference in New Issue