forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInefficientVectorOperationCheck.cpp
149 lines (133 loc) · 5.78 KB
/
InefficientVectorOperationCheck.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//===--- InefficientVectorOperationCheck.cpp - clang-tidy------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "InefficientVectorOperationCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
#include "../utils/DeclRefExprUtils.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace performance {
namespace {
// Matcher names. Given the code:
//
// \code
// void f() {
// vector<T> v;
// for (int i = 0; i < 10 + 1; ++i) {
// v.push_back(i);
// }
// }
// \endcode
//
// The matcher names are bound to following parts of the AST:
// - LoopName: The entire for loop (as ForStmt).
// - LoopParentName: The body of function f (as CompoundStmt).
// - VectorVarDeclName: 'v' in (as VarDecl).
// - VectorVarDeclStmatName: The entire 'std::vector<T> v;' statement (as
// DeclStmt).
// - PushBackCallName: 'v.push_back(i)' (as cxxMemberCallExpr).
// - LoopInitVarName: 'i' (as VarDecl).
// - LoopEndExpr: '10+1' (as Expr).
static const char LoopCounterName[] = "for_loop_counter";
static const char LoopParentName[] = "loop_parent";
static const char VectorVarDeclName[] = "vector_var_decl";
static const char VectorVarDeclStmtName[] = "vector_var_decl_stmt";
static const char PushBackCallName[] = "push_back_call";
static const char LoopInitVarName[] = "loop_init_var";
static const char LoopEndExprName[] = "loop_end_expr";
} // namespace
void InefficientVectorOperationCheck::registerMatchers(MatchFinder *Finder) {
const auto VectorDecl = cxxRecordDecl(hasName("::std::vector"));
const auto VectorDefaultConstructorCall = cxxConstructExpr(
hasType(VectorDecl),
hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
const auto VectorVarDecl =
varDecl(hasInitializer(VectorDefaultConstructorCall))
.bind(VectorVarDeclName);
const auto PushBackCallExpr =
cxxMemberCallExpr(
callee(cxxMethodDecl(hasName("push_back"))), on(hasType(VectorDecl)),
onImplicitObjectArgument(declRefExpr(to(VectorVarDecl))))
.bind(PushBackCallName);
const auto PushBackCall =
expr(anyOf(PushBackCallExpr, exprWithCleanups(has(PushBackCallExpr))));
const auto VectorVarDefStmt =
declStmt(hasSingleDecl(equalsBoundNode(VectorVarDeclName)))
.bind(VectorVarDeclStmtName);
const auto LoopVarInit =
declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0))))
.bind(LoopInitVarName)));
const auto RefersToLoopVar = ignoringParenImpCasts(
declRefExpr(to(varDecl(equalsBoundNode(LoopInitVarName)))));
// Match counter-based for loops:
// for (int i = 0; i < n; ++i) { v.push_back(...); }
//
// FIXME: Support more types of counter-based loops like decrement loops.
Finder->addMatcher(
forStmt(
hasLoopInit(LoopVarInit),
hasCondition(binaryOperator(
hasOperatorName("<"), hasLHS(RefersToLoopVar),
hasRHS(expr(unless(hasDescendant(expr(RefersToLoopVar))))
.bind(LoopEndExprName)))),
hasIncrement(unaryOperator(hasOperatorName("++"),
hasUnaryOperand(RefersToLoopVar))),
hasBody(anyOf(compoundStmt(statementCountIs(1), has(PushBackCall)),
PushBackCall)),
hasParent(compoundStmt(has(VectorVarDefStmt)).bind(LoopParentName)))
.bind(LoopCounterName),
this);
}
void InefficientVectorOperationCheck::check(
const MatchFinder::MatchResult &Result) {
if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
return;
const SourceManager &SM = *Result.SourceManager;
const auto *ForLoop = Result.Nodes.getNodeAs<ForStmt>(LoopCounterName);
const auto *PushBackCall =
Result.Nodes.getNodeAs<CXXMemberCallExpr>(PushBackCallName);
const auto *LoopEndExpr = Result.Nodes.getNodeAs<Expr>(LoopEndExprName);
const auto *LoopParent = Result.Nodes.getNodeAs<CompoundStmt>(LoopParentName);
const auto *VectorVarDecl =
Result.Nodes.getNodeAs<VarDecl>(VectorVarDeclName);
llvm::SmallPtrSet<const DeclRefExpr *, 16> AllVectorVarRefs =
utils::decl_ref_expr::allDeclRefExprs(*VectorVarDecl, *LoopParent,
*Result.Context);
for (const auto *Ref : AllVectorVarRefs) {
// Skip cases where there are usages (defined as DeclRefExpr that refers to
// "v") of vector variable `v` before the for loop. We consider these usages
// are operations causing memory preallocation (e.g. "v.resize(n)",
// "v.reserve(n)").
//
// FIXME: make it more intelligent to identify the pre-allocating operations
// before the for loop.
if (SM.isBeforeInTranslationUnit(Ref->getLocation(),
ForLoop->getLocStart())) {
return;
}
}
llvm::StringRef LoopEndSource = clang::Lexer::getSourceText(
CharSourceRange::getTokenRange(LoopEndExpr->getSourceRange()), SM,
clang::LangOptions());
llvm::StringRef VectorVarName = clang::Lexer::getSourceText(
CharSourceRange::getTokenRange(
PushBackCall->getImplicitObjectArgument()->getSourceRange()),
SM, clang::LangOptions());
std::string ReserveStmt =
(VectorVarName + ".reserve(" + LoopEndSource + ");\n").str();
diag(PushBackCall->getLocStart(),
"'push_back' is called inside a loop; "
"consider pre-allocating the vector capacity before the loop.")
<< FixItHint::CreateInsertion(ForLoop->getLocStart(), ReserveStmt);
}
} // namespace performance
} // namespace tidy
} // namespace clang