diff --git a/clang/lib/Analysis/CalledOnceCheck.cpp b/clang/lib/Analysis/CalledOnceCheck.cpp --- a/clang/lib/Analysis/CalledOnceCheck.cpp +++ b/clang/lib/Analysis/CalledOnceCheck.cpp @@ -22,6 +22,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/FlowSensitive/DataflowWorklist.h" +#include "clang/Basic/Builtins.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/BitVector.h" @@ -330,6 +331,29 @@ return Visit(OVE->getSourceExpr()); } + const DeclRefExpr *VisitCallExpr(const CallExpr *CE) { + if (!ShouldRetrieveFromComparisons) + return nullptr; + + // We want to see through some of the boolean builtin functions + // that we are likely to see in conditions. + switch (CE->getBuiltinCallee()) { + case Builtin::BI__builtin_expect: + case Builtin::BI__builtin_expect_with_probability: { + assert(CE->getNumArgs() >= 2); + + const DeclRefExpr *Candidate = Visit(CE->getArg(0)); + return Candidate != nullptr ? Candidate : Visit(CE->getArg(1)); + } + + case Builtin::BI__builtin_unpredictable: + return Visit(CE->getArg(0)); + + default: + return nullptr; + } + } + const DeclRefExpr *VisitExpr(const Expr *E) { // It is a fallback method that gets called whenever the actual type // of the given expression is not covered. diff --git a/clang/test/SemaObjC/warn-called-once.m b/clang/test/SemaObjC/warn-called-once.m --- a/clang/test/SemaObjC/warn-called-once.m +++ b/clang/test/SemaObjC/warn-called-once.m @@ -4,6 +4,11 @@ #define nil (id)0 #define CALLED_ONCE __attribute__((called_once)) #define NORETURN __attribute__((noreturn)) +#define LIKELY(X) __builtin_expect(!!(X), 1) +#define UNLIKELY(X) __builtin_expect(!!(X), 0) +#define LIKELY_WITH_PROBA(X, P) __builtin_expect_with_probability(!!(X), 1, P) +#define UNLIKELY_WITH_PROBA(X, P) __builtin_expect_with_probability(!!(X), 0, P) +#define UNPRED(X) __builtin_unpredictable((long)(X)) @protocol NSObject @end @@ -547,6 +552,70 @@ // no-warning } +void call_with_builtin_check_1(int (^callback)(void) CALLED_ONCE) { + if (LIKELY(callback)) + callback(); + // no-warning +} + +void call_with_builtin_check_2(int (^callback)(void) CALLED_ONCE) { + if (!UNLIKELY(callback)) { + } else { + callback(); + } + // no-warning +} + +void call_with_builtin_check_3(int (^callback)(void) CALLED_ONCE) { + if (__builtin_expect((long)callback, 0L)) { + } else { + callback(); + } + // no-warning +} + +void call_with_builtin_check_4(int (^callback)(void) CALLED_ONCE) { + if (__builtin_expect(0L, (long)callback)) { + } else { + callback(); + } + // no-warning +} + +void call_with_builtin_check_5(int (^callback)(void) CALLED_ONCE) { + if (LIKELY_WITH_PROBA(callback, 0.9)) + callback(); + // no-warning +} + +void call_with_builtin_check_6(int (^callback)(void) CALLED_ONCE) { + if (!UNLIKELY_WITH_PROBA(callback, 0.9)) { + } else { + callback(); + } + // no-warning +} + +void call_with_builtin_check_7(int (^callback)(void) CALLED_ONCE) { + if (UNPRED(callback)) { + } else { + callback(); + } + // no-warning +} + +void call_with_builtin_check_8(int (^callback)(void) CALLED_ONCE) { + if (LIKELY(callback != nil)) + callback(); + // no-warning +} + +void call_with_builtin_check_9(int (^callback)(void) CALLED_ONCE) { + if (!UNLIKELY(callback == NULL)) + callback(); + // no-warning +} + void unreachable_true_branch(void (^callback)(void) CALLED_ONCE) { if (0) {