Skip to content

[MLIR][Test] Extend coverage of unrealized_conversion_cast operation #142152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

peymanbr
Copy link

@peymanbr peymanbr commented May 30, 2025

[MLIR][Test] Extend coverage of unrealized_conversion_cast operation

Adds a suite of IR tests for the unrealized_conversion_cast operation, covering a wide range of type conversion scenarios and edge cases:

  • Basic functionality
  • Identity casts (input and output types are the same)
  • Type system boundaries (e.g., shaped, tuple, and custom types)
  • Pass integration and reconciliation behavior
  • Location and attribute preservation
  • Structural and usage pattern stress (e.g., fan-out, deep chains)
  • Integration with control flow and region-based operations
  • Memory types and dimensionality boundaries (e.g., dynamic shapes, unranked tensors)
  • Canonicalization interaction (e.g., with constant folding, CSE, DCE)
  • Complex and nested type patterns
  • Edge cases and boundary conditions (e.g., bitwidths, scalable vectors)

These tests help guard against regressions and provide a robust reference for dialect authors using unrealized_conversion_cast in type conversion scenarios across dialect boundaries.

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added the mlir label May 30, 2025
@llvmbot
Copy link
Member

llvmbot commented May 30, 2025

@llvm/pr-subscribers-mlir

Author: Peyman B (peymanbr)

Changes

Adds a suite of IR tests for the unrealized_conversion_cast operation, covering a wide range of type conversion scenarios and edge cases:

  • Basic functionality
  • Identity casts (input and output types are the same)
  • Type system boundaries (e.g., shaped, tuple, and custom types)
  • Pass integration and reconciliation behavior
  • Location and attribute preservation
  • Structural and usage pattern stress (e.g., fan-out, deep chains)
  • Integration with control flow and region-based operations
  • Memory types and dimensionality boundaries (e.g., dynamic shapes, unranked tensors)
  • Canonicalization interaction (e.g., with constant folding, CSE, DCE)
  • Complex and nested type patterns
  • Edge cases and boundary conditions (e.g., bitwidths, scalable vectors)

These tests help guard against regressions and provide a robust reference for dialect authors using unrealized_conversion_cast in type conversion scenarios across dialect boundaries.


Patch is 26.26 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/142152.diff

1 Files Affected:

  • (added) mlir/test/IR/unrealized-conversion-cast.mlir (+677)
diff --git a/mlir/test/IR/unrealized-conversion-cast.mlir b/mlir/test/IR/unrealized-conversion-cast.mlir
new file mode 100644
index 0000000000000..8df24652bb9a4
--- /dev/null
+++ b/mlir/test/IR/unrealized-conversion-cast.mlir
@@ -0,0 +1,677 @@
+// RUN: mlir-opt %s -verify-diagnostics -split-input-file \
+// RUN:   -allow-unregistered-dialect
+// RUN: mlir-opt %s -reconcile-unrealized-casts -split-input-file \
+// RUN:   -allow-unregistered-dialect | FileCheck %s --check-prefix=RECONCILE
+// RUN: mlir-opt %s -canonicalize -reconcile-unrealized-casts \
+// RUN:   -split-input-file -allow-unregistered-dialect | FileCheck %s \
+// RUN:   --check-prefix=CANON
+
+//===----------------------------------------------------------------------===//
+// Basic functionality tests
+//===----------------------------------------------------------------------===//
+
+module {
+  // CHECK-LABEL: func @cast_basic
+  func.func @cast_basic(%arg0: i32) -> f32 {
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}} : i32 to f32
+    %cast = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+    return %cast : f32
+  }
+
+  // CHECK-LABEL: func @cast_multiple_operands
+  func.func @cast_multiple_operands(%arg0: i32, %arg1: f64) -> (f32, i64) {
+    // CHECK: %{{.*}}:2 = builtin.unrealized_conversion_cast %{{.*}}, %{{.*}} :
+    // CHECK-SAME: i32, f64 to f32, i64
+    %cast:2 = builtin.unrealized_conversion_cast %arg0, %arg1 :
+              i32, f64 to f32, i64
+    return %cast#0, %cast#1 : f32, i64
+  }
+
+  // CHECK-LABEL: func @cast_generation
+  func.func @cast_generation() -> f32 {
+    %unit = arith.constant 0 : i32
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}} : i32 to f32
+    %cast = builtin.unrealized_conversion_cast %unit : i32 to f32
+    return %cast : f32
+  }
+
+  // CHECK-LABEL: func @cast_no_results
+  func.func @cast_no_results(%arg0: i32) {
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}} : i32 to i32
+    %cast = builtin.unrealized_conversion_cast %arg0 : i32 to i32
+    return
+  }
+
+  // CHECK-LABEL: func @cast_empty_valid
+  func.func @cast_empty_valid() {
+    %dummy = arith.constant 0 : i32
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}} : i32 to i32
+    %cast = builtin.unrealized_conversion_cast %dummy : i32 to i32
+    return
+  }
+
+  // CHECK-LABEL: func @cast_same_type
+  func.func @cast_same_type(%arg0: i32) -> i32 {
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}} : i32 to i32
+    %cast = builtin.unrealized_conversion_cast %arg0 : i32 to i32
+    return %cast : i32
+  }
+
+  // CHECK-LABEL: func @cast_chained
+  func.func @cast_chained(%arg0: i32) -> f64 {
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}} : i32 to f32
+    %cast1 = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}} : f32 to f64
+    %cast2 = builtin.unrealized_conversion_cast %cast1 : f32 to f64
+    return %cast2 : f64
+  }
+
+  // CHECK-LABEL: func @cast_n_to_m_valid
+  func.func @cast_n_to_m_valid(%arg0: i32, %arg1: f64) -> f32 {
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}}, %{{.*}} :
+    // CHECK-SAME: i32, f64 to f32
+    %cast = builtin.unrealized_conversion_cast %arg0, %arg1 :
+            i32, f64 to f32
+    return %cast : f32
+  }
+
+  // CHECK-LABEL: func @cast_1_to_n_valid
+  func.func @cast_1_to_n_valid(%arg0: i32) -> (f32, f64) {
+    // CHECK: %{{.*}}:2 = builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: i32 to f32, f64
+    %cast:2 = builtin.unrealized_conversion_cast %arg0 :
+              i32 to f32, f64
+    return %cast#0, %cast#1 : f32, f64
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// Identity casts (same input/output type)
+//===----------------------------------------------------------------------===//
+
+module {
+  // CHECK-LABEL: func @cast_same_type
+  func.func @cast_same_type() {
+    %dummy = arith.constant 0 : i32
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: i32 to i32
+    %cast1 = builtin.unrealized_conversion_cast %dummy : i32 to i32
+    %cast2 = builtin.unrealized_conversion_cast %dummy : i32 to i32
+    return
+  }
+
+  // CHECK-LABEL: func @cast_multiple_values
+  func.func @cast_multiple_values(%arg0: i32, %arg1: f32) -> (i32, f32) {
+    // CHECK: %{{.*}}:2 = builtin.unrealized_conversion_cast %{{.*}}, %{{.*}} :
+    // CHECK-SAME: i32, f32 to i32, f32
+    %cast:2 = builtin.unrealized_conversion_cast %arg0, %arg1 :
+              i32, f32 to i32, f32
+    return %cast#0, %cast#1 : i32, f32
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// Type system boundaries tests
+//===----------------------------------------------------------------------===//
+
+module {
+  // CHECK-LABEL: func @cast_primitive_to_aggregate
+  func.func @cast_primitive_to_aggregate(%arg0: i32) -> tensor<1xi32> {
+    // CHECK: builtin.unrealized_conversion_cast %{{.*}} : i32 to tensor<1xi32>
+    %cast = builtin.unrealized_conversion_cast %arg0 : i32 to tensor<1xi32>
+    return %cast : tensor<1xi32>
+  }
+
+  // CHECK-LABEL: func @cast_shaped_types
+  func.func @cast_shaped_types(%arg0: tensor<4x4xf32>) -> memref<4x4xf32> {
+    // CHECK: builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: tensor<4x4xf32> to memref<4x4xf32>
+    %cast = builtin.unrealized_conversion_cast %arg0 :
+            tensor<4x4xf32> to memref<4x4xf32>
+    return %cast : memref<4x4xf32>
+  }
+
+  // CHECK-LABEL: func @cast_complex_types
+  func.func @cast_complex_types(%arg0: tensor<4xi32>) -> memref<4xf32> {
+    // CHECK: builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: tensor<4xi32> to memref<4xf32>
+    %cast = builtin.unrealized_conversion_cast %arg0 :
+            tensor<4xi32> to memref<4xf32>
+    return %cast : memref<4xf32>
+  }
+
+  // CHECK-LABEL: func @cast_vector_types
+  func.func @cast_vector_types(%arg0: vector<4xi32>) -> vector<4xf32> {
+    // CHECK: builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: vector<4xi32> to vector<4xf32>
+    %cast = builtin.unrealized_conversion_cast %arg0 :
+            vector<4xi32> to vector<4xf32>
+    return %cast : vector<4xf32>
+  }
+
+  // CHECK-LABEL: func @cast_index_types
+  func.func @cast_index_types(%arg0: index) -> i64 {
+    // CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i64
+    %cast = builtin.unrealized_conversion_cast %arg0 : index to i64
+    return %cast : i64
+  }
+
+  // CHECK-LABEL: func @cast_tuple_types
+  func.func @cast_tuple_types(%arg0: tuple<i32, f32>) -> tuple<f64, i64> {
+    // CHECK: builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: tuple<i32, f32> to tuple<f64, i64>
+    %cast = builtin.unrealized_conversion_cast %arg0 :
+            tuple<i32, f32> to tuple<f64, i64>
+    return %cast : tuple<f64, i64>
+  }
+
+  // CHECK-LABEL: func @cast_complex_nested
+  func.func @cast_complex_nested(
+      %arg0: tuple<tensor<2xi32>, memref<4xf64>>)
+      -> tuple<memref<2xf32>, tensor<4xi64>> {
+    // CHECK: builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: tuple<tensor<2xi32>, memref<4xf64>> to
+    // CHECK-SAME: tuple<memref<2xf32>, tensor<4xi64>>
+    %cast = builtin.unrealized_conversion_cast %arg0 :
+            tuple<tensor<2xi32>, memref<4xf64>> to
+            tuple<memref<2xf32>, tensor<4xi64>>
+    return %cast : tuple<memref<2xf32>, tensor<4xi64>>
+  }
+
+  // CHECK-LABEL: func @cast_unregistered_dialects
+  func.func @cast_unregistered_dialects(
+      %arg0: !custom.type1<"param">) -> !other.type2 {
+    // CHECK: builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: !custom.type1<"param"> to !other.type2
+    %cast = builtin.unrealized_conversion_cast %arg0 :
+            !custom.type1<"param"> to !other.type2
+    return %cast : !other.type2
+  }
+
+  // CHECK-LABEL: func @cast_function_types
+  func.func @cast_function_types(
+      %arg0: !mydialect.func<i32 -> f32>)
+      -> !mydialect.func<f64 -> i64> {
+    // CHECK: builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: !mydialect.func<i32 -> f32> to !mydialect.func<f64 -> i64>
+    %cast = builtin.unrealized_conversion_cast %arg0 :
+            !mydialect.func<i32 -> f32> to !mydialect.func<f64 -> i64>
+    return %cast : !mydialect.func<f64 -> i64>
+  }
+
+  // CHECK-LABEL: func @cast_variadic_function_types
+  func.func @cast_variadic_function_types(
+      %arg0: !mydialect.func<(i32, ...) -> f32>)
+      -> !mydialect.func<(...) -> ()> {
+    // CHECK: builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: !mydialect.func<(i32, ...) -> f32>
+    // CHECK-SAME: to !mydialect.func<(...) -> ()>
+    %cast = builtin.unrealized_conversion_cast %arg0 :
+            !mydialect.func<(i32, ...) -> f32> to
+            !mydialect.func<(...) -> ()>
+    return %cast : !mydialect.func<(...) -> ()>
+  }
+
+  // CHECK-LABEL: func @cast_complex_element_types
+  func.func @cast_complex_element_types(
+      %arg0: complex<f32>) -> complex<f64> {
+    // CHECK: builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: complex<f32> to complex<f64>
+    %cast = builtin.unrealized_conversion_cast %arg0 :
+            complex<f32> to complex<f64>
+    return %cast : complex<f64>
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// Pass integration and reconciliation behavior tests
+//===----------------------------------------------------------------------===//
+
+module {
+  // RECONCILE-LABEL: func @cast_chain_elimination
+  func.func @cast_chain_elimination(%arg0: i32) -> i32 {
+    %cast1 = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+    %cast2 = builtin.unrealized_conversion_cast %cast1 : f32 to i32
+    // RECONCILE: return %arg0 : i32
+    return %cast2 : i32
+  }
+
+  // RECONCILE-LABEL: func @cast_partial_chain
+  func.func @cast_partial_chain(%arg0: i32) -> f64 {
+    %cast1 = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+    %cast2 = builtin.unrealized_conversion_cast %cast1 : f32 to f64
+    return %cast2 : f64
+  }
+
+  // RECONCILE-LABEL: func @cast_no_elimination
+  func.func @cast_no_elimination(%arg0: i32) -> f32 {
+    // RECONCILE: unrealized_conversion_cast
+    %cast = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+    return %cast : f32
+  }
+
+  // RECONCILE-LABEL: func @cast_multi_use_no_elimination
+  func.func @cast_multi_use_no_elimination(%arg0: i32) -> (f32, f32) {
+    // RECONCILE: unrealized_conversion_cast
+    %cast = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+    return %cast, %cast : f32, f32
+  }
+
+  // RECONCILE-LABEL: func @cast_complex_chain_elimination
+  func.func @cast_complex_chain_elimination(
+      %arg0: i32, %arg1: f32) -> (i32, f32) {
+    %cast1:2 = builtin.unrealized_conversion_cast
+               %arg0, %arg1 : i32, f32 to f64, i64
+    %cast2:2 = builtin.unrealized_conversion_cast
+               %cast1#0, %cast1#1 : f64, i64 to i32, f32
+    // RECONCILE: return %arg0, %arg1 : i32, f32
+    return %cast2#0, %cast2#1 : i32, f32
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// Location and attribute preservation tests
+//===----------------------------------------------------------------------===//
+
+module {
+  // CHECK-LABEL: func @cast_with_location
+  func.func @cast_with_location(%arg0: i32) -> f32 {
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: i32 to f32 loc("test_location")
+    %cast = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+            loc("test_location")
+    return %cast : f32
+  }
+
+  // CHECK-LABEL: func @cast_nested_locations
+  func.func @cast_nested_locations(%arg0: i32) -> f32 {
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: i32 to f32 loc(callsite("outer" at "inner"))
+    %cast = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+            loc(callsite("outer" at "inner"))
+    return %cast : f32
+  }
+
+  // CHECK-LABEL: func @cast_with_attributes
+  func.func @cast_with_attributes(%arg0: i32) -> f32 {
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: i32 to f32 {test_attr = "value"}
+    %cast = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+            {test_attr = "value"}
+    return %cast : f32
+  }
+
+  // CHECK-LABEL: func @cast_with_mixed_attributes
+  func.func @cast_with_mixed_attributes(%arg0: i32) -> f32 {
+    // CHECK: %{{.*}} = builtin.unrealized_conversion_cast %{{.*}} :
+    // CHECK-SAME: i32 to f32 {
+    // CHECK-SAME: flag = true, number = 42 : i64, name = "test"}
+    %cast = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+            {flag = true, number = 42 : i64, name = "test"}
+    return %cast : f32
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// Structural and usage pattern stress tests
+//===----------------------------------------------------------------------===//
+
+module {
+  // CHECK-LABEL: func @cast_many_operands
+  func.func @cast_many_operands(
+      %a: i32, %b: i32, %c: i32, %d: i32, %e: i32) ->
+      (f32, f32, f32, f32, f32) {
+    // CHECK: %{{.*}}:5 = builtin.unrealized_conversion_cast %{{.*}}, %{{.*}},
+    // CHECK-SAME: %{{.*}}, %{{.*}}, %{{.*}} : i32, i32, i32, i32, i32
+    // CHECK-SAME: to f32, f32, f32, f32, f32
+    %cast:5 = builtin.unrealized_conversion_cast
+              %a, %b, %c, %d, %e : i32, i32, i32, i32, i32
+              to f32, f32, f32, f32, f32
+    return %cast#0, %cast#1, %cast#2, %cast#3, %cast#4 :
+           f32, f32, f32, f32, f32
+  }
+
+  // CHECK-LABEL: func @cast_deep_chain
+  func.func @cast_deep_chain(%arg0: i32) -> i64 {
+    // CHECK: i32 to f32
+    %0 = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+    // CHECK: f32 to f64
+    %1 = builtin.unrealized_conversion_cast %0 : f32 to f64
+    // CHECK: f64 to i8
+    %2 = builtin.unrealized_conversion_cast %1 : f64 to i8
+    // CHECK: i8 to i16
+    %3 = builtin.unrealized_conversion_cast %2 : i8 to i16
+    // CHECK: i16 to i64
+    %4 = builtin.unrealized_conversion_cast %3 : i16 to i64
+    return %4 : i64
+  }
+
+  // CHECK-LABEL: func @cast_wide_fanout
+  func.func @cast_wide_fanout(%arg0: i32) ->
+      (f32, f32, f32, f32, f32, f32, f32, f32) {
+    // CHECK: i32 to f32
+    %cast = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+    return %cast, %cast, %cast, %cast,
+           %cast, %cast, %cast, %cast :
+           f32, f32, f32, f32, f32, f32, f32, f32
+  }
+
+  // CHECK-LABEL: func @cast_diamond_pattern
+  func.func @cast_diamond_pattern(%arg0: i32) -> f64 {
+    // CHECK: i32 to f32
+    %cast1 = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+    // CHECK: f32 to i64
+    %cast2 = builtin.unrealized_conversion_cast %cast1 : f32 to i64
+    // CHECK: f32 to i16
+    %cast3 = builtin.unrealized_conversion_cast %cast1 : f32 to i16
+    // CHECK: i64, i16 to f64
+    %cast4 = builtin.unrealized_conversion_cast
+             %cast2, %cast3 : i64, i16 to f64
+    return %cast4 : f64
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// Integration with control flow and region-based operations
+//===----------------------------------------------------------------------===//
+
+module {
+  // CHECK-LABEL: func @cast_in_control_flow
+  func.func @cast_in_control_flow(%cond: i1, %arg0: i32) -> f32 {
+    cf.cond_br %cond, ^bb1, ^bb2
+  ^bb1:
+    // CHECK: i32 to f32
+    %cast1 = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+    cf.br ^bb3(%cast1 : f32)
+  ^bb2:
+    // CHECK: i32 to f32
+    %cast2 = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+    cf.br ^bb3(%cast2 : f32)
+  ^bb3(%result: f32):
+    return %result : f32
+  }
+
+  // CHECK-LABEL: func @cast_with_regions
+  func.func @cast_with_regions(%arg0: i32) -> f32 {
+    %result = scf.execute_region -> f32 {
+      // CHECK: i32 to f32
+      %cast = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+      scf.yield %cast : f32
+    }
+    return %result : f32
+  }
+
+  // CHECK-LABEL: func @cast_in_loops
+  func.func @cast_in_loops(%arg0: i32, %ub: index) -> f32 {
+    %c0 = arith.constant 0 : index
+    %c1 = arith.constant 1 : index
+    // CHECK: i32 to f32
+    %init = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+    %result = scf.for %i = %c0 to %ub step %c1
+               iter_args(%iter = %init) -> (f32) {
+      // CHECK: f32 to i32
+      %temp = builtin.unrealized_conversion_cast %iter : f32 to i32
+      // CHECK: i32 to f32
+      %cast = builtin.unrealized_conversion_cast %temp : i32 to f32
+      scf.yield %cast : f32
+    }
+    return %result : f32
+  }
+
+  // CHECK-LABEL: func @cast_in_nested_regions
+  func.func @cast_in_nested_regions(%arg0: i32, %cond: i1) -> f32 {
+    %result = scf.execute_region -> f32 {
+      %inner_result = scf.if %cond -> f32 {
+        // CHECK: i32 to f32
+        %cast1 = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+        scf.yield %cast1 : f32
+      } else {
+        // CHECK: i32 to f32
+        %cast2 = builtin.unrealized_conversion_cast %arg0 : i32 to f32
+        scf.yield %cast2 : f32
+      }
+      scf.yield %inner_result : f32
+    }
+    return %result : f32
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// Memory types and dimensionality boundaries tests
+//===----------------------------------------------------------------------===//
+
+module {
+  // CHECK-LABEL: func @cast_large_types
+  func.func @cast_large_types(
+    %arg0: tensor<1024x1024x1024xf32>
+  ) -> memref<1024x1024x1024xf64> {
+    // CHECK: tensor<1024x1024x1024xf32> to memref<1024x1024x1024xf64>
+    %cast = builtin.unrealized_conversion_cast %arg0
+      : tensor<1024x1024x1024xf32> to memref<1024x1024x1024xf64>
+    return %cast : memref<1024x1024x1024xf64>
+  }
+
+  // CHECK-LABEL: func @cast_dynamic_shapes
+  func.func @cast_dynamic_shapes(
+    %arg0: tensor<?x?xf32>
+  ) -> memref<?x?xf64> {
+    // CHECK: tensor<?x?xf32> to memref<?x?xf64>
+    %cast = builtin.unrealized_conversion_cast %arg0
+      : tensor<?x?xf32> to memref<?x?xf64>
+    return %cast : memref<?x?xf64>
+  }
+
+  // CHECK-LABEL: func @cast_mixed_dynamic_static
+  func.func @cast_mixed_dynamic_static(
+    %arg0: tensor<?x4x?xf32>
+  ) -> memref<8x?x16xf64> {
+    // CHECK: tensor<?x4x?xf32> to memref<8x?x16xf64>
+    %cast = builtin.unrealized_conversion_cast %arg0
+      : tensor<?x4x?xf32> to memref<8x?x16xf64>
+    return %cast : memref<8x?x16xf64>
+  }
+
+  // CHECK-LABEL: func @cast_strided_memrefs
+  func.func @cast_strided_memrefs(
+    %arg0: memref<4x4xf32, strided<[4, 1]>>
+  ) -> memref<4x4xf64, strided<[8, 2]>> {
+    // CHECK: memref<4x4xf32, strided<[4, 1]>> to
+    // CHECK-SAME: memref<4x4xf64, strided<[8, 2]>>
+    %cast = builtin.unrealized_conversion_cast %arg0
+      : memref<4x4xf32, strided<[4, 1]>>
+        to memref<4x4xf64, strided<[8, 2]>>
+    return %cast : memref<4x4xf64, strided<[8, 2]>>
+  }
+
+  // CHECK-LABEL: func @cast_unranked_tensors
+  func.func @cast_unranked_tensors(
+    %arg0: tensor<*xf32>
+  ) -> tensor<*xf64> {
+    // CHECK: tensor<*xf32> to tensor<*xf64>
+    %cast = builtin.unrealized_conversion_cast %arg0
+      : tensor<*xf32> to tensor<*xf64>
+    return %cast : tensor<*xf64>
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// Canonicalization interaction tests
+//===----------------------------------------------------------------------===//
+
+module {
+  // CANON-LABEL: func @cast_with_canonicalization
+  func.func @cast_with_canonicalization(%arg0: i32) -> i32 {
+    %c1 = arith.constant 1 : i32
+    %add = arith.addi %arg0, %c1 : i32
+    %cast1 = builtin.unrealized_conversion_cast %add : i32 to f32
+    %cast2 = builtin.unrealized_conversion_cast %cast1 : f32 to i32
+    return %cast2 : i32
+  }
+
+  // CANON-LABEL: func @cast_constant_folding_interaction
+  func.func @cast_constant_folding_interaction() -> f64 {
+    %c0 = arith.constant 0 : i32
+    %c1 = arith.constant 1 : i32
+    %add = arith.addi %c0, %c1 : i32
+    %cast1 = builti...
[truncated]

@peymanbr peymanbr force-pushed the mlir-test-unrealized-cast branch from 126bad1 to 8163bf4 Compare May 30, 2025 16:08
Adds a suite of IR tests for the unrealized_conversion_cast operation,
covering a wide range of type conversion scenarios and edge cases:

- Basic functionality
- Identity casts (input and output types are the same)
- Type system boundaries (e.g., shaped, tuple, and custom types)
- Pass integration and reconciliation behavior
- Location and attribute preservation
- Structural, usage pattern and compilation-time stress (e.g.,
  fan-out, deep chains)
- Integration with control flow and region-based operations
- Memory types and dimensionality boundaries (e.g., dynamic shapes,
  unranked tensors)
- Canonicalization interaction (e.g., with constant folding, CSE, DCE)
- Complex and nested type patterns
- Edge cases and boundary conditions (e.g., bitwidths, scalable vectors)

These tests help guard against regressions and provide a robust
reference for dialect authors using unrealized_conversion_cast in
type conversion scenarios across dialect boundaries.
@peymanbr peymanbr force-pushed the mlir-test-unrealized-cast branch from 8163bf4 to c5a263b Compare May 30, 2025 19:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants