-
Notifications
You must be signed in to change notification settings - Fork 14.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 13 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,68 @@ 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); | ||
// 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) == | ||
// max(sizeof(struct s), | ||
// offsetof(struct s, array) + p->count * sizeof(*p->array)) | ||
// | ||
if (const ValueDecl *CountedByFD = FindCountedByField(E)) { | ||
const RecordDecl *OuterRD = | ||
CountedByFD->getDeclContext()->getOuterLexicalRecordContext(); | ||
ASTContext &Ctx = getContext(); | ||
|
||
// Load the counted_by field. | ||
const Expr *CountedByExpr = BuildCountedByFieldExpr(E, CountedByFD); | ||
llvm::Value *CountedByInst = | ||
EmitAnyExprToTemp(CountedByExpr).getScalarVal(); | ||
|
||
// Get the size of the flexible array member's base type. | ||
const ValueDecl *FAM = FindFlexibleArrayMemberField(Ctx, OuterRD); | ||
const ArrayType *ArrayTy = Ctx.getAsArrayType(FAM->getType()); | ||
CharUnits Size = Ctx.getTypeSizeInChars(ArrayTy->getElementType()); | ||
llvm::Constant *ElemSize = | ||
llvm::ConstantInt::get(CountedByInst->getType(), Size.getQuantity()); | ||
|
||
llvm::Value *FAMSize = Builder.CreateMul(CountedByInst, ElemSize); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's more a sanitizer check, which should abort. This code is just dealing with the __bdos calls. If it returns a negative value, then that's badness. I know that it can return
And you want There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm...I think maybe I should put a check in to see if the types are signed and then perform the appropriate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay. I converted the instructions to carry the signedness of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, it seems like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you need to use pointers here. See https://godbolt.org/z/qWjs4TeW6 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, yes it seems like I'll have to ensure that we're not pointing outside of the struct here. |
||
llvm::Value *Res = Builder.CreateZExtOrTrunc(FAMSize, ResType); | ||
|
||
if (const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) { | ||
// The whole struct is specificed in the __bdos. | ||
// Get the full size of the struct. | ||
const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(OuterRD); | ||
llvm::Value *SizeofStruct = | ||
ConstantInt::get(ResType, Layout.getSize().getQuantity(), true); | ||
|
||
// Get the offset of the FAM. | ||
CharUnits Offset = Ctx.toCharUnitsFromBits(Ctx.getFieldOffset(FAM)); | ||
llvm::Value *FAMOffset = | ||
ConstantInt::get(ResType, Offset.getQuantity(), true); | ||
|
||
// max(sizeof(struct s), | ||
// offsetof(struct s, array) + p->count * sizeof(*p->array)) | ||
llvm::Value *OffsetAndFAMSize = Builder.CreateAdd(FAMOffset, Res); | ||
Res = Builder.CreateBinaryIntrinsic(llvm::Intrinsic::smax, | ||
OffsetAndFAMSize, SizeofStruct); | ||
} | ||
|
||
// At this point, we know that \p ME is a flexible array member. | ||
const auto *ArrayTy = getContext().getAsArrayType(ME->getType()); | ||
unsigned Size = getContext().getTypeSize(ArrayTy->getElementType()); | ||
|
||
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 Res; | ||
} | ||
} | ||
|
||
|
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.
Should we handle the case where
FAM == nullptr
?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.
At this point, we know that FAM can't be null. The
FindCountedByField
call above can only return non-null if there's a FAM.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 see. So sounds like this relies on the fact that counted_by can currently be added to a flexible array member only? Should we add an assertion here to make it easier to deal with when that assumption breaks in future?
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.
There's a sema error check to ensure that it's applied only to a FAM. I suppose it can't hurt to add an assert though.