From 758fbacea5b93307d7456dc4e2c6d3379b8aa781 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Tue, 13 Feb 2018 21:31:47 +0000 Subject: [PATCH] Teach Wreturn-type, Wunreachable-code, and alpha.deadcode.UnreachableCode to treat __assume(0) like __builtin_unreachable. Fixes PR29134. https://reviews.llvm.org/D43221 llvm-svn: 325052 --- clang/include/clang/AST/Expr.h | 4 ++++ clang/lib/AST/Expr.cpp | 12 +++++++++++ clang/lib/Analysis/CFG.cpp | 2 +- clang/lib/Analysis/ReachableCode.cpp | 25 ++++++++++++++++++---- .../Checkers/UnreachableCodeChecker.cpp | 3 ++- clang/test/Analysis/unreachable-code-path.c | 1 + clang/test/Sema/return.c | 12 +++++++++++ clang/test/Sema/warn-unreachable.c | 4 ++++ 8 files changed, 57 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 9d7a29e..247850c 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -2357,6 +2357,10 @@ public: SourceLocation getLocStart() const LLVM_READONLY; SourceLocation getLocEnd() const LLVM_READONLY; + /// Return true if this is a call to __assume() or __builtin_assume() with + /// a non-value-dependent constant parameter evaluating as false. + bool isBuiltinAssumeFalse(const ASTContext &Ctx) const; + bool isCallToStdMove() const { const FunctionDecl* FD = getDirectCallee(); return getNumArgs() == 1 && FD && FD->isInStdNamespace() && diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 7ddab93..66f6170 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2917,6 +2917,18 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, return false; } +bool CallExpr::isBuiltinAssumeFalse(const ASTContext &Ctx) const { + const FunctionDecl* FD = getDirectCallee(); + if (!FD || (FD->getBuiltinID() != Builtin::BI__assume && + FD->getBuiltinID() != Builtin::BI__builtin_assume)) + return false; + + const Expr* Arg = getArg(0); + bool ArgVal; + return !Arg->isValueDependent() && + Arg->EvaluateAsBooleanCondition(ArgVal, Ctx) && !ArgVal; +} + namespace { /// \brief Look for any side effects within a Stmt. class SideEffectFinder : public ConstEvaluatedExprVisitor { diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index cec5a65..9fe9ad1 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -2134,7 +2134,7 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { bool OmitArguments = false; if (FunctionDecl *FD = C->getDirectCallee()) { - if (FD->isNoReturn()) + if (FD->isNoReturn() || C->isBuiltinAssumeFalse(*Context)) NoReturn = true; if (FD->hasAttr()) AddEHEdge = false; diff --git a/clang/lib/Analysis/ReachableCode.cpp b/clang/lib/Analysis/ReachableCode.cpp index 7e72795..f644d50 100644 --- a/clang/lib/Analysis/ReachableCode.cpp +++ b/clang/lib/Analysis/ReachableCode.cpp @@ -66,6 +66,21 @@ static bool isBuiltinUnreachable(const Stmt *S) { return false; } +static bool isBuiltinAssumeFalse(const CFGBlock *B, const Stmt *S, + ASTContext &C) { + if (B->empty()) { + // Happens if S is B's terminator and B contains nothing else + // (e.g. a CFGBlock containing only a goto). + return false; + } + if (Optional CS = B->back().getAs()) { + if (const auto *CE = dyn_cast(CS->getStmt())) { + return CE->getCallee()->IgnoreCasts() == S && CE->isBuiltinAssumeFalse(C); + } + } + return false; +} + static bool isDeadReturn(const CFGBlock *B, const Stmt *S) { // Look to see if the current control flow ends with a 'return', and see if // 'S' is a substatement. The 'return' may not be the last element in the @@ -372,6 +387,7 @@ namespace { llvm::BitVector &Reachable; SmallVector WorkList; Preprocessor &PP; + ASTContext &C; typedef SmallVector, 12> DeferredLocsTy; @@ -379,10 +395,10 @@ namespace { DeferredLocsTy DeferredLocs; public: - DeadCodeScan(llvm::BitVector &reachable, Preprocessor &PP) + DeadCodeScan(llvm::BitVector &reachable, Preprocessor &PP, ASTContext &C) : Visited(reachable.size()), Reachable(reachable), - PP(PP) {} + PP(PP), C(C) {} void enqueue(const CFGBlock *block); unsigned scanBackwards(const CFGBlock *Start, @@ -600,7 +616,8 @@ void DeadCodeScan::reportDeadCode(const CFGBlock *B, if (isa(S)) { UK = reachable_code::UK_Break; - } else if (isTrivialDoWhile(B, S) || isBuiltinUnreachable(S)) { + } else if (isTrivialDoWhile(B, S) || isBuiltinUnreachable(S) || + isBuiltinAssumeFalse(B, S, C)) { return; } else if (isDeadReturn(B, S)) { @@ -693,7 +710,7 @@ void FindUnreachableCode(AnalysisDeclContext &AC, Preprocessor &PP, if (reachable[block->getBlockID()]) continue; - DeadCodeScan DS(reachable, PP); + DeadCodeScan DS(reachable, PP, AC.getASTContext()); numReachable += DS.scanBackwards(block, CB); if (numReachable == cfg->getNumBlockIDs()) diff --git a/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index 6f21e86..dbd12cc 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -132,7 +132,8 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, ci != ce; ++ci) { if (Optional S = (*ci).getAs()) if (const CallExpr *CE = dyn_cast(S->getStmt())) { - if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable) { + if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable || + CE->isBuiltinAssumeFalse(Eng.getContext())) { foundUnreachable = true; break; } diff --git a/clang/test/Analysis/unreachable-code-path.c b/clang/test/Analysis/unreachable-code-path.c index effa4d9..95cc4ea 100644 --- a/clang/test/Analysis/unreachable-code-path.c +++ b/clang/test/Analysis/unreachable-code-path.c @@ -63,6 +63,7 @@ void test6(const char *c) { if (c) return; if (!c) return; __builtin_unreachable(); // no-warning + __builtin_assume(0); // no-warning } // Compile-time constant false positives diff --git a/clang/test/Sema/return.c b/clang/test/Sema/return.c index 2a58a6ef..debf5ab 100644 --- a/clang/test/Sema/return.c +++ b/clang/test/Sema/return.c @@ -283,6 +283,18 @@ lbl: goto lbl; } +int test36a(int b) { + if (b) + return 43; + __builtin_unreachable(); +} + +int test36b(int b) { + if (b) + return 43; + __builtin_assume(0); +} + // PR19074. void abort(void) __attribute__((noreturn)); #define av_assert0(cond) do {\ diff --git a/clang/test/Sema/warn-unreachable.c b/clang/test/Sema/warn-unreachable.c index 440aa0a..aec3b07 100644 --- a/clang/test/Sema/warn-unreachable.c +++ b/clang/test/Sema/warn-unreachable.c @@ -468,6 +468,7 @@ int pr13910_foo(int x) { else return x; __builtin_unreachable(); // expected no warning + __builtin_assume(0); // expected no warning } int pr13910_bar(int x) { @@ -485,16 +486,19 @@ int pr13910_bar2(int x) { return x; pr13910_foo(x); // expected-warning {{code will never be executed}} __builtin_unreachable(); // expected no warning + __builtin_assume(0); // expected no warning pr13910_foo(x); // expected-warning {{code will never be executed}} } void pr13910_noreturn() { raze(); __builtin_unreachable(); // expected no warning + __builtin_assume(0); // expected no warning } void pr13910_assert() { myassert(0 && "unreachable"); return; __builtin_unreachable(); // expected no warning + __builtin_assume(0); // expected no warning } -- 2.7.4