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 9d5253e2ea [Fix][Relax] Skip ReorderPermuteDimsAfterConcat for 
unknown-rank inputs (#20216)
9d5253e2ea is described below

commit 9d5253e2ea59897664b92c0e8b5508fd83475fdd
Author: yanght27 <[email protected]>
AuthorDate: Sat Aug 29 12:23:27 2026 +0800

    [Fix][Relax] Skip ReorderPermuteDimsAfterConcat for unknown-rank inputs 
(#20216)
    
    Fixes #20204.
    
    `ReorderPermuteDimsAfterConcat` derives the permutation for
    `permute_dims(..., axes=None)` from the input rank. When an input tensor
    has unknown rank, its `ndim` is `-1`, but the pass previously asserted
    that `ndim >= 0`. As a result, valid Relax IR could raise an
    `InternalError`.
    
    This PR changes the axis derivation helper to return
    `ffi::Optional<ffi::Array<int64_t>>`. When the axes are implicit and the
    input rank is unknown, a concrete permutation cannot be derived, so the
    pass conservatively leaves the original expression unchanged. Existing
    behavior is preserved for known-rank inputs and explicit axes.
    
    A regression test covers unknown-rank inputs followed by `concat` and
    verifies that the pass neither rewrites the expression nor raises an
    error.
    
    Testing:
    
    - Built the modified C++ target successfully.
    - `test_transform_reorder_permute_dims_after_concat.py`: 7 passed.
    - Pre-commit hooks passed on both modified files.
    - The original issue reproducer completes without an `InternalError`.
---
 .../transform/reorder_permute_dims_after_concat.cc | 66 ++++++++++++++--------
 ..._transform_reorder_permute_dims_after_concat.py | 20 +++++++
 2 files changed, 61 insertions(+), 25 deletions(-)

diff --git a/src/relax/transform/reorder_permute_dims_after_concat.cc 
b/src/relax/transform/reorder_permute_dims_after_concat.cc
index e1dbbab826..91f7633d76 100644
--- a/src/relax/transform/reorder_permute_dims_after_concat.cc
+++ b/src/relax/transform/reorder_permute_dims_after_concat.cc
@@ -91,40 +91,55 @@ std::tuple<DFPattern, ffi::TypedFunction<Expr(Expr, 
ffi::Map<DFPattern, Expr>)>>
     return attrs->axes;
   };
 
-  auto get_permute_dims_axes =
-      [get_permute_dims_optional_axes](const Expr& expr) -> 
ffi::Array<int64_t> {
+  auto try_get_permute_dims_axes =
+      [get_permute_dims_optional_axes](const Expr& expr) -> 
ffi::Optional<ffi::Array<int64_t>> {
     if (auto opt_axes = get_permute_dims_optional_axes(expr)) {
-      return opt_axes.value();
-    } else {
-      auto call = expr.as_or_throw<Call>();
-      ffi::Array<int64_t> permutation;
-      auto arg_ty = call->args[0]->ty.as<TensorTypeNode>();
-      TVM_FFI_ICHECK(arg_ty) << "Expected permute_dims to have a single tensor 
argument, "
-                             << "but argument " << call->args[0] << " has type 
"
-                             << call->args[0]->ty;
-      TVM_FFI_ICHECK_GE(arg_ty->ndim, 0);
-      size_t ndim = arg_ty->ndim;
-      for (size_t i = 0; i < ndim; i++) {
-        permutation.push_back(static_cast<int64_t>(ndim - i - 1));
-      }
-      return permutation;
+      return opt_axes;
+    }
+
+    auto call = expr.as_or_throw<Call>();
+    ffi::Array<int64_t> permutation;
+    auto arg_ty = call->args[0]->ty.as<TensorTypeNode>();
+    TVM_FFI_ICHECK(arg_ty) << "Expected permute_dims to have a single tensor 
argument, "
+                           << "but argument " << call->args[0] << " has type " 
<< call->args[0]->ty;
+
+    // An implicit permutation depends on the input rank, so it cannot be 
materialized here.
+    if (arg_ty->IsUnknownNdim()) {
+      return std::nullopt;
+    }
+
+    size_t ndim = arg_ty->ndim;
+    for (size_t i = 0; i < ndim; i++) {
+      permutation.push_back(static_cast<int64_t>(ndim - i - 1));
     }
+    return permutation;
   };
 
-  auto permute_dims_axes_are_compatible = [&](const ffi::Array<Expr>& 
permute_dims) -> bool {
-    auto first_axes = get_permute_dims_axes(permute_dims[0]);
+  auto try_get_compatible_permute_dims_axes =
+      [try_get_permute_dims_axes](
+          const ffi::Array<Expr>& permute_dims) -> 
ffi::Optional<ffi::Array<int64_t>> {
+    auto opt_first_axes = try_get_permute_dims_axes(permute_dims[0]);
+    if (!opt_first_axes) {
+      return std::nullopt;
+    }
+    const auto& first_axes = opt_first_axes.value();
+
     for (size_t i_arg = 1; i_arg < permute_dims.size(); i_arg++) {
-      auto i_axes = get_permute_dims_axes(permute_dims[i_arg]);
+      auto opt_i_axes = try_get_permute_dims_axes(permute_dims[i_arg]);
+      if (!opt_i_axes) {
+        return std::nullopt;
+      }
+      const auto& i_axes = opt_i_axes.value();
       if (i_axes.size() != first_axes.size()) {
-        return false;
+        return std::nullopt;
       }
       for (size_t i_axis = 0; i_axis < first_axes.size(); i_axis++) {
         if (i_axes[i_axis] != first_axes[i_axis]) {
-          return false;
+          return std::nullopt;
         }
       }
     }
-    return true;
+    return opt_first_axes;
   };
 
   auto rewriter = [=](Expr expr, ffi::Map<DFPattern, Expr> matches) -> Expr {
@@ -141,9 +156,12 @@ std::tuple<DFPattern, ffi::TypedFunction<Expr(Expr, 
ffi::Map<DFPattern, Expr>)>>
         << "Pattern match should return at least " << min_concat << " items, 
but only found "
         << all_permute_dims.size() << ": " << all_permute_dims;
 
-    if (!permute_dims_axes_are_compatible(all_permute_dims)) {
+    auto opt_permute_dims_axes = 
try_get_compatible_permute_dims_axes(all_permute_dims);
+    if (!opt_permute_dims_axes) {
       return expr;
     }
+    const auto& permute_dims_axes = opt_permute_dims_axes.value();
+
     ffi::Optional<ffi::Array<int64_t>> permute_axes =
         get_permute_dims_optional_axes(all_permute_dims[0]);
 
@@ -151,8 +169,6 @@ std::tuple<DFPattern, ffi::TypedFunction<Expr(Expr, 
ffi::Map<DFPattern, Expr>)>>
     auto concat_attrs = concat_call->attrs.as<ConcatAttrs>();
     TVM_FFI_ICHECK(concat_attrs);
 
-    auto permute_dims_axes = get_permute_dims_axes(all_permute_dims[0]);
-
     int64_t old_concat_axis = concat_attrs->axis.value_or(0);
     int64_t ndim = static_cast<int64_t>(permute_dims_axes.size());
     if (old_concat_axis < 0) {
diff --git 
a/tests/python/relax/test_transform_reorder_permute_dims_after_concat.py 
b/tests/python/relax/test_transform_reorder_permute_dims_after_concat.py
index 2da6cfcda9..4f8cd848d4 100644
--- a/tests/python/relax/test_transform_reorder_permute_dims_after_concat.py
+++ b/tests/python/relax/test_transform_reorder_permute_dims_after_concat.py
@@ -70,6 +70,26 @@ class TestSimple(Base):
             return out
 
 
+class TestDoNotRewriteUnknownRank(Base):
+    """Do not rewrite implicit permutations when their rank is unknown."""
+
+    @I.ir_module
+    class Before:
+        @R.function
+        def main(
+            x: R.Tensor(dtype="float32"),
+            y: R.Tensor(dtype="float32"),
+        ):
+            with R.dataflow():
+                x_t = R.permute_dims(x)
+                y_t = R.permute_dims(y)
+                out = R.concat([x_t, y_t], axis=0)
+                R.output(out)
+            return out
+
+    Expected = Before
+
+
 class TestCombineExplicitAndImplicitAxes(Base):
     """Check for explicit axes to be permuted
 

Reply via email to