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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits