Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -259,29 +259,9 @@ MostDerivedPathLength = Entries.size(); } void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E, uint64_t N); - /// Add N to the address of this subobject. - void adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N) { - if (Invalid) return; - if (MostDerivedPathLength == Entries.size() && MostDerivedArraySize) { - Entries.back().ArrayIndex += N; - if (Entries.back().ArrayIndex > MostDerivedArraySize) { - diagnosePointerArithmetic(Info, E, Entries.back().ArrayIndex); - setInvalid(); - } - return; - } - // [expr.add]p4: For the purposes of these operators, a pointer to a - // nonarray object behaves the same as a pointer to the first element of - // an array of length one with the type of the object as its element type. - if (IsOnePastTheEnd && N == (uint64_t)-1) - IsOnePastTheEnd = false; - else if (!IsOnePastTheEnd && N == 1) - IsOnePastTheEnd = true; - else if (N != 0) { - diagnosePointerArithmetic(Info, E, uint64_t(IsOnePastTheEnd) + N); - setInvalid(); - } - } + + /// Add N to the index of this subobject. + void adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N); }; /// A stack frame in the constexpr call stack. @@ -492,14 +472,42 @@ /// optimizer if we don't constant fold them here, but in an unevaluated /// context we try to fold them immediately since the optimizer never /// gets a chance to look at it. - EM_PotentialConstantExpressionUnevaluated + EM_PotentialConstantExpressionUnevaluated, + + /// Evaluate as a potential constant expression, ignoring any side-effects + /// that may occur. The intent of this mode is to determine an LValue's + /// Offset, so things not ordinarily allowed in constexprs + /// (reinterpret_casts, OOB array indices, etc.) are allowed. As such, the + /// Offset of any given LValue may not be a multiple of the LValue's + /// type's size (e.g. in + /// short s[2]; + /// char *p = (char*)s + 1;) + EM_OffsetFold, + + /// Identical to EM_OffsetFold, but we're evaluating as though the + /// expression is a constant expression. + EM_ConstantExpressionOffsetFold, + + /// Identical to OffsetFold, except we should only evaluate for + /// subobjects. Containers are assumed to be valid and are otherwise + /// ignored. NOTE: At the moment, this may result in LValues set from + /// prvalue Exprs. + EM_PartialOffsetFold, + + /// Identical to ConstantExpressionOffsetFold, except we should only + /// evaluate for subobjects. Containers are assumed to be valid and are + /// otherwise ignored. NOTE: At the moment, this may result in LValues set + /// from prvalue Exprs. + EM_ConstantExpressionPartialOffsetFold, } EvalMode; /// Are we checking whether the expression is a potential constant /// expression? bool checkingPotentialConstantExpression() const { return EvalMode == EM_PotentialConstantExpression || - EvalMode == EM_PotentialConstantExpressionUnevaluated; + EvalMode == EM_PotentialConstantExpressionUnevaluated || + EvalMode == EM_OffsetFold || + EvalMode == EM_PartialOffsetFold; } /// Are we checking an expression for overflow? @@ -595,6 +603,10 @@ case EM_PotentialConstantExpression: case EM_ConstantExpressionUnevaluated: case EM_PotentialConstantExpressionUnevaluated: + case EM_OffsetFold: + case EM_PartialOffsetFold: + case EM_ConstantExpressionOffsetFold: + case EM_ConstantExpressionPartialOffsetFold: HasActiveDiagnostic = false; return OptionalDiagnostic(); } @@ -669,11 +681,15 @@ case EM_PotentialConstantExpressionUnevaluated: case EM_EvaluateForOverflow: case EM_IgnoreSideEffects: + case EM_OffsetFold: + case EM_PartialOffsetFold: return true; case EM_ConstantExpression: case EM_ConstantExpressionUnevaluated: case EM_ConstantFold: + case EM_ConstantExpressionOffsetFold: + case EM_ConstantExpressionPartialOffsetFold: return false; } llvm_unreachable("Missed EvalMode case"); @@ -696,16 +712,46 @@ case EM_PotentialConstantExpression: case EM_PotentialConstantExpressionUnevaluated: case EM_EvaluateForOverflow: + case EM_OffsetFold: + case EM_PartialOffsetFold: return true; case EM_ConstantExpression: case EM_ConstantExpressionUnevaluated: case EM_ConstantFold: case EM_IgnoreSideEffects: + case EM_ConstantExpressionOffsetFold: + case EM_ConstantExpressionPartialOffsetFold: return false; } llvm_unreachable("Missed EvalMode case"); } + + bool allowReinterpretCasts() const { + return EvalMode == EM_OffsetFold || + EvalMode == EM_PartialOffsetFold || + EvalMode == EM_ConstantExpressionOffsetFold || + EvalMode == EM_ConstantExpressionPartialOffsetFold; + } + + bool allowOutOfBoundsIndices() const { + return EvalMode == EM_OffsetFold || + EvalMode == EM_PartialOffsetFold || + EvalMode == EM_ConstantExpressionOffsetFold || + EvalMode == EM_ConstantExpressionPartialOffsetFold; + } + + bool allowNonObjectBoundaryOffsets() const { + return EvalMode == EM_OffsetFold || + EvalMode == EM_PartialOffsetFold || + EvalMode == EM_ConstantExpressionOffsetFold || + EvalMode == EM_ConstantExpressionPartialOffsetFold; + } + + bool assumeBaseExprIsValid() const { + return EvalMode == EM_PartialOffsetFold || + EvalMode == EM_ConstantExpressionPartialOffsetFold; + } }; /// Object used to treat all foldable expressions as constant expressions. @@ -736,6 +782,29 @@ } }; + /// RAII object used to treat the current evaluation as the correct pointer + /// offset fold for the current EvalMode + struct FoldOffsetRAII { + EvalInfo &Info; + EvalInfo::EvaluationMode OldMode; + explicit FoldOffsetRAII(EvalInfo &Info, bool PartialEval) + : Info(Info), OldMode(Info.EvalMode) { + if (Info.checkingPotentialConstantExpression()) { + if (PartialEval) + Info.EvalMode = EvalInfo::EM_PartialOffsetFold; + else + Info.EvalMode = EvalInfo::EM_OffsetFold; + return; + } + if (PartialEval) + Info.EvalMode = EvalInfo::EM_ConstantExpressionPartialOffsetFold; + else + Info.EvalMode = EvalInfo::EM_ConstantExpressionOffsetFold; + } + + ~FoldOffsetRAII() { Info.EvalMode = OldMode; } + }; + /// RAII object used to suppress diagnostics and side-effects from a /// speculative evaluation. class SpeculativeEvaluationRAII { @@ -818,6 +887,30 @@ setInvalid(); } +void SubobjectDesignator::adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N) { + if (Invalid) return; + if (MostDerivedPathLength == Entries.size() && MostDerivedArraySize) { + Entries.back().ArrayIndex += N; + if (!Info.allowOutOfBoundsIndices() && + Entries.back().ArrayIndex > MostDerivedArraySize) { + diagnosePointerArithmetic(Info, E, Entries.back().ArrayIndex); + setInvalid(); + } + return; + } + // [expr.add]p4: For the purposes of these operators, a pointer to a + // nonarray object behaves the same as a pointer to the first element of + // an array of length one with the type of the object as its element type. + if (IsOnePastTheEnd && N == (uint64_t)-1) + IsOnePastTheEnd = false; + else if (!IsOnePastTheEnd && N == 1) + IsOnePastTheEnd = true; + else if (N != 0) { + diagnosePointerArithmetic(Info, E, uint64_t(IsOnePastTheEnd) + N); + setInvalid(); + } +} + CallStackFrame::CallStackFrame(EvalInfo &Info, SourceLocation CallLoc, const FunctionDecl *Callee, const LValue *This, APValue *Arguments) @@ -1905,8 +1998,33 @@ return false; // Compute the new offset in the appropriate width. - LVal.Offset += Adjustment * SizeOfPointee; - LVal.adjustIndex(Info, E, Adjustment); + CharUnits AddedOffset = Adjustment * SizeOfPointee; + int64_t IndexAdjustment = Adjustment; + + // If we allow offsets that aren't on object boundaries, we need to take + // into account any additional offsets that aren't fully accounted for + // (already) by indices + if (Info.allowNonObjectBoundaryOffsets()) { + QualType ArrayType = LVal.Designator.MostDerivedType; + // If ArrayType is null, we're offsetting a constant, not an array index. + if (!ArrayType.isNull() && !ArrayType->isIncompleteType() && + ArrayType != EltTy) { + CharUnits SizeOfArray; + if (!HandleSizeof(Info, E->getExprLoc(), ArrayType, SizeOfArray)) + return false; + + if (SizeOfArray != SizeOfPointee) { + CharUnits OffBoundary = + CharUnits::fromQuantity(LVal.Offset % SizeOfArray); + AddedOffset += OffBoundary; + LVal.Offset -= OffBoundary; + IndexAdjustment = AddedOffset / SizeOfArray; + } + } + } + + LVal.Offset += AddedOffset; + LVal.adjustIndex(Info, E, IndexAdjustment); return true; } @@ -4189,10 +4307,14 @@ /// A member expression where the object is a prvalue is itself a prvalue. bool VisitMemberExpr(const MemberExpr *E) { assert(!E->isArrow() && "missing call to bound member function?"); - APValue Val; - if (!Evaluate(Val, Info, E->getBase())) - return false; + if (!Evaluate(Val, Info, E->getBase())) { + if (!Info.assumeBaseExprIsValid()) + return false; + CharUnits Offset = CharUnits::Zero(); + APValue::NoLValuePath NoPath; + Val.setLValue(E->getBase(), Offset, NoPath, 0); + } QualType BaseTy = E->getBase()->getType(); @@ -4340,20 +4462,23 @@ bool VisitMemberExpr(const MemberExpr *E) { // Handle non-static data members. QualType BaseTy; + bool EvalOK; if (E->isArrow()) { - if (!EvaluatePointer(E->getBase(), Result, this->Info)) - return false; + EvalOK = EvaluatePointer(E->getBase(), Result, this->Info); BaseTy = E->getBase()->getType()->castAs()->getPointeeType(); } else if (E->getBase()->isRValue()) { assert(E->getBase()->getType()->isRecordType()); - if (!EvaluateTemporary(E->getBase(), Result, this->Info)) - return false; + EvalOK = EvaluateTemporary(E->getBase(), Result, this->Info); BaseTy = E->getBase()->getType(); } else { - if (!this->Visit(E->getBase())) - return false; + EvalOK = this->Visit(E->getBase()); BaseTy = E->getBase()->getType(); } + if (!EvalOK) { + if (!this->Info.assumeBaseExprIsValid()) + return false; + Result.set(E->getBase()); + } const ValueDecl *MD = E->getMemberDecl(); if (const FieldDecl *FD = dyn_cast(E->getMemberDecl())) { @@ -4623,13 +4748,14 @@ } bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) { - // Handle static data members. + if (Info.assumeBaseExprIsValid()) + return Success(E); + if (const VarDecl *VD = dyn_cast(E->getMemberDecl())) { VisitIgnoredValue(E->getBase()); return VisitVarDecl(E, VD); } - // Handle static member functions. if (const CXXMethodDecl *MD = dyn_cast(E->getMemberDecl())) { if (MD->isStatic()) { VisitIgnoredValue(E->getBase()); @@ -4747,7 +4873,6 @@ return true; } public: - PointerExprEvaluator(EvalInfo &info, LValue &Result) : ExprEvaluatorBaseTy(info), Result(Result) {} @@ -4765,7 +4890,7 @@ bool VisitObjCStringLiteral(const ObjCStringLiteral *E) { return Success(E); } bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E) - { return Success(E); } + { return Success(E); } bool VisitAddrLabelExpr(const AddrLabelExpr *E) { return Success(E); } bool VisitCallExpr(const CallExpr *E); @@ -4846,7 +4971,7 @@ // Bitcasts to cv void* are static_casts, not reinterpret_casts, so are // permitted in constant expressions in C++11. Bitcasts from cv void* are // also static_casts, but we disallow them as a resolution to DR1312. - if (!E->getType()->isVoidPointerType()) { + if (!Info.allowReinterpretCasts() && !E->getType()->isVoidPointerType()) { Result.Designator.setInvalid(); if (SubExpr->getType()->isVoidPointerType()) CCEDiag(E, diag::note_constexpr_invalid_cast) @@ -6165,7 +6290,7 @@ /// Retrieves the "underlying object type" of the given expression, /// as used by __builtin_object_size. -static QualType getObjectType(APValue::LValueBase B) { +static QualType GetObjectType(APValue::LValueBase B) { if (const ValueDecl *D = B.dyn_cast()) { if (const VarDecl *VD = dyn_cast(D)) return VD->getType(); @@ -6185,17 +6310,17 @@ // The operand of __builtin_object_size is never evaluated for side-effects. // If there are any, but we can determine the pointed-to object anyway, then // ignore the side-effects. + bool Subobject = Type & 1; SpeculativeEvaluationRAII SpeculativeEval(Info); - FoldConstant Fold(Info, true); + FoldOffsetRAII Fold(Info, Subobject); if (!EvaluatePointer(E->getArg(0), Base, Info)) return false; } CharUnits BaseOffset = Base.getLValueOffset(); - - // If we point to before the start of the object, there are no - // accessible bytes. - if (BaseOffset < CharUnits::Zero()) + // If we point to before the start of the object, there are no accessible + // bytes. + if (BaseOffset.isNegative()) return Success(0, E); // MostDerivedType is null if we're dealing with a literal such as nullptr or @@ -6208,12 +6333,12 @@ // If Type & 1 is 0, the object in question is the complete object; reset to // a complete object designator in that case. // - // If Type is 1 and we've lost track of the subobject, just find the complete - // object instead. (If Type is 3, that's not correct behavior and we should - // return 0 instead.) + // We can't pretend that Type is 0 if Type is 1 and the Designator is invalid, + // because the LValue's Base may be invalid as well. This is because we + // executed a partial offset fold in EvaluatePointer. LValue End = Base; - if (((Type & 1) == 0) || (End.Designator.Invalid && Type == 1)) { - QualType T = getObjectType(End.getLValueBase()); + if ((Type & 1) == 0) { + QualType T = GetObjectType(End.getLValueBase()); if (T.isNull()) End.Designator.setInvalid(); else { @@ -6222,15 +6347,14 @@ } } - // FIXME: We should produce a valid object size for an unknown object with a - // known designator, if Type & 1 is 1. For instance: + // We produce a valid object size for an unknown object with a known + // designator, if Type & 1 is 1. For instance: // // extern struct X { char buff[32]; int a, b, c; } *p; // int a = __builtin_object_size(p->buff + 4, 3); // returns 28 // int b = __builtin_object_size(p->buff + 4, 2); // returns 0, not 40 // - // This is GCC's behavior. We currently don't do this, but (hopefully) will in - // the near future. + // This matches GCC's behavior. // If it is not possible to determine which objects ptr points to at compile // time, __builtin_object_size should return (size_t) -1 for type 0 or 1 @@ -6244,23 +6368,29 @@ int64_t AmountToAdd = 1; if (End.Designator.MostDerivedArraySize && End.Designator.Entries.size() == End.Designator.MostDerivedPathLength) { - // We got a pointer to an array. Step to its end. + // We got a pointer to an array. Step to its end. Note that this can be + // negative (because array indices can be negative/OOB). AmountToAdd = End.Designator.MostDerivedArraySize - - End.Designator.Entries.back().ArrayIndex; - } else if (End.Designator.IsOnePastTheEnd) { + End.Designator.Entries.back().ArrayIndex; + } else if (End.Designator.isOnePastTheEnd()) { // We're already pointing at the end of the object. AmountToAdd = 0; } - if (End.Designator.MostDerivedType->isIncompleteType() || - End.Designator.MostDerivedType->isFunctionType()) + QualType PointeeType = End.Designator.MostDerivedType; + assert(!PointeeType.isNull()); + if (PointeeType->isIncompleteType() || PointeeType->isFunctionType()) return Error(E); - if (!HandleLValueArrayAdjustment(Info, E, End, End.Designator.MostDerivedType, - AmountToAdd)) + CharUnits SizeOfPointee; + if (!HandleSizeof(Info, E->getExprLoc(), PointeeType, SizeOfPointee)) return false; - auto EndOffset = End.getLValueOffset(); + CharUnits EndOffset = End.getLValueOffset(); + // If Start's Designator is offset from an object boundary, End's is, as well. + // We need to account for this. + EndOffset += SizeOfPointee * AmountToAdd; + EndOffset -= CharUnits::fromQuantity(EndOffset % SizeOfPointee); if (BaseOffset > EndOffset) return Success(0, E); @@ -6297,6 +6427,10 @@ case EvalInfo::EM_ConstantFold: case EvalInfo::EM_EvaluateForOverflow: case EvalInfo::EM_IgnoreSideEffects: + case EvalInfo::EM_OffsetFold: + case EvalInfo::EM_PartialOffsetFold: + case EvalInfo::EM_ConstantExpressionOffsetFold: + case EvalInfo::EM_ConstantExpressionPartialOffsetFold: // Leave it to IR generation. return Error(E); case EvalInfo::EM_ConstantExpressionUnevaluated: Index: test/CodeGen/object-size.c =================================================================== --- test/CodeGen/object-size.c +++ test/CodeGen/object-size.c @@ -161,6 +161,15 @@ gi = __builtin_object_size(&foo.a, 2); // CHECK: store i32 4 gi = __builtin_object_size(&foo.a, 3); + + // CHECK: store i32 4 + gi = __builtin_object_size(&foo.b, 0); + // CHECK: store i32 4 + gi = __builtin_object_size(&foo.b, 1); + // CHECK: store i32 4 + gi = __builtin_object_size(&foo.b, 2); + // CHECK: store i32 4 + gi = __builtin_object_size(&foo.b, 3); } // CHECK: @test20 @@ -221,25 +230,59 @@ gi = __builtin_object_size(&t[9].t[10], 2); // CHECK: store i32 0 gi = __builtin_object_size(&t[9].t[10], 3); + + // CHECK: store i32 0 + gi = __builtin_object_size((char*)&t[0] + sizeof(t), 0); + // CHECK: store i32 0 + gi = __builtin_object_size((char*)&t[0] + sizeof(t), 1); + // CHECK: store i32 0 + gi = __builtin_object_size((char*)&t[0] + sizeof(t), 2); + // CHECK: store i32 0 + gi = __builtin_object_size((char*)&t[0] + sizeof(t), 3); + + // CHECK: store i32 0 + gi = __builtin_object_size((char*)&t[9].t[0] + 10*sizeof(t[0].t), 0); + // CHECK: store i32 0 + gi = __builtin_object_size((char*)&t[9].t[0] + 10*sizeof(t[0].t), 1); + // CHECK: store i32 0 + gi = __builtin_object_size((char*)&t[9].t[0] + 10*sizeof(t[0].t), 2); + // CHECK: store i32 0 + gi = __builtin_object_size((char*)&t[9].t[0] + 10*sizeof(t[0].t), 3); } -struct Test23Ty { int t[10]; }; +struct Test23Ty { int a; int t[10]; }; // CHECK: @test23 -void test23(struct Test22Ty *p) { +void test23(struct Test23Ty *p) { // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false) gi = __builtin_object_size(p, 0); // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false) gi = __builtin_object_size(p, 1); // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true) gi = __builtin_object_size(p, 2); - // Note: this is currently fixed at 0 because LLVM doesn't have sufficient // data to correctly handle type=3 // CHECK: store i32 0 gi = __builtin_object_size(p, 3); -} + // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false) + gi = __builtin_object_size(&p->a, 0); + // CHECK: store i32 4 + gi = __builtin_object_size(&p->a, 1); + // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true) + gi = __builtin_object_size(&p->a, 2); + // CHECK: store i32 4 + gi = __builtin_object_size(&p->a, 3); + + // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false) + gi = __builtin_object_size(&p->t[5], 0); + // CHECK: store i32 20 + gi = __builtin_object_size(&p->t[5], 1); + // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true) + gi = __builtin_object_size(&p->t[5], 2); + // CHECK: store i32 20 + gi = __builtin_object_size(&p->t[5], 3); +} // PR24493 -- ICE if __builtin_object_size called with NULL and (Type & 1) != 0 // CHECK @test24 @@ -280,3 +323,72 @@ // CHECK: store i32 0 gi = __builtin_object_size((void*)0 + 0x1000, 3); } + +// CHECK: @test26 +void test26(struct Test23Ty *p) { + struct { int t[10]; } t[10]; + + // CHECK: store i32 356 + gi = __builtin_object_size((char*)&t[1].t[1], 0); + // CHECK: store i32 36 + gi = __builtin_object_size((char*)&t[1].t[1], 1); + // CHECK: store i32 356 + gi = __builtin_object_size((char*)&t[1].t[1], 2); + // CHECK: store i32 36 + gi = __builtin_object_size((char*)&t[1].t[1], 3); +} + +// CHECK: @test27 +void test27() { + struct { int t[10]; } t[10]; + + // CHECK: store i32 359 + gi = __builtin_object_size((char*)&t[1].t[0]+1, 0); + // CHECK: store i32 39 + gi = __builtin_object_size((char*)&t[1].t[0]+1, 1); + // CHECK: store i32 359 + gi = __builtin_object_size((char*)&t[1].t[0]+1, 2); + // CHECK: store i32 39 + gi = __builtin_object_size((char*)&t[1].t[0]+1, 3); +} + +// CHECK: @test28 +void test28() { + struct { int v[10]; } t[10]; + + // CHECK: store i32 356 + gi = __builtin_object_size(&t[0].v[11], 0); + // CHECK: store i32 0 + gi = __builtin_object_size(&t[0].v[12], 1); + // CHECK: store i32 348 + gi = __builtin_object_size(&t[0].v[13], 2); + // CHECK: store i32 0 + gi = __builtin_object_size(&t[0].v[14], 3); +} + +struct Test29IncompleteTy; + +// CHECK: @test29 +void test29(struct Test29IncompleteTy *t) { + // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false) + gi = __builtin_object_size(t, 0); + // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false) + gi = __builtin_object_size(t, 1); + // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true) + gi = __builtin_object_size(t, 2); + // Note: this is currently fixed at 0 because LLVM doesn't have sufficient + // data to correctly handle type=3 + // CHECK: store i32 0 + gi = __builtin_object_size(t, 3); + + // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* {{.*}}, i1 false) + gi = __builtin_object_size(&test29, 0); + // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* {{.*}}, i1 false) + gi = __builtin_object_size(&test29, 1); + // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* {{.*}}, i1 true) + gi = __builtin_object_size(&test29, 2); + // Note: this is currently fixed at 0 because LLVM doesn't have sufficient + // data to correctly handle type=3 + // CHECK: store i32 0 + gi = __builtin_object_size(&test29, 3); +}