-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[CodeGen] Revamp counted_by calculations #70606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
19dd7db
36b5271
8c6880b
a5fac56
4d2e39c
e70b487
3f98e8e
b503de0
82588c3
dc8f0df
6260945
7a20a34
1e94fec
c9819d7
163de1d
7abab68
cbdfc3b
c15f340
e6f5299
ff321b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -859,53 +859,60 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type, | |
| } | ||
|
|
||
| if (IsDynamic) { | ||
| LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = | ||
| getLangOpts().getStrictFlexArraysLevel(); | ||
| const Expr *Base = E->IgnoreParenImpCasts(); | ||
|
|
||
| if (FieldDecl *FD = FindCountedByField(Base, StrictFlexArraysLevel)) { | ||
| const auto *ME = dyn_cast<MemberExpr>(Base); | ||
| llvm::Value *ObjectSize = nullptr; | ||
|
|
||
| if (!ME) { | ||
| const auto *DRE = dyn_cast<DeclRefExpr>(Base); | ||
| ValueDecl *VD = nullptr; | ||
|
|
||
| ObjectSize = ConstantInt::get( | ||
| ResType, | ||
| getContext().getTypeSize(DRE->getType()->getPointeeType()) / 8, | ||
| true); | ||
|
|
||
| if (auto *RD = DRE->getType()->getPointeeType()->getAsRecordDecl()) | ||
| VD = RD->getLastField(); | ||
|
|
||
| Expr *ICE = ImplicitCastExpr::Create( | ||
| getContext(), DRE->getType(), CK_LValueToRValue, | ||
| const_cast<Expr *>(cast<Expr>(DRE)), nullptr, VK_PRValue, | ||
| FPOptionsOverride()); | ||
| ME = MemberExpr::CreateImplicit(getContext(), ICE, true, VD, | ||
| VD->getType(), VK_LValue, OK_Ordinary); | ||
| } | ||
|
|
||
| // At this point, we know that \p ME is a flexible array member. | ||
| const auto *ArrayTy = getContext().getAsArrayType(ME->getType()); | ||
| // The code generated here calculates the size of a struct with a flexible | ||
| // array member that uses the counted_by attribute. There are two instances | ||
| // we handle: | ||
| // | ||
| // struct s { | ||
| // unsigned long flags; | ||
| // int count; | ||
| // int array[] __attribute__((counted_by(count))); | ||
| // } | ||
| // | ||
| // 1) bdos of the flexible array itself: | ||
| // | ||
| // __builtin_dynamic_object_size(p->array, 1) == | ||
| // p->count * sizeof(*p->array) | ||
| // | ||
| // 2) bdos of the whole struct, including the flexible array: | ||
| // | ||
| // __builtin_dynamic_object_size(p, 1) == | ||
| // sizeof(*p) + p->count * sizeof(*p->array) | ||
| // | ||
| if (const ValueDecl *CountedByFD = FindCountedByField(E)) { | ||
| // Find the flexible array member. | ||
| const RecordDecl *OuterRD = | ||
| CountedByFD->getDeclContext()->getOuterLexicalRecordContext(); | ||
| const ValueDecl *FAM = | ||
| FindFlexibleArrayMemberField(getContext(), OuterRD); | ||
|
|
||
| // Find the outer struct expr (i.e. p in p->a.b.c.d). | ||
| Expr *CountedByExpr = | ||
| BuildCountedByFieldExpr(const_cast<Expr *>(E), CountedByFD); | ||
|
||
|
|
||
| // Load the counted_by field. | ||
| llvm::Value *CountedByInstr = | ||
| EmitAnyExprToTemp(CountedByExpr).getScalarVal(); | ||
|
|
||
| // Get the size of the flexible array member's base type. | ||
| const auto *ArrayTy = getContext().getAsArrayType(FAM->getType()); | ||
| unsigned Size = getContext().getTypeSize(ArrayTy->getElementType()); | ||
| llvm::Constant *ArraySize = | ||
| llvm::ConstantInt::get(CountedByInstr->getType(), Size / 8); | ||
|
|
||
| llvm::Value *ObjectSize = Builder.CreateMul(CountedByInstr, ArraySize); | ||
| ObjectSize = Builder.CreateZExtOrTrunc(ObjectSize, ResType); | ||
|
|
||
| if (const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) { | ||
| // The whole struct is specificed in the __bdos. | ||
| QualType StructTy = DRE->getType()->getPointeeType(); | ||
| llvm::Value *StructSize = ConstantInt::get( | ||
| ResType, getContext().getTypeSize(StructTy) / 8, true); | ||
| ObjectSize = Builder.CreateAdd(StructSize, ObjectSize); | ||
| } | ||
|
|
||
| llvm::Value *CountField = | ||
| EmitAnyExprToTemp(MemberExpr::CreateImplicit( | ||
| getContext(), const_cast<Expr *>(ME->getBase()), | ||
| ME->isArrow(), FD, FD->getType(), VK_LValue, | ||
| OK_Ordinary)) | ||
| .getScalarVal(); | ||
|
|
||
| llvm::Value *Mul = Builder.CreateMul( | ||
| CountField, llvm::ConstantInt::get(CountField->getType(), Size / 8)); | ||
| Mul = Builder.CreateZExtOrTrunc(Mul, ResType); | ||
|
|
||
| if (ObjectSize) | ||
| return Builder.CreateAdd(ObjectSize, Mul); | ||
|
|
||
| return Mul; | ||
| // PULL THE STRING!! | ||
| return ObjectSize; | ||
| } | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to the alignment and padding, the size of
struct sisn't necessarily the start offset of the flexible array member. FYI https://godbolt.org/z/bMb19n9TzI think the equation should be something like this:
max (offset(struct s, array) + p->count * sizeof(*p->array), sizeof(*p))There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand the issue involved here. From what I can tell, the
getContext().getTypeSize(StructTy)returns the number of bits in the whole structure, which I divide by 8 to get bytes.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is about the semantics.
int array[__counted_by(count)]means you havecountelements starting right from the flexible memberarrayoffset, which isoffsetof(struct s, array).Whereas,
sizeof(*p) + p->count * sizeof(*p->array)indicates you havecountelements starting from at the end of the struct, which isn't always the same as the offset ofarraybecause of the padding and alignment rule. https://godbolt.org/z/bMb19n9Tz shows whensizeof(struct s)andoffsetof(struct s, array)aren't the same. The problem is that this can result in the object size bigger than what it should be.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I never want
offsetofhere. Thesizeof(*p)will report the correct size of the array without adding on any size for the flexible array member, when it has no value (size that is). Sosizeof(*p)for the array in the example is 16, which is correct. Possibly the only issue may be when the flexible array member isn't strict, i.e. it has something likeint array[0]orint array[1].There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was why I suggested to have
maxhere:max (offset(struct s, array) + p->count * sizeof(*p->array), sizeof(*p))so that it can return the correct size when the struct size is bigger than the end of the array due to the padding.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I don’t understand what we don’t mutually understand. In the same situation:
We have an ASAN trap. Here are all the ways I can think of to reconcile this fact with the model that you propose:
f;__builtin_dynamic_object_size(p, 1);__builtin_dynamic_object_sizeis wrong.The only one that makes sense to me is that the definition chosen for
__builtin_dynamic_object_size. Which one is it to you? Do you see another way out?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I think we are talking past to each other a little bit. That comment I was responding to this:
I'm saying "the full struct size" isn't exactly
struct + fambecause fam doesn't always exactly start from sizeof(struct) when there is a padding in the struct due to the alignment. Andsizeof(*p) + p->count * sizeof(*p->array)gives us value that's more than the full struct size.As we all see in the previous examples, the full struct size is
offsetof(struct s, fam) + sizeof(*p->array) * p->countwith the result being round up toalignof(struct s).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rapidsna Ah! Okay. That makes more sense, thank you. I'll see what I can do here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that it's okay for the
count * sizeof(fam)to be less than the actual size of the fam. It's there to ensure the index doesn't go over what's expected.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I added dc8f0df, which should take care of this issue here. Sorry about my confusion. PTAL.