================ @@ -1060,238 +1061,348 @@ CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type, return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true); } -const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset( - ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl, - uint64_t &Offset) { +namespace { + +/// StructFieldAccess is a simple visitor class to grab the first MemberExpr +/// from an Expr. It records any ArraySubscriptExpr we meet along the way. +struct StructFieldAccess + : public ConstStmtVisitor<StructFieldAccess, const MemberExpr *> { + const ArraySubscriptExpr *ASE = nullptr; + bool AddrOfSeen = false; + + const MemberExpr *VisitMemberExpr(const MemberExpr *E) { + if (AddrOfSeen && E->getType()->isArrayType()) + // Avoid forms like '&ptr->array'. + return nullptr; + return E; + } + + const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { + AddrOfSeen = false; // '&ptr->array[idx]' is okay. + ASE = E; + return Visit(E->getBase()); + } + const MemberExpr *VisitCastExpr(const CastExpr *E) { + return Visit(E->getSubExpr()); + } + const MemberExpr *VisitParenExpr(const ParenExpr *E) { + return Visit(E->getSubExpr()); + } + const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) { + AddrOfSeen = true; + return Visit(E->getSubExpr()); + } + const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) { + AddrOfSeen = false; + return Visit(E->getSubExpr()); + } +}; + +} // end anonymous namespace + +/// Find a struct's flexible array member. It may be embedded inside multiple +/// sub-structs, but must still be the last field. +static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF, + ASTContext &Ctx, + const RecordDecl *RD) { const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = - getLangOpts().getStrictFlexArraysLevel(); - uint32_t FieldNo = 0; + CGF.getLangOpts().getStrictFlexArraysLevel(); if (RD->isImplicit()) return nullptr; for (const FieldDecl *FD : RD->fields()) { - if ((!FAMDecl || FD == FAMDecl) && - Decl::isFlexibleArrayMemberLike( + if (Decl::isFlexibleArrayMemberLike( Ctx, FD, FD->getType(), StrictFlexArraysLevel, - /*IgnoreTemplateOrMacroSubstitution=*/true)) { - const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD); - Offset += Layout.getFieldOffset(FieldNo); + /*IgnoreTemplateOrMacroSubstitution=*/true)) return FD; + + if (auto RT = FD->getType()->getAs<RecordType>()) + if (const FieldDecl *FD = + FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl())) + return FD; + } + + return nullptr; +} + +/// Calculate the offset of a struct field. It may be embedded inside multiple +/// sub-structs. +static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, + const FieldDecl *FD, int64_t &Offset) { + if (RD->isImplicit()) + return false; + + const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD); + uint32_t FieldNo = 0; + + for (const FieldDecl *Field : RD->fields()) { + if (Field == FD) { + Offset += Layout.getFieldOffset(FieldNo); + return true; } - QualType Ty = FD->getType(); - if (Ty->isRecordType()) { - if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset( - Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) { - const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD); + if (auto RT = Field->getType()->getAs<RecordType>()) { + if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) { Offset += Layout.getFieldOffset(FieldNo); - return Field; + return true; } } if (!RD->isUnion()) ++FieldNo; } - return nullptr; + return false; } -static unsigned CountCountedByAttrs(const RecordDecl *RD) { - unsigned Num = 0; +llvm::Value * +CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE, + unsigned Type, + llvm::IntegerType *ResType) { + ASTContext &Ctx = getContext(); - for (const FieldDecl *FD : RD->fields()) { - if (FD->getType()->isCountAttributedType()) - return ++Num; + // Note: If the whole struct is specificed in the __bdos (i.e. Visitor + // returns a DeclRefExpr). The calculation of the whole size of the structure + // with a flexible array member can be done in two ways: + // + // 1) sizeof(struct S) + count * sizeof(typeof(fam)) + // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam)) + // + // The first will add additional padding after the end of the array + // allocation while the second method is more precise, but not quite expected + // from programmers. See + // https://lore.kernel.org/lkml/ZvV6X5FPBBW7CO1f@archlinux/ for a discussion + // of the topic. + // + // GCC isn't (currently) able to calculate __bdos on a pointer to the whole + // structure. Therefore, because of the above issue, we choose to match what + // GCC does for consistency's sake. - QualType Ty = FD->getType(); - if (Ty->isRecordType()) - Num += CountCountedByAttrs(Ty->getAsRecordDecl()); - } + StructFieldAccess Visitor; + const MemberExpr *ME = Visitor.Visit(E); + if (!ME) + return nullptr; - return Num; -} + const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()); + if (!FD) + 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: + const RecordDecl *RD = FD->getDeclContext()->getOuterLexicalRecordContext(); + const FieldDecl *FlexibleArrayMemberFD = nullptr; + + if (Decl::isFlexibleArrayMemberLike( + Ctx, FD, FD->getType(), getLangOpts().getStrictFlexArraysLevel(), + /*IgnoreTemplateOrMacroSubstitution=*/true)) + FlexibleArrayMemberFD = FD; + else + FlexibleArrayMemberFD = FindFlexibleArrayMemberField(*this, Ctx, RD); + + if (!FlexibleArrayMemberFD || + !FlexibleArrayMemberFD->getType()->isCountAttributedType()) + return nullptr; + + const FieldDecl *CountFD = FlexibleArrayMemberFD->findCountedByField(); + if (!CountFD) + // Can't find the field referenced by the "counted_by" attribute. + return nullptr; + + const Expr *Idx = nullptr; + if (Visitor.ASE) { + Idx = Visitor.ASE->getIdx(); + + if (Idx->HasSideEffects(Ctx)) + // We can't have side-effects. + return getDefaultBuiltinObjectSizeResult(Type, ResType); + + if (const auto *IL = dyn_cast<IntegerLiteral>(Idx)) { + int64_t Val = IL->getValue().getSExtValue(); + if (Val < 0) + return getDefaultBuiltinObjectSizeResult(Type, ResType); + + // The index is 0, so we don't need to take it into account. + if (Val == 0) + Idx = nullptr; + } + } + + // Calculate the flexible array member's object size using these formulae + // (note: if the calculation is negative, we return 0.): // - // struct s { - // unsigned long flags; - // int count; - // int array[] __attribute__((counted_by(count))); - // } + // struct p; + // struct s { + // /* ... */ + // int count; + // struct p *array[] __attribute__((counted_by(count))); + // }; // - // 1) bdos of the flexible array itself: + // 1) 'ptr->array': // - // __builtin_dynamic_object_size(p->array, 1) == - // p->count * sizeof(*p->array) + // size_t count = (size_t) ptr->count; // - // 2) bdos of a pointer into the flexible array: + // size_t flexible_array_member_base_size = sizeof (*ptr->array); + // size_t flexible_array_member_size = + // count * flexible_array_member_base_size; // - // __builtin_dynamic_object_size(&p->array[42], 1) == - // (p->count - 42) * sizeof(*p->array) + // if (flexible_array_member_size < 0) + // return 0; + // return flexible_array_member_size; // - // 2) bdos of the whole struct, including the flexible array: + // 2) '&ptr->array[idx]': // - // __builtin_dynamic_object_size(p, 1) == - // max(sizeof(struct s), - // offsetof(struct s, array) + p->count * sizeof(*p->array)) + // size_t count = (size_t) ptr->count; + // size_t index = (size_t) idx; // - 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) - 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; - } - } + // size_t flexible_array_member_base_size = sizeof (*ptr->array); + // size_t flexible_array_member_size = + // count * flexible_array_member_base_size; + // + // size_t index_size = index * flexible_array_member_base_size; + // + // if (flexible_array_member_size < 0 || index < 0) + // return 0; + // return flexible_array_member_size - index_size; + // + // 3) '&ptr->field': + // + // size_t count = (size_t) ptr->count; + // size_t sizeof_struct = sizeof (struct s); + // + // size_t flexible_array_member_base_size = sizeof (*ptr->array); + // size_t flexible_array_member_size = + // count * flexible_array_member_base_size; + // + // size_t field_offset = offsetof (struct s, field); + // size_t offset_diff = struct_size - field_offset; + // + // if (flexible_array_member_size < 0) + // return 0; + // return flexible_array_member_size; + // + // 4) '&ptr->field_array[idx]': + // + // size_t count = (size_t) ptr->count; + // size_t index = (size_t) idx; + // size_t sizeof_struct = sizeof (struct s); + // + // size_t flexible_array_member_base_size = sizeof (*ptr->array); + // size_t flexible_array_member_size = + // count * flexible_array_member_base_size; + // + // size_t field_base_size = sizeof (*ptr->field_array); + // size_t field_offset = offsetof (struct s, field) + // field_offset += index * field_base_size; + // + // size_t offset_diff = sizeof_struct - field_offset; + // + // if (flexible_array_member_size < 0 || index < 0) + // return 0; + // return offset_diff + flexible_array_member_size; - // Get the flexible array member Decl. - const RecordDecl *OuterRD = nullptr; - const FieldDecl *FAMDecl = 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(); - FAMDecl = dyn_cast<FieldDecl>(VD); - if (!FAMDecl) - return nullptr; - } 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 we have a situation like this: - // - // struct union_of_fams { - // int flags; - // union { - // signed char normal_field; - // struct { - // int count1; - // int arr1[] __counted_by(count1); - // }; - // struct { - // signed char count2; - // int arr2[] __counted_by(count2); - // }; - // }; - // }; - // - // We don't know which 'count' to use in this scenario: - // - // size_t get_size(struct union_of_fams *p) { - // return __builtin_dynamic_object_size(p, 1); - // } - // - // Instead of calculating a wrong number, we give up. - if (OuterRD && CountCountedByAttrs(OuterRD) > 1) - return nullptr; - } + QualType CountTy = CountFD->getType(); + bool IsSigned = CountTy->isSignedIntegerType(); - if (!OuterRD) - return nullptr; + QualType FlexibleArrayMemberTy = FlexibleArrayMemberFD->getType(); + QualType FieldTy = FD->getType(); - // We call FindFlexibleArrayMemberAndOffset even if FAMDecl is non-null to - // get its offset. - uint64_t Offset = 0; - FAMDecl = - FindFlexibleArrayMemberFieldAndOffset(Ctx, OuterRD, FAMDecl, Offset); - Offset = Ctx.toCharUnitsFromBits(Offset).getQuantity(); + // Explicit cast because otherwise the CharWidth will promote an i32's into + // u64's leading to overflows.. + int64_t CharWidth = static_cast<int64_t>(CGM.getContext().getCharWidth()); - if (!FAMDecl || !FAMDecl->getType()->isCountAttributedType()) - // No flexible array member found or it doesn't have the "counted_by" - // attribute. + // size_t count = (size_t) ptr->count; + Value *Count = EmitLoadOfCountedByField(ME, FlexibleArrayMemberFD, CountFD); + if (!Count) return nullptr; + Count = Builder.CreateIntCast(Count, ResType, IsSigned, "count"); - const FieldDecl *CountedByFD = FAMDecl->findCountedByField(); - if (!CountedByFD) - // Can't find the field referenced by the "counted_by" attribute. - return nullptr; - - if (isa<DeclRefExpr>(Base)) - // The whole struct is specificed in the __bdos. The calculation of the - // whole size of the structure can be done in two ways: - // - // 1) sizeof(struct S) + count * sizeof(typeof(fam)) - // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam)) - // - // The first will add additional padding after the end of the array, - // allocation while the second method is more precise, but not quite - // expected from programmers. See - // https://lore.kernel.org/lkml/ZvV6X5FPBBW7CO1f@archlinux/ for a - // discussion of the topic. - // - // GCC isn't (currently) able to calculate __bdos on a pointer to the whole - // structure. Therefore, because of the above issue, we'll choose to match - // what GCC does for consistency's sake. - return nullptr; - - // Build a load of the counted_by field. - bool IsSigned = CountedByFD->getType()->isSignedIntegerType(); - Value *CountedByInst = EmitLoadOfCountedByField(Base, FAMDecl, CountedByFD); - if (!CountedByInst) - return getDefaultBuiltinObjectSizeResult(Type, ResType); - - CountedByInst = Builder.CreateIntCast(CountedByInst, ResType, IsSigned); - - // Build a load of the index and subtract it from the count. - Value *IdxInst = nullptr; + // size_t index = (size_t) ptr->index; + Value *Index = nullptr; if (Idx) { - if (Idx->HasSideEffects(getContext())) - // We can't have side-effects. - return getDefaultBuiltinObjectSizeResult(Type, ResType); - bool IdxSigned = Idx->getType()->isSignedIntegerType(); - IdxInst = EmitAnyExprToTemp(Idx).getScalarVal(); - IdxInst = Builder.CreateIntCast(IdxInst, ResType, IdxSigned); - - // 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(ResType, Size.getQuantity(), IsSigned); - Value *Res = - Builder.CreateMul(CountedByInst, ElemSize, "", !IsSigned, IsSigned); - Res = Builder.CreateIntCast(Res, ResType, IsSigned); - - // 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)); + Index = EmitScalarExpr(Idx); + Index = Builder.CreateIntCast(Index, ResType, IdxSigned, "index"); + } + + // size_t sizeof_struct = sizeof (struct s); + llvm::StructType *StructTy = getTypes().getCGRecordLayout(RD).getLLVMType(); + const llvm::DataLayout &Layout = CGM.getDataLayout(); + TypeSize Size = Layout.getTypeSizeInBits(StructTy); + Value *SizeofStruct = + llvm::ConstantInt::get(ResType, Size.getKnownMinValue() / CharWidth); + + // size_t field_offset = offsetof (struct s, field); + Value *FieldOffset = nullptr; + if (FlexibleArrayMemberFD != FD) { + int64_t Offset = 0; + GetFieldOffset(Ctx, RD, FD, Offset); + FieldOffset = llvm::ConstantInt::get(ResType, Offset / CharWidth, IsSigned); + } + + // size_t flexible_array_member_base_size = sizeof (*ptr->array); + const ArrayType *ArrayTy = Ctx.getAsArrayType(FlexibleArrayMemberTy); + CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType()); + auto *FlexibleArrayMemberBaseSize = + llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned); + + // size_t flexible_array_member_size = + // count * flexible_array_member_base_size; + Value *FlexibleArrayMemberSize = + Builder.CreateMul(Count, FlexibleArrayMemberBaseSize, + "flexible_array_member_size", !IsSigned, IsSigned); + + auto CheckForNegative = [&](Value *Res) { + Value *Cmp = Builder.CreateIsNotNeg(Res); + if (Index) ---------------- nickdesaulniers wrote:
hopefully inlined, maybe not given the number of callers. But I have a suggestion for just inlining it if each branch produces a single `Value*` scoped outside of themselves, so that there's only one call to `CheckForNegative`, at which point, you might as well just manually inline the lambda. https://github.com/llvm/llvm-project/pull/122198 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits