ahatanak created this revision.
ahatanak added reviewers: rsmith, george.burgess.iv, aaron.ballman.
ahatanak added subscribers: cfe-commits, dexonsmith, hfinkel.

This patch adds support for attribute "overallocated", which will be used to 
indicate a union or struct is over-allocated. This is needed to have 
__builtin_object_size correctly compute the size of an object when malloc 
allocates extra space at the end of a struct or union. For example,

struct S {
   int a;
   char s[32];
};

void *p = malloc(sizeof(S) + 64);
unsigned s = builtin_object_size(((struct S*)p)->s, 1); // "s" should be 
32+64=96, not 32.

The link to the relevant discussion on cfe-dev is here:

http://lists.llvm.org/pipermail/cfe-dev/2016-March/047782.html

http://reviews.llvm.org/D21453

Files:
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  lib/AST/ExprConstant.cpp
  lib/Sema/SemaDeclAttr.cpp
  test/CodeGen/object-size.c
  test/CodeGen/object-size.cpp

Index: test/CodeGen/object-size.cpp
===================================================================
--- test/CodeGen/object-size.cpp
+++ test/CodeGen/object-size.cpp
@@ -62,3 +62,29 @@
   // CHECK: store i32 16
   gi = __builtin_object_size(&c->bs[0].buf[0], 3);
 }
+
+struct S0 {
+  int a[16], b[16];
+} __attribute__((overallocated));
+
+struct S1 : S0 {
+};
+
+struct S2 : S1 {
+} __attribute__((overallocated));
+
+// CHECK-LABEL: define void @_Z5test3v()
+void test3() {
+  struct S0 *s0;
+  struct S1 *s1;
+  struct S2 *s2;
+
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+  gi = __builtin_object_size(s0->b, 1);
+
+  // CHECK: store i32 64, i32* @gi
+  gi = __builtin_object_size(s1->b, 1);
+
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+  gi = __builtin_object_size(s2->b, 1);
+}
Index: test/CodeGen/object-size.c
===================================================================
--- test/CodeGen/object-size.c
+++ test/CodeGen/object-size.c
@@ -517,3 +517,49 @@
   // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
   gi = __builtin_object_size(&dsv[9].snd[0], 1);
 }
+
+union U0 {
+  int a[16], b[16];
+} __attribute__((overallocated));
+
+struct S0 {
+  int a[16], b[16];
+};
+
+struct S1 {
+  int a[16], b[16];
+} __attribute__((overallocated));
+
+struct S2 {
+  int a[16], b[16];
+  struct S1 s1;
+  struct S0 s0;
+} __attribute__((overallocated));
+
+// CHECK-LABEL: @test32
+void test32() {
+  union U0 *u0;
+  struct S1 *s1;
+  struct S2 *s2;
+
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+  gi = __builtin_object_size(u0->a, 1);
+
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+  gi = __builtin_object_size(u0->b, 1);
+
+  // CHECK: store i32 64, i32* @gi
+  gi = __builtin_object_size(s1->a, 1);
+
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+  gi = __builtin_object_size(s1->b, 1);
+
+  // CHECK: store i32 64, i32* @gi
+  gi = __builtin_object_size(s2->b, 1);
+
+  // CHECK: store i32 64, i32* @gi
+  gi = __builtin_object_size(s2->s1.b, 1);
+
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+  gi = __builtin_object_size(&s2->s0, 1);
+}
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -4929,6 +4929,12 @@
   }
 }
 
+static void handleOverAllocatedAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+  D->addAttr(::new (S.Context)
+             OverAllocatedAttr(Attr.getLoc(), S.Context,
+                               Attr.getAttributeSpellingListIndex()));
+}
+
 static void handleAMDGPUNumVGPRAttr(Sema &S, Decl *D,
                                     const AttributeList &Attr) {
   uint32_t NumRegs;
@@ -5394,6 +5400,9 @@
   case AttributeList::AT_X86ForceAlignArgPointer:
     handleX86ForceAlignArgPointerAttr(S, D, Attr);
     break;
+  case AttributeList::AT_OverAllocated:
+    handleOverAllocatedAttr(S, D, Attr);
+    break;
   case AttributeList::AT_DLLExport:
   case AttributeList::AT_DLLImport:
     handleDLLAttr(S, D, Attr);
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -1049,7 +1049,12 @@
     APValue::LValueBase Base;
     CharUnits Offset;
     bool InvalidBase : 1;
-    unsigned CallIndex : 31;
+
+    // Indicates the enclosing struct is marked overallocated. This is used in
+    // computation of __builtin_object_size.
+    bool OverAllocated = 1;
+
+    unsigned CallIndex : 30;
     SubobjectDesignator Designator;
 
     const APValue::LValueBase getLValueBase() const { return Base; }
@@ -1059,6 +1064,8 @@
     SubobjectDesignator &getLValueDesignator() { return Designator; }
     const SubobjectDesignator &getLValueDesignator() const { return Designator;}
 
+    LValue() : OverAllocated(false) {}
+
     void moveInto(APValue &V) const {
       if (Designator.Invalid)
         V = APValue(Base, Offset, APValue::NoLValuePath(), CallIndex);
@@ -4572,6 +4579,15 @@
       EvalOK = this->Visit(E->getBase());
       BaseTy = E->getBase()->getType();
     }
+
+    // Check to see if the parent record is marked overallocated.
+    const TagDecl *TD = BaseTy->getAsTagDecl();
+
+    if (isa<CXXRecordDecl>(TD))
+      TD = E->getBase()->getBestDynamicClassType();
+
+    Result.OverAllocated = TD->hasAttr<OverAllocatedAttr>();
+
     if (!EvalOK) {
       if (!this->Info.allowInvalidBaseExpr())
         return false;
@@ -6728,13 +6744,15 @@
   // strcpy(&F->c[0], Bar);
   //
   // So, if we see that we're examining a 1-length (or 0-length) array at the
-  // end of a struct with an unknown base, we give up instead of breaking code
-  // that behaves this way. Note that we only do this when Type=1, because
-  // Type=3 is a lower bound, so answering conservatively is fine.
+  // end of a struct with an unknown base or the last field of a struct marked
+  // overallocated, we give up instead of breaking code that behaves this way.
+  // Note that we only do this when Type=1, because Type=3 is a lower bound, so
+  // answering conservatively is fine.
   if (End.InvalidBase && SubobjectOnly && Type == 1 &&
       End.Designator.Entries.size() == End.Designator.MostDerivedPathLength &&
-      End.Designator.MostDerivedIsArrayElement &&
-      End.Designator.MostDerivedArraySize < 2 &&
+      ((End.Designator.MostDerivedIsArrayElement &&
+        End.Designator.MostDerivedArraySize < 2) ||
+       End.OverAllocated) &&
       isDesignatorAtObjectEnd(Info.Ctx, End))
     return false;
 
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -2067,6 +2067,25 @@
   }];
 }
 
+def OverAllocatedDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+Use ``overallocated`` to indicate a struct or union is over-allocated. For example,
+
+.. code-block:: c++
+
+struct S {
+  char a[4], char b[4];
+} __attribute__((overallocated));
+
+unsigned foo() {
+  void *p = malloc(sizeof(struct S) + 32);
+  return __builtin_object_size(((struct S*)p)->b, 1); // Returns 36 instead of 4.
+}
+
+  }];
+}
+
 def InternalLinkageDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -2274,6 +2274,12 @@
   let Documentation = [LoopHintDocs, UnrollHintDocs];
 }
 
+def OverAllocated : InheritableAttr {
+  let Spellings = [GNU<"overallocated">, CXX11<"clang", "overallocated">];
+  let Subjects = SubjectList<[Record], ErrorDiag, "ExpectedStructOrUnion">;
+  let Documentation = [OverAllocatedDocs];
+}
+
 def CapturedRecord : InheritableAttr {
   // This attribute has no spellings as it is only ever created implicitly.
   let Spellings = [];
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to