Skip to content

Commit 1b45e8c

Browse files
authored
[CIR] Add initial support for array cookies (#1297)
This patch adds the minimal support for array cookies needed to enable ClangIR generation for an array new expression that requires cookies but does not require an explicit initializer. This only provides the cookie support for the base Itanium CXXABI. Different cookie calculations are required for AppleARM64, which will be added in a subsequent patch.
1 parent 34bd74e commit 1b45e8c

File tree

8 files changed

+253
-17
lines changed

8 files changed

+253
-17
lines changed

clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ bool CIRGenCXXABI::isZeroInitializable(const MemberPointerType *MPT) {
8989
CharUnits CIRGenCXXABI::getArrayCookieSize(const CXXNewExpr *E) {
9090
if (!requiresArrayCookie(E))
9191
return CharUnits::Zero();
92-
llvm_unreachable("NYI");
92+
return getArrayCookieSizeImpl(E->getAllocatedType());
9393
}
9494

9595
bool CIRGenCXXABI::requiresArrayCookie(const CXXNewExpr *E) {

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

+20
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,26 @@ class CIRGenCXXABI {
362362
///
363363
/// \param E - the new-expression being allocated.
364364
virtual CharUnits getArrayCookieSize(const CXXNewExpr *E);
365+
366+
/// Initialize the array cookie for the given allocation.
367+
///
368+
/// \param NewPtr - a char* which is the presumed-non-null
369+
/// return value of the allocation function
370+
/// \param NumElements - the computed number of elements,
371+
/// potentially collapsed from the multidimensional array case;
372+
/// always a size_t
373+
/// \param ElementType - the base element allocated type,
374+
/// i.e. the allocated type after stripping all array types
375+
virtual Address initializeArrayCookie(CIRGenFunction &CGF, Address NewPtr,
376+
mlir::Value NumElements,
377+
const CXXNewExpr *E,
378+
QualType ElementType) = 0;
379+
380+
protected:
381+
/// Returns the extra size required in order to store the array
382+
/// cookie for the given type. Assumes that an array cookie is
383+
/// required.
384+
virtual CharUnits getArrayCookieSizeImpl(QualType ElementType) = 0;
365385
};
366386

367387
/// Creates and Itanium-family ABI

clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp

+60-4
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,7 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &CGF, const CXXNewExpr *e,
623623
mlir::cast<cir::IntAttr>(constNumElements).getValue();
624624

625625
unsigned numElementsWidth = count.getBitWidth();
626+
bool hasAnyOverflow = false;
626627

627628
// The equivalent code in CodeGen/CGExprCXX.cpp handles these cases as
628629
// overflow, but they should never happen. The size argument is implicitly
@@ -653,10 +654,22 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &CGF, const CXXNewExpr *e,
653654

654655
// Add in the cookie, and check whether it's overflowed.
655656
if (cookieSize != 0) {
656-
llvm_unreachable("NYI");
657+
// Save the current size without a cookie. This shouldn't be
658+
// used if there was overflow.
659+
sizeWithoutCookie = CGF.getBuilder().getConstInt(
660+
Loc, allocationSize.zextOrTrunc(sizeWidth));
661+
662+
allocationSize = allocationSize.uadd_ov(cookieSize, overflow);
663+
hasAnyOverflow |= overflow;
657664
}
658665

659-
size = CGF.getBuilder().getConstInt(Loc, allocationSize);
666+
// On overflow, produce a -1 so operator new will fail.
667+
if (hasAnyOverflow) {
668+
size =
669+
CGF.getBuilder().getConstInt(Loc, llvm::APInt::getAllOnes(sizeWidth));
670+
} else {
671+
size = CGF.getBuilder().getConstInt(Loc, allocationSize);
672+
}
660673
} else {
661674
// TODO: Handle the variable size case
662675
llvm_unreachable("NYI");
@@ -858,6 +871,46 @@ void CIRGenFunction::emitNewArrayInitializer(
858871
if (!E->hasInitializer())
859872
return;
860873

874+
Address CurPtr = BeginPtr;
875+
876+
unsigned InitListElements = 0;
877+
878+
const Expr *Init = E->getInitializer();
879+
CleanupDeactivationScope deactivation(*this);
880+
881+
const InitListExpr *ILE = dyn_cast<InitListExpr>(Init);
882+
if (ILE) {
883+
llvm_unreachable("NYI");
884+
}
885+
886+
// If all elements have already been initialized, skip any further
887+
// initialization.
888+
auto ConstOp = dyn_cast<cir::ConstantOp>(NumElements.getDefiningOp());
889+
if (ConstOp) {
890+
auto ConstIntAttr = mlir::dyn_cast<cir::IntAttr>(ConstOp.getValue());
891+
// Just skip out if the constant count is zero.
892+
if (ConstIntAttr && ConstIntAttr.getUInt() <= InitListElements)
893+
return;
894+
}
895+
896+
assert(Init && "have trailing elements to initialize but no initializer");
897+
898+
// If this is a constructor call, try to optimize it out, and failing that
899+
// emit a single loop to initialize all remaining elements.
900+
if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(Init)) {
901+
CXXConstructorDecl *Ctor = CCE->getConstructor();
902+
if (Ctor->isTrivial()) {
903+
// If new expression did not specify value-initialization, then there
904+
// is no initialization.
905+
if (!CCE->requiresZeroInitialization() || Ctor->getParent()->isEmpty())
906+
return;
907+
908+
llvm_unreachable("NYI");
909+
}
910+
911+
llvm_unreachable("NYI");
912+
}
913+
861914
llvm_unreachable("NYI");
862915
}
863916

@@ -1088,7 +1141,8 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *E) {
10881141
++ParamsToSkip;
10891142

10901143
if (allocSize != allocSizeWithoutCookie) {
1091-
llvm_unreachable("NYI");
1144+
CharUnits cookieAlign = getSizeAlign(); // FIXME: Ask the ABI.
1145+
allocAlign = std::max(allocAlign, cookieAlign);
10921146
}
10931147

10941148
// The allocation alignment may be passed as the second argument.
@@ -1186,7 +1240,9 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *E) {
11861240
assert((allocSize == allocSizeWithoutCookie) ==
11871241
CalculateCookiePadding(*this, E).isZero());
11881242
if (allocSize != allocSizeWithoutCookie) {
1189-
llvm_unreachable("NYI");
1243+
assert(E->isArray());
1244+
allocation = CGM.getCXXABI().initializeArrayCookie(
1245+
*this, allocation, numElements, E, allocType);
11901246
}
11911247

11921248
mlir::Type elementTy;

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

+64
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,13 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
324324
cir::MethodAttr buildVirtualMethodAttr(cir::MethodType MethodTy,
325325
const CXXMethodDecl *MD) override;
326326

327+
Address initializeArrayCookie(CIRGenFunction &CGF, Address NewPtr,
328+
mlir::Value NumElements, const CXXNewExpr *E,
329+
QualType ElementType) override;
330+
331+
protected:
332+
CharUnits getArrayCookieSizeImpl(QualType ElementType) override;
333+
327334
/**************************** RTTI Uniqueness ******************************/
328335
protected:
329336
/// Returns true if the ABI requires RTTI type_info objects to be unique
@@ -2637,3 +2644,60 @@ CIRGenItaniumCXXABI::buildVirtualMethodAttr(cir::MethodType MethodTy,
26372644
bool CIRGenItaniumCXXABI::isZeroInitializable(const MemberPointerType *MPT) {
26382645
return MPT->isMemberFunctionPointer();
26392646
}
2647+
2648+
/************************** Array allocation cookies **************************/
2649+
2650+
CharUnits CIRGenItaniumCXXABI::getArrayCookieSizeImpl(QualType ElementType) {
2651+
// The array cookie is a size_t; pad that up to the element alignment.
2652+
// The cookie is actually right-justified in that space.
2653+
return std::max(
2654+
CharUnits::fromQuantity(CGM.SizeSizeInBytes),
2655+
CGM.getASTContext().getPreferredTypeAlignInChars(ElementType));
2656+
}
2657+
2658+
Address CIRGenItaniumCXXABI::initializeArrayCookie(CIRGenFunction &CGF,
2659+
Address NewPtr,
2660+
mlir::Value NumElements,
2661+
const CXXNewExpr *E,
2662+
QualType ElementType) {
2663+
assert(requiresArrayCookie(E));
2664+
2665+
// TODO: Get the address space when sanitizer support is implemented
2666+
2667+
ASTContext &Ctx = getContext();
2668+
CharUnits SizeSize = CGF.getSizeSize();
2669+
mlir::Location Loc = CGF.getLoc(E->getSourceRange());
2670+
2671+
// The size of the cookie.
2672+
CharUnits CookieSize =
2673+
std::max(SizeSize, Ctx.getPreferredTypeAlignInChars(ElementType));
2674+
assert(CookieSize == getArrayCookieSizeImpl(ElementType));
2675+
2676+
// Compute an offset to the cookie.
2677+
Address CookiePtr = NewPtr;
2678+
CharUnits CookieOffset = CookieSize - SizeSize;
2679+
if (!CookieOffset.isZero()) {
2680+
auto CastOp = CGF.getBuilder().createPtrBitcast(
2681+
CookiePtr.getPointer(), CGF.getBuilder().getUIntNTy(8));
2682+
auto OffsetOp = CGF.getBuilder().getSignedInt(
2683+
Loc, CookieOffset.getQuantity(), /*width=*/32);
2684+
auto DataPtr = CGF.getBuilder().createPtrStride(Loc, CastOp, OffsetOp);
2685+
CookiePtr = Address(DataPtr, NewPtr.getType(), NewPtr.getAlignment());
2686+
}
2687+
2688+
// Write the number of elements into the appropriate slot.
2689+
Address NumElementsPtr = CookiePtr.withElementType(CGF.SizeTy);
2690+
CGF.getBuilder().createStore(Loc, NumElements, NumElementsPtr);
2691+
2692+
if (CGF.SanOpts.has(SanitizerKind::Address))
2693+
llvm_unreachable("NYI");
2694+
2695+
// Finally, compute a pointer to the actual data buffer by skipping
2696+
// over the cookie completely.
2697+
int64_t Offset = (CookieSize.getQuantity());
2698+
auto CastOp = CGF.getBuilder().createPtrBitcast(
2699+
NewPtr.getPointer(), CGF.getBuilder().getUIntNTy(8));
2700+
auto OffsetOp = CGF.getBuilder().getSignedInt(Loc, Offset, /*width=*/32);
2701+
auto DataPtr = CGF.getBuilder().createPtrStride(Loc, CastOp, OffsetOp);
2702+
return Address(DataPtr, NewPtr.getType(), NewPtr.getAlignment());
2703+
}

clang/lib/CIR/CodeGen/CIRGenModule.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,10 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
145145
.toCharUnitsFromBits(
146146
astContext.getTargetInfo().getPointerAlign(LangAS::Default))
147147
.getQuantity();
148-
// TODO: SizeSizeInBytes
148+
SizeSizeInBytes =
149+
astContext
150+
.toCharUnitsFromBits(astContext.getTargetInfo().getMaxPointerWidth())
151+
.getQuantity();
149152
// TODO: IntAlignInBytes
150153
UCharTy = cir::IntType::get(&getMLIRContext(),
151154
astContext.getTargetInfo().getCharWidth(),

clang/lib/CIR/CodeGen/CIRGenTypeCache.h

+10-10
Original file line numberDiff line numberDiff line change
@@ -105,19 +105,19 @@ struct CIRGenTypeCache {
105105
};
106106

107107
/// The size and alignment of size_t.
108-
// union {
109-
// unsigned char SizeSizeInBytes; // sizeof(size_t)
110-
// unsigned char SizeAlignInBytes;
111-
// };
108+
union {
109+
unsigned char SizeSizeInBytes; // sizeof(size_t)
110+
unsigned char SizeAlignInBytes;
111+
};
112112

113113
cir::AddressSpaceAttr CIRAllocaAddressSpace;
114114

115-
// clang::CharUnits getSizeSize() const {
116-
// return clang::CharUnits::fromQuantity(SizeSizeInBytes);
117-
// }
118-
// clang::CharUnits getSizeAlign() const {
119-
// return clang::CharUnits::fromQuantity(SizeAlignInBytes);
120-
// }
115+
clang::CharUnits getSizeSize() const {
116+
return clang::CharUnits::fromQuantity(SizeSizeInBytes);
117+
}
118+
clang::CharUnits getSizeAlign() const {
119+
return clang::CharUnits::fromQuantity(SizeAlignInBytes);
120+
}
121121
clang::CharUnits getPointerSize() const {
122122
return clang::CharUnits::fromQuantity(PointerSizeInBytes);
123123
}

clang/test/CIR/CodeGen/new.cpp

+54
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,57 @@ void t_new_multidim_constant_size() {
9090
// CHECK: %5 = cir.cast(bitcast, %0 : !cir.ptr<!cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>>), !cir.ptr<!cir.ptr<!cir.double>>
9191
// CHECK: cir.store %4, %5 : !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>
9292
// CHECK: }
93+
94+
class C {
95+
public:
96+
~C();
97+
};
98+
99+
void t_constant_size_nontrivial() {
100+
auto p = new C[3];
101+
}
102+
103+
// CHECK: cir.func @_Z26t_constant_size_nontrivialv()
104+
// CHECK: %0 = cir.alloca !cir.ptr<!ty_C>, !cir.ptr<!cir.ptr<!ty_C>>, ["p", init] {alignment = 8 : i64}
105+
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<3> : !u64i
106+
// CHECK: %[[#SIZE_WITHOUT_COOKIE:]] = cir.const #cir.int<3> : !u64i
107+
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<11> : !u64i
108+
// CHECK: %4 = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
109+
// CHECK: %5 = cir.cast(bitcast, %4 : !cir.ptr<!void>), !cir.ptr<!u64i>
110+
// CHECK: cir.store %[[#NUM_ELEMENTS]], %5 : !u64i, !cir.ptr<!u64i>
111+
// CHECK: %6 = cir.cast(bitcast, %4 : !cir.ptr<!void>), !cir.ptr<!u8i>
112+
// CHECK: %[[#COOKIE_SIZE:]] = cir.const #cir.int<8> : !s32i
113+
// CHECK: %8 = cir.ptr_stride(%6 : !cir.ptr<!u8i>, %[[#COOKIE_SIZE]] : !s32i), !cir.ptr<!u8i>
114+
// CHECK: %9 = cir.cast(bitcast, %8 : !cir.ptr<!u8i>), !cir.ptr<!ty_C>
115+
// CHECK: cir.store %9, %0 : !cir.ptr<!ty_C>, !cir.ptr<!cir.ptr<!ty_C>>
116+
// CHECK: cir.return
117+
// CHECK: }
118+
119+
class D {
120+
public:
121+
int x;
122+
~D();
123+
};
124+
125+
void t_constant_size_nontrivial2() {
126+
auto p = new D[3];
127+
}
128+
129+
// In this test SIZE_WITHOUT_COOKIE isn't used, but it would be if there were
130+
// an initializer.
131+
132+
// CHECK: cir.func @_Z27t_constant_size_nontrivial2v()
133+
// CHECK: %0 = cir.alloca !cir.ptr<!ty_D>, !cir.ptr<!cir.ptr<!ty_D>>, ["p", init] {alignment = 8 : i64}
134+
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<3> : !u64i
135+
// CHECK: %[[#SIZE_WITHOUT_COOKIE:]] = cir.const #cir.int<12> : !u64i
136+
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<20> : !u64i
137+
// CHECK: %4 = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
138+
// CHECK: %5 = cir.cast(bitcast, %4 : !cir.ptr<!void>), !cir.ptr<!u64i>
139+
// CHECK: cir.store %[[#NUM_ELEMENTS]], %5 : !u64i, !cir.ptr<!u64i>
140+
// CHECK: %6 = cir.cast(bitcast, %4 : !cir.ptr<!void>), !cir.ptr<!u8i>
141+
// CHECK: %[[#COOKIE_SIZE:]] = cir.const #cir.int<8> : !s32i
142+
// CHECK: %8 = cir.ptr_stride(%6 : !cir.ptr<!u8i>, %[[#COOKIE_SIZE]] : !s32i), !cir.ptr<!u8i>
143+
// CHECK: %9 = cir.cast(bitcast, %8 : !cir.ptr<!u8i>), !cir.ptr<!ty_D>
144+
// CHECK: cir.store %9, %0 : !cir.ptr<!ty_D>, !cir.ptr<!cir.ptr<!ty_D>>
145+
// CHECK: cir.return
146+
// CHECK: }

clang/test/CIR/Lowering/new.cpp

+40-1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,43 @@ void t_new_multidim_constant_size() {
1717
// LLVM: @_Z28t_new_multidim_constant_sizev()
1818
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
1919
// LLVM: %[[ADDR:.*]] = call ptr @_Znam(i64 192)
20-
// LLVM: store ptr %[[ADDR]], ptr %[[ALLOCA]], align 8
20+
// LLVM: store ptr %[[ADDR]], ptr %[[ALLOCA]], align 8
21+
22+
class C {
23+
public:
24+
~C();
25+
};
26+
27+
void t_constant_size_nontrivial() {
28+
auto p = new C[3];
29+
}
30+
31+
// Note: The below differs from the IR emitted by clang without -fclangir in
32+
// several respects. (1) The alloca here has an extra "i64 1"
33+
// (2) The operator new call is missing "noalias noundef nonnull" on
34+
// the call and "noundef" on the argument, (3) The getelementptr is
35+
// missing "inbounds"
36+
37+
// LLVM: @_Z26t_constant_size_nontrivialv()
38+
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
39+
// LLVM: %[[COOKIE_PTR:.*]] = call ptr @_Znam(i64 11)
40+
// LLVM: store i64 3, ptr %[[COOKIE_PTR]], align 8
41+
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr i8, ptr %[[COOKIE_PTR]], i64 8
42+
// LLVM: store ptr %[[ALLOCATED_PTR]], ptr %[[ALLOCA]], align 8
43+
44+
class D {
45+
public:
46+
int x;
47+
~D();
48+
};
49+
50+
void t_constant_size_nontrivial2() {
51+
auto p = new D[3];
52+
}
53+
54+
// LLVM: @_Z27t_constant_size_nontrivial2v()
55+
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
56+
// LLVM: %[[COOKIE_PTR:.*]] = call ptr @_Znam(i64 20)
57+
// LLVM: store i64 3, ptr %[[COOKIE_PTR]], align 8
58+
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr i8, ptr %[[COOKIE_PTR]], i64 8
59+
// LLVM: store ptr %[[ALLOCATED_PTR]], ptr %[[ALLOCA]], align 8

0 commit comments

Comments
 (0)