Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -2287,6 +2287,7 @@ Stmt **SubExprs; unsigned NumArgs; SourceLocation RParenLoc; + bool CanDelayEvaluation; void updateDependenciesFromArg(Expr *Arg); @@ -2413,6 +2414,11 @@ /// evaluate side-effects within its arguments. bool isUnevaluatedBuiltinCall(const ASTContext &Ctx) const; + // Used by __builtin_constant_p() to indicate that its evaluation can be + // delayed until after inlining. + void setCanDelayEvaluation(bool V) { CanDelayEvaluation = V; } + bool getCanDelayEvaluation() const { return CanDelayEvaluation; } + /// getCallReturnType - Get the return type of the call expr. This is not /// always the type of the expr itself, if the return type is a reference /// type. Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4098,6 +4098,7 @@ void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T); void MarkDeclarationsReferencedInExpr(Expr *E, bool SkipLocalVariables = false); + void MaybeMarkBuiltinConstantPCannotDelayEvaluation(Expr *E); /// Try to recover by turning the given expression into a /// call. Returns true if recovery was attempted or an error was Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1198,7 +1198,7 @@ : Expr(SC, t, VK, OK_Ordinary, fn->isTypeDependent(), fn->isValueDependent(), fn->isInstantiationDependent(), fn->containsUnexpandedParameterPack()), - NumArgs(args.size()) { + NumArgs(args.size()), CanDelayEvaluation(true) { unsigned NumPreArgs = preargs.size(); SubExprs = new (C) Stmt *[args.size()+PREARGS_START+NumPreArgs]; Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -8155,8 +8155,19 @@ return Success(Val.countLeadingZeros(), E); } - case Builtin::BI__builtin_constant_p: - return Success(EvaluateBuiltinConstantP(Info.Ctx, E->getArg(0)), E); + case Builtin::BI__builtin_constant_p: { + auto Arg = E->getArg(0); + if (EvaluateBuiltinConstantP(Info.Ctx, Arg)) + return Success(true, E); + Arg = Arg->IgnoreParenCasts(); + if (isa(Arg->IgnoreParenCasts()) && + !Arg->HasSideEffects(Info.Ctx) && E->getCanDelayEvaluation()) { + // Note: This diagnostic won't be shown to the user. + Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); + return false; + } + return Success(false, E); + } case Builtin::BI__builtin_ctz: case Builtin::BI__builtin_ctzl: Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -1768,6 +1768,12 @@ case Builtin::BI__builtin_rotateright64: return emitRotate(E, true); + case Builtin::BI__builtin_constant_p: + if (CGM.getCodeGenOpts().OptimizationLevel == 0 || + hasAggregateEvaluationKind(E->getArg(0)->getType())) + return RValue::get(ConstantInt::get(Int32Ty, 0)); + return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::is_constant)); + case Builtin::BI__builtin_object_size: { unsigned Type = E->getArg(1)->EvaluateKnownConstInt(getContext()).getZExtValue(); Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -5767,9 +5767,10 @@ ? VK_RValue : VK_LValue; - return MaybeBindToTemporary( - new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, - VK, LiteralExpr, isFileScope)); + auto CLE = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, + VK, LiteralExpr, isFileScope); + MaybeMarkBuiltinConstantPCannotDelayEvaluation(CLE); + return MaybeBindToTemporary(CLE); } ExprResult @@ -5795,6 +5796,7 @@ InitListExpr *E = new (Context) InitListExpr(Context, LBraceLoc, InitArgList, RBraceLoc); E->setType(Context.VoidTy); // FIXME: just a place holder for now. + MaybeMarkBuiltinConstantPCannotDelayEvaluation(E); return E; } @@ -15585,6 +15587,49 @@ EvaluatedExprMarker(*this, SkipLocalVariables).Visit(E); } +namespace { + // Helper class that marks all calls to __builtin_constant_p that cannot be + // evaluated after inlining. + // + // GCC honors __builtin_constant_p() after inlining. Clang performs inlining + // too late to do that in the front-end. Therefore, clang converts bcp calls + // that can't be evaluated to a constant to an intrinsic so that the + // middle-end can determine whether or not it's constant after inlining. But + // this won't work for initializer lists and other constructs. For those, we + // need to be conservative in our analysis. So if we cannot determine that a + // value is constant before LLVM IR generation, then we resolve the bcp call + // to "false". + struct BuiltinConstantPCannotDelayEvaluation : + public EvaluatedExprVisitor { + typedef EvaluatedExprVisitor + Inherited; + + BuiltinConstantPCannotDelayEvaluation(Sema &S) + : Inherited(S.Context) { } + + void VisitCallExpr(CallExpr *E) { + auto DC = E->getDirectCallee(); + if (!DC || DC->getBuiltinID() != Builtin::BI__builtin_constant_p) + return; + E->setCanDelayEvaluation(false); + } + + void VisitInitListExpr(InitListExpr *E) { + for (auto Elem : *E) + Visit(Elem); + } + }; +} + +// Mark all calls to __builtin_constant_p that cannot have their evaluation +// delayed. I.e., we won't generate an llvm.is.constant() intrinsic if the front +// end is unable to ensure the argument is constant. E.g., when a +// __builtin_constant_p() is used in an InitListExpr or subscript of a global +// VarDecl. +void Sema::MaybeMarkBuiltinConstantPCannotDelayEvaluation(Expr *E) { + BuiltinConstantPCannotDelayEvaluation(*this).Visit(E); +} + /// Emit a diagnostic that describes an effect on the run-time behavior /// of the program being compiled. /// Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -4435,6 +4435,11 @@ checkNullabilityConsistency(S, SimplePointerKind::Array, DeclType.Loc); } + if (ArraySize && !D.isExpressionContext()) + // Mark all __builtin_constant_p() calls in an array size expression as + // needing to be evaluated early. + S.MaybeMarkBuiltinConstantPCannotDelayEvaluation(ArraySize); + T = S.BuildArrayType(T, ASM, ArraySize, ATI.TypeQuals, SourceRange(DeclType.Loc, DeclType.EndLoc), Name); break; Index: test/Analysis/builtin-functions.cpp =================================================================== --- test/Analysis/builtin-functions.cpp +++ test/Analysis/builtin-functions.cpp @@ -70,14 +70,14 @@ const int j = 2; constexpr int k = 3; clang_analyzer_eval(__builtin_constant_p(42) == 1); // expected-warning {{TRUE}} - clang_analyzer_eval(__builtin_constant_p(i) == 0); // expected-warning {{TRUE}} + clang_analyzer_eval(__builtin_constant_p(i) == 0); // expected-warning {{UNKNOWN}} clang_analyzer_eval(__builtin_constant_p(j) == 1); // expected-warning {{TRUE}} clang_analyzer_eval(__builtin_constant_p(k) == 1); // expected-warning {{TRUE}} clang_analyzer_eval(__builtin_constant_p(i + 42) == 0); // expected-warning {{TRUE}} clang_analyzer_eval(__builtin_constant_p(j + 42) == 1); // expected-warning {{TRUE}} clang_analyzer_eval(__builtin_constant_p(k + 42) == 1); // expected-warning {{TRUE}} clang_analyzer_eval(__builtin_constant_p(" ") == 1); // expected-warning {{TRUE}} - clang_analyzer_eval(__builtin_constant_p(test_constant_p) == 0); // expected-warning {{TRUE}} + clang_analyzer_eval(__builtin_constant_p(test_constant_p) == 0); // expected-warning {{UNKNOWN}} clang_analyzer_eval(__builtin_constant_p(k - 3) == 0); // expected-warning {{FALSE}} clang_analyzer_eval(__builtin_constant_p(k - 3) == 1); // expected-warning {{TRUE}} } Index: test/Sema/builtins.c =================================================================== --- test/Sema/builtins.c +++ test/Sema/builtins.c @@ -122,6 +122,14 @@ __builtin_constant_p(1, 2); // expected-error {{too many arguments}} } +// __builtin_constant_p cannot resolve non-constants such as a file scoped array. +int expr; +char y[__builtin_constant_p(expr) ? -1 : 1]; // no warning, the builtin is false. + +// no warning, the builtin is false. +struct foo { int a; }; +struct foo x = (struct foo) { __builtin_constant_p(42) ? 37 : 927 }; + const int test17_n = 0; const char test17_c[] = {1, 2, 3, 0}; const char test17_d[] = {1, 2, 3, 4}; @@ -168,14 +176,17 @@ // a builtin. ASSERT(OPT("abc")); ASSERT(!OPT("abcd")); + // In these cases, the strlen is non-constant, but the __builtin_constant_p - // is 0: the array size is not an ICE but is foldable. - ASSERT(!OPT(test17_c)); // expected-warning {{folded}} + // is 0: the array size is not an ICE. + ASSERT(!OPT(test17_c)); // no warning expected + ASSERT(!OPT((char*)test17_c)); // no warning expected + ASSERT(!OPT(test17_d)); // no warning expected + ASSERT(!OPT((char*)test17_d)); // no warning expected + + // These are foldable. ASSERT(!OPT(&test17_c[0])); // expected-warning {{folded}} - ASSERT(!OPT((char*)test17_c)); // expected-warning {{folded}} - ASSERT(!OPT(test17_d)); // expected-warning {{folded}} ASSERT(!OPT(&test17_d[0])); // expected-warning {{folded}} - ASSERT(!OPT((char*)test17_d)); // expected-warning {{folded}} #undef OPT #undef T @@ -287,3 +298,18 @@ memcpy(buf, src, 11); // expected-warning{{'memcpy' will always overflow; destination buffer has size 10, but size argument is 11}} my_memcpy(buf, src, 11); // expected-warning{{'__builtin___memcpy_chk' will always overflow; destination buffer has size 10, but size argument is 11}} } + +size_t test24(int a) { + char x[__builtin_constant_p(a) ? -1 : 1]; // no warning expected + return strlen(x); +} + +__attribute__((always_inline)) +size_t test25(int a) { + char x[__builtin_constant_p(a) ? 1 : -1]; // no warning expected + return strlen(x); +} + +size_t test26() { + return test25(2); +}