From bfe5a44dde6243818898e7cfada316d762c59742 Mon Sep 17 00:00:00 2001 From: Leonard Chan Date: Wed, 16 Jan 2019 18:53:05 +0000 Subject: [Fixed Point Arithmetic] Add APFixedPoint to APValue This adds APFixedPoint to the union of values that can be represented with an APValue. Differential Revision: https://reviews.llvm.org/D56746 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@351368 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index da093ff22c..6fced96484 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -2027,6 +2027,9 @@ static bool HandleConversionToBool(const APValue &Val, bool &Result) { case APValue::Int: Result = Val.getInt().getBoolValue(); return true; + case APValue::FixedPoint: + Result = Val.getFixedPoint().getBoolValue(); + return true; case APValue::Float: Result = !Val.getFloat().isZero(); return true; -- cgit v1.2.3 From 87155c67f399837e1875acd5658a8ff76c3f3164 Mon Sep 17 00:00:00 2001 From: Leonard Chan Date: Fri, 18 Jan 2019 21:04:25 +0000 Subject: [Fixed Point Arithmetic] Fixed Point Addition Constant Expression Evaluation This patch includes logic for constant expression evaluation of fixed point additions. Differential Revision: https://reviews.llvm.org/D55868 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@351593 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 197 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 148 insertions(+), 49 deletions(-) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 6fced96484..de9943653c 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -44,6 +44,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" +#include "clang/Basic/FixedPoint.h" #include "clang/Basic/TargetInfo.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" @@ -613,6 +614,15 @@ namespace { } return *this; } + + OptionalDiagnostic &operator<<(const APFixedPoint &FX) { + if (Diag) { + SmallVector Buffer; + FX.toString(Buffer); + *Diag << StringRef(Buffer.data(), Buffer.size()); + } + return *this; + } }; /// A cleanup, and a flag indicating whether it is lifetime-extended. @@ -1613,6 +1623,14 @@ static bool EvaluateAtomic(const Expr *E, const LValue *This, APValue &Result, EvalInfo &Info); static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result); +/// Evaluate an integer or fixed point expression into an APResult. +static bool EvaluateFixedPointOrInteger(const Expr *E, APFixedPoint &Result, + EvalInfo &Info); + +/// Evaluate only a fixed point expression into an APResult. +static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result, + EvalInfo &Info); + //===----------------------------------------------------------------------===// // Misc utilities //===----------------------------------------------------------------------===// @@ -7493,53 +7511,27 @@ class FixedPointExprEvaluator FixedPointExprEvaluator(EvalInfo &info, APValue &result) : ExprEvaluatorBaseTy(info), Result(result) {} - bool Success(const llvm::APSInt &SI, const Expr *E, APValue &Result) { - assert(E->getType()->isFixedPointType() && "Invalid evaluation result."); - assert(SI.isSigned() == E->getType()->isSignedFixedPointType() && - "Invalid evaluation result."); - assert(SI.getBitWidth() == Info.Ctx.getIntWidth(E->getType()) && - "Invalid evaluation result."); - Result = APValue(SI); - return true; - } - bool Success(const llvm::APSInt &SI, const Expr *E) { - return Success(SI, E, Result); - } - - bool Success(const llvm::APInt &I, const Expr *E, APValue &Result) { - assert(E->getType()->isFixedPointType() && "Invalid evaluation result."); - assert(I.getBitWidth() == Info.Ctx.getIntWidth(E->getType()) && - "Invalid evaluation result."); - Result = APValue(APSInt(I)); - Result.getInt().setIsUnsigned(E->getType()->isUnsignedFixedPointType()); - return true; - } bool Success(const llvm::APInt &I, const Expr *E) { - return Success(I, E, Result); + return Success( + APFixedPoint(I, Info.Ctx.getFixedPointSemantics(E->getType())), E); } - bool Success(uint64_t Value, const Expr *E, APValue &Result) { - assert(E->getType()->isFixedPointType() && "Invalid evaluation result."); - Result = APValue(Info.Ctx.MakeIntValue(Value, E->getType())); - return true; - } bool Success(uint64_t Value, const Expr *E) { - return Success(Value, E, Result); - } - - bool Success(CharUnits Size, const Expr *E) { - return Success(Size.getQuantity(), E); + return Success( + APFixedPoint(Value, Info.Ctx.getFixedPointSemantics(E->getType())), E); } bool Success(const APValue &V, const Expr *E) { - if (V.isLValue() || V.isAddrLabelDiff()) { - Result = V; - return true; - } - return Success(V.getInt(), E); + return Success(V.getFixedPoint(), E); } - bool ZeroInitialization(const Expr *E) { return Success(0, E); } + bool Success(const APFixedPoint &V, const Expr *E) { + assert(E->getType()->isFixedPointType() && "Invalid evaluation result."); + assert(V.getWidth() == Info.Ctx.getIntWidth(E->getType()) && + "Invalid evaluation result."); + Result = APValue(V); + return true; + } //===--------------------------------------------------------------------===// // Visitor Methods @@ -7549,7 +7541,9 @@ class FixedPointExprEvaluator return Success(E->getValue(), E); } + bool VisitCastExpr(const CastExpr *E); bool VisitUnaryOperator(const UnaryOperator *E); + bool VisitBinaryOperator(const BinaryOperator *E); }; } // end anonymous namespace @@ -7581,6 +7575,36 @@ static bool EvaluateInteger(const Expr *E, APSInt &Result, EvalInfo &Info) { return true; } +static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result, + EvalInfo &Info) { + if (E->getType()->isFixedPointType()) { + APValue Val; + if (!FixedPointExprEvaluator(Info, Val).Visit(E)) + return false; + if (!Val.isFixedPoint()) + return false; + + Result = Val.getFixedPoint(); + return true; + } + return false; +} + +static bool EvaluateFixedPointOrInteger(const Expr *E, APFixedPoint &Result, + EvalInfo &Info) { + if (E->getType()->isIntegerType()) { + auto FXSema = Info.Ctx.getFixedPointSemantics(E->getType()); + APSInt Val; + if (!EvaluateInteger(E, Val, Info)) + return false; + Result = APFixedPoint(Val, FXSema); + return true; + } else if (E->getType()->isFixedPointType()) { + return EvaluateFixedPoint(E, Result, Info); + } + return false; +} + /// Check whether the given declaration can be directly converted to an integral /// rvalue. If not, no diagnostic is produced; there are other things we can /// try. @@ -9781,7 +9805,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { APValue Val; if (!Evaluate(Val, Info, SubExpr)) return false; - return Success(Val.getInt().getBoolValue(), E); + return Success(Val.getFixedPoint().getBoolValue(), E); } case CK_IntegralCast: { @@ -9901,16 +9925,13 @@ bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { return Visit(E->getSubExpr()); case UO_Minus: { if (!Visit(E->getSubExpr())) return false; - if (!Result.isInt()) return Error(E); - const APSInt &Value = Result.getInt(); - if (Value.isSigned() && Value.isMinSignedValue() && E->canOverflow()) { - SmallString<64> S; - FixedPointValueToString(S, Value, - Info.Ctx.getTypeInfo(E->getType()).Width); - Info.CCEDiag(E, diag::note_constexpr_overflow) << S << E->getType(); - if (Info.noteUndefinedBehavior()) return false; - } - return Success(-Value, E); + if (!Result.isFixedPoint()) + return Error(E); + bool Overflowed; + APFixedPoint Negated = Result.getFixedPoint().negate(&Overflowed); + if (Overflowed && !HandleOverflow(Info, E, Negated, E->getType())) + return false; + return Success(Negated, E); } case UO_LNot: { bool bres; @@ -9921,6 +9942,61 @@ bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { } } +bool FixedPointExprEvaluator::VisitCastExpr(const CastExpr *E) { + const Expr *SubExpr = E->getSubExpr(); + QualType DestType = E->getType(); + assert(DestType->isFixedPointType() && + "Expected destination type to be a fixed point type"); + auto DestFXSema = Info.Ctx.getFixedPointSemantics(DestType); + + switch (E->getCastKind()) { + case CK_FixedPointCast: { + APFixedPoint Src(Info.Ctx.getFixedPointSemantics(SubExpr->getType())); + if (!EvaluateFixedPoint(SubExpr, Src, Info)) + return false; + bool Overflowed; + APFixedPoint Result = Src.convert(DestFXSema, &Overflowed); + if (Overflowed && !HandleOverflow(Info, E, Result, DestType)) + return false; + return Success(Result, E); + } + case CK_NoOp: + case CK_LValueToRValue: + return ExprEvaluatorBaseTy::VisitCastExpr(E); + default: + return Error(E); + } +} + +bool FixedPointExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); + FixedPointSemantics ResultFXSema = + Info.Ctx.getFixedPointSemantics(E->getType()); + + APFixedPoint LHSFX(Info.Ctx.getFixedPointSemantics(LHS->getType())); + if (!EvaluateFixedPointOrInteger(LHS, LHSFX, Info)) + return false; + APFixedPoint RHSFX(Info.Ctx.getFixedPointSemantics(RHS->getType())); + if (!EvaluateFixedPointOrInteger(RHS, RHSFX, Info)) + return false; + + switch (E->getOpcode()) { + case BO_Add: { + bool AddOverflow, ConversionOverflow; + APFixedPoint Result = LHSFX.add(RHSFX, &AddOverflow) + .convert(ResultFXSema, &ConversionOverflow); + if ((AddOverflow || ConversionOverflow) && + !HandleOverflow(Info, E, Result, E->getType())) + return false; + return Success(Result, E); + } + default: + return false; + } + llvm_unreachable("Should've exited before this"); +} + //===----------------------------------------------------------------------===// // Float Evaluation //===----------------------------------------------------------------------===// @@ -10932,6 +11008,23 @@ static bool EvaluateAsInt(const Expr *E, Expr::EvalResult &ExprResult, return true; } +static bool EvaluateAsFixedPoint(const Expr *E, Expr::EvalResult &ExprResult, + const ASTContext &Ctx, + Expr::SideEffectsKind AllowSideEffects, + EvalInfo &Info) { + if (!E->getType()->isFixedPointType()) + return false; + + if (!::EvaluateAsRValue(E, ExprResult, Ctx, Info)) + return false; + + if (!ExprResult.Val.isFixedPoint() || + hasUnacceptableSideEffect(ExprResult, AllowSideEffects)) + return false; + + return true; +} + /// EvaluateAsRValue - Return true if this is a constant which we can fold using /// any crazy technique (that has nothing to do with language standards) that /// we want to. If this function returns true, it returns the folded constant @@ -10957,6 +11050,12 @@ bool Expr::EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, return ::EvaluateAsInt(this, Result, Ctx, AllowSideEffects, Info); } +bool Expr::EvaluateAsFixedPoint(EvalResult &Result, const ASTContext &Ctx, + SideEffectsKind AllowSideEffects) const { + EvalInfo Info(Ctx, Result, EvalInfo::EM_IgnoreSideEffects); + return ::EvaluateAsFixedPoint(this, Result, Ctx, AllowSideEffects, Info); +} + bool Expr::EvaluateAsFloat(APFloat &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects) const { if (!getType()->isRealFloatingType()) -- cgit v1.2.3 From 324f918438715b4a0d024af5930628c1674f4fcd Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Sat, 19 Jan 2019 08:50:56 +0000 Subject: Update the file headers across all of the LLVM projects in the monorepo to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@351636 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index de9943653c..62845a45c3 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1,9 +1,8 @@ //===--- ExprConstant.cpp - Expression Constant Evaluator -----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // -- cgit v1.2.3 From c18e7e9007970a3105617f03bc9d1de89fa1a3e1 Mon Sep 17 00:00:00 2001 From: Erik Pilkington Date: Wed, 30 Jan 2019 20:34:53 +0000 Subject: Add a new builtin: __builtin_dynamic_object_size This builtin has the same UI as __builtin_object_size, but has the potential to be evaluated dynamically. It is meant to be used as a drop-in replacement for libraries that use __builtin_object_size when a dynamic checking mode is enabled. For instance, __builtin_object_size fails to provide any extra checking in the following function: void f(size_t alloc) { char* p = malloc(alloc); strcpy(p, "foobar"); // expands to __builtin___strcpy_chk(p, "foobar", __builtin_object_size(p, 0)) } This is an overflow if alloc < 7, but because LLVM can't fold the object size intrinsic statically, it folds __builtin_object_size to -1. With __builtin_dynamic_object_size, alloc is passed through to __builtin___strcpy_chk. rdar://32212419 Differential revision: https://reviews.llvm.org/D56760 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@352665 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 62845a45c3..8f20e12a51 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -8190,6 +8190,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, default: return ExprEvaluatorBaseTy::VisitCallExpr(E); + case Builtin::BI__builtin_dynamic_object_size: case Builtin::BI__builtin_object_size: { // The type was checked when we built the expression. unsigned Type = -- cgit v1.2.3 From 4e215e73ed6b499f3ccf23263c96cd188e0eda2c Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Fri, 8 Feb 2019 21:18:46 +0000 Subject: [Sema] Make string literal init an rvalue. This allows substantially simplifying the expression evaluation code, because we don't have to special-case lvalues which are actually string literal initialization. This currently throws away an optimization where we would avoid creating an array APValue for string literal initialization. If we really want to optimize this case, we should fix APValue so it can store simple arrays more efficiently, like llvm::ConstantDataArray. This shouldn't affect the memory usage for other string literals. (Not sure if this is a blocker; I don't think string literal init is common enough for this to be a serious issue, but I could be wrong.) The change to test/CodeGenObjC/encode-test.m is a weird side-effect of these changes: we currently don't constant-evaluate arrays in C, so the strlen call shouldn't be folded, but lvalue string init managed to get around that check. I this this is fine. Fixes https://bugs.llvm.org/show_bug.cgi?id=40430 . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@353569 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 69 ++++++++++++++++++------------------------------ 1 file changed, 25 insertions(+), 44 deletions(-) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 8f20e12a51..801ee96839 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -2688,9 +2688,11 @@ static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit, } // Expand a string literal into an array of characters. -static void expandStringLiteral(EvalInfo &Info, const Expr *Lit, +// +// FIXME: This is inefficient; we should probably introduce something similar +// to the LLVM ConstantDataArray to make this cheaper. +static void expandStringLiteral(EvalInfo &Info, const StringLiteral *S, APValue &Result) { - const StringLiteral *S = cast(Lit); const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(S->getType()); assert(CAT && "string literal isn't an array"); @@ -2884,18 +2886,6 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, ObjType = CAT->getElementType(); - // An array object is represented as either an Array APValue or as an - // LValue which refers to a string literal. - if (O->isLValue()) { - assert(I == N - 1 && "extracting subobject of character?"); - assert(!O->hasLValuePath() || O->getLValuePath().empty()); - if (handler.AccessKind != AK_Read) - expandStringLiteral(Info, O->getLValueBase().get(), - *O); - else - return handler.foundString(*O, ObjType, Index); - } - if (O->getArrayInitializedElts() > Index) O = &O->getArrayInitializedElt(Index); else if (handler.AccessKind != AK_Read) { @@ -3008,11 +2998,6 @@ struct ExtractSubobjectHandler { Result = APValue(Value); return true; } - bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) { - Result = APValue(extractStringLiteralCharacter( - Info, Subobj.getLValueBase().get(), Character)); - return true; - } }; } // end anonymous namespace @@ -3070,9 +3055,6 @@ struct ModifySubobjectHandler { Value = NewVal.getFloat(); return true; } - bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) { - llvm_unreachable("shouldn't encounter string elements with ExpandArrays"); - } }; } // end anonymous namespace @@ -3386,12 +3368,20 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, CompleteObject LitObj(&Lit, Base->getType(), false); return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal); } else if (isa(Base) || isa(Base)) { - // We represent a string literal array as an lvalue pointing at the - // corresponding expression, rather than building an array of chars. - // FIXME: Support ObjCEncodeExpr, MakeStringConstant - APValue Str(Base, CharUnits::Zero(), APValue::NoLValuePath(), 0); - CompleteObject StrObj(&Str, Base->getType(), false); - return extractSubobject(Info, Conv, StrObj, LVal.Designator, RVal); + // Special-case character extraction so we don't have to construct an + // APValue for the whole string. + assert(LVal.Designator.Entries.size() == 1 && + "Can only read characters from string literals"); + if (LVal.Designator.isOnePastTheEnd()) { + if (Info.getLangOpts().CPlusPlus11) + Info.FFDiag(Conv, diag::note_constexpr_access_past_end) << AK_Read; + else + Info.FFDiag(Conv); + return false; + } + uint64_t CharIndex = LVal.Designator.Entries[0].ArrayIndex; + RVal = APValue(extractStringLiteralCharacter(Info, Base, CharIndex)); + return true; } } @@ -3517,9 +3507,6 @@ struct CompoundAssignSubobjectHandler { LVal.moveInto(Subobj); return true; } - bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) { - llvm_unreachable("shouldn't encounter string elements here"); - } }; } // end anonymous namespace @@ -3668,9 +3655,6 @@ struct IncDecSubobjectHandler { LVal.moveInto(Subobj); return true; } - bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) { - llvm_unreachable("shouldn't encounter string elements here"); - } }; } // end anonymous namespace @@ -7150,8 +7134,7 @@ namespace { : ExprEvaluatorBaseTy(Info), This(This), Result(Result) {} bool Success(const APValue &V, const Expr *E) { - assert((V.isArray() || V.isLValue()) && - "expected array or string literal"); + assert(V.isArray() && "expected array"); Result = V; return true; } @@ -7182,6 +7165,10 @@ namespace { bool VisitCXXConstructExpr(const CXXConstructExpr *E, const LValue &Subobject, APValue *Value, QualType Type); + bool VisitStringLiteral(const StringLiteral *E) { + expandStringLiteral(Info, E, Result); + return true; + } }; } // end anonymous namespace @@ -7214,14 +7201,8 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { // C++11 [dcl.init.string]p1: A char array [...] can be initialized by [...] // an appropriately-typed string literal enclosed in braces. - if (E->isStringLiteralInit()) { - LValue LV; - if (!EvaluateLValue(E->getInit(0), LV, Info)) - return false; - APValue Val; - LV.moveInto(Val); - return Success(Val, E); - } + if (E->isStringLiteralInit()) + return Visit(E->getInit(0)); bool Success = true; -- cgit v1.2.3 From d9c40ccfc4852997df56264c1ff5c50a793c1bc6 Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Sat, 9 Feb 2019 02:22:17 +0000 Subject: Fix buildbot failure from r353569. I assumed lvalue-to-rvalue conversions of array type would never happen, but apparently clang-tidy tries in some cases. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@353598 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 801ee96839..19e6b53b09 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -3370,8 +3370,15 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, } else if (isa(Base) || isa(Base)) { // Special-case character extraction so we don't have to construct an // APValue for the whole string. - assert(LVal.Designator.Entries.size() == 1 && + assert(LVal.Designator.Entries.size() <= 1 && "Can only read characters from string literals"); + if (LVal.Designator.Entries.empty()) { + // Fail for now for LValue to RValue conversion of an array. + // (This shouldn't show up in C/C++, but it could be triggered by a + // weird EvaluateAsRValue call from a tool.) + Info.FFDiag(Conv); + return false; + } if (LVal.Designator.isOnePastTheEnd()) { if (Info.getLangOpts().CPlusPlus11) Info.FFDiag(Conv, diag::note_constexpr_access_past_end) << AK_Read; -- cgit v1.2.3 From 8cdd2381793911f203f4d2cbdb54b257673cd585 Mon Sep 17 00:00:00 2001 From: Clement Courbet Date: Thu, 14 Feb 2019 12:00:34 +0000 Subject: [Builtins] Treat `bcmp` as a builtin. Summary: This makes it consistent with `memcmp` and `__builtin_bcmp`. Also see the discussion in https://reviews.llvm.org/D56593. Reviewers: jyknight Subscribers: kristina, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D58120 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@354023 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 19e6b53b09..c08e8e689d 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -8426,6 +8426,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BIstrncmp: case Builtin::BIwcsncmp: case Builtin::BImemcmp: + case Builtin::BIbcmp: case Builtin::BIwmemcmp: // A call to strlen is not a constant expression. if (Info.getLangOpts().CPlusPlus11) @@ -8440,6 +8441,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BI__builtin_strncmp: case Builtin::BI__builtin_wcsncmp: case Builtin::BI__builtin_memcmp: + case Builtin::BI__builtin_bcmp: case Builtin::BI__builtin_wmemcmp: { LValue String1, String2; if (!EvaluatePointer(E->getArg(0), String1, Info) || @@ -8470,7 +8472,9 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, QualType CharTy2 = String2.Designator.getType(Info.Ctx); bool IsRawByte = BuiltinOp == Builtin::BImemcmp || - BuiltinOp == Builtin::BI__builtin_memcmp; + BuiltinOp == Builtin::BIbcmp || + BuiltinOp == Builtin::BI__builtin_memcmp || + BuiltinOp == Builtin::BI__builtin_bcmp; assert(IsRawByte || (Info.Ctx.hasSameUnqualifiedType( @@ -8538,10 +8542,12 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return Success(0, E); } - bool StopAtNull = (BuiltinOp != Builtin::BImemcmp && - BuiltinOp != Builtin::BIwmemcmp && - BuiltinOp != Builtin::BI__builtin_memcmp && - BuiltinOp != Builtin::BI__builtin_wmemcmp); + bool StopAtNull = + (BuiltinOp != Builtin::BImemcmp && BuiltinOp != Builtin::BIbcmp && + BuiltinOp != Builtin::BIwmemcmp && + BuiltinOp != Builtin::BI__builtin_memcmp && + BuiltinOp != Builtin::BI__builtin_bcmp && + BuiltinOp != Builtin::BI__builtin_wmemcmp); bool IsWide = BuiltinOp == Builtin::BIwcscmp || BuiltinOp == Builtin::BIwcsncmp || BuiltinOp == Builtin::BIwmemcmp || -- cgit v1.2.3 From 19aa7637bc5d0199bc48bb1b79c30cc0b9c54350 Mon Sep 17 00:00:00 2001 From: Leonard Chan Date: Thu, 21 Feb 2019 20:50:09 +0000 Subject: [Fixed Point Arithmetic] Fixed Point Comparisons This patch implements fixed point comparisons with other fixed point types and integers. This also provides constant expression evaluation for them. Differential Revision: https://reviews.llvm.org/D57219 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@354621 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index c08e8e689d..44ccb74e3f 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -9144,6 +9144,22 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, return Success(CCR::Equal, E); } + if (LHSTy->isFixedPointType() || RHSTy->isFixedPointType()) { + APFixedPoint LHSFX(Info.Ctx.getFixedPointSemantics(LHSTy)); + APFixedPoint RHSFX(Info.Ctx.getFixedPointSemantics(RHSTy)); + + bool LHSOK = EvaluateFixedPointOrInteger(E->getLHS(), LHSFX, Info); + if (!LHSOK && !Info.noteFailure()) + return false; + if (!EvaluateFixedPointOrInteger(E->getRHS(), RHSFX, Info) || !LHSOK) + return false; + if (LHSFX < RHSFX) + return Success(CCR::Less, E); + if (LHSFX > RHSFX) + return Success(CCR::Greater, E); + return Success(CCR::Equal, E); + } + if (LHSTy->isAnyComplexType() || RHSTy->isAnyComplexType()) { ComplexValue LHS, RHS; bool LHSOK; -- cgit v1.2.3 From ad9a67e1f7dae24e41d70541d09019900ec029cd Mon Sep 17 00:00:00 2001 From: Leonard Chan Date: Wed, 6 Mar 2019 00:28:43 +0000 Subject: [Fixed Point Arithmetic] Fixed Point and Integer Conversions This patch includes the necessary code for converting between a fixed point type and integer. This also includes constant expression evaluation for conversions with these types. Differential Revision: https://reviews.llvm.org/D56900 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@355462 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 44ccb74e3f..1a21ad2539 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -9776,6 +9776,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_AddressSpaceConversion: case CK_IntToOCLSampler: case CK_FixedPointCast: + case CK_IntegralToFixedPoint: llvm_unreachable("invalid cast kind for integral value"); case CK_BitCast: @@ -9810,6 +9811,19 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { return Success(IntResult, E); } + case CK_FixedPointToIntegral: { + APFixedPoint Src(Info.Ctx.getFixedPointSemantics(SrcType)); + if (!EvaluateFixedPoint(SubExpr, Src, Info)) + return false; + bool Overflowed; + llvm::APSInt Result = Src.convertToInt( + Info.Ctx.getIntWidth(DestType), + DestType->isSignedIntegerOrEnumerationType(), &Overflowed); + if (Overflowed && !HandleOverflow(Info, E, Result, DestType)) + return false; + return Success(Result, E); + } + case CK_FixedPointToBoolean: { // Unsigned padding does not affect this. APValue Val; @@ -9970,6 +9984,20 @@ bool FixedPointExprEvaluator::VisitCastExpr(const CastExpr *E) { return false; return Success(Result, E); } + case CK_IntegralToFixedPoint: { + APSInt Src; + if (!EvaluateInteger(SubExpr, Src, Info)) + return false; + + bool Overflowed; + APFixedPoint IntResult = APFixedPoint::getFromIntValue( + Src, Info.Ctx.getFixedPointSemantics(DestType), &Overflowed); + + if (Overflowed && !HandleOverflow(Info, E, IntResult, DestType)) + return false; + + return Success(IntResult, E); + } case CK_NoOp: case CK_LValueToRValue: return ExprEvaluatorBaseTy::VisitCastExpr(E); @@ -10371,6 +10399,8 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_IntToOCLSampler: case CK_FixedPointCast: case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: llvm_unreachable("invalid cast kind for complex value"); case CK_LValueToRValue: -- cgit v1.2.3 From d47768c5aa75c418c8e6ba96b1c05f8582b30702 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Wed, 6 Mar 2019 10:26:19 +0000 Subject: Inline asm constraints: allow ICE-like pointers for the "n" constraint (PR40890) Apparently GCC allows this, and there's code relying on it (see bug). The idea is to allow expression that would have been allowed if they were cast to int. So I based the code on how such a cast would be done (the CK_PointerToIntegral case in IntExprEvaluator::VisitCastExpr()). Differential Revision: https://reviews.llvm.org/D58821 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@355491 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 1a21ad2539..111cffe373 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -9872,13 +9872,12 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { return true; } - uint64_t V; - if (LV.isNullPointer()) - V = Info.Ctx.getTargetNullPointerValue(SrcType); - else - V = LV.getLValueOffset().getQuantity(); + APSInt AsInt; + APValue V; + LV.moveInto(V); + if (!V.toIntegralConstant(AsInt, SrcType, Info.Ctx)) + llvm_unreachable("Can't cast this!"); - APSInt AsInt = Info.Ctx.MakeIntValue(V, SrcType); return Success(HandleIntToIntCast(Info, E, DestType, SrcType, AsInt), E); } -- cgit v1.2.3 From 4874bff565a9f92c41a1efad0b3d633371b5930c Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Fri, 8 Mar 2019 04:45:37 +0000 Subject: [ObjC] Emit a boxed expression as a compile-time constant if the expression inside the parentheses is a valid UTF-8 string literal. Previously clang emitted an expression like @("abc") as a message send to stringWithUTF8String. This commit makes clang emit the boxed expression as a compile-time constant instead. This commit also has the effect of silencing the nullable-to-nonnull conversion warning clang started emitting after r317727, which originally motivated this commit (see https://oleb.net/2018/@keypath). rdar://problem/42684601 Differential Revision: https://reviews.llvm.org/D58729 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@355662 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 111cffe373..77443b67de 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1743,6 +1743,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) { case Expr::CXXTypeidExprClass: case Expr::CXXUuidofExprClass: return true; + case Expr::ObjCBoxedExprClass: + return cast(E)->isExpressibleAsConstantInitializer(); case Expr::CallExprClass: return IsStringLiteralCall(cast(E)); // For GCC compatibility, &&label has static storage duration. @@ -5794,6 +5796,8 @@ public: bool VisitObjCStringLiteral(const ObjCStringLiteral *E) { return Success(E); } bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E) { + if (E->isExpressibleAsConstantInitializer()) + return Success(E); if (Info.noteFailure()) EvaluateIgnoredValue(Info, E->getSubExpr()); return Error(E); -- cgit v1.2.3 From 06ce34ef97311cddc2f69b6514bc9d4b5cf8c43e Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Fri, 8 Mar 2019 22:06:48 +0000 Subject: [8.0 Regression] Fix handling of `__builtin_constant_p` inside template arguments, enumerators, case statements, and the enable_if attribute. Summary: The following code is accepted by Clang 7 and prior but rejected by the upcoming 8 release and in trunk [1] ``` // error {{never produces a constant expression}} void foo(const char* s) __attribute__((enable_if(__builtin_constant_p(*s) == false, "trap"))) {} void test() { foo("abc"); } ``` Prior to Clang 8, the call to `__builtin_constant_p` was a constant expression returning false. Currently, it's not a valid constant expression. The bug is caused because we failed to set `InConstantContext` when attempting to evaluate unevaluated constant expressions. [1] https://godbolt.org/z/ksAjmq Reviewers: rsmith, hans, sbenza Reviewed By: rsmith Subscribers: kristina, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D59038 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@355743 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 77443b67de..1bae6d8c40 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -11131,6 +11131,7 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage, const ASTContext &Ctx) const { EvalInfo::EvaluationMode EM = EvalInfo::EM_ConstantExpression; EvalInfo Info(Ctx, Result, EM); + Info.InConstantContext = true; if (!::Evaluate(Result.Val, Info, this)) return false; @@ -11771,6 +11772,7 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx, const Expr *This) const { Expr::EvalStatus Status; EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpressionUnevaluated); + Info.InConstantContext = true; LValue ThisVal; const LValue *ThisPtr = nullptr; @@ -11854,6 +11856,7 @@ bool Expr::isPotentialConstantExprUnevaluated(Expr *E, EvalInfo Info(FD->getASTContext(), Status, EvalInfo::EM_PotentialConstantExpressionUnevaluated); + Info.InConstantContext = true; // Fabricate a call stack frame to give the arguments a plausible cover story. ArrayRef Args; -- cgit v1.2.3 From 53a7d337cbffe544d78978fde7a76df4d24b5e40 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 24 Apr 2019 01:29:28 +0000 Subject: Fix interactions between __builtin_constant_p and constexpr to match current trunk GCC. GCC permits information from outside the operand of __builtin_constant_p (but in the same constant evaluation context) to be used within that operand; clang now does so too. A few other minor deviations from GCC's behavior showed up in my testing and are also fixed (matching GCC): * Clang now supports nullptr_t as the argument type for __builtin_constant_p * Clang now returns true from __builtin_constant_p if called with a null pointer * Clang now returns true from __builtin_constant_p if called with an integer cast to pointer type git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@359059 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 63 +++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 28 deletions(-) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 1bae6d8c40..168681b710 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -7801,19 +7801,33 @@ EvaluateBuiltinClassifyType(const CallExpr *E, const LangOptions &LangOpts) { } /// EvaluateBuiltinConstantPForLValue - Determine the result of -/// __builtin_constant_p when applied to the given lvalue. +/// __builtin_constant_p when applied to the given pointer. /// -/// An lvalue is only "constant" if it is a pointer or reference to the first -/// character of a string literal. -template -static bool EvaluateBuiltinConstantPForLValue(const LValue &LV) { - const Expr *E = LV.getLValueBase().template dyn_cast(); - return E && isa(E) && LV.getLValueOffset().isZero(); +/// A pointer is only "constant" if it is null (or a pointer cast to integer) +/// or it points to the first character of a string literal. +static bool EvaluateBuiltinConstantPForLValue(const APValue &LV) { + APValue::LValueBase Base = LV.getLValueBase(); + if (Base.isNull()) { + // A null base is acceptable. + return true; + } else if (const Expr *E = Base.dyn_cast()) { + if (!isa(E)) + return false; + return LV.getLValueOffset().isZero(); + } else { + // Any other base is not constant enough for GCC. + return false; + } } /// EvaluateBuiltinConstantP - Evaluate __builtin_constant_p as similarly to /// GCC as we can manage. -static bool EvaluateBuiltinConstantP(ASTContext &Ctx, const Expr *Arg) { +static bool EvaluateBuiltinConstantP(EvalInfo &Info, const Expr *Arg) { + // Constant-folding is always enabled for the operand of __builtin_constant_p + // (even when the enclosing evaluation context otherwise requires a strict + // language-specific constant expression). + FoldConstant Fold(Info, true); + QualType ArgType = Arg->getType(); // __builtin_constant_p always has one operand. The rules which gcc follows @@ -7821,34 +7835,27 @@ static bool EvaluateBuiltinConstantP(ASTContext &Ctx, const Expr *Arg) { // // - If the operand is of integral, floating, complex or enumeration type, // and can be folded to a known value of that type, it returns 1. - // - If the operand and can be folded to a pointer to the first character - // of a string literal (or such a pointer cast to an integral type), it - // returns 1. + // - If the operand can be folded to a pointer to the first character + // of a string literal (or such a pointer cast to an integral type) + // or to a null pointer or an integer cast to a pointer, it returns 1. // // Otherwise, it returns 0. // // FIXME: GCC also intends to return 1 for literals of aggregate types, but // its support for this does not currently work. - if (ArgType->isIntegralOrEnumerationType()) { - Expr::EvalResult Result; - if (!Arg->EvaluateAsRValue(Result, Ctx) || Result.HasSideEffects) + if (ArgType->isIntegralOrEnumerationType() || ArgType->isFloatingType() || + ArgType->isAnyComplexType() || ArgType->isPointerType() || + ArgType->isNullPtrType()) { + APValue V; + if (!::EvaluateAsRValue(Info, Arg, V)) return false; - APValue &V = Result.Val; - if (V.getKind() == APValue::Int) - return true; + // For a pointer (possibly cast to integer), there are special rules. if (V.getKind() == APValue::LValue) return EvaluateBuiltinConstantPForLValue(V); - } else if (ArgType->isFloatingType() || ArgType->isAnyComplexType()) { - return Arg->isEvaluatable(Ctx); - } else if (ArgType->isPointerType() || Arg->isGLValue()) { - LValue LV; - Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold); - if ((Arg->isGLValue() ? EvaluateLValue(Arg, LV, Info) - : EvaluatePointer(Arg, LV, Info)) && - !Status.HasSideEffects) - return EvaluateBuiltinConstantPForLValue(LV); + + // Otherwise, any constant value is good enough. + return V.getKind() != APValue::Uninitialized; } // Anything else isn't considered to be sufficiently constant. @@ -8259,7 +8266,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BI__builtin_constant_p: { auto Arg = E->getArg(0); - if (EvaluateBuiltinConstantP(Info.Ctx, Arg)) + if (EvaluateBuiltinConstantP(Info, Arg)) return Success(true, E); auto ArgTy = Arg->IgnoreImplicit()->getType(); if (!Info.InConstantContext && !Arg->HasSideEffects(Info.Ctx) && -- cgit v1.2.3 From 418eae9689448e9a0cf7abcda8cca56f420b752f Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Wed, 24 Apr 2019 02:23:30 +0000 Subject: [Builtins] Implement __builtin_is_constant_evaluated for use in C++2a Summary: This patch implements `__builtin_is_constant_evaluated` as specifier by [P0595R2](https://wg21.link/p0595r2). It is built on the back of Bill Wendling's work for `__builtin_constant_p()`. More tests to come, but early feedback is appreciated. I plan to implement warnings for common mis-usages like those belowe in a following patch: ``` void foo(int x) { if constexpr (std::is_constant_evaluated())) { // condition is always `true`. Should use plain `if` instead. foo_constexpr(x); } else { foo_runtime(x); } } ``` Reviewers: rsmith, MaskRay, bruno, void Reviewed By: rsmith Subscribers: dexonsmith, zoecarver, fdeazeve, kristina, cfe-commits Differential Revision: https://reviews.llvm.org/D55500 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@359067 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 168681b710..0a8b60d70d 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -8279,6 +8279,9 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return Success(false, E); } + case Builtin::BI__builtin_is_constant_evaluated: + return Success(Info.InConstantContext, E); + case Builtin::BI__builtin_ctz: case Builtin::BI__builtin_ctzl: case Builtin::BI__builtin_ctzll: @@ -11139,6 +11142,7 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage, EvalInfo::EvaluationMode EM = EvalInfo::EM_ConstantExpression; EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; + if (!::Evaluate(Result.Val, Info, this)) return false; -- cgit v1.2.3 From 2b5f7f9e2e42cca4f8df5763cd2529324a2d421a Mon Sep 17 00:00:00 2001 From: Jorge Gorbe Moya Date: Sat, 27 Apr 2019 00:32:04 +0000 Subject: Revert Fix interactions between __builtin_constant_p and constexpr to match current trunk GCC. This reverts r359059 (git commit 0b098754b73f3b96d00ecb1c7605760b11c90298) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@359361 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 63 +++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 35 deletions(-) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 0a8b60d70d..a30e83c632 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -7801,33 +7801,19 @@ EvaluateBuiltinClassifyType(const CallExpr *E, const LangOptions &LangOpts) { } /// EvaluateBuiltinConstantPForLValue - Determine the result of -/// __builtin_constant_p when applied to the given pointer. +/// __builtin_constant_p when applied to the given lvalue. /// -/// A pointer is only "constant" if it is null (or a pointer cast to integer) -/// or it points to the first character of a string literal. -static bool EvaluateBuiltinConstantPForLValue(const APValue &LV) { - APValue::LValueBase Base = LV.getLValueBase(); - if (Base.isNull()) { - // A null base is acceptable. - return true; - } else if (const Expr *E = Base.dyn_cast()) { - if (!isa(E)) - return false; - return LV.getLValueOffset().isZero(); - } else { - // Any other base is not constant enough for GCC. - return false; - } +/// An lvalue is only "constant" if it is a pointer or reference to the first +/// character of a string literal. +template +static bool EvaluateBuiltinConstantPForLValue(const LValue &LV) { + const Expr *E = LV.getLValueBase().template dyn_cast(); + return E && isa(E) && LV.getLValueOffset().isZero(); } /// EvaluateBuiltinConstantP - Evaluate __builtin_constant_p as similarly to /// GCC as we can manage. -static bool EvaluateBuiltinConstantP(EvalInfo &Info, const Expr *Arg) { - // Constant-folding is always enabled for the operand of __builtin_constant_p - // (even when the enclosing evaluation context otherwise requires a strict - // language-specific constant expression). - FoldConstant Fold(Info, true); - +static bool EvaluateBuiltinConstantP(ASTContext &Ctx, const Expr *Arg) { QualType ArgType = Arg->getType(); // __builtin_constant_p always has one operand. The rules which gcc follows @@ -7835,27 +7821,34 @@ static bool EvaluateBuiltinConstantP(EvalInfo &Info, const Expr *Arg) { // // - If the operand is of integral, floating, complex or enumeration type, // and can be folded to a known value of that type, it returns 1. - // - If the operand can be folded to a pointer to the first character - // of a string literal (or such a pointer cast to an integral type) - // or to a null pointer or an integer cast to a pointer, it returns 1. + // - If the operand and can be folded to a pointer to the first character + // of a string literal (or such a pointer cast to an integral type), it + // returns 1. // // Otherwise, it returns 0. // // FIXME: GCC also intends to return 1 for literals of aggregate types, but // its support for this does not currently work. - if (ArgType->isIntegralOrEnumerationType() || ArgType->isFloatingType() || - ArgType->isAnyComplexType() || ArgType->isPointerType() || - ArgType->isNullPtrType()) { - APValue V; - if (!::EvaluateAsRValue(Info, Arg, V)) + if (ArgType->isIntegralOrEnumerationType()) { + Expr::EvalResult Result; + if (!Arg->EvaluateAsRValue(Result, Ctx) || Result.HasSideEffects) return false; - // For a pointer (possibly cast to integer), there are special rules. + APValue &V = Result.Val; + if (V.getKind() == APValue::Int) + return true; if (V.getKind() == APValue::LValue) return EvaluateBuiltinConstantPForLValue(V); - - // Otherwise, any constant value is good enough. - return V.getKind() != APValue::Uninitialized; + } else if (ArgType->isFloatingType() || ArgType->isAnyComplexType()) { + return Arg->isEvaluatable(Ctx); + } else if (ArgType->isPointerType() || Arg->isGLValue()) { + LValue LV; + Expr::EvalStatus Status; + EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold); + if ((Arg->isGLValue() ? EvaluateLValue(Arg, LV, Info) + : EvaluatePointer(Arg, LV, Info)) && + !Status.HasSideEffects) + return EvaluateBuiltinConstantPForLValue(LV); } // Anything else isn't considered to be sufficiently constant. @@ -8266,7 +8259,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BI__builtin_constant_p: { auto Arg = E->getArg(0); - if (EvaluateBuiltinConstantP(Info, Arg)) + if (EvaluateBuiltinConstantP(Info.Ctx, Arg)) return Success(true, E); auto ArgTy = Arg->IgnoreImplicit()->getType(); if (!Info.InConstantContext && !Arg->HasSideEffects(Info.Ctx) && -- cgit v1.2.3 From 009d37df084524bc027fa893b47b17faba9815fa Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 27 Apr 2019 02:58:17 +0000 Subject: Reinstate r359059, reverted in r359361, with a fix to properly prevent us emitting the operand of __builtin_constant_p if it has side-effects. Original commit message: Fix interactions between __builtin_constant_p and constexpr to match current trunk GCC. GCC permits information from outside the operand of __builtin_constant_p (but in the same constant evaluation context) to be used within that operand; clang now does so too. A few other minor deviations from GCC's behavior showed up in my testing and are also fixed (matching GCC): * Clang now supports nullptr_t as the argument type for __builtin_constant_p * Clang now returns true from __builtin_constant_p if called with a null pointer * Clang now returns true from __builtin_constant_p if called with an integer cast to pointer type git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@359367 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 79 ++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 36 deletions(-) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index a30e83c632..24b28971af 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -7801,19 +7801,33 @@ EvaluateBuiltinClassifyType(const CallExpr *E, const LangOptions &LangOpts) { } /// EvaluateBuiltinConstantPForLValue - Determine the result of -/// __builtin_constant_p when applied to the given lvalue. +/// __builtin_constant_p when applied to the given pointer. /// -/// An lvalue is only "constant" if it is a pointer or reference to the first -/// character of a string literal. -template -static bool EvaluateBuiltinConstantPForLValue(const LValue &LV) { - const Expr *E = LV.getLValueBase().template dyn_cast(); - return E && isa(E) && LV.getLValueOffset().isZero(); +/// A pointer is only "constant" if it is null (or a pointer cast to integer) +/// or it points to the first character of a string literal. +static bool EvaluateBuiltinConstantPForLValue(const APValue &LV) { + APValue::LValueBase Base = LV.getLValueBase(); + if (Base.isNull()) { + // A null base is acceptable. + return true; + } else if (const Expr *E = Base.dyn_cast()) { + if (!isa(E)) + return false; + return LV.getLValueOffset().isZero(); + } else { + // Any other base is not constant enough for GCC. + return false; + } } /// EvaluateBuiltinConstantP - Evaluate __builtin_constant_p as similarly to /// GCC as we can manage. -static bool EvaluateBuiltinConstantP(ASTContext &Ctx, const Expr *Arg) { +static bool EvaluateBuiltinConstantP(EvalInfo &Info, const Expr *Arg) { + // Constant-folding is always enabled for the operand of __builtin_constant_p + // (even when the enclosing evaluation context otherwise requires a strict + // language-specific constant expression). + FoldConstant Fold(Info, true); + QualType ArgType = Arg->getType(); // __builtin_constant_p always has one operand. The rules which gcc follows @@ -7821,34 +7835,30 @@ static bool EvaluateBuiltinConstantP(ASTContext &Ctx, const Expr *Arg) { // // - If the operand is of integral, floating, complex or enumeration type, // and can be folded to a known value of that type, it returns 1. - // - If the operand and can be folded to a pointer to the first character - // of a string literal (or such a pointer cast to an integral type), it - // returns 1. + // - If the operand can be folded to a pointer to the first character + // of a string literal (or such a pointer cast to an integral type) + // or to a null pointer or an integer cast to a pointer, it returns 1. // // Otherwise, it returns 0. // // FIXME: GCC also intends to return 1 for literals of aggregate types, but - // its support for this does not currently work. - if (ArgType->isIntegralOrEnumerationType()) { - Expr::EvalResult Result; - if (!Arg->EvaluateAsRValue(Result, Ctx) || Result.HasSideEffects) + // its support for this did not work prior to GCC 9 and is not yet well + // understood. + if (ArgType->isIntegralOrEnumerationType() || ArgType->isFloatingType() || + ArgType->isAnyComplexType() || ArgType->isPointerType() || + ArgType->isNullPtrType()) { + APValue V; + if (!::EvaluateAsRValue(Info, Arg, V)) { + Fold.keepDiagnostics(); return false; + } - APValue &V = Result.Val; - if (V.getKind() == APValue::Int) - return true; + // For a pointer (possibly cast to integer), there are special rules. if (V.getKind() == APValue::LValue) return EvaluateBuiltinConstantPForLValue(V); - } else if (ArgType->isFloatingType() || ArgType->isAnyComplexType()) { - return Arg->isEvaluatable(Ctx); - } else if (ArgType->isPointerType() || Arg->isGLValue()) { - LValue LV; - Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold); - if ((Arg->isGLValue() ? EvaluateLValue(Arg, LV, Info) - : EvaluatePointer(Arg, LV, Info)) && - !Status.HasSideEffects) - return EvaluateBuiltinConstantPForLValue(LV); + + // Otherwise, any constant value is good enough. + return V.getKind() != APValue::Uninitialized; } // Anything else isn't considered to be sufficiently constant. @@ -8258,18 +8268,15 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, } case Builtin::BI__builtin_constant_p: { - auto Arg = E->getArg(0); - if (EvaluateBuiltinConstantP(Info.Ctx, Arg)) + const Expr *Arg = E->getArg(0); + if (EvaluateBuiltinConstantP(Info, Arg)) return Success(true, E); - auto ArgTy = Arg->IgnoreImplicit()->getType(); - if (!Info.InConstantContext && !Arg->HasSideEffects(Info.Ctx) && - !ArgTy->isAggregateType() && !ArgTy->isPointerType()) { - // We can delay calculation of __builtin_constant_p until after - // inlining. Note: This diagnostic won't be shown to the user. + else if (Info.InConstantContext) + return Success(false, E); + else { Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); return false; } - return Success(false, E); } case Builtin::BI__builtin_is_constant_evaluated: -- cgit v1.2.3 From b4f79aa4368802682268e2f39a29dd8f9b107b59 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 2 May 2019 23:21:28 +0000 Subject: Fix -Wunsequenced false-positives in code controlled by a branch on __builtin_constant_p. If the operand of __builtin_constant_p is not constant and has side-effects, then code controlled by a branch on it is unreachable and we should not emit runtime behavior warnings in such code. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@359844 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 24b28971af..8386ce8f2d 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -8269,11 +8269,14 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BI__builtin_constant_p: { const Expr *Arg = E->getArg(0); - if (EvaluateBuiltinConstantP(Info, Arg)) + if (EvaluateBuiltinConstantP(Info, Arg)) { return Success(true, E); - else if (Info.InConstantContext) + } else if (Info.InConstantContext || Arg->HasSideEffects(Info.Ctx)) { + // Outside a constant context, eagerly evaluate to false in the presence + // of side-effects in order to avoid -Wunsequenced false-positives in + // a branch on __builtin_constant_p(expr). return Success(false, E); - else { + } else { Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); return false; } -- cgit v1.2.3 From 1801280d2dcb52a20c177f780cd4e1d37d0b5eb3 Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Fri, 3 May 2019 18:11:31 +0000 Subject: Remove else-after-return git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@359913 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 8386ce8f2d..6b3f4dcefa 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -8269,17 +8269,16 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BI__builtin_constant_p: { const Expr *Arg = E->getArg(0); - if (EvaluateBuiltinConstantP(Info, Arg)) { + if (EvaluateBuiltinConstantP(Info, Arg)) return Success(true, E); - } else if (Info.InConstantContext || Arg->HasSideEffects(Info.Ctx)) { + if (Info.InConstantContext || Arg->HasSideEffects(Info.Ctx)) { // Outside a constant context, eagerly evaluate to false in the presence // of side-effects in order to avoid -Wunsequenced false-positives in // a branch on __builtin_constant_p(expr). return Success(false, E); - } else { - Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); - return false; } + Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); + return false; } case Builtin::BI__builtin_is_constant_evaluated: -- cgit v1.2.3 From e9b780230d2dde954d8f6750e05c4b89bc99f0d7 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 4 May 2019 04:00:45 +0000 Subject: Disallow the operand of __builtin_constant_p from modifying enclosing state when it's encountered while evaluating a constexpr function. We attempt to follow GCC trunk's behavior here, but it is somewhat inscrutible, so our behavior is only approximately the same for now. Specifically, we only permit modification of objects whose lifetime began within the operand of the __builtin_constant_p. GCC appears to have effectively the same restriction, but also some unknown restriction based on where and how the local state of the constexpr function is mentioned within the operand (see added testcases). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@359958 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) (limited to 'lib/AST/ExprConstant.cpp') diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 6b3f4dcefa..11e753c077 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -716,6 +716,10 @@ namespace { EvaluatingObject(Decl, {CallIndex, Version})); } + /// If we're currently speculatively evaluating, the outermost call stack + /// depth at which we can mutate state, otherwise 0. + unsigned SpeculativeEvaluationDepth = 0; + /// The current array initialization index, if we're performing array /// initialization. uint64_t ArrayInitIndex = -1; @@ -728,9 +732,6 @@ namespace { /// fold (not just why it's not strictly a constant expression)? bool HasFoldFailureDiagnostic; - /// Whether or not we're currently speculatively evaluating. - bool IsSpeculativelyEvaluating; - /// Whether or not we're in a context where the front end requires a /// constant value. bool InConstantContext; @@ -795,7 +796,7 @@ namespace { BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr), EvaluatingDecl((const ValueDecl *)nullptr), EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), - HasFoldFailureDiagnostic(false), IsSpeculativelyEvaluating(false), + HasFoldFailureDiagnostic(false), InConstantContext(false), EvalMode(Mode) {} void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) { @@ -823,14 +824,20 @@ namespace { return false; } - CallStackFrame *getCallFrame(unsigned CallIndex) { - assert(CallIndex && "no call index in getCallFrame"); + std::pair + getCallFrameAndDepth(unsigned CallIndex) { + assert(CallIndex && "no call index in getCallFrameAndDepth"); // We will eventually hit BottomFrame, which has Index 1, so Frame can't // be null in this loop. + unsigned Depth = CallStackDepth; CallStackFrame *Frame = CurrentCall; - while (Frame->Index > CallIndex) + while (Frame->Index > CallIndex) { Frame = Frame->Caller; - return (Frame->Index == CallIndex) ? Frame : nullptr; + --Depth; + } + if (Frame->Index == CallIndex) + return {Frame, Depth}; + return {nullptr, 0}; } bool nextStep(const Stmt *S) { @@ -1111,12 +1118,12 @@ namespace { class SpeculativeEvaluationRAII { EvalInfo *Info = nullptr; Expr::EvalStatus OldStatus; - bool OldIsSpeculativelyEvaluating; + unsigned OldSpeculativeEvaluationDepth; void moveFromAndCancel(SpeculativeEvaluationRAII &&Other) { Info = Other.Info; OldStatus = Other.OldStatus; - OldIsSpeculativelyEvaluating = Other.OldIsSpeculativelyEvaluating; + OldSpeculativeEvaluationDepth = Other.OldSpeculativeEvaluationDepth; Other.Info = nullptr; } @@ -1125,7 +1132,7 @@ namespace { return; Info->EvalStatus = OldStatus; - Info->IsSpeculativelyEvaluating = OldIsSpeculativelyEvaluating; + Info->SpeculativeEvaluationDepth = OldSpeculativeEvaluationDepth; } public: @@ -1134,9 +1141,9 @@ namespace { SpeculativeEvaluationRAII( EvalInfo &Info, SmallVectorImpl *NewDiag = nullptr) : Info(&Info), OldStatus(Info.EvalStatus), - OldIsSpeculativelyEvaluating(Info.IsSpeculativelyEvaluating) { + OldSpeculativeEvaluationDepth(Info.SpeculativeEvaluationDepth) { Info.EvalStatus.Diag = NewDiag; - Info.IsSpeculativelyEvaluating = true; + Info.SpeculativeEvaluationDepth = Info.CallStackDepth + 1; } SpeculativeEvaluationRAII(const SpeculativeEvaluationRAII &Other) = delete; @@ -3138,8 +3145,10 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, } CallStackFrame *Frame = nullptr; + unsigned Depth = 0; if (LVal.getLValueCallIndex()) { - Frame = Info.getCallFrame(LVal.getLValueCallIndex()); + std::tie(Frame, Depth) = + Info.getCallFrameAndDepth(LVal.getLValueCallIndex()); if (!Frame) { Info.FFDiag(E, diag::note_constexpr_lifetime_ended, 1) << AK << LVal.Base.is(); @@ -3330,7 +3339,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, // to be read here (but take care with 'mutable' fields). if ((Frame && Info.getLangOpts().CPlusPlus14 && Info.EvalStatus.HasSideEffects) || - (AK != AK_Read && Info.IsSpeculativelyEvaluating)) + (AK != AK_Read && Depth < Info.SpeculativeEvaluationDepth)) return CompleteObject(); return CompleteObject(BaseVal, BaseType, LifetimeStartedInEvaluation); @@ -7823,6 +7832,10 @@ static bool EvaluateBuiltinConstantPForLValue(const APValue &LV) { /// EvaluateBuiltinConstantP - Evaluate __builtin_constant_p as similarly to /// GCC as we can manage. static bool EvaluateBuiltinConstantP(EvalInfo &Info, const Expr *Arg) { + // This evaluation is not permitted to have side-effects, so evaluate it in + // a speculative evaluation context. + SpeculativeEvaluationRAII SpeculativeEval(Info); + // Constant-folding is always enabled for the operand of __builtin_constant_p // (even when the enclosing evaluation context otherwise requires a strict // language-specific constant expression). -- cgit v1.2.3