Skip to content
This repository was archived by the owner on Oct 24, 2019. It is now read-only.

Commit a382bb1

Browse files
committed
Merge remote-tracking branch 'origin/swift-5.1-branch' into stable
2 parents f6ae6ad + a98dc4a commit a382bb1

File tree

9 files changed

+174
-34
lines changed

9 files changed

+174
-34
lines changed

include/clang/Analysis/CFG.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,12 @@ class CFG {
11731173
/// implementation needs such an interface.
11741174
unsigned size() const { return NumBlockIDs; }
11751175

1176+
/// Returns true if the CFG has no branches. Usually it boils down to the CFG
1177+
/// having exactly three blocks (entry, the actual code, exit), but sometimes
1178+
/// more blocks appear due to having control flow that can be fully
1179+
/// resolved in compile time.
1180+
bool isLinear() const;
1181+
11761182
//===--------------------------------------------------------------------===//
11771183
// CFG Debugging: Pretty-Printing and Visualization.
11781184
//===--------------------------------------------------------------------===//

include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,25 @@ class ExprEngine : public SubEngine {
714714
AnalyzerOptions &Opts,
715715
const EvalCallOptions &CallOpts);
716716

717+
/// See if the given AnalysisDeclContext is built for a function that we
718+
/// should always inline simply because it's small enough.
719+
/// Apart from "small" functions, we also have "large" functions
720+
/// (cf. isLarge()), some of which are huge (cf. isHuge()), and we classify
721+
/// the remaining functions as "medium".
722+
bool isSmall(AnalysisDeclContext *ADC) const;
723+
724+
/// See if the given AnalysisDeclContext is built for a function that we
725+
/// should inline carefully because it looks pretty large.
726+
bool isLarge(AnalysisDeclContext *ADC) const;
727+
728+
/// See if the given AnalysisDeclContext is built for a function that we
729+
/// should never inline because it's legit gigantic.
730+
bool isHuge(AnalysisDeclContext *ADC) const;
731+
732+
/// See if the given AnalysisDeclContext is built for a function that we
733+
/// should inline, just by looking at the declaration of the function.
734+
bool mayInlineDecl(AnalysisDeclContext *ADC) const;
735+
717736
/// Checks our policies and decides weither the given call should be inlined.
718737
bool shouldInlineCall(const CallEvent &Call, const Decl *D,
719738
const ExplodedNode *Pred,

lib/Analysis/CFG.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4678,6 +4678,51 @@ std::unique_ptr<CFG> CFG::buildCFG(const Decl *D, Stmt *Statement,
46784678
return Builder.buildCFG(D, Statement);
46794679
}
46804680

4681+
bool CFG::isLinear() const {
4682+
// Quick path: if we only have the ENTRY block, the EXIT block, and some code
4683+
// in between, then we have no room for control flow.
4684+
if (size() <= 3)
4685+
return true;
4686+
4687+
// Traverse the CFG until we find a branch.
4688+
// TODO: While this should still be very fast,
4689+
// maybe we should cache the answer.
4690+
llvm::SmallPtrSet<const CFGBlock *, 4> Visited;
4691+
const CFGBlock *B = Entry;
4692+
while (B != Exit) {
4693+
auto IteratorAndFlag = Visited.insert(B);
4694+
if (!IteratorAndFlag.second) {
4695+
// We looped back to a block that we've already visited. Not linear.
4696+
return false;
4697+
}
4698+
4699+
// Iterate over reachable successors.
4700+
const CFGBlock *FirstReachableB = nullptr;
4701+
for (const CFGBlock::AdjacentBlock &AB : B->succs()) {
4702+
if (!AB.isReachable())
4703+
continue;
4704+
4705+
if (FirstReachableB == nullptr) {
4706+
FirstReachableB = &*AB;
4707+
} else {
4708+
// We've encountered a branch. It's not a linear CFG.
4709+
return false;
4710+
}
4711+
}
4712+
4713+
if (!FirstReachableB) {
4714+
// We reached a dead end. EXIT is unreachable. This is linear enough.
4715+
return true;
4716+
}
4717+
4718+
// There's only one way to move forward. Proceed.
4719+
B = FirstReachableB;
4720+
}
4721+
4722+
// We reached EXIT and found no branches.
4723+
return true;
4724+
}
4725+
46814726
const CXXDestructorDecl *
46824727
CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
46834728
switch (getKind()) {

lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ bool SmartPtrModeling::isNullAfterMoveMethod(
3939
// TODO: Handle other methods, such as .get() or .release().
4040
// But once we do, we'd need a visitor to explain null dereferences
4141
// that are found via such modeling.
42-
const auto *CD = dyn_cast<CXXConversionDecl>(Call->getDecl());
42+
const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call->getDecl());
4343
return CD && CD->getConversionType()->isBooleanType();
4444
}
4545

lib/StaticAnalyzer/Core/BugReporterVisitors.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -538,15 +538,14 @@ class NoStoreFuncVisitor final : public BugReporterVisitor {
538538
// initialize its out-parameter, and additionally check that such
539539
// precondition can actually be fulfilled on the current path.
540540
if (Call.isInSystemHeader()) {
541-
// We make an exception for system header functions that have no branches,
542-
// i.e. have exactly 3 CFG blocks: begin, all its code, end. Such
543-
// functions unconditionally fail to initialize the variable.
541+
// We make an exception for system header functions that have no branches.
542+
// Such functions unconditionally fail to initialize the variable.
544543
// If they call other functions that have more paths within them,
545544
// this suppression would still apply when we visit these inner functions.
546545
// One common example of a standard function that doesn't ever initialize
547546
// its out parameter is operator placement new; it's up to the follow-up
548547
// constructor (if any) to initialize the memory.
549-
if (N->getStackFrame()->getCFG()->size() > 3)
548+
if (!N->getStackFrame()->getCFG()->isLinear())
550549
R.markInvalid(getTag(), nullptr);
551550
return nullptr;
552551
}

lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,26 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
365365
}
366366
}
367367

368+
bool ExprEngine::isSmall(AnalysisDeclContext *ADC) const {
369+
// When there are no branches in the function, it means that there's no
370+
// exponential complexity introduced by inlining such function.
371+
// Such functions also don't trigger various fundamental problems
372+
// with our inlining mechanism, such as the problem of
373+
// inlined defensive checks. Hence isLinear().
374+
const CFG *Cfg = ADC->getCFG();
375+
return Cfg->isLinear() || Cfg->size() <= AMgr.options.AlwaysInlineSize;
376+
}
377+
378+
bool ExprEngine::isLarge(AnalysisDeclContext *ADC) const {
379+
const CFG *Cfg = ADC->getCFG();
380+
return Cfg->size() >= AMgr.options.MinCFGSizeTreatFunctionsAsLarge;
381+
}
382+
383+
bool ExprEngine::isHuge(AnalysisDeclContext *ADC) const {
384+
const CFG *Cfg = ADC->getCFG();
385+
return Cfg->getNumBlockIDs() > AMgr.options.MaxInlinableSize;
386+
}
387+
368388
void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx,
369389
bool &IsRecursive, unsigned &StackDepth) {
370390
IsRecursive = false;
@@ -385,8 +405,7 @@ void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx,
385405

386406
// Do not count the small functions when determining the stack depth.
387407
AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(DI);
388-
const CFG *CalleeCFG = CalleeADC->getCFG();
389-
if (CalleeCFG->getNumBlockIDs() > AMgr.options.AlwaysInlineSize)
408+
if (!isSmall(CalleeADC))
390409
++StackDepth;
391410
}
392411
LCtx = LCtx->getParent();
@@ -833,8 +852,7 @@ static bool isCXXSharedPtrDtor(const FunctionDecl *FD) {
833852
/// This checks static properties of the function, such as its signature and
834853
/// CFG, to determine whether the analyzer should ever consider inlining it,
835854
/// in any context.
836-
static bool mayInlineDecl(AnalysisManager &AMgr,
837-
AnalysisDeclContext *CalleeADC) {
855+
bool ExprEngine::mayInlineDecl(AnalysisDeclContext *CalleeADC) const {
838856
AnalyzerOptions &Opts = AMgr.getAnalyzerOptions();
839857
// FIXME: Do not inline variadic calls.
840858
if (CallEvent::isVariadic(CalleeADC->getDecl()))
@@ -879,7 +897,7 @@ static bool mayInlineDecl(AnalysisManager &AMgr,
879897
return false;
880898

881899
// Do not inline large functions.
882-
if (CalleeCFG->getNumBlockIDs() > Opts.MaxInlinableSize)
900+
if (isHuge(CalleeADC))
883901
return false;
884902

885903
// It is possible that the live variables analysis cannot be
@@ -919,7 +937,7 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
919937
} else {
920938
// We haven't actually checked the static properties of this function yet.
921939
// Do that now, and record our decision in the function summaries.
922-
if (mayInlineDecl(getAnalysisManager(), CalleeADC)) {
940+
if (mayInlineDecl(CalleeADC)) {
923941
Engine.FunctionSummaries->markMayInline(D);
924942
} else {
925943
Engine.FunctionSummaries->markShouldNotInline(D);
@@ -940,29 +958,23 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
940958
return false;
941959
}
942960

943-
const CFG *CalleeCFG = CalleeADC->getCFG();
944-
945961
// Do not inline if recursive or we've reached max stack frame count.
946962
bool IsRecursive = false;
947963
unsigned StackDepth = 0;
948964
examineStackFrames(D, Pred->getLocationContext(), IsRecursive, StackDepth);
949965
if ((StackDepth >= Opts.InlineMaxStackDepth) &&
950-
((CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize)
951-
|| IsRecursive))
966+
(!isSmall(CalleeADC) || IsRecursive))
952967
return false;
953968

954969
// Do not inline large functions too many times.
955970
if ((Engine.FunctionSummaries->getNumTimesInlined(D) >
956971
Opts.MaxTimesInlineLarge) &&
957-
CalleeCFG->getNumBlockIDs() >=
958-
Opts.MinCFGSizeTreatFunctionsAsLarge) {
972+
isLarge(CalleeADC)) {
959973
NumReachedInlineCountMax++;
960974
return false;
961975
}
962976

963-
if (HowToInline == Inline_Minimal &&
964-
(CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize
965-
|| IsRecursive))
977+
if (HowToInline == Inline_Minimal && (!isSmall(CalleeADC) || IsRecursive))
966978
return false;
967979

968980
return true;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
2+
// RUN: -analyzer-inline-max-stack-depth=5 -w -std=c++17 -verify %s
3+
4+
void clang_analyzer_eval(bool);
5+
6+
namespace inline_large_functions_with_if_constexpr {
7+
bool f0() { if constexpr (true); return true; }
8+
bool f1() { if constexpr (true); return f0(); }
9+
bool f2() { if constexpr (true); return f1(); }
10+
bool f3() { if constexpr (true); return f2(); }
11+
bool f4() { if constexpr (true); return f3(); }
12+
bool f5() { if constexpr (true); return f4(); }
13+
bool f6() { if constexpr (true); return f5(); }
14+
bool f7() { if constexpr (true); return f6(); }
15+
void bar() {
16+
clang_analyzer_eval(f7()); // expected-warning{{TRUE}}
17+
}
18+
} // namespace inline_large_functions_with_if_constexpr

test/Analysis/smart-ptr.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,13 @@ void derefAfterMove(std::unique_ptr<int> P) {
1616
// TODO: Report a null dereference (instead).
1717
*P.get() = 1; // expected-warning {{Method called on moved-from object 'P'}}
1818
}
19+
20+
// Don't crash when attempting to model a call with unknown callee.
21+
namespace testUnknownCallee {
22+
struct S {
23+
void foo();
24+
};
25+
void bar(S *s, void (S::*func)(void)) {
26+
(s->*func)(); // no-crash
27+
}
28+
} // namespace testUnknownCallee

unittests/Analysis/CFGTest.cpp

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,41 @@ namespace clang {
1818
namespace analysis {
1919
namespace {
2020

21-
enum BuildResult {
22-
ToolFailed,
23-
ToolRan,
24-
SawFunctionBody,
25-
BuiltCFG,
21+
class BuildResult {
22+
public:
23+
enum Status {
24+
ToolFailed,
25+
ToolRan,
26+
SawFunctionBody,
27+
BuiltCFG,
28+
};
29+
30+
BuildResult(Status S, std::unique_ptr<CFG> Cfg = nullptr)
31+
: S(S), Cfg(std::move(Cfg)) {}
32+
33+
Status getStatus() const { return S; }
34+
CFG *getCFG() const { return Cfg.get(); }
35+
36+
private:
37+
Status S;
38+
std::unique_ptr<CFG> Cfg;
2639
};
2740

2841
class CFGCallback : public ast_matchers::MatchFinder::MatchCallback {
2942
public:
30-
BuildResult TheBuildResult = ToolRan;
43+
BuildResult TheBuildResult = BuildResult::ToolRan;
3144

3245
void run(const ast_matchers::MatchFinder::MatchResult &Result) override {
3346
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
3447
Stmt *Body = Func->getBody();
3548
if (!Body)
3649
return;
37-
TheBuildResult = SawFunctionBody;
50+
TheBuildResult = BuildResult::SawFunctionBody;
3851
CFG::BuildOptions Options;
3952
Options.AddImplicitDtors = true;
40-
if (CFG::buildCFG(nullptr, Body, Result.Context, Options))
41-
TheBuildResult = BuiltCFG;
53+
if (std::unique_ptr<CFG> Cfg =
54+
CFG::buildCFG(nullptr, Body, Result.Context, Options))
55+
TheBuildResult = {BuildResult::BuiltCFG, std::move(Cfg)};
4256
}
4357
};
4458

@@ -51,8 +65,8 @@ BuildResult BuildCFG(const char *Code) {
5165
tooling::newFrontendActionFactory(&Finder));
5266
std::vector<std::string> Args = {"-std=c++11", "-fno-delayed-template-parsing"};
5367
if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args))
54-
return ToolFailed;
55-
return Callback.TheBuildResult;
68+
return BuildResult::ToolFailed;
69+
return std::move(Callback.TheBuildResult);
5670
}
5771

5872
// Constructing a CFG for a range-based for over a dependent type fails (but
@@ -64,7 +78,7 @@ TEST(CFG, RangeBasedForOverDependentType) {
6478
" for (const Foo *TheFoo : Range) {\n"
6579
" }\n"
6680
"}\n";
67-
EXPECT_EQ(SawFunctionBody, BuildCFG(Code));
81+
EXPECT_EQ(BuildResult::SawFunctionBody, BuildCFG(Code).getStatus());
6882
}
6983

7084
// Constructing a CFG containing a delete expression on a dependent type should
@@ -74,7 +88,7 @@ TEST(CFG, DeleteExpressionOnDependentType) {
7488
"void f(T t) {\n"
7589
" delete t;\n"
7690
"}\n";
77-
EXPECT_EQ(BuiltCFG, BuildCFG(Code));
91+
EXPECT_EQ(BuildResult::BuiltCFG, BuildCFG(Code).getStatus());
7892
}
7993

8094
// Constructing a CFG on a function template with a variable of incomplete type
@@ -84,7 +98,24 @@ TEST(CFG, VariableOfIncompleteType) {
8498
" class Undefined;\n"
8599
" Undefined u;\n"
86100
"}\n";
87-
EXPECT_EQ(BuiltCFG, BuildCFG(Code));
101+
EXPECT_EQ(BuildResult::BuiltCFG, BuildCFG(Code).getStatus());
102+
}
103+
104+
TEST(CFG, IsLinear) {
105+
auto expectLinear = [](bool IsLinear, const char *Code) {
106+
BuildResult B = BuildCFG(Code);
107+
EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus());
108+
EXPECT_EQ(IsLinear, B.getCFG()->isLinear());
109+
};
110+
111+
expectLinear(true, "void foo() {}");
112+
expectLinear(true, "void foo() { if (true) return; }");
113+
expectLinear(true, "void foo() { if constexpr (false); }");
114+
expectLinear(false, "void foo(bool coin) { if (coin) return; }");
115+
expectLinear(false, "void foo() { for(;;); }");
116+
expectLinear(false, "void foo() { do {} while (true); }");
117+
expectLinear(true, "void foo() { do {} while (false); }");
118+
expectLinear(true, "void foo() { foo(); }"); // Recursion is not our problem.
88119
}
89120

90121
} // namespace

0 commit comments

Comments
 (0)