Skip to content

Commit c8e3813

Browse files
committed
[CIR] Add limited support for array new
1 parent 7de73c4 commit c8e3813

File tree

6 files changed

+243
-7
lines changed

6 files changed

+243
-7
lines changed

clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "CIRGenFunction.h"
1616

1717
#include "clang/AST/Decl.h"
18+
#include "clang/AST/ExprCXX.h"
1819
#include "clang/AST/GlobalDecl.h"
1920

2021
using namespace clang;
@@ -75,3 +76,20 @@ void CIRGenCXXABI::setCXXABIThisValue(CIRGenFunction &cgf,
7576
assert(getThisDecl(cgf) && "no 'this' variable for function");
7677
cgf.cxxabiThisValue = thisPtr;
7778
}
79+
80+
CharUnits CIRGenCXXABI::getArrayCookieSize(const CXXNewExpr *E) {
81+
if (!requiresArrayCookie(E))
82+
return CharUnits::Zero();
83+
84+
cgm.errorNYI(E->getSourceRange(), "CIRGenCXXABI::getArrayCookieSize");
85+
return CharUnits::Zero();
86+
}
87+
88+
bool CIRGenCXXABI::requiresArrayCookie(const CXXNewExpr *E) {
89+
// If the class's usual deallocation function takes two arguments,
90+
// it needs a cookie.
91+
if (E->doesUsualArrayDeleteWantSize())
92+
return true;
93+
94+
return E->getAllocatedType().isDestructedType();
95+
}

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class CIRGenCXXABI {
2828
CIRGenModule &cgm;
2929
std::unique_ptr<clang::MangleContext> mangleContext;
3030

31+
virtual bool requiresArrayCookie(const CXXNewExpr *E);
32+
3133
public:
3234
// TODO(cir): make this protected when target-specific CIRGenCXXABIs are
3335
// implemented.
@@ -241,6 +243,19 @@ class CIRGenCXXABI {
241243
void setStructorImplicitParamValue(CIRGenFunction &cgf, mlir::Value val) {
242244
cgf.cxxStructorImplicitParamValue = val;
243245
}
246+
247+
/**************************** Array cookies ******************************/
248+
249+
/// Returns the extra size required in order to store the array
250+
/// cookie for the given new-expression. May return 0 to indicate that no
251+
/// array cookie is required.
252+
///
253+
/// Several cases are filtered out before this method is called:
254+
/// - non-array allocations never need a cookie
255+
/// - calls to \::operator new(size_t, void*) never need a cookie
256+
///
257+
/// \param E - the new-expression being allocated.
258+
virtual CharUnits getArrayCookieSize(const CXXNewExpr *E);
244259
};
245260

246261
/// Creates and Itanium-family ABI

clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp

Lines changed: 132 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "CIRGenCXXABI.h"
14+
#include "CIRGenConstantEmitter.h"
1415
#include "CIRGenFunction.h"
1516

1617
#include "clang/AST/DeclCXX.h"
@@ -264,6 +265,19 @@ static UsualDeleteParams getUsualDeleteParams(const FunctionDecl *fd) {
264265
return params;
265266
}
266267

268+
static CharUnits calculateCookiePadding(CIRGenFunction &cgf,
269+
const CXXNewExpr *e) {
270+
if (!e->isArray())
271+
return CharUnits::Zero();
272+
273+
// No cookie is required if the operator new[] being used is the
274+
// reserved placement operator new[].
275+
if (e->getOperatorNew()->isReservedGlobalPlacementOperator())
276+
return CharUnits::Zero();
277+
278+
return cgf.cgm.getCXXABI().getArrayCookieSize(e);
279+
}
280+
267281
static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
268282
unsigned minElements,
269283
mlir::Value &numElements,
@@ -278,8 +292,98 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
278292
return sizeWithoutCookie;
279293
}
280294

281-
cgf.cgm.errorNYI(e->getSourceRange(), "emitCXXNewAllocSize: array");
282-
return {};
295+
// The width of size_t.
296+
unsigned sizeWidth = cgf.cgm.getDataLayout().getTypeSizeInBits(cgf.SizeTy);
297+
298+
// The number of elements can be have an arbitrary integer type;
299+
// essentially, we need to multiply it by a constant factor, add a
300+
// cookie size, and verify that the result is representable as a
301+
// size_t. That's just a gloss, though, and it's wrong in one
302+
// important way: if the count is negative, it's an error even if
303+
// the cookie size would bring the total size >= 0.
304+
//
305+
// If the array size is constant, Sema will have prevented negative
306+
// values and size overflow.
307+
308+
// Compute the constant factor.
309+
llvm::APInt arraySizeMultiplier(sizeWidth, 1);
310+
while (const ConstantArrayType *cat =
311+
cgf.getContext().getAsConstantArrayType(type)) {
312+
type = cat->getElementType();
313+
arraySizeMultiplier *= cat->getSize();
314+
}
315+
316+
CharUnits typeSize = cgf.getContext().getTypeSizeInChars(type);
317+
llvm::APInt typeSizeMultiplier(sizeWidth, typeSize.getQuantity());
318+
typeSizeMultiplier *= arraySizeMultiplier;
319+
320+
// Figure out the cookie size.
321+
llvm::APInt cookieSize(sizeWidth,
322+
calculateCookiePadding(cgf, e).getQuantity());
323+
324+
// This will be a size_t.
325+
mlir::Value size;
326+
327+
// Emit the array size expression.
328+
// We multiply the size of all dimensions for NumElements.
329+
// e.g for 'int[2][3]', ElemType is 'int' and NumElements is 6.
330+
const Expr *arraySize = *e->getArraySize();
331+
mlir::Attribute constNumElements =
332+
ConstantEmitter(cgf.cgm, &cgf)
333+
.emitAbstract(arraySize, arraySize->getType());
334+
if (constNumElements) {
335+
// Get an APInt from the constant
336+
const llvm::APInt &count =
337+
mlir::cast<cir::IntAttr>(constNumElements).getValue();
338+
339+
unsigned numElementsWidth = count.getBitWidth();
340+
341+
// The equivalent code in CodeGen/CGExprCXX.cpp handles these cases as
342+
// overflow, but they should never happen. The size argument is implicitly
343+
// cast to a size_t, so it can never be negative and numElementsWidth will
344+
// always equal sizeWidth.
345+
assert(!count.isNegative() && "Expected non-negative array size");
346+
assert(numElementsWidth == sizeWidth &&
347+
"Expected a size_t array size constant");
348+
349+
// Okay, compute a count at the right width.
350+
llvm::APInt adjustedCount = count.zextOrTrunc(sizeWidth);
351+
352+
// Scale numElements by that. This might overflow, but we don't
353+
// care because it only overflows if allocationSize does, too, and
354+
// if that overflows then we shouldn't use this.
355+
// This emits a constant that may not be used, but we can't tell here
356+
// whether it will be needed or not.
357+
numElements =
358+
cgf.getBuilder().getConstInt(loc, adjustedCount * arraySizeMultiplier);
359+
360+
// Compute the size before cookie, and track whether it overflowed.
361+
bool overflow;
362+
llvm::APInt allocationSize =
363+
adjustedCount.umul_ov(typeSizeMultiplier, overflow);
364+
365+
// Sema prevents us from hitting this case
366+
assert(!overflow && "Overflow in array allocation size");
367+
368+
// Add in the cookie, and check whether it's overflowed.
369+
if (cookieSize != 0) {
370+
cgf.cgm.errorNYI(e->getSourceRange(),
371+
"emitCXXNewAllocSize: array cookie");
372+
}
373+
374+
size = cgf.getBuilder().getConstInt(loc, allocationSize);
375+
} else {
376+
// TODO: Handle the variable size case
377+
cgf.cgm.errorNYI(e->getSourceRange(),
378+
"emitCXXNewAllocSize: variable array size");
379+
}
380+
381+
if (cookieSize == 0)
382+
sizeWithoutCookie = size;
383+
else
384+
assert(sizeWithoutCookie && "didn't set sizeWithoutCookie?");
385+
386+
return size;
283387
}
284388

285389
static void storeAnyExprIntoOneUnit(CIRGenFunction &cgf, const Expr *init,
@@ -308,13 +412,26 @@ static void storeAnyExprIntoOneUnit(CIRGenFunction &cgf, const Expr *init,
308412
llvm_unreachable("bad evaluation kind");
309413
}
310414

415+
void CIRGenFunction::emitNewArrayInitializer(
416+
const CXXNewExpr *e, QualType elementType, mlir::Type elementTy,
417+
Address beginPtr, mlir::Value numElements,
418+
mlir::Value allocSizeWithoutCookie) {
419+
// If we have a type with trivial initialization and no initializer,
420+
// there's nothing to do.
421+
if (!e->hasInitializer())
422+
return;
423+
424+
llvm_unreachable("NYI");
425+
}
426+
311427
static void emitNewInitializer(CIRGenFunction &cgf, const CXXNewExpr *e,
312428
QualType elementType, mlir::Type elementTy,
313429
Address newPtr, mlir::Value numElements,
314430
mlir::Value allocSizeWithoutCookie) {
315431
assert(!cir::MissingFeatures::generateDebugInfo());
316432
if (e->isArray()) {
317-
cgf.cgm.errorNYI(e->getSourceRange(), "emitNewInitializer: array");
433+
cgf.emitNewArrayInitializer(e, elementType, elementTy, newPtr, numElements,
434+
allocSizeWithoutCookie);
318435
} else if (const Expr *init = e->getInitializer()) {
319436
storeAnyExprIntoOneUnit(cgf, init, e->getAllocatedType(), newPtr,
320437
AggValueSlot::DoesNotOverlap);
@@ -583,14 +700,22 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *e) {
583700

584701
// If there's an operator delete, enter a cleanup to call it if an
585702
// exception is thrown.
586-
if (e->getOperatorDelete() &&
587-
!e->getOperatorDelete()->isReservedGlobalPlacementOperator())
588-
cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: operator delete");
703+
// TODO: Handle operator delete cleanup for exception safety
704+
// if (e->getOperatorDelete() &&
705+
// !e->getOperatorDelete()->isReservedGlobalPlacementOperator())
706+
// cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: operator delete");
589707

590708
if (allocSize != allocSizeWithoutCookie)
591709
cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: array with cookies");
592710

593-
mlir::Type elementTy = convertTypeForMem(allocType);
711+
mlir::Type elementTy;
712+
if (e->isArray()) {
713+
// For array new, use the allocated type to handle multidimensional arrays
714+
// correctly
715+
elementTy = convertTypeForMem(e->getAllocatedType());
716+
} else {
717+
elementTy = convertTypeForMem(allocType);
718+
}
594719
Address result = builder.createElementBitCast(getLoc(e->getSourceRange()),
595720
allocation, elementTy);
596721

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,11 @@ class CIRGenFunction : public CIRGenTypeCache {
12281228

12291229
mlir::Value emitCXXNewExpr(const CXXNewExpr *e);
12301230

1231+
void emitNewArrayInitializer(const CXXNewExpr *E, QualType ElementType,
1232+
mlir::Type ElementTy, Address BeginPtr,
1233+
mlir::Value NumElements,
1234+
mlir::Value AllocSizeWithoutCookie);
1235+
12311236
RValue emitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *e,
12321237
const CXXMethodDecl *md,
12331238
ReturnValueSlot returnValue);

clang/test/CIR/CodeGen/new.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,56 @@ void test_new_with_complex_type() {
180180
// OGCG: store float 1.000000e+00, ptr %[[COMPLEX_REAL_PTR]], align 8
181181
// OGCG: store float 2.000000e+00, ptr %[[COMPLEX_IMAG_PTR]], align 4
182182
// OGCG: store ptr %[[NEW_COMPLEX]], ptr %[[A_ADDR]], align 8
183+
184+
void t_new_constant_size() {
185+
auto p = new double[16];
186+
}
187+
188+
// In this test, NUM_ELEMENTS isn't used because no cookie is needed and there
189+
// are no constructor calls needed.
190+
191+
// CHECK: cir.func{{.*}} @_Z19t_new_constant_sizev()
192+
// CHECK: %0 = cir.alloca !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>, ["p", init] {alignment = 8 : i64}
193+
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<16> : !u64i
194+
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<128> : !u64i
195+
// CHECK: %3 = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
196+
// CHECK: %4 = cir.cast(bitcast, %3 : !cir.ptr<!void>), !cir.ptr<!cir.double>
197+
// CHECK: cir.store align(8) %4, %0 : !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>
198+
// CHECK: cir.return
199+
// CHECK: }
200+
201+
// LLVM: define{{.*}} void @_Z19t_new_constant_sizev
202+
// LLVM: %[[P_ADDR:.*]] = alloca ptr, i64 1, align 8
203+
// LLVM: %[[CALL:.*]] = call ptr @_Znam(i64 128)
204+
// LLVM: store ptr %[[CALL]], ptr %[[P_ADDR]], align 8
205+
206+
// OGCG: define{{.*}} void @_Z19t_new_constant_sizev
207+
// OGCG: %[[P_ADDR:.*]] = alloca ptr, align 8
208+
// OGCG: %[[CALL:.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 128)
209+
// OGCG: store ptr %[[CALL]], ptr %[[P_ADDR]], align 8
210+
211+
212+
void t_new_multidim_constant_size() {
213+
auto p = new double[2][3][4];
214+
}
215+
216+
// As above, NUM_ELEMENTS isn't used.
217+
218+
// CHECK: cir.func{{.*}} @_Z28t_new_multidim_constant_sizev()
219+
// CHECK: %0 = cir.alloca !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>, !cir.ptr<!cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>>, ["p", init] {alignment = 8 : i64}
220+
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<24> : !u64i
221+
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<192> : !u64i
222+
// CHECK: %3 = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
223+
// CHECK: %4 = cir.cast(bitcast, %3 : !cir.ptr<!void>), !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>
224+
// CHECK: cir.store align(8) %4, %0 : !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>, !cir.ptr<!cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>>
225+
// CHECK: }
226+
227+
// LLVM: define{{.*}} void @_Z28t_new_multidim_constant_sizev
228+
// LLVM: %[[P_ADDR:.*]] = alloca ptr, i64 1, align 8
229+
// LLVM: %[[CALL:.*]] = call ptr @_Znam(i64 192)
230+
// LLVM: store ptr %[[CALL]], ptr %[[P_ADDR]], align 8
231+
232+
// OGCG: define{{.*}} void @_Z28t_new_multidim_constant_sizev
233+
// OGCG: %[[P_ADDR:.*]] = alloca ptr, align 8
234+
// OGCG: %[[CALL:.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 192)
235+
// OGCG: store ptr %[[CALL]], ptr %[[P_ADDR]], align 8

clang/test/CIR/Lowering/new.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
2+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
3+
4+
void t_new_constant_size() {
5+
auto p = new double[16];
6+
}
7+
8+
// LLVM: @_Z19t_new_constant_sizev()
9+
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
10+
// LLVM: %[[ADDR:.*]] = call ptr @_Znam(i64 128)
11+
// LLVM: store ptr %[[ADDR]], ptr %[[ALLOCA]], align 8
12+
13+
void t_new_multidim_constant_size() {
14+
auto p = new double[2][3][4];
15+
}
16+
17+
// LLVM: @_Z28t_new_multidim_constant_sizev()
18+
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
19+
// LLVM: %[[ADDR:.*]] = call ptr @_Znam(i64 192)
20+
// LLVM: store ptr %[[ADDR]], ptr %[[ALLOCA]], align 8

0 commit comments

Comments
 (0)