[Mlir-commits] [mlir] [mlir][EmitC] Convert MemRef::DeallocOp (PR #194591)

ioana ghiban llvmlistbot at llvm.org
Thu Jun 4 01:55:46 PDT 2026


https://github.com/ioghiban updated https://github.com/llvm/llvm-project/pull/194591

>From c4617cfbbab60caa38435158a9b0048b077f0bc5 Mon Sep 17 00:00:00 2001
From: Ioana Ghiban <ioana.ghiban at arm.com>
Date: Tue, 28 Apr 2026 12:15:28 +0200
Subject: [PATCH 1/5] [mlir][EmitC] Convert MemRef::DeallocOp

Assisted-by: Codex (refine implementation + tests). I reviewed all code and tests before submission.
---
 .../Conversion/MemRefToEmitC/MemRefToEmitC.h  |  1 +
 .../MemRefToEmitC/MemRefToEmitC.cpp           | 34 +++++++++++++++++--
 .../MemRefToEmitC/MemRefToEmitCPass.cpp       |  3 +-
 3 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/mlir/include/mlir/Conversion/MemRefToEmitC/MemRefToEmitC.h b/mlir/include/mlir/Conversion/MemRefToEmitC/MemRefToEmitC.h
index 5abfb3d7e72dd..64c9ce091dcd0 100644
--- a/mlir/include/mlir/Conversion/MemRefToEmitC/MemRefToEmitC.h
+++ b/mlir/include/mlir/Conversion/MemRefToEmitC/MemRefToEmitC.h
@@ -10,6 +10,7 @@
 
 constexpr const char *alignedAllocFunctionName = "aligned_alloc";
 constexpr const char *mallocFunctionName = "malloc";
+constexpr const char *freeFunctionName = "free";
 constexpr const char *memcpyFunctionName = "memcpy";
 constexpr const char *cppStandardLibraryHeader = "cstdlib";
 constexpr const char *cStandardLibraryHeader = "stdlib.h";
diff --git a/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp b/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp
index b8924c63adf09..1b249fe18049a 100644
--- a/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp
+++ b/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp
@@ -237,6 +237,36 @@ struct ConvertAlloc final : public OpConversionPattern<memref::AllocOp> {
   }
 };
 
+struct ConvertDealloc final : public OpConversionPattern<memref::DeallocOp> {
+  using OpConversionPattern::OpConversionPattern;
+
+  LogicalResult
+  matchAndRewrite(memref::DeallocOp deallocOp, OpAdaptor operands,
+                  ConversionPatternRewriter &rewriter) const override {
+    Location loc = deallocOp.getLoc();
+    Value strippedPtr = stripPointerUnrealizedCast(operands.getMemref());
+    if (!strippedPtr) {
+      return rewriter.notifyMatchFailure(
+          loc, "expected pointer-backed memref for EmitC deallocation");
+    }
+
+    // `memref.alloc` lowers supported memrefs to one contiguous heap buffer
+    // (`malloc`/`aligned_alloc`) and load/store lowerings linearize all
+    // multi-dimensional indexing separately. Deallocation therefore only needs
+    // the original base pointer cast to `void *` for `free`; there is no
+    // pointer-of-pointer structure, shape walk, or size recomputation here.
+    Type opaqueVoidPtrType = emitc::PointerType::get(
+        emitc::OpaqueType::get(rewriter.getContext(), "void"));
+    Value freeArg =
+        emitc::CastOp::create(rewriter, loc, opaqueVoidPtrType, strippedPtr);
+    emitc::CallOpaqueOp freeCall = emitc::CallOpaqueOp::create(
+        rewriter, loc, TypeRange{}, rewriter.getStringAttr(freeFunctionName),
+        ValueRange{freeArg});
+    rewriter.replaceOp(deallocOp, freeCall.getResults());
+    return success();
+  }
+};
+
 struct ConvertCopy final : public OpConversionPattern<memref::CopyOp> {
   using OpConversionPattern::OpConversionPattern;
 
@@ -472,7 +502,7 @@ void mlir::populateMemRefToEmitCTypeConversion(TypeConverter &typeConverter) {
 
 void mlir::populateMemRefToEmitCConversionPatterns(
     RewritePatternSet &patterns, const TypeConverter &converter) {
-  patterns.add<ConvertAlloca, ConvertAlloc, ConvertCopy, ConvertGlobal,
-               ConvertGetGlobal, ConvertLoad, ConvertStore>(
+  patterns.add<ConvertAlloca, ConvertAlloc, ConvertDealloc, ConvertCopy,
+               ConvertGlobal, ConvertGetGlobal, ConvertLoad, ConvertStore>(
       converter, patterns.getContext());
 }
diff --git a/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitCPass.cpp b/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitCPass.cpp
index a073a9acf752f..10e46a5fd7c63 100644
--- a/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitCPass.cpp
+++ b/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitCPass.cpp
@@ -76,7 +76,8 @@ struct ConvertMemRefToEmitCPass
     module.walk([&](mlir::emitc::CallOpaqueOp callOp) {
       StringRef expectedHeader;
       if (callOp.getCallee() == alignedAllocFunctionName ||
-          callOp.getCallee() == mallocFunctionName)
+          callOp.getCallee() == mallocFunctionName ||
+          callOp.getCallee() == freeFunctionName)
         expectedHeader = options.lowerToCpp ? cppStandardLibraryHeader
                                             : cStandardLibraryHeader;
       else if (callOp.getCallee() == memcpyFunctionName)

>From 6b049ad91dfc786800c9f8bfd9622c327924dc6d Mon Sep 17 00:00:00 2001
From: Ioana Ghiban <ioana.ghiban at arm.com>
Date: Mon, 18 May 2026 13:05:21 +0200
Subject: [PATCH 2/5] Address first round of comments

---
 .../MemRefToEmitC/MemRefToEmitC.cpp           |  12 +-
 .../MemRefToEmitC/MemRefToEmitCPass.cpp       |   4 +-
 .../MemRefToEmitC/memref-to-emitc-alloc.mlir  | 110 ++++++++++++++++++
 .../MemRefToEmitC/memref-to-emitc-failed.mlir |   8 ++
 4 files changed, 126 insertions(+), 8 deletions(-)
 create mode 100644 mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc.mlir

diff --git a/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp b/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp
index 1b249fe18049a..32b22a36ced56 100644
--- a/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp
+++ b/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp
@@ -244,17 +244,17 @@ struct ConvertDealloc final : public OpConversionPattern<memref::DeallocOp> {
   matchAndRewrite(memref::DeallocOp deallocOp, OpAdaptor operands,
                   ConversionPatternRewriter &rewriter) const override {
     Location loc = deallocOp.getLoc();
+    // Deliberately narrow since EmitC lowering needs to recover an actual heap
+    // pointer value to pass to free().
     Value strippedPtr = stripPointerUnrealizedCast(operands.getMemref());
     if (!strippedPtr) {
       return rewriter.notifyMatchFailure(
           loc, "expected pointer-backed memref for EmitC deallocation");
     }
 
-    // `memref.alloc` lowers supported memrefs to one contiguous heap buffer
-    // (`malloc`/`aligned_alloc`) and load/store lowerings linearize all
-    // multi-dimensional indexing separately. Deallocation therefore only needs
-    // the original base pointer cast to `void *` for `free`; there is no
-    // pointer-of-pointer structure, shape walk, or size recomputation here.
+    // The allocation APIs used by this conversion return `void *`, and `free`
+    // expects that same pointer type. Deallocation therefore only needs the
+    // recovered base pointer cast back to `void *` before calling `free`.
     Type opaqueVoidPtrType = emitc::PointerType::get(
         emitc::OpaqueType::get(rewriter.getContext(), "void"));
     Value freeArg =
@@ -502,7 +502,7 @@ void mlir::populateMemRefToEmitCTypeConversion(TypeConverter &typeConverter) {
 
 void mlir::populateMemRefToEmitCConversionPatterns(
     RewritePatternSet &patterns, const TypeConverter &converter) {
-  patterns.add<ConvertAlloca, ConvertAlloc, ConvertDealloc, ConvertCopy,
+  patterns.add<ConvertAlloca, ConvertAlloc, ConvertCopy, ConvertDealloc,
                ConvertGlobal, ConvertGetGlobal, ConvertLoad, ConvertStore>(
       converter, patterns.getContext());
 }
diff --git a/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitCPass.cpp b/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitCPass.cpp
index 10e46a5fd7c63..29974c213fcfb 100644
--- a/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitCPass.cpp
+++ b/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitCPass.cpp
@@ -76,8 +76,8 @@ struct ConvertMemRefToEmitCPass
     module.walk([&](mlir::emitc::CallOpaqueOp callOp) {
       StringRef expectedHeader;
       if (callOp.getCallee() == alignedAllocFunctionName ||
-          callOp.getCallee() == mallocFunctionName ||
-          callOp.getCallee() == freeFunctionName)
+          callOp.getCallee() == freeFunctionName ||
+          callOp.getCallee() == mallocFunctionName)
         expectedHeader = options.lowerToCpp ? cppStandardLibraryHeader
                                             : cStandardLibraryHeader;
       else if (callOp.getCallee() == memcpyFunctionName)
diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc.mlir
new file mode 100644
index 0000000000000..69f2bf8d55292
--- /dev/null
+++ b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc.mlir
@@ -0,0 +1,110 @@
+// RUN: mlir-opt -convert-memref-to-emitc="lower-to-cpp=true" %s -split-input-file | FileCheck %s --check-prefix=CPP
+// RUN: mlir-opt -convert-memref-to-emitc="lower-to-cpp=false" %s -split-input-file | FileCheck %s --check-prefix=NOCPP
+
+/// These tests are intentionally narrow and cover only heap-backed memrefs,
+/// deallocated via `free`: `memref.alloc` results and their pointer-backed
+/// form. `memref.alloca` is stack storage, and ordinary memref function
+/// arguments lower to arrays rather than owned heap pointers in this conversion.
+
+func.func @alloc_and_dealloc() {
+  %alloc = memref.alloc() : memref<999xi32>
+  return
+}
+
+// CPP:        emitc.include <"cstdlib">
+// CPP-LABEL:  alloc_and_dealloc
+// CPP:          %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
+// CPP:          %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
+// CPP:          %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
+// CPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
+// CPP:          %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
+// CPP-SAME:       : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// CPP:          %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
+// CPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// CPP:          %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
+// CPP-SAME:       : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
+// CPP:          emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// CPP:          return
+
+// NOCPP:        emitc.include <"stdlib.h">
+// NOCPP-LABEL:  alloc_and_dealloc
+// NOCPP:          %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
+// NOCPP:          %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
+// NOCPP:          %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
+// NOCPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
+// NOCPP:          %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
+// NOCPP-SAME:       : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP:          %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
+// NOCPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// NOCPP:          %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
+// NOCPP-SAME:       : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP:          emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// NOCPP:          return
+
+func.func @alloc_aligned() {
+  %alloc = memref.alloc() {alignment = 64 : i64} : memref<999xf32>
+  return
+}
+
+// CPP-LABEL: alloc_and_dealloc_aligned
+// CPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [f32]} : () -> !emitc.size_t 
+// CPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
+// CPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
+// CPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
+// CPP:         %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
+// CPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "aligned_alloc"(%[[ALIGNMENT]], %[[ALLOC_TOTAL_SIZE]])
+// CPP-SAME:       : (!emitc.size_t, !emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// CPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
+// CPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<f32>
+// CPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
+// CPP-SAME:       : !emitc.ptr<f32> to !emitc.ptr<!emitc.opaque<"void">>
+// CPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// CPP:         return
+
+// NOCPP-LABEL: alloc_and_dealloc_aligned
+// NOCPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [f32]} : () -> !emitc.size_t 
+// NOCPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
+// NOCPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
+// NOCPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
+// NOCPP:         %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
+// NOCPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "aligned_alloc"(%[[ALIGNMENT]], %[[ALLOC_TOTAL_SIZE]])
+// NOCPP-SAME:      : (!emitc.size_t, !emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
+// NOCPP-SAME:      : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<f32>
+// NOCPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
+// NOCPP-SAME:      : !emitc.ptr<f32> to !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// NOCPP:         return
+
+func.func @allocating_multi() {
+  %alloc_5 = memref.alloc() : memref<7x999xi32>
+  return
+}
+
+// CPP-LABEL: allocating_and_deallocating_multi
+// CPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t 
+// CPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
+// CPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
+// CPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
+// CPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
+// CPP-SAME:       : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">
+// CPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
+// CPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// CPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
+// CPP-SAME:       : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
+// CPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// CPP:         return
+
+// NOCPP-LABEL: allocating_and_deallocating_multi
+// NOCPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t 
+// NOCPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
+// NOCPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
+// NOCPP-SAME:      : (!emitc.size_t, index) -> !emitc.size_t
+// NOCPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
+// NOCPP-SAME:      : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
+// NOCPP-SAME:      : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// NOCPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
+// NOCPP-SAME:      : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// NOCPP:         return
diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-failed.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-failed.mlir
index b6eccfc8f0050..a299a6a86c859 100644
--- a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-failed.mlir
+++ b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-failed.mlir
@@ -17,6 +17,14 @@ func.func @alloca_with_alignment() {
 
 // -----
 
+func.func @alloc_and_dealloc_arg(%arg0: memref<999xi32>) {
+  // expected-error at +1 {{failed to legalize operation 'memref.dealloc'}}
+  memref.dealloc %arg0 : memref<999xi32>
+  return
+}
+
+// -----
+
 func.func @non_identity_layout() {
   // expected-error at +1 {{failed to legalize operation 'memref.alloca'}}
   %0 = memref.alloca() : memref<4x3xf32, affine_map<(d0, d1) -> (d1, d0)>>

>From d72eb618f0792db919a79e8d284ec2d90af8124d Mon Sep 17 00:00:00 2001
From: Ioana Ghiban <ioana.ghiban at arm.com>
Date: Thu, 28 May 2026 17:14:35 +0200
Subject: [PATCH 3/5] Address second round of comments

---
 .../MemRefToEmitC/MemRefToEmitC.cpp           |   7 +-
 .../memref-to-emitc-alloc-dealloc.mlir        | 151 +++++++++++-------
 .../MemRefToEmitC/memref-to-emitc-alloc.mlir  | 110 -------------
 .../MemRefToEmitC/memref-to-emitc-failed.mlir |  31 ++++
 4 files changed, 131 insertions(+), 168 deletions(-)
 delete mode 100644 mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc.mlir

diff --git a/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp b/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp
index 32b22a36ced56..87144aac9f6f9 100644
--- a/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp
+++ b/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp
@@ -244,15 +244,16 @@ struct ConvertDealloc final : public OpConversionPattern<memref::DeallocOp> {
   matchAndRewrite(memref::DeallocOp deallocOp, OpAdaptor operands,
                   ConversionPatternRewriter &rewriter) const override {
     Location loc = deallocOp.getLoc();
-    // Deliberately narrow since EmitC lowering needs to recover an actual heap
-    // pointer value to pass to free().
+    // `free` can only be emitted when the dealloc operand is recoverable as an
+    // `emitc.ptr<T>`. In the current conversion, that happens via an
+    // unrealized_conversion_cast from the pointer-backed EmitC form.
     Value strippedPtr = stripPointerUnrealizedCast(operands.getMemref());
     if (!strippedPtr) {
       return rewriter.notifyMatchFailure(
           loc, "expected pointer-backed memref for EmitC deallocation");
     }
 
-    // The allocation APIs used by this conversion return `void *`, and `free`
+    // The allocation APIs used by MemRefToEmitC return `void *`, and `free`
     // expects that same pointer type. Deallocation therefore only needs the
     // recovered base pointer cast back to `void *` before calling `free`.
     Type opaqueVoidPtrType = emitc::PointerType::get(
diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir
index ca7f3fbe20eff..4fbfd199da2cd 100644
--- a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir
+++ b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir
@@ -1,72 +1,113 @@
-// RUN: mlir-opt -convert-memref-to-emitc="lower-to-cpp=true" %s -split-input-file | FileCheck %s --check-prefix=CPP
-// RUN: mlir-opt -convert-memref-to-emitc="lower-to-cpp=false" %s -split-input-file | FileCheck %s --check-prefix=NOCPP
+// RUN: mlir-opt -convert-memref-to-emitc="lower-to-cpp=true" %s | FileCheck %s --check-prefix=CPP
+// RUN: mlir-opt -convert-memref-to-emitc="lower-to-cpp=false" %s | FileCheck %s --check-prefix=NOCPP
 
-func.func @alloc() {
+/// Tests for converting `memref.alloc` and `memref.dealloc`.
+/// At the moment, `memref.dealloc` lowering only accepts the pointer-backed form
+/// produced by the current `memref.alloc` lowering, so alloc and dealloc tests
+/// are kept together.
+
+func.func @alloc_and_dealloc() {
   %alloc = memref.alloc() : memref<999xi32>
+  memref.dealloc %alloc : memref<999xi32>
   return
 }
 
-// CPP:      module {
-// CPP-NEXT:   emitc.include <"cstdlib">
-// CPP-LABEL:  alloc()
-// CPP-NEXT:   %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t 
-// CPP-NEXT:   %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
-// CPP-NEXT:   %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
-// CPP-NEXT:   %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// CPP-NEXT:   %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
-// CPP-NEXT:   return
+// CPP:        emitc.include <"cstdlib">
+// CPP-LABEL:  alloc_and_dealloc
+// CPP:          %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
+// CPP:          %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
+// CPP:          %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
+// CPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
+// CPP:          %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
+// CPP-SAME:       : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// CPP:          %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
+// CPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// CPP:          %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
+// CPP-SAME:       : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
+// CPP:          emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// CPP:          return
 
-// NOCPP:      module {
-// NOCPP-NEXT:   emitc.include <"stdlib.h">
-// NOCPP-LABEL: alloc()
-// NOCPP-NEXT:   %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t 
-// NOCPP-NEXT:   %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
-// NOCPP-NEXT:   %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
-// NOCPP-NEXT:   %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP-NEXT:   %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
-// NOCPP-NEXT:   return
+// NOCPP:        emitc.include <"stdlib.h">
+// NOCPP-LABEL:  alloc_and_dealloc
+// NOCPP:          %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
+// NOCPP:          %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
+// NOCPP:          %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
+// NOCPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
+// NOCPP:          %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
+// NOCPP-SAME:       : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP:          %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
+// NOCPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// NOCPP:          %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
+// NOCPP-SAME:       : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP:          emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// NOCPP:          return
 
-func.func @alloc_aligned() {
+func.func @alloc_and_dealloc_aligned() {
   %alloc = memref.alloc() {alignment = 64 : i64} : memref<999xf32>
+  memref.dealloc %alloc : memref<999xf32>
   return
 }
 
-// CPP-LABEL: alloc_aligned
-// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [f32]}> : () -> !emitc.size_t 
-// CPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
-// CPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
-// CPP-NEXT: %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
-// CPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "aligned_alloc"(%[[ALIGNMENT]], %[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t, !emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// CPP-NEXT: %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<f32>
-// CPP-NEXT: return
+// CPP-LABEL: alloc_and_dealloc_aligned
+// CPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [f32]} : () -> !emitc.size_t 
+// CPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
+// CPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
+// CPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
+// CPP:         %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
+// CPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "aligned_alloc"(%[[ALIGNMENT]], %[[ALLOC_TOTAL_SIZE]])
+// CPP-SAME:       : (!emitc.size_t, !emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// CPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
+// CPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<f32>
+// CPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
+// CPP-SAME:       : !emitc.ptr<f32> to !emitc.ptr<!emitc.opaque<"void">>
+// CPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// CPP:         return
 
-// NOCPP-LABEL: alloc_aligned
-// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [f32]}> : () -> !emitc.size_t 
-// NOCPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
-// NOCPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
-// NOCPP-NEXT: %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
-// NOCPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "aligned_alloc"(%[[ALIGNMENT]], %[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t, !emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP-NEXT: %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<f32>
-// NOCPP-NEXT: return
+// NOCPP-LABEL: alloc_and_dealloc_aligned
+// NOCPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [f32]} : () -> !emitc.size_t 
+// NOCPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
+// NOCPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
+// NOCPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
+// NOCPP:         %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
+// NOCPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "aligned_alloc"(%[[ALIGNMENT]], %[[ALLOC_TOTAL_SIZE]])
+// NOCPP-SAME:      : (!emitc.size_t, !emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
+// NOCPP-SAME:      : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<f32>
+// NOCPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
+// NOCPP-SAME:      : !emitc.ptr<f32> to !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// NOCPP:         return
 
-func.func @allocating_multi() {
-  %alloc_5 = memref.alloc() : memref<7x999xi32>
+func.func @allocating_and_deallocating_multi() {
+  %alloc = memref.alloc() : memref<7x999xi32>
+  memref.dealloc %alloc : memref<7x999xi32>
   return
 }
 
-// CPP-LABEL: allocating_multi
-// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t 
-// CPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
-// CPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
-// CPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">
-// CPP-NEXT: %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
-// CPP-NEXT: return 
-
-// NOCPP-LABEL: allocating_multi
-// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t 
-// NOCPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
-// NOCPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
-// NOCPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP-NEXT: %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
-// NOCPP-NEXT: return 
+// CPP-LABEL: allocating_and_deallocating_multi
+// CPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t 
+// CPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
+// CPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
+// CPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
+// CPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
+// CPP-SAME:       : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">
+// CPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
+// CPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// CPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
+// CPP-SAME:       : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
+// CPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// CPP:         return
 
+// NOCPP-LABEL: allocating_and_deallocating_multi
+// NOCPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t 
+// NOCPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
+// NOCPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
+// NOCPP-SAME:      : (!emitc.size_t, index) -> !emitc.size_t
+// NOCPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
+// NOCPP-SAME:      : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
+// NOCPP-SAME:      : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// NOCPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
+// NOCPP-SAME:      : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// NOCPP:         return
diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc.mlir
deleted file mode 100644
index 69f2bf8d55292..0000000000000
--- a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc.mlir
+++ /dev/null
@@ -1,110 +0,0 @@
-// RUN: mlir-opt -convert-memref-to-emitc="lower-to-cpp=true" %s -split-input-file | FileCheck %s --check-prefix=CPP
-// RUN: mlir-opt -convert-memref-to-emitc="lower-to-cpp=false" %s -split-input-file | FileCheck %s --check-prefix=NOCPP
-
-/// These tests are intentionally narrow and cover only heap-backed memrefs,
-/// deallocated via `free`: `memref.alloc` results and their pointer-backed
-/// form. `memref.alloca` is stack storage, and ordinary memref function
-/// arguments lower to arrays rather than owned heap pointers in this conversion.
-
-func.func @alloc_and_dealloc() {
-  %alloc = memref.alloc() : memref<999xi32>
-  return
-}
-
-// CPP:        emitc.include <"cstdlib">
-// CPP-LABEL:  alloc_and_dealloc
-// CPP:          %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
-// CPP:          %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
-// CPP:          %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
-// CPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
-// CPP:          %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
-// CPP-SAME:       : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// CPP:          %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
-// CPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
-// CPP:          %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
-// CPP-SAME:       : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
-// CPP:          emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
-// CPP:          return
-
-// NOCPP:        emitc.include <"stdlib.h">
-// NOCPP-LABEL:  alloc_and_dealloc
-// NOCPP:          %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
-// NOCPP:          %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
-// NOCPP:          %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
-// NOCPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
-// NOCPP:          %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
-// NOCPP-SAME:       : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP:          %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
-// NOCPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
-// NOCPP:          %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
-// NOCPP-SAME:       : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP:          emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
-// NOCPP:          return
-
-func.func @alloc_aligned() {
-  %alloc = memref.alloc() {alignment = 64 : i64} : memref<999xf32>
-  return
-}
-
-// CPP-LABEL: alloc_and_dealloc_aligned
-// CPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [f32]} : () -> !emitc.size_t 
-// CPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
-// CPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
-// CPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
-// CPP:         %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
-// CPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "aligned_alloc"(%[[ALIGNMENT]], %[[ALLOC_TOTAL_SIZE]])
-// CPP-SAME:       : (!emitc.size_t, !emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// CPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
-// CPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<f32>
-// CPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
-// CPP-SAME:       : !emitc.ptr<f32> to !emitc.ptr<!emitc.opaque<"void">>
-// CPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
-// CPP:         return
-
-// NOCPP-LABEL: alloc_and_dealloc_aligned
-// NOCPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [f32]} : () -> !emitc.size_t 
-// NOCPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
-// NOCPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
-// NOCPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
-// NOCPP:         %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
-// NOCPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "aligned_alloc"(%[[ALIGNMENT]], %[[ALLOC_TOTAL_SIZE]])
-// NOCPP-SAME:      : (!emitc.size_t, !emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
-// NOCPP-SAME:      : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<f32>
-// NOCPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
-// NOCPP-SAME:      : !emitc.ptr<f32> to !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
-// NOCPP:         return
-
-func.func @allocating_multi() {
-  %alloc_5 = memref.alloc() : memref<7x999xi32>
-  return
-}
-
-// CPP-LABEL: allocating_and_deallocating_multi
-// CPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t 
-// CPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
-// CPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
-// CPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
-// CPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
-// CPP-SAME:       : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">
-// CPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
-// CPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
-// CPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
-// CPP-SAME:       : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
-// CPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
-// CPP:         return
-
-// NOCPP-LABEL: allocating_and_deallocating_multi
-// NOCPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t 
-// NOCPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
-// NOCPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
-// NOCPP-SAME:      : (!emitc.size_t, index) -> !emitc.size_t
-// NOCPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
-// NOCPP-SAME:      : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
-// NOCPP-SAME:      : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
-// NOCPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
-// NOCPP-SAME:      : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
-// NOCPP:         return
diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-failed.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-failed.mlir
index a299a6a86c859..f398de98ebf45 100644
--- a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-failed.mlir
+++ b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-failed.mlir
@@ -25,6 +25,37 @@ func.func @alloc_and_dealloc_arg(%arg0: memref<999xi32>) {
 
 // -----
 
+func.func @alloca_and_dealloc() {
+  %0 = memref.alloca() : memref<4xf32>
+  // expected-error at +1 {{failed to legalize operation 'memref.dealloc'}}
+  memref.dealloc %0 : memref<4xf32>
+  return
+}
+
+// -----
+
+memref.global "private" constant @g_dense : memref<4xf32> = dense<[0.0, 1.0, 2.0, 3.0]>
+
+func.func @get_global_dense_and_dealloc() {
+  %0 = memref.get_global @g_dense : memref<4xf32>
+  // expected-error at +1 {{failed to legalize operation 'memref.dealloc'}}
+  memref.dealloc %0 : memref<4xf32>
+  return
+}
+
+// -----
+
+memref.global "private" @g_uninit : memref<4xf32> = uninitialized
+
+func.func @get_global_uninit_and_dealloc() {
+  %0 = memref.get_global @g_uninit : memref<4xf32>
+  // expected-error at +1 {{failed to legalize operation 'memref.dealloc'}}
+  memref.dealloc %0 : memref<4xf32>
+  return
+}
+
+// -----
+
 func.func @non_identity_layout() {
   // expected-error at +1 {{failed to legalize operation 'memref.alloca'}}
   %0 = memref.alloca() : memref<4x3xf32, affine_map<(d0, d1) -> (d1, d0)>>

>From a61210b8cc8e5e565967670501b03c903f2ed443 Mon Sep 17 00:00:00 2001
From: Ioana Ghiban <ioana.ghiban at arm.com>
Date: Tue, 2 Jun 2026 11:15:04 +0200
Subject: [PATCH 4/5] Address third round of comments

---
 .../memref-to-emitc-alloc-dealloc.mlir        | 134 ++++++++----------
 .../MemRefToEmitC/memref-to-emitc-failed.mlir |   8 ++
 2 files changed, 64 insertions(+), 78 deletions(-)

diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir
index 4fbfd199da2cd..f98e67dcb6428 100644
--- a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir
+++ b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir
@@ -12,35 +12,29 @@ func.func @alloc_and_dealloc() {
   return
 }
 
-// CPP:        emitc.include <"cstdlib">
-// CPP-LABEL:  alloc_and_dealloc
-// CPP:          %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
-// CPP:          %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
-// CPP:          %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
-// CPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
-// CPP:          %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
-// CPP-SAME:       : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// CPP:          %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
-// CPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
-// CPP:          %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
-// CPP-SAME:       : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
-// CPP:          emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
-// CPP:          return
+// CPP:      module {
+// CPP-NEXT:   emitc.include <"cstdlib">
+// CPP-LABEL:  alloc_and_dealloc()
+// CPP-NEXT:   %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
+// CPP-NEXT:   %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
+// CPP-NEXT:   %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
+// CPP-NEXT:   %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// CPP-NEXT:   %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// CPP-NEXT:   %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]] : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
+// CPP-NEXT:   emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// CPP-NEXT:   return
 
-// NOCPP:        emitc.include <"stdlib.h">
-// NOCPP-LABEL:  alloc_and_dealloc
-// NOCPP:          %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
-// NOCPP:          %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
-// NOCPP:          %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
-// NOCPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
-// NOCPP:          %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
-// NOCPP-SAME:       : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP:          %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
-// NOCPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
-// NOCPP:          %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
-// NOCPP-SAME:       : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP:          emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
-// NOCPP:          return
+// NOCPP:      module {
+// NOCPP-NEXT:   emitc.include <"stdlib.h">
+// NOCPP-LABEL:  alloc_and_dealloc()
+// NOCPP-NEXT:   %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
+// NOCPP-NEXT:   %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
+// NOCPP-NEXT:   %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
+// NOCPP-NEXT:   %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP-NEXT:   %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// NOCPP-NEXT:   %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]] : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP-NEXT:   emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// NOCPP-NEXT:   return
 
 func.func @alloc_and_dealloc_aligned() {
   %alloc = memref.alloc() {alignment = 64 : i64} : memref<999xf32>
@@ -49,34 +43,26 @@ func.func @alloc_and_dealloc_aligned() {
 }
 
 // CPP-LABEL: alloc_and_dealloc_aligned
-// CPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [f32]} : () -> !emitc.size_t 
-// CPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
-// CPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
-// CPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
-// CPP:         %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
-// CPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "aligned_alloc"(%[[ALIGNMENT]], %[[ALLOC_TOTAL_SIZE]])
-// CPP-SAME:       : (!emitc.size_t, !emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// CPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
-// CPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<f32>
-// CPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
-// CPP-SAME:       : !emitc.ptr<f32> to !emitc.ptr<!emitc.opaque<"void">>
-// CPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
-// CPP:         return
+// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [f32]} : () -> !emitc.size_t 
+// CPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
+// CPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
+// CPP-NEXT: %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
+// CPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "aligned_alloc"(%[[ALIGNMENT]], %[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t, !emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// CPP-NEXT: %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<f32>
+// CPP-NEXT: %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]] : !emitc.ptr<f32> to !emitc.ptr<!emitc.opaque<"void">>
+// CPP-NEXT: emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// CPP-NEXT: return
 
 // NOCPP-LABEL: alloc_and_dealloc_aligned
-// NOCPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [f32]} : () -> !emitc.size_t 
-// NOCPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
-// NOCPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
-// NOCPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
-// NOCPP:         %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
-// NOCPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "aligned_alloc"(%[[ALIGNMENT]], %[[ALLOC_TOTAL_SIZE]])
-// NOCPP-SAME:      : (!emitc.size_t, !emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
-// NOCPP-SAME:      : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<f32>
-// NOCPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
-// NOCPP-SAME:      : !emitc.ptr<f32> to !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
-// NOCPP:         return
+// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [f32]} : () -> !emitc.size_t 
+// NOCPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
+// NOCPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
+// NOCPP-NEXT: %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
+// NOCPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "aligned_alloc"(%[[ALIGNMENT]], %[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t, !emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP-NEXT: %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<f32>
+// NOCPP-NEXT: %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]] : !emitc.ptr<f32> to !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP-NEXT: emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// NOCPP-NEXT: return
 
 func.func @allocating_and_deallocating_multi() {
   %alloc = memref.alloc() : memref<7x999xi32>
@@ -85,29 +71,21 @@ func.func @allocating_and_deallocating_multi() {
 }
 
 // CPP-LABEL: allocating_and_deallocating_multi
-// CPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t 
-// CPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
-// CPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
-// CPP-SAME:       : (!emitc.size_t, index) -> !emitc.size_t
-// CPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
-// CPP-SAME:       : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">
-// CPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
-// CPP-SAME:       : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
-// CPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
-// CPP-SAME:       : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
-// CPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
-// CPP:         return
+// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t 
+// CPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
+// CPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
+// CPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">
+// CPP-NEXT: %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// CPP-NEXT: %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]] : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
+// CPP-NEXT: emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// CPP-NEXT: return
 
 // NOCPP-LABEL: allocating_and_deallocating_multi
-// NOCPP:         %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t 
-// NOCPP:         %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
-// NOCPP:         %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]]
-// NOCPP-SAME:      : (!emitc.size_t, index) -> !emitc.size_t
-// NOCPP:         %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]])
-// NOCPP-SAME:      : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP:         %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]]
-// NOCPP-SAME:      : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
-// NOCPP:         %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]]
-// NOCPP-SAME:      : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
-// NOCPP:         emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
-// NOCPP:         return
+// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t 
+// NOCPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
+// NOCPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
+// NOCPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP-NEXT: %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// NOCPP-NEXT: %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]] : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
+// NOCPP-NEXT: emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
+// NOCPP-NEXT: return
diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-failed.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-failed.mlir
index f398de98ebf45..753f749fabbd6 100644
--- a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-failed.mlir
+++ b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-failed.mlir
@@ -17,6 +17,9 @@ func.func @alloca_with_alignment() {
 
 // -----
 
+// Unsupported: memref.dealloc of a function argument. Function arguments do not
+// lower to the pointer-backed EmitC form expected by the current dealloc
+// lowering.
 func.func @alloc_and_dealloc_arg(%arg0: memref<999xi32>) {
   // expected-error at +1 {{failed to legalize operation 'memref.dealloc'}}
   memref.dealloc %arg0 : memref<999xi32>
@@ -25,6 +28,8 @@ func.func @alloc_and_dealloc_arg(%arg0: memref<999xi32>) {
 
 // -----
 
+// Unsupported: memref.dealloc of stack storage. memref.alloca does not lower to
+// a heap pointer that can be passed to free().
 func.func @alloca_and_dealloc() {
   %0 = memref.alloca() : memref<4xf32>
   // expected-error at +1 {{failed to legalize operation 'memref.dealloc'}}
@@ -36,6 +41,8 @@ func.func @alloca_and_dealloc() {
 
 memref.global "private" constant @g_dense : memref<4xf32> = dense<[0.0, 1.0, 2.0, 3.0]>
 
+// Unsupported: memref.dealloc of a dense-initialized global. memref.get_global
+// does not produce the pointer-backed EmitC form accepted by dealloc lowering.
 func.func @get_global_dense_and_dealloc() {
   %0 = memref.get_global @g_dense : memref<4xf32>
   // expected-error at +1 {{failed to legalize operation 'memref.dealloc'}}
@@ -47,6 +54,7 @@ func.func @get_global_dense_and_dealloc() {
 
 memref.global "private" @g_uninit : memref<4xf32> = uninitialized
 
+// Unsupported: memref.dealloc of an uninitialized global.
 func.func @get_global_uninit_and_dealloc() {
   %0 = memref.get_global @g_uninit : memref<4xf32>
   // expected-error at +1 {{failed to legalize operation 'memref.dealloc'}}

>From 793b41f980a8260b50706c6fffa1d72078c7edb7 Mon Sep 17 00:00:00 2001
From: Ioana Ghiban <ioana.ghiban at arm.com>
Date: Thu, 4 Jun 2026 10:33:02 +0200
Subject: [PATCH 5/5] Rebase

---
 .../MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir
index f98e67dcb6428..9d20667a6490c 100644
--- a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir
+++ b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir
@@ -15,7 +15,7 @@ func.func @alloc_and_dealloc() {
 // CPP:      module {
 // CPP-NEXT:   emitc.include <"cstdlib">
 // CPP-LABEL:  alloc_and_dealloc()
-// CPP-NEXT:   %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
+// CPP-NEXT:   %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t
 // CPP-NEXT:   %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
 // CPP-NEXT:   %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
 // CPP-NEXT:   %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
@@ -27,7 +27,7 @@ func.func @alloc_and_dealloc() {
 // NOCPP:      module {
 // NOCPP-NEXT:   emitc.include <"stdlib.h">
 // NOCPP-LABEL:  alloc_and_dealloc()
-// NOCPP-NEXT:   %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
+// NOCPP-NEXT:   %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t
 // NOCPP-NEXT:   %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
 // NOCPP-NEXT:   %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
 // NOCPP-NEXT:   %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
@@ -43,7 +43,7 @@ func.func @alloc_and_dealloc_aligned() {
 }
 
 // CPP-LABEL: alloc_and_dealloc_aligned
-// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [f32]} : () -> !emitc.size_t 
+// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [f32]}> : () -> !emitc.size_t
 // CPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
 // CPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
 // CPP-NEXT: %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
@@ -54,7 +54,7 @@ func.func @alloc_and_dealloc_aligned() {
 // CPP-NEXT: return
 
 // NOCPP-LABEL: alloc_and_dealloc_aligned
-// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [f32]} : () -> !emitc.size_t 
+// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [f32]}> : () -> !emitc.size_t
 // NOCPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index
 // NOCPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
 // NOCPP-NEXT: %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t 
@@ -71,7 +71,7 @@ func.func @allocating_and_deallocating_multi() {
 }
 
 // CPP-LABEL: allocating_and_deallocating_multi
-// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t 
+// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t
 // CPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
 // CPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
 // CPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">
@@ -81,7 +81,7 @@ func.func @allocating_and_deallocating_multi() {
 // CPP-NEXT: return
 
 // NOCPP-LABEL: allocating_and_deallocating_multi
-// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t 
+// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t
 // NOCPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index
 // NOCPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
 // NOCPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>



More information about the Mlir-commits mailing list