Skip to content
3 changes: 1 addition & 2 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ CharUnits CIRGenCXXABI::getArrayCookieSize(const CXXNewExpr *e) {
if (!requiresArrayCookie(e))
return CharUnits::Zero();

cgm.errorNYI(e->getSourceRange(), "CIRGenCXXABI::getArrayCookieSize");
return CharUnits::Zero();
return getArrayCookieSizeImpl(e->getAllocatedType());
}

bool CIRGenCXXABI::requiresArrayCookie(const CXXNewExpr *e) {
Expand Down
22 changes: 21 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,28 @@ class CIRGenCXXABI {
/// - non-array allocations never need a cookie
/// - calls to \::operator new(size_t, void*) never need a cookie
///
/// \param E - the new-expression being allocated.
/// \param e - the new-expression being allocated.
virtual CharUnits getArrayCookieSize(const CXXNewExpr *e);

/// Initialize the array cookie for the given allocation.
///
/// \param newPtr - a char* which is the presumed-non-null
/// return value of the allocation function
/// \param numElements - the computed number of elements,
/// potentially collapsed from the multidimensional array case;
/// always a size_t
/// \param elementType - the base element allocated type,
/// i.e. the allocated type after stripping all array types
virtual Address initializeArrayCookie(CIRGenFunction &cgf, Address newPtr,
mlir::Value numElements,
const CXXNewExpr *e,
QualType elementType) = 0;

protected:
/// Returns the extra size required in order to store the array
/// cookie for the given type. Assumes that an array cookie is
/// required.
virtual CharUnits getArrayCookieSizeImpl(QualType elementType) = 0;
};

/// Creates and Itanium-family ABI
Expand Down
73 changes: 64 additions & 9 deletions clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
mlir::cast<cir::IntAttr>(constNumElements).getValue();

unsigned numElementsWidth = count.getBitWidth();
bool hasAnyOverflow = false;

// The equivalent code in CodeGen/CGExprCXX.cpp handles these cases as
// overflow, but that should never happen. The size argument is implicitly
Expand Down Expand Up @@ -336,11 +337,22 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,

// Add in the cookie, and check whether it's overflowed.
if (cookieSize != 0) {
cgf.cgm.errorNYI(e->getSourceRange(),
"emitCXXNewAllocSize: array cookie");
// Save the current size without a cookie. This shouldn't be
// used if there was overflow
sizeWithoutCookie = cgf.getBuilder().getConstInt(
loc, allocationSize.zextOrTrunc(sizeWidth));

allocationSize = allocationSize.uadd_ov(cookieSize, overflow);
hasAnyOverflow |= overflow;
}

size = cgf.getBuilder().getConstInt(loc, allocationSize);
// On overflow, produce a -1 so operator new will fail
if (hasAnyOverflow) {
size =
cgf.getBuilder().getConstInt(loc, llvm::APInt::getAllOnes(sizeWidth));
} else {
size = cgf.getBuilder().getConstInt(loc, allocationSize);
}
} else {
// TODO: Handle the variable size case
cgf.cgm.errorNYI(e->getSourceRange(),
Expand Down Expand Up @@ -390,7 +402,50 @@ void CIRGenFunction::emitNewArrayInitializer(
if (!e->hasInitializer())
return;

cgm.errorNYI(e->getSourceRange(), "emitNewArrayInitializer");
unsigned initListElements = 0;

const Expr *init = e->getInitializer();
const InitListExpr *ile = dyn_cast<InitListExpr>(init);
if (ile) {
cgm.errorNYI(ile->getSourceRange(), "emitNewArrayInitializer: init list");
return;
}

// If all elements have already been initialized, skip any further
// initialization.
auto constOp = mlir::dyn_cast<cir::ConstantOp>(numElements.getDefiningOp());
if (constOp) {
auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constOp.getValue());
// Just skip out if the constant count is zero.
if (constIntAttr && constIntAttr.getUInt() <= initListElements)
return;
}

assert(init && "have trailing elements to initialize but no initializer");

// If this is a constructor call, try to optimize it out, and failing that
// emit a single loop to initialize all remaining elements.
if (const CXXConstructExpr *cce = dyn_cast<CXXConstructExpr>(init)) {
CXXConstructorDecl *ctor = cce->getConstructor();
if (ctor->isTrivial()) {
// If new expression did not specify value-initialization, then there
// is no initialization.
if (!cce->requiresZeroInitialization())
return;

cgm.errorNYI(cce->getSourceRange(),
"emitNewArrayInitializer: trivial ctor zero-init");
return;
}

cgm.errorNYI(cce->getSourceRange(),
"emitNewArrayInitializer: ctor initializer");
return;
}

cgm.errorNYI(init->getSourceRange(),
"emitNewArrayInitializer: unsupported initializer");
return;
}

static void emitNewInitializer(CIRGenFunction &cgf, const CXXNewExpr *e,
Expand Down Expand Up @@ -586,9 +641,6 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *e) {

// If there is a brace-initializer, cannot allocate fewer elements than inits.
unsigned minElements = 0;
if (e->isArray() && e->hasInitializer()) {
cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: array initializer");
}

mlir::Value numElements = nullptr;
mlir::Value allocSizeWithoutCookie = nullptr;
Expand Down Expand Up @@ -667,8 +719,11 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *e) {
!e->getOperatorDelete()->isReservedGlobalPlacementOperator())
cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: operator delete");

if (allocSize != allocSizeWithoutCookie)
cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: array with cookies");
if (allocSize != allocSizeWithoutCookie) {
assert(e->isArray());
allocation = cgm.getCXXABI().initializeArrayCookie(
*this, allocation, numElements, e, allocType);
}

mlir::Type elementTy;
if (e->isArray()) {
Expand Down
75 changes: 74 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,14 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
cir::PointerType destCIRTy, bool isRefCast,
Address src) override;

/**************************** RTTI Uniqueness ******************************/
Address initializeArrayCookie(CIRGenFunction &cgf, Address newPtr,
mlir::Value numElements, const CXXNewExpr *e,
QualType elementType) override;

protected:
CharUnits getArrayCookieSizeImpl(QualType elementType) override;

/**************************** RTTI Uniqueness ******************************/
/// Returns true if the ABI requires RTTI type_info objects to be unique
/// across a program.
virtual bool shouldRTTIBeUnique() const { return true; }
Expand Down Expand Up @@ -2003,3 +2009,70 @@ mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf,
return cgf.getBuilder().createDynCast(loc, src.getPointer(), destCIRTy,
isRefCast, castInfo);
}

/************************** Array allocation cookies **************************/

CharUnits CIRGenItaniumCXXABI::getArrayCookieSizeImpl(QualType elementType) {
// The array cookie is a size_t; pad that up to the element alignment.
// The cookie is actually right-justified in that space.
return std::max(
cgm.getSizeSize(),
cgm.getASTContext().getPreferredTypeAlignInChars(elementType));
}

Address CIRGenItaniumCXXABI::initializeArrayCookie(CIRGenFunction &cgf,
Address newPtr,
mlir::Value numElements,
const CXXNewExpr *e,
QualType elementType) {
assert(requiresArrayCookie(e));

// TODO: When sanitizer support is implemented, we'll need to
// get the address space from `newPtr`.
assert(!cir::MissingFeatures::addressSpace());
assert(!cir::MissingFeatures::sanitizers());

ASTContext &ctx = cgm.getASTContext();
CharUnits sizeSize = cgf.getSizeSize();
mlir::Location loc = cgf.getLoc(e->getSourceRange());

// The size of the cookie.
CharUnits cookieSize =
std::max(sizeSize, ctx.getPreferredTypeAlignInChars(elementType));
assert(cookieSize == getArrayCookieSizeImpl(elementType));

cir::PointerType u8PtrTy = cgf.getBuilder().getUInt8PtrTy();
mlir::Value baseBytePtr =
cgf.getBuilder().createPtrBitcast(newPtr.getPointer(), u8PtrTy);

// Compute an offset to the cookie.
CharUnits cookieOffset = cookieSize - sizeSize;
mlir::Value cookiePtrValue = baseBytePtr;
if (!cookieOffset.isZero()) {
mlir::Value offsetOp = cgf.getBuilder().getSignedInt(
loc, cookieOffset.getQuantity(), /*width=*/32);
cookiePtrValue =
cgf.getBuilder().createPtrStride(loc, cookiePtrValue, offsetOp);
}

CharUnits baseAlignment = newPtr.getAlignment();
CharUnits cookiePtrAlignment = baseAlignment.alignmentAtOffset(cookieOffset);
Address cookiePtr(cookiePtrValue, u8PtrTy, cookiePtrAlignment);

// Write the number of elements into the appropriate slot.
Address numElementsPtr =
cookiePtr.withElementType(cgf.getBuilder(), cgf.SizeTy);
cgf.getBuilder().createStore(loc, numElements, numElementsPtr);

// Finally, compute a pointer to the actual data buffer by skipping
// over the cookie completely.
mlir::Value dataOffset =
cgf.getBuilder().getSignedInt(loc, cookieSize.getQuantity(),
/*width=*/32);
mlir::Value dataPtr =
cgf.getBuilder().createPtrStride(loc, baseBytePtr, dataOffset);
mlir::Value finalPtr =
cgf.getBuilder().createPtrBitcast(dataPtr, newPtr.getElementType());
CharUnits finalAlignment = baseAlignment.alignmentAtOffset(cookieSize);
return Address(finalPtr, newPtr.getElementType(), finalAlignment);
}
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
// TODO(CIR): Should be updated once TypeSizeInfoAttr is upstreamed
const unsigned sizeTypeSize =
astContext.getTypeSize(astContext.getSignedSizeType());
SizeAlignInBytes = astContext.toCharUnitsFromBits(sizeTypeSize).getQuantity();
SizeSizeInBytes = astContext.toCharUnitsFromBits(sizeTypeSize).getQuantity();
// In CIRGenTypeCache, UIntPtrTy and SizeType are fields of the same union
UIntPtrTy =
cir::IntType::get(&getMLIRContext(), sizeTypeSize, /*isSigned=*/false);
Expand Down
10 changes: 8 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenTypeCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,17 @@ struct CIRGenTypeCache {
unsigned char PointerSizeInBytes;
};

/// The alignment of size_t.
unsigned char SizeAlignInBytes;
/// The size and alignment of size_t.
union {
unsigned char SizeSizeInBytes; // sizeof(size_t)
unsigned char SizeAlignInBytes;
};

cir::TargetAddressSpaceAttr cirAllocaAddressSpace;

clang::CharUnits getSizeSize() const {
return clang::CharUnits::fromQuantity(SizeSizeInBytes);
}
clang::CharUnits getSizeAlign() const {
return clang::CharUnits::fromQuantity(SizeAlignInBytes);
}
Expand Down
121 changes: 121 additions & 0 deletions clang/test/CIR/CodeGen/new.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,127 @@ void t_new_constant_size() {
// OGCG: %[[CALL:.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 128)
// OGCG: store ptr %[[CALL]], ptr %[[P_ADDR]], align 8

class C {
public:
~C();
};

void t_constant_size_nontrivial() {
auto p = new C[3];
}

// CHECK: cir.func{{.*}} @_Z26t_constant_size_nontrivialv()
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>>, ["p", init] {alignment = 8 : i64}
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<3> : !u64i
// CHECK: %[[#SIZE_WITHOUT_COOKIE:]] = cir.const #cir.int<3> : !u64i
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<11> : !u64i
// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
// CHECK: %[[COOKIE_PTR_BASE:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[COOKIE_PTR:.*]] = cir.cast bitcast %[[COOKIE_PTR_BASE]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!u64i>
// CHECK: cir.store align(8) %[[#NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
// CHECK: %[[#COOKIE_SIZE:]] = cir.const #cir.int<8> : !s32i
// CHECK: %[[DATA_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[#COOKIE_SIZE]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[DATA_PTR_VOID:.*]] = cir.cast bitcast %[[DATA_PTR_RAW]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!void>
// CHECK: %[[DATA_PTR:.*]] = cir.cast bitcast %[[DATA_PTR_VOID]] : !cir.ptr<!void> -> !cir.ptr<!rec_C>
// CHECK: cir.store align(8) %[[DATA_PTR]], %[[P_ADDR]] : !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>>
// CHECK: cir.return
// CHECK: }

// LLVM: @_Z26t_constant_size_nontrivialv()
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
// LLVM: %[[COOKIE_PTR:.*]] = call ptr @_Znam(i64 11)
// LLVM: store i64 3, ptr %[[COOKIE_PTR]], align 8
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr ptr, ptr %[[COOKIE_PTR]], i64 8
// LLVM: store ptr %[[ALLOCATED_PTR]], ptr %[[ALLOCA]], align 8

// OGCG: @_Z26t_constant_size_nontrivialv()
// OGCG: %[[ALLOCA:.*]] = alloca ptr, align 8
// OGCG: %[[COOKIE_PTR:.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 11)
// OGCG: store i64 3, ptr %[[COOKIE_PTR]], align 8
// OGCG: %[[ALLOCATED_PTR:.*]] = getelementptr inbounds i8, ptr %[[COOKIE_PTR]], i64 8
// OGCG: store ptr %[[ALLOCATED_PTR]], ptr %[[ALLOCA]], align 8

class D {
public:
int x;
~D();
};

void t_constant_size_nontrivial2() {
auto p = new D[3];
}

// In this test SIZE_WITHOUT_COOKIE isn't used, but it would be if there were
// an initializer.

// CHECK: cir.func{{.*}} @_Z27t_constant_size_nontrivial2v()
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!rec_D>, !cir.ptr<!cir.ptr<!rec_D>>, ["p", init] {alignment = 8 : i64}
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<3> : !u64i
// CHECK: %[[#SIZE_WITHOUT_COOKIE:]] = cir.const #cir.int<12> : !u64i
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<20> : !u64i
// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
// CHECK: %[[COOKIE_PTR_BASE:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[COOKIE_PTR:.*]] = cir.cast bitcast %[[COOKIE_PTR_BASE]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!u64i>
// CHECK: cir.store align(8) %[[#NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
// CHECK: %[[#COOKIE_SIZE:]] = cir.const #cir.int<8> : !s32i
// CHECK: %[[DATA_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[#COOKIE_SIZE]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[DATA_PTR_VOID:.*]] = cir.cast bitcast %[[DATA_PTR_RAW]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!void>
// CHECK: %[[DATA_PTR:.*]] = cir.cast bitcast %[[DATA_PTR_VOID]] : !cir.ptr<!void> -> !cir.ptr<!rec_D>
// CHECK: cir.store align(8) %[[DATA_PTR]], %[[P_ADDR]] : !cir.ptr<!rec_D>, !cir.ptr<!cir.ptr<!rec_D>>
// CHECK: cir.return
// CHECK: }

// LLVM: @_Z27t_constant_size_nontrivial2v()
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
// LLVM: %[[COOKIE_PTR:.*]] = call ptr @_Znam(i64 20)
// LLVM: store i64 3, ptr %[[COOKIE_PTR]], align 8
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr ptr, ptr %[[COOKIE_PTR]], i64 8
// LLVM: store ptr %[[ALLOCATED_PTR]], ptr %[[ALLOCA]], align 8

struct alignas(16) E {
int x;
~E();
};

void t_align16_nontrivial() {
auto p = new E[2];
}

// CHECK: cir.func{{.*}} @_Z20t_align16_nontrivialv()
// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!rec_E>, !cir.ptr<!cir.ptr<!rec_E>>, ["p", init] {alignment = 8 : i64}
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<2> : !u64i
// CHECK: %[[#SIZE_WITHOUT_COOKIE:]] = cir.const #cir.int<32> : !u64i
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<48> : !u64i
// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
// CHECK: %[[COOKIE_PTR_BASE:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[COOKIE_OFFSET:.*]] = cir.const #cir.int<8> : !s32i
// CHECK: %[[COOKIE_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[COOKIE_OFFSET]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[COOKIE_PTR:.*]] = cir.cast bitcast %[[COOKIE_PTR_RAW]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!u64i>
// CHECK: cir.store align(8) %[[#NUM_ELEMENTS]], %[[COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
// CHECK: %[[#COOKIE_SIZE:]] = cir.const #cir.int<16> : !s32i
// CHECK: %[[DATA_PTR_RAW:.*]] = cir.ptr_stride %[[COOKIE_PTR_BASE]], %[[#COOKIE_SIZE]] : (!cir.ptr<!cir.ptr<!u8i>>, !s32i) -> !cir.ptr<!cir.ptr<!u8i>>
// CHECK: %[[DATA_PTR_VOID:.*]] = cir.cast bitcast %[[DATA_PTR_RAW]] : !cir.ptr<!cir.ptr<!u8i>> -> !cir.ptr<!void>
// CHECK: %[[DATA_PTR:.*]] = cir.cast bitcast %[[DATA_PTR_VOID]] : !cir.ptr<!void> -> !cir.ptr<!rec_E>
// CHECK: cir.store align(8) %[[DATA_PTR]], %[[P_ADDR]] : !cir.ptr<!rec_E>, !cir.ptr<!cir.ptr<!rec_E>>
// CHECK: cir.return
// CHECK: }

// LLVM: @_Z20t_align16_nontrivialv()
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
// LLVM: %[[RAW_PTR:.*]] = call ptr @_Znam(i64 48)
// LLVM: %[[COOKIE_PTR:.*]] = getelementptr ptr, ptr %[[RAW_PTR]], i64 8
// LLVM: store i64 2, ptr %[[COOKIE_PTR]], align 8
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr ptr, ptr %[[RAW_PTR]], i64 16
// LLVM: store ptr %[[ALLOCATED_PTR]], ptr %[[ALLOCA]], align 8

// OGCG: define{{.*}} void @_Z20t_align16_nontrivialv
// OGCG: %[[ALLOCA:.*]] = alloca ptr, align 8
// OGCG: %[[RAW_PTR:.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 48)
// OGCG: %[[COOKIE_PTR:.*]] = getelementptr inbounds i8, ptr %[[RAW_PTR]], i64 8
// OGCG: store i64 2, ptr %[[COOKIE_PTR]], align 8
// OGCG: %[[ALLOCATED_PTR:.*]] = getelementptr inbounds i8, ptr %[[RAW_PTR]], i64 16
// OGCG: store ptr %[[ALLOCATED_PTR]], ptr %[[ALLOCA]], align 8
// OGCG: ret void

void t_new_multidim_constant_size() {
auto p = new double[2][3][4];
Expand Down