llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Bill Wendling (bwendling) <details> <summary>Changes</summary> If we're able to, we convert a call to __builtin_dynamic_object_size into a calculation. This is a cleaner implementation, and has the potential to improve optimizations and, especially, inlining, because optimizing around an 'llvm.objectsize' call isn't trivial. --- Full diff: https://github.com/llvm/llvm-project/pull/80256.diff 3 Files Affected: - (modified) clang/lib/CodeGen/CGBuiltin.cpp (+149-6) - (modified) clang/lib/CodeGen/CGExpr.cpp (+7-6) - (modified) clang/lib/CodeGen/CodeGenFunction.h (+6) ``````````diff diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index f3ab5ad7b08ec..2c109821de43c 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -26,6 +26,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/OSLog.h" #include "clang/AST/OperationKinds.h" +#include "clang/AST/StmtVisitor.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" @@ -1051,6 +1052,145 @@ CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type, return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned)); } +namespace { + +/// \p StructBaseExpr returns the base \p Expr with a structure or union type. +struct StructBaseExpr : public ConstStmtVisitor<StructBaseExpr, const Expr *> { + StructBaseExpr() = default; + + //===--------------------------------------------------------------------===// + // Visitor Methods + //===--------------------------------------------------------------------===// + + const Expr *VisitStmt(const Stmt *S) { return nullptr; } + + const Expr *Visit(const Expr *E) { + QualType Ty = E->getType(); + if (Ty->isStructureType() || Ty->isUnionType()) + return E; + + return ConstStmtVisitor<StructBaseExpr, const Expr *>::Visit(E); + } + + const Expr *VisitDeclRefExpr(const DeclRefExpr *E) { return E; } + + const Expr *VisitMemberExpr(const MemberExpr *E) { + return Visit(E->getBase()); + } + const Expr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { + return Visit(E->getBase()); + } + const Expr *VisitCastExpr(const CastExpr *E) { + return Visit(E->getSubExpr()); + } + const Expr *VisitParenExpr(const ParenExpr *E) { + return Visit(E->getSubExpr()); + } + const Expr *VisitUnaryAddrOf(const clang::UnaryOperator *E) { + return Visit(E->getSubExpr()); + } + const Expr *VisitUnaryDeref(const clang::UnaryOperator *E) { + return Visit(E->getSubExpr()); + } +}; + +} // end anonymous namespace + +/// The offset of a field from the beginning of the record. +llvm::Value * +CodeGenFunction::tryEmitObjectSizeCalculation(const Expr *E, unsigned Type, + llvm::IntegerType *ResType) { + if ((Type & 0x01) != 0) + // We handle only the whole object size. + return nullptr; + + E = E->IgnoreParenImpCasts(); + + const Expr *Base = StructBaseExpr().Visit(E); + if (!Base) + return nullptr; + + const RecordDecl *RD = Base->getType()->getAsRecordDecl(); + if (!RD) + return nullptr; + + // Get the full size of the struct. + ASTContext &Ctx = getContext(); + const RecordDecl *OuterRD = RD->getOuterLexicalRecordContext(); + const clang::Type *RT = OuterRD->getTypeForDecl(); + CharUnits RecordSize = Ctx.getTypeSizeInChars(RT); + + Value *Res = nullptr; + + if (const auto *U = dyn_cast<UnaryOperator>(E); + U && (U->getOpcode() == UO_AddrOf || U->getOpcode() == UO_Deref)) + E = U->getSubExpr()->IgnoreParenImpCasts(); + + if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) { + const Expr *Idx = ASE->getIdx(); + Base = ASE->getBase()->IgnoreParenImpCasts(); + + if (const auto *ME = dyn_cast<MemberExpr>(Base); + ME && ME->getType()->isConstantArrayType()) { + // The simple case: + // + // struct s { + // int arr[42]; + // char c; + // /* others */ + // }; + // + // __builtin_dynamic_object_size(&p->arr[idx], 0); + // + // We can translate the __builtin_dynamic_object_call into: + // + // sizeof(struct s) - offsetof(arr) - (idx * sizeof(int)) + // + bool IsSigned = Idx->getType()->isSignedIntegerType(); + Value *IdxInst = EmitAnyExprToTemp(Idx).getScalarVal(); + IdxInst = Builder.CreateIntCast(IdxInst, ResType, IsSigned); + + const ConstantArrayType *CAT = cast<ConstantArrayType>(ME->getType()); + CharUnits ElemSize = Ctx.getTypeSizeInChars(CAT->getElementType()); + Value *ElemSizeInst = Builder.getInt32(ElemSize.getQuantity()); + ElemSizeInst = Builder.CreateIntCast(ElemSizeInst, ResType, IsSigned); + + // idx * sizeof(<arr element type>) + Res = Builder.CreateMul(IdxInst, ElemSizeInst, "", !IsSigned, IsSigned); + + // sizeof(struct s) + Value *RecordSizeInst = Builder.getInt32(RecordSize.getQuantity()); + RecordSizeInst = Builder.CreateIntCast(RecordSizeInst, ResType, + RT->isSignedIntegerType()); + + // offsetof(arr) + int64_t Offset = 0; + getFieldOffsetInBits(OuterRD, cast<FieldDecl>(ME->getMemberDecl()), + Offset); + + CharUnits OffsetVal = Ctx.toCharUnitsFromBits(Offset); + Value *OffsetInst = Builder.getInt64(OffsetVal.getQuantity()); + OffsetInst = Builder.CreateIntCast(OffsetInst, ResType, IsSigned); + + // sizeof(struct s) - offsetof(arr) ... + RecordSizeInst = Builder.CreateSub(RecordSizeInst, OffsetInst, "", + !IsSigned, IsSigned); + // ... - (idx * sizeof(<arr element type>)) + RecordSizeInst = + Builder.CreateSub(RecordSizeInst, Res, "", !IsSigned, IsSigned); + + // Don't allow the index or result to be negative. + Value *Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(Res), + Builder.CreateIsNotNeg(RecordSizeInst)); + + Res = Builder.CreateSelect(Cmp, RecordSizeInst, + ConstantInt::get(ResType, 0, IsSigned)); + } + } + + return Res; +} + /// 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 @@ -1083,18 +1223,21 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type, } } + // 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. + if (Type == 3 || (!EmittedE && E->HasSideEffects(getContext()))) + return getDefaultBuiltinObjectSizeResult(Type, ResType); + 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. - if (Type == 3 || (!EmittedE && E->HasSideEffects(getContext()))) - return getDefaultBuiltinObjectSizeResult(Type, ResType); + if (Value *V = tryEmitObjectSizeCalculation(E, Type, ResType)) + return V; + } Value *Ptr = EmittedE ? EmittedE : EmitScalarExpr(E); assert(Ptr->getType()->isPointerTy() && diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 4a2f3caad6588..ce7b3040e94c1 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4064,15 +4064,16 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr, } /// The offset of a field from the beginning of the record. -static bool getFieldOffsetInBits(CodeGenFunction &CGF, const RecordDecl *RD, - const FieldDecl *FD, int64_t &Offset) { - ASTContext &Ctx = CGF.getContext(); +bool CodeGenFunction::getFieldOffsetInBits(const RecordDecl *RD, + const FieldDecl *FD, + int64_t &Offset) { + ASTContext &Ctx = getContext(); const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD); unsigned FieldNo = 0; for (const Decl *D : RD->decls()) { if (const auto *Record = dyn_cast<RecordDecl>(D)) - if (getFieldOffsetInBits(CGF, Record, FD, Offset)) { + if (getFieldOffsetInBits(Record, FD, Offset)) { Offset += Layout.getFieldOffset(FieldNo); return true; } @@ -4108,11 +4109,11 @@ static std::optional<int64_t> getOffsetDifferenceInBits(CodeGenFunction &CGF, return std::optional<int64_t>(); int64_t FD1Offset = 0; - if (!getFieldOffsetInBits(CGF, FD1OuterRec, FD1, FD1Offset)) + if (!CGF.getFieldOffsetInBits(FD1OuterRec, FD1, FD1Offset)) return std::optional<int64_t>(); int64_t FD2Offset = 0; - if (!getFieldOffsetInBits(CGF, FD2OuterRec, FD2, FD2Offset)) + if (!CGF.getFieldOffsetInBits(FD2OuterRec, FD2, FD2Offset)) return std::optional<int64_t>(); return std::make_optional<int64_t>(FD1Offset - FD2Offset); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 143ad64e8816b..5a0725a76149a 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3093,6 +3093,9 @@ class CodeGenFunction : public CodeGenTypeCache { const FieldDecl *FAMDecl, const FieldDecl *CountDecl); + bool getFieldOffsetInBits(const RecordDecl *RD, const FieldDecl *FD, + int64_t &Offset); + llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, bool isInc, bool isPre); ComplexPairTy EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV, @@ -4892,6 +4895,9 @@ class CodeGenFunction : public CodeGenTypeCache { llvm::Value *EmittedE, bool IsDynamic); + llvm::Value *tryEmitObjectSizeCalculation(const Expr *E, unsigned Type, + llvm::IntegerType *ResType); + llvm::Value *emitFlexibleArrayMemberSize(const Expr *E, unsigned Type, llvm::IntegerType *ResType); `````````` </details> https://github.com/llvm/llvm-project/pull/80256 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits