[Mlir-commits] [mlir] [mlir][SCF] Handle dropped operands in structural type conversion (PR #220473)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue Sep 1 20:55:42 PDT 2026
https://github.com/AkunCube created https://github.com/llvm/llvm-project/pull/220473
SCF structural type conversion assumes that `scf.for` bounds and `scf.if` conditions are converted to exactly one value. A 1:N conversion can drop or split these operands, causing `getSingleElement` to trigger an assertion.
Reject conversions with non-single-valued bounds or conditions and add regression coverage.
Fixes: https://github.com/llvm/llvm-project/issues/220459
>From 88dc721cd915fd99ce814218c1f7f05c21419cd6 Mon Sep 17 00:00:00 2001
From: cubehan3 <cubehan3 at gmail.com>
Date: Wed, 2 Sep 2026 03:51:36 +0000
Subject: [PATCH] [mlir][SCF] Handle dropped operands in structural type
conversion
---
.../Transforms/StructuralTypeConversions.cpp | 10 +++++++
.../test-legalize-type-conversion.mlir | 27 +++++++++++++++++++
mlir/test/lib/Dialect/Test/TestPatterns.cpp | 9 +++++++
3 files changed, 46 insertions(+)
diff --git a/mlir/lib/Dialect/SCF/Transforms/StructuralTypeConversions.cpp b/mlir/lib/Dialect/SCF/Transforms/StructuralTypeConversions.cpp
index 9468927021495..3816007771ebe 100644
--- a/mlir/lib/Dialect/SCF/Transforms/StructuralTypeConversions.cpp
+++ b/mlir/lib/Dialect/SCF/Transforms/StructuralTypeConversions.cpp
@@ -89,6 +89,14 @@ class ConvertForOpTypes
std::optional<ForOp> convertSourceOp(ForOp op, OneToNOpAdaptor adaptor,
ConversionPatternRewriter &rewriter,
TypeRange dstTypes) const {
+ // Loop bounds are single operands in the SCF IR. A 1:N conversion may
+ // produce zero or multiple values, in which case this operation cannot be
+ // reconstructed.
+ if (!llvm::hasSingleElement(adaptor.getLowerBound()) ||
+ !llvm::hasSingleElement(adaptor.getUpperBound()) ||
+ !llvm::hasSingleElement(adaptor.getStep()))
+ return std::nullopt;
+
// Create a empty new op and inline the regions from the old op.
//
// This is a little bit tricky. We have two concerns here:
@@ -141,6 +149,8 @@ class ConvertIfOpTypes
std::optional<IfOp> convertSourceOp(IfOp op, OneToNOpAdaptor adaptor,
ConversionPatternRewriter &rewriter,
TypeRange dstTypes) const {
+ if (!llvm::hasSingleElement(adaptor.getCondition()))
+ return std::nullopt;
IfOp newOp =
IfOp::create(rewriter, op.getLoc(), dstTypes,
diff --git a/mlir/test/Transforms/test-legalize-type-conversion.mlir b/mlir/test/Transforms/test-legalize-type-conversion.mlir
index 7a8bf06a3a060..352483520ff74 100644
--- a/mlir/test/Transforms/test-legalize-type-conversion.mlir
+++ b/mlir/test/Transforms/test-legalize-type-conversion.mlir
@@ -113,6 +113,33 @@ func.func @test_signature_conversion_no_converter() {
// -----
+// Do not assert when type conversion drops an SCF loop bound.
+func.func @test_scf_for_dropped_bound(%arg0: i32) {
+ // expected-error at +1 {{failed to legalize operation 'scf.for' that was explicitly marked illegal}}
+ %0 = scf.for %iv = %arg0 to %arg0 step %arg0 iter_args(%arg = %arg0) -> (i32) : i32 {
+ scf.yield %arg : i32
+ }
+ return
+}
+
+// -----
+
+// Do not assert when type conversion drops an SCF if condition.
+func.func @test_scf_if_dropped_condition() {
+ %cond = "test.context_op"() {drop_i1} : () -> i1
+ // expected-error at +1 {{failed to legalize operation 'scf.if' that was explicitly marked illegal}}
+ %result = scf.if %cond -> (i32) {
+ %value = "test.context_op"() : () -> i32
+ scf.yield %value : i32
+ } else {
+ %value = "test.context_op"() : () -> i32
+ scf.yield %value : i32
+ }
+ return
+}
+
+// -----
+
// CHECK-LABEL: @recursive_type_conversion
func.func @recursive_type_conversion() {
// CHECK: !test.test_rec<outer_converted_type, smpla>
diff --git a/mlir/test/lib/Dialect/Test/TestPatterns.cpp b/mlir/test/lib/Dialect/Test/TestPatterns.cpp
index fbc5ab76d9ba1..06517910e270e 100644
--- a/mlir/test/lib/Dialect/Test/TestPatterns.cpp
+++ b/mlir/test/lib/Dialect/Test/TestPatterns.cpp
@@ -2213,6 +2213,15 @@ struct TestTypeConversionDriver
return IntegerType::get(v.getContext(),
intType.getWidth() + incrementAttr.getInt());
});
+ converter.addConversion(
+ [](Value v, SmallVectorImpl<Type> &) -> std::optional<LogicalResult> {
+ // Test dropping an i1 value used as an SCF condition.
+ Operation *definingOp = v.getDefiningOp();
+ if (!definingOp || !v.getType().isInteger(1) ||
+ !definingOp->hasAttr("drop_i1"))
+ return std::nullopt;
+ return success();
+ });
/// Add the legal set of type materializations.
converter.addSourceMaterialization([](OpBuilder &builder, Type resultType,
More information about the Mlir-commits
mailing list