[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