diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/CodeGen/CGBuiltin.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/CodeGen/CGBuiltin.cpp | 244 |
1 files changed, 242 insertions, 2 deletions
diff --git a/contrib/llvm-project/clang/lib/CodeGen/CGBuiltin.cpp b/contrib/llvm-project/clang/lib/CodeGen/CGBuiltin.cpp index f71dbf1729a1..998fcc3af581 100644 --- a/contrib/llvm-project/clang/lib/CodeGen/CGBuiltin.cpp +++ b/contrib/llvm-project/clang/lib/CodeGen/CGBuiltin.cpp @@ -25,6 +25,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/OSLog.h" +#include "clang/AST/OperationKinds.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" @@ -818,6 +819,238 @@ CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type, return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true); } +const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberField( + ASTContext &Ctx, const RecordDecl *RD, StringRef Name, uint64_t &Offset) { + const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = + getLangOpts().getStrictFlexArraysLevel(); + unsigned FieldNo = 0; + bool IsUnion = RD->isUnion(); + + for (const Decl *D : RD->decls()) { + if (const auto *Field = dyn_cast<FieldDecl>(D); + Field && (Name.empty() || Field->getNameAsString() == Name) && + 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, Name, Offset)) { + const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD); + Offset += Layout.getFieldOffset(FieldNo); + return Field; + } + + if (!IsUnion && isa<FieldDecl>(D)) + ++FieldNo; + } + + return nullptr; +} + +static unsigned CountCountedByAttrs(const RecordDecl *RD) { + unsigned Num = 0; + + for (const Decl *D : RD->decls()) { + if (const auto *FD = dyn_cast<FieldDecl>(D); + FD && FD->hasAttr<CountedByAttr>()) { + return ++Num; + } + + if (const auto *Rec = dyn_cast<RecordDecl>(D)) + Num += CountCountedByAttrs(Rec); + } + + return Num; +} + +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) + 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; + std::string FAMName; + 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(); + FAMName = VD->getNameAsString(); + } 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 konw 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; + } + + if (!OuterRD) + return nullptr; + + uint64_t Offset = 0; + const FieldDecl *FAMDecl = + FindFlexibleArrayMemberField(Ctx, OuterRD, FAMName, 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 getDefaultBuiltinObjectSizeResult(Type, ResType); + + CountedByInst = Builder.CreateIntCast(CountedByInst, ResType, IsSigned); + + // Build a load of the index and subtract it from the count. + Value *IdxInst = 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 *FAMSize = + Builder.CreateMul(CountedByInst, ElemSize, "", !IsSigned, IsSigned); + FAMSize = Builder.CreateIntCast(FAMSize, ResType, IsSigned); + 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)); +} + /// Returns a Value corresponding to the size of the given expression. /// This Value may be either of the following: /// - A llvm::Argument (if E is a param with the pass_object_size attribute on @@ -850,6 +1083,13 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type, } } + if (IsDynamic) { + // Emit special code for a flexible array member with the "counted_by" + // attribute. + if (Value *V = emitFlexibleArrayMemberSize(E, Type, ResType)) + return V; + } + // LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't // evaluate E for side-effects. In either case, we shouldn't lower to // @llvm.objectsize. @@ -9681,8 +9921,8 @@ Value *CodeGenFunction::EmitSVEMaskedStore(const CallExpr *E, bool IsQuadStore = false; switch (IntrinsicID) { - case Intrinsic::aarch64_sve_st1uwq: - case Intrinsic::aarch64_sve_st1udq: + case Intrinsic::aarch64_sve_st1wq: + case Intrinsic::aarch64_sve_st1dq: AddrMemoryTy = llvm::ScalableVectorType::get(MemEltTy, 1); PredTy = llvm::ScalableVectorType::get(IntegerType::get(getLLVMContext(), 1), 1); |