Author: Andy Kaylor
Date: 2026-03-05T21:13:42Z
New Revision: 6bd8820704d4d41fb3e41ec7032ce8ef2330bf91

URL: 
https://github.com/llvm/llvm-project/commit/6bd8820704d4d41fb3e41ec7032ce8ef2330bf91
DIFF: 
https://github.com/llvm/llvm-project/commit/6bd8820704d4d41fb3e41ec7032ce8ef2330bf91.diff

LOG: [CIR] Add support for delete cleanup after new operators (#184707)

This adds support for calling operator delete when an exception is
thrown during initialization following an operator new call.

This does not yet handle the case where a temporary object is
materialized during the object initialization. That case is marked by
the "setupCleanupBlockActivation" diagnostic in deactivateCleanupBlock
and will be implemented in a future change.

Added: 
    clang/test/CIR/CodeGen/new-delete.cpp

Modified: 
    clang/include/clang/CIR/MissingFeatures.h
    clang/lib/CIR/CodeGen/CIRGenCall.cpp
    clang/lib/CIR/CodeGen/CIRGenCall.h
    clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
    clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
    clang/lib/CIR/CodeGen/CIRGenFunction.h
    clang/lib/CIR/CodeGen/EHScopeStack.h

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index d206503d914f5..1e3a2c9af35d1 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -291,7 +291,6 @@ struct MissingFeatures {
   static bool handleBuiltinICEArguments() { return false; }
   static bool hip() { return false; }
   static bool incrementProfileCounter() { return false; }
-  static bool innermostEHScope() { return false; }
   static bool insertBuiltinUnpredictable() { return false; }
   static bool instrumentation() { return false; }
   static bool intrinsicElementTypeSupport() { return false; }
@@ -348,6 +347,7 @@ struct MissingFeatures {
   static bool targetCodeGenInfoGetNullPointer() { return false; }
   static bool thunks() { return false; }
   static bool tryEmitAsConstant() { return false; }
+  static bool typeAwareAllocation() { return false; }
   static bool typeChecks() { return false; }
   static bool useEHCleanupForArray() { return false; }
   static bool vaArgABILowering() { return false; }

diff  --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 157dc3fdd56fb..61ccd85cd6342 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -1021,6 +1021,16 @@ CIRGenTypes::arrangeFunctionDeclaration(const 
FunctionDecl *fd) {
   return arrangeFreeFunctionType(funcTy.castAs<FunctionProtoType>());
 }
 
+RValue CallArg::getRValue(CIRGenFunction &cgf, mlir::Location loc) const {
+  if (!hasLV)
+    return rv;
+  LValue copy = cgf.makeAddrLValue(cgf.createMemTemp(ty, loc), ty);
+  cgf.emitAggregateCopy(copy, lv, ty, AggValueSlot::DoesNotOverlap,
+                        lv.isVolatile());
+  isUsed = true;
+  return RValue::getAggregate(copy.getAddress());
+}
+
 static cir::CIRCallOpInterface
 emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
                cir::FuncType indirectFuncTy, mlir::Value indirectFuncVal,

diff  --git a/clang/lib/CIR/CodeGen/CIRGenCall.h 
b/clang/lib/CIR/CodeGen/CIRGenCall.h
index 347bd4a7c8266..b30b4969ca45e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.h
@@ -202,7 +202,7 @@ struct CallArg {
 
   /// A data-flow flag to make sure getRValue and/or copyInto are not
   /// called twice for duplicated IR emission.
-  [[maybe_unused]] mutable bool isUsed;
+  mutable bool isUsed;
 
 public:
   clang::QualType ty;
@@ -215,6 +215,10 @@ struct CallArg {
 
   bool hasLValue() const { return hasLV; }
 
+  /// \returns an independent RValue. If the CallArg contains an LValue,
+  /// a temporary copy is returned.
+  RValue getRValue(CIRGenFunction &cgf, mlir::Location loc) const;
+
   LValue getKnownLValue() const {
     assert(hasLV && !isUsed);
     return lv;

diff  --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
index bdb2947200f23..cbed8452810c5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
@@ -95,7 +95,6 @@ void *EHScopeStack::pushCleanup(CleanupKind kind, size_t 
size) {
   bool isLifetimeMarker = kind & LifetimeMarker;
   bool skipCleanupScope = false;
 
-  assert(!cir::MissingFeatures::innermostEHScope());
   cir::CleanupKind cleanupKind = cir::CleanupKind::All;
   if (isEHCleanup && cgf->getLangOpts().Exceptions) {
     cleanupKind =
@@ -193,6 +192,25 @@ bool EHScopeStack::requiresCatchOrCleanup() const {
   return false;
 }
 
+/// Deactive a cleanup that was created in an active state.
+void CIRGenFunction::deactivateCleanupBlock(EHScopeStack::stable_iterator c,
+                                            mlir::Operation *dominatingIP) {
+  assert(c != ehStack.stable_end() && "deactivating bottom of stack?");
+  EHCleanupScope &scope = cast<EHCleanupScope>(*ehStack.find(c));
+  assert(scope.isActive() && "double deactivation");
+
+  // If it's the top of the stack, just pop it, but do so only if it belongs
+  // to the current RunCleanupsScope.
+  if (c == ehStack.stable_begin() &&
+      currentCleanupStackDepth.strictlyEncloses(c)) {
+    popCleanupBlock();
+    return;
+  }
+
+  // Otherwise, follow the general case.
+  cgm.errorNYI("deactivateCleanupBlock: setupCleanupBlockActivation");
+}
+
 static void emitCleanup(CIRGenFunction &cgf, cir::CleanupScopeOp cleanupScope,
                         EHScopeStack::Cleanup *cleanup,
                         EHScopeStack::Cleanup::Flags flags) {
@@ -245,10 +263,11 @@ void CIRGenFunction::popCleanupBlock() {
   bool hasFallthrough = fallthroughSource != nullptr && isActive;
 
   bool requiresNormalCleanup = scope.isNormalCleanup() && hasFallthrough;
+  bool requiresEHCleanup = scope.isEHCleanup() && hasFallthrough;
 
   // If we don't need the cleanup at all, we're done.
   assert(!cir::MissingFeatures::ehCleanupScopeRequiresEHCleanup());
-  if (!requiresNormalCleanup) {
+  if (!requiresNormalCleanup && !requiresEHCleanup) {
     ehStack.popCleanup();
     return;
   }

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 97f496c89ab0f..35f74e7120b0b 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/Support/TrailingObjects.h"
 
 using namespace clang;
 using namespace clang::CIRGen;
@@ -647,6 +648,209 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction 
&cgf, const CXXNewExpr *e,
   return size;
 }
 
+/// 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;
+}
+
+RValue CIRGenFunction::emitNewOrDeleteBuiltinCall(const FunctionProtoType 
*type,
+                                                  const CallExpr *callExpr,
+                                                  OverloadedOperatorKind op) {
+  CallArgList args;
+  emitCallArgs(args, type, callExpr->arguments());
+  // Find the allocation or deallocation function that we're calling.
+  ASTContext &astContext = getContext();
+  assert(op == OO_New || op == OO_Delete);
+  DeclarationName name = astContext.DeclarationNames.getCXXOperatorName(op);
+
+  clang::DeclContextLookupResult lookupResult =
+      astContext.getTranslationUnitDecl()->lookup(name);
+  for (const NamedDecl *decl : lookupResult) {
+    if (const auto *funcDecl = dyn_cast<FunctionDecl>(decl)) {
+      if (astContext.hasSameType(funcDecl->getType().getTypePtr(), type)) {
+        if (sanOpts.has(SanitizerKind::AllocToken)) {
+          // TODO: Set !alloc_token metadata.
+          assert(!cir::MissingFeatures::allocToken());
+          cgm.errorNYI("Alloc token sanitizer not yet supported!");
+        }
+
+        // Emit the call to operator new/delete.
+        return emitNewDeleteCall(*this, funcDecl, type, args);
+      }
+    }
+  }
+
+  llvm_unreachable("predeclared global operator new/delete is missing");
+}
+
+namespace {
+template <typename Traits> struct PlacementArg {
+  typename Traits::RValueTy argValue;
+  QualType argType;
+};
+
+/// A cleanup to call the given 'operator delete' function upon abnormal
+/// exit from a new expression. Templated on a traits type that deals with
+/// ensuring that the arguments dominate the cleanup if necessary.
+template <typename Traits>
+class CallDeleteDuringNew final
+    : public EHScopeStack::Cleanup,
+      private llvm::TrailingObjects<CallDeleteDuringNew<Traits>,
+                                    PlacementArg<Traits>> {
+  using TrailingObj =
+      llvm::TrailingObjects<CallDeleteDuringNew<Traits>, PlacementArg<Traits>>;
+  friend TrailingObj;
+  using TrailingObj::getTrailingObjects;
+
+  /// Type used to hold llvm::Value*s.
+  typedef typename Traits::ValueTy ValueTy;
+  /// Type used to hold RValues.
+  typedef typename Traits::RValueTy RValueTy;
+
+  unsigned numPlacementArgs : 30;
+  LLVM_PREFERRED_TYPE(AlignedAllocationMode)
+  unsigned passAlignmentToPlacementDelete : 1;
+  const FunctionDecl *operatorDelete;
+  ValueTy ptr;
+  ValueTy allocSize;
+  CharUnits allocAlign;
+
+  PlacementArg<Traits> *getPlacementArgs() { return getTrailingObjects(); }
+
+  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);
+  }
+
+  CallDeleteDuringNew(size_t numPlacementArgs,
+                      const FunctionDecl *operatorDelete, ValueTy ptr,
+                      ValueTy allocSize,
+                      const ImplicitAllocationParameters &iap,
+                      CharUnits allocAlign, const CallArgList *newArgs,
+                      unsigned numNonPlacementArgs, CIRGenFunction *cgf,
+                      mlir::Location loc)
+      : 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);
+    }
+  }
+
+  void emit(CIRGenFunction &cgf, Flags flags) override {
+    const auto *fpt = operatorDelete->getType()->castAs<FunctionProtoType>();
+    CallArgList deleteArgs;
+
+    unsigned firstNonTypeArg = 0;
+    TypeAwareAllocationMode typeAwareDeallocation = 
TypeAwareAllocationMode::No;
+    assert(!cir::MissingFeatures::typeAwareAllocation());
+
+    // The first argument after type-identity parameter (if any) is always
+    // a void* (or C* for a destroying operator delete for class type C).
+    deleteArgs.add(Traits::get(cgf, ptr), fpt->getParamType(firstNonTypeArg));
+
+    // Figure out what other parameters we should be implicitly passing.
+    UsualDeleteParams params;
+    if (numPlacementArgs) {
+      // A placement deallocation function is implicitly passed an alignment
+      // if the placement allocation function was, but is never passed a size.
+      params.Alignment =
+          alignedAllocationModeFromBool(passAlignmentToPlacementDelete);
+      params.TypeAwareDelete = typeAwareDeallocation;
+      params.Size = isTypeAwareAllocation(params.TypeAwareDelete);
+    } else {
+      // For a non-placement new-expression, 'operator delete' can take a
+      // size and/or an alignment if it has the right parameters.
+      params = operatorDelete->getUsualDeleteParams();
+    }
+
+    assert(!params.DestroyingDelete &&
+           "should not call destroying delete in a new-expression");
+
+    // The second argument can be a std::size_t (for non-placement delete).
+    if (params.Size)
+      deleteArgs.add(Traits::get(cgf, allocSize),
+                     cgf.getContext().getSizeType());
+
+    // The next (second or third) argument can be a std::align_val_t, which
+    // is an enum whose underlying type is std::size_t.
+    // FIXME: Use the right type as the parameter type. Note that in a call
+    // to operator delete(size_t, ...), we may not have it available.
+    if (isAlignedAllocation(params.Alignment))
+      cgf.cgm.errorNYI("CallDeleteDuringNew: aligned allocation");
+
+    // Pass the rest of the arguments, which must match exactly.
+    for (unsigned i = 0; i != numPlacementArgs; ++i) {
+      auto arg = getPlacementArgs()[i];
+      deleteArgs.add(Traits::get(cgf, arg.argValue), arg.argType);
+    }
+
+    // Call 'operator delete'.
+    emitNewDeleteCall(cgf, operatorDelete, fpt, deleteArgs);
+  }
+};
+} // namespace
+
+/// Enter a cleanup to call 'operator delete' if the initializer in a
+/// new-expression throws.
+static void enterNewDeleteCleanup(CIRGenFunction &cgf, const CXXNewExpr *e,
+                                  Address newPtr, mlir::Value allocSize,
+                                  CharUnits allocAlign,
+                                  const CallArgList &newArgs) {
+  unsigned numNonPlacementArgs = e->getNumImplicitArgs();
+
+  // If we're not inside a conditional branch, then the cleanup will
+  // dominate and we can do the easier (and more efficient) thing.
+  if (!cgf.isInConditionalBranch()) {
+    struct DirectCleanupTraits {
+      typedef mlir::Value ValueTy;
+      typedef RValue RValueTy;
+      static RValue get(CIRGenFunction &, ValueTy v) { return RValue::get(v); }
+      static RValue get(CIRGenFunction &, RValueTy v) { return v; }
+    };
+
+    typedef CallDeleteDuringNew<DirectCleanupTraits> DirectCleanup;
+
+    assert(!cir::MissingFeatures::typeAwareAllocation());
+    cgf.ehStack.pushCleanupWithExtra<DirectCleanup>(
+        EHCleanup, e->getNumPlacementArgs(), e->getOperatorDelete(),
+        newPtr.getPointer(), allocSize, e->implicitAllocationParameters(),
+        allocAlign, &newArgs, numNonPlacementArgs, &cgf,
+        cgf.getLoc(e->getSourceRange()));
+
+    return;
+  }
+
+  cgf.cgm.errorNYI(e->getSourceRange(),
+                   "enterNewDeleteCleanup: conditional branch");
+}
+
 static void storeAnyExprIntoOneUnit(CIRGenFunction &cgf, const Expr *init,
                                     QualType allocType, Address newPtr,
                                     AggValueSlot::Overlap_t mayOverlap) {
@@ -912,59 +1116,6 @@ RValue CIRGenFunction::emitCXXPseudoDestructorExpr(
   return RValue::get(nullptr);
 }
 
-/// 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;
-}
-
-RValue CIRGenFunction::emitNewOrDeleteBuiltinCall(const FunctionProtoType 
*type,
-                                                  const CallExpr *callExpr,
-                                                  OverloadedOperatorKind op) {
-  CallArgList args;
-  emitCallArgs(args, type, callExpr->arguments());
-  // Find the allocation or deallocation function that we're calling.
-  ASTContext &astContext = getContext();
-  assert(op == OO_New || op == OO_Delete);
-  DeclarationName name = astContext.DeclarationNames.getCXXOperatorName(op);
-
-  clang::DeclContextLookupResult lookupResult =
-      astContext.getTranslationUnitDecl()->lookup(name);
-  for (const auto *decl : lookupResult) {
-    if (const auto *funcDecl = dyn_cast<FunctionDecl>(decl)) {
-      if (astContext.hasSameType(funcDecl->getType(), QualType(type, 0))) {
-        if (sanOpts.has(SanitizerKind::AllocToken)) {
-          // TODO: Set !alloc_token metadata.
-          assert(!cir::MissingFeatures::allocToken());
-          cgm.errorNYI("Alloc token sanitizer not yet supported!");
-        }
-
-        // Emit the call to operator new/delete.
-        return emitNewDeleteCall(*this, funcDecl, type, args);
-      }
-    }
-  }
-
-  llvm_unreachable("predeclared global operator new/delete is missing");
-}
-
 namespace {
 /// Calls the given 'operator delete' on a single object.
 struct CallObjectDelete final : EHScopeStack::Cleanup {
@@ -1190,10 +1341,24 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const 
CXXNewExpr *e) {
     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");
+  // exception is thrown. If we do this, we'll be creating the result pointer
+  // inside a cleanup scope, either with a bitcast or an offset based on the
+  // array cookie size. However, we need to return that pointer from outside
+  // the cleanup scope, so we need to store it in a temporary variable.
+  bool useNewDeleteCleanup =
+      e->getOperatorDelete() &&
+      !e->getOperatorDelete()->isReservedGlobalPlacementOperator();
+  EHScopeStack::stable_iterator operatorDeleteCleanup;
+  mlir::Operation *cleanupDominator = nullptr;
+  if (useNewDeleteCleanup) {
+    assert(!cir::MissingFeatures::typeAwareAllocation());
+    enterNewDeleteCleanup(*this, e, allocation, allocSize, allocAlign,
+                          allocatorArgs);
+    operatorDeleteCleanup = ehStack.stable_begin();
+    cleanupDominator =
+        cir::UnreachableOp::create(builder, getLoc(e->getSourceRange()))
+            .getOperation();
+  }
 
   if (allocSize != allocSizeWithoutCookie) {
     assert(e->isArray());
@@ -1212,6 +1377,16 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const 
CXXNewExpr *e) {
   Address result = builder.createElementBitCast(getLoc(e->getSourceRange()),
                                                 allocation, elementTy);
 
+  // If we're inside a new delete cleanup, store the result pointer.
+  Address resultPtr = Address::invalid();
+  if (useNewDeleteCleanup) {
+    resultPtr =
+        createTempAlloca(builder.getPointerTo(elementTy), 
result.getAlignment(),
+                         getLoc(e->getSourceRange()), "__new_result");
+    builder.createStore(getLoc(e->getSourceRange()), result.getPointer(),
+                        resultPtr);
+  }
+
   // 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
@@ -1224,6 +1399,21 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const 
CXXNewExpr *e) {
 
   emitNewInitializer(*this, e, allocType, elementTy, result, numElements,
                      allocSizeWithoutCookie);
+
+  // Deactivate the 'operator delete' cleanup if we finished
+  // initialization.
+  if (useNewDeleteCleanup) {
+    assert(operatorDeleteCleanup.isValid());
+    assert(resultPtr.isValid());
+    deactivateCleanupBlock(operatorDeleteCleanup, cleanupDominator);
+    cleanupDominator->erase();
+    cir::LoadOp loadResult =
+        builder.createLoad(getLoc(e->getSourceRange()), resultPtr);
+    result = result.withPointer(loadResult.getResult());
+  }
+
+  assert(!cir::MissingFeatures::exprNewNullCheck());
+
   return result.getPointer();
 }
 

diff  --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 539d7839d1dfe..0e82958ef6f39 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -970,6 +970,16 @@ class CIRGenFunction : public CIRGenTypeCache {
                         ArrayRef<mlir::Value *> valuesToReload = {});
   void popCleanupBlock();
 
+  /// Deactivates the given cleanup block. The block cannot be reactivated. 
Pops
+  /// it if it's the top of the stack.
+  ///
+  /// \param DominatingIP - An instruction which is known to
+  ///   dominate the current IP (if set) and which lies along
+  ///   all paths of execution between the current IP and the
+  ///   the point at which the cleanup comes into scope.
+  void deactivateCleanupBlock(EHScopeStack::stable_iterator cleanup,
+                              mlir::Operation *dominatingIP);
+
   /// Push a cleanup to be run at the end of the current full-expression.  Safe
   /// against the possibility that we're currently inside a
   /// conditionally-evaluated expression.

diff  --git a/clang/lib/CIR/CodeGen/EHScopeStack.h 
b/clang/lib/CIR/CodeGen/EHScopeStack.h
index 9d614c858dbe1..09b78820a2587 100644
--- a/clang/lib/CIR/CodeGen/EHScopeStack.h
+++ b/clang/lib/CIR/CodeGen/EHScopeStack.h
@@ -187,6 +187,25 @@ class EHScopeStack {
     [[maybe_unused]] Cleanup *obj = new (buffer) T(a...);
   }
 
+  /// Push a cleanup with non-constant storage requirements on the
+  /// stack.  The cleanup type must provide an additional static method:
+  ///   static size_t getExtraSize(size_t);
+  /// The argument to this method will be the value N, which will also
+  /// be passed as the first argument to the constructor.
+  ///
+  /// The data stored in the extra storage must obey the same
+  /// restrictions as normal cleanup member data.
+  ///
+  /// The pointer returned from this method is valid until the cleanup
+  /// stack is modified.
+  template <class T, class... As>
+  T *pushCleanupWithExtra(CleanupKind kind, size_t n, As... a) {
+    static_assert(alignof(T) <= ScopeStackAlignment,
+                  "Cleanup's alignment is too large.");
+    void *buffer = pushCleanup(kind, sizeof(T) + T::getExtraSize(n));
+    return new (buffer) T(n, a...);
+  }
+
   void setCGF(CIRGenFunction *inCGF) { cgf = inCGF; }
 
   /// Pops a cleanup scope off the stack.  This is private to 
CIRGenCleanup.cpp.

diff  --git a/clang/test/CIR/CodeGen/new-delete.cpp 
b/clang/test/CIR/CodeGen/new-delete.cpp
new file mode 100644
index 0000000000000..58db8f8646f4c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/new-delete.cpp
@@ -0,0 +1,164 @@
+// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu 
-fclangir -emit-cir -std=c++98 -fcxx-exceptions -fexceptions -o %t.cir
+// RUN: FileCheck -check-prefixes=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu 
-fclangir -emit-llvm -std=c++98 -fcxx-exceptions -fexceptions -o %t-cir.ll
+// RUN: FileCheck -check-prefixes=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu 
-emit-llvm -std=c++98 -fcxx-exceptions -fexceptions -o %t.ll
+// RUN: FileCheck -check-prefixes=OGCG --input-file=%t.ll %s
+
+
+struct A { A(int); ~A(); void *p; };
+
+A *a() {
+  return new A(5);
+}
+
+// CIR: cir.func {{.*}} @_Z1av() -> !cir.ptr<!rec_A> {
+// CIR:   %[[RETVAL:.*]] = cir.alloca !cir.ptr<!rec_A>, 
!cir.ptr<!cir.ptr<!rec_A>>, ["__retval"]
+// CIR:   %[[NEW_RESULT:.*]] = cir.alloca !cir.ptr<!rec_A>, 
!cir.ptr<!cir.ptr<!rec_A>>, ["__new_result"]
+// CIR:   %[[ALLOC_SIZE:.*]] = cir.const #cir.int<8> : !u64i
+// CIR:   %[[PTR:.*]] = cir.call @_Znwm(%[[ALLOC_SIZE]])
+// CIR:   cir.cleanup.scope {
+// CIR:     %[[PTR_A:.*]] = cir.cast bitcast %[[PTR]] : !cir.ptr<!void> -> 
!cir.ptr<!rec_A>
+// CIR:     cir.store{{.*}} %[[PTR_A]], %[[NEW_RESULT]] : !cir.ptr<!rec_A>, 
!cir.ptr<!cir.ptr<!rec_A>>
+// CIR:     %[[FIVE:.*]] = cir.const #cir.int<5> : !s32i
+// CIR:     cir.call @_ZN1AC1Ei(%[[PTR_A]], %[[FIVE]])
+// CIR:     cir.yield
+// CIR:   } cleanup  eh {
+// CIR:     cir.call @_ZdlPv(%[[PTR]]) nothrow : (!cir.ptr<!void>) -> ()
+// CIR:     cir.yield
+// CIR:   }
+
+// LLVM: define {{.*}} ptr @_Z1av() {{.*}} personality ptr 
@__gxx_personality_v0 {
+// LLVM:   %[[RETVAL:.*]] = alloca ptr
+// LLVM:   %[[NEW_RESULT:.*]] = alloca ptr
+// LLVM:   %[[PTR:.*]] = call ptr @_Znwm(i64 8)
+// LLVM:   br label %[[EH_SCOPE:.*]]
+// LLVM: [[EH_SCOPE]]:
+// LLVM:   store ptr %[[PTR]], ptr %[[NEW_RESULT]]
+// LLVM:   invoke void @_ZN1AC1Ei(ptr %[[PTR]], i32 5)
+// LLVM:           to label %[[INVOKE_CONT:.*]] unwind label %[[UNWIND:.*]]
+// LLVM: [[INVOKE_CONT]]:
+// LLVM:   br label %[[EH_SCOPE_END:.*]]
+// LLVM: [[UNWIND]]:
+// LLVM:   %[[EXN:.*]] = landingpad { ptr, i32 }
+// LLVM:          cleanup
+// LLVM:   %[[EXN_PTR:.*]] = extractvalue { ptr, i32 } %[[EXN]], 0
+// LLVM:   %[[TYPEID:.*]] = extractvalue { ptr, i32 } %[[EXN]], 1
+// LLVM:   br label %[[EH_CLEANUP:.*]]
+// LLVM: [[EH_CLEANUP]]:
+// LLVM:   %[[EXN_PTR_PHI:.*]] = phi ptr [ %[[EXN_PTR]], %[[UNWIND]] ]
+// LLVM:   %[[TYPEID_PHI:.*]] = phi i32 [ %[[TYPEID]], %[[UNWIND]] ]
+// LLVM:   call void @_ZdlPv(ptr %[[PTR]])
+// LLVM:   %[[EXN_INSERT:.*]] = insertvalue { ptr, i32 } poison, ptr 
%[[EXN_PTR_PHI]], 0
+// LLVM:   %[[EXN_INSERT_2:.*]] = insertvalue { ptr, i32 } %[[EXN_INSERT]], 
i32 %[[TYPEID_PHI]], 1
+// LLVM:   resume { ptr, i32 } %[[EXN_INSERT_2]]
+// LLVM: [[EH_SCOPE_END]]:
+// LLVM:   %[[LOAD:.*]] = load ptr, ptr %[[NEW_RESULT]]
+// LLVM:   store ptr %[[LOAD]], ptr %[[RETVAL]]
+// LLVM:   %[[RET:.*]] = load ptr, ptr %[[RETVAL]]
+// LLVM:   ret ptr %[[RET]]
+
+// OGCG: define {{.*}} ptr @_Z1av() {{.*}} personality ptr 
@__gxx_personality_v0 {
+// OGCG:   %[[EXN_SLOT:.*]] = alloca ptr
+// OGCG:   %[[EHSELECTOR_SLOT:.*]] = alloca i32
+// OGCG:   %[[PTR:.*]] = call {{.*}} ptr @_Znwm(i64 8)
+// OGCG:   invoke void @_ZN1AC1Ei(ptr {{.*}} %[[PTR]], i32 5)
+// OGCG:           to label %[[INVOKE_CONT:.*]] unwind label %[[UNWIND:.*]]
+// OGCG: [[INVOKE_CONT]]:
+// OGCG:   ret ptr %[[PTR]]
+// OGCG: [[UNWIND]]:
+// OGCG:   %[[EXN:.*]] = landingpad { ptr, i32 }
+// OGCG:          cleanup
+// OGCG:   %[[EXN_PTR:.*]] = extractvalue { ptr, i32 } %[[EXN]], 0
+// OGCG:   store ptr %[[EXN_PTR]], ptr %[[EXN_SLOT]]
+// OGCG:   %[[TYPEID:.*]] = extractvalue { ptr, i32 } %[[EXN]], 1
+// OGCG:   store i32 %[[TYPEID]], ptr %[[EHSELECTOR_SLOT]]
+// OGCG:   call void @_ZdlPv(ptr %[[PTR]])
+// OGCG:   br label %[[EH_RESUME:.*]]
+// OGCG: [[EH_RESUME]]:
+// OGCG:   %[[EXN_PTR:.*]] = load ptr, ptr %[[EXN_SLOT]]
+// OGCG:   %[[EHSELECTOR:.*]] = load i32, ptr %[[EHSELECTOR_SLOT]]
+// OGCG:   %[[EXN_INSERT:.*]] = insertvalue { ptr, i32 } poison, ptr 
%[[EXN_PTR]], 0
+// OGCG:   %[[EXN_INSERT_2:.*]] = insertvalue { ptr, i32 } %[[EXN_INSERT]], 
i32 %[[EHSELECTOR]], 1
+// OGCG:   resume { ptr, i32 } %[[EXN_INSERT_2]]
+
+A *b() {
+  extern int foo();
+  return new A(foo());
+}
+
+// CIR: cir.func {{.*}} @_Z1bv() -> !cir.ptr<!rec_A> {
+// CIR:   %[[RETVAL:.*]] = cir.alloca !cir.ptr<!rec_A>, 
!cir.ptr<!cir.ptr<!rec_A>>, ["__retval"]
+// CIR:   %[[NEW_RESULT:.*]] = cir.alloca !cir.ptr<!rec_A>, 
!cir.ptr<!cir.ptr<!rec_A>>, ["__new_result"]
+// CIR:   %[[ALLOC_SIZE:.*]] = cir.const #cir.int<8> : !u64i
+// CIR:   %[[PTR:.*]] = cir.call @_Znwm(%[[ALLOC_SIZE]])
+// CIR:   cir.cleanup.scope {
+// CIR:     %[[PTR_A:.*]] = cir.cast bitcast %[[PTR]] : !cir.ptr<!void> -> 
!cir.ptr<!rec_A>
+// CIR:     cir.store{{.*}} %[[PTR_A]], %[[NEW_RESULT]] : !cir.ptr<!rec_A>, 
!cir.ptr<!cir.ptr<!rec_A>>
+// CIR:     %[[FOO:.*]] = cir.call @_Z3foov() : () -> !s32i
+// CIR:     cir.call @_ZN1AC1Ei(%[[PTR_A]], %[[FOO]])
+// CIR:     cir.yield
+// CIR:   } cleanup  eh {
+// CIR:     cir.call @_ZdlPv(%[[PTR]]) nothrow : (!cir.ptr<!void>) -> ()
+// CIR:     cir.yield
+// CIR:   }
+
+// LLVM: define {{.*}} ptr @_Z1bv() {{.*}} personality ptr 
@__gxx_personality_v0 {
+// LLVM:   %[[RETVAL:.*]] = alloca ptr
+// LLVM:   %[[NEW_RESULT:.*]] = alloca ptr
+// LLVM:   %[[PTR:.*]] = call ptr @_Znwm(i64 8)
+// LLVM:   br label %[[EH_SCOPE:.*]]
+// LLVM: [[EH_SCOPE]]:
+// LLVM:   store ptr %[[PTR]], ptr %[[NEW_RESULT]]
+// LLVM:   %[[FOO:.*]] = invoke i32 @_Z3foov()
+// LLVM:           to label %[[INVOKE_CONT:.*]] unwind label %[[UNWIND:.*]]
+// LLVM: [[INVOKE_CONT]]:
+// LLVM:   invoke void @_ZN1AC1Ei(ptr %[[PTR]], i32 %[[FOO]])
+// LLVM:           to label %[[INVOKE_CONT_2:.*]] unwind label %[[UNWIND:.*]]
+// LLVM: [[INVOKE_CONT_2]]:
+// LLVM:   br label %[[EH_SCOPE_END:.*]]
+// LLVM: [[UNWIND]]:
+// LLVM:   %[[EXN:.*]] = landingpad { ptr, i32 }
+// LLVM:          cleanup
+// LLVM:   %[[EXN_PTR:.*]] = extractvalue { ptr, i32 } %[[EXN]], 0
+// LLVM:   %[[TYPEID:.*]] = extractvalue { ptr, i32 } %[[EXN]], 1
+// LLVM:   br label %[[EH_CLEANUP:.*]]
+// LLVM: [[EH_CLEANUP]]:
+// LLVM:   %[[EXN_PTR_PHI:.*]] = phi ptr [ %[[EXN_PTR]], %[[UNWIND]] ]
+// LLVM:   %[[TYPEID_PHI:.*]] = phi i32 [ %[[TYPEID]], %[[UNWIND]] ]
+// LLVM:   call void @_ZdlPv(ptr %[[PTR]])
+// LLVM:   %[[EXN_INSERT:.*]] = insertvalue { ptr, i32 } poison, ptr 
%[[EXN_PTR_PHI]], 0
+// LLVM:   %[[EXN_INSERT_2:.*]] = insertvalue { ptr, i32 } %[[EXN_INSERT]], 
i32 %[[TYPEID_PHI]], 1
+// LLVM:   resume { ptr, i32 } %[[EXN_INSERT_2]]
+// LLVM: [[EH_SCOPE_END]]:
+// LLVM:   %[[LOAD:.*]] = load ptr, ptr %[[NEW_RESULT]]
+// LLVM:   store ptr %[[LOAD]], ptr %[[RETVAL]]
+// LLVM:   %[[RET:.*]] = load ptr, ptr %[[RETVAL]]
+// LLVM:   ret ptr %[[RET]]
+
+// OGCG: define {{.*}} ptr @_Z1bv() {{.*}} personality ptr 
@__gxx_personality_v0 {
+// OGCG:   %[[EXN_SLOT:.*]] = alloca ptr
+// OGCG:   %[[EHSELECTOR_SLOT:.*]] = alloca i32
+// OGCG:   %[[PTR:.*]] = call {{.*}} ptr @_Znwm(i64 8)
+// OGCG:   %[[FOO:.*]] = invoke i32 @_Z3foov()
+// OGCG:           to label %[[INVOKE_CONT:.*]] unwind label %[[UNWIND:.*]]
+// OGCG: [[INVOKE_CONT]]:
+// OGCG:   invoke void @_ZN1AC1Ei(ptr {{.*}} %[[PTR]], i32 %[[FOO]])
+// OGCG:           to label %[[INVOKE_CONT_2:.*]] unwind label %[[UNWIND:.*]]
+// OGCG: [[INVOKE_CONT_2]]:
+// OGCG:   ret ptr %[[PTR]]
+// OGCG: [[UNWIND]]:
+// OGCG:   %[[EXN:.*]] = landingpad { ptr, i32 }
+// OGCG:          cleanup
+// OGCG:   %[[EXN_PTR:.*]] = extractvalue { ptr, i32 } %[[EXN]], 0
+// OGCG:   store ptr %[[EXN_PTR]], ptr %[[EXN_SLOT]]
+// OGCG:   %[[TYPEID:.*]] = extractvalue { ptr, i32 } %[[EXN]], 1
+// OGCG:   store i32 %[[TYPEID]], ptr %[[EHSELECTOR_SLOT]]
+// OGCG:   call void @_ZdlPv(ptr %[[PTR]])
+// OGCG:   br label %[[EH_RESUME:.*]]
+// OGCG: [[EH_RESUME]]:
+// OGCG:   %[[EXN_PTR:.*]] = load ptr, ptr %[[EXN_SLOT]]
+// OGCG:   %[[EHSELECTOR:.*]] = load i32, ptr %[[EHSELECTOR_SLOT]]
+// OGCG:   %[[EXN_INSERT:.*]] = insertvalue { ptr, i32 } poison, ptr 
%[[EXN_PTR]], 0
+// OGCG:   %[[EXN_INSERT_2:.*]] = insertvalue { ptr, i32 } %[[EXN_INSERT]], 
i32 %[[EHSELECTOR]], 1
+// OGCG:   resume { ptr, i32 } %[[EXN_INSERT_2]]


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to