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 45713b39a3 [REFACTOR][TIRx][S-TIR] Localize SSA conversion and 
simplify lexical remapping (#20695)
45713b39a3 is described below

commit 45713b39a3b1b87abe7ddf51fb1822fb6795f71c
Author: Tianqi Chen <[email protected]>
AuthorDate: Mon Oct 12 22:44:13 2026 -0400

    [REFACTOR][TIRx][S-TIR] Localize SSA conversion and simplify lexical 
remapping (#20695)
    
    Keep SSA conversion local to TIRx and S-TIR, removing S-TIR's dependency
    on the private TIRx converter while preserving its block-specific
    traversal.
    
    Replace remap histories and scope-exit callbacks with scope-local maps
    of the latest variable binding. Definition tracking remains global
    across scopes and functions; pattern definitions reuse only visible
    bindings. Function attributes and metadata are preserved when parameters
    or bodies change, and derived function signatures are refreshed.
---
 include/tvm/ir/scope_stack.h              |   4 +
 src/s_tir/transform/ir_utils.cc           | 234 ++++++++++++++++++++++++++++--
 src/s_tir/transform/ir_utils.h            |   3 +-
 src/s_tir/transform/lower_opaque_block.cc |   1 +
 src/tirx/transform/ir_utils.cc            | 171 ++++++++++------------
 src/tirx/transform/ir_utils.h             |  85 -----------
 6 files changed, 307 insertions(+), 191 deletions(-)

diff --git a/include/tvm/ir/scope_stack.h b/include/tvm/ir/scope_stack.h
index b5ea10656f..2fdc546ac4 100644
--- a/include/tvm/ir/scope_stack.h
+++ b/include/tvm/ir/scope_stack.h
@@ -57,6 +57,10 @@ class ScopeStack {
   /*! \brief Construct with one initial scope level. */
   ScopeStack() { stack_.emplace_back(); }
 
+  /*! \brief Iterate active scopes from innermost to outermost. */
+  auto rbegin() const { return stack_.rbegin(); }
+  auto rend() const { return stack_.rend(); }
+
   /*! \brief Return the number of active scopes. */
   size_t size() const { return stack_.size(); }
 
diff --git a/src/s_tir/transform/ir_utils.cc b/src/s_tir/transform/ir_utils.cc
index 326657dcb3..6b13342329 100644
--- a/src/s_tir/transform/ir_utils.cc
+++ b/src/s_tir/transform/ir_utils.cc
@@ -19,36 +19,39 @@
 
 #include "ir_utils.h"
 
+#include <tvm/ffi/extra/structural_visit.h>
+#include <tvm/ffi/reflection/registry.h>
 #include <tvm/ir/attrs.h>
 #include <tvm/ir/prim/op.h>
+#include <tvm/ir/scope_stack.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/memory.h>
 
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+
 namespace tvm {
 namespace s_tir {
 using namespace tirx;
 using namespace tvm::prim;
 
 namespace {
-class SIRConvertSSA final : public tirx::IRConvertSSA {
+class SIRConvertSSA final : public StmtExprMutator {
  public:
-  TVM_DEFINE_OBJECT_FUNCTOR_DEFAULT_CONSTRUCTOR(SIRConvertSSA, 
tirx::IRConvertSSA)
-  using tirx::IRConvertSSA::Mutate_;
-
- protected:
-  static void InitVTable(VTable* table) {
-    tirx::IRConvertSSA::InitVTable(table);
-    SetDispatch<SIRConvertSSA, SBlockNode>(table);
-    SetDispatch<SIRConvertSSA, SBlockRealizeNode>(table);
-  }
+  TVM_DEFINE_OBJECT_FUNCTOR_DEFAULT_CONSTRUCTOR(SIRConvertSSA, StmtExprMutator)
+  using StmtExprMutator::Mutate;
+  using StmtExprMutator::Mutate_;
+  Function VisitFunction(Function func);
+  IRModule VisitIRModule(IRModule mod);
 
- public:
   UnchangedOr<Stmt> Mutate_(const SBlockNode* op, InplaceMode mode) {
     SBlock block = ffi::GetRef<SBlock>(op);
-    return WithScope([&]() -> Stmt {
+    return scope_.WithNewScope([&]() -> Stmt {
       auto iter_vars = op->iter_vars.Map([&](IterVar iter) {
         Var var = DefineVar(iter->var);
         if (!var.same_as(iter->var)) iter.CopyOnWrite()->var = 
var.as_or_throw<PrimVar>();
@@ -64,7 +67,214 @@ class SIRConvertSSA final : public tirx::IRConvertSSA {
   UnchangedOr<Stmt> Mutate_(const SBlockRealizeNode* op, InplaceMode mode) {
     return s_tir::StmtExprMutator::MutateBlockRealize(this, op, mode);
   }
+
+ private:
+  UnchangedOr<Expr> Mutate_(const VarNode* op, InplaceMode inplace_mode) final;
+  Var DefineVar(Var var);
+  ffi::Optional<Var> GetVarRemap(const Var& var);
+  UnchangedOr<Stmt> Mutate_(const BindNode* op, InplaceMode inplace_mode) 
final;
+  UnchangedOr<Stmt> Mutate_(const IfNode* op, InplaceMode inplace_mode) final;
+  UnchangedOr<Stmt> Mutate_(const ForNode* op, InplaceMode inplace_mode) final;
+  UnchangedOr<Stmt> Mutate_(const WhileNode* op, InplaceMode inplace_mode) 
final;
+  UnchangedOr<Stmt> Mutate_(const RegionStmtNode* op, InplaceMode 
inplace_mode) final;
+  UnchangedOr<PrimExpr> Mutate_(const prim::LetNode* op, InplaceMode 
inplace_mode) final;
+  static Var MakeNewVar(const Var& old_var);
+  void SetVarRemap(const Var& old_var, const Var& new_var);
+
+  // Lexical visibility ends with each scope, but definitions stay unique 
across the module.
+  ScopeStack<std::unordered_map<const VarNode*, Var>> scope_;
+  std::unordered_set<const VarNode*> defined_;
 };
+
+Function SIRConvertSSA::VisitFunction(Function func) {
+  return scope_.WithNewScope([&]() -> Function {
+    std::unordered_set<const VarNode*> parameter_symbols;
+    // Define explicit parameters before the symbolic values in their types.
+    for (const Var& param : func->params) {
+      DefineVar(param);
+      parameter_symbols.insert(param.get());
+    }
+    for (const Var& param : func->params) {
+      ffi::StructuralWalk<ffi::WalkOrder::kPostOrder>(
+          param->ty, [&](const Var& var) -> ffi::Expected<ffi::WalkResult> {
+            if (!parameter_symbols.count(var.get())) {
+              parameter_symbols.insert(var.get());
+              DefineVar(var);
+            }
+            return ffi::WalkResult::Advance();
+          });
+    }
+    auto params = func->params.Map([&](const Var& var) {
+      Var mapped = GetVarRemap(var).value_or(var);
+      Type type = Mutate(var->ty, InplaceMode::kDisallow)
+                      .as_or_throw<UnchangedOr<Type>>()
+                      .ValueOrUnchanged(var->ty);
+      if (!type.same_as(mapped->ty)) {
+        mapped = mapped.CopyWithType(type);
+        SetVarRemap(var, mapped);
+      }
+      return mapped;
+    });
+
+    auto body_result = Mutate(func->body, InplaceMode::kDisallow);
+    bool body_unchanged = body_result.UnchangedOrSameAs(func->body);
+    auto body = std::move(body_result).ValueOrUnchanged(func->body);
+
+    if (!params.same_as(func->params) || !body_unchanged) {
+      auto* node = func.CopyOnWrite();
+      node->params = std::move(params);
+      node->body = std::move(body);
+      node->ty = node->func_type_annotation();
+    }
+    return func;
+  });
+}
+
+UnchangedOr<Expr> SIRConvertSSA::Mutate_(const VarNode* op, InplaceMode 
inplace_mode) {
+  Var var = ffi::GetRef<Var>(op);
+  if (def_region_kind() != kTVMFFIDefRegionKindNone) {
+    if (def_region_kind() == kTVMFFIDefRegionKindPattern) {
+      if (auto mapped = GetVarRemap(var)) return mapped.value();
+    }
+    return DefineVar(var);
+  }
+  Var mapped = GetVarRemap(var).value_or(var);
+  if (!mapped.same_as(var)) return mapped;
+  return ffi::Unchanged();
+}
+
+UnchangedOr<PrimExpr> SIRConvertSSA::Mutate_(const prim::LetNode* op, 
InplaceMode inplace_mode) {
+  ffi::Any previous_remap = VarRemapGet(op->var);
+  PrimExpr result = scope_.WithNewScope([&] {
+    return StmtExprMutator::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<PrimExpr>(op));
+  });
+  VarRemapSet(op->var, previous_remap);
+  return result;
+}
+
+Var SIRConvertSSA::DefineVar(Var var) {
+  Type type = WithDefRegionKind(kTVMFFIDefRegionKindNone, [&] {
+    return Mutate(var->ty, InplaceMode::kDisallow)
+        .as_or_throw<UnchangedOr<Type>>()
+        .ValueOrUnchanged(var->ty);
+  });
+  Var result = defined_.count(var.get()) ? MakeNewVar(var) : var;
+  defined_.insert(var.get());
+  if (!type.same_as(result->ty)) result = result.CopyWithType(type);
+  SetVarRemap(var, result);
+  return result;
+}
+
+ffi::Optional<Var> SIRConvertSSA::GetVarRemap(const Var& var) {
+  for (auto it = scope_.rbegin(); it != scope_.rend(); ++it) {
+    if (auto found = it->find(var.get()); found != it->end()) return 
found->second;
+  }
+  return std::nullopt;
+}
+
+UnchangedOr<Stmt> SIRConvertSSA::Mutate_(const BindNode* op, InplaceMode 
inplace_mode) {
+  Var var = op->var;
+  ffi::Any previous_remap = VarRemapGet(var);
+  // The ordinary Bind path rewrites the RHS before defining the Var and 
propagates its type.
+  Stmt stmt = StmtExprMutator::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<Stmt>(op));
+  Var bound_var = stmt.as<BindNode>()->var;
+  if (!bound_var.same_as(GetVarRemap(var).value_or(var))) SetVarRemap(var, 
bound_var);
+  VarRemapSet(var, previous_remap);
+  return stmt;
+}
+
+UnchangedOr<Stmt> SIRConvertSSA::Mutate_(const IfNode* op, InplaceMode 
inplace_mode) {
+  // Each branch gets its own scope so Bind remaps in one branch
+  // do not leak into the other.
+  auto condition_result = Mutate(op->condition, inplace_mode);
+  bool condition_unchanged = condition_result.UnchangedOrSameAs(op->condition);
+  PrimExpr condition = 
std::move(condition_result).ValueOrUnchanged(op->condition);
+  Stmt then_case = scope_.WithNewScope([&]() -> Stmt {
+    return Mutate(op->then_case, inplace_mode).ValueOrUnchanged(op->then_case);
+  });
+  ffi::Optional<SeqStmt> else_case;
+  if (op->else_case) {
+    else_case = scope_.WithNewScope([&]() -> SeqStmt {
+      return Mutate(op->else_case.value(), 
inplace_mode).ValueOrUnchanged(op->else_case.value());
+    });
+  }
+  if (condition_unchanged && then_case.same_as(op->then_case) && 
else_case.same_as(op->else_case)) {
+    return ffi::Unchanged();
+  }
+  return If(condition, then_case, else_case);
+}
+
+UnchangedOr<Stmt> SIRConvertSSA::Mutate_(const ForNode* op, InplaceMode 
inplace_mode) {
+  const Var& v = op->loop_var;
+  if (defined_.count(v.get())) {
+    return scope_.WithNewScope([&]() -> Stmt {
+      Var new_var = MakeNewVar(v);
+      SetVarRemap(v, new_var);
+      Stmt stmt =
+          StmtExprMutator::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<Stmt>(op));
+      auto n = ffi::make_object<ForNode>(*stmt.as<ForNode>());
+      n->loop_var = new_var.as_or_throw<PrimVar>();
+      return For(n);
+    });
+  } else {
+    defined_.insert(v.get());
+    return scope_.WithNewScope([&]() -> Stmt {
+      SetVarRemap(v, v);
+      return StmtExprMutator::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<Stmt>(op));
+    });
+  }
+}
+
+UnchangedOr<Stmt> SIRConvertSSA::Mutate_(const WhileNode* op, InplaceMode 
inplace_mode) {
+  return scope_.WithNewScope([&]() -> Stmt {
+    return StmtExprMutator::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<Stmt>(op));
+  });
+}
+
+UnchangedOr<Stmt> SIRConvertSSA::Mutate_(const RegionStmtNode* op, InplaceMode 
inplace_mode) {
+  auto attrs = Mutate(op->attrs, InplaceMode::kDisallow)
+                   .as_or_throw<UnchangedOr<DictAttrs>>()
+                   .ValueOrUnchanged(op->attrs);
+  auto args = Mutate(op->args, InplaceMode::kDisallow)
+                  .as_or_throw<UnchangedOr<ffi::Array<Expr>>>()
+                  .ValueOrUnchanged(op->args);
+  ffi::Array<Var> params;
+  Stmt body = scope_.WithNewScope([&]() -> Stmt {
+    for (const Var& var : op->body_params) params.push_back(DefineVar(var));
+    return Mutate(op->body, InplaceMode::kDisallow).ValueOrUnchanged(op->body);
+  });
+  ffi::Array<Var> results;
+  for (const Var& var : op->result_vars) results.push_back(DefineVar(var));
+  return RegionStmt(op->op, std::move(args), std::move(params), 
std::move(attrs), std::move(body),
+                    std::move(results), op->loc);
+}
+
+Var SIRConvertSSA::MakeNewVar(const Var& old_var) {
+  return Var(old_var->name, old_var->ty, old_var->loc);
+}
+
+void SIRConvertSSA::SetVarRemap(const Var& old_var, const Var& new_var) {
+  scope_.Current().insert_or_assign(old_var.get(), new_var);
+}
+
+IRModule SIRConvertSSA::VisitIRModule(IRModule mod) {
+  ffi::Map<GlobalVar, BaseFunc> functions;
+  bool made_change = false;
+  for (auto [gvar, base_func] : mod->functions) {
+    if (auto* ptr = base_func.as<tirx::FunctionNode>()) {
+      auto updated = VisitFunction(ffi::GetRef<tirx::Function>(ptr));
+      if (!updated.same_as(base_func)) {
+        made_change = true;
+        base_func = updated;
+      }
+    }
+    functions.Set(gvar, base_func);
+  }
+  if (made_change) {
+    mod.CopyOnWrite()->functions = std::move(functions);
+  }
+  return mod;
+}
 }  // namespace
 
 Stmt ConvertSSA(Stmt stmt) {
diff --git a/src/s_tir/transform/ir_utils.h b/src/s_tir/transform/ir_utils.h
index b23d30eb5b..9eddc15957 100644
--- a/src/s_tir/transform/ir_utils.h
+++ b/src/s_tir/transform/ir_utils.h
@@ -21,13 +21,12 @@
 #define TVM_S_TIR_TRANSFORM_IR_UTILS_H_
 
 #include <tvm/ffi/container/tuple.h>
+#include <tvm/ir/module.h>
 #include <tvm/s_tir/stmt.h>
 
 #include <string>
 #include <unordered_map>
 
-#include "../../tirx/transform/ir_utils.h"
-
 namespace tvm {
 namespace s_tir {
 
diff --git a/src/s_tir/transform/lower_opaque_block.cc 
b/src/s_tir/transform/lower_opaque_block.cc
index 1ae256fb15..a9e90650f3 100644
--- a/src/s_tir/transform/lower_opaque_block.cc
+++ b/src/s_tir/transform/lower_opaque_block.cc
@@ -23,6 +23,7 @@
 
 #include <tvm/ffi/cast.h>
 #include <tvm/ffi/reflection/registry.h>
+#include <tvm/ir/prim/op.h>
 #include <tvm/ir/scope_stack.h>
 #include <tvm/s_tir/stmt.h>
 #include <tvm/s_tir/stmt_functor.h>
diff --git a/src/tirx/transform/ir_utils.cc b/src/tirx/transform/ir_utils.cc
index 536eb6da8e..6aa37aa957 100644
--- a/src/tirx/transform/ir_utils.cc
+++ b/src/tirx/transform/ir_utils.cc
@@ -43,83 +43,86 @@ namespace tvm {
 namespace tirx {
 using namespace tvm::prim;
 
-Function IRConvertSSA::VisitFunction(Function func) {
-  std::unordered_set<const VarNode*> parameter_symbols;
-  // Define explicit parameters before the symbolic values in their types.
-  for (const Var& param : func->params) {
-    DefineVar(param);
-    parameter_symbols.insert(param.get());
-  }
-  for (const Var& param : func->params) {
-    ffi::StructuralWalk<ffi::WalkOrder::kPostOrder>(
-        param->ty, [&](const Var& var) -> ffi::Expected<ffi::WalkResult> {
-          if (!parameter_symbols.count(var.get())) {
-            parameter_symbols.insert(var.get());
-            DefineVar(var);
-          }
-          return ffi::WalkResult::Advance();
-        });
-  }
-  auto params = func->params.Map([&](const Var& var) {
-    Var mapped = GetRemappedVar(var);
-    Type type = Mutate(var->ty, InplaceMode::kDisallow)
-                    .as_or_throw<UnchangedOr<Type>>()
-                    .ValueOrUnchanged(var->ty);
-    if (!type.same_as(mapped->ty)) {
-      mapped = mapped.CopyWithType(type);
-      PushVarRemap(var, mapped);
-    }
-    return mapped;
-  });
+namespace {
+class IRConvertSSA final : public StmtExprMutator {
+ public:
+  TVM_DEFINE_OBJECT_FUNCTOR_DEFAULT_CONSTRUCTOR(IRConvertSSA, StmtExprMutator)
+  using StmtExprMutator::Mutate;
+  using StmtExprMutator::Mutate_;
+  Function VisitFunction(Function func);
+  IRModule VisitIRModule(IRModule mod);
 
-  auto attrs = [&]() -> DictAttrs {
-    ffi::Map<ffi::String, ffi::Any> dict;
-    bool made_change = false;
+ private:
+  UnchangedOr<Expr> Mutate_(const VarNode* op, InplaceMode inplace_mode) final;
+  Var DefineVar(Var var);
+  ffi::Optional<Var> GetVarRemap(const Var& var);
+  UnchangedOr<Stmt> Mutate_(const BindNode* op, InplaceMode inplace_mode) 
final;
+  UnchangedOr<Stmt> Mutate_(const IfNode* op, InplaceMode inplace_mode) final;
+  UnchangedOr<Stmt> Mutate_(const ForNode* op, InplaceMode inplace_mode) final;
+  UnchangedOr<Stmt> Mutate_(const WhileNode* op, InplaceMode inplace_mode) 
final;
+  UnchangedOr<Stmt> Mutate_(const RegionStmtNode* op, InplaceMode 
inplace_mode) final;
+  UnchangedOr<PrimExpr> Mutate_(const prim::LetNode* op, InplaceMode 
inplace_mode) final;
+  static Var MakeNewVar(const Var& old_var);
+  void SetVarRemap(const Var& old_var, const Var& new_var);
 
-    for (const auto& [key, old_value] : func->attrs->dict) {
-      auto value = old_value;
-      if (auto expr = value.as<PrimExpr>()) {
-        value = Mutate(expr.value(), 
InplaceMode::kDisallow).ValueOrUnchanged(expr.value());
-      } else if (auto* stmt = value.as<StmtNode>()) {
-        value = Mutate(ffi::GetRef<Stmt>(stmt), InplaceMode::kDisallow)
-                    .ValueOrUnchanged(ffi::GetRef<Stmt>(stmt));
-      }
+  // Lexical visibility ends with each scope, but definitions stay unique 
across the module.
+  ScopeStack<std::unordered_map<const VarNode*, Var>> scope_;
+  std::unordered_set<const VarNode*> defined_;
+};
 
-      made_change = made_change || !value.same_as(old_value);
-      dict.Set(key, value);
+Function IRConvertSSA::VisitFunction(Function func) {
+  return scope_.WithNewScope([&]() -> Function {
+    std::unordered_set<const VarNode*> parameter_symbols;
+    // Define explicit parameters before the symbolic values in their types.
+    for (const Var& param : func->params) {
+      DefineVar(param);
+      parameter_symbols.insert(param.get());
     }
-
-    if (made_change) {
-      return DictAttrs(dict);
-    } else {
-      return func->attrs;
+    for (const Var& param : func->params) {
+      ffi::StructuralWalk<ffi::WalkOrder::kPostOrder>(
+          param->ty, [&](const Var& var) -> ffi::Expected<ffi::WalkResult> {
+            if (!parameter_symbols.count(var.get())) {
+              parameter_symbols.insert(var.get());
+              DefineVar(var);
+            }
+            return ffi::WalkResult::Advance();
+          });
     }
-  }();
+    auto params = func->params.Map([&](const Var& var) {
+      Var mapped = GetVarRemap(var).value_or(var);
+      Type type = Mutate(var->ty, InplaceMode::kDisallow)
+                      .as_or_throw<UnchangedOr<Type>>()
+                      .ValueOrUnchanged(var->ty);
+      if (!type.same_as(mapped->ty)) {
+        mapped = mapped.CopyWithType(type);
+        SetVarRemap(var, mapped);
+      }
+      return mapped;
+    });
 
-  auto body_result = Mutate(func->body, InplaceMode::kDisallow);
-  bool body_unchanged = body_result.UnchangedOrSameAs(func->body);
-  auto body = std::move(body_result).ValueOrUnchanged(func->body);
+    auto body_result = Mutate(func->body, InplaceMode::kDisallow);
+    bool body_unchanged = body_result.UnchangedOrSameAs(func->body);
+    auto body = std::move(body_result).ValueOrUnchanged(func->body);
 
-  // If anything changed, update the returned function
-  if (!params.same_as(func->params) || !attrs.same_as(func->attrs) || 
!body_unchanged) {
-    func = Function(params, body, func->ret_type, attrs);
-  }
-
-  // Pop function-scope remaps in reverse order
-  PopAllRemapsInCurrentScope();
-  return func;
+    if (!params.same_as(func->params) || !body_unchanged) {
+      auto* node = func.CopyOnWrite();
+      node->params = std::move(params);
+      node->body = std::move(body);
+      node->ty = node->func_type_annotation();
+    }
+    return func;
+  });
 }
 
 UnchangedOr<Expr> IRConvertSSA::Mutate_(const VarNode* op, InplaceMode 
inplace_mode) {
   Var var = ffi::GetRef<Var>(op);
   if (def_region_kind() != kTVMFFIDefRegionKindNone) {
     if (def_region_kind() == kTVMFFIDefRegionKindPattern) {
-      Var mapped = GetRemappedVar(var);
-      if (!mapped.same_as(var) || defined_.count(var.get())) return mapped;
+      if (auto mapped = GetVarRemap(var)) return mapped.value();
     }
     return DefineVar(var);
   }
-  Var mapped = GetRemappedVar(var);
+  Var mapped = GetVarRemap(var).value_or(var);
   if (!mapped.same_as(var)) return mapped;
   return ffi::Unchanged();
 }
@@ -133,10 +136,6 @@ UnchangedOr<PrimExpr> IRConvertSSA::Mutate_(const 
prim::LetNode* op, InplaceMode
   return result;
 }
 
-Stmt IRConvertSSA::WithScope(const std::function<Stmt()>& body) {
-  return scope_.WithNewScope(body);
-}
-
 Var IRConvertSSA::DefineVar(Var var) {
   Type type = WithDefRegionKind(kTVMFFIDefRegionKindNone, [&] {
     return Mutate(var->ty, InplaceMode::kDisallow)
@@ -146,17 +145,15 @@ Var IRConvertSSA::DefineVar(Var var) {
   Var result = defined_.count(var.get()) ? MakeNewVar(var) : var;
   defined_.insert(var.get());
   if (!type.same_as(result->ty)) result = result.CopyWithType(type);
-  if (!result.same_as(var)) PushVarRemap(var, result);
+  SetVarRemap(var, result);
   return result;
 }
 
-Var IRConvertSSA::GetRemappedVar(Var var) {
-  if (auto it = scoped_var_remap_.find(var.get());
-      it != scoped_var_remap_.end() && it->second.size()) {
-    return it->second.back();
-  } else {
-    return var;
+ffi::Optional<Var> IRConvertSSA::GetVarRemap(const Var& var) {
+  for (auto it = scope_.rbegin(); it != scope_.rend(); ++it) {
+    if (auto found = it->find(var.get()); found != it->end()) return 
found->second;
   }
+  return std::nullopt;
 }
 
 UnchangedOr<Stmt> IRConvertSSA::Mutate_(const BindNode* op, InplaceMode 
inplace_mode) {
@@ -165,7 +162,7 @@ UnchangedOr<Stmt> IRConvertSSA::Mutate_(const BindNode* op, 
InplaceMode inplace_
   // The ordinary Bind path rewrites the RHS before defining the Var and 
propagates its type.
   Stmt stmt = StmtExprMutator::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<Stmt>(op));
   Var bound_var = stmt.as<BindNode>()->var;
-  if (!bound_var.same_as(GetRemappedVar(var))) PushVarRemap(var, bound_var);
+  if (!bound_var.same_as(GetVarRemap(var).value_or(var))) SetVarRemap(var, 
bound_var);
   VarRemapSet(var, previous_remap);
   return stmt;
 }
@@ -196,7 +193,7 @@ UnchangedOr<Stmt> IRConvertSSA::Mutate_(const ForNode* op, 
InplaceMode inplace_m
   if (defined_.count(v.get())) {
     return scope_.WithNewScope([&]() -> Stmt {
       Var new_var = MakeNewVar(v);
-      PushVarRemap(v, new_var);
+      SetVarRemap(v, new_var);
       Stmt stmt =
           StmtExprMutator::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<Stmt>(op));
       auto n = ffi::make_object<ForNode>(*stmt.as<ForNode>());
@@ -206,6 +203,7 @@ UnchangedOr<Stmt> IRConvertSSA::Mutate_(const ForNode* op, 
InplaceMode inplace_m
   } else {
     defined_.insert(v.get());
     return scope_.WithNewScope([&]() -> Stmt {
+      SetVarRemap(v, v);
       return StmtExprMutator::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<Stmt>(op));
     });
   }
@@ -239,24 +237,8 @@ Var IRConvertSSA::MakeNewVar(const Var& old_var) {
   return Var(old_var->name, old_var->ty, old_var->loc);
 }
 
-void IRConvertSSA::PushVarRemap(const Var& old_var, const Var& new_var) {
-  scoped_var_remap_[old_var.get()].push_back(new_var);
-  auto& level = scope_.Current();
-  level.parent = this;
-  level.push_back({old_var, new_var});
-}
-
-void IRConvertSSA::PopAllRemapsInCurrentScope() {
-  auto& current = scope_.Current();
-  while (current.size()) {
-    auto& remap = current.back();
-    scoped_var_remap_[remap.old_var.get()].pop_back();
-    current.pop_back();
-  }
-}
-
-Stmt ConvertSSA(Stmt stmt) {
-  return ffi::make_object<IRConvertSSA>()->Mutate(stmt, 
InplaceMode::kAllow).ValueOrUnchanged(stmt);
+void IRConvertSSA::SetVarRemap(const Var& old_var, const Var& new_var) {
+  scope_.Current().insert_or_assign(old_var.get(), new_var);
 }
 
 IRModule IRConvertSSA::VisitIRModule(IRModule mod) {
@@ -277,6 +259,11 @@ IRModule IRConvertSSA::VisitIRModule(IRModule mod) {
   }
   return mod;
 }
+}  // namespace
+
+Stmt ConvertSSA(Stmt stmt) {
+  return ffi::make_object<IRConvertSSA>()->Mutate(stmt, 
InplaceMode::kAllow).ValueOrUnchanged(stmt);
+}
 
 namespace transform {
 Pass ConvertSSA() {
diff --git a/src/tirx/transform/ir_utils.h b/src/tirx/transform/ir_utils.h
index 614fe5e7e9..63acf59af9 100644
--- a/src/tirx/transform/ir_utils.h
+++ b/src/tirx/transform/ir_utils.h
@@ -27,7 +27,6 @@
 #include <tvm/ir/function.h>
 #include <tvm/ir/prim/expr.h>
 #include <tvm/ir/prim/op.h>
-#include <tvm/ir/scope_stack.h>
 #include <tvm/ir/with_context.h>
 #include <tvm/runtime/device_api.h>
 #include <tvm/sym/int_set.h>
@@ -35,13 +34,11 @@
 #include <tvm/tirx/layout.h>
 #include <tvm/tirx/op/abi.h>
 #include <tvm/tirx/op/memory.h>
-#include <tvm/tirx/stmt_functor.h>
 
 #include <functional>
 #include <optional>
 #include <string>
 #include <unordered_map>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
@@ -92,88 +89,6 @@ inline int GetTempAllocaAlignment(const PrimType& type, 
int64_t const_size) {
  */
 Stmt ConvertSSA(Stmt stmt);
 
-/*! \brief Shared SSA renaming algorithm; dialects extend statement dispatch 
explicitly. */
-class IRConvertSSA : public StmtExprMutator {
- public:
-  TVM_DEFINE_OBJECT_FUNCTOR_DEFAULT_CONSTRUCTOR(IRConvertSSA, StmtExprMutator)
-  using StmtExprMutator::Mutate;
-  using StmtExprMutator::Mutate_;
-  Function VisitFunction(Function func);
-  IRModule VisitIRModule(IRModule mod);
-
- protected:
-  explicit IRConvertSSA(const VTable* table) : StmtExprMutator(table) {}
-  UnchangedOr<Expr> Mutate_(const VarNode* op, InplaceMode inplace_mode) final;
-  Stmt WithScope(const std::function<Stmt()>& body);
-  Var DefineVar(Var var);
-  Var GetRemappedVar(Var var);
-  UnchangedOr<Stmt> Mutate_(const BindNode* op, InplaceMode inplace_mode) 
final;
-  UnchangedOr<Stmt> Mutate_(const IfNode* op, InplaceMode inplace_mode) final;
-  UnchangedOr<Stmt> Mutate_(const ForNode* op, InplaceMode inplace_mode) final;
-  UnchangedOr<Stmt> Mutate_(const WhileNode* op, InplaceMode inplace_mode) 
final;
-  UnchangedOr<Stmt> Mutate_(const RegionStmtNode* op, InplaceMode 
inplace_mode) final;
-  UnchangedOr<PrimExpr> Mutate_(const prim::LetNode* op, InplaceMode 
inplace_mode) final;
-  static Var MakeNewVar(const Var& old_var);
-  void PushVarRemap(const Var& old_var, const Var& new_var);
-  void PopAllRemapsInCurrentScope();
-
- private:
-  struct VarRemap {
-    Var old_var;
-    Var new_var;
-  };
-  /*! \brief Scope stack: each scope level holds the remaps introduced in that 
scope.
-   *
-   * When a body-carrying statement (For, Allocate, or a dialect statement) 
calls
-   * scope_.WithNewScope([&]{...}), a new scope level is pushed.
-   * Bind statements push their remaps to the current scope.
-   * On scope exit, the destructor of std::vector<VarRemap> triggers,
-   * and we undo all remaps in that level.
-   *
-   * Note: ScopeStack<T>::WithNewScope calls T's destructor on exit.
-   * std::vector's destructor destroys elements but does NOT call custom
-   * cleanup.  So we wrap the vector in ScopeLevel which handles cleanup.
-   */
-  struct ScopeLevel {
-    std::vector<VarRemap> remaps;
-    IRConvertSSA* parent{nullptr};
-
-    void push_back(VarRemap remap) { remaps.push_back(std::move(remap)); }
-    size_t size() const { return remaps.size(); }
-    VarRemap& back() { return remaps.back(); }
-    void pop_back() { remaps.pop_back(); }
-
-    void Clear() {
-      if (!parent) return;
-      while (!remaps.empty()) {
-        parent->scoped_var_remap_[remaps.back().old_var.get()].pop_back();
-        remaps.pop_back();
-      }
-    }
-    ~ScopeLevel() { Clear(); }
-    ScopeLevel() = default;
-    ScopeLevel(const ScopeLevel&) = delete;
-    ScopeLevel& operator=(const ScopeLevel&) = delete;
-    ScopeLevel(ScopeLevel&& other) noexcept
-        : remaps(std::move(other.remaps)), parent(other.parent) {
-      other.parent = nullptr;
-    }
-    ScopeLevel& operator=(ScopeLevel&& other) noexcept {
-      if (this != &other) {
-        Clear();
-        remaps = std::move(other.remaps);
-        parent = other.parent;
-        other.parent = nullptr;
-      }
-      return *this;
-    }
-  };
-
-  std::unordered_map<const VarNode*, std::vector<Var>> scoped_var_remap_;
-  std::unordered_set<const VarNode*> defined_;
-  ScopeStack<ScopeLevel> scope_;
-};
-
 }  // namespace tirx
 }  // namespace tvm
 #endif  // TVM_TIR_TRANSFORM_IR_UTILS_H_

Reply via email to