This is an automated email from the ASF dual-hosted git repository.

tlopex pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new 1f7c99d3f4 [Relax][CoreML] Fix CoreML partition pass (#19711)
1f7c99d3f4 is described below

commit 1f7c99d3f45b33a99c06061702da0da911fc60ec
Author: Shushi Hong <[email protected]>
AuthorDate: Tue Jun 9 22:38:28 2026 -0400

    [Relax][CoreML] Fix CoreML partition pass (#19711)
    
    This fixes CoreML Relax partitioning after `FoldDataflowBlockOutput` was
    folded into `CanonicalizeBindings`.
    
    `partition_for_coreml` still called
    `relax.transform.FoldDataflowBlockOutput()`, but that pass is no longer
    exported. This caused CoreML partition tests to fail with:
    
    ```text
    AttributeError: module 'tvm.relax.transform' has no attribute 
'FoldDataflowBlockOutput'
    ```
    
    The old pass behavior is now covered by `CanonicalizeBindings`, so this
    updates CoreML partitioning to call `CanonicalizeBindings()` instead.
    
    The existing CoreML test file now also includes a partition-only
    regression test. The end-to-end CoreML tests remain guarded by the
    CoreML runtime requirements, while the partition test can run without
    coremltools, Xcode, or a CoreML runtime.
---
 python/tvm/relax/backend/metal/coreml.py  |  2 +-
 tests/python/relax/test_codegen_coreml.py | 44 ++++++++++++++++++++++++++++---
 2 files changed, 41 insertions(+), 5 deletions(-)

diff --git a/python/tvm/relax/backend/metal/coreml.py 
b/python/tvm/relax/backend/metal/coreml.py
index 7dd8ea3958..e719d2f1c7 100644
--- a/python/tvm/relax/backend/metal/coreml.py
+++ b/python/tvm/relax/backend/metal/coreml.py
@@ -169,7 +169,7 @@ def partition_for_coreml(mod):
     """
 
     patterns = get_patterns_with_prefix("coreml")
-    mod = transform.FoldDataflowBlockOutput()(mod)
+    mod = transform.CanonicalizeBindings()(mod)
     mod = transform.FuseOpsByPattern(patterns, bind_constants=True, 
annotate_codegen=False)(mod)
     mod = transform.MergeCompositeFunctions()(mod)
     return mod
diff --git a/tests/python/relax/test_codegen_coreml.py 
b/tests/python/relax/test_codegen_coreml.py
index e9b9bcb09c..2068044bbe 100644
--- a/tests/python/relax/test_codegen_coreml.py
+++ b/tests/python/relax/test_codegen_coreml.py
@@ -14,6 +14,8 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+import importlib.util
+
 import numpy as np
 import pytest
 
@@ -21,7 +23,6 @@ import tvm
 import tvm.testing
 from tvm import relax
 
-requires_coremltools = tvm.testing.requires_package("coremltools")
 target, dev = "llvm", tvm.cpu()
 
 
@@ -36,12 +37,35 @@ def _has_xcode():
     return False
 
 
-pytestmark = pytest.mark.skipif(
-    not (requires_coremltools and _has_xcode()),
+requires_coreml_runtime = pytest.mark.skipif(
+    not (importlib.util.find_spec("coremltools") and _has_xcode()),
     reason="coreml is not enabled.",
 )
 
 
+def test_partition_for_coreml_uses_current_relax_passes():
+    from tvm.relax.backend.metal.coreml import partition_for_coreml
+
+    x = relax.Var("x", relax.TensorStructInfo([10, 10], "float32"))
+    y = relax.Var("y", relax.TensorStructInfo([10, 10], "float32"))
+    bb = relax.BlockBuilder()
+    with bb.function("main", [x, y]):
+        with bb.dataflow():
+            lv0 = bb.emit(relax.op.add(x, y))
+            gv = bb.emit_output(lv0)
+        bb.emit_func_output(gv)
+
+    partitioned = partition_for_coreml(bb.get())
+
+    assert relax.analysis.well_formed(partitioned)
+    assert any(
+        getattr(func, "attrs", None) is not None
+        and "Codegen" in func.attrs
+        and str(func.attrs["Codegen"]) == "coreml"
+        for func in partitioned.functions.values()
+    )
+
+
 def verify(mod, inputs):
     from tvm.relax.backend.metal.coreml import partition_for_coreml
 
@@ -65,6 +89,7 @@ def verify(mod, inputs):
     tvm.testing.assert_allclose(out1.numpy(), out2.numpy(), rtol=1e-3, 
atol=1e-3)
 
 
+@requires_coreml_runtime
 def test_add():
     x = relax.Var("x", relax.TensorStructInfo([10, 10], "float32"))
     y = relax.Var("y", relax.TensorStructInfo([10, 10], "float32"))
@@ -80,6 +105,7 @@ def test_add():
     verify(mod, [x_data, y_data])
 
 
+@requires_coreml_runtime
 def test_add_const():
     x = relax.Var("x", relax.TensorStructInfo([10, 10], "float32"))
     y = relax.const(np.ones([10, 10]), "float32")
@@ -94,6 +120,7 @@ def test_add_const():
     verify(mod, [x_data])
 
 
+@requires_coreml_runtime
 def test_multiply():
     x = relax.Var("x", relax.TensorStructInfo([10, 10], "float32"))
     y = relax.Var("y", relax.TensorStructInfo([10, 10], "float32"))
@@ -110,6 +137,7 @@ def test_multiply():
     verify(mod, [x_data, y_data])
 
 
+@requires_coreml_runtime
 def test_matmul():
     x = relax.Var("x", relax.TensorStructInfo([8, 10], "float32"))
     y = relax.Constant(tvm.runtime.tensor(np.random.rand(10, 
8).astype("float32"), dev))
@@ -139,6 +167,7 @@ def test_matmul():
     verify(mod, [x_data, y_data])
 
 
+@requires_coreml_runtime
 def test_clip():
     x = relax.Var("x", relax.TensorStructInfo([10, 10], "float32"))
     bb = relax.BlockBuilder()
@@ -168,6 +197,7 @@ def test_clip():
     verify(mod, [x_data])
 
 
+@requires_coreml_runtime
 def test_expand_dims():
     def get_mod(axis):
         x = relax.Var("x", relax.TensorStructInfo([10, 10], "float32"))
@@ -184,6 +214,7 @@ def test_expand_dims():
     verify(get_mod(axis=1), [x_data])
 
 
+@requires_coreml_runtime
 def test_relu():
     x = relax.Var("x", relax.TensorStructInfo([10, 10], "float32"))
     bb = relax.BlockBuilder()
@@ -198,6 +229,7 @@ def test_relu():
     verify(mod, [x_data])
 
 
+@requires_coreml_runtime
 def test_batch_flatten():
     x = relax.Var("x", relax.TensorStructInfo([10, 10, 10], "float32"))
     bb = relax.BlockBuilder()
@@ -212,7 +244,7 @@ def test_batch_flatten():
     verify(mod, [x_data])
 
 
-@requires_coremltools
+@requires_coreml_runtime
 def test_softmax():
     x = relax.Var("x", relax.TensorStructInfo([10, 10], "float32"))
     bb = relax.BlockBuilder()
@@ -227,6 +259,7 @@ def test_softmax():
     verify(mod, [x_data])
 
 
+@requires_coreml_runtime
 def test_conv2d():
     x = relax.Var("x", relax.TensorStructInfo([1, 3, 224, 224], "float32"))
     w = relax.const(np.zeros((16, 3, 3, 3), dtype="float32"))
@@ -241,6 +274,7 @@ def test_conv2d():
     verify(mod, [x_data])
 
 
+@requires_coreml_runtime
 def test_global_avg_pool2d():
     x = relax.Var("x", relax.TensorStructInfo([1, 1, 10, 10], "float32"))
     bb = relax.BlockBuilder()
@@ -254,6 +288,7 @@ def test_global_avg_pool2d():
     verify(mod, [x_data])
 
 
+@requires_coreml_runtime
 def test_subgraph1():
     x = relax.Var("x", relax.TensorStructInfo([10, 10], "float32"))
     y = relax.Var("y", relax.TensorStructInfo([10, 10], "float32"))
@@ -270,6 +305,7 @@ def test_subgraph1():
     verify(mod, [x_data, y_data])
 
 
+@requires_coreml_runtime
 def test_subgraph2():
     x = relax.Var("x", relax.TensorStructInfo([10, 10], "float32"))
     y = relax.Var("y", relax.TensorStructInfo([10, 10], "float32"))

Reply via email to