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