Skip to content

Commit 9ffbe92

Browse files
authored
[CIR] Add limited support for array new (#1286)
This change adds initial support for array new expressions where the array size is constant and the element does not require a cookie.
1 parent 7bdf358 commit 9ffbe92

File tree

6 files changed

+208
-19
lines changed

6 files changed

+208
-19
lines changed

clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,18 @@ bool CIRGenCXXABI::isZeroInitializable(const MemberPointerType *MPT) {
8585
// Fake answer.
8686
return true;
8787
}
88+
89+
CharUnits CIRGenCXXABI::getArrayCookieSize(const CXXNewExpr *E) {
90+
if (!requiresArrayCookie(E))
91+
return CharUnits::Zero();
92+
llvm_unreachable("NYI");
93+
}
94+
95+
bool CIRGenCXXABI::requiresArrayCookie(const CXXNewExpr *E) {
96+
// If the class's usual deallocation function takes two arguments,
97+
// it needs a cookie.
98+
if (E->doesUsualArrayDeleteWantSize())
99+
return true;
100+
101+
return E->getAllocatedType().isDestructedType();
102+
}

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

+15
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class CIRGenCXXABI {
3838

3939
clang::ASTContext &getContext() const { return CGM.getASTContext(); }
4040

41+
virtual bool requiresArrayCookie(const CXXNewExpr *E);
42+
4143
public:
4244
/// Similar to AddedStructorArgs, but only notes the number of additional
4345
/// arguments.
@@ -347,6 +349,19 @@ class CIRGenCXXABI {
347349

348350
virtual cir::MethodAttr buildVirtualMethodAttr(cir::MethodType MethodTy,
349351
const CXXMethodDecl *MD) = 0;
352+
353+
/**************************** Array cookies ******************************/
354+
355+
/// Returns the extra size required in order to store the array
356+
/// cookie for the given new-expression. May return 0 to indicate that no
357+
/// array cookie is required.
358+
///
359+
/// Several cases are filtered out before this method is called:
360+
/// - non-array allocations never need a cookie
361+
/// - calls to \::operator new(size_t, void*) never need a cookie
362+
///
363+
/// \param E - the new-expression being allocated.
364+
virtual CharUnits getArrayCookieSize(const CXXNewExpr *E);
350365
};
351366

352367
/// Creates and Itanium-family ABI

clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp

+119-19
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
1414
#include "clang/CIR/MissingFeatures.h"
1515
#include <CIRGenCXXABI.h>
16+
#include <CIRGenCstEmitter.h>
1617
#include <CIRGenFunction.h>
1718
#include <CIRGenModule.h>
1819
#include <CIRGenValue.h>
@@ -549,11 +550,25 @@ static UsualDeleteParams getUsualDeleteParams(const FunctionDecl *FD) {
549550
return Params;
550551
}
551552

553+
static CharUnits CalculateCookiePadding(CIRGenFunction &CGF,
554+
const CXXNewExpr *E) {
555+
if (!E->isArray())
556+
return CharUnits::Zero();
557+
558+
// No cookie is required if the operator new[] being used is the
559+
// reserved placement operator new[].
560+
if (E->getOperatorNew()->isReservedGlobalPlacementOperator())
561+
return CharUnits::Zero();
562+
563+
return CGF.CGM.getCXXABI().getArrayCookieSize(E);
564+
}
565+
552566
static mlir::Value emitCXXNewAllocSize(CIRGenFunction &CGF, const CXXNewExpr *e,
553567
unsigned minElements,
554568
mlir::Value &numElements,
555569
mlir::Value &sizeWithoutCookie) {
556570
QualType type = e->getAllocatedType();
571+
mlir::Location Loc = CGF.getLoc(e->getSourceRange());
557572

558573
if (!e->isArray()) {
559574
CharUnits typeSize = CGF.getContext().getTypeSizeInChars(type);
@@ -563,7 +578,96 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &CGF, const CXXNewExpr *e,
563578
return sizeWithoutCookie;
564579
}
565580

566-
llvm_unreachable("NYI");
581+
// The width of size_t.
582+
unsigned sizeWidth = CGF.CGM.getDataLayout().getTypeSizeInBits(CGF.SizeTy);
583+
584+
// The number of elements can be have an arbitrary integer type;
585+
// essentially, we need to multiply it by a constant factor, add a
586+
// cookie size, and verify that the result is representable as a
587+
// size_t. That's just a gloss, though, and it's wrong in one
588+
// important way: if the count is negative, it's an error even if
589+
// the cookie size would bring the total size >= 0.
590+
//
591+
// If the array size is constant, Sema will have prevented negative
592+
// values and size overflow.
593+
594+
// Compute the constant factor.
595+
llvm::APInt arraySizeMultiplier(sizeWidth, 1);
596+
while (const ConstantArrayType *CAT =
597+
CGF.getContext().getAsConstantArrayType(type)) {
598+
type = CAT->getElementType();
599+
arraySizeMultiplier *= CAT->getSize();
600+
}
601+
602+
CharUnits typeSize = CGF.getContext().getTypeSizeInChars(type);
603+
llvm::APInt typeSizeMultiplier(sizeWidth, typeSize.getQuantity());
604+
typeSizeMultiplier *= arraySizeMultiplier;
605+
606+
// Figure out the cookie size.
607+
llvm::APInt cookieSize(sizeWidth,
608+
CalculateCookiePadding(CGF, e).getQuantity());
609+
610+
// This will be a size_t.
611+
mlir::Value size;
612+
613+
// Emit the array size expression.
614+
// We multiply the size of all dimensions for NumElements.
615+
// e.g for 'int[2][3]', ElemType is 'int' and NumElements is 6.
616+
const Expr *arraySize = *e->getArraySize();
617+
mlir::Attribute constNumElements =
618+
ConstantEmitter(CGF.CGM, &CGF)
619+
.tryEmitAbstract(arraySize, arraySize->getType());
620+
if (constNumElements) {
621+
// Get an APInt from the constant
622+
const llvm::APInt &count =
623+
mlir::cast<cir::IntAttr>(constNumElements).getValue();
624+
625+
unsigned numElementsWidth = count.getBitWidth();
626+
627+
// The equivalent code in CodeGen/CGExprCXX.cpp handles these cases as
628+
// overflow, but they should never happen. The size argument is implicitly
629+
// cast to a size_t, so it can never be negative and numElementsWidth will
630+
// always equal sizeWidth.
631+
assert(!count.isNegative() && "Expected non-negative array size");
632+
assert(numElementsWidth == sizeWidth &&
633+
"Expected a size_t array size constant");
634+
635+
// Okay, compute a count at the right width.
636+
llvm::APInt adjustedCount = count.zextOrTrunc(sizeWidth);
637+
638+
// Scale numElements by that. This might overflow, but we don't
639+
// care because it only overflows if allocationSize does, too, and
640+
// if that overflows then we shouldn't use this.
641+
// This emits a constant that may not be used, but we can't tell here
642+
// whether it will be needed or not.
643+
numElements =
644+
CGF.getBuilder().getConstInt(Loc, adjustedCount * arraySizeMultiplier);
645+
646+
// Compute the size before cookie, and track whether it overflowed.
647+
bool overflow;
648+
llvm::APInt allocationSize =
649+
adjustedCount.umul_ov(typeSizeMultiplier, overflow);
650+
651+
// Sema prevents us from hitting this case
652+
assert(!overflow && "Overflow in array allocation size");
653+
654+
// Add in the cookie, and check whether it's overflowed.
655+
if (cookieSize != 0) {
656+
llvm_unreachable("NYI");
657+
}
658+
659+
size = CGF.getBuilder().getConstInt(Loc, allocationSize);
660+
} else {
661+
// TODO: Handle the variable size case
662+
llvm_unreachable("NYI");
663+
}
664+
665+
if (cookieSize == 0)
666+
sizeWithoutCookie = size;
667+
else
668+
assert(sizeWithoutCookie && "didn't set sizeWithoutCookie?");
669+
670+
return size;
567671
}
568672

569673
namespace {
@@ -745,33 +849,32 @@ static void StoreAnyExprIntoOneUnit(CIRGenFunction &CGF, const Expr *Init,
745849
llvm_unreachable("bad evaluation kind");
746850
}
747851

852+
void CIRGenFunction::emitNewArrayInitializer(
853+
const CXXNewExpr *E, QualType ElementType, mlir::Type ElementTy,
854+
Address BeginPtr, mlir::Value NumElements,
855+
mlir::Value AllocSizeWithoutCookie) {
856+
// If we have a type with trivial initialization and no initializer,
857+
// there's nothing to do.
858+
if (!E->hasInitializer())
859+
return;
860+
861+
llvm_unreachable("NYI");
862+
}
863+
748864
static void emitNewInitializer(CIRGenFunction &CGF, const CXXNewExpr *E,
749865
QualType ElementType, mlir::Type ElementTy,
750866
Address NewPtr, mlir::Value NumElements,
751867
mlir::Value AllocSizeWithoutCookie) {
752868
assert(!cir::MissingFeatures::generateDebugInfo());
753869
if (E->isArray()) {
754-
llvm_unreachable("NYI");
870+
CGF.emitNewArrayInitializer(E, ElementType, ElementTy, NewPtr, NumElements,
871+
AllocSizeWithoutCookie);
755872
} else if (const Expr *Init = E->getInitializer()) {
756873
StoreAnyExprIntoOneUnit(CGF, Init, E->getAllocatedType(), NewPtr,
757874
AggValueSlot::DoesNotOverlap);
758875
}
759876
}
760877

761-
static CharUnits CalculateCookiePadding(CIRGenFunction &CGF,
762-
const CXXNewExpr *E) {
763-
if (!E->isArray())
764-
return CharUnits::Zero();
765-
766-
// No cookie is required if the operator new[] being used is the
767-
// reserved placement operator new[].
768-
if (E->getOperatorNew()->isReservedGlobalPlacementOperator())
769-
return CharUnits::Zero();
770-
771-
llvm_unreachable("NYI");
772-
// return CGF.CGM.getCXXABI().GetArrayCookieSize(E);
773-
}
774-
775878
namespace {
776879
/// Calls the given 'operator delete' on a single object.
777880
struct CallObjectDelete final : EHScopeStack::Cleanup {
@@ -1129,9 +1232,6 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *E) {
11291232
emitNewInitializer(*this, E, allocType, elementTy, result, numElements,
11301233
allocSizeWithoutCookie);
11311234
auto resultPtr = result.getPointer();
1132-
if (E->isArray()) {
1133-
llvm_unreachable("NYI");
1134-
}
11351235

11361236
// Deactivate the 'operator delete' cleanup if we finished
11371237
// initialization.

clang/lib/CIR/CodeGen/CIRGenFunction.h

+5
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,11 @@ class CIRGenFunction : public CIRGenTypeCache {
726726
mlir::Value emitCXXNewExpr(const CXXNewExpr *E);
727727
void emitCXXDeleteExpr(const CXXDeleteExpr *E);
728728

729+
void emitNewArrayInitializer(const CXXNewExpr *E, QualType ElementType,
730+
mlir::Type ElementTy, Address BeginPtr,
731+
mlir::Value NumElements,
732+
mlir::Value AllocSizeWithoutCookie);
733+
729734
void emitCXXAggrConstructorCall(const CXXConstructorDecl *D,
730735
const clang::ArrayType *ArrayTy,
731736
Address ArrayPtr, const CXXConstructExpr *E,

clang/test/CIR/CodeGen/new.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,37 @@ void t() {
5656
B b;
5757
b.construct(&b);
5858
}
59+
60+
61+
void t_new_constant_size() {
62+
auto p = new double[16];
63+
}
64+
65+
// In this test, NUM_ELEMENTS isn't used because no cookie is needed and there
66+
// are no constructor calls needed.
67+
68+
// CHECK: cir.func @_Z19t_new_constant_sizev()
69+
// CHECK: %0 = cir.alloca !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>, ["p", init] {alignment = 8 : i64}
70+
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<16> : !u64i
71+
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<128> : !u64i
72+
// CHECK: %3 = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
73+
// CHECK: %4 = cir.cast(bitcast, %3 : !cir.ptr<!void>), !cir.ptr<!cir.double>
74+
// CHECK: cir.store %4, %0 : !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>
75+
// CHECK: cir.return
76+
// CHECK: }
77+
78+
void t_new_multidim_constant_size() {
79+
auto p = new double[2][3][4];
80+
}
81+
82+
// As above, NUM_ELEMENTS isn't used.
83+
84+
// CHECK: cir.func @_Z28t_new_multidim_constant_sizev()
85+
// 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}
86+
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<24> : !u64i
87+
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<192> : !u64i
88+
// CHECK: %3 = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
89+
// CHECK: %4 = cir.cast(bitcast, %3 : !cir.ptr<!void>), !cir.ptr<!cir.double>
90+
// 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>>
91+
// CHECK: cir.store %4, %5 : !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>
92+
// CHECK: }

clang/test/CIR/Lowering/new.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %clang_cc1 -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)