[clang] [Clang] Convert __builtin_dynamic_object_size into a calculation (PR #80256)

Bill Wendling via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 31 23:28:23 PST 2024


https://github.com/bwendling created https://github.com/llvm/llvm-project/pull/80256

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.

>From d08abc7a9454557ffa43fc14611177f2c4e7fba4 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Wed, 31 Jan 2024 13:07:53 -0800
Subject: [PATCH] [Clang] Convert __builtin_dynamic_object_size into a
 calculation

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.
---
 clang/lib/CodeGen/CGBuiltin.cpp     | 155 ++++++++++++++++++++++++++--
 clang/lib/CodeGen/CGExpr.cpp        |  13 +--
 clang/lib/CodeGen/CodeGenFunction.h |   6 ++
 3 files changed, 162 insertions(+), 12 deletions(-)

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);
 



More information about the cfe-commits mailing list