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

tqchen 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 9a044debd1 [REFACTOR][TIRx] Use structural definition renewal in core 
transforms (#20601)
9a044debd1 is described below

commit 9a044debd110c6bc84c2bd10a6065127c2f44fd0
Author: Tianqi Chen <[email protected]>
AuthorDate: Thu Oct 8 10:58:36 2026 -0400

    [REFACTOR][TIRx] Use structural definition renewal in core transforms 
(#20601)
    
    Provide TIRx-owned definition renewal through structural mapping,
    preserving bound and free identities through native structural hooks.
    Mark match-buffer targets as pattern definitions and migrate renewal
    consumers to the shared helper, removing the S-TIR-specific renewal API.
    Remove the S-TIR block precondition from index narrowing.
---
 include/tvm/s_tir/stmt.h                           |   3 +-
 include/tvm/s_tir/transform.h                      |   9 --
 include/tvm/tirx/function.h                        |  11 ++
 python/tvm/s_tir/__init__.py                       |  18 ---
 python/tvm/tirx/__init__.py                        |   2 +-
 python/tvm/tirx/function.py                        |  19 +++
 .../transform/lower_global_view_to_local_view.cc   |   4 +-
 .../transform/attach_attr_layout_free_buffers.cc   |   4 +-
 src/relax/transform/compute_prim_value.cc          |   4 +-
 src/relax/transform/fuse_tir.cc                    |   6 +-
 src/relax/transform/split_call_tir_by_pattern.cc   |   6 +-
 .../transform/split_layout_rewrite_preproc.cc      |   6 +-
 src/s_tir/stmt.cc                                  |   6 +-
 src/s_tir/transform/renew_defs.cc                  | 175 ---------------------
 src/tirx/ir/function.cc                            |  29 ++++
 src/tirx/transform/bind_target.cc                  |   5 +-
 src/tirx/transform/force_narrow_index_to_i32.cc    |   8 -
 tests/python/s_tir/test_s_tir_renew_defs.py        |  14 +-
 .../test_tirx_force_narrow_reject_blocks.py        |  35 -----
 19 files changed, 89 insertions(+), 275 deletions(-)

diff --git a/include/tvm/s_tir/stmt.h b/include/tvm/s_tir/stmt.h
index 16096291d5..817a1008ba 100644
--- a/include/tvm/s_tir/stmt.h
+++ b/include/tvm/s_tir/stmt.h
@@ -57,7 +57,8 @@ class MatchBufferRegionNode : public ffi::Object {
   static void RegisterReflection() {
     namespace refl = tvm::ffi::reflection;
     refl::ObjectDef<MatchBufferRegionNode>()
-        .def_ro("buffer", &MatchBufferRegionNode::buffer, 
refl::AttachFieldFlag::SEqHashDefSimple())
+        .def_ro("buffer", &MatchBufferRegionNode::buffer,
+                refl::AttachFieldFlag::SEqHashDefPattern())
         .def_ro("source", &MatchBufferRegionNode::source);
   }
 
diff --git a/include/tvm/s_tir/transform.h b/include/tvm/s_tir/transform.h
index d2aa71098d..5a9c0b7277 100644
--- a/include/tvm/s_tir/transform.h
+++ b/include/tvm/s_tir/transform.h
@@ -35,15 +35,6 @@
 
 namespace tvm {
 namespace s_tir {
-/*!
- * \brief Renew the definition nodes for a TIR, including Var, Buffer and 
IterVar.
- *        This pass works as a simple DeepCopy to duplicate a function with 
different Vars and
- *        Buffers but the same behavior
- * \param func The input Function.
- * \return The renewed func.
- */
-TVM_DLL tirx::Function RenewDefs(const tirx::Function& func);
-
 namespace transform {
 
 using tirx::transform::CreateFunctionPass;
diff --git a/include/tvm/tirx/function.h b/include/tvm/tirx/function.h
index b99547ac27..d84eab6e9e 100644
--- a/include/tvm/tirx/function.h
+++ b/include/tvm/tirx/function.h
@@ -126,6 +126,17 @@ class Function : public BaseFunc {
   TVM_DEFINE_OBJECT_REF_COW_METHOD(FunctionNode);
 };
 
+/*!
+ * \brief Renew variable definitions and remap their uses in a function.
+ *
+ * Definition regions follow the registered structural hooks. Free variables
+ * and function attributes are preserved, and the function signature is 
rebuilt.
+ *
+ * \param func The function to renew.
+ * \return The function with fresh variable definitions.
+ */
+TVM_DLL Function RenewDef(Function func);
+
 /*!
  * \brief Specialize parameters of Function.
  * \param func The Function to be specialized.
diff --git a/python/tvm/s_tir/__init__.py b/python/tvm/s_tir/__init__.py
index d4c79f0e44..da34ed52b9 100644
--- a/python/tvm/s_tir/__init__.py
+++ b/python/tvm/s_tir/__init__.py
@@ -45,24 +45,6 @@ if not _RUNTIME_ONLY:
     from . import dlight
 
 
-def renew_defs(func):
-    """Re-generate the definition nodes for a TIR, including VarDef, BufferDef.
-    This pass works as a simple DeepCopy to duplicate a function with 
different Vars and
-    Buffers but the same behavior
-
-    Parameters
-    ----------
-    func: Function
-        The input function
-
-    Returns
-    -------
-    result : Function
-        The new generated func.
-    """
-    return _ffi_api.RenewDefs(func)
-
-
 def _initialize_script_namespace():
     from . import script
 
diff --git a/python/tvm/tirx/__init__.py b/python/tvm/tirx/__init__.py
index 7c100bffee..217fee603d 100644
--- a/python/tvm/tirx/__init__.py
+++ b/python/tvm/tirx/__init__.py
@@ -56,7 +56,7 @@ from .stmt import BufferRegion, BufferRegionType
 from .stmt import ScopeIdDefStmt
 from .tile_primitive import DispatchContext, TilePrimitiveCall
 
-from .function import Function, IndexMap
+from .function import Function, IndexMap, renew_def
 
 from .op import call_packed_lowered, call_cpacked_lowered, 
register_intrin_lowering
 from .op import call_packed, call_cpacked, call_intrin, call_pure_extern, 
call_extern
diff --git a/python/tvm/tirx/function.py b/python/tvm/tirx/function.py
index fe081387ac..d5db3c9a3b 100644
--- a/python/tvm/tirx/function.py
+++ b/python/tvm/tirx/function.py
@@ -169,6 +169,25 @@ class Function(BaseFunc, Scriptable):
         return _ffi_api.Specialize(self, param_map)  # type: ignore
 
 
+def renew_def(func: Function) -> Function:
+    """Renew variable definitions and remap their uses in a function.
+
+    Definition regions follow the registered structural hooks. Free variables
+    and function attributes are preserved, and the function signature is 
rebuilt.
+
+    Parameters
+    ----------
+    func : Function
+        The function to renew.
+
+    Returns
+    -------
+    Function
+        The function with fresh variable definitions.
+    """
+    return _ffi_api.RenewDef(func)
+
+
 @tvm_ffi.register_object("tirx.IndexMap")
 class IndexMap(Object):
     """A mapping from multi-dimensional indices to another set of 
multi-dimensional indices
diff --git a/src/relax/distributed/transform/lower_global_view_to_local_view.cc 
b/src/relax/distributed/transform/lower_global_view_to_local_view.cc
index 0409288048..a6765eba55 100644
--- a/src/relax/distributed/transform/lower_global_view_to_local_view.cc
+++ b/src/relax/distributed/transform/lower_global_view_to_local_view.cc
@@ -29,7 +29,7 @@
 #include <tvm/relax/op/ccl.h>
 #include <tvm/s_tir/stmt.h>
 #include <tvm/s_tir/stmt_functor.h>
-#include <tvm/s_tir/transform.h>
+#include <tvm/tirx/function.h>
 
 #include "../../../s_tir/schedule/transform.h"
 #include "utils.h"
@@ -129,7 +129,7 @@ class DistributedBufferCompactor : public 
s_tir::StmtExprMutator {
  public:
   static std::tuple<tirx::Function, std::string> DistBufferCompact(
       const std::vector<ShardingSpec>& sharding_specs, tirx::Function 
function) {
-    function = s_tir::RenewDefs(function);
+    function = tirx::RenewDef(function);
     auto compactor = 
ffi::make_object<DistributedBufferCompactor>(sharding_specs, function);
     ffi::Array<Var> new_params;
     ffi::Map<TensorVar, TensorVar> replace_buffer_map;
diff --git a/src/relax/transform/attach_attr_layout_free_buffers.cc 
b/src/relax/transform/attach_attr_layout_free_buffers.cc
index 5122b69856..f784cdb617 100644
--- a/src/relax/transform/attach_attr_layout_free_buffers.cc
+++ b/src/relax/transform/attach_attr_layout_free_buffers.cc
@@ -24,7 +24,7 @@
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/relax/expr_functor.h>
 #include <tvm/relax/transform.h>
-#include <tvm/s_tir/transform.h>
+#include <tvm/tirx/function.h>
 #include <tvm/tirx/stmt_functor.h>
 
 namespace tvm {
@@ -84,7 +84,7 @@ class AttrAttacher : public ExprMutator {
     tirx::Function func = 
WithAttr(mod_->Lookup(gv).as_or_throw<tirx::Function>(),
                                    "layout_free_buffers", layout_free_buffers);
     // Renew defs
-    func = s_tir::RenewDefs(func);
+    func = tirx::RenewDef(func);
     // Add the updated tirx::Function in the IRModule
     // Note the blockbuilder would automatically combine the same tirx function
     // So we don't need to worry about the duplicate insertion
diff --git a/src/relax/transform/compute_prim_value.cc 
b/src/relax/transform/compute_prim_value.cc
index a7a3590306..2ba822a2b0 100644
--- a/src/relax/transform/compute_prim_value.cc
+++ b/src/relax/transform/compute_prim_value.cc
@@ -23,8 +23,8 @@
 #include <tvm/relax/op_attr_types.h>
 #include <tvm/relax/transform.h>
 #include <tvm/s_tir/analysis.h>
-#include <tvm/s_tir/transform.h>
 #include <tvm/tirx/analysis.h>
+#include <tvm/tirx/function.h>
 #include <tvm/tirx/op.h>
 #include <tvm/tirx/stmt_functor.h>
 
@@ -116,7 +116,7 @@ class PrimExprComputeInjector : public ExprMutator {
 
     tirx::Function func(param_vars, tirx::SeqStmt(body), ret_ty,
                         DictAttrs({{tirx::attr::kIsHostFunc, true}, 
{tvm::attr::kSTir, true}}));
-    func = s_tir::RenewDefs(func);
+    func = tirx::RenewDef(func);
 
     auto callee = builder_->AddFunction(func, "compute_symbolic_expr");
 
diff --git a/src/relax/transform/fuse_tir.cc b/src/relax/transform/fuse_tir.cc
index d73558f0bd..7fdd975c6e 100644
--- a/src/relax/transform/fuse_tir.cc
+++ b/src/relax/transform/fuse_tir.cc
@@ -26,7 +26,7 @@
 #include <tvm/relax/type.h>
 #include <tvm/s_tir/stmt.h>
 #include <tvm/s_tir/stmt_functor.h>
-#include <tvm/s_tir/transform.h>
+#include <tvm/tirx/function.h>
 #include <tvm/tirx/op.h>
 
 #include <unordered_map>
@@ -570,7 +570,7 @@ class FusedTIRConstructor : public ExprVisitor {
     tirx::Function function_ = mod_->Lookup(gv).as_or_throw<tirx::Function>();
 
     // Step 2. Renew all vars/buffer definitions and blocks to avoid 
duplication
-    tirx::Function function = s_tir::RenewDefs(function_);
+    tirx::Function function = tirx::RenewDef(function_);
 
     // Step 3. Check functions are all schedulable funcs. i.e. the body of 
func is root block
     // TODO(Siyuan): support un-schedulable functions.
@@ -889,7 +889,7 @@ class FusedTIRConstructor : public ExprVisitor {
     });
     tirx::Function func(params, tirx::SeqStmt(body), VoidType(), 
DictAttrs(attr_map));
     // Renew function defs to prevent using the same symbolic vars in 
different functions
-    return s_tir::RenewDefs(func);
+    return tirx::RenewDef(func);
   }
 
   /*! \brief Get DynTensor numbers from recursive Tuples. */
diff --git a/src/relax/transform/split_call_tir_by_pattern.cc 
b/src/relax/transform/split_call_tir_by_pattern.cc
index 50a49adbe7..7be76c2c46 100644
--- a/src/relax/transform/split_call_tir_by_pattern.cc
+++ b/src/relax/transform/split_call_tir_by_pattern.cc
@@ -30,8 +30,8 @@
 #include <tvm/relax/type.h>
 #include <tvm/s_tir/stmt.h>
 #include <tvm/s_tir/stmt_functor.h>
-#include <tvm/s_tir/transform.h>
 #include <tvm/sym/analyzer.h>
+#include <tvm/tirx/function.h>
 #include <tvm/tirx/op.h>
 
 #include "../../s_tir/schedule/ir_comparator.h"
@@ -760,8 +760,8 @@ class SplitMutator : public ExprMutator {
       new_call->args = {lib_func, call->args[1]};
       return Call(new_call);
     }
-    tirx::Function func1 = s_tir::RenewDefs(split_funcs.first);
-    tirx::Function func2 = s_tir::RenewDefs(split_funcs.second.value());
+    tirx::Function func1 = tirx::RenewDef(split_funcs.first);
+    tirx::Function func2 = tirx::RenewDef(split_funcs.second.value());
     TVM_FFI_ICHECK(arg_partition.size() == 2);
     // emit the first call to the library kernel
     ffi::Array<Expr> args1;
diff --git a/src/relax/transform/split_layout_rewrite_preproc.cc 
b/src/relax/transform/split_layout_rewrite_preproc.cc
index 0340b81897..6e6600058d 100644
--- a/src/relax/transform/split_layout_rewrite_preproc.cc
+++ b/src/relax/transform/split_layout_rewrite_preproc.cc
@@ -27,7 +27,7 @@
 #include <tvm/relax/transform.h>
 #include <tvm/s_tir/stmt.h>
 #include <tvm/s_tir/stmt_functor.h>
-#include <tvm/s_tir/transform.h>
+#include <tvm/tirx/function.h>
 
 #include <algorithm>
 #include <cstddef>
@@ -101,7 +101,7 @@ class SplitFunctionLayoutRewrite : public 
s_tir::StmtExprMutator {
     DictAttrs attrs(dict);
     tirx::Function func = tirx::Function(params, SeqStmt(body), VoidType(), 
attrs);
 
-    return s_tir::RenewDefs(func);
+    return tirx::RenewDef(func);
   }
 
   tirx::Function create_compute_func() const {
@@ -147,7 +147,7 @@ class SplitFunctionLayoutRewrite : public 
s_tir::StmtExprMutator {
     DictAttrs attrs(dict);
     tirx::Function func = tirx::Function(params, SeqStmt(body), VoidType(), 
attrs);
 
-    return s_tir::RenewDefs(func);
+    return tirx::RenewDef(func);
   }
 
   void visit_root_block(const s_tir::SBlockNode* op) {
diff --git a/src/s_tir/stmt.cc b/src/s_tir/stmt.cc
index 711b64ba66..d86b9ae42d 100644
--- a/src/s_tir/stmt.cc
+++ b/src/s_tir/stmt.cc
@@ -83,7 +83,7 @@ TVM_FFI_INLINE 
ffi::Expected<ffi::Optional<ffi::VisitInterrupt>> MatchBufferRegi
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
MatchBufferRegionNode>(
           value);
   TVM_FFI_S_VISIT_MAYBE_EARLY_RETURN(visitor->WithDefRegionKind(
-      kTVMFFIDefRegionKindSimple, [&]() { return 
visitor->VisitExpected(self->buffer); }));
+      kTVMFFIDefRegionKindPattern, [&]() { return 
visitor->VisitExpected(self->buffer); }));
   TVM_FFI_S_VISIT_MAYBE_EARLY_RETURN(visitor->VisitExpected(self->source));
   return std::nullopt;
 }
@@ -94,7 +94,7 @@ TVM_FFI_INLINE ffi::Expected<ffi::UnchangedOr<ffi::Any>> 
MatchBufferRegionMutate
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
MatchBufferRegionNode>(
           value);
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<TensorVar>, mapped_buffer,
-                                    
mutator->WithDefRegionKind(kTVMFFIDefRegionKindSimple, [&]() {
+                                    
mutator->WithDefRegionKind(kTVMFFIDefRegionKindPattern, [&]() {
                                       return 
mutator->MutateExpected(self->buffer);
                                     }));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<TensorRegion>, 
mapped_source,
@@ -115,7 +115,7 @@ TVM_FFI_INLINE ffi::Expected<ffi::UnchangedOr<ffi::Any>> 
MatchBufferRegionMaybeI
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
MatchBufferRegionNode>(
           value));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<TensorVar>, mapped_buffer,
-                                    
mutator->WithDefRegionKind(kTVMFFIDefRegionKindSimple, [&]() {
+                                    
mutator->WithDefRegionKind(kTVMFFIDefRegionKindPattern, [&]() {
                                       return 
mutator->MutateExpected(self->buffer,
                                                                      
ffi::InplaceMode::kAllow);
                                     }));
diff --git a/src/s_tir/transform/renew_defs.cc 
b/src/s_tir/transform/renew_defs.cc
deleted file mode 100644
index 71636ed46f..0000000000
--- a/src/s_tir/transform/renew_defs.cc
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/*!
- * \file renew_defs.cc
- * \brief Renew the definition nodes for a TIR, including Var, Buffer and 
IterVar.
- */
-
-#include <tvm/ffi/cast.h>
-#include <tvm/ffi/reflection/registry.h>
-#include <tvm/s_tir/stmt.h>
-#include <tvm/s_tir/stmt_functor.h>
-#include <tvm/s_tir/transform.h>
-
-namespace tvm {
-namespace s_tir {
-using namespace tvm::tirx;
-
-#define STMT_REGENERATE_VAR_DEF(NODE, FIELD)                                   
             \
-  UnchangedOr<Stmt> Mutate_(const NODE* op, InplaceMode inplace_mode) final {  
             \
-    Var new_var = this->ReDefineVar(op->FIELD);                                
             \
-    Stmt stmt =                                                                
             \
-        StmtExprMutator::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<Stmt>(op)); \
-    op = stmt.as<NODE>();                                                      
             \
-    TVM_FFI_ICHECK(op != nullptr);                                             
             \
-    auto n = ffi::make_object<NODE>(*op);                                      
             \
-    n->FIELD = 
std::move(new_var).as_or_throw<std::decay_t<decltype(n->FIELD)>>();          \
-    return Stmt(n);                                                            
             \
-  }
-
-class RenewDefMutator : public StmtExprMutator {
- public:
-  using StmtExprMutator::Mutate;
-  using StmtExprMutator::Mutate_;
-
-  static Function Transform(const Function& func) {
-    auto generator = ffi::make_object<RenewDefMutator>();
-    // Establish explicit parameters and symbols in their types before 
visiting uses.
-    for (const Var& param : func->params) {
-      if (param->ty.as<PrimTypeNode>()) generator->ReDefineVar(param);
-    }
-    ffi::Array<Var> params = func->params.Map([&](const Var& param) {
-      auto mapped = generator->VarRemapGet(param);
-      return mapped != nullptr ? mapped.as_or_throw<Var>() : 
generator->ReDefineVar(param);
-    });
-    // Visit body
-    auto body = generator->Mutate(func->body).ValueOrUnchanged(func->body);
-    // Recreate function
-    return Function(params, body, func->ret_type, func->attrs, func->span);
-  }
-
- private:
-  UnchangedOr<Stmt> Mutate_(const BindNode* op, InplaceMode inplace_mode) 
final {
-    // Result-type patterns may introduce metadata symbols. Renew those before
-    // rewriting the RHS; the Bind's own definition is published only 
afterwards.
-    WithDefRegionKind(kTVMFFIDefRegionKindPattern,
-                      [&] { return Mutate(op->value->ty, 
InplaceMode::kDisallow); });
-    return StmtExprMutator::Mutate_(op, inplace_mode);
-  }
-
-  STMT_REGENERATE_VAR_DEF(ForNode, loop_var);
-
-  UnchangedOr<Expr> Mutate_(const VarNode* op, InplaceMode inplace_mode) final 
{
-    Var var = ffi::GetRef<Var>(op);
-    if (auto mapped = VarRemapGet(var); mapped != nullptr) {
-      return mapped.as_or_throw<Expr>();
-    }
-    if (def_region_kind() != kTVMFFIDefRegionKindNone) return ReDefineVar(var);
-    return ffi::Unchanged();
-  }
-
-  UnchangedOr<Stmt> Mutate_(const SBlockNode* op, InplaceMode inplace_mode) 
final {
-    // Step 0. Re-define Itervars
-    ffi::Array<IterVar> iter_vars =
-        op->iter_vars.Map(std::bind(&RenewDefMutator::VisitIterVar, this, 
std::placeholders::_1));
-
-    // Step 1. Re-define buffers allocated under the block
-    ffi::Array<TensorVar> alloc_buffers = op->alloc_buffers.Map([this](const 
TensorVar& buf) {
-      return this->ReDefineVar(buf.var()).as_or_throw<TensorVar>();
-    });
-
-    // Step 2. Re-define match_buffers
-    ffi::Array<MatchBufferRegion> match_buffers = op->match_buffers.Map(
-        std::bind(&RenewDefMutator::VisitMatchBuffer, this, 
std::placeholders::_1));
-
-    // Step 3. Visit body
-    ffi::Optional<SeqStmt> init = std::nullopt;
-    if (op->init.has_value()) {
-      init = this->Mutate(op->init.value(), 
inplace_mode).ValueOrUnchanged(op->init.value());
-    }
-    Stmt body = this->Mutate(op->body, 
inplace_mode).ValueOrUnchanged(op->body);
-
-    // Step 4. Revisit access region
-    ffi::Array<TensorRegion> reads =
-        op->reads.Map(std::bind(&RenewDefMutator::VisitBufferRegion, this, 
std::placeholders::_1));
-    ffi::Array<TensorRegion> writes =
-        op->writes.Map(std::bind(&RenewDefMutator::VisitBufferRegion, this, 
std::placeholders::_1));
-
-    // Step 5. Regenerate block. Since the defs are changed, we need to create 
a new block
-    auto n = ffi::make_object<SBlockNode>(*op);
-    n->iter_vars = std::move(iter_vars);
-    n->alloc_buffers = std::move(alloc_buffers);
-    n->match_buffers = std::move(match_buffers);
-    n->reads = std::move(reads);
-    n->writes = std::move(writes);
-    n->body = std::move(body);
-    n->init = std::move(init);
-
-    return Stmt(n);
-  }
-
-  Var ReDefineVar(const Var& var) {
-    if (auto mapped = VarRemapGet(var); mapped != nullptr) return 
mapped.as_or_throw<Var>();
-    Type type = WithDefRegionKind(kTVMFFIDefRegionKindPattern, [&] {
-      return Mutate(var->ty, InplaceMode::kDisallow)
-          .as_or_throw<UnchangedOr<Type>>()
-          .ValueOrUnchanged(var->ty);
-    });
-    Var new_var(var->name, type, var->span);
-    VarRemapSet(var, new_var);
-    return new_var;
-  }
-
-  IterVar VisitIterVar(const IterVar& iter_var) {
-    auto mapped = VarRemapGet(iter_var);
-    if (mapped != nullptr) return mapped.as_or_throw<IterVar>();
-    PrimExpr min =
-        Mutate(iter_var->dom->min, 
InplaceMode::kDisallow).ValueOrUnchanged(iter_var->dom->min);
-    PrimExpr extent = Mutate(iter_var->dom->extent, InplaceMode::kDisallow)
-                          .ValueOrUnchanged(iter_var->dom->extent);
-    IterVar new_iter_var(Range::FromMinExtent(min, extent),
-                         ReDefineVar(iter_var->var).as_or_throw<PrimVar>(), 
iter_var->iter_type,
-                         iter_var->thread_tag);
-    VarRemapSet(iter_var, new_iter_var);
-    return new_iter_var;
-  }
-
-  MatchBufferRegion VisitMatchBuffer(const MatchBufferRegion& match_buffer) {
-    TensorVar buffer = 
ReDefineVar(match_buffer->buffer.var()).as_or_throw<TensorVar>();
-    TensorRegion region = VisitBufferRegion(match_buffer->source);
-    return MatchBufferRegion(std::move(buffer), std::move(region));
-  }
-
-  TensorRegion VisitBufferRegion(const TensorRegion& buffer_region) {
-    return Mutate(buffer_region, InplaceMode::kDisallow)
-        .ValueOrUnchanged(buffer_region)
-        .as_or_throw<TensorRegion>();
-  }
-};
-
-Function RenewDefs(const Function& func) { return 
RenewDefMutator::Transform(func); }
-
-TVM_FFI_STATIC_INIT_BLOCK() {
-  namespace refl = tvm::ffi::reflection;
-  refl::GlobalDef().def("s_tir.RenewDefs", RenewDefs);
-}
-
-}  // namespace s_tir
-}  // namespace tvm
diff --git a/src/tirx/ir/function.cc b/src/tirx/ir/function.cc
index 97db05bc86..f6ad45e077 100644
--- a/src/tirx/ir/function.cc
+++ b/src/tirx/ir/function.cc
@@ -123,6 +123,34 @@ Function::Function(ffi::Array<tirx::Var> params, 
ffi::Optional<SeqStmt> body, Ty
   data_ = std::move(n);
 }
 
+Function RenewDef(Function func) {
+  // Seed original definitions so identities created by structural hooks are 
preserved.
+  ffi::Map<Var, Var> definition_remap;
+  ffi::StructuralWalk<ffi::WalkOrder::kPostOrder>(func,
+                                                  [&](const Var& var, 
TVMFFIDefRegionKind kind) {
+                                                    if (kind != 
kTVMFFIDefRegionKindNone)
+                                                      
definition_remap.Set(var, var);
+                                                    return 
ffi::WalkResult::Advance();
+                                                  });
+  return ffi::StructuralMap<ffi::WalkOrder::kPostOrder>(
+             func,
+             [remap = std::move(definition_remap)](const Var& var,
+                                                   TVMFFIDefRegionKind kind) 
mutable {
+               auto mapped = remap.Get(var);
+               if (!mapped.has_value()) return var;
+               if (!mapped.value().same_as(var)) return mapped.value();
+               if (kind == kTVMFFIDefRegionKindNone) return var;
+               Var fresh(var->name, var->ty, var->span);
+               remap.Set(var, fresh);
+               return fresh;
+             },
+             [](const Function& mapped) {
+               return Function(mapped->params, mapped->body, mapped->ret_type, 
mapped->attrs,
+                               mapped->span);
+             })
+      .as_or_throw<Function>();
+}
+
 TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
   FunctionNode::RegisterReflection();
@@ -137,6 +165,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       "tirx.Function",
       [](ffi::Array<tirx::Var> params, ffi::Optional<SeqStmt> body, Type 
ret_type, DictAttrs attrs,
          Span span) { return Function(params, body, ret_type, attrs, span); });
+  refl::GlobalDef().def("tirx.RenewDef", RenewDef);
 }
 
 FuncType FunctionNode::func_type_annotation() const {
diff --git a/src/tirx/transform/bind_target.cc 
b/src/tirx/transform/bind_target.cc
index 5379904f0e..b36a32e8f1 100644
--- a/src/tirx/transform/bind_target.cc
+++ b/src/tirx/transform/bind_target.cc
@@ -37,8 +37,7 @@
 #include <tvm/ffi/cast.h>
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ir/unique_name_supply.h>
-#include <tvm/s_tir/stmt.h>
-#include <tvm/s_tir/transform.h>
+#include <tvm/tirx/function.h>
 #include <tvm/tirx/op.h>
 #include <tvm/tirx/stmt_functor.h>
 #include <tvm/tirx/transform.h>
@@ -308,7 +307,7 @@ IRModule BindTarget(IRModule mod, const Target& target) {
       if (called_by_host && called_by_device) {
         // Rule 4.1: Called by both host and device
         // Bind device target to current function
-        Function host_func = s_tir::RenewDefs(function);
+        Function host_func = tirx::RenewDef(function);
         new_mod->Update(gvar,
                         WithAttr(std::move(function), tvm::attr::kTarget, 
target_without_host));
 
diff --git a/src/tirx/transform/force_narrow_index_to_i32.cc 
b/src/tirx/transform/force_narrow_index_to_i32.cc
index 1fe5192d00..e48e06242b 100644
--- a/src/tirx/transform/force_narrow_index_to_i32.cc
+++ b/src/tirx/transform/force_narrow_index_to_i32.cc
@@ -27,7 +27,6 @@
 
 #include <tvm/ffi/cast.h>
 #include <tvm/ffi/reflection/registry.h>
-#include <tvm/s_tir/stmt.h>
 #include <tvm/tirx/stmt_functor.h>
 #include <tvm/tirx/transform.h>
 
@@ -37,13 +36,6 @@ namespace tirx {
 class Int32DTypeNarrower : public 
Int32DTypeNarrowerBase<IndexDataTypeNormalizer> {
  public:
   static Function RewriteDataType(Function func) {
-    // The TIRX normalizer does not rewrite S-TIR block iterators, regions, or 
match buffers, so
-    // narrowing a function that still contains blocks would leave their index 
types inconsistent.
-    if (func->body.has_value() && 
ContainsNode<s_tir::SBlockRealizeNode>(func->body.value())) {
-      TVM_FFI_THROW(ValueError)
-          << "tirx.transform.ForceNarrowIndexToInt32 requires a function 
without S-TIR blocks. "
-          << "Use s_tir.transform.ForceNarrowIndexToInt32 before block 
lowering.";
-    }
     CheckBufferParams(func);
     auto narrower = ffi::make_object<Int32DTypeNarrower>(func);
     return narrower->Rewrite(func);
diff --git a/tests/python/s_tir/test_s_tir_renew_defs.py 
b/tests/python/s_tir/test_s_tir_renew_defs.py
index e4fbfe7a0a..2517a2b50f 100644
--- a/tests/python/s_tir/test_s_tir_renew_defs.py
+++ b/tests/python/s_tir/test_s_tir_renew_defs.py
@@ -66,7 +66,7 @@ def test_simple():
                 B[vi, vj] = A[vi, vj] * 2.0
 
     f1 = elementwise
-    f2 = tvm.s_tir.renew_defs(f1)
+    f2 = tvm.tirx.renew_def(f1)
     tvm.ir.assert_structural_equal(f1, f2)
 
     _check_func_signature_remap(f1, f2)
@@ -109,7 +109,7 @@ def test_match_buffer():
                     B[vi, vj] = A0[vi, vj] * 2.0
 
     f1 = func_match_buffer
-    f2 = tvm.s_tir.renew_defs(f1)
+    f2 = tvm.tirx.renew_def(f1)
     tvm.ir.assert_structural_equal(f1, f2)
 
     _check_func_signature_remap(f1, f2)
@@ -145,7 +145,7 @@ def test_undefined_buffer():
             A[i] = A[i] + T.float16(1.0)
 
     f1 = access_alloc
-    f2 = tvm.s_tir.renew_defs(f1)
+    f2 = tvm.tirx.renew_def(f1)
     tvm.ir.assert_structural_equal(f1, f2)
 
     # AllocTensor is now a flat statement in SeqStmt
@@ -168,7 +168,7 @@ def test_symbolic_func():
             B[i, j * 2 + 1] = A[i, j]
 
     f1 = symbolic_func
-    f2 = tvm.s_tir.renew_defs(f1)
+    f2 = tvm.tirx.renew_def(f1)
     tvm.ir.assert_structural_equal(f1, f2)
 
 
@@ -183,7 +183,7 @@ def test_buffer_params():
                 B[vi, vj] = A[vi * 2 + vj]
 
     f1 = main
-    f2 = tvm.s_tir.renew_defs(main)
+    f2 = tvm.tirx.renew_def(main)
     tvm.ir.assert_structural_equal(f1, f2)
     assert f1.params[1].shape[0] != f2.params[1].shape[0]
 
@@ -192,7 +192,7 @@ def test_compound_buffer_param_shape_var():
     n = tvm.tirx.Var("n", "int32")
     A = tvm.tirx.decl_tensor((tvm.tirx.max(n, 1),), layout=None)
     f1 = tvm.tirx.Function([A], tvm.tirx.Evaluate(n))
-    f2 = tvm.s_tir.renew_defs(f1)
+    f2 = tvm.tirx.renew_def(f1)
 
     tvm.ir.assert_structural_equal(f1, f2)
     assert not f1.body[0].value.same_as(f2.body[0].value)
@@ -214,7 +214,7 @@ def test_gather():
                 T_take[v_ax0, v_ax1] = A[B[v_ax0], v_ax1]
 
     f1 = take
-    f2 = tvm.s_tir.renew_defs(take)
+    f2 = tvm.tirx.renew_def(take)
     tvm.ir.assert_structural_equal(f1, f2)
 
 
diff --git 
a/tests/python/s_tir/transform/test_tirx_force_narrow_reject_blocks.py 
b/tests/python/s_tir/transform/test_tirx_force_narrow_reject_blocks.py
deleted file mode 100644
index 6a8db049c6..0000000000
--- a/tests/python/s_tir/transform/test_tirx_force_narrow_reject_blocks.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-import pytest
-
-import tvm
-from tvm.script import s_tir as Ts
-from tvm.script import tirx as T
-
-
-def test_reject_blocks():
-    @Ts.function(private=True)
-    def before(A: T.Tensor((128,), "float32"), B: T.Tensor((128,), "float32")):
-        for i in T.serial(0, T.int64(16)):
-            for j in T.serial(0, T.int64(8)):
-                with Ts.sblock():
-                    vi = Ts.axis.spatial(T.int64(128), i * T.int64(8) + j)
-                    B[vi] = A[vi] + T.float32(1)
-
-    mod = tvm.IRModule.from_expr(before)
-    with pytest.raises(ValueError, match="requires a function without S-TIR 
blocks"):
-        tvm.tirx.transform.ForceNarrowIndexToInt32()(mod)

Reply via email to