Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -2357,6 +2357,10 @@ 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() && Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -2917,6 +2917,18 @@ 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 { Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -2078,7 +2078,7 @@ bool OmitArguments = false; if (FunctionDecl *FD = C->getDirectCallee()) { - if (FD->isNoReturn()) + if (FD->isNoReturn() || C->isBuiltinAssumeFalse(*Context)) NoReturn = true; if (FD->hasAttr()) AddEHEdge = false; Index: lib/Analysis/ReachableCode.cpp =================================================================== --- lib/Analysis/ReachableCode.cpp +++ lib/Analysis/ReachableCode.cpp @@ -66,6 +66,21 @@ 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 @@ llvm::BitVector &Reachable; SmallVector WorkList; Preprocessor &PP; + ASTContext &C; typedef SmallVector, 12> DeferredLocsTy; @@ -379,10 +395,10 @@ 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 @@ 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 @@ if (reachable[block->getBlockID()]) continue; - DeadCodeScan DS(reachable, PP); + DeadCodeScan DS(reachable, PP, AC.getASTContext()); numReachable += DS.scanBackwards(block, CB); if (numReachable == cfg->getNumBlockIDs()) Index: lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -132,7 +132,8 @@ 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; } Index: test/Analysis/unreachable-code-path.c =================================================================== --- test/Analysis/unreachable-code-path.c +++ test/Analysis/unreachable-code-path.c @@ -63,6 +63,7 @@ if (c) return; if (!c) return; __builtin_unreachable(); // no-warning + __builtin_assume(0); // no-warning } // Compile-time constant false positives Index: test/Sema/return.c =================================================================== --- test/Sema/return.c +++ test/Sema/return.c @@ -283,6 +283,18 @@ 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 {\ Index: test/Sema/warn-unreachable.c =================================================================== --- test/Sema/warn-unreachable.c +++ test/Sema/warn-unreachable.c @@ -468,6 +468,7 @@ else return x; __builtin_unreachable(); // expected no warning + __builtin_assume(0); // expected no warning } int pr13910_bar(int x) { @@ -485,16 +486,19 @@ 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 }