<div dir="ltr"><div>> <span style="font-size:12.8px">It might be reasonable for __builtin_object_size(..., 2) to give up if [...]</span></div><div><br></div>That sounds like the best overall solution to me, as well. Working on a fix now.</div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Sep 11, 2015 at 1:22 PM, Richard Smith <span dir="ltr"><<a href="mailto:richard@metafoo.co.uk" target="_blank">richard@metafoo.co.uk</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><span class="">On Fri, Sep 11, 2015 at 12:15 PM, Mikhail Zolotukhin via cfe-commits <span dir="ltr"><<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word">Hi George,<div><br></div><div>After this commit we started to trap on the following case:</div><div><div><br></div><div><font face="Menlo">#include <string.h></font></div><div><font face="Menlo">typedef struct {</font></div><div><font face="Menlo"> int n;</font></div><div><font face="Menlo"> char key[1];</font></div><div><font face="Menlo">} obj_t;</font></div><div><font face="Menlo"><br></font></div><div><font face="Menlo">char *str = "hello";</font></div><div><font face="Menlo"><br></font></div><div><font face="Menlo">int main() {</font></div><div><font face="Menlo"> obj_t* p = (obj_t*)malloc(strlen(str) + 1 + sizeof(int));</font></div><div><font face="Menlo"> strcpy(p->key, str);</font></div><div><font face="Menlo"> free(p);</font></div><div><font face="Menlo"> return 0;</font></div><div><font face="Menlo">}</font></div></div><div><br></div><div>As far as I understand, this might be a common pattern in pre-C99 codebase, and it fails when compiled with -D_FORTIFY_SOURCE=2. Is there a way we could fix it in clang, or the only fix is to use less strict FORTIFY_SOURCE level?</div></div></blockquote><div><br></div></span><div>It might be reasonable for __builtin_object_size(..., 2) to give up if:</div><div><br></div><div>1) we lost track of the complete object, and</div><div>2) the designator refers to the final subobject of the currently-known complete object, and</div><div>3) that subobject is either a flexible array member or an array of bound 0 or 1.</div><div><br></div><div>Then we'd leave it to IR generation to do the llvm.object.size(..., i1 false) dance, and in this case to grab the size of the malloc (minus the offset of 'key').</div><div><div class="h5"><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><div>Thanks,</div><div>Michael</div><div><div><div><br><div><blockquote type="cite"><div>On Sep 4, 2015, at 2:28 PM, George Burgess IV via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>> wrote:</div><br><div><div>Author: gbiv<br>Date: Fri Sep 4 16:28:13 2015<br>New Revision: 246877<br><br>URL: <a href="http://llvm.org/viewvc/llvm-project?rev=246877&view=rev" target="_blank">http://llvm.org/viewvc/llvm-project?rev=246877&view=rev</a><br>Log:<br>Increase accuracy of __builtin_object_size.<br><br>Improvements:<br><br>- For all types, we would give up in a case such as:<br> __builtin_object_size((char*)&foo, N);<br> even if we could provide an answer to<br> __builtin_object_size(&foo, N);<br> We now provide the same answer for both of the above examples in all<br> cases.<br><br>- For type=1|3, we now support subobjects with unknown bases, as long<br> as the designator is valid.<br><br>Thanks to Richard Smith for the review + design planning.<br><br>Review: <a href="http://reviews.llvm.org/D12169" target="_blank">http://reviews.llvm.org/D12169</a><br><br><br>Modified:<br> cfe/trunk/lib/AST/ExprConstant.cpp<br> cfe/trunk/test/CodeGen/object-size.c<br><br>Modified: cfe/trunk/lib/AST/ExprConstant.cpp<br>URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=246877&r1=246876&r2=246877&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=246877&r1=246876&r2=246877&view=diff</a><br>==============================================================================<br>--- cfe/trunk/lib/AST/ExprConstant.cpp (original)<br>+++ cfe/trunk/lib/AST/ExprConstant.cpp Fri Sep 4 16:28:13 2015<br>@@ -492,7 +492,11 @@ namespace {<br> /// optimizer if we don't constant fold them here, but in an unevaluated<br> /// context we try to fold them immediately since the optimizer never<br> /// gets a chance to look at it.<br>- EM_PotentialConstantExpressionUnevaluated<br>+ EM_PotentialConstantExpressionUnevaluated,<br>+<br>+ /// Evaluate as a constant expression. Continue evaluating if we find a<br>+ /// MemberExpr with a base that can't be evaluated.<br>+ EM_DesignatorFold,<br> } EvalMode;<br><br> /// Are we checking whether the expression is a potential constant<br>@@ -595,6 +599,7 @@ namespace {<br> case EM_PotentialConstantExpression:<br> case EM_ConstantExpressionUnevaluated:<br> case EM_PotentialConstantExpressionUnevaluated:<br>+ case EM_DesignatorFold:<br> HasActiveDiagnostic = false;<br> return OptionalDiagnostic();<br> }<br>@@ -674,6 +679,7 @@ namespace {<br> case EM_ConstantExpression:<br> case EM_ConstantExpressionUnevaluated:<br> case EM_ConstantFold:<br>+ case EM_DesignatorFold:<br> return false;<br> }<br> llvm_unreachable("Missed EvalMode case");<br>@@ -702,10 +708,15 @@ namespace {<br> case EM_ConstantExpressionUnevaluated:<br> case EM_ConstantFold:<br> case EM_IgnoreSideEffects:<br>+ case EM_DesignatorFold:<br> return false;<br> }<br> llvm_unreachable("Missed EvalMode case");<br> }<br>+<br>+ bool allowInvalidBaseExpr() const {<br>+ return EvalMode == EM_DesignatorFold;<br>+ }<br> };<br><br> /// Object used to treat all foldable expressions as constant expressions.<br>@@ -736,6 +747,21 @@ namespace {<br> }<br> };<br><br>+ /// RAII object used to treat the current evaluation as the correct pointer<br>+ /// offset fold for the current EvalMode<br>+ struct FoldOffsetRAII {<br>+ EvalInfo &Info;<br>+ EvalInfo::EvaluationMode OldMode;<br>+ explicit FoldOffsetRAII(EvalInfo &Info, bool Subobject)<br>+ : Info(Info), OldMode(Info.EvalMode) {<br>+ if (!Info.checkingPotentialConstantExpression())<br>+ Info.EvalMode = Subobject ? EvalInfo::EM_DesignatorFold<br>+ : EvalInfo::EM_ConstantFold;<br>+ }<br>+<br>+ ~FoldOffsetRAII() { Info.EvalMode = OldMode; }<br>+ };<br>+<br> /// RAII object used to suppress diagnostics and side-effects from a<br> /// speculative evaluation.<br> class SpeculativeEvaluationRAII {<br>@@ -917,7 +943,8 @@ namespace {<br> struct LValue {<br> APValue::LValueBase Base;<br> CharUnits Offset;<br>- unsigned CallIndex;<br>+ bool InvalidBase : 1;<br>+ unsigned CallIndex : 31;<br> SubobjectDesignator Designator;<br><br> const APValue::LValueBase getLValueBase() const { return Base; }<br>@@ -938,17 +965,23 @@ namespace {<br> assert(V.isLValue());<br> Base = V.getLValueBase();<br> Offset = V.getLValueOffset();<br>+ InvalidBase = false;<br> CallIndex = V.getLValueCallIndex();<br> Designator = SubobjectDesignator(Ctx, V);<br> }<br><br>- void set(APValue::LValueBase B, unsigned I = 0) {<br>+ void set(APValue::LValueBase B, unsigned I = 0, bool BInvalid = false) {<br> Base = B;<br> Offset = CharUnits::Zero();<br>+ InvalidBase = BInvalid;<br> CallIndex = I;<br> Designator = SubobjectDesignator(getType(B));<br> }<br><br>+ void setInvalid(APValue::LValueBase B, unsigned I = 0) {<br>+ set(B, I, true);<br>+ }<br>+<br> // Check that this LValue is not based on a null pointer. If it is, produce<br> // a diagnostic and mark the designator as invalid.<br> bool checkNullPointer(EvalInfo &Info, const Expr *E,<br>@@ -4368,20 +4401,23 @@ public:<br> bool VisitMemberExpr(const MemberExpr *E) {<br> // Handle non-static data members.<br> QualType BaseTy;<br>+ bool EvalOK;<br> if (E->isArrow()) {<br>- if (!EvaluatePointer(E->getBase(), Result, this->Info))<br>- return false;<br>+ EvalOK = EvaluatePointer(E->getBase(), Result, this->Info);<br> BaseTy = E->getBase()->getType()->castAs<PointerType>()->getPointeeType();<br> } else if (E->getBase()->isRValue()) {<br> assert(E->getBase()->getType()->isRecordType());<br>- if (!EvaluateTemporary(E->getBase(), Result, this->Info))<br>- return false;<br>+ EvalOK = EvaluateTemporary(E->getBase(), Result, this->Info);<br> BaseTy = E->getBase()->getType();<br> } else {<br>- if (!this->Visit(E->getBase()))<br>- return false;<br>+ EvalOK = this->Visit(E->getBase());<br> BaseTy = E->getBase()->getType();<br> }<br>+ if (!EvalOK) {<br>+ if (!this->Info.allowInvalidBaseExpr())<br>+ return false;<br>+ Result.setInvalid(E->getBase());<br>+ }<br><br> const ValueDecl *MD = E->getMemberDecl();<br> if (const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl())) {<br>@@ -4793,7 +4829,7 @@ public:<br> bool VisitObjCStringLiteral(const ObjCStringLiteral *E)<br> { return Success(E); }<br> bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E)<br>- { return Success(E); } <br>+ { return Success(E); }<br> bool VisitAddrLabelExpr(const AddrLabelExpr *E)<br> { return Success(E); }<br> bool VisitCallExpr(const CallExpr *E);<br>@@ -4919,6 +4955,7 @@ bool PointerExprEvaluator::VisitCastExpr<br> unsigned Size = Info.Ctx.getTypeSize(E->getType());<br> uint64_t N = Value.getInt().extOrTrunc(Size).getZExtValue();<br> Result.Base = (Expr*)nullptr;<br>+ Result.InvalidBase = false;<br> Result.Offset = CharUnits::fromQuantity(N);<br> Result.CallIndex = 0;<br> Result.Designator.setInvalid();<br>@@ -6211,6 +6248,26 @@ static QualType getObjectType(APValue::L<br> return QualType();<br> }<br><br>+/// A more selective version of E->IgnoreParenCasts for<br>+/// TryEvaluateBuiltinObjectSize. This ignores casts/parens that serve only to<br>+/// change the type of E.<br>+/// Ex. For E = `(short*)((char*)(&foo))`, returns `&foo`<br>+///<br>+/// Always returns an RValue with a pointer representation.<br>+static const Expr *ignorePointerCastsAndParens(const Expr *E) {<br>+ assert(E->isRValue() && E->getType()->hasPointerRepresentation());<br>+<br>+ auto *NoParens = E->IgnoreParens();<br>+ auto *Cast = dyn_cast<CastExpr>(NoParens);<br>+ if (Cast == nullptr || Cast->getCastKind() == CK_DerivedToBase)<br>+ return NoParens;<br>+<br>+ auto *SubExpr = Cast->getSubExpr();<br>+ if (!SubExpr->getType()->hasPointerRepresentation() || !SubExpr->isRValue())<br>+ return NoParens;<br>+ return ignorePointerCastsAndParens(SubExpr);<br>+}<br>+<br> bool IntExprEvaluator::TryEvaluateBuiltinObjectSize(const CallExpr *E,<br> unsigned Type) {<br> // Determine the denoted object.<br>@@ -6220,23 +6277,35 @@ bool IntExprEvaluator::TryEvaluateBuilti<br> // If there are any, but we can determine the pointed-to object anyway, then<br> // ignore the side-effects.<br> SpeculativeEvaluationRAII SpeculativeEval(Info);<br>- FoldConstant Fold(Info, true);<br>- if (!EvaluatePointer(E->getArg(0), Base, Info))<br>+ FoldOffsetRAII Fold(Info, Type & 1);<br>+ const Expr *Ptr = ignorePointerCastsAndParens(E->getArg(0));<br>+ if (!EvaluatePointer(Ptr, Base, Info))<br> return false;<br> }<br><br> CharUnits BaseOffset = Base.getLValueOffset();<br>-<br>- // If we point to before the start of the object, there are no<br>- // accessible bytes.<br>- if (BaseOffset < CharUnits::Zero())<br>+ // If we point to before the start of the object, there are no accessible<br>+ // bytes.<br>+ if (BaseOffset.isNegative())<br> return Success(0, E);<br><br>- // MostDerivedType is null if we're dealing with a literal such as nullptr or<br>- // (char*)0x100000. Lower it to LLVM in either case so it can figure out what<br>- // to do with it.<br>- // FIXME(gbiv): Try to do a better job with this in clang.<br>- if (Base.Designator.MostDerivedType.isNull())<br>+ // In the case where we're not dealing with a subobject, we discard the<br>+ // subobject bit.<br>+ if (!Base.Designator.Invalid && Base.Designator.Entries.empty())<br>+ Type = Type & ~1U;<br>+<br>+ // If Type & 1 is 0, we need to be able to statically guarantee that the bytes<br>+ // exist. If we can't verify the base, then we can't do that.<br>+ //<br>+ // As a special case, we produce a valid object size for an unknown object<br>+ // with a known designator if Type & 1 is 1. For instance:<br>+ //<br>+ // extern struct X { char buff[32]; int a, b, c; } *p;<br>+ // int a = __builtin_object_size(p->buff + 4, 3); // returns 28<br>+ // int b = __builtin_object_size(p->buff + 4, 2); // returns 0, not 40<br>+ //<br>+ // This matches GCC's behavior.<br>+ if ((Type & 1) == 0 && Base.InvalidBase)<br> return Error(E);<br><br> // If Type & 1 is 0, the object in question is the complete object; reset to<br>@@ -6256,16 +6325,6 @@ bool IntExprEvaluator::TryEvaluateBuilti<br> }<br> }<br><br>- // FIXME: We should produce a valid object size for an unknown object with a<br>- // known designator, if Type & 1 is 1. For instance:<br>- //<br>- // extern struct X { char buff[32]; int a, b, c; } *p;<br>- // int a = __builtin_object_size(p->buff + 4, 3); // returns 28<br>- // int b = __builtin_object_size(p->buff + 4, 2); // returns 0, not 40<br>- //<br>- // This is GCC's behavior. We currently don't do this, but (hopefully) will in<br>- // the near future.<br>-<br> // If it is not possible to determine which objects ptr points to at compile<br> // time, __builtin_object_size should return (size_t) -1 for type 0 or 1<br> // and (size_t) 0 for type 2 or 3.<br>@@ -6280,14 +6339,15 @@ bool IntExprEvaluator::TryEvaluateBuilti<br> End.Designator.Entries.size() == End.Designator.MostDerivedPathLength) {<br> // We got a pointer to an array. Step to its end.<br> AmountToAdd = End.Designator.MostDerivedArraySize -<br>- End.Designator.Entries.back().ArrayIndex;<br>- } else if (End.Designator.IsOnePastTheEnd) {<br>+ End.Designator.Entries.back().ArrayIndex;<br>+ } else if (End.Designator.isOnePastTheEnd()) {<br> // We're already pointing at the end of the object.<br> AmountToAdd = 0;<br> }<br><br>- if (End.Designator.MostDerivedType->isIncompleteType() ||<br>- End.Designator.MostDerivedType->isFunctionType())<br>+ QualType PointeeType = End.Designator.MostDerivedType;<br>+ assert(!PointeeType.isNull());<br>+ if (PointeeType->isIncompleteType() || PointeeType->isFunctionType())<br> return Error(E);<br><br> if (!HandleLValueArrayAdjustment(Info, E, End, End.Designator.MostDerivedType,<br>@@ -6331,6 +6391,7 @@ bool IntExprEvaluator::VisitCallExpr(con<br> case EvalInfo::EM_ConstantFold:<br> case EvalInfo::EM_EvaluateForOverflow:<br> case EvalInfo::EM_IgnoreSideEffects:<br>+ case EvalInfo::EM_DesignatorFold:<br> // Leave it to IR generation.<br> return Error(E);<br> case EvalInfo::EM_ConstantExpressionUnevaluated:<br><br>Modified: cfe/trunk/test/CodeGen/object-size.c<br>URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/object-size.c?rev=246877&r1=246876&r2=246877&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/object-size.c?rev=246877&r1=246876&r2=246877&view=diff</a><br>==============================================================================<br>--- cfe/trunk/test/CodeGen/object-size.c (original)<br>+++ cfe/trunk/test/CodeGen/object-size.c Fri Sep 4 16:28:13 2015<br>@@ -161,6 +161,15 @@ void test19() {<br> gi = __builtin_object_size(&foo.a, 2);<br> // CHECK: store i32 4<br> gi = __builtin_object_size(&foo.a, 3);<br>+<br>+ // CHECK: store i32 4<br>+ gi = __builtin_object_size(&foo.b, 0);<br>+ // CHECK: store i32 4<br>+ gi = __builtin_object_size(&foo.b, 1);<br>+ // CHECK: store i32 4<br>+ gi = __builtin_object_size(&foo.b, 2);<br>+ // CHECK: store i32 4<br>+ gi = __builtin_object_size(&foo.b, 3);<br> }<br><br> // CHECK: @test20<br>@@ -221,25 +230,59 @@ void test22() {<br> gi = __builtin_object_size(&t[9].t[10], 2);<br> // CHECK: store i32 0<br> gi = __builtin_object_size(&t[9].t[10], 3);<br>+<br>+ // CHECK: store i32 0<br>+ gi = __builtin_object_size((char*)&t[0] + sizeof(t), 0);<br>+ // CHECK: store i32 0<br>+ gi = __builtin_object_size((char*)&t[0] + sizeof(t), 1);<br>+ // CHECK: store i32 0<br>+ gi = __builtin_object_size((char*)&t[0] + sizeof(t), 2);<br>+ // CHECK: store i32 0<br>+ gi = __builtin_object_size((char*)&t[0] + sizeof(t), 3);<br>+<br>+ // CHECK: store i32 0<br>+ gi = __builtin_object_size((char*)&t[9].t[0] + 10*sizeof(t[0].t), 0);<br>+ // CHECK: store i32 0<br>+ gi = __builtin_object_size((char*)&t[9].t[0] + 10*sizeof(t[0].t), 1);<br>+ // CHECK: store i32 0<br>+ gi = __builtin_object_size((char*)&t[9].t[0] + 10*sizeof(t[0].t), 2);<br>+ // CHECK: store i32 0<br>+ gi = __builtin_object_size((char*)&t[9].t[0] + 10*sizeof(t[0].t), 3);<br> }<br><br>-struct Test23Ty { int t[10]; };<br>+struct Test23Ty { int a; int t[10]; };<br><br> // CHECK: @test23<br>-void test23(struct Test22Ty *p) {<br>+void test23(struct Test23Ty *p) {<br> // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)<br> gi = __builtin_object_size(p, 0);<br> // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)<br> gi = __builtin_object_size(p, 1);<br> // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)<br> gi = __builtin_object_size(p, 2);<br>-<br> // Note: this is currently fixed at 0 because LLVM doesn't have sufficient<br> // data to correctly handle type=3<br> // CHECK: store i32 0<br> gi = __builtin_object_size(p, 3);<br>-}<br><br>+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)<br>+ gi = __builtin_object_size(&p->a, 0);<br>+ // CHECK: store i32 4<br>+ gi = __builtin_object_size(&p->a, 1);<br>+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)<br>+ gi = __builtin_object_size(&p->a, 2);<br>+ // CHECK: store i32 4<br>+ gi = __builtin_object_size(&p->a, 3);<br>+<br>+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)<br>+ gi = __builtin_object_size(&p->t[5], 0);<br>+ // CHECK: store i32 20<br>+ gi = __builtin_object_size(&p->t[5], 1);<br>+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)<br>+ gi = __builtin_object_size(&p->t[5], 2);<br>+ // CHECK: store i32 20<br>+ gi = __builtin_object_size(&p->t[5], 3);<br>+}<br><br> // PR24493 -- ICE if __builtin_object_size called with NULL and (Type & 1) != 0<br> // CHECK: @test24<br>@@ -280,3 +323,71 @@ void test25() {<br> // CHECK: store i32 0<br> gi = __builtin_object_size((void*)0 + 0x1000, 3);<br> }<br>+<br>+// CHECK: @test26<br>+void test26() {<br>+ struct { int v[10]; } t[10];<br>+<br>+ // CHECK: store i32 316<br>+ gi = __builtin_object_size(&t[1].v[11], 0);<br>+ // CHECK: store i32 312<br>+ gi = __builtin_object_size(&t[1].v[12], 1);<br>+ // CHECK: store i32 308<br>+ gi = __builtin_object_size(&t[1].v[13], 2);<br>+ // CHECK: store i32 0<br>+ gi = __builtin_object_size(&t[1].v[14], 3);<br>+}<br>+<br>+struct Test27IncompleteTy;<br>+<br>+// CHECK: @test27<br>+void test27(struct Test27IncompleteTy *t) {<br>+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)<br>+ gi = __builtin_object_size(t, 0);<br>+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)<br>+ gi = __builtin_object_size(t, 1);<br>+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)<br>+ gi = __builtin_object_size(t, 2);<br>+ // Note: this is currently fixed at 0 because LLVM doesn't have sufficient<br>+ // data to correctly handle type=3<br>+ // CHECK: store i32 0<br>+ gi = __builtin_object_size(t, 3);<br>+<br>+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* {{.*}}, i1 false)<br>+ gi = __builtin_object_size(&test27, 0);<br>+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* {{.*}}, i1 false)<br>+ gi = __builtin_object_size(&test27, 1);<br>+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* {{.*}}, i1 true)<br>+ gi = __builtin_object_size(&test27, 2);<br>+ // Note: this is currently fixed at 0 because LLVM doesn't have sufficient<br>+ // data to correctly handle type=3<br>+ // CHECK: store i32 0<br>+ gi = __builtin_object_size(&test27, 3);<br>+}<br>+<br>+// The intent of this test is to ensure that __builtin_object_size treats `&foo`<br>+// and `(T*)&foo` identically, when used as the pointer argument.<br>+// CHECK: @test28<br>+void test28() {<br>+ struct { int v[10]; } t[10];<br>+<br>+#define addCasts(s) ((char*)((short*)(s)))<br>+ // CHECK: store i32 360<br>+ gi = __builtin_object_size(addCasts(&t[1]), 0);<br>+ // CHECK: store i32 360<br>+ gi = __builtin_object_size(addCasts(&t[1]), 1);<br>+ // CHECK: store i32 360<br>+ gi = __builtin_object_size(addCasts(&t[1]), 2);<br>+ // CHECK: store i32 360<br>+ gi = __builtin_object_size(addCasts(&t[1]), 3);<br>+<br>+ // CHECK: store i32 356<br>+ gi = __builtin_object_size(addCasts(&t[1].v[1]), 0);<br>+ // CHECK: store i32 36<br>+ gi = __builtin_object_size(addCasts(&t[1].v[1]), 1);<br>+ // CHECK: store i32 356<br>+ gi = __builtin_object_size(addCasts(&t[1].v[1]), 2);<br>+ // CHECK: store i32 36<br>+ gi = __builtin_object_size(addCasts(&t[1].v[1]), 3);<br>+#undef addCasts<br>+}<br><br><br>_______________________________________________<br>cfe-commits mailing list<br><a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br><a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br></div></div></blockquote></div><br></div></div></div></div><br>_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
<br></blockquote></div></div></div><br></div></div>
</blockquote></div><br></div>