<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">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><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><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 class="h5"><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">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><br></div></div>