Skip to content
Merged
15 changes: 15 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,18 @@ bool CIRGenCXXABI::isZeroInitializable(const MemberPointerType *MPT) {
// Fake answer.
return true;
}

CharUnits CIRGenCXXABI::getArrayCookieSize(const CXXNewExpr *E) {
if (!requiresArrayCookie(E))
return CharUnits::Zero();
llvm_unreachable("NYI");
}

bool CIRGenCXXABI::requiresArrayCookie(const CXXNewExpr *E) {
// If the class's usual deallocation function takes two arguments,
// it needs a cookie.
if (E->doesUsualArrayDeleteWantSize())
return true;

return E->getAllocatedType().isDestructedType();
}
15 changes: 15 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class CIRGenCXXABI {

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

virtual bool requiresArrayCookie(const CXXNewExpr *E);

public:
/// Similar to AddedStructorArgs, but only notes the number of additional
/// arguments.
Expand Down Expand Up @@ -347,6 +349,19 @@ class CIRGenCXXABI {

virtual cir::MethodAttr buildVirtualMethodAttr(cir::MethodType MethodTy,
const CXXMethodDecl *MD) = 0;

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

/// Returns the extra size required in order to store the array
/// cookie for the given new-expression. May return 0 to indicate that no
/// array cookie is required.
///
/// Several cases are filtered out before this method is called:
/// - 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.
virtual CharUnits getArrayCookieSize(const CXXNewExpr *E);
};

/// Creates and Itanium-family ABI
Expand Down
138 changes: 119 additions & 19 deletions clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/CIR/MissingFeatures.h"
#include <CIRGenCXXABI.h>
#include <CIRGenCstEmitter.h>
#include <CIRGenFunction.h>
#include <CIRGenModule.h>
#include <CIRGenValue.h>
Expand Down Expand Up @@ -549,11 +550,25 @@ static UsualDeleteParams getUsualDeleteParams(const FunctionDecl *FD) {
return Params;
}

static CharUnits CalculateCookiePadding(CIRGenFunction &CGF,
const CXXNewExpr *E) {
if (!E->isArray())
return CharUnits::Zero();

// No cookie is required if the operator new[] being used is the
// reserved placement operator new[].
if (E->getOperatorNew()->isReservedGlobalPlacementOperator())
return CharUnits::Zero();

return CGF.CGM.getCXXABI().getArrayCookieSize(E);
}

static mlir::Value emitCXXNewAllocSize(CIRGenFunction &CGF, const CXXNewExpr *e,
unsigned minElements,
mlir::Value &numElements,
mlir::Value &sizeWithoutCookie) {
QualType type = e->getAllocatedType();
mlir::Location Loc = CGF.getLoc(e->getSourceRange());

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

llvm_unreachable("NYI");
// The width of size_t.
unsigned sizeWidth = CGF.CGM.getDataLayout().getTypeSizeInBits(CGF.SizeTy);

// The number of elements can be have an arbitrary integer type;
// essentially, we need to multiply it by a constant factor, add a
// cookie size, and verify that the result is representable as a
// size_t. That's just a gloss, though, and it's wrong in one
// important way: if the count is negative, it's an error even if
// the cookie size would bring the total size >= 0.
//
// If the array size is constant, Sema will have prevented negative
// values and size overflow.

// Compute the constant factor.
llvm::APInt arraySizeMultiplier(sizeWidth, 1);
while (const ConstantArrayType *CAT =
CGF.getContext().getAsConstantArrayType(type)) {
type = CAT->getElementType();
arraySizeMultiplier *= CAT->getSize();
}

CharUnits typeSize = CGF.getContext().getTypeSizeInChars(type);
llvm::APInt typeSizeMultiplier(sizeWidth, typeSize.getQuantity());
typeSizeMultiplier *= arraySizeMultiplier;

// Figure out the cookie size.
llvm::APInt cookieSize(sizeWidth,
CalculateCookiePadding(CGF, e).getQuantity());

// This will be a size_t.
mlir::Value size;

// Emit the array size expression.
// We multiply the size of all dimensions for NumElements.
// e.g for 'int[2][3]', ElemType is 'int' and NumElements is 6.
const Expr *arraySize = *e->getArraySize();
mlir::Attribute constNumElements =
ConstantEmitter(CGF.CGM, &CGF)
.tryEmitAbstract(arraySize, arraySize->getType());
if (constNumElements) {
// Get an APInt from the constant
const llvm::APInt &count =
mlir::cast<cir::IntAttr>(constNumElements).getValue();

unsigned numElementsWidth = count.getBitWidth();

// The equivalent code in CodeGen/CGExprCXX.cpp handles these cases as
// overflow, but they should never happen. The size argument is implicitly
// cast to a size_t, so it can never be negative and numElementsWidth will
// always equal sizeWidth.
assert(!count.isNegative() && "Expected non-negative array size");
assert(numElementsWidth == sizeWidth &&
"Expected a size_t array size constant");

// Okay, compute a count at the right width.
llvm::APInt adjustedCount = count.zextOrTrunc(sizeWidth);

// Scale numElements by that. This might overflow, but we don't
// care because it only overflows if allocationSize does, too, and
// if that overflows then we shouldn't use this.
// This emits a constant that may not be used, but we can't tell here
// whether it will be needed or not.
numElements =
CGF.getBuilder().getConstInt(Loc, adjustedCount * arraySizeMultiplier);

// Compute the size before cookie, and track whether it overflowed.
bool overflow;
llvm::APInt allocationSize =
adjustedCount.umul_ov(typeSizeMultiplier, overflow);

// Sema prevents us from hitting this case
assert(!overflow && "Overflow in array allocation size");

// Add in the cookie, and check whether it's overflowed.
if (cookieSize != 0) {
llvm_unreachable("NYI");
}

size = CGF.getBuilder().getConstInt(Loc, allocationSize);
} else {
// TODO: Handle the variable size case
llvm_unreachable("NYI");
}

if (cookieSize == 0)
sizeWithoutCookie = size;
else
assert(sizeWithoutCookie && "didn't set sizeWithoutCookie?");

return size;
}

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

void CIRGenFunction::emitNewArrayInitializer(
const CXXNewExpr *E, QualType ElementType, mlir::Type ElementTy,
Address BeginPtr, mlir::Value NumElements,
mlir::Value AllocSizeWithoutCookie) {
// If we have a type with trivial initialization and no initializer,
// there's nothing to do.
if (!E->hasInitializer())
return;

llvm_unreachable("NYI");
}

static void emitNewInitializer(CIRGenFunction &CGF, const CXXNewExpr *E,
QualType ElementType, mlir::Type ElementTy,
Address NewPtr, mlir::Value NumElements,
mlir::Value AllocSizeWithoutCookie) {
assert(!cir::MissingFeatures::generateDebugInfo());
if (E->isArray()) {
llvm_unreachable("NYI");
CGF.emitNewArrayInitializer(E, ElementType, ElementTy, NewPtr, NumElements,
AllocSizeWithoutCookie);
} else if (const Expr *Init = E->getInitializer()) {
StoreAnyExprIntoOneUnit(CGF, Init, E->getAllocatedType(), NewPtr,
AggValueSlot::DoesNotOverlap);
}
}

static CharUnits CalculateCookiePadding(CIRGenFunction &CGF,
const CXXNewExpr *E) {
if (!E->isArray())
return CharUnits::Zero();

// No cookie is required if the operator new[] being used is the
// reserved placement operator new[].
if (E->getOperatorNew()->isReservedGlobalPlacementOperator())
return CharUnits::Zero();

llvm_unreachable("NYI");
// return CGF.CGM.getCXXABI().GetArrayCookieSize(E);
}

namespace {
/// Calls the given 'operator delete' on a single object.
struct CallObjectDelete final : EHScopeStack::Cleanup {
Expand Down Expand Up @@ -1129,9 +1232,6 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *E) {
emitNewInitializer(*this, E, allocType, elementTy, result, numElements,
allocSizeWithoutCookie);
auto resultPtr = result.getPointer();
if (E->isArray()) {
llvm_unreachable("NYI");
}

// Deactivate the 'operator delete' cleanup if we finished
// initialization.
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,11 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::Value emitCXXNewExpr(const CXXNewExpr *E);
void emitCXXDeleteExpr(const CXXDeleteExpr *E);

void emitNewArrayInitializer(const CXXNewExpr *E, QualType ElementType,
mlir::Type ElementTy, Address BeginPtr,
mlir::Value NumElements,
mlir::Value AllocSizeWithoutCookie);

void emitCXXAggrConstructorCall(const CXXConstructorDecl *D,
const clang::ArrayType *ArrayTy,
Address ArrayPtr, const CXXConstructExpr *E,
Expand Down
34 changes: 34 additions & 0 deletions clang/test/CIR/CodeGen/new.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,37 @@ void t() {
B b;
b.construct(&b);
}


void t_new_constant_size() {
auto p = new double[16];
}

// In this test, NUM_ELEMENTS isn't used because no cookie is needed and there
// are no constructor calls needed.

// CHECK: cir.func @_Z19t_new_constant_sizev()
// CHECK: %0 = cir.alloca !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>, ["p", init] {alignment = 8 : i64}
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<16> : !u64i
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<128> : !u64i
// CHECK: %3 = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
// CHECK: %4 = cir.cast(bitcast, %3 : !cir.ptr<!void>), !cir.ptr<!cir.double>
// CHECK: cir.store %4, %0 : !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>
// CHECK: cir.return
// CHECK: }

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

// As above, NUM_ELEMENTS isn't used.

// CHECK: cir.func @_Z28t_new_multidim_constant_sizev()
// 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}
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<24> : !u64i
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<192> : !u64i
// CHECK: %3 = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
// CHECK: %4 = cir.cast(bitcast, %3 : !cir.ptr<!void>), !cir.ptr<!cir.double>
// 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>>
// CHECK: cir.store %4, %5 : !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>
// CHECK: }
Loading