Skip to content

Commit 09f0f38

Browse files
authored
[CIR] Add limited support for array new (#161095)
This change adds initial support for array new expressions where the array size is constant and the element does not require a cookie. Ported from ClangIR incubator PR [#1286 ](llvm/clangir#1286). This is the first PR in a series intended to close #160383.
1 parent f43721a commit 09f0f38

File tree

5 files changed

+219
-4
lines changed

5 files changed

+219
-4
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.
@@ -245,6 +247,19 @@ class CIRGenCXXABI {
245247
void setStructorImplicitParamValue(CIRGenFunction &cgf, mlir::Value val) {
246248
cgf.cxxStructorImplicitParamValue = val;
247249
}
250+
251+
/**************************** Array cookies ******************************/
252+
253+
/// Returns the extra size required in order to store the array
254+
/// cookie for the given new-expression. May return 0 to indicate that no
255+
/// array cookie is required.
256+
///
257+
/// Several cases are filtered out before this method is called:
258+
/// - non-array allocations never need a cookie
259+
/// - calls to \::operator new(size_t, void*) never need a cookie
260+
///
261+
/// \param E - the new-expression being allocated.
262+
virtual CharUnits getArrayCookieSize(const CXXNewExpr *e);
248263
};
249264

250265
/// Creates and Itanium-family ABI

clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp

Lines changed: 128 additions & 4 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"
@@ -210,6 +211,19 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorCall(
210211
return emitCall(fnInfo, callee, returnValue, args, nullptr, loc);
211212
}
212213

214+
static CharUnits calculateCookiePadding(CIRGenFunction &cgf,
215+
const CXXNewExpr *e) {
216+
if (!e->isArray())
217+
return CharUnits::Zero();
218+
219+
// No cookie is required if the operator new[] being used is the
220+
// reserved placement operator new[].
221+
if (e->getOperatorNew()->isReservedGlobalPlacementOperator())
222+
return CharUnits::Zero();
223+
224+
return cgf.cgm.getCXXABI().getArrayCookieSize(e);
225+
}
226+
213227
static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
214228
unsigned minElements,
215229
mlir::Value &numElements,
@@ -224,8 +238,98 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
224238
return sizeWithoutCookie;
225239
}
226240

227-
cgf.cgm.errorNYI(e->getSourceRange(), "emitCXXNewAllocSize: array");
228-
return {};
241+
// The width of size_t.
242+
unsigned sizeWidth = cgf.cgm.getDataLayout().getTypeSizeInBits(cgf.SizeTy);
243+
244+
// The number of elements can be have an arbitrary integer type;
245+
// essentially, we need to multiply it by a constant factor, add a
246+
// cookie size, and verify that the result is representable as a
247+
// size_t. That's just a gloss, though, and it's wrong in one
248+
// important way: if the count is negative, it's an error even if
249+
// the cookie size would bring the total size >= 0.
250+
//
251+
// If the array size is constant, Sema will have prevented negative
252+
// values and size overflow.
253+
254+
// Compute the constant factor.
255+
llvm::APInt arraySizeMultiplier(sizeWidth, 1);
256+
while (const ConstantArrayType *cat =
257+
cgf.getContext().getAsConstantArrayType(type)) {
258+
type = cat->getElementType();
259+
arraySizeMultiplier *= cat->getSize();
260+
}
261+
262+
CharUnits typeSize = cgf.getContext().getTypeSizeInChars(type);
263+
llvm::APInt typeSizeMultiplier(sizeWidth, typeSize.getQuantity());
264+
typeSizeMultiplier *= arraySizeMultiplier;
265+
266+
// Figure out the cookie size.
267+
llvm::APInt cookieSize(sizeWidth,
268+
calculateCookiePadding(cgf, e).getQuantity());
269+
270+
// This will be a size_t.
271+
mlir::Value size;
272+
273+
// Emit the array size expression.
274+
// We multiply the size of all dimensions for NumElements.
275+
// e.g for 'int[2][3]', ElemType is 'int' and NumElements is 6.
276+
const Expr *arraySize = *e->getArraySize();
277+
mlir::Attribute constNumElements =
278+
ConstantEmitter(cgf.cgm, &cgf)
279+
.emitAbstract(arraySize, arraySize->getType());
280+
if (constNumElements) {
281+
// Get an APInt from the constant
282+
const llvm::APInt &count =
283+
mlir::cast<cir::IntAttr>(constNumElements).getValue();
284+
285+
unsigned numElementsWidth = count.getBitWidth();
286+
287+
// The equivalent code in CodeGen/CGExprCXX.cpp handles these cases as
288+
// overflow, but that should never happen. The size argument is implicitly
289+
// cast to a size_t, so it can never be negative and numElementsWidth will
290+
// always equal sizeWidth.
291+
assert(!count.isNegative() && "Expected non-negative array size");
292+
assert(numElementsWidth == sizeWidth &&
293+
"Expected a size_t array size constant");
294+
295+
// Okay, compute a count at the right width.
296+
llvm::APInt adjustedCount = count.zextOrTrunc(sizeWidth);
297+
298+
// Scale numElements by that. This might overflow, but we don't
299+
// care because it only overflows if allocationSize does too, and
300+
// if that overflows then we shouldn't use this.
301+
// This emits a constant that may not be used, but we can't tell here
302+
// whether it will be needed or not.
303+
numElements =
304+
cgf.getBuilder().getConstInt(loc, adjustedCount * arraySizeMultiplier);
305+
306+
// Compute the size before cookie, and track whether it overflowed.
307+
bool overflow;
308+
llvm::APInt allocationSize =
309+
adjustedCount.umul_ov(typeSizeMultiplier, overflow);
310+
311+
// Sema prevents us from hitting this case
312+
assert(!overflow && "Overflow in array allocation size");
313+
314+
// Add in the cookie, and check whether it's overflowed.
315+
if (cookieSize != 0) {
316+
cgf.cgm.errorNYI(e->getSourceRange(),
317+
"emitCXXNewAllocSize: array cookie");
318+
}
319+
320+
size = cgf.getBuilder().getConstInt(loc, allocationSize);
321+
} else {
322+
// TODO: Handle the variable size case
323+
cgf.cgm.errorNYI(e->getSourceRange(),
324+
"emitCXXNewAllocSize: variable array size");
325+
}
326+
327+
if (cookieSize == 0)
328+
sizeWithoutCookie = size;
329+
else
330+
assert(sizeWithoutCookie && "didn't set sizeWithoutCookie?");
331+
332+
return size;
229333
}
230334

231335
static void storeAnyExprIntoOneUnit(CIRGenFunction &cgf, const Expr *init,
@@ -254,13 +358,26 @@ static void storeAnyExprIntoOneUnit(CIRGenFunction &cgf, const Expr *init,
254358
llvm_unreachable("bad evaluation kind");
255359
}
256360

361+
void CIRGenFunction::emitNewArrayInitializer(
362+
const CXXNewExpr *e, QualType elementType, mlir::Type elementTy,
363+
Address beginPtr, mlir::Value numElements,
364+
mlir::Value allocSizeWithoutCookie) {
365+
// If we have a type with trivial initialization and no initializer,
366+
// there's nothing to do.
367+
if (!e->hasInitializer())
368+
return;
369+
370+
cgm.errorNYI(e->getSourceRange(), "emitNewArrayInitializer");
371+
}
372+
257373
static void emitNewInitializer(CIRGenFunction &cgf, const CXXNewExpr *e,
258374
QualType elementType, mlir::Type elementTy,
259375
Address newPtr, mlir::Value numElements,
260376
mlir::Value allocSizeWithoutCookie) {
261377
assert(!cir::MissingFeatures::generateDebugInfo());
262378
if (e->isArray()) {
263-
cgf.cgm.errorNYI(e->getSourceRange(), "emitNewInitializer: array");
379+
cgf.emitNewArrayInitializer(e, elementType, elementTy, newPtr, numElements,
380+
allocSizeWithoutCookie);
264381
} else if (const Expr *init = e->getInitializer()) {
265382
storeAnyExprIntoOneUnit(cgf, init, e->getAllocatedType(), newPtr,
266383
AggValueSlot::DoesNotOverlap);
@@ -536,7 +653,14 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *e) {
536653
if (allocSize != allocSizeWithoutCookie)
537654
cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: array with cookies");
538655

539-
mlir::Type elementTy = convertTypeForMem(allocType);
656+
mlir::Type elementTy;
657+
if (e->isArray()) {
658+
// For array new, use the allocated type to handle multidimensional arrays
659+
// correctly
660+
elementTy = convertTypeForMem(e->getAllocatedType());
661+
} else {
662+
elementTy = convertTypeForMem(allocType);
663+
}
540664
Address result = builder.createElementBitCast(getLoc(e->getSourceRange()),
541665
allocation, elementTy);
542666

clang/lib/CIR/CodeGen/CIRGenFunction.h

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

12551255
mlir::Value emitCXXNewExpr(const CXXNewExpr *e);
12561256

1257+
void emitNewArrayInitializer(const CXXNewExpr *E, QualType ElementType,
1258+
mlir::Type ElementTy, Address BeginPtr,
1259+
mlir::Value NumElements,
1260+
mlir::Value AllocSizeWithoutCookie);
1261+
12571262
RValue emitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *e,
12581263
const CXXMethodDecl *md,
12591264
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: %[[P_ADDR:.*]] = 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: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
196+
// CHECK: %[[TYPED_PTR:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.double>
197+
// CHECK: cir.store align(8) %[[TYPED_PTR]], %[[P_ADDR]] : !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: %[[P_ADDR:.*]] = 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: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
223+
// CHECK: %[[TYPED_PTR:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>
224+
// CHECK: cir.store align(8) %[[TYPED_PTR]], %[[P_ADDR]] : !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

0 commit comments

Comments
 (0)