[clang] [CIR] Implement support for delete after new in a conditional branch (PR #192544)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 17 16:55:49 PDT 2026
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/192544
>From 255094984cc09c61d74d9bb676f32cf8daf654e4 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 16 Apr 2026 09:25:41 -0700
Subject: [PATCH 1/3] [CIR] Implement support for delete after new in a
conditional branch
This implements handling for calling delete in an EH handler after a
call to new when the new call appears inside a conditional operation,
which requires the new result to be spilled inside the cleanup scope
and reloaded after.
This implementation introduces the DominatingValue helper class, which
is adapted from classic codegen, but only the parts of that class that
are needed for the current change are implemented. This will likely
be expanded in a future change as other uses are added.
Assisted-by: Cursor / claude-4.6-opus-high
---
clang/lib/CIR/CodeGen/CIRGenCleanup.cpp | 83 ++++++++++++
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 56 +++++---
clang/lib/CIR/CodeGen/CIRGenFunction.h | 38 ++++++
clang/lib/CIR/CodeGen/EHScopeStack.h | 12 ++
clang/test/CIR/CodeGen/new-delete.cpp | 165 ++++++++++++++++++++++++
5 files changed, 339 insertions(+), 15 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
index 55c27107e709d..816a39eb09060 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
@@ -86,6 +86,89 @@ Address CIRGenFunction::createCleanupActiveFlag() {
return active;
}
+void CIRGenFunction::initFullExprCleanup() {
+ initFullExprCleanupWithFlag(createCleanupActiveFlag());
+}
+
+void CIRGenFunction::initFullExprCleanupWithFlag(Address activeFlag) {
+ EHCleanupScope &cleanup = cast<EHCleanupScope>(*ehStack.begin());
+ assert(!cleanup.hasActiveFlag() && "cleanup already has active flag?");
+ cleanup.setActiveFlag(activeFlag);
+
+ if (cleanup.isNormalCleanup())
+ cleanup.setTestFlagInNormalCleanup();
+ if (cleanup.isEHCleanup())
+ cleanup.setTestFlagInEHCleanup();
+}
+
+//===----------------------------------------------------------------------===//
+// DominatingCIRValue / DominatingValue<RValue>
+//===----------------------------------------------------------------------===//
+
+bool DominatingCIRValue::needsSaving(mlir::Value value) {
+ if (!value)
+ return false;
+
+ // If it's a block argument, we don't need to save.
+ mlir::Operation *definingOp = value.getDefiningOp();
+ if (!definingOp)
+ return false;
+
+ // If the value is defined in the function entry block, it already dominates
+ // everything, so we don't need to save it.
+ mlir::Block *currBlock = definingOp->getBlock();
+ if (auto fnOp = definingOp->getParentOfType<cir::FuncOp>())
+ if (&fnOp.getBody().front() == currBlock)
+ return false;
+
+ return true;
+}
+
+DominatingCIRValue::saved_type DominatingCIRValue::save(CIRGenFunction &cgf,
+ mlir::Value value) {
+ if (!needsSaving(value))
+ return saved_type(value, false);
+
+ // Otherwise, we need an alloca.
+ CharUnits align = CharUnits::fromQuantity(
+ cgf.cgm.getDataLayout().getABITypeAlign(value.getType()));
+ mlir::Location loc = value.getLoc();
+ Address alloca =
+ cgf.createTempAlloca(value.getType(), align, loc, "cond-cleanup.save");
+ cgf.getBuilder().createStore(loc, value, alloca);
+
+ return saved_type(alloca.emitRawPointer(), true);
+}
+
+mlir::Value DominatingCIRValue::restore(CIRGenFunction &cgf,
+ saved_type value) {
+ // If the value says it wasn't saved, trust that it's still dominating.
+ if (!value.getInt())
+ return value.getPointer();
+
+ // Otherwise, it should be an alloca instruction, as set up in save().
+ auto alloca = value.getPointer().getDefiningOp<cir::AllocaOp>();
+ return cgf.getBuilder().createAlignedLoad(
+ alloca.getLoc(), alloca.getAllocaType(), alloca,
+ llvm::MaybeAlign(alloca.getAlignment()));
+}
+
+DominatingValue<RValue>::saved_type
+DominatingValue<RValue>::saved_type::save(CIRGenFunction &cgf, RValue rv) {
+ if (rv.isScalar())
+ return saved_type(DominatingCIRValue::save(cgf, rv.getValue()));
+
+ if (rv.isAggregate())
+ cgf.cgm.errorNYI("DominatingValue<RValue>::save for aggregate type");
+ else
+ cgf.cgm.errorNYI("DominatingValue<RValue>::save for complex type");
+ return saved_type(DominatingCIRValue::saved_type());
+}
+
+RValue DominatingValue<RValue>::saved_type::restore(CIRGenFunction &cgf) {
+ return RValue::get(DominatingCIRValue::restore(cgf, val));
+}
+
CIRGenFunction::FullExprCleanupScope::FullExprCleanupScope(CIRGenFunction &cgf,
const Expr *subExpr)
: cgf(cgf), cleanups(cgf), scope(nullptr),
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 30a833564ec2f..e77c4294e1ba3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -798,12 +798,12 @@ class CallDeleteDuringNew final
PlacementArg<Traits> *getPlacementArgs() { return getTrailingObjects(); }
+public:
void setPlacementArg(unsigned i, RValueTy argValue, QualType argType) {
assert(i < numPlacementArgs && "index out of range");
getPlacementArgs()[i] = {argValue, argType};
}
-public:
static size_t getExtraSize(size_t numPlacementArgs) {
return TrailingObj::template additionalSizeToAlloc<PlacementArg<Traits>>(
numPlacementArgs);
@@ -813,18 +813,11 @@ class CallDeleteDuringNew final
const FunctionDecl *operatorDelete, ValueTy ptr,
ValueTy allocSize,
const ImplicitAllocationParameters &iap,
- CharUnits allocAlign, const CallArgList *newArgs,
- unsigned numNonPlacementArgs, CIRGenFunction *cgf,
- mlir::Location loc)
+ CharUnits allocAlign)
: numPlacementArgs(numPlacementArgs),
passAlignmentToPlacementDelete(isAlignedAllocation(iap.PassAlignment)),
operatorDelete(operatorDelete), ptr(ptr), allocSize(allocSize),
- allocAlign(allocAlign) {
- for (unsigned i = 0, n = numPlacementArgs; i != n; ++i) {
- const CallArg &arg = (*newArgs)[i + numNonPlacementArgs];
- setPlacementArg(i, arg.getRValue(*cgf, loc), arg.ty);
- }
- }
+ allocAlign(allocAlign) {}
void emit(CIRGenFunction &cgf, Flags flags) override {
const auto *fpt = operatorDelete->getType()->castAs<FunctionProtoType>();
@@ -901,17 +894,50 @@ static void enterNewDeleteCleanup(CIRGenFunction &cgf, const CXXNewExpr *e,
typedef CallDeleteDuringNew<DirectCleanupTraits> DirectCleanup;
assert(!cir::MissingFeatures::typeAwareAllocation());
- cgf.ehStack.pushCleanupWithExtra<DirectCleanup>(
+ DirectCleanup *cleanup = cgf.ehStack.pushCleanupWithExtra<DirectCleanup>(
EHCleanup, e->getNumPlacementArgs(), e->getOperatorDelete(),
newPtr.getPointer(), allocSize, e->implicitAllocationParameters(),
- allocAlign, &newArgs, numNonPlacementArgs, &cgf,
- cgf.getLoc(e->getSourceRange()));
+ allocAlign);
+ for (unsigned i = 0, n = e->getNumPlacementArgs(); i != n; ++i) {
+ const CallArg &arg = newArgs[i + numNonPlacementArgs];
+ cleanup->setPlacementArg(i, arg.getRValue(cgf, cgf.getLoc(e->getSourceRange())),
+ arg.ty);
+ }
return;
}
- cgf.cgm.errorNYI(e->getSourceRange(),
- "enterNewDeleteCleanup: conditional branch");
+ // Otherwise, we need to save all this stuff.
+ DominatingValue<RValue>::saved_type savedNewPtr =
+ DominatingValue<RValue>::save(cgf, RValue::get(newPtr.getPointer()));
+ DominatingValue<RValue>::saved_type savedAllocSize =
+ DominatingValue<RValue>::save(cgf, RValue::get(allocSize));
+
+ struct ConditionalCleanupTraits {
+ typedef DominatingValue<RValue>::saved_type ValueTy;
+ typedef DominatingValue<RValue>::saved_type RValueTy;
+ static RValue get(CIRGenFunction &cgf, ValueTy v) {
+ return v.restore(cgf);
+ }
+ };
+ typedef CallDeleteDuringNew<ConditionalCleanupTraits> ConditionalCleanup;
+
+ assert(!cir::MissingFeatures::typeAwareAllocation());
+ ConditionalCleanup *cleanup =
+ cgf.ehStack.pushCleanupWithExtra<ConditionalCleanup>(
+ EHCleanup, e->getNumPlacementArgs(), e->getOperatorDelete(),
+ savedNewPtr, savedAllocSize, e->implicitAllocationParameters(),
+ allocAlign);
+ for (unsigned i = 0, n = e->getNumPlacementArgs(); i != n; ++i) {
+ const CallArg &arg = newArgs[i + numNonPlacementArgs];
+ cleanup->setPlacementArg(
+ i,
+ DominatingValue<RValue>::save(
+ cgf, arg.getRValue(cgf, cgf.getLoc(e->getSourceRange()))),
+ arg.ty);
+ }
+
+ cgf.initFullExprCleanup();
}
static void storeAnyExprIntoOneUnit(CIRGenFunction &cgf, const Expr *init,
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 7f91cba115f04..1905800554838 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1094,6 +1094,11 @@ class CIRGenFunction : public CIRGenTypeCache {
/// true at the current insertion point (inside the conditional branch).
Address createCleanupActiveFlag();
+ /// Set up the last cleanup that was pushed as a conditional
+ /// full-expression cleanup.
+ void initFullExprCleanup();
+ void initFullExprCleanupWithFlag(Address activeFlag);
+
/// Promote a single pending cleanup entry onto the EH scope stack. If the
/// entry has a valid activeFlag, the cleanup is configured as conditional.
/// Defined in CIRGenDecl.cpp where the concrete cleanup types are visible.
@@ -2600,6 +2605,39 @@ class CIRGenFunction : public CIRGenTypeCache {
};
};
+/// Helper class with most of the code for saving a value for a
+/// conditional expression cleanup.
+struct DominatingCIRValue {
+ typedef llvm::PointerIntPair<mlir::Value, 1, bool> saved_type;
+
+ /// Answer whether the given value needs extra work to be saved.
+ static bool needsSaving(mlir::Value value);
+
+ static saved_type save(CIRGenFunction &cgf, mlir::Value value);
+ static mlir::Value restore(CIRGenFunction &cgf, saved_type value);
+};
+
+/// A specialization of DominatingValue for RValue.
+template <> struct DominatingValue<RValue> {
+ typedef RValue type;
+ class saved_type {
+ DominatingCIRValue::saved_type val;
+
+ saved_type(DominatingCIRValue::saved_type val) : val(val) {}
+
+ public:
+ static saved_type save(CIRGenFunction &cgf, RValue value);
+ RValue restore(CIRGenFunction &cgf);
+ };
+
+ static saved_type save(CIRGenFunction &cgf, type value) {
+ return saved_type::save(cgf, value);
+ }
+ static type restore(CIRGenFunction &cgf, saved_type value) {
+ return value.restore(cgf);
+ }
+};
+
} // namespace clang::CIRGen
#endif
diff --git a/clang/lib/CIR/CodeGen/EHScopeStack.h b/clang/lib/CIR/CodeGen/EHScopeStack.h
index 308d98f108101..9e7a917d1d6cf 100644
--- a/clang/lib/CIR/CodeGen/EHScopeStack.h
+++ b/clang/lib/CIR/CodeGen/EHScopeStack.h
@@ -25,6 +25,18 @@ namespace clang::CIRGen {
class CIRGenFunction;
+template <class T> struct InvariantValue {
+ using type = T;
+ using saved_type = T;
+ static bool needsSaving(type value) { return false; }
+ static saved_type save(CIRGenFunction &cgf, type value) { return value; }
+ static type restore(CIRGenFunction &cgf, saved_type value) { return value; }
+};
+
+/// A metaprogramming class for ensuring that a value will dominate an
+/// arbitrary position in a function.
+template <class T> struct DominatingValue : InvariantValue<T> {};
+
enum CleanupKind : unsigned {
/// Denotes a cleanup that should run when a scope is exited using exceptional
/// control flow (a throw statement leading to stack unwinding, ).
diff --git a/clang/test/CIR/CodeGen/new-delete.cpp b/clang/test/CIR/CodeGen/new-delete.cpp
index d0c8c7d851c70..71e3407001ddb 100644
--- a/clang/test/CIR/CodeGen/new-delete.cpp
+++ b/clang/test/CIR/CodeGen/new-delete.cpp
@@ -247,6 +247,171 @@ B *c() {
// OGCG: %[[EXN_INSERT_2:.*]] = insertvalue { ptr, i32 } %[[EXN_INSERT]], i32 %[[EHSELECTOR]], 1
// OGCG: resume { ptr, i32 } %[[EXN_INSERT_2]]
+struct C {
+ C();
+ ~C();
+};
+
+C *test_new_delete_conditional(bool cond) {
+ return cond ? new C : 0;
+}
+
+// CIR-LABEL: @_Z27test_new_delete_conditionalb
+// CIR: %[[CLEANUP_COND:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cleanup.cond"]
+// CIR: %[[FALSE:.*]] = cir.const #false
+// CIR: cir.store %[[FALSE]], %[[CLEANUP_COND]]
+// CIR: %[[TERN_RESULT:.*]] = cir.ternary
+// CIR: %[[PTR_SAVE:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["cond-cleanup.save"]
+// CIR: %[[SIZE_SAVE:.*]] = cir.alloca !u64i, !cir.ptr<!u64i>, ["cond-cleanup.save"]
+// CIR: %[[NEW_RESULT:.*]] = cir.alloca !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>>, ["__new_result"]
+// CIR: %[[ALLOC_SIZE:.*]] = cir.const #cir.int<1> : !u64i
+// CIR: %[[NEW_PTR:.*]] = cir.call @_Znwm(%[[ALLOC_SIZE]])
+// CIR: cir.store {{.*}}%[[NEW_PTR]], %[[PTR_SAVE]]
+// CIR: cir.store {{.*}}%[[ALLOC_SIZE]], %[[SIZE_SAVE]]
+// CIR: cir.cleanup.scope {
+// CIR: %[[TRUE:.*]] = cir.const #true
+// CIR: cir.store %[[TRUE]], %[[CLEANUP_COND]]
+// CIR: %[[NEW_AS_C:.*]] = cir.cast bitcast %[[NEW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!rec_C>
+// CIR: cir.store{{.*}} %[[NEW_AS_C]], %[[NEW_RESULT]]
+// CIR: cir.call @_ZN1CC1Ev(%[[NEW_AS_C]])
+// CIR: cir.yield
+// CIR: } cleanup eh {
+// CIR: %[[FLAG:.*]] = cir.load {{.*}} %[[CLEANUP_COND]]
+// CIR: cir.if %[[FLAG]] {
+// CIR: %[[RESTORED_PTR:.*]] = cir.load {{.*}} %[[PTR_SAVE]]
+// CIR: cir.call @_ZdlPv(%[[RESTORED_PTR]])
+// CIR: }
+// CIR: cir.yield
+// CIR: }
+// CIR: %[[TRUE_RESULT:.*]] = cir.load{{.*}} %[[NEW_RESULT]]
+// CIR: cir.yield %[[TRUE_RESULT]] : !cir.ptr<!rec_C>
+// CIR: }, false {
+// CIR: %[[FALSE_RESULT:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_C>
+// CIR: cir.yield %[[FALSE_RESULT]] : !cir.ptr<!rec_C>
+// CIR: }) : (!cir.bool) -> !cir.ptr<!rec_C>
+
+// LLVM-LABEL: @_Z27test_new_delete_conditionalb
+// LLVM: store i8 0, ptr %[[CLEANUP_FLAG:.*]], align 1
+// LLVM: br i1 {{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]]
+// LLVM: [[TRUE_BB]]:
+// LLVM: %[[NEWP:.*]] = call ptr @_Znwm(i64 1) #[[ATTR_BUILTIN_NEW]]
+// LLVM: store ptr %[[NEWP]], ptr %[[SAVE_PTR:.*]], align 8
+// LLVM: store i8 1, ptr %[[CLEANUP_FLAG]], align 1
+// LLVM: invoke void @_ZN1CC1Ev(ptr {{.*}}%[[NEWP]])
+// LLVM: to label %{{.*}} unwind label %[[LPAD:.*]]
+// LLVM: [[LPAD]]:
+// LLVM: landingpad { ptr, i32 }
+// LLVM-NEXT: cleanup
+// LLVM: %[[IS_ACTIVE:.*]] = trunc i8 %{{.*}} to i1
+// LLVM: br i1 %[[IS_ACTIVE]], label %[[DO_DEL:.*]], label %[[SKIP_DEL:.*]]
+// LLVM: [[DO_DEL]]:
+// LLVM: %[[LOAD_PTR:.*]] = load ptr, ptr %[[SAVE_PTR]], align 8
+// LLVM: call void @_ZdlPv(ptr %[[LOAD_PTR]]) #[[ATTR_BUILTIN_DEL]]
+// LLVM: resume
+
+// OGCG-LABEL: @_Z27test_new_delete_conditionalb
+// OGCG: store i1 false, ptr %[[OG_FLAG:.*]], align 1
+// OGCG: br i1 {{.*}}, label %[[OG_TRUE:.*]], label %[[OG_FALSE:.*]]
+// OGCG: [[OG_TRUE]]:
+// OGCG: %[[OG_NEWP:.*]] = call noalias nonnull ptr @_Znwm(i64 1) #[[OGCG_ATTR_BUILTIN_NEW]]
+// OGCG: store ptr %[[OG_NEWP]], ptr %[[OG_SAVE:.*]], align 8
+// OGCG: store i1 true, ptr %[[OG_FLAG]], align 1
+// OGCG: invoke void @_ZN1CC1Ev(ptr {{.*}}%[[OG_NEWP]])
+// OGCG: to label %{{.*}} unwind label %[[OG_LPAD:.*]]
+// OGCG: [[OG_LPAD]]:
+// OGCG: landingpad { ptr, i32 }
+// OGCG-NEXT: cleanup
+// OGCG: %[[OG_ACTIVE:.*]] = load i1, ptr %[[OG_FLAG]], align 1
+// OGCG: br i1 %[[OG_ACTIVE]], label %[[OG_DO_DEL:.*]], label %[[OG_SKIP_DEL:.*]]
+// OGCG: [[OG_DO_DEL]]:
+// OGCG: %[[OG_LOAD:.*]] = load ptr, ptr %[[OG_SAVE]], align 8
+// OGCG: call void @_ZdlPv(ptr %[[OG_LOAD]]) #[[OGCG_ATTR_BUILTIN_DEL]]
+// OGCG: resume
+
+void *operator new(unsigned long, int);
+void operator delete(void *, int);
+
+C *test_new_delete_conditional_with_placement(bool cond, int tag) {
+ return cond ? new (tag) C : 0;
+}
+
+// CIR-LABEL: @_Z42test_new_delete_conditional_with_placementbi
+// CIR: %[[CLEANUP_COND:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cleanup.cond"]
+// CIR: %[[TERN_RESULT:.*]] = cir.ternary
+// CIR: %[[PTR_SAVE:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["cond-cleanup.save"]
+// CIR: %[[SIZE_SAVE:.*]] = cir.alloca !u64i, !cir.ptr<!u64i>, ["cond-cleanup.save"]
+// CIR: %[[TAG_SAVE:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["cond-cleanup.save"]
+// CIR: %[[NEW_RESULT:.*]] = cir.alloca !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>>, ["__new_result"]
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !u64i
+// CIR: %[[TAG_VAL:.*]] = cir.load{{.*}}
+// CIR: %[[NEW_PTR:.*]] = cir.call @_Znwmi(%[[ONE]], %[[TAG_VAL]])
+// CIR: cir.store{{.*}} %[[NEW_PTR]], %[[PTR_SAVE]]
+// CIR: cir.store{{.*}} %[[ONE]], %[[SIZE_SAVE]]
+// CIR: cir.cleanup.scope {
+// CIR: cir.store{{.*}} %[[TAG_VAL]], %[[TAG_SAVE]]
+// CIR: %[[TRUE:.*]] = cir.const #true
+// CIR: cir.store %[[TRUE]], %[[CLEANUP_COND]]
+// CIR: %[[NEW_AS_C:.*]] = cir.cast bitcast %[[NEW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!rec_C>
+// CIR: cir.store{{.*}} %[[NEW_AS_C]], %[[NEW_RESULT]]
+// CIR: cir.call @_ZN1CC1Ev(%[[NEW_AS_C]])
+// CIR: } cleanup eh {
+// CIR: %[[FLAG:.*]] = cir.load{{.*}} %[[CLEANUP_COND]]
+// CIR: cir.if %[[FLAG]] {
+// CIR: %[[RESTORED_PTR:.*]] = cir.load {{.*}} %[[PTR_SAVE]]
+// CIR: %[[RESTORED_TAG:.*]] = cir.load {{.*}} %[[TAG_SAVE]]
+// CIR: cir.call @_ZdlPvi(%[[RESTORED_PTR]], %[[RESTORED_TAG]])
+// CIR: }
+// CIR: cir.yield
+// CIR: }
+// CIR: %[[TRUE_RESULT:.*]] = cir.load{{.*}} %[[NEW_RESULT]]
+// CIR: cir.yield %[[TRUE_RESULT]] : !cir.ptr<!rec_C>
+// CIR: }, false {
+// CIR: %[[FALSE_RESULT:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_C>
+// CIR: cir.yield %[[FALSE_RESULT]] : !cir.ptr<!rec_C>
+// CIR: }) : (!cir.bool) -> !cir.ptr<!rec_C>
+
+// LLVM-LABEL: @_Z42test_new_delete_conditional_with_placementbi
+// LLVM: store i8 0, ptr %[[PL_FLAG:.*]], align 1
+// LLVM: br i1 {{.*}}, label %[[PL_TRUE:.*]], label %[[PL_FALSE:.*]]
+// LLVM: [[PL_TRUE]]:
+// LLVM: %[[PL_TAG:.*]] = load i32, ptr %{{.*}}, align 4
+// LLVM: %[[PL_NEWP:.*]] = call ptr @_Znwmi(i64 1, i32 %[[PL_TAG]])
+// LLVM: store ptr %[[PL_NEWP]], ptr %[[PL_SAVE_PTR:.*]], align 8
+// LLVM: store i32 %[[PL_TAG]], ptr %[[PL_SAVE_TAG:.*]], align 4
+// LLVM: store i8 1, ptr %[[PL_FLAG]], align 1
+// LLVM: invoke void @_ZN1CC1Ev(ptr {{.*}}%[[PL_NEWP]])
+// LLVM: to label %{{.*}} unwind label %[[PL_LPAD:.*]]
+// LLVM: [[PL_LPAD]]:
+// LLVM: landingpad { ptr, i32 }
+// LLVM-NEXT: cleanup
+// LLVM: %[[PL_ACTIVE:.*]] = trunc i8 %{{.*}} to i1
+// LLVM: br i1 %[[PL_ACTIVE]], label %[[PL_DO_DEL:.*]], label %[[PL_SKIP_DEL:.*]]
+// LLVM: [[PL_DO_DEL]]:
+// LLVM: %[[PL_LOAD_PTR:.*]] = load ptr, ptr %[[PL_SAVE_PTR]], align 8
+// LLVM: %[[PL_LOAD_TAG:.*]] = load i32, ptr %[[PL_SAVE_TAG]], align 4
+// LLVM: invoke void @_ZdlPvi(ptr %[[PL_LOAD_PTR]], i32 %[[PL_LOAD_TAG]])
+
+// OGCG-LABEL: @_Z42test_new_delete_conditional_with_placementbi
+// OGCG: store i1 false, ptr %[[OGP_FLAG:.*]], align 1
+// OGCG: br i1 {{.*}}, label %[[OGP_TRUE:.*]], label %[[OGP_FALSE:.*]]
+// OGCG: [[OGP_TRUE]]:
+// OGCG: %[[OGP_TAG:.*]] = load i32, ptr %{{.*}}, align 4
+// OGCG: %[[OGP_NEWP:.*]] = call ptr @_Znwmi(i64 1, i32 %[[OGP_TAG]])
+// OGCG: store ptr %[[OGP_NEWP]], ptr %[[OGP_SAVE_PTR:.*]], align 8
+// OGCG: store i32 %[[OGP_TAG]], ptr %[[OGP_SAVE_TAG:.*]], align 4
+// OGCG: store i1 true, ptr %[[OGP_FLAG]], align 1
+// OGCG: invoke void @_ZN1CC1Ev(ptr {{.*}}%[[OGP_NEWP]])
+// OGCG: to label %{{.*}} unwind label %[[OGP_LPAD:.*]]
+// OGCG: [[OGP_LPAD]]:
+// OGCG: landingpad { ptr, i32 }
+// OGCG-NEXT: cleanup
+// OGCG: %[[OGP_ACTIVE:.*]] = load i1, ptr %[[OGP_FLAG]], align 1
+// OGCG: br i1 %[[OGP_ACTIVE]], label %[[OGP_DO_DEL:.*]], label %[[OGP_SKIP_DEL:.*]]
+// OGCG: [[OGP_DO_DEL]]:
+// OGCG: %[[OGP_LOAD_PTR:.*]] = load ptr, ptr %[[OGP_SAVE_PTR]], align 8
+// OGCG: %[[OGP_LOAD_TAG:.*]] = load i32, ptr %[[OGP_SAVE_TAG]], align 4
+// OGCG: invoke void @_ZdlPvi(ptr %[[OGP_LOAD_PTR]], i32 %[[OGP_LOAD_TAG]])
+
// LLVM-DAG: attributes #[[ATTR_BUILTIN_NEW]] = {{{.*}}builtin{{.*}}}
// LLVM-DAG: attributes #[[ATTR_BUILTIN_DEL]] = {{{.*}}builtin{{.*}}}
// OGCG-DAG: attributes #[[OGCG_ATTR_BUILTIN_NEW]] = {{{.*}}builtin{{.*}}}
>From 00fe07e7adc12b74073ff086632c53778bdf7173 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 16 Apr 2026 14:31:31 -0700
Subject: [PATCH 2/3] Fix formatting
---
clang/lib/CIR/CodeGen/CIRGenCleanup.cpp | 3 +--
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 8 +++-----
2 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
index 816a39eb09060..569975dae2c81 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
@@ -140,8 +140,7 @@ DominatingCIRValue::saved_type DominatingCIRValue::save(CIRGenFunction &cgf,
return saved_type(alloca.emitRawPointer(), true);
}
-mlir::Value DominatingCIRValue::restore(CIRGenFunction &cgf,
- saved_type value) {
+mlir::Value DominatingCIRValue::restore(CIRGenFunction &cgf, saved_type value) {
// If the value says it wasn't saved, trust that it's still dominating.
if (!value.getInt())
return value.getPointer();
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index e77c4294e1ba3..21634a9235243 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -900,8 +900,8 @@ static void enterNewDeleteCleanup(CIRGenFunction &cgf, const CXXNewExpr *e,
allocAlign);
for (unsigned i = 0, n = e->getNumPlacementArgs(); i != n; ++i) {
const CallArg &arg = newArgs[i + numNonPlacementArgs];
- cleanup->setPlacementArg(i, arg.getRValue(cgf, cgf.getLoc(e->getSourceRange())),
- arg.ty);
+ cleanup->setPlacementArg(
+ i, arg.getRValue(cgf, cgf.getLoc(e->getSourceRange())), arg.ty);
}
return;
@@ -916,9 +916,7 @@ static void enterNewDeleteCleanup(CIRGenFunction &cgf, const CXXNewExpr *e,
struct ConditionalCleanupTraits {
typedef DominatingValue<RValue>::saved_type ValueTy;
typedef DominatingValue<RValue>::saved_type RValueTy;
- static RValue get(CIRGenFunction &cgf, ValueTy v) {
- return v.restore(cgf);
- }
+ static RValue get(CIRGenFunction &cgf, ValueTy v) { return v.restore(cgf); }
};
typedef CallDeleteDuringNew<ConditionalCleanupTraits> ConditionalCleanup;
>From bb487929a60c11f0cf6606aa9d9ea2c503576e0a Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 17 Apr 2026 16:54:25 -0700
Subject: [PATCH 3/3] Address review feedback
---
clang/lib/CIR/CodeGen/CIRGenCleanup.cpp | 67 ----------
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 35 ++++--
clang/lib/CIR/CodeGen/CIRGenFunction.h | 33 -----
clang/lib/CIR/CodeGen/EHScopeStack.h | 12 --
clang/test/CIR/CodeGen/new-delete.cpp | 161 ++++++++++++++++++++++++
5 files changed, 185 insertions(+), 123 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
index 569975dae2c81..d756d05da3caf 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
@@ -101,73 +101,6 @@ void CIRGenFunction::initFullExprCleanupWithFlag(Address activeFlag) {
cleanup.setTestFlagInEHCleanup();
}
-//===----------------------------------------------------------------------===//
-// DominatingCIRValue / DominatingValue<RValue>
-//===----------------------------------------------------------------------===//
-
-bool DominatingCIRValue::needsSaving(mlir::Value value) {
- if (!value)
- return false;
-
- // If it's a block argument, we don't need to save.
- mlir::Operation *definingOp = value.getDefiningOp();
- if (!definingOp)
- return false;
-
- // If the value is defined in the function entry block, it already dominates
- // everything, so we don't need to save it.
- mlir::Block *currBlock = definingOp->getBlock();
- if (auto fnOp = definingOp->getParentOfType<cir::FuncOp>())
- if (&fnOp.getBody().front() == currBlock)
- return false;
-
- return true;
-}
-
-DominatingCIRValue::saved_type DominatingCIRValue::save(CIRGenFunction &cgf,
- mlir::Value value) {
- if (!needsSaving(value))
- return saved_type(value, false);
-
- // Otherwise, we need an alloca.
- CharUnits align = CharUnits::fromQuantity(
- cgf.cgm.getDataLayout().getABITypeAlign(value.getType()));
- mlir::Location loc = value.getLoc();
- Address alloca =
- cgf.createTempAlloca(value.getType(), align, loc, "cond-cleanup.save");
- cgf.getBuilder().createStore(loc, value, alloca);
-
- return saved_type(alloca.emitRawPointer(), true);
-}
-
-mlir::Value DominatingCIRValue::restore(CIRGenFunction &cgf, saved_type value) {
- // If the value says it wasn't saved, trust that it's still dominating.
- if (!value.getInt())
- return value.getPointer();
-
- // Otherwise, it should be an alloca instruction, as set up in save().
- auto alloca = value.getPointer().getDefiningOp<cir::AllocaOp>();
- return cgf.getBuilder().createAlignedLoad(
- alloca.getLoc(), alloca.getAllocaType(), alloca,
- llvm::MaybeAlign(alloca.getAlignment()));
-}
-
-DominatingValue<RValue>::saved_type
-DominatingValue<RValue>::saved_type::save(CIRGenFunction &cgf, RValue rv) {
- if (rv.isScalar())
- return saved_type(DominatingCIRValue::save(cgf, rv.getValue()));
-
- if (rv.isAggregate())
- cgf.cgm.errorNYI("DominatingValue<RValue>::save for aggregate type");
- else
- cgf.cgm.errorNYI("DominatingValue<RValue>::save for complex type");
- return saved_type(DominatingCIRValue::saved_type());
-}
-
-RValue DominatingValue<RValue>::saved_type::restore(CIRGenFunction &cgf) {
- return RValue::get(DominatingCIRValue::restore(cgf, val));
-}
-
CIRGenFunction::FullExprCleanupScope::FullExprCleanupScope(CIRGenFunction &cgf,
const Expr *subExpr)
: cgf(cgf), cleanups(cgf), scope(nullptr),
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 21634a9235243..82c1dcd36b5f9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -20,6 +20,7 @@
#include "clang/AST/ExprObjC.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/CIR/MissingFeatures.h"
+#include "llvm/ADT/Sequence.h"
#include "llvm/Support/TrailingObjects.h"
using namespace clang;
@@ -898,7 +899,7 @@ static void enterNewDeleteCleanup(CIRGenFunction &cgf, const CXXNewExpr *e,
EHCleanup, e->getNumPlacementArgs(), e->getOperatorDelete(),
newPtr.getPointer(), allocSize, e->implicitAllocationParameters(),
allocAlign);
- for (unsigned i = 0, n = e->getNumPlacementArgs(); i != n; ++i) {
+ for (auto i : llvm::seq<unsigned>(0, e->getNumPlacementArgs())) {
const CallArg &arg = newArgs[i + numNonPlacementArgs];
cleanup->setPlacementArg(
i, arg.getRValue(cgf, cgf.getLoc(e->getSourceRange())), arg.ty);
@@ -908,15 +909,27 @@ static void enterNewDeleteCleanup(CIRGenFunction &cgf, const CXXNewExpr *e,
}
// Otherwise, we need to save all this stuff.
- DominatingValue<RValue>::saved_type savedNewPtr =
- DominatingValue<RValue>::save(cgf, RValue::get(newPtr.getPointer()));
- DominatingValue<RValue>::saved_type savedAllocSize =
- DominatingValue<RValue>::save(cgf, RValue::get(allocSize));
+ auto saveValue = [&](mlir::Value value) -> mlir::Value {
+ CharUnits align = CharUnits::fromQuantity(
+ cgf.cgm.getDataLayout().getABITypeAlign(value.getType()));
+ Address alloca = cgf.createTempAlloca(value.getType(), align,
+ value.getLoc(), "cond-cleanup.save");
+ cgf.getBuilder().createStore(value.getLoc(), value, alloca);
+ return alloca.emitRawPointer();
+ };
+
+ mlir::Value savedNewPtr = saveValue(newPtr.getPointer());
+ mlir::Value savedAllocSize = saveValue(allocSize);
struct ConditionalCleanupTraits {
- typedef DominatingValue<RValue>::saved_type ValueTy;
- typedef DominatingValue<RValue>::saved_type RValueTy;
- static RValue get(CIRGenFunction &cgf, ValueTy v) { return v.restore(cgf); }
+ typedef mlir::Value ValueTy;
+ typedef mlir::Value RValueTy;
+ static RValue get(CIRGenFunction &cgf, ValueTy v) {
+ auto alloca = v.getDefiningOp<cir::AllocaOp>();
+ return RValue::get(cgf.getBuilder().createAlignedLoad(
+ alloca.getLoc(), alloca.getAllocaType(), alloca,
+ llvm::MaybeAlign(alloca.getAlignment())));
+ }
};
typedef CallDeleteDuringNew<ConditionalCleanupTraits> ConditionalCleanup;
@@ -926,12 +939,12 @@ static void enterNewDeleteCleanup(CIRGenFunction &cgf, const CXXNewExpr *e,
EHCleanup, e->getNumPlacementArgs(), e->getOperatorDelete(),
savedNewPtr, savedAllocSize, e->implicitAllocationParameters(),
allocAlign);
- for (unsigned i = 0, n = e->getNumPlacementArgs(); i != n; ++i) {
+ for (auto i : llvm::seq<unsigned>(0, e->getNumPlacementArgs())) {
const CallArg &arg = newArgs[i + numNonPlacementArgs];
cleanup->setPlacementArg(
i,
- DominatingValue<RValue>::save(
- cgf, arg.getRValue(cgf, cgf.getLoc(e->getSourceRange()))),
+ saveValue(
+ arg.getRValue(cgf, cgf.getLoc(e->getSourceRange())).getValue()),
arg.ty);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 1905800554838..6fe9dd751d43a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -2605,39 +2605,6 @@ class CIRGenFunction : public CIRGenTypeCache {
};
};
-/// Helper class with most of the code for saving a value for a
-/// conditional expression cleanup.
-struct DominatingCIRValue {
- typedef llvm::PointerIntPair<mlir::Value, 1, bool> saved_type;
-
- /// Answer whether the given value needs extra work to be saved.
- static bool needsSaving(mlir::Value value);
-
- static saved_type save(CIRGenFunction &cgf, mlir::Value value);
- static mlir::Value restore(CIRGenFunction &cgf, saved_type value);
-};
-
-/// A specialization of DominatingValue for RValue.
-template <> struct DominatingValue<RValue> {
- typedef RValue type;
- class saved_type {
- DominatingCIRValue::saved_type val;
-
- saved_type(DominatingCIRValue::saved_type val) : val(val) {}
-
- public:
- static saved_type save(CIRGenFunction &cgf, RValue value);
- RValue restore(CIRGenFunction &cgf);
- };
-
- static saved_type save(CIRGenFunction &cgf, type value) {
- return saved_type::save(cgf, value);
- }
- static type restore(CIRGenFunction &cgf, saved_type value) {
- return value.restore(cgf);
- }
-};
-
} // namespace clang::CIRGen
#endif
diff --git a/clang/lib/CIR/CodeGen/EHScopeStack.h b/clang/lib/CIR/CodeGen/EHScopeStack.h
index 9e7a917d1d6cf..308d98f108101 100644
--- a/clang/lib/CIR/CodeGen/EHScopeStack.h
+++ b/clang/lib/CIR/CodeGen/EHScopeStack.h
@@ -25,18 +25,6 @@ namespace clang::CIRGen {
class CIRGenFunction;
-template <class T> struct InvariantValue {
- using type = T;
- using saved_type = T;
- static bool needsSaving(type value) { return false; }
- static saved_type save(CIRGenFunction &cgf, type value) { return value; }
- static type restore(CIRGenFunction &cgf, saved_type value) { return value; }
-};
-
-/// A metaprogramming class for ensuring that a value will dominate an
-/// arbitrary position in a function.
-template <class T> struct DominatingValue : InvariantValue<T> {};
-
enum CleanupKind : unsigned {
/// Denotes a cleanup that should run when a scope is exited using exceptional
/// control flow (a throw statement leading to stack unwinding, ).
diff --git a/clang/test/CIR/CodeGen/new-delete.cpp b/clang/test/CIR/CodeGen/new-delete.cpp
index 71e3407001ddb..6902d37b25e56 100644
--- a/clang/test/CIR/CodeGen/new-delete.cpp
+++ b/clang/test/CIR/CodeGen/new-delete.cpp
@@ -412,6 +412,167 @@ C *test_new_delete_conditional_with_placement(bool cond, int tag) {
// OGCG: %[[OGP_LOAD_TAG:.*]] = load i32, ptr %[[OGP_SAVE_TAG]], align 4
// OGCG: invoke void @_ZdlPvi(ptr %[[OGP_LOAD_PTR]], i32 %[[OGP_LOAD_TAG]])
+struct D {
+ D();
+ static void operator delete(void *, unsigned long);
+ static void operator delete[](void *, unsigned long);
+};
+
+D *test_new_delete_conditional_with_size(bool cond) {
+ return cond ? new D : 0;
+}
+
+// CIR-LABEL: @_Z37test_new_delete_conditional_with_sizeb
+// CIR: %[[CLEANUP_COND:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cleanup.cond"]
+// CIR: %[[FALSE:.*]] = cir.const #false
+// CIR: cir.store %[[FALSE]], %[[CLEANUP_COND]]
+// CIR: cir.ternary
+// CIR: %[[PTR_SAVE:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["cond-cleanup.save"]
+// CIR: %[[SIZE_SAVE:.*]] = cir.alloca !u64i, !cir.ptr<!u64i>, ["cond-cleanup.save"]
+// CIR: %[[NEW_RESULT:.*]] = cir.alloca !cir.ptr<!rec_D>, !cir.ptr<!cir.ptr<!rec_D>>, ["__new_result"]
+// CIR: %[[ALLOC_SIZE:.*]] = cir.const #cir.int<1> : !u64i
+// CIR: %[[NEW_PTR:.*]] = cir.call @_Znwm(%[[ALLOC_SIZE]])
+// CIR: cir.store {{.*}}%[[NEW_PTR]], %[[PTR_SAVE]]
+// CIR: cir.store {{.*}}%[[ALLOC_SIZE]], %[[SIZE_SAVE]]
+// CIR: cir.cleanup.scope {
+// CIR: %[[TRUE:.*]] = cir.const #true
+// CIR: cir.store %[[TRUE]], %[[CLEANUP_COND]]
+// CIR: cir.call @_ZN1DC1Ev
+// CIR: cir.yield
+// CIR: } cleanup eh {
+// CIR: %[[FLAG:.*]] = cir.load {{.*}} %[[CLEANUP_COND]]
+// CIR: cir.if %[[FLAG]] {
+// CIR: %[[RESTORED_PTR:.*]] = cir.load {{.*}} %[[PTR_SAVE]]
+// CIR: %[[RESTORED_SIZE:.*]] = cir.load {{.*}} %[[SIZE_SAVE]]
+// CIR: cir.call @_ZN1DdlEPvm(%[[RESTORED_PTR]], %[[RESTORED_SIZE]])
+// CIR: }
+// CIR: cir.yield
+// CIR: }
+// CIR: %[[TRUE_RESULT:.*]] = cir.load{{.*}} %[[NEW_RESULT]]
+// CIR: cir.yield %[[TRUE_RESULT]] : !cir.ptr<!rec_D>
+// CIR: }, false {
+// CIR: %[[FALSE_RESULT:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_D>
+// CIR: cir.yield %[[FALSE_RESULT]] : !cir.ptr<!rec_D>
+// CIR: }) : (!cir.bool) -> !cir.ptr<!rec_D>
+
+// LLVM-LABEL: @_Z37test_new_delete_conditional_with_sizeb
+// LLVM: store i8 0, ptr %[[SD_FLAG:.*]], align 1
+// LLVM: br i1 {{.*}}, label %[[SD_TRUE:.*]], label %[[SD_FALSE:.*]]
+// LLVM: [[SD_TRUE]]:
+// LLVM: %[[SD_NEWP:.*]] = call ptr @_Znwm(i64 1)
+// LLVM: store ptr %[[SD_NEWP]], ptr %[[SD_SAVE_PTR:.*]], align 8
+// LLVM: store i64 1, ptr %[[SD_SAVE_SIZE:.*]], align 8
+// LLVM: store i8 1, ptr %[[SD_FLAG]], align 1
+// LLVM: invoke void @_ZN1DC1Ev(ptr {{.*}}%[[SD_NEWP]])
+// LLVM: to label %{{.*}} unwind label %[[SD_LPAD:.*]]
+// LLVM: [[SD_LPAD]]:
+// LLVM: landingpad { ptr, i32 }
+// LLVM-NEXT: cleanup
+// LLVM: %[[SD_ACTIVE:.*]] = trunc i8 %{{.*}} to i1
+// LLVM: br i1 %[[SD_ACTIVE]], label %[[SD_DO_DEL:.*]], label %[[SD_SKIP_DEL:.*]]
+// LLVM: [[SD_DO_DEL]]:
+// LLVM: %[[SD_LOAD_PTR:.*]] = load ptr, ptr %[[SD_SAVE_PTR]], align 8
+// LLVM: %[[SD_LOAD_SIZE:.*]] = load i64, ptr %[[SD_SAVE_SIZE]], align 8
+// LLVM: invoke void @_ZN1DdlEPvm(ptr %[[SD_LOAD_PTR]], i64 %[[SD_LOAD_SIZE]])
+
+// OGCG-LABEL: @_Z37test_new_delete_conditional_with_sizeb
+// OGCG: store i1 false, ptr %[[OGS_FLAG:.*]], align 1
+// OGCG: br i1 {{.*}}, label %[[OGS_TRUE:.*]], label %[[OGS_FALSE:.*]]
+// OGCG: [[OGS_TRUE]]:
+// OGCG: %[[OGS_NEWP:.*]] = call noalias nonnull ptr @_Znwm(i64 1)
+// OGCG: store ptr %[[OGS_NEWP]], ptr %[[OGS_SAVE:.*]], align 8
+// OGCG: store i1 true, ptr %[[OGS_FLAG]], align 1
+// OGCG: invoke void @_ZN1DC1Ev(ptr {{.*}}%[[OGS_NEWP]])
+// OGCG: to label %{{.*}} unwind label %[[OGS_LPAD:.*]]
+// OGCG: [[OGS_LPAD]]:
+// OGCG: landingpad { ptr, i32 }
+// OGCG-NEXT: cleanup
+// OGCG: %[[OGS_ACTIVE:.*]] = load i1, ptr %[[OGS_FLAG]], align 1
+// OGCG: br i1 %[[OGS_ACTIVE]], label %[[OGS_DO_DEL:.*]], label %[[OGS_SKIP_DEL:.*]]
+// OGCG: [[OGS_DO_DEL]]:
+// OGCG: %[[OGS_LOAD_PTR:.*]] = load ptr, ptr %[[OGS_SAVE]], align 8
+// OGCG: invoke void @_ZN1DdlEPvm(ptr %[[OGS_LOAD_PTR]], i64 1)
+
+D *test_new_delete_conditional_array(bool cond, int n) {
+ return cond ? new D[n] : 0;
+}
+
+// CIR-LABEL: @_Z33test_new_delete_conditional_arraybi
+// CIR: %[[CLEANUP_COND:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cleanup.cond"]
+// CIR: %[[FALSE:.*]] = cir.const #false
+// CIR: cir.store %[[FALSE]], %[[CLEANUP_COND]]
+// CIR: cir.ternary
+// CIR: %[[PTR_SAVE:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["cond-cleanup.save"]
+// CIR: %[[SIZE_SAVE:.*]] = cir.alloca !u64i, !cir.ptr<!u64i>, ["cond-cleanup.save"]
+// CIR: %[[NEW_RESULT:.*]] = cir.alloca !cir.ptr<!rec_D>, !cir.ptr<!cir.ptr<!rec_D>>, ["__new_result"]
+// CIR: %[[N:.*]] = cir.load {{.*}} : !cir.ptr<!s32i>, !s32i
+// CIR: %[[N_EXT:.*]] = cir.cast integral %[[N]] : !s32i -> !s64i
+// CIR: %result, %overflow = cir.add.overflow %{{.*}}, %{{.*}} : !u64i -> !u64i
+// CIR: %[[ALLOC_SIZE:.*]] = cir.select
+// CIR: %[[NEW_PTR:.*]] = cir.call @_Znam(%[[ALLOC_SIZE]])
+// CIR: cir.store {{.*}}%[[NEW_PTR]], %[[PTR_SAVE]]
+// CIR: cir.store {{.*}}%[[ALLOC_SIZE]], %[[SIZE_SAVE]]
+// CIR: cir.cleanup.scope {
+// CIR: %[[TRUE:.*]] = cir.const #true
+// CIR: cir.store %[[TRUE]], %[[CLEANUP_COND]]
+// CIR: cir.call @_ZN1DC1Ev
+// CIR: cir.yield
+// CIR: } cleanup eh {
+// CIR: %[[FLAG:.*]] = cir.load {{.*}} %[[CLEANUP_COND]]
+// CIR: cir.if %[[FLAG]] {
+// CIR: %[[RESTORED_PTR:.*]] = cir.load {{.*}} %[[PTR_SAVE]]
+// CIR: %[[RESTORED_SIZE:.*]] = cir.load {{.*}} %[[SIZE_SAVE]]
+// CIR: cir.call @_ZN1DdaEPvm(%[[RESTORED_PTR]], %[[RESTORED_SIZE]])
+// CIR: }
+// CIR: cir.yield
+// CIR: }
+// CIR: %[[TRUE_RESULT:.*]] = cir.load{{.*}} %[[NEW_RESULT]]
+// CIR: cir.yield %[[TRUE_RESULT]] : !cir.ptr<!rec_D>
+// CIR: }, false {
+// CIR: %[[FALSE_RESULT:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_D>
+// CIR: cir.yield %[[FALSE_RESULT]] : !cir.ptr<!rec_D>
+// CIR: }) : (!cir.bool) -> !cir.ptr<!rec_D>
+
+// LLVM-LABEL: @_Z33test_new_delete_conditional_arraybi
+// LLVM: store i8 0, ptr %[[ARR_FLAG:.*]], align 1
+// LLVM: br i1 {{.*}}, label %[[ARR_TRUE:.*]], label %[[ARR_FALSE:.*]]
+// LLVM: [[ARR_TRUE]]:
+// LLVM: %[[ARR_ALLOC_SIZE:.*]] = select i1 %{{.*}}, i64 -1, i64 %{{.*}}
+// LLVM: %[[ARR_NEWP:.*]] = call ptr @_Znam(i64 %[[ARR_ALLOC_SIZE]])
+// LLVM: store ptr %[[ARR_NEWP]], ptr %[[ARR_SAVE_PTR:.*]], align 8
+// LLVM: store i64 %[[ARR_ALLOC_SIZE]], ptr %[[ARR_SAVE_SIZE:.*]], align 8
+// LLVM: store i8 1, ptr %[[ARR_FLAG]], align 1
+// LLVM: invoke void @_ZN1DC1Ev
+// LLVM: [[ARR_LPAD:.*]]:
+// LLVM: landingpad { ptr, i32 }
+// LLVM-NEXT: cleanup
+// LLVM: %[[ARR_ACTIVE:.*]] = trunc i8 %{{.*}} to i1
+// LLVM: br i1 %[[ARR_ACTIVE]], label %[[ARR_DO_DEL:.*]], label %[[ARR_SKIP_DEL:.*]]
+// LLVM: [[ARR_DO_DEL]]:
+// LLVM: %[[ARR_LOAD_PTR:.*]] = load ptr, ptr %[[ARR_SAVE_PTR]], align 8
+// LLVM: %[[ARR_LOAD_SIZE:.*]] = load i64, ptr %[[ARR_SAVE_SIZE]], align 8
+// LLVM: invoke void @_ZN1DdaEPvm(ptr %[[ARR_LOAD_PTR]], i64 %[[ARR_LOAD_SIZE]])
+
+// OGCG-LABEL: @_Z33test_new_delete_conditional_arraybi
+// OGCG: store i1 false, ptr %[[OGA_FLAG:.*]], align 1
+// OGCG: br i1 {{.*}}, label %[[OGA_TRUE:.*]], label %[[OGA_FALSE:.*]]
+// OGCG: [[OGA_TRUE]]:
+// OGCG: %[[OGA_ALLOC_SIZE:.*]] = select i1 %{{.*}}, i64 -1, i64 %{{.*}}
+// OGCG: %[[OGA_NEWP:.*]] = call noalias nonnull ptr @_Znam(i64 %[[OGA_ALLOC_SIZE]])
+// OGCG: store ptr %[[OGA_NEWP]], ptr %[[OGA_SAVE_PTR:.*]], align 8
+// OGCG: store i64 %[[OGA_ALLOC_SIZE]], ptr %[[OGA_SAVE_SIZE:.*]], align 8
+// OGCG: store i1 true, ptr %[[OGA_FLAG]], align 1
+// OGCG: invoke void @_ZN1DC1Ev
+// OGCG: [[OGA_LPAD:.*]]:
+// OGCG: landingpad { ptr, i32 }
+// OGCG-NEXT: cleanup
+// OGCG: %[[OGA_ACTIVE:.*]] = load i1, ptr %[[OGA_FLAG]], align 1
+// OGCG: br i1 %[[OGA_ACTIVE]], label %[[OGA_DO_DEL:.*]], label %[[OGA_SKIP_DEL:.*]]
+// OGCG: [[OGA_DO_DEL]]:
+// OGCG: %[[OGA_LOAD_PTR:.*]] = load ptr, ptr %[[OGA_SAVE_PTR]], align 8
+// OGCG: %[[OGA_LOAD_SIZE:.*]] = load i64, ptr %[[OGA_SAVE_SIZE]], align 8
+// OGCG: invoke void @_ZN1DdaEPvm(ptr %[[OGA_LOAD_PTR]], i64 %[[OGA_LOAD_SIZE]])
+
// LLVM-DAG: attributes #[[ATTR_BUILTIN_NEW]] = {{{.*}}builtin{{.*}}}
// LLVM-DAG: attributes #[[ATTR_BUILTIN_DEL]] = {{{.*}}builtin{{.*}}}
// OGCG-DAG: attributes #[[OGCG_ATTR_BUILTIN_NEW]] = {{{.*}}builtin{{.*}}}
More information about the cfe-commits
mailing list