diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp index 585dd78bab34..82b253ba4100 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp @@ -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(); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 127d59c54892..ce217a6a6cd0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -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. @@ -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 diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp index 83af7ee98f58..90a55ff1a87f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp @@ -13,6 +13,7 @@ #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/MissingFeatures.h" #include +#include #include #include #include @@ -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); @@ -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(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 { @@ -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 { @@ -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. diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 047f7488f5dc..bbf1024951db 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -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, diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp index 6e829dd5de6b..9447aff3b10a 100644 --- a/clang/test/CIR/CodeGen/new.cpp +++ b/clang/test/CIR/CodeGen/new.cpp @@ -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.ptr>, ["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 +// CHECK: %4 = cir.cast(bitcast, %3 : !cir.ptr), !cir.ptr +// CHECK: cir.store %4, %0 : !cir.ptr, !cir.ptr> +// 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 x 3>>, !cir.ptr 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 +// CHECK: %4 = cir.cast(bitcast, %3 : !cir.ptr), !cir.ptr +// CHECK: %5 = cir.cast(bitcast, %0 : !cir.ptr x 3>>>), !cir.ptr> +// CHECK: cir.store %4, %5 : !cir.ptr, !cir.ptr> +// CHECK: } diff --git a/clang/test/CIR/Lowering/new.cpp b/clang/test/CIR/Lowering/new.cpp new file mode 100644 index 000000000000..9760854fce20 --- /dev/null +++ b/clang/test/CIR/Lowering/new.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM + +void t_new_constant_size() { + auto p = new double[16]; +} + +// LLVM: @_Z19t_new_constant_sizev() +// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8 +// LLVM: %[[ADDR:.*]] = call ptr @_Znam(i64 128) +// LLVM: store ptr %[[ADDR]], ptr %[[ALLOCA]], align 8 + +void t_new_multidim_constant_size() { + auto p = new double[2][3][4]; +} + +// LLVM: @_Z28t_new_multidim_constant_sizev() +// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8 +// LLVM: %[[ADDR:.*]] = call ptr @_Znam(i64 192) +// LLVM: store ptr %[[ADDR]], ptr %[[ALLOCA]], align 8 \ No newline at end of file