[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