george.burgess.iv updated this revision to Diff 32985.
george.burgess.iv added a comment.
Mostly redid the patch; removed OutermostExprEvaluator, moved evaluation intent
into `EvaluationMode`, made it so the more interesting rulebreaking that
`__builtin_object_size` allows is only allowed when evaluating
`__builtin_object_size`.
Not entirely sold on adding 4 EvaluationModes, but we need to know two bits of
information, both of which impact how we evaluate things:
- Whether we're evaluating an actual ConstantExpression or a
PotentialConstantExpression (if we always assume the former, we get memory
corruption by reaching into `CallStackFrame::Arguments` sometimes; if we assume
the latter, `attribute(enable_if)` breaks).
- If we're allowed to assume that the base of a `MemberExpr` is valid.
Also, I'm not 100% on calling `LValue.set(MaybeAnRValue)` if we are assuming
that the base of a `MemberExpr` is valid. However, in such a case, I would
think that it's safe to assume that the caller doesn't care about the base.
Plus, it's In The Documentation(TM) that you shouldn't rely on
`Result.getLValueBase()` being a valid base when the EvalMode would allow this
assumption to be made.
http://reviews.llvm.org/D12169
Files:
lib/AST/ExprConstant.cpp
test/CodeGen/object-size.c
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);
+}
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<PointerType>()->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<FieldDecl>(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<VarDecl>(E->getMemberDecl())) {
VisitIgnoredValue(E->getBase());
return VisitVarDecl(E, VD);
}
- // Handle static member functions.
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(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<const ValueDecl*>()) {
if (const VarDecl *VD = dyn_cast<VarDecl>(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,29 +6333,28 @@
// 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 {
End.Designator = SubobjectDesignator(T);
End.Offset = CharUnits::Zero();
}
}
- // 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:
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits