[clang] [Clang] Implement the 'counted_by' attribute (PR #76348)
Bill Wendling via cfe-commits
cfe-commits at lists.llvm.org
Mon Jan 8 12:54:49 PST 2024
================
@@ -818,6 +819,189 @@ CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
}
+const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberField(
+ ASTContext &Ctx, const RecordDecl *RD, uint64_t &Offset) {
+ const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
+ getLangOpts().getStrictFlexArraysLevel();
+ unsigned FieldNo = 0;
+
+ for (const Decl *D : RD->decls()) {
+ if (const auto *Field = dyn_cast<FieldDecl>(D);
+ Field && Decl::isFlexibleArrayMemberLike(
+ Ctx, Field, Field->getType(), StrictFlexArraysLevel,
+ /*IgnoreTemplateOrMacroSubstitution=*/true)) {
+ const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+ Offset += Layout.getFieldOffset(FieldNo);
+ return Field;
+ }
+
+ if (const auto *Record = dyn_cast<RecordDecl>(D))
+ if (const FieldDecl *Field =
+ FindFlexibleArrayMemberField(Ctx, Record, Offset)) {
+ const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+ Offset += Layout.getFieldOffset(FieldNo);
+ return Field;
+ }
+
+ if (isa<FieldDecl>(D))
+ ++FieldNo;
+ }
+
+ return nullptr;
+}
+
+llvm::Value *
+CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
+ llvm::IntegerType *ResType) {
+ // 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 a pointer into the flexible array:
+ //
+ // __builtin_dynamic_object_size(&p->array[42], 1) ==
+ // (p->count - 42) * 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))
+ //
+ ASTContext &Ctx = getContext();
+ const Expr *Base = E->IgnoreParenImpCasts();
+ const Expr *Idx = nullptr;
+
+ if (const auto *UO = dyn_cast<UnaryOperator>(Base);
+ UO && UO->getOpcode() == UO_AddrOf) {
+ Expr *SubExpr = UO->getSubExpr()->IgnoreParenImpCasts();
+ if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(SubExpr)) {
+ Base = ASE->getBase()->IgnoreParenImpCasts();
+ Idx = ASE->getIdx()->IgnoreParenImpCasts();
+
+ if (const auto *IL = dyn_cast<IntegerLiteral>(Idx)) {
+ int64_t Val = IL->getValue().getSExtValue();
+ if (Val < 0)
+ // __bdos returns 0 for negative indexes into an array in a struct.
+ return getDefaultBuiltinObjectSizeResult(Type, ResType);
+
+ if (Val == 0)
+ // The index is 0, so we don't need to take it into account.
+ Idx = nullptr;
+ }
+ } else {
+ // Potential pointer to another element in the struct.
+ Base = SubExpr;
+ }
+ }
+
+ // Get the flexible array member Decl.
+ const RecordDecl *OuterRD = nullptr;
+ if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
+ // Check if \p Base is referencing the FAM itself.
+ const ValueDecl *VD = ME->getMemberDecl();
+ OuterRD = VD->getDeclContext()->getOuterLexicalRecordContext();
+ } else if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
+ // Check if we're pointing to the whole struct.
+ QualType Ty = DRE->getDecl()->getType();
+ if (Ty->isPointerType())
+ Ty = Ty->getPointeeType();
+ OuterRD = Ty->getAsRecordDecl();
+ }
+
+ if (!OuterRD)
+ return nullptr;
+
+ uint64_t Offset = 0;
+ const FieldDecl *FAMDecl = FindFlexibleArrayMemberField(Ctx, OuterRD, Offset);
+ Offset = Ctx.toCharUnitsFromBits(Offset).getQuantity();
+
+ if (!FAMDecl || !FAMDecl->hasAttr<CountedByAttr>())
+ // No flexible array member found or it doesn't have the "counted_by"
+ // attribute.
+ return nullptr;
+
+ const FieldDecl *CountedByFD = FindCountedByField(FAMDecl);
+ if (!CountedByFD)
+ // Can't find the field referenced by the "counted_by" attribute.
+ return nullptr;
+
+ // Build a load of the counted_by field.
+ bool IsSigned = CountedByFD->getType()->isSignedIntegerType();
+ Value *CountedByInst = EmitCountedByFieldExpr(Base, FAMDecl, CountedByFD);
+ if (!CountedByInst)
+ return nullptr;
+
+ llvm::Type *CountedByTy = CountedByInst->getType();
+
+ // Build a load of the index and subtract it from the count.
+ Value *IdxInst = nullptr;
+ if (Idx) {
+ bool IdxSigned = Idx->getType()->isSignedIntegerType();
+ IdxInst = EmitAnyExprToTemp(Idx).getScalarVal();
+ IdxInst = IdxSigned ? Builder.CreateSExtOrTrunc(IdxInst, CountedByTy)
+ : Builder.CreateZExtOrTrunc(IdxInst, CountedByTy);
+
+ // We go ahead with the calculation here. If the index turns out to be
+ // negative, we'll catch it at the end.
+ CountedByInst =
+ Builder.CreateSub(CountedByInst, IdxInst, "", !IsSigned, IsSigned);
+ }
+
+ // Calculate how large the flexible array member is in bytes.
+ const ArrayType *ArrayTy = Ctx.getAsArrayType(FAMDecl->getType());
+ CharUnits Size = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
+ llvm::Constant *ElemSize =
+ llvm::ConstantInt::get(CountedByTy, Size.getQuantity(), IsSigned);
+ Value *FAMSize =
+ Builder.CreateMul(CountedByInst, ElemSize, "", !IsSigned, IsSigned);
+ FAMSize = IsSigned ? Builder.CreateSExtOrTrunc(FAMSize, ResType)
+ : Builder.CreateZExtOrTrunc(FAMSize, ResType);
+ Value *Res = FAMSize;
+
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
+ // The whole struct is specificed in the __bdos.
+ const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(OuterRD);
+
+ // Get the offset of the FAM.
+ llvm::Constant *FAMOffset = ConstantInt::get(ResType, Offset, IsSigned);
+ Value *OffsetAndFAMSize =
+ Builder.CreateAdd(FAMOffset, Res, "", !IsSigned, IsSigned);
+
+ // Get the full size of the struct.
+ llvm::Constant *SizeofStruct =
+ ConstantInt::get(ResType, Layout.getSize().getQuantity(), IsSigned);
+
+ // max(sizeof(struct s),
+ // offsetof(struct s, array) + p->count * sizeof(*p->array))
+ Res = IsSigned
+ ? Builder.CreateBinaryIntrinsic(llvm::Intrinsic::smax,
+ OffsetAndFAMSize, SizeofStruct)
+ : Builder.CreateBinaryIntrinsic(llvm::Intrinsic::umax,
+ OffsetAndFAMSize, SizeofStruct);
+ }
+
+ // A negative \p IdxInst or \p CountedByInst means that the index lands
+ // outside of the flexible array member. If that's the case, we want to
+ // return 0.
+ Value *Cmp = Builder.CreateIsNotNeg(CountedByInst);
+ if (IdxInst)
+ Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(IdxInst), Cmp);
+
+ return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
----------------
bwendling wrote:
Done.
https://github.com/llvm/llvm-project/pull/76348
More information about the cfe-commits
mailing list