[clang] 74cabdb - [CIR] Add basic support for operator new (#145802)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 27 09:43:29 PDT 2025
Author: Andy Kaylor
Date: 2025-06-27T09:43:26-07:00
New Revision: 74cabdb806aea341f6bdcc57e2377882f08a4684
URL: https://github.com/llvm/llvm-project/commit/74cabdb806aea341f6bdcc57e2377882f08a4684
DIFF: https://github.com/llvm/llvm-project/commit/74cabdb806aea341f6bdcc57e2377882f08a4684.diff
LOG: [CIR] Add basic support for operator new (#145802)
This adds the code to handle operator new expressions in ClangIR.
Added:
clang/test/CIR/CodeGen/new.cpp
Modified:
clang/include/clang/CIR/MissingFeatures.h
clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
clang/lib/CIR/CodeGen/CIRGenFunction.h
clang/lib/CIR/CodeGen/CIRGenModule.cpp
clang/lib/CIR/CodeGen/CIRGenTypeCache.h
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 7009d6d7d702a..06dc1ab714dd7 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -110,6 +110,9 @@ struct MissingFeatures {
static bool opCallLandingPad() { return false; }
static bool opCallContinueBlock() { return false; }
+ // CXXNewExpr
+ static bool exprNewNullCheck() { return false; }
+
// FnInfoOpts -- This is used to track whether calls are chain calls or
// instance methods. Classic codegen uses chain call to track and extra free
// register for x86 and uses instance method as a condition for a thunk
@@ -170,6 +173,7 @@ struct MissingFeatures {
static bool armComputeVolatileBitfields() { return false; }
static bool asmLabelAttr() { return false; }
static bool astVarDeclInterface() { return false; }
+ static bool attributeBuiltin() { return false; }
static bool attributeNoBuiltin() { return false; }
static bool bitfields() { return false; }
static bool builtinCall() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp
index d3888baea5d5e..e0d30ad22d271 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp
@@ -197,3 +197,195 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorCall(
assert(!cir::MissingFeatures::opCallMustTail());
return emitCall(fnInfo, callee, returnValue, args, nullptr, loc);
}
+
+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);
+ sizeWithoutCookie = cgf.getBuilder().getConstant(
+ loc, cir::IntAttr::get(cgf.SizeTy, typeSize.getQuantity()));
+ return sizeWithoutCookie;
+ }
+
+ cgf.cgm.errorNYI(e->getSourceRange(), "emitCXXNewAllocSize: array");
+ return {};
+}
+
+static void storeAnyExprIntoOneUnit(CIRGenFunction &cgf, const Expr *init,
+ QualType allocType, Address newPtr,
+ AggValueSlot::Overlap_t mayOverlap) {
+ // FIXME: Refactor with emitExprAsInit.
+ switch (cgf.getEvaluationKind(allocType)) {
+ case cir::TEK_Scalar:
+ cgf.emitScalarInit(init, cgf.getLoc(init->getSourceRange()),
+ cgf.makeAddrLValue(newPtr, allocType), false);
+ return;
+ case cir::TEK_Complex:
+ cgf.cgm.errorNYI(init->getSourceRange(),
+ "storeAnyExprIntoOneUnit: complex");
+ return;
+ case cir::TEK_Aggregate: {
+ assert(!cir::MissingFeatures::aggValueSlotGC());
+ assert(!cir::MissingFeatures::sanitizers());
+ AggValueSlot slot = AggValueSlot::forAddr(
+ newPtr, allocType.getQualifiers(), AggValueSlot::IsDestructed,
+ AggValueSlot::IsNotAliased, mayOverlap, AggValueSlot::IsNotZeroed);
+ cgf.emitAggExpr(init, slot);
+ return;
+ }
+ }
+ llvm_unreachable("bad evaluation kind");
+}
+
+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()) {
+ cgf.cgm.errorNYI(e->getSourceRange(), "emitNewInitializer: array");
+ } else if (const Expr *init = e->getInitializer()) {
+ storeAnyExprIntoOneUnit(cgf, init, e->getAllocatedType(), newPtr,
+ AggValueSlot::DoesNotOverlap);
+ }
+}
+
+/// Emit a call to an operator new or operator delete function, as implicitly
+/// created by new-expressions and delete-expressions.
+static RValue emitNewDeleteCall(CIRGenFunction &cgf,
+ const FunctionDecl *calleeDecl,
+ const FunctionProtoType *calleeType,
+ const CallArgList &args) {
+ cir::CIRCallOpInterface callOrTryCall;
+ cir::FuncOp calleePtr = cgf.cgm.getAddrOfFunction(calleeDecl);
+ CIRGenCallee callee =
+ CIRGenCallee::forDirect(calleePtr, GlobalDecl(calleeDecl));
+ RValue rv =
+ cgf.emitCall(cgf.cgm.getTypes().arrangeFreeFunctionCall(args, calleeType),
+ callee, ReturnValueSlot(), args, &callOrTryCall);
+
+ /// C++1y [expr.new]p10:
+ /// [In a new-expression,] an implementation is allowed to omit a call
+ /// to a replaceable global allocation function.
+ ///
+ /// We model such elidable calls with the 'builtin' attribute.
+ assert(!cir::MissingFeatures::attributeBuiltin());
+ return rv;
+}
+
+mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *e) {
+ // The element type being allocated.
+ QualType allocType = getContext().getBaseElementType(e->getAllocatedType());
+
+ // 1. Build a call to the allocation function.
+ FunctionDecl *allocator = e->getOperatorNew();
+
+ // 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;
+ mlir::Value allocSize = emitCXXNewAllocSize(
+ *this, e, minElements, numElements, allocSizeWithoutCookie);
+ CharUnits allocAlign = getContext().getTypeAlignInChars(allocType);
+
+ // Emit the allocation call.
+ Address allocation = Address::invalid();
+ CallArgList allocatorArgs;
+ if (allocator->isReservedGlobalPlacementOperator()) {
+ cgm.errorNYI(e->getSourceRange(),
+ "emitCXXNewExpr: reserved global placement operator");
+ } else {
+ const FunctionProtoType *allocatorType =
+ allocator->getType()->castAs<FunctionProtoType>();
+ unsigned paramsToSkip = 0;
+
+ // The allocation size is the first argument.
+ QualType sizeType = getContext().getSizeType();
+ allocatorArgs.add(RValue::get(allocSize), sizeType);
+ ++paramsToSkip;
+
+ if (allocSize != allocSizeWithoutCookie) {
+ CharUnits cookieAlign = getSizeAlign(); // FIXME: Ask the ABI.
+ allocAlign = std::max(allocAlign, cookieAlign);
+ }
+
+ // The allocation alignment may be passed as the second argument.
+ if (e->passAlignment()) {
+ cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: pass alignment");
+ }
+
+ // FIXME: Why do we not pass a CalleeDecl here?
+ emitCallArgs(allocatorArgs, allocatorType, e->placement_arguments(),
+ AbstractCallee(), paramsToSkip);
+ RValue rv =
+ emitNewDeleteCall(*this, allocator, allocatorType, allocatorArgs);
+
+ // Set !heapallocsite metadata on the call to operator new.
+ assert(!cir::MissingFeatures::generateDebugInfo());
+
+ // If this was a call to a global replaceable allocation function that does
+ // not take an alignment argument, the allocator is known to produce storage
+ // that's suitably aligned for any object that fits, up to a known
+ // threshold. Otherwise assume it's suitably aligned for the allocated type.
+ CharUnits allocationAlign = allocAlign;
+ if (!e->passAlignment() &&
+ allocator->isReplaceableGlobalAllocationFunction()) {
+ const TargetInfo &target = cgm.getASTContext().getTargetInfo();
+ unsigned allocatorAlign = llvm::bit_floor(std::min<uint64_t>(
+ target.getNewAlign(), getContext().getTypeSize(allocType)));
+ allocationAlign = std::max(
+ allocationAlign, getContext().toCharUnitsFromBits(allocatorAlign));
+ }
+
+ mlir::Value allocPtr = rv.getValue();
+ allocation = Address(
+ allocPtr, mlir::cast<cir::PointerType>(allocPtr.getType()).getPointee(),
+ allocationAlign);
+ }
+
+ // Emit a null check on the allocation result if the allocation
+ // function is allowed to return null (because it has a non-throwing
+ // exception spec or is the reserved placement new) and we have an
+ // interesting initializer will be running sanitizers on the initialization.
+ bool nullCheck = e->shouldNullCheckAllocation() &&
+ (!allocType.isPODType(getContext()) || e->hasInitializer());
+ assert(!cir::MissingFeatures::exprNewNullCheck());
+ if (nullCheck)
+ cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: null check");
+
+ // If there's an operator delete, enter a cleanup to call it if an
+ // exception is thrown.
+ if (e->getOperatorDelete() &&
+ !e->getOperatorDelete()->isReservedGlobalPlacementOperator())
+ cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: operator delete");
+
+ if (allocSize != allocSizeWithoutCookie)
+ cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: array with cookies");
+
+ mlir::Type elementTy = convertTypeForMem(allocType);
+ Address result = builder.createElementBitCast(getLoc(e->getSourceRange()),
+ allocation, elementTy);
+
+ // Passing pointer through launder.invariant.group to avoid propagation of
+ // vptrs information which may be included in previous type.
+ // To not break LTO with
diff erent optimizations levels, we do it regardless
+ // of optimization level.
+ if (cgm.getCodeGenOpts().StrictVTablePointers &&
+ allocator->isReservedGlobalPlacementOperator())
+ cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: strict vtable pointers");
+
+ assert(!cir::MissingFeatures::sanitizers());
+
+ emitNewInitializer(*this, e, allocType, elementTy, result, numElements,
+ allocSizeWithoutCookie);
+ return result.getPointer();
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index b6270ea36fe05..955bb5ffc4395 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -609,6 +609,10 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
mlir::Value VisitCXXThisExpr(CXXThisExpr *te) { return cgf.loadCXXThis(); }
+ mlir::Value VisitCXXNewExpr(const CXXNewExpr *e) {
+ return cgf.emitCXXNewExpr(e);
+ }
+
/// Emit a conversion from the specified type to the specified destination
/// type, both of which are CIR scalar types.
/// TODO: do we need ScalarConversionOpts here? Should be done in another
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 2e54243f18cff..7e6fdf130cca1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -770,6 +770,15 @@ class CIRGenFunction : public CIRGenTypeCache {
const CIRGenCallee &callee, ReturnValueSlot returnValue,
const CallArgList &args, cir::CIRCallOpInterface *callOp,
mlir::Location loc);
+ RValue emitCall(const CIRGenFunctionInfo &funcInfo,
+ const CIRGenCallee &callee, ReturnValueSlot returnValue,
+ const CallArgList &args,
+ cir::CIRCallOpInterface *callOrTryCall = nullptr) {
+ assert(currSrcLoc && "source location must have been set");
+ return emitCall(funcInfo, callee, returnValue, args, callOrTryCall,
+ *currSrcLoc);
+ }
+
RValue emitCall(clang::QualType calleeTy, const CIRGenCallee &callee,
const clang::CallExpr *e, ReturnValueSlot returnValue);
void emitCallArg(CallArgList &args, const clang::Expr *e,
@@ -836,6 +845,8 @@ class CIRGenFunction : public CIRGenTypeCache {
clang::NestedNameSpecifier *qualifier, bool isArrow,
const clang::Expr *base);
+ mlir::Value emitCXXNewExpr(const CXXNewExpr *e);
+
RValue emitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *e,
const CXXMethodDecl *md,
ReturnValueSlot returnValue);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 63cd55aef06e7..0f38d9aee5e72 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -95,6 +95,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();
// In CIRGenTypeCache, UIntPtrTy and SizeType are fields of the same union
UIntPtrTy =
cir::IntType::get(&getMLIRContext(), sizeTypeSize, /*isSigned=*/false);
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h
index 12dbc3297a072..1d081d53ad15c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h
@@ -66,6 +66,13 @@ struct CIRGenTypeCache {
unsigned char PointerSizeInBytes;
};
+ /// The alignment of size_t.
+ unsigned char SizeAlignInBytes;
+
+ clang::CharUnits getSizeAlign() const {
+ return clang::CharUnits::fromQuantity(SizeAlignInBytes);
+ }
+
clang::CharUnits getPointerAlign() const {
return clang::CharUnits::fromQuantity(PointerAlignInBytes);
}
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
new file mode 100644
index 0000000000000..4f88addc6116c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -0,0 +1,158 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+struct S {
+ int a;
+ int b;
+};
+
+void test_basic_new() {
+ S *ps = new S;
+ int *pn = new int;
+ double *pd = new double;
+}
+
+// CHECK: cir.func{{.*}} @_Z14test_basic_newv
+// CHECK: %[[PS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["ps", init]
+// CHECK: %[[PN_ADDR:.*]] = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["pn", init]
+// CHECK: %[[PD_ADDR:.*]] = cir.alloca !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>, ["pd", init]
+// CHECK: %[[EIGHT:.*]] = cir.const #cir.int<8>
+// CHECK: %[[NEW_S:.*]] = cir.call @_Znwm(%[[EIGHT]])
+// CHECK: %[[NEW_S_PTR:.*]] = cir.cast(bitcast, %[[NEW_S]]
+// CHECK: cir.call @_ZN1SC1Ev(%[[NEW_S_PTR]])
+// CHECK: cir.store{{.*}} %[[NEW_S_PTR]], %[[PS_ADDR]]
+// CHECK: %[[FOUR:.*]] = cir.const #cir.int<4>
+// CHECK: %[[NEW_INT:.*]] = cir.call @_Znwm(%[[FOUR]])
+// CHECK: %[[NEW_INT_PTR:.*]] = cir.cast(bitcast, %[[NEW_INT]]
+// CHECK: cir.store{{.*}} %[[NEW_INT_PTR]], %[[PN_ADDR]]
+// CHECK: %[[EIGHT:.*]] = cir.const #cir.int<8>
+// CHECK: %[[NEW_DOUBLE:.*]] = cir.call @_Znwm(%[[EIGHT]])
+// CHECK: %[[NEW_DOUBLE_PTR:.*]] = cir.cast(bitcast, %[[NEW_DOUBLE]]
+// CHECK: cir.store{{.*}} %[[NEW_DOUBLE_PTR]], %[[PD_ADDR]]
+// CHECK: cir.return
+
+// LLVM: define{{.*}} void @_Z14test_basic_newv
+// LLVM: %[[PS_ADDR:.*]] = alloca ptr, i64 1, align 8
+// LLVM: %[[PN_ADDR:.*]] = alloca ptr, i64 1, align 8
+// LLVM: %[[PD_ADDR:.*]] = alloca ptr, i64 1, align 8
+// LLVM: %[[NEW_S:.*]] = call{{.*}} ptr @_Znwm(i64 8)
+// LLVM: call{{.*}} void @_ZN1SC1Ev(ptr %[[NEW_S]])
+// LLVM: store ptr %[[NEW_S]], ptr %[[PS_ADDR]], align 8
+// LLVM: %[[NEW_INT:.*]] = call{{.*}} ptr @_Znwm(i64 4)
+// LLVM: store ptr %[[NEW_INT]], ptr %[[PN_ADDR]], align 8
+// LLVM: %[[NEW_DOUBLE:.*]] = call{{.*}} ptr @_Znwm(i64 8)
+// LLVM: store ptr %[[NEW_DOUBLE]], ptr %[[PD_ADDR]], align 8
+// LLVM: ret void
+
+// NOTE: OGCG elides the constructor call here, but CIR does not.
+
+// OGCG: define{{.*}} void @_Z14test_basic_newv
+// OGCG: %[[PS_ADDR:.*]] = alloca ptr, align 8
+// OGCG: %[[PN_ADDR:.*]] = alloca ptr, align 8
+// OGCG: %[[PD_ADDR:.*]] = alloca ptr, align 8
+// OGCG: %[[NEW_S:.*]] = call{{.*}} ptr @_Znwm(i64 noundef 8)
+// OGCG: store ptr %[[NEW_S]], ptr %[[PS_ADDR]], align 8
+// OGCG: %[[NEW_INT:.*]] = call{{.*}} ptr @_Znwm(i64 noundef 4)
+// OGCG: store ptr %[[NEW_INT]], ptr %[[PN_ADDR]], align 8
+// OGCG: %[[NEW_DOUBLE:.*]] = call{{.*}} ptr @_Znwm(i64 noundef 8)
+// OGCG: store ptr %[[NEW_DOUBLE]], ptr %[[PD_ADDR]], align 8
+// OGCG: ret void
+
+void test_new_with_init() {
+ int *pn = new int{2};
+ double *pd = new double{3.0};
+}
+
+// CHECK: cir.func{{.*}} @_Z18test_new_with_initv
+// CHECK: %[[PN_ADDR:.*]] = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["pn", init]
+// CHECK: %[[PD_ADDR:.*]] = cir.alloca !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>, ["pd", init]
+// CHECK: %[[FOUR:.*]] = cir.const #cir.int<4>
+// CHECK: %[[NEW_INT:.*]] = cir.call @_Znwm(%[[FOUR]])
+// CHECK: %[[NEW_INT_PTR:.*]] = cir.cast(bitcast, %[[NEW_INT]]
+// CHECK: %[[TWO:.*]] = cir.const #cir.int<2>
+// CHECK: cir.store{{.*}} %[[TWO]], %[[NEW_INT_PTR]]
+// CHECK: cir.store{{.*}} %[[NEW_INT_PTR]], %[[PN_ADDR]]
+// CHECK: %[[EIGHT:.*]] = cir.const #cir.int<8>
+// CHECK: %[[NEW_DOUBLE:.*]] = cir.call @_Znwm(%[[EIGHT]])
+// CHECK: %[[NEW_DOUBLE_PTR:.*]] = cir.cast(bitcast, %[[NEW_DOUBLE]]
+// CHECK: %[[THREE:.*]] = cir.const #cir.fp<3.000000e+00>
+// CHECK: cir.store{{.*}} %[[THREE]], %[[NEW_DOUBLE_PTR]]
+// CHECK: cir.store{{.*}} %[[NEW_DOUBLE_PTR]], %[[PD_ADDR]]
+// CHECK: cir.return
+
+// LLVM: define{{.*}} void @_Z18test_new_with_initv
+// LLVM: %[[PN_ADDR:.*]] = alloca ptr, i64 1, align 8
+// LLVM: %[[PD_ADDR:.*]] = alloca ptr, i64 1, align 8
+// LLVM: %[[NEW_INT:.*]] = call{{.*}} ptr @_Znwm(i64 4)
+// LLVM: store i32 2, ptr %[[NEW_INT]], align 4
+// LLVM: store ptr %[[NEW_INT]], ptr %[[PN_ADDR]], align 8
+// LLVM: %[[NEW_DOUBLE:.*]] = call{{.*}} ptr @_Znwm(i64 8)
+// LLVM: store double 3.000000e+00, ptr %[[NEW_DOUBLE]], align 8
+// LLVM: store ptr %[[NEW_DOUBLE]], ptr %[[PD_ADDR]], align 8
+// LLVM: ret void
+
+// OGCG: define{{.*}} void @_Z18test_new_with_initv
+// OGCG: %[[PN_ADDR:.*]] = alloca ptr, align 8
+// OGCG: %[[PD_ADDR:.*]] = alloca ptr, align 8
+// OGCG: %[[NEW_INT:.*]] = call{{.*}} ptr @_Znwm(i64 noundef 4)
+// OGCG: store i32 2, ptr %[[NEW_INT]], align 4
+// OGCG: store ptr %[[NEW_INT]], ptr %[[PN_ADDR]], align 8
+// OGCG: %[[NEW_DOUBLE:.*]] = call{{.*}} ptr @_Znwm(i64 noundef 8)
+// OGCG: store double 3.000000e+00, ptr %[[NEW_DOUBLE]], align 8
+// OGCG: store ptr %[[NEW_DOUBLE]], ptr %[[PD_ADDR]], align 8
+// OGCG: ret void
+
+struct S2 {
+ S2();
+ S2(int, int);
+ int a;
+ int b;
+};
+
+void test_new_with_ctor() {
+ S2 *ps2 = new S2;
+ S2 *ps2_2 = new S2(1, 2);
+}
+
+// CHECK: cir.func{{.*}} @_Z18test_new_with_ctorv
+// CHECK: %[[PS2_ADDR:.*]] = cir.alloca !cir.ptr<!rec_S2>, !cir.ptr<!cir.ptr<!rec_S2>>, ["ps2", init]
+// CHECK: %[[PS2_2_ADDR:.*]] = cir.alloca !cir.ptr<!rec_S2>, !cir.ptr<!cir.ptr<!rec_S2>>, ["ps2_2", init]
+// CHECK: %[[EIGHT:.*]] = cir.const #cir.int<8>
+// CHECK: %[[NEW_S2:.*]] = cir.call @_Znwm(%[[EIGHT]])
+// CHECK: %[[NEW_S2_PTR:.*]] = cir.cast(bitcast, %[[NEW_S2]]
+// CHECK: cir.call @_ZN2S2C1Ev(%[[NEW_S2_PTR]])
+// CHECK: cir.store{{.*}} %[[NEW_S2_PTR]], %[[PS2_ADDR]]
+// CHECK: %[[EIGHT:.*]] = cir.const #cir.int<8>
+// CHECK: %[[NEW_S2_2:.*]] = cir.call @_Znwm(%[[EIGHT]])
+// CHECK: %[[NEW_S2_2_PTR:.*]] = cir.cast(bitcast, %[[NEW_S2_2]]
+// CHECK: %[[ONE:.*]] = cir.const #cir.int<1>
+// CHECK: %[[TWO:.*]] = cir.const #cir.int<2>
+// CHECK: cir.call @_ZN2S2C1Eii(%[[NEW_S2_2_PTR]], %[[ONE]], %[[TWO]])
+// CHECK: cir.store{{.*}} %[[NEW_S2_2_PTR]], %[[PS2_2_ADDR]]
+// CHECK: cir.return
+
+// LLVM: define{{.*}} void @_Z18test_new_with_ctorv
+// LLVM: %[[PS2_ADDR:.*]] = alloca ptr, i64 1, align 8
+// LLVM: %[[PS2_2_ADDR:.*]] = alloca ptr, i64 1, align 8
+// LLVM: %[[NEW_S2:.*]] = call{{.*}} ptr @_Znwm(i64 8)
+// LLVM: call{{.*}} void @_ZN2S2C1Ev(ptr %[[NEW_S2]])
+// LLVM: store ptr %[[NEW_S2]], ptr %[[PS2_ADDR]], align 8
+// LLVM: %[[NEW_S2_2:.*]] = call{{.*}} ptr @_Znwm(i64 8)
+// LLVM: call{{.*}} void @_ZN2S2C1Eii(ptr %[[NEW_S2_2]], i32 1, i32 2)
+// LLVM: store ptr %[[NEW_S2_2]], ptr %[[PS2_2_ADDR]], align 8
+// LLVM: ret void
+
+// OGCG: define{{.*}} void @_Z18test_new_with_ctorv
+// OGCG: %[[PS2_ADDR:.*]] = alloca ptr, align 8
+// OGCG: %[[PS2_2_ADDR:.*]] = alloca ptr, align 8
+// OGCG: %[[NEW_S2:.*]] = call{{.*}} ptr @_Znwm(i64 noundef 8)
+// OGCG: call{{.*}} void @_ZN2S2C1Ev(ptr {{.*}} %[[NEW_S2]])
+// OGCG: store ptr %[[NEW_S2]], ptr %[[PS2_ADDR]], align 8
+// OGCG: %[[NEW_S2_2:.*]] = call{{.*}} ptr @_Znwm(i64 noundef 8)
+// OGCG: call{{.*}} void @_ZN2S2C1Eii(ptr {{.*}} %[[NEW_S2_2]], i32 noundef 1, i32 noundef 2)
+// OGCG: store ptr %[[NEW_S2_2]], ptr %[[PS2_2_ADDR]], align 8
+// OGCG: ret void
More information about the cfe-commits
mailing list