Skip to content

Commit d08abc7

Browse files
committed
[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.
1 parent ebe8733 commit d08abc7

File tree

3 files changed

+162
-12
lines changed

3 files changed

+162
-12
lines changed

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 149 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "clang/AST/Decl.h"
2727
#include "clang/AST/OSLog.h"
2828
#include "clang/AST/OperationKinds.h"
29+
#include "clang/AST/StmtVisitor.h"
2930
#include "clang/Basic/TargetBuiltins.h"
3031
#include "clang/Basic/TargetInfo.h"
3132
#include "clang/Basic/TargetOptions.h"
@@ -1051,6 +1052,145 @@ CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
10511052
return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
10521053
}
10531054

1055+
namespace {
1056+
1057+
/// \p StructBaseExpr returns the base \p Expr with a structure or union type.
1058+
struct StructBaseExpr : public ConstStmtVisitor<StructBaseExpr, const Expr *> {
1059+
StructBaseExpr() = default;
1060+
1061+
//===--------------------------------------------------------------------===//
1062+
// Visitor Methods
1063+
//===--------------------------------------------------------------------===//
1064+
1065+
const Expr *VisitStmt(const Stmt *S) { return nullptr; }
1066+
1067+
const Expr *Visit(const Expr *E) {
1068+
QualType Ty = E->getType();
1069+
if (Ty->isStructureType() || Ty->isUnionType())
1070+
return E;
1071+
1072+
return ConstStmtVisitor<StructBaseExpr, const Expr *>::Visit(E);
1073+
}
1074+
1075+
const Expr *VisitDeclRefExpr(const DeclRefExpr *E) { return E; }
1076+
1077+
const Expr *VisitMemberExpr(const MemberExpr *E) {
1078+
return Visit(E->getBase());
1079+
}
1080+
const Expr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
1081+
return Visit(E->getBase());
1082+
}
1083+
const Expr *VisitCastExpr(const CastExpr *E) {
1084+
return Visit(E->getSubExpr());
1085+
}
1086+
const Expr *VisitParenExpr(const ParenExpr *E) {
1087+
return Visit(E->getSubExpr());
1088+
}
1089+
const Expr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
1090+
return Visit(E->getSubExpr());
1091+
}
1092+
const Expr *VisitUnaryDeref(const clang::UnaryOperator *E) {
1093+
return Visit(E->getSubExpr());
1094+
}
1095+
};
1096+
1097+
} // end anonymous namespace
1098+
1099+
/// The offset of a field from the beginning of the record.
1100+
llvm::Value *
1101+
CodeGenFunction::tryEmitObjectSizeCalculation(const Expr *E, unsigned Type,
1102+
llvm::IntegerType *ResType) {
1103+
if ((Type & 0x01) != 0)
1104+
// We handle only the whole object size.
1105+
return nullptr;
1106+
1107+
E = E->IgnoreParenImpCasts();
1108+
1109+
const Expr *Base = StructBaseExpr().Visit(E);
1110+
if (!Base)
1111+
return nullptr;
1112+
1113+
const RecordDecl *RD = Base->getType()->getAsRecordDecl();
1114+
if (!RD)
1115+
return nullptr;
1116+
1117+
// Get the full size of the struct.
1118+
ASTContext &Ctx = getContext();
1119+
const RecordDecl *OuterRD = RD->getOuterLexicalRecordContext();
1120+
const clang::Type *RT = OuterRD->getTypeForDecl();
1121+
CharUnits RecordSize = Ctx.getTypeSizeInChars(RT);
1122+
1123+
Value *Res = nullptr;
1124+
1125+
if (const auto *U = dyn_cast<UnaryOperator>(E);
1126+
U && (U->getOpcode() == UO_AddrOf || U->getOpcode() == UO_Deref))
1127+
E = U->getSubExpr()->IgnoreParenImpCasts();
1128+
1129+
if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
1130+
const Expr *Idx = ASE->getIdx();
1131+
Base = ASE->getBase()->IgnoreParenImpCasts();
1132+
1133+
if (const auto *ME = dyn_cast<MemberExpr>(Base);
1134+
ME && ME->getType()->isConstantArrayType()) {
1135+
// The simple case:
1136+
//
1137+
// struct s {
1138+
// int arr[42];
1139+
// char c;
1140+
// /* others */
1141+
// };
1142+
//
1143+
// __builtin_dynamic_object_size(&p->arr[idx], 0);
1144+
//
1145+
// We can translate the __builtin_dynamic_object_call into:
1146+
//
1147+
// sizeof(struct s) - offsetof(arr) - (idx * sizeof(int))
1148+
//
1149+
bool IsSigned = Idx->getType()->isSignedIntegerType();
1150+
Value *IdxInst = EmitAnyExprToTemp(Idx).getScalarVal();
1151+
IdxInst = Builder.CreateIntCast(IdxInst, ResType, IsSigned);
1152+
1153+
const ConstantArrayType *CAT = cast<ConstantArrayType>(ME->getType());
1154+
CharUnits ElemSize = Ctx.getTypeSizeInChars(CAT->getElementType());
1155+
Value *ElemSizeInst = Builder.getInt32(ElemSize.getQuantity());
1156+
ElemSizeInst = Builder.CreateIntCast(ElemSizeInst, ResType, IsSigned);
1157+
1158+
// idx * sizeof(<arr element type>)
1159+
Res = Builder.CreateMul(IdxInst, ElemSizeInst, "", !IsSigned, IsSigned);
1160+
1161+
// sizeof(struct s)
1162+
Value *RecordSizeInst = Builder.getInt32(RecordSize.getQuantity());
1163+
RecordSizeInst = Builder.CreateIntCast(RecordSizeInst, ResType,
1164+
RT->isSignedIntegerType());
1165+
1166+
// offsetof(arr)
1167+
int64_t Offset = 0;
1168+
getFieldOffsetInBits(OuterRD, cast<FieldDecl>(ME->getMemberDecl()),
1169+
Offset);
1170+
1171+
CharUnits OffsetVal = Ctx.toCharUnitsFromBits(Offset);
1172+
Value *OffsetInst = Builder.getInt64(OffsetVal.getQuantity());
1173+
OffsetInst = Builder.CreateIntCast(OffsetInst, ResType, IsSigned);
1174+
1175+
// sizeof(struct s) - offsetof(arr) ...
1176+
RecordSizeInst = Builder.CreateSub(RecordSizeInst, OffsetInst, "",
1177+
!IsSigned, IsSigned);
1178+
// ... - (idx * sizeof(<arr element type>))
1179+
RecordSizeInst =
1180+
Builder.CreateSub(RecordSizeInst, Res, "", !IsSigned, IsSigned);
1181+
1182+
// Don't allow the index or result to be negative.
1183+
Value *Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(Res),
1184+
Builder.CreateIsNotNeg(RecordSizeInst));
1185+
1186+
Res = Builder.CreateSelect(Cmp, RecordSizeInst,
1187+
ConstantInt::get(ResType, 0, IsSigned));
1188+
}
1189+
}
1190+
1191+
return Res;
1192+
}
1193+
10541194
/// Returns a Value corresponding to the size of the given expression.
10551195
/// This Value may be either of the following:
10561196
/// - 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,
10831223
}
10841224
}
10851225

1226+
// LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't
1227+
// evaluate E for side-effects. In either case, we shouldn't lower to
1228+
// @llvm.objectsize.
1229+
if (Type == 3 || (!EmittedE && E->HasSideEffects(getContext())))
1230+
return getDefaultBuiltinObjectSizeResult(Type, ResType);
1231+
10861232
if (IsDynamic) {
10871233
// Emit special code for a flexible array member with the "counted_by"
10881234
// attribute.
10891235
if (Value *V = emitFlexibleArrayMemberSize(E, Type, ResType))
10901236
return V;
1091-
}
10921237

1093-
// LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't
1094-
// evaluate E for side-effects. In either case, we shouldn't lower to
1095-
// @llvm.objectsize.
1096-
if (Type == 3 || (!EmittedE && E->HasSideEffects(getContext())))
1097-
return getDefaultBuiltinObjectSizeResult(Type, ResType);
1238+
if (Value *V = tryEmitObjectSizeCalculation(E, Type, ResType))
1239+
return V;
1240+
}
10981241

10991242
Value *Ptr = EmittedE ? EmittedE : EmitScalarExpr(E);
11001243
assert(Ptr->getType()->isPointerTy() &&

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4064,15 +4064,16 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr,
40644064
}
40654065

40664066
/// The offset of a field from the beginning of the record.
4067-
static bool getFieldOffsetInBits(CodeGenFunction &CGF, const RecordDecl *RD,
4068-
const FieldDecl *FD, int64_t &Offset) {
4069-
ASTContext &Ctx = CGF.getContext();
4067+
bool CodeGenFunction::getFieldOffsetInBits(const RecordDecl *RD,
4068+
const FieldDecl *FD,
4069+
int64_t &Offset) {
4070+
ASTContext &Ctx = getContext();
40704071
const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
40714072
unsigned FieldNo = 0;
40724073

40734074
for (const Decl *D : RD->decls()) {
40744075
if (const auto *Record = dyn_cast<RecordDecl>(D))
4075-
if (getFieldOffsetInBits(CGF, Record, FD, Offset)) {
4076+
if (getFieldOffsetInBits(Record, FD, Offset)) {
40764077
Offset += Layout.getFieldOffset(FieldNo);
40774078
return true;
40784079
}
@@ -4108,11 +4109,11 @@ static std::optional<int64_t> getOffsetDifferenceInBits(CodeGenFunction &CGF,
41084109
return std::optional<int64_t>();
41094110

41104111
int64_t FD1Offset = 0;
4111-
if (!getFieldOffsetInBits(CGF, FD1OuterRec, FD1, FD1Offset))
4112+
if (!CGF.getFieldOffsetInBits(FD1OuterRec, FD1, FD1Offset))
41124113
return std::optional<int64_t>();
41134114

41144115
int64_t FD2Offset = 0;
4115-
if (!getFieldOffsetInBits(CGF, FD2OuterRec, FD2, FD2Offset))
4116+
if (!CGF.getFieldOffsetInBits(FD2OuterRec, FD2, FD2Offset))
41164117
return std::optional<int64_t>();
41174118

41184119
return std::make_optional<int64_t>(FD1Offset - FD2Offset);

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3093,6 +3093,9 @@ class CodeGenFunction : public CodeGenTypeCache {
30933093
const FieldDecl *FAMDecl,
30943094
const FieldDecl *CountDecl);
30953095

3096+
bool getFieldOffsetInBits(const RecordDecl *RD, const FieldDecl *FD,
3097+
int64_t &Offset);
3098+
30963099
llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
30973100
bool isInc, bool isPre);
30983101
ComplexPairTy EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV,
@@ -4892,6 +4895,9 @@ class CodeGenFunction : public CodeGenTypeCache {
48924895
llvm::Value *EmittedE,
48934896
bool IsDynamic);
48944897

4898+
llvm::Value *tryEmitObjectSizeCalculation(const Expr *E, unsigned Type,
4899+
llvm::IntegerType *ResType);
4900+
48954901
llvm::Value *emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
48964902
llvm::IntegerType *ResType);
48974903

0 commit comments

Comments
 (0)