[Mlir-commits] [mlir] [mlir][Func][EmitC] Bail-out to avoid errors from MemRef array conversions (PR #198583)

ioana ghiban llvmlistbot at llvm.org
Wed May 27 07:35:36 PDT 2026


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

>From c652bf466cb223e4f404fb369384cbf60b5419c2 Mon Sep 17 00:00:00 2001
From: Ioana Ghiban <ioana.ghiban at arm.com>
Date: Tue, 19 May 2026 17:22:50 +0200
Subject: [PATCH 1/2] [mlir][Func][EmitC] Bail-out to avoid errors from MemRef
 array conversions

---
 .../Conversion/ArithToEmitC/ArithToEmitC.cpp  |   3 +
 .../Conversion/FuncToEmitC/FuncToEmitC.cpp    |  21 +++
 mlir/test/Dialect/EmitC/func/ops.mlir         | 139 ++++++++++++++++++
 3 files changed, 163 insertions(+)
 create mode 100644 mlir/test/Dialect/EmitC/func/ops.mlir

diff --git a/mlir/lib/Conversion/ArithToEmitC/ArithToEmitC.cpp b/mlir/lib/Conversion/ArithToEmitC/ArithToEmitC.cpp
index d003dc7a6dff3..0086a32603014 100644
--- a/mlir/lib/Conversion/ArithToEmitC/ArithToEmitC.cpp
+++ b/mlir/lib/Conversion/ArithToEmitC/ArithToEmitC.cpp
@@ -62,6 +62,9 @@ class ArithConstantOpConversionPattern
     Type newTy = this->getTypeConverter()->convertType(arithConst.getType());
     if (!newTy)
       return rewriter.notifyMatchFailure(arithConst, "type conversion failed");
+    if (isa<MemRefType>(arithConst.getType()))
+      return rewriter.notifyMatchFailure(
+          arithConst, "memref constants cannot be converted");
     rewriter.replaceOpWithNewOp<emitc::ConstantOp>(arithConst, newTy,
                                                    adaptor.getValue());
     return success();
diff --git a/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp b/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
index d2fb359c9aabe..7be90eb3d7b71 100644
--- a/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
+++ b/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
@@ -60,6 +60,18 @@ class CallOpConversion final : public OpConversionPattern<func::CallOp> {
       return rewriter.notifyMatchFailure(
           callOp, "only functions with zero or one result can be converted");
 
+    if (callOp.getNumResults() == 1) {
+      Type resultType =
+          getTypeConverter()->convertType(callOp.getResult(0).getType());
+      if (!resultType)
+        return rewriter.notifyMatchFailure(callOp,
+                                           "result type conversion failed");
+      if (isa<emitc::ArrayType>(resultType))
+        return rewriter.notifyMatchFailure(
+            callOp,
+            "converting functions returning arrays is not supported ATM");
+    }
+
     rewriter.replaceOpWithNewOp<emitc::CallOp>(callOp, callOp.getResultTypes(),
                                                adaptor.getOperands(),
                                                callOp->getAttrs());
@@ -97,6 +109,10 @@ class FuncOpConversion final : public OpConversionPattern<func::FuncOp> {
       if (!resultType)
         return rewriter.notifyMatchFailure(funcOp,
                                            "result type conversion failed");
+      if (isa<emitc::ArrayType>(resultType))
+        return rewriter.notifyMatchFailure(
+            funcOp,
+            "converting functions returning arrays is not supported ATM");
     }
 
     // Create the converted `emitc.func` op.
@@ -149,6 +165,11 @@ class ReturnOpConversion final : public OpConversionPattern<func::ReturnOp> {
     if (returnOp.getNumOperands() > 1)
       return rewriter.notifyMatchFailure(
           returnOp, "only zero or one operand is supported");
+    if (returnOp.getNumOperands() == 1 &&
+        isa<emitc::ArrayType>(adaptor.getOperands()[0].getType()))
+      return rewriter.notifyMatchFailure(
+          returnOp,
+          "converting functions returning arrays is not supported ATM");
 
     rewriter.replaceOpWithNewOp<emitc::ReturnOp>(
         returnOp,
diff --git a/mlir/test/Dialect/EmitC/func/ops.mlir b/mlir/test/Dialect/EmitC/func/ops.mlir
new file mode 100644
index 0000000000000..9b2ee83c6d6d7
--- /dev/null
+++ b/mlir/test/Dialect/EmitC/func/ops.mlir
@@ -0,0 +1,139 @@
+// RUN: mlir-opt -convert-to-emitc %s | FileCheck %s
+
+//===----------------------------------------------------------------------===//
+// Negative tests (must NOT rewrite)
+//===----------------------------------------------------------------------===//
+
+func.func private @negative_rank0_alloc() -> memref<i32> {
+  %alloc = memref.alloc() : memref<i32>
+  return %alloc : memref<i32>
+}
+// CHECK-LABEL:   func.func private @negative_rank0_alloc() -> memref<i32>
+// CHECK:           %[[ALLOC:.*]] = memref.alloc() : memref<i32>
+// CHECK:           return %[[ALLOC]] : memref<i32>
+// CHECK:         }
+
+func.func private @negative_rank0_arg(%arg0: memref<i32>) -> memref<i32> {
+  return %arg0 : memref<i32>
+}
+// CHECK-LABEL:   func.func private @negative_rank0_arg(
+// CHECK-SAME:      %[[SRC:.*]]: memref<i32>) -> memref<i32>
+// CHECK:           return %[[SRC]] : memref<i32>
+// CHECK:         }
+
+func.func private @negative_rank1_alloc() -> memref<1xi32> {
+  %alloc = memref.alloc() : memref<1xi32>
+  return %alloc : memref<1xi32>
+}
+// CHECK-LABEL:   func.func private @negative_rank1_alloc() -> memref<1xi32>
+// CHECK:           %[[SIZEOF_I32:.*]] = emitc.call_opaque "sizeof"() {args = [i32]} : () -> !emitc.size_t
+// CHECK:           %[[NUM_ELEMS:.*]] = "emitc.constant"() <{value = 1 : index}> : () -> index
+// CHECK:           %[[TOTAL_BYTES:.*]] = emitc.mul %[[SIZEOF_I32]], %[[NUM_ELEMS]] : (!emitc.size_t, index) -> !emitc.size_t
+// CHECK:           %[[MALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[TOTAL_BYTES]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
+// CHECK:           %[[ELEM_PTR:.*]] = emitc.cast %[[MALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+// CHECK:           %[[UNREALIZED_CONVERSION_ELEM_PTR:.*]] = builtin.unrealized_conversion_cast %[[ELEM_PTR]] : !emitc.ptr<i32> to memref<1xi32>
+// CHECK:           return %[[UNREALIZED_CONVERSION_ELEM_PTR]] : memref<1xi32>
+// CHECK:         }
+
+func.func private @negative_rank1_arg(%arg0: memref<1xi32>) -> memref<1xi32> {
+  return %arg0 : memref<1xi32>
+}
+// CHECK-LABEL:   func.func private @negative_rank1_arg(
+// CHECK-SAME:      %[[SRC:.*]]: memref<1xi32>) -> memref<1xi32>
+// CHECK:           return %[[SRC]] : memref<1xi32>
+// CHECK:         }
+
+func.func private @negative_rank2_arg(%arg0: memref<1x1xi32>) -> memref<1x1xi32> {
+  return %arg0 : memref<1x1xi32>
+}
+// CHECK-LABEL:   func.func private @negative_rank2_arg(
+// CHECK-SAME:      %[[SRC:.*]]: memref<1x1xi32>) -> memref<1x1xi32>
+// CHECK:           return %[[SRC]] : memref<1x1xi32>
+// CHECK:         }
+
+func.func private @negative_rank0_constant() -> memref<i64> {
+  %0 = arith.constant dense<-1> : memref<i64>
+  return %0 : memref<i64>
+}
+// CHECK-LABEL:   func.func private @negative_rank0_constant() -> memref<i64> {
+// CHECK:           %[[CONST:.*]] = arith.constant dense<-1> : memref<i64>
+// CHECK:           return %[[CONST]] : memref<i64>
+// CHECK:         }
+
+func.func private @negative_rank1_constant() -> memref<1xi64> {
+  %0 = arith.constant dense<[-1]> : memref<1xi64>
+  return %0 : memref<1xi64>
+}
+// CHECK-LABEL:   func.func private @negative_rank1_constant() -> memref<1xi64> {
+// CHECK:           %[[CONST:.*]] = arith.constant dense<-1> : memref<1xi64>
+// CHECK:           return %[[CONST]] : memref<1xi64>
+// CHECK:         }
+
+func.func private @multiple_elements(%arg0: memref<2xi64>) -> memref<2xi64> {
+  return %arg0 : memref<2xi64>
+}
+// CHECK-LABEL:   func.func private @multiple_elements(
+// CHECK-SAME:      %[[SRC:.*]]: memref<2xi64>) -> memref<2xi64>
+// CHECK:           return %[[SRC]] : memref<2xi64>
+// CHECK:         }
+
+func.func private @negative_multiple_returns(%arg0: memref<1xi32>) -> (memref<1xi32>, i32) {
+  %1 = arith.constant 7 : i32
+  return %arg0, %1 : memref<1xi32>, i32
+}
+// CHECK-LABEL:   func.func private @negative_multiple_returns(
+// CHECK-SAME:      %[[SRC:.*]]: memref<1xi32>) -> (memref<1xi32>, i32)
+// CHECK:           %[[C0:.*]] = "emitc.constant"() <{value = 7 : i32}> : () -> i32
+// CHECK:           return %[[SRC]], %[[C0]] : memref<1xi32>, i32
+// CHECK:         }
+
+func.func private @negative_dynamic_shape(%arg0: memref<?xi32>) -> memref<?xi32> {
+  return %arg0 : memref<?xi32>
+}
+// CHECK-LABEL:   func.func private @negative_dynamic_shape(
+// CHECK-SAME:      %[[SRC:.*]]: memref<?xi32>) -> memref<?xi32>
+// CHECK:           return %[[SRC]] : memref<?xi32>
+// CHECK:         }
+
+func.func private @negative_non_identity_layout(%arg0: memref<1x1xi32, strided<[2, 1], offset: 0>>)
+    -> memref<1x1xi32, strided<[2, 1], offset: 0>> {
+  return %arg0 : memref<1x1xi32, strided<[2, 1], offset: 0>>
+}
+// CHECK-LABEL:   func.func private @negative_non_identity_layout(
+// CHECK-SAME:      %[[SRC:.*]]: memref<1x1xi32, strided<[2, 1]>>) -> memref<1x1xi32, strided<[2, 1]>>
+// CHECK:           return %[[SRC]] : memref<1x1xi32, strided<[2, 1]>>
+// CHECK:         }
+
+func.func private @negative_unranked(%arg0: memref<*xi32>) -> memref<*xi32> {
+  return %arg0 : memref<*xi32>
+}
+// CHECK-LABEL:   func.func private @negative_unranked(
+// CHECK-SAME:      %[[SRC:.*]]: memref<*xi32>) -> memref<*xi32>
+// CHECK:           return %[[SRC]] : memref<*xi32>
+// CHECK:         }
+
+func.func @negative_public_function(%arg0: memref<1xi64>) -> memref<1xi64> {
+  return %arg0 : memref<1xi64>
+}
+// CHECK-LABEL:   func.func @negative_public_function(
+// CHECK-SAME:      %[[SRC:.*]]: memref<1xi64>) -> memref<1xi64>
+// CHECK:           return %[[SRC]] : memref<1xi64>
+// CHECK:         }
+
+func.func private @negative_callee(%arg0: memref<1xi64>) -> memref<1xi64> {
+  return %arg0 : memref<1xi64>
+}
+// CHECK-LABEL:   func.func private @negative_callee(
+// CHECK-SAME:      %[[SRC:.*]]: memref<1xi64>) -> memref<1xi64>
+// CHECK:           return %[[SRC]] : memref<1xi64>
+// CHECK:         }
+
+func.func private @negative_caller(%arg0: memref<1xi64>) -> memref<1xi64> {
+  %0 = call @negative_callee(%arg0) : (memref<1xi64>) -> memref<1xi64>
+  return %0 : memref<1xi64>
+}
+// CHECK-LABEL:   func.func private @negative_caller(
+// CHECK-SAME:      %[[SRC:.*]]: memref<1xi64>) -> memref<1xi64> {
+// CHECK:           %[[VAL:.*]] = call @negative_callee(%[[SRC]]) : (memref<1xi64>) -> memref<1xi64>
+// CHECK:           return %[[VAL]] : memref<1xi64>
+// CHECK:         }

>From bdd13fc268381e45cb8b35cc88ad5f2c235d1385 Mon Sep 17 00:00:00 2001
From: Ioana Ghiban <ioana.ghiban at arm.com>
Date: Wed, 27 May 2026 16:23:54 +0200
Subject: [PATCH 2/2] Address first round of comments

---
 .../Conversion/FuncToEmitC/FuncToEmitC.cpp    |  4 ++--
 mlir/test/Dialect/EmitC/func/ops.mlir         | 21 +++----------------
 2 files changed, 5 insertions(+), 20 deletions(-)

diff --git a/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp b/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
index 7be90eb3d7b71..67ca5b606d166 100644
--- a/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
+++ b/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
@@ -69,7 +69,7 @@ class CallOpConversion final : public OpConversionPattern<func::CallOp> {
       if (isa<emitc::ArrayType>(resultType))
         return rewriter.notifyMatchFailure(
             callOp,
-            "converting functions returning arrays is not supported ATM");
+            "converting function calls returning arrays is not supported ATM");
     }
 
     rewriter.replaceOpWithNewOp<emitc::CallOp>(callOp, callOp.getResultTypes(),
@@ -169,7 +169,7 @@ class ReturnOpConversion final : public OpConversionPattern<func::ReturnOp> {
         isa<emitc::ArrayType>(adaptor.getOperands()[0].getType()))
       return rewriter.notifyMatchFailure(
           returnOp,
-          "converting functions returning arrays is not supported ATM");
+          "converting returnOp is only supported for non-array values ATM");
 
     rewriter.replaceOpWithNewOp<emitc::ReturnOp>(
         returnOp,
diff --git a/mlir/test/Dialect/EmitC/func/ops.mlir b/mlir/test/Dialect/EmitC/func/ops.mlir
index 9b2ee83c6d6d7..6660e790e1a5f 100644
--- a/mlir/test/Dialect/EmitC/func/ops.mlir
+++ b/mlir/test/Dialect/EmitC/func/ops.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt -convert-to-emitc %s | FileCheck %s
+// RUN: mlir-opt -convert-to-emitc -verify-diagnostics %s | FileCheck %s
 
 //===----------------------------------------------------------------------===//
 // Negative tests (must NOT rewrite)
@@ -9,9 +9,8 @@ func.func private @negative_rank0_alloc() -> memref<i32> {
   return %alloc : memref<i32>
 }
 // CHECK-LABEL:   func.func private @negative_rank0_alloc() -> memref<i32>
-// CHECK:           %[[ALLOC:.*]] = memref.alloc() : memref<i32>
-// CHECK:           return %[[ALLOC]] : memref<i32>
-// CHECK:         }
+// CHECK:           %[[ALLOC:.*]] = memref.alloc()
+// CHECK:           return %[[ALLOC]]
 
 func.func private @negative_rank0_arg(%arg0: memref<i32>) -> memref<i32> {
   return %arg0 : memref<i32>
@@ -19,7 +18,6 @@ func.func private @negative_rank0_arg(%arg0: memref<i32>) -> memref<i32> {
 // CHECK-LABEL:   func.func private @negative_rank0_arg(
 // CHECK-SAME:      %[[SRC:.*]]: memref<i32>) -> memref<i32>
 // CHECK:           return %[[SRC]] : memref<i32>
-// CHECK:         }
 
 func.func private @negative_rank1_alloc() -> memref<1xi32> {
   %alloc = memref.alloc() : memref<1xi32>
@@ -33,7 +31,6 @@ func.func private @negative_rank1_alloc() -> memref<1xi32> {
 // CHECK:           %[[ELEM_PTR:.*]] = emitc.cast %[[MALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
 // CHECK:           %[[UNREALIZED_CONVERSION_ELEM_PTR:.*]] = builtin.unrealized_conversion_cast %[[ELEM_PTR]] : !emitc.ptr<i32> to memref<1xi32>
 // CHECK:           return %[[UNREALIZED_CONVERSION_ELEM_PTR]] : memref<1xi32>
-// CHECK:         }
 
 func.func private @negative_rank1_arg(%arg0: memref<1xi32>) -> memref<1xi32> {
   return %arg0 : memref<1xi32>
@@ -41,7 +38,6 @@ func.func private @negative_rank1_arg(%arg0: memref<1xi32>) -> memref<1xi32> {
 // CHECK-LABEL:   func.func private @negative_rank1_arg(
 // CHECK-SAME:      %[[SRC:.*]]: memref<1xi32>) -> memref<1xi32>
 // CHECK:           return %[[SRC]] : memref<1xi32>
-// CHECK:         }
 
 func.func private @negative_rank2_arg(%arg0: memref<1x1xi32>) -> memref<1x1xi32> {
   return %arg0 : memref<1x1xi32>
@@ -49,7 +45,6 @@ func.func private @negative_rank2_arg(%arg0: memref<1x1xi32>) -> memref<1x1xi32>
 // CHECK-LABEL:   func.func private @negative_rank2_arg(
 // CHECK-SAME:      %[[SRC:.*]]: memref<1x1xi32>) -> memref<1x1xi32>
 // CHECK:           return %[[SRC]] : memref<1x1xi32>
-// CHECK:         }
 
 func.func private @negative_rank0_constant() -> memref<i64> {
   %0 = arith.constant dense<-1> : memref<i64>
@@ -58,7 +53,6 @@ func.func private @negative_rank0_constant() -> memref<i64> {
 // CHECK-LABEL:   func.func private @negative_rank0_constant() -> memref<i64> {
 // CHECK:           %[[CONST:.*]] = arith.constant dense<-1> : memref<i64>
 // CHECK:           return %[[CONST]] : memref<i64>
-// CHECK:         }
 
 func.func private @negative_rank1_constant() -> memref<1xi64> {
   %0 = arith.constant dense<[-1]> : memref<1xi64>
@@ -67,7 +61,6 @@ func.func private @negative_rank1_constant() -> memref<1xi64> {
 // CHECK-LABEL:   func.func private @negative_rank1_constant() -> memref<1xi64> {
 // CHECK:           %[[CONST:.*]] = arith.constant dense<-1> : memref<1xi64>
 // CHECK:           return %[[CONST]] : memref<1xi64>
-// CHECK:         }
 
 func.func private @multiple_elements(%arg0: memref<2xi64>) -> memref<2xi64> {
   return %arg0 : memref<2xi64>
@@ -75,7 +68,6 @@ func.func private @multiple_elements(%arg0: memref<2xi64>) -> memref<2xi64> {
 // CHECK-LABEL:   func.func private @multiple_elements(
 // CHECK-SAME:      %[[SRC:.*]]: memref<2xi64>) -> memref<2xi64>
 // CHECK:           return %[[SRC]] : memref<2xi64>
-// CHECK:         }
 
 func.func private @negative_multiple_returns(%arg0: memref<1xi32>) -> (memref<1xi32>, i32) {
   %1 = arith.constant 7 : i32
@@ -85,7 +77,6 @@ func.func private @negative_multiple_returns(%arg0: memref<1xi32>) -> (memref<1x
 // CHECK-SAME:      %[[SRC:.*]]: memref<1xi32>) -> (memref<1xi32>, i32)
 // CHECK:           %[[C0:.*]] = "emitc.constant"() <{value = 7 : i32}> : () -> i32
 // CHECK:           return %[[SRC]], %[[C0]] : memref<1xi32>, i32
-// CHECK:         }
 
 func.func private @negative_dynamic_shape(%arg0: memref<?xi32>) -> memref<?xi32> {
   return %arg0 : memref<?xi32>
@@ -93,7 +84,6 @@ func.func private @negative_dynamic_shape(%arg0: memref<?xi32>) -> memref<?xi32>
 // CHECK-LABEL:   func.func private @negative_dynamic_shape(
 // CHECK-SAME:      %[[SRC:.*]]: memref<?xi32>) -> memref<?xi32>
 // CHECK:           return %[[SRC]] : memref<?xi32>
-// CHECK:         }
 
 func.func private @negative_non_identity_layout(%arg0: memref<1x1xi32, strided<[2, 1], offset: 0>>)
     -> memref<1x1xi32, strided<[2, 1], offset: 0>> {
@@ -102,7 +92,6 @@ func.func private @negative_non_identity_layout(%arg0: memref<1x1xi32, strided<[
 // CHECK-LABEL:   func.func private @negative_non_identity_layout(
 // CHECK-SAME:      %[[SRC:.*]]: memref<1x1xi32, strided<[2, 1]>>) -> memref<1x1xi32, strided<[2, 1]>>
 // CHECK:           return %[[SRC]] : memref<1x1xi32, strided<[2, 1]>>
-// CHECK:         }
 
 func.func private @negative_unranked(%arg0: memref<*xi32>) -> memref<*xi32> {
   return %arg0 : memref<*xi32>
@@ -110,7 +99,6 @@ func.func private @negative_unranked(%arg0: memref<*xi32>) -> memref<*xi32> {
 // CHECK-LABEL:   func.func private @negative_unranked(
 // CHECK-SAME:      %[[SRC:.*]]: memref<*xi32>) -> memref<*xi32>
 // CHECK:           return %[[SRC]] : memref<*xi32>
-// CHECK:         }
 
 func.func @negative_public_function(%arg0: memref<1xi64>) -> memref<1xi64> {
   return %arg0 : memref<1xi64>
@@ -118,7 +106,6 @@ func.func @negative_public_function(%arg0: memref<1xi64>) -> memref<1xi64> {
 // CHECK-LABEL:   func.func @negative_public_function(
 // CHECK-SAME:      %[[SRC:.*]]: memref<1xi64>) -> memref<1xi64>
 // CHECK:           return %[[SRC]] : memref<1xi64>
-// CHECK:         }
 
 func.func private @negative_callee(%arg0: memref<1xi64>) -> memref<1xi64> {
   return %arg0 : memref<1xi64>
@@ -126,7 +113,6 @@ func.func private @negative_callee(%arg0: memref<1xi64>) -> memref<1xi64> {
 // CHECK-LABEL:   func.func private @negative_callee(
 // CHECK-SAME:      %[[SRC:.*]]: memref<1xi64>) -> memref<1xi64>
 // CHECK:           return %[[SRC]] : memref<1xi64>
-// CHECK:         }
 
 func.func private @negative_caller(%arg0: memref<1xi64>) -> memref<1xi64> {
   %0 = call @negative_callee(%arg0) : (memref<1xi64>) -> memref<1xi64>
@@ -136,4 +122,3 @@ func.func private @negative_caller(%arg0: memref<1xi64>) -> memref<1xi64> {
 // CHECK-SAME:      %[[SRC:.*]]: memref<1xi64>) -> memref<1xi64> {
 // CHECK:           %[[VAL:.*]] = call @negative_callee(%[[SRC]]) : (memref<1xi64>) -> memref<1xi64>
 // CHECK:           return %[[VAL]] : memref<1xi64>
-// CHECK:         }



More information about the Mlir-commits mailing list