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 545bd7b3c7 Phase out Relax-specific Id aliases (#19959)
545bd7b3c7 is described below

commit 545bd7b3c727a3bd64e9de26df0c13467d13bc15
Author: Tianqi Chen <[email protected]>
AuthorDate: Tue Jul 7 06:28:57 2026 +0800

    Phase out Relax-specific Id aliases (#19959)
    
    Remove the Relax-specific Id indirection and use Var/DataflowVar object
    identity directly.
    
    Type-changing rewrites now remap definitions, uses, and binding lookups
    coherently while preserving reflection, serialization, and the
    DataflowVar subtype. Existing tests are adjusted for the API change; no
    new test files or test cases are added.
    
    Validation: the compiler and C++ tests build successfully; focused C++
    coverage passes 4/4; the affected existing Python matrices pass 549
    tests with 2 expected xfails; source censuses, diff checks, and
    applicable hooks pass.
---
 include/tvm/relax/expr.h                           | 86 +++-------------------
 include/tvm/relax/expr_functor.h                   |  2 +-
 python/tvm/relax/__init__.py                       |  1 -
 python/tvm/relax/expr.py                           | 44 ++---------
 python/tvm/relax/expr_functor.py                   | 21 +++---
 .../tvm/relax/transform/lazy_transform_params.py   |  2 +-
 src/relax/analysis/var2value.cc                    |  4 +-
 src/relax/backend/contrib/codegen_c/codegen_c.h    |  2 +-
 .../backend/contrib/codegen_json/codegen_json.h    |  2 +-
 src/relax/backend/contrib/cutlass/codegen.cc       |  2 +-
 src/relax/backend/vm/codegen_vm.cc                 |  2 +-
 src/relax/backend/vm/codegen_vm_tir.cc             |  2 +-
 src/relax/backend/vm/vm_shape_lower.cc             |  2 +-
 src/relax/distributed/transform/lower_distir.cc    | 10 +--
 .../distributed/transform/propagate_sharding.cc    | 17 +++--
 src/relax/ir/binding_rewrite.cc                    |  2 +-
 src/relax/ir/block_builder.cc                      | 16 ++--
 src/relax/ir/dataflow_matcher.cc                   |  2 +-
 src/relax/ir/expr.cc                               | 54 +++-----------
 src/relax/ir/expr_functor.cc                       | 32 +++++---
 src/relax/ir/py_expr_functor.cc                    |  6 +-
 src/relax/ir/transform.cc                          |  8 +-
 src/relax/script/builder/frame.cc                  | 10 +--
 src/relax/script/builder/ir.cc                     | 12 +--
 src/relax/script/builder/utils.h                   |  6 +-
 src/relax/script/printer/utils.h                   |  2 +-
 src/relax/training/utils.cc                        |  6 +-
 src/relax/transform/bind_params.cc                 |  4 +-
 src/relax/transform/bundle_model_params.cc         |  2 +-
 src/relax/transform/canonicalize_bindings.cc       | 36 ++++-----
 src/relax/transform/convert_layout.cc              |  2 +-
 src/relax/transform/eliminate_common_subexpr.cc    |  2 +-
 src/relax/transform/expand_tuple_arguments.cc      |  2 +-
 src/relax/transform/fuse_ops.cc                    | 18 ++---
 src/relax/transform/fuse_tir.cc                    |  6 +-
 src/relax/transform/gradient.cc                    | 55 +++++++-------
 src/relax/transform/gradient_simplifier.cc         |  2 +-
 src/relax/transform/inline_functions.cc            |  2 +-
 src/relax/transform/lambda_lift.cc                 | 11 +--
 src/relax/transform/lazy_transform_params.cc       |  6 +-
 src/relax/transform/lift_transform_params.cc       |  4 +-
 src/relax/transform/realize_vdevice.cc             |  4 +-
 src/relax/transform/rewrite_cuda_graph.cc          | 13 ++--
 .../specialize_primfunc_based_on_callsite.cc       |  2 +-
 src/relax/transform/to_mixed_precision.cc          | 14 ++--
 src/relax/transform/to_non_dataflow.cc             |  4 +-
 src/relax/transform/update_param_type.cc           |  2 +-
 src/relax/transform/utils.h                        | 14 ++--
 src/relax/utils.cc                                 |  2 +-
 tests/python/relax/test_expr_functor.py            |  4 +-
 .../relax/test_transform_canonicalize_bindings.py  |  4 +-
 51 files changed, 229 insertions(+), 341 deletions(-)

diff --git a/include/tvm/relax/expr.h b/include/tvm/relax/expr.h
index 51b4ed43bd..b8ed8cfc23 100644
--- a/include/tvm/relax/expr.h
+++ b/include/tvm/relax/expr.h
@@ -36,44 +36,6 @@
 namespace tvm {
 namespace relax {
 
-/*!
- * \brief The unique identifier of variables.
- *
- * Id is like name to the variables,
- * except that id is unique for each Var.
- *
- * \note Do not create Id directly, they are created in Var.
- */
-class IdNode : public ffi::Object {
- public:
-  /*!
-   * \brief The name of the variable,
-   *  this only acts as a hint to the user,
-   *  and is not used for equality.
-   */
-  ffi::String name_hint;
-
-  static void RegisterReflection() {
-    namespace refl = tvm::ffi::reflection;
-    refl::ObjectDef<IdNode>().def_ro("name_hint", &IdNode::name_hint,
-                                     refl::AttachFieldFlag::SEqHashIgnore());
-  }
-
-  static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = 
kTVMFFISEqHashKindFreeVar;
-  TVM_FFI_DECLARE_OBJECT_INFO_FINAL("relax.Id", IdNode, ffi::Object);
-};
-
-class Id : public ffi::ObjectRef {
- public:
-  /*!
-   * \brief The constructor
-   * \param name_hint The name of the variable.
-   */
-  TVM_DLL explicit Id(ffi::String name_hint);
-
-  TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(Id, ffi::ObjectRef, IdNode);
-};
-
 /*! \brief Tuple container */
 class TupleNode : public ExprNode {
  public:
@@ -173,48 +135,28 @@ class ShapeExpr : public Expr {
 /*! \brief The variable class for all Relax bindings. */
 class VarNode : public ExprNode {
  public:
-  /*! \brief The identifier of the variable, which is used for comparing 
stable equality across
-   * transformations. */
-  Id vid;
-
-  /*! \return The name hint of the variable */
-  const ffi::String& name_hint() const { return vid->name_hint; }
+  /*!
+   * \brief The hint to the variable name.
+   * \note Each variable is uniquely identified by its address.
+   */
+  ffi::String name_hint;
 
   static void RegisterReflection() {
     namespace refl = tvm::ffi::reflection;
-    refl::ObjectDef<VarNode>().def_ro("vid", &VarNode::vid);
-    // customize structural equal and hash to include ty
-    refl::TypeAttrDef<VarNode>()
-        .def("__s_equal__", &VarNode::SEqual)
-        .def("__s_hash__", &VarNode::SHash);
+    refl::ObjectDef<VarNode>().def_ro("name_hint", &VarNode::name_hint,
+                                      refl::AttachFieldFlag::SEqHashIgnore());
   }
 
-  bool SEqual(const VarNode* other,
-              ffi::TypedFunction<bool(AnyView, AnyView, bool, AnyView)> equal) 
const {
-    return equal(vid, other->vid, false, "vid") && equal(ty, other->ty, false, 
"ty");
-  }
-
-  int64_t SHash(int64_t init_hash, ffi::TypedFunction<int64_t(AnyView, 
int64_t, bool)> hash) const {
-    int64_t hash_value = init_hash;
-    hash_value = hash(vid, hash_value, false);
-    hash_value = hash(ty, hash_value, false);
-    return hash_value;
-  }
-
-  static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = 
kTVMFFISEqHashKindDAGNode;
+  static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = 
kTVMFFISEqHashKindFreeVar;
   static constexpr const uint32_t _type_child_slots = 1;
   TVM_FFI_DECLARE_OBJECT_INFO("relax.expr.Var", VarNode, ExprNode);
 };
 
 class Var : public Expr {
  public:
-  TVM_DLL explicit Var(ffi::String name_hint, ffi::Optional<Type> 
ty_annotation, Span span = Span())
-      : Var(Id(name_hint), ty_annotation, span) {}
-
-  TVM_DLL explicit Var(Id vid, ffi::Optional<Type> ty_annotation, Span span = 
Span());
+  TVM_DLL explicit Var(ffi::String name_hint, ffi::Optional<Type> 
ty_annotation,
+                       Span span = Span());
   TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(Var, Expr, VarNode);
-
-  VarNode* CopyOnWrite();
 };
 
 /*! \brief A sub-type of the variable node used to mark dataflow variables from
@@ -227,20 +169,16 @@ class DataflowVarNode : public VarNode {
     refl::ObjectDef<DataflowVarNode>();
   }
 
-  static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = 
kTVMFFISEqHashKindDAGNode;
+  static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = 
kTVMFFISEqHashKindFreeVar;
   TVM_FFI_DECLARE_OBJECT_INFO_FINAL("relax.expr.DataflowVar", DataflowVarNode, 
VarNode);
 };
 
 class DataflowVar : public Var {
  public:
   TVM_DLL explicit DataflowVar(ffi::String name_hint, ffi::Optional<Type> 
ty_annotation,
-                               Span span = Span())
-      : DataflowVar(Id(name_hint), ty_annotation, span) {}
-
-  TVM_DLL explicit DataflowVar(Id vid, ffi::Optional<Type> ty_annotation, Span 
span = Span());
+                               Span span = Span());
 
   TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(DataflowVar, Var, 
DataflowVarNode);
-  TVM_DEFINE_OBJECT_REF_COW_METHOD(DataflowVarNode);
 };
 
 /*!
diff --git a/include/tvm/relax/expr_functor.h b/include/tvm/relax/expr_functor.h
index 05261d8c5e..7f1388803d 100644
--- a/include/tvm/relax/expr_functor.h
+++ b/include/tvm/relax/expr_functor.h
@@ -559,7 +559,7 @@ class ExprMutator : public ExprMutatorBase {
   BlockBuilder builder_;
 
   /*! \brief Remap a var to a new var in use-site. */
-  std::unordered_map<Id, Var, ffi::ObjectPtrHash, ffi::ObjectPtrEqual> 
var_remap_;
+  std::unordered_map<Var, Var, ffi::ObjectPtrHash, ffi::ObjectPtrEqual> 
var_remap_;
 
  private:
   using TSelf = ExprMutator;
diff --git a/python/tvm/relax/__init__.py b/python/tvm/relax/__init__.py
index 59f8b81695..c28ca1b2c0 100644
--- a/python/tvm/relax/__init__.py
+++ b/python/tvm/relax/__init__.py
@@ -26,7 +26,6 @@ from tvm.ir import Call
 from .expr import (
     Expr,
     Span,
-    Id,
     GlobalVar,
     Var,
     DataflowVar,
diff --git a/python/tvm/relax/expr.py b/python/tvm/relax/expr.py
index 18f7d80312..c92a08e868 100644
--- a/python/tvm/relax/expr.py
+++ b/python/tvm/relax/expr.py
@@ -30,7 +30,6 @@ import tvm.ir
 import tvm.relax
 import tvm.runtime
 from tvm import DataType
-from tvm.runtime import Object
 
 from ..ir import BaseFunc, Node, Span
 from ..runtime import Scriptable
@@ -75,18 +74,6 @@ def prim_value(value: Expr | int | float, dtype: str | None 
= None) -> Expr:
     raise TypeError(f"Cannot convert {value} with type {type(value)} to 
`Expr`")
 
 
-@tvm_ffi.register_object("relax.Id")
-class Id(Object):
-    """Unique identifier(name) used in Var.
-    Guaranteed to be stable across all passes.
-    """
-
-    name_hint: str
-
-    def __init__(self):
-        raise RuntimeError("Cannot directly construct Id")
-
-
 def _relax_type_is_base_of(self: Type, derived: Type) -> bool:
     """Check if this Relax type is a base of another Relax type."""
 
@@ -429,7 +416,7 @@ class Var(ExprWithOp):
 
     Parameters
     ----------
-    name_hint: str | Id
+    name_hint: str
         The name hint of the variable.
 
     ty: Optional[Type]
@@ -439,12 +426,12 @@ class Var(ExprWithOp):
         Span that points to original source code
     """
 
-    vid: Id
+    name_hint: str
     span: Span | None
 
     def __init__(
         self,
-        name_hint: str | Id,
+        name_hint: str,
         ty: Type | None = None,
         span: Span | None = None,
     ) -> None:
@@ -457,18 +444,12 @@ class Var(ExprWithOp):
                     "use relax.TensorType(shape, dtype)."
                 )
         self.__init_handle_by_constructor__(
-            _ffi_api.Var if isinstance(name_hint, str) else 
_ffi_api.VarFromId,  # type: ignore
+            _ffi_api.Var,  # type: ignore
             name_hint,
             ty,
             span,
         )
 
-    @property
-    def name_hint(self) -> str:
-        """Get name hint of the current var."""
-        name = str(self.vid.name_hint)
-        return name
-
 
 @tvm_ffi.register_object("relax.expr.DataflowVar")
 class DataflowVar(Var):
@@ -478,7 +459,7 @@ class DataflowVar(Var):
 
     Parameters
     ----------
-    name_hint: str | Id
+    name_hint: str
         The name hint of the variable.
 
     ty: Optional[Type]
@@ -488,12 +469,12 @@ class DataflowVar(Var):
         Span that points to original source code
     """
 
-    vid: Id
+    name_hint: str
     span: Span | None
 
     def __init__(
         self,
-        name_hint: str | Id,
+        name_hint: str,
         ty: Type | None = None,
         span: Span | None = None,
     ) -> None:
@@ -507,16 +488,7 @@ class DataflowVar(Var):
                     "use relax.TensorType(shape, dtype)."
                 )
 
-        self.__init_handle_by_constructor__(
-            (
-                _ffi_api.DataflowVar  # type: ignore
-                if isinstance(name_hint, str)
-                else _ffi_api.DataflowVarFromId
-            ),  # type: ignore
-            name_hint,
-            ty,
-            span,
-        )
+        self.__init_handle_by_constructor__(_ffi_api.DataflowVar, name_hint, 
ty, span)  # type: ignore
 
 
 @tvm_ffi.register_object("relax.expr.StringImm")
diff --git a/python/tvm/relax/expr_functor.py b/python/tvm/relax/expr_functor.py
index 9f0e398424..39da280eee 100644
--- a/python/tvm/relax/expr_functor.py
+++ b/python/tvm/relax/expr_functor.py
@@ -40,7 +40,6 @@ from .expr import (
     ExternFunc,
     Function,
     GlobalVar,
-    Id,
     If,
     MatchCast,
     SeqExpr,
@@ -1453,26 +1452,28 @@ class PyExprMutator:
         """
         return _ffi_api.PyExprMutatorVisitExprPostOrder(self._outer(), expr)  
# type: ignore
 
-    def set_var_remap(self, vid: Id, var: Var) -> None:
+    def set_var_remap(self, old_var: Var, new_var: Var) -> None:
         """Remap a var to a new var in use-site.
 
         Parameters
         ----------
-        vid : Id
-            The vid of the old var.
-        var : Var
+        old_var : Var
+            The old var.
+        new_var : Var
             The new var.
         """
         # Using self._outer() to ref _PyExprMutator
-        return _ffi_api.PyExprMutatorSetVarRemap(self._outer(), vid, var)  # 
type: ignore
+        return _ffi_api.PyExprMutatorSetVarRemap(  # type: ignore
+            self._outer(), old_var, new_var
+        )
 
-    def get_var_remap(self, vid: Id) -> Var:
+    def get_var_remap(self, var: Var) -> Var:
         """Remap a var to a new var in use-site.
 
         Parameters
         ----------
-        vid : Id
-            The vid of the old var
+        var : Var
+            The old var.
 
         Returns
         -------
@@ -1480,7 +1481,7 @@ class PyExprMutator:
             The remapped var.
         """
         # Using self._outer() to ref _PyExprMutator
-        return _ffi_api.PyExprMutatorGetVarRemap(self._outer(), vid)  # type: 
ignore
+        return _ffi_api.PyExprMutatorGetVarRemap(self._outer(), var)  # type: 
ignore
 
     def visit_with_new_scope(self, expr: Expr) -> Expr:
         """Rewrite the expr with a new scope, used in a Function's body and 
the branches of If.
diff --git a/python/tvm/relax/transform/lazy_transform_params.py 
b/python/tvm/relax/transform/lazy_transform_params.py
index 4eb3b39619..2711e3287d 100644
--- a/python/tvm/relax/transform/lazy_transform_params.py
+++ b/python/tvm/relax/transform/lazy_transform_params.py
@@ -312,7 +312,7 @@ class LazyOutputMutator(PyExprMutator):
         if binding.var == self.func_creator.out_tuple_var:
             # The function after rewriting returns a empty tuple.
             func_output = self.builder_.emit(relax.Tuple([]))
-            self.set_var_remap(binding.var.vid, func_output)
+            self.set_var_remap(binding.var, func_output)
             return
 
         super().visit_var_binding_(binding)
diff --git a/src/relax/analysis/var2value.cc b/src/relax/analysis/var2value.cc
index 3af778efe4..a5e30c8139 100644
--- a/src/relax/analysis/var2value.cc
+++ b/src/relax/analysis/var2value.cc
@@ -74,12 +74,12 @@ class Name2BindingAnalysis : public relax::ExprVisitor {
   // so we use standard container for internal usage.
   std::map<ffi::String, ffi::Array<Binding>> name2bindings_;
   void VisitBinding_(const VarBindingNode* binding) override {
-    const auto& vname = binding->var->name_hint();
+    const auto& vname = binding->var->name_hint;
     name2bindings_[vname].push_back(ffi::GetRef<VarBinding>(binding));
   }
 
   void VisitBinding_(const MatchCastNode* binding) override {
-    const auto& vname = binding->var->name_hint();
+    const auto& vname = binding->var->name_hint;
     name2bindings_[vname].push_back(ffi::GetRef<MatchCast>(binding));
   }
 };
diff --git a/src/relax/backend/contrib/codegen_c/codegen_c.h 
b/src/relax/backend/contrib/codegen_c/codegen_c.h
index b183fd85f9..58f76c92a8 100644
--- a/src/relax/backend/contrib/codegen_c/codegen_c.h
+++ b/src/relax/backend/contrib/codegen_c/codegen_c.h
@@ -282,7 +282,7 @@ class CodegenCBase {
 
     for (const auto& arg : args) {
       const auto& dtype_str = GetDtypeString(arg);
-      code_stream_ << dtype_str << "* " << arg->name_hint() << ", ";
+      code_stream_ << dtype_str << "* " << arg->name_hint << ", ";
     }
     for (size_t i = 0; i < outs.size() - 1; ++i) {
       code_stream_ << outs[i].dtype << "* out" << i << ", ";
diff --git a/src/relax/backend/contrib/codegen_json/codegen_json.h 
b/src/relax/backend/contrib/codegen_json/codegen_json.h
index 06c4733e70..7c9f1a1ea7 100644
--- a/src/relax/backend/contrib/codegen_json/codegen_json.h
+++ b/src/relax/backend/contrib/codegen_json/codegen_json.h
@@ -238,7 +238,7 @@ class JSONSerializer : public 
relax::MemoizedExprTranslator<NodeEntries> {
   void serialize(Function func) {
     // First we convert all the parameters into input nodes.
     for (const auto& param : func->params) {
-      auto node_ptr = std::make_shared<JSONGraphNode>(param->name_hint(), 
"input" /* op_type_ */);
+      auto node_ptr = std::make_shared<JSONGraphNode>(param->name_hint, 
"input" /* op_type_ */);
       memo_[param] = AddNode(node_ptr, param);
     }
     heads_ = VisitExpr(func->body);
diff --git a/src/relax/backend/contrib/cutlass/codegen.cc 
b/src/relax/backend/contrib/cutlass/codegen.cc
index 2894b75685..a299289266 100644
--- a/src/relax/backend/contrib/cutlass/codegen.cc
+++ b/src/relax/backend/contrib/cutlass/codegen.cc
@@ -157,7 +157,7 @@ class CodegenCutlass : public 
relax::MemoizedExprTranslator<OutputType>,
 
   void AddParm(Var param) {
     ext_func_args_.push_back(param);
-    auto v_name = name_sup_->FreshName(param->name_hint());
+    auto v_name = name_sup_->FreshName(param->name_hint);
     var_name_map_[param.get()] = v_name;
   }
 
diff --git a/src/relax/backend/vm/codegen_vm.cc 
b/src/relax/backend/vm/codegen_vm.cc
index 380671473d..c27a39279f 100644
--- a/src/relax/backend/vm/codegen_vm.cc
+++ b/src/relax/backend/vm/codegen_vm.cc
@@ -91,7 +91,7 @@ class CodeGenVM : public ExprFunctor<Instruction::Arg(const 
Expr&)> {
 
     ffi::Array<ffi::String> param_names;
     for (Var param : func->params) {
-      param_names.push_back(param->name_hint());
+      param_names.push_back(param->name_hint);
     }
 
     builder_->EmitFunction(gsymbol.value(), func->params.size(), param_names);
diff --git a/src/relax/backend/vm/codegen_vm_tir.cc 
b/src/relax/backend/vm/codegen_vm_tir.cc
index 439a855e59..ee515489da 100644
--- a/src/relax/backend/vm/codegen_vm_tir.cc
+++ b/src/relax/backend/vm/codegen_vm_tir.cc
@@ -171,7 +171,7 @@ class CodeGenVMTIR : public 
ExprFunctor<ffi::Optional<Expr>(const Expr&)> {
 
     ffi::Array<ffi::String> param_names;
     for (Var param : func->params) {
-      param_names.push_back(param->name_hint());
+      param_names.push_back(param->name_hint);
     }
     // declare this function.
     builder_->DeclareFunction(gsymbol.value(), 
vm::VMFuncInfo::FuncKind::kVMTIRFunc);
diff --git a/src/relax/backend/vm/vm_shape_lower.cc 
b/src/relax/backend/vm/vm_shape_lower.cc
index bc2f5d485c..3fb8ea0434 100644
--- a/src/relax/backend/vm/vm_shape_lower.cc
+++ b/src/relax/backend/vm/vm_shape_lower.cc
@@ -305,7 +305,7 @@ class VMShapeLowerMutator
         Type ty = GetType(func->params[i]);
         std::ostringstream err_ctx;
         err_ctx << "ErrorContext(fn=" << gvar->name_hint << ", loc=param[" << i
-                << "], param=" << func->params[i]->name_hint() << ", 
annotation=" << ty << ") ";
+                << "], param=" << func->params[i]->name_hint << ", 
annotation=" << ty << ") ";
         this->CheckMatchCast(ty, func->params[i], true, i >= num_input, 
err_ctx.str(),
                              &match_todos);
       }
diff --git a/src/relax/distributed/transform/lower_distir.cc 
b/src/relax/distributed/transform/lower_distir.cc
index 2f6b79ecb3..01522183bb 100644
--- a/src/relax/distributed/transform/lower_distir.cc
+++ b/src/relax/distributed/transform/lower_distir.cc
@@ -116,7 +116,7 @@ class DistIRSharder : public ExprMutator {
     Type old_ty = GetType(input);
     Type new_ty = ConvertType(old_ty, false);
     if (const auto* var = input.as<VarNode>()) {
-      Var new_param(var->name_hint(), new_ty);
+      Var new_param(var->name_hint, new_ty);
       return new_param;
     } else if (const auto* constant = input.as<ConstantNode>()) {
       for (const auto& spec : 
old_ty.as_or_throw<DTensorType>()->placement->dim_specs) {
@@ -137,7 +137,7 @@ class DistIRSharder : public ExprMutator {
     if (sharding_spec->kind == PlacementSpecKind::kReplica) {
       Var new_var = builder_->Emit(broadcast_from_worker0(new_expr));
       if (const auto* var = old_expr.as<VarNode>()) {
-        var_remap_[var->vid] = new_var;
+        var_remap_[ffi::GetRef<Var>(var)] = new_var;
       } else {
         tuple_getitem_remap_[old_expr.as_or_throw<TupleGetItem>()] = new_var;
       }
@@ -145,7 +145,7 @@ class DistIRSharder : public ExprMutator {
       Var scatter_var = builder_->Emit(
           scatter_from_worker0(new_expr, dtensor_ty->device_mesh->shape[0], 
sharding_spec->axis));
       if (const auto* var = old_expr.as<VarNode>()) {
-        var_remap_[var->vid] = scatter_var;
+        var_remap_[ffi::GetRef<Var>(var)] = scatter_var;
       } else {
         tuple_getitem_remap_[old_expr.as_or_throw<TupleGetItem>()] = 
scatter_var;
       }
@@ -174,7 +174,7 @@ class DistIRSharder : public ExprMutator {
     ffi::Array<Var> new_params;
     for (const Var& var : func->params) {
       Var new_param = ShardInputParamTensorAndConstant(var).as_or_throw<Var>();
-      var_remap_[var->vid] = new_param;
+      var_remap_[var] = new_param;
       new_params.push_back(new_param);
     }
     func_ = func;
@@ -186,7 +186,7 @@ class DistIRSharder : public ExprMutator {
 
   void VisitBinding_(const VarBindingNode* binding, const TupleGetItemNode* 
val) {
     if (tuple_getitem_remap_.count(ffi::GetRef<TupleGetItem>(val))) {
-      var_remap_[binding->var->vid] = 
tuple_getitem_remap_[ffi::GetRef<TupleGetItem>(val)];
+      var_remap_[binding->var] = 
tuple_getitem_remap_[ffi::GetRef<TupleGetItem>(val)];
     } else {
       ExprMutator::VisitBinding_(binding, val);
     }
diff --git a/src/relax/distributed/transform/propagate_sharding.cc 
b/src/relax/distributed/transform/propagate_sharding.cc
index 28db86b0a3..738d5dc0b2 100644
--- a/src/relax/distributed/transform/propagate_sharding.cc
+++ b/src/relax/distributed/transform/propagate_sharding.cc
@@ -285,7 +285,7 @@ class ShardingConflictHandler : public ExprVisitor {
 
       if (device_mesh.has_value()) {
         TVM_FFI_ICHECK(ffi::StructuralEqual()(device_mesh.value(), 
sharding_spec.first))
-            << "Sharding conflict detected for tensor " << var->name_hint()
+            << "Sharding conflict detected for tensor " << var->name_hint
             << ": Device Mesh mismatch"
             << ". Conflict Handling logic will be added in the future.";
       } else {
@@ -294,7 +294,7 @@ class ShardingConflictHandler : public ExprVisitor {
       if (i >= 0) {
         int sharding_dim = sharding_spec.second;
         TVM_FFI_ICHECK(sharded_mesh_dim.count(sharding_dim) == 0)
-            << "Sharding conflict detected for tensor " << var->name_hint()
+            << "Sharding conflict detected for tensor " << var->name_hint
             << ": Replicate sharding device mesh axis " << sharding_dim
             << ". Conflict Handling logic will be added in the future.";
         sharded_mesh_dim.insert(sharding_dim);
@@ -401,7 +401,7 @@ class DistributedIRBuilder : public ExprMutator {
     }
 
     if (const auto* var = tensor.as<VarNode>()) {
-      Var new_param(var->name_hint(), new_ty);
+      Var new_param(var->name_hint, new_ty);
       return new_param;
     } else if (const auto* constant = tensor.as<ConstantNode>()) {
       Constant new_constant(constant->data, new_ty);
@@ -566,7 +566,7 @@ class DistributedIRBuilder : public ExprMutator {
         new_value = InsertRedistribute(new_value, device_mesh, placements[0]);
       }
       if (const auto* var = new_value.as<VarNode>()) {
-        var_remap_[binding->var->vid] = ffi::GetRef<Var>(var);
+        var_remap_[binding->var] = ffi::GetRef<Var>(var);
       } else {
         ReEmitBinding(binding, builder_->Normalize(new_value));
       }
@@ -574,7 +574,7 @@ class DistributedIRBuilder : public ExprMutator {
       const auto* inferred_tuple_ty = new_call->ty.as<TupleTypeNode>();
       TVM_FFI_ICHECK(inferred_tuple_ty) << new_call;
       Var new_var = builder_->Emit(new_call);
-      var_remap_[binding->var->vid] = new_var;
+      var_remap_[binding->var] = new_var;
       for (int i = 0; i < static_cast<int>(inferred_tuple_ty->fields.size()); 
i++) {
         if (!ffi::StructuralEqual()(
                 
DTensorType(inferred_tuple_ty->fields[i].as_or_throw<DTensorType>()->tensor_ty,
@@ -590,16 +590,17 @@ class DistributedIRBuilder : public ExprMutator {
 
   void VisitBinding_(const VarBindingNode* binding, const TupleGetItemNode* 
val) {
     if (tuple_getitem_remap_.count(ffi::GetRef<TupleGetItem>(val))) {
-      var_remap_[binding->var->vid] = 
tuple_getitem_remap_[ffi::GetRef<TupleGetItem>(val)];
+      var_remap_[binding->var] = 
tuple_getitem_remap_[ffi::GetRef<TupleGetItem>(val)];
     } else {
       ExprMutator::VisitBinding_(binding, val);
     }
   }
 
   Expr VisitExpr_(const VarNode* var) final {
-    auto it = input_tensor_remap_.find(ffi::GetRef<Var>(var));
+    Var var_ref = ffi::GetRef<Var>(var);
+    auto it = input_tensor_remap_.find(var_ref);
     if (it != input_tensor_remap_.end()) {
-      var_remap_[var->vid] = (*it).second;
+      var_remap_[var_ref] = (*it).second;
     }
     return ExprMutator::VisitExpr_(var);
   }
diff --git a/src/relax/ir/binding_rewrite.cc b/src/relax/ir/binding_rewrite.cc
index af21c05673..44348e6cef 100644
--- a/src/relax/ir/binding_rewrite.cc
+++ b/src/relax/ir/binding_rewrite.cc
@@ -55,7 +55,7 @@ DataflowBlockRewrite::DataflowBlockRewrite(DataflowBlock dfb, 
Function root_fn)
   n->to_users_ = std::move(p.first);
   n->fn_outputs_ = std::move(p.second);
   n->name_supply_ = UniqueNameSupply(n->to_users_.begin(), n->to_users_.end(),
-                                     [](const auto& p) { return 
p.first->name_hint(); });
+                                     [](const auto& p) { return 
p.first->name_hint; });
 
   data_ = std::move(n);
 }
diff --git a/src/relax/ir/block_builder.cc b/src/relax/ir/block_builder.cc
index 8048cda6b2..60cefd081d 100644
--- a/src/relax/ir/block_builder.cc
+++ b/src/relax/ir/block_builder.cc
@@ -152,7 +152,7 @@ class BlockBuilderImpl : public BlockBuilderNode {
   // Scope management
   //-------------------------------
   ffi::Optional<Expr> LookupBinding(const Var& var) final {
-    auto it = binding_table_.find(var->vid);
+    auto it = binding_table_.find(var);
     if (it == binding_table_.end()) return std::nullopt;
     return it->second;
   }
@@ -276,7 +276,7 @@ class BlockBuilderImpl : public BlockBuilderNode {
       TVM_FFI_ICHECK(!var_binding->var->ty.IsMissing());
       TVM_FFI_ICHECK(!var_binding->value->ty.IsMissing());
       cur_frame->bindings.push_back(binding);
-      binding_table_[var_binding->var->vid] = var_binding->value;
+      binding_table_[var_binding->var] = var_binding->value;
     } else if (const auto* match_cast = binding.as<MatchCastNode>()) {
       if (!cur_frame->is_dataflow) {
         TVM_FFI_ICHECK(!match_cast->var.as<DataflowVarNode>())
@@ -342,7 +342,7 @@ class BlockBuilderImpl : public BlockBuilderNode {
   std::vector<ScopeFrame> scope_stack_;
 
   /*! \brief A binding table that maps var to value. */
-  std::unordered_map<Id, Expr, ffi::ObjectPtrHash, ffi::ObjectPtrEqual> 
binding_table_;
+  std::unordered_map<Var, Expr, ffi::ObjectPtrHash, ffi::ObjectPtrEqual> 
binding_table_;
 
   /*! \brief A unique name supply to get unique names for IR construction. */
   UniqueNameSupply name_supply_;
@@ -393,7 +393,7 @@ class BlockBuilderImpl : public BlockBuilderNode {
     CurrentBindingBlockFrame()->bindings.push_back(VarBinding(var, expr));
 
     // update the binding table
-    binding_table_[var->vid] = expr;
+    binding_table_[var] = expr;
 
     return var;
   }
@@ -408,9 +408,9 @@ class BlockBuilderImpl : public BlockBuilderNode {
     if (name_hint.empty()) {
       name_hint = is_dataflow ? "lv" : "gv";
     }
-    Id vid = Id(GetUniqueName(name_hint));
-    return is_dataflow ? DataflowVar(vid, /*ty_annotation=*/std::nullopt)
-                       : Var(vid, /*ty_annotation=*/std::nullopt);
+    name_hint = GetUniqueName(name_hint);
+    return is_dataflow ? DataflowVar(name_hint, /*ty_annotation=*/std::nullopt)
+                       : Var(name_hint, /*ty_annotation=*/std::nullopt);
   }
 
  private:
@@ -584,7 +584,7 @@ class Normalizer : public BlockBuilderImpl, private 
ExprFunctor<Expr(const Expr&
   Expr VisitVar_(const typename T::ContainerType* var) {
     // Parameters and free-vars must be present with type
     // Other vars must have already been normalized through binding
-    TVM_FFI_ICHECK(!var->ty.IsMissing()) << "Var " << var->name_hint() << " 
does not have type.";
+    TVM_FFI_ICHECK(!var->ty.IsMissing()) << "Var " << var->name_hint << " does 
not have type.";
     return ffi::GetRef<Var>(var);
   }
 
diff --git a/src/relax/ir/dataflow_matcher.cc b/src/relax/ir/dataflow_matcher.cc
index 3f7edc6ae8..ef881e317b 100644
--- a/src/relax/ir/dataflow_matcher.cc
+++ b/src/relax/ir/dataflow_matcher.cc
@@ -582,7 +582,7 @@ bool DFPatternMatcher::VisitDFPattern_(const 
VarPatternNode* op, const Expr& exp
   // We don't jump for var pattern, as there's no need to access its value to 
judge it.
   if (const auto* var_node = expr.as<VarNode>()) {
     // "" means any name.
-    return "" == op->name_hint() || op->name_hint() == var_node->name_hint();
+    return "" == op->name_hint() || op->name_hint() == var_node->name_hint;
   }
   return false;
 }
diff --git a/src/relax/ir/expr.cc b/src/relax/ir/expr.cc
index dc54fef78f..3da61a48fb 100644
--- a/src/relax/ir/expr.cc
+++ b/src/relax/ir/expr.cc
@@ -28,7 +28,6 @@ namespace tvm {
 namespace relax {
 
 TVM_FFI_STATIC_INIT_BLOCK() {
-  IdNode::RegisterReflection();
   TupleNode::RegisterReflection();
   TupleGetItemNode::RegisterReflection();
   ShapeExprNode::RegisterReflection();
@@ -48,12 +47,6 @@ TVM_FFI_STATIC_INIT_BLOCK() {
   ExternFuncNode::RegisterReflection();
 }
 
-Id::Id(ffi::String name_hint) {
-  ffi::ObjectPtr<IdNode> n = ffi::make_object<IdNode>();
-  n->name_hint = std::move(name_hint);
-  data_ = std::move(n);
-}
-
 If::If(Expr cond, Expr true_branch, Expr false_branch, Span span) {
   ffi::ObjectPtr<IfNode> n = ffi::make_object<IfNode>();
   n->cond = std::move(cond);
@@ -146,9 +139,9 @@ TVM_FFI_STATIC_INIT_BLOCK() {
   });
 }
 
-Var::Var(Id vid, ffi::Optional<Type> ty_annotation, Span span) {
+Var::Var(ffi::String name_hint, ffi::Optional<Type> ty_annotation, Span span) {
   ffi::ObjectPtr<VarNode> n = ffi::make_object<VarNode>();
-  n->vid = std::move(vid);
+  n->name_hint = std::move(name_hint);
   if (ty_annotation.has_value()) {
     n->ty = ty_annotation.value();
   }
@@ -156,38 +149,15 @@ Var::Var(Id vid, ffi::Optional<Type> ty_annotation, Span 
span) {
   data_ = std::move(n);
 }
 
-VarNode* Var::CopyOnWrite() {
-  // The `TVM_DEFINE_OBJECT_REF_COW_METHOD` cannot be used for
-  // Var, because it is the base class for `DataflowBlock`.
-  // If the `TVM_DEFINE_OBJECT_REF_COW_METHOD` were used, the
-  // automatic implementation would erroneously convert from a
-  // `DataflowBlock` to a `Var`.
-  TVM_FFI_ICHECK(data_ != nullptr);
-  if (!data_.unique()) {
-    ffi::ObjectPtr<VarNode> node;
-    if (auto dataflow_var = as<DataflowVarNode>()) {
-      node = ffi::make_object<DataflowVarNode>(*dataflow_var);
-    } else {
-      node = ffi::make_object<VarNode>(*(operator->()));
-    }
-    ffi::ObjectPtr<ffi::Object>(std::move(node)).swap(data_);
-  }
-  return static_cast<VarNode*>(data_.get());
-}
-
 TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
-  refl::GlobalDef()
-      .def("relax.Var", [](ffi::String name_hint, ffi::Optional<Type> 
ty_annotation,
-                           Span span) { return Var(name_hint, ty_annotation, 
span); })
-      .def("relax.VarFromId", [](Id vid, ffi::Optional<Type> ty_annotation, 
Span span) {
-        return Var(vid, ty_annotation, span);
-      });
+  refl::GlobalDef().def("relax.Var", [](ffi::String name_hint, 
ffi::Optional<Type> ty_annotation,
+                                        Span span) { return Var(name_hint, 
ty_annotation, span); });
 }
 
-DataflowVar::DataflowVar(Id vid, ffi::Optional<Type> ty_annotation, Span span) 
{
+DataflowVar::DataflowVar(ffi::String name_hint, ffi::Optional<Type> 
ty_annotation, Span span) {
   ffi::ObjectPtr<DataflowVarNode> n = ffi::make_object<DataflowVarNode>();
-  n->vid = std::move(vid);
+  n->name_hint = std::move(name_hint);
   if (ty_annotation.has_value()) {
     n->ty = ty_annotation.value();
   }
@@ -197,14 +167,10 @@ DataflowVar::DataflowVar(Id vid, ffi::Optional<Type> 
ty_annotation, Span span) {
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
-  refl::GlobalDef()
-      .def("relax.DataflowVar",
-           [](ffi::String name_hint, ffi::Optional<Type> ty_annotation, Span 
span) {
-             return DataflowVar(name_hint, ty_annotation, span);
-           })
-      .def("relax.DataflowVarFromId", [](Id vid, ffi::Optional<Type> 
ty_annotation, Span span) {
-        return DataflowVar(vid, ty_annotation, span);
-      });
+  refl::GlobalDef().def("relax.DataflowVar",
+                        [](ffi::String name_hint, ffi::Optional<Type> 
ty_annotation, Span span) {
+                          return DataflowVar(name_hint, ty_annotation, span);
+                        });
 }
 
 Constant::Constant(runtime::Tensor data, ffi::Optional<Type> ty_annotation, 
Span span) {
diff --git a/src/relax/ir/expr_functor.cc b/src/relax/ir/expr_functor.cc
index 7f9ff73759..963d302936 100644
--- a/src/relax/ir/expr_functor.cc
+++ b/src/relax/ir/expr_functor.cc
@@ -550,13 +550,14 @@ Expr ExprMutator::VisitExpr(const Expr& expr) {
 
 // Visit the use-site of a defined Var
 Expr ExprMutator::VisitExpr_(const VarNode* op) {
-  auto it = var_remap_.find(op->vid);
+  Var var = ffi::GetRef<Var>(op);
+  auto it = var_remap_.find(var);
   if (it != var_remap_.end()) {
     return it->second;
   }
 
   // default case return self.
-  return ffi::GetRef<Expr>(op);
+  return var;
 }
 
 // Visit the use-site of a defined DataflowVar
@@ -571,7 +572,7 @@ Expr ExprMutator::VisitExpr_(const FunctionNode* op) {
     Var new_param = this->VisitVarDef(param);
     params.push_back(new_param);
     if (!param.same_as(new_param)) {
-      var_remap_[param->vid] = new_param;
+      var_remap_[param] = new_param;
       all_params_unchanged = false;
     }
   }
@@ -660,6 +661,7 @@ RELAX_EXPR_MUTATOR_VISIT_BINDING_IMPL(DataTypeImmNode);
 
 void ExprMutator::ReEmitBinding(const VarBindingNode* binding, Expr new_value) 
{
   Var new_var = this->VisitVarDef(binding->var);
+  Var visited_var = new_var;
 
   // fast path: re-emit binding if nothing changes
   if (new_var.same_as(binding->var) && new_value.same_as(binding->value)) {
@@ -679,8 +681,9 @@ void ExprMutator::ReEmitBinding(const VarBindingNode* 
binding, Expr new_value) {
     new_var = temp;
   }
 
-  this->var_remap_[binding->var->vid] = new_var;
-  this->var_remap_[new_var->vid] = new_var;
+  this->var_remap_[binding->var] = new_var;
+  this->var_remap_[visited_var] = new_var;
+  this->var_remap_[new_var] = new_var;
 
   builder_->EmitNormalized(VarBinding(new_var, new_value));
 }
@@ -690,6 +693,7 @@ void ExprMutator::VisitBinding_(const MatchCastNode* 
binding) {
   Type new_ty = this->VisitExprDepTypeField(binding->ty);
 
   Var new_var = this->VisitVarDef(binding->var);
+  Var visited_var = new_var;
 
   MatchCast new_binding = [&]() -> MatchCast {
     if (new_var.same_as(binding->var) && new_value.same_as(binding->value) &&
@@ -700,8 +704,9 @@ void ExprMutator::VisitBinding_(const MatchCastNode* 
binding) {
       new_value = builder_->NormalizeArgument(new_value);
       new_var = WithType(new_var, new_ty);
 
-      var_remap_[binding->var->vid] = new_var;
-      var_remap_[new_var->vid] = new_var;
+      var_remap_[binding->var] = new_var;
+      var_remap_[visited_var] = new_var;
+      var_remap_[new_var] = new_var;
 
       return MatchCast(new_var, new_value, new_ty, binding->span);
     }
@@ -733,7 +738,9 @@ Var ExprMutator::VisitVarDef_(const DataflowVarNode* var) {
   // provide default behavior in subclasses, we may produce a Var
   // where we should produce a DataflowVar.
   if (!output->IsInstance<DataflowVarNode>()) {
-    output = DataflowVar(output->vid, GetType(output), output->span);
+    Var delegated_output = output;
+    output = DataflowVar(output->name_hint, GetType(output), output->span);
+    var_remap_[delegated_output] = output;
   }
   return output;
 }
@@ -744,7 +751,7 @@ Var ExprMutator::VisitVarDef_(const VarNode* var) {
     if (ty.same_as(var->ty)) {
       return ffi::GetRef<Var>(var);
     } else {
-      return Var(var->vid, ty, var->span);
+      return Var(var->name_hint, ty, var->span);
     }
   } else {
     return ffi::GetRef<Var>(var);
@@ -830,6 +837,9 @@ Expr ExprMutator::VisitWithInnerScope(const Expr& expr) {
 }
 
 ffi::Optional<Expr> ExprMutator::LookupBinding(const Var& var) {
+  if (auto it = var_remap_.find(var); it != var_remap_.end()) {
+    return builder_->LookupBinding(it->second);
+  }
   return builder_->LookupBinding(var);
 }
 
@@ -842,8 +852,8 @@ Var ExprMutator::WithType(Var var, Type ty) {
     if (var->ty.same_as(ty) || ffi::StructuralEqual()(var->ty, ty)) {
       return var;
     } else {
-      Var new_var = var.as<DataflowVarNode>() ? DataflowVar(var->vid, ty, 
var->span)
-                                              : Var(var->vid, ty, var->span);
+      Var new_var = var.as<DataflowVarNode>() ? DataflowVar(var->name_hint, 
ty, var->span)
+                                              : Var(var->name_hint, ty, 
var->span);
       return new_var;
     }
   } else {
diff --git a/src/relax/ir/py_expr_functor.cc b/src/relax/ir/py_expr_functor.cc
index 1257948663..be64ae6459 100644
--- a/src/relax/ir/py_expr_functor.cc
+++ b/src/relax/ir/py_expr_functor.cc
@@ -680,9 +680,11 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       .def("relax.PyExprMutatorWithType",
            [](PyExprMutator mutator, Var var, Type ty) { return 
mutator->WithType(var, ty); })
       .def("relax.PyExprMutatorSetVarRemap",
-           [](PyExprMutator mutator, Id id, Var var) { return 
mutator->var_remap_[id] = var; })
+           [](PyExprMutator mutator, Var old_var, Var new_var) {
+             return mutator->var_remap_[old_var] = new_var;
+           })
       .def("relax.PyExprMutatorGetVarRemap",
-           [](PyExprMutator mutator, Id id) { return mutator->var_remap_[id]; 
});
+           [](PyExprMutator mutator, Var var) { return 
mutator->var_remap_[var]; });
 }
 
 TVM_FFI_STATIC_INIT_BLOCK() {
diff --git a/src/relax/ir/transform.cc b/src/relax/ir/transform.cc
index 8b1815812e..75f3873ce7 100644
--- a/src/relax/ir/transform.cc
+++ b/src/relax/ir/transform.cc
@@ -230,7 +230,7 @@ class DataflowBlockMutator : public ExprMutator {
         }
       }
       if (!var.as<DataflowVarNode>()) {
-        global_scope_vars.Set(var->name_hint(), var);
+        global_scope_vars.Set(var->name_hint, var);
       }
     }
 
@@ -252,10 +252,10 @@ class DataflowBlockMutator : public ExprMutator {
           }
         }
       }
-      if (!var.as<DataflowVarNode>() && 
global_scope_vars.count(var->name_hint()) > 0) {
-        TVM_FFI_ICHECK(var.same_as(global_scope_vars[var->name_hint()]))
+      if (!var.as<DataflowVarNode>() && 
global_scope_vars.count(var->name_hint) > 0) {
+        TVM_FFI_ICHECK(var.same_as(global_scope_vars[var->name_hint]))
             << "Error: DataflowBlock Pass should not rewrite any GlobalScope 
Var.";
-        global_scope_vars.erase(var->name_hint());
+        global_scope_vars.erase(var->name_hint);
       }
     }
     TVM_FFI_ICHECK(global_scope_vars.empty() && symbolic_vars.empty())
diff --git a/src/relax/script/builder/frame.cc 
b/src/relax/script/builder/frame.cc
index 1aa7eb9e20..80fec50fce 100644
--- a/src/relax/script/builder/frame.cc
+++ b/src/relax/script/builder/frame.cc
@@ -136,7 +136,7 @@ void BindingBlockFrameNode::EnterWithScope() {
 class VarReplacer : public tvm::relax::ExprMutator {
  public:
   explicit VarReplacer(
-      std::unordered_map<tvm::relax::Id, tvm::relax::Var, ffi::ObjectPtrHash, 
ffi::ObjectPtrEqual>
+      std::unordered_map<tvm::relax::Var, tvm::relax::Var, ffi::ObjectPtrHash, 
ffi::ObjectPtrEqual>
           var_remap) {
     var_remap_ = std::move(var_remap);
   }
@@ -144,7 +144,7 @@ class VarReplacer : public tvm::relax::ExprMutator {
   tvm::relax::Var VisitVarDef(const tvm::relax::Var& var) override {
     // ExprMutator only applies var_remap_ at usage sites.  This
     // applies var_remap_ at each definition site as well.
-    if (auto it = var_remap_.find(var->vid); it != var_remap_.end()) {
+    if (auto it = var_remap_.find(var); it != var_remap_.end()) {
       return it->second;
     } else {
       return var;
@@ -168,12 +168,12 @@ void BindingBlockFrameNode::ExitWithScope() {
   if (is_dataflow) {
     // Step 3.0.  Define a map to replace variables
     ffi::Array<tvm::relax::Var> new_output_vars;
-    std::unordered_map<tvm::relax::Id, tvm::relax::Var, ffi::ObjectPtrHash, 
ffi::ObjectPtrEqual>
+    std::unordered_map<tvm::relax::Var, tvm::relax::Var, ffi::ObjectPtrHash, 
ffi::ObjectPtrEqual>
         var_remap;
     for (const auto& output_var : output_vars) {
-      tvm::relax::Var new_output_var(output_var->name_hint(), 
GetType(output_var));
+      tvm::relax::Var new_output_var(output_var->name_hint, 
GetType(output_var));
       new_output_vars.push_back(new_output_var);
-      var_remap[output_var->vid] = new_output_var;
+      var_remap[output_var] = new_output_var;
     }
     VarReplacer mutator(std::move(var_remap));
 
diff --git a/src/relax/script/builder/ir.cc b/src/relax/script/builder/ir.cc
index 35b513b07b..32f9f66d59 100644
--- a/src/relax/script/builder/ir.cc
+++ b/src/relax/script/builder/ir.cc
@@ -36,20 +36,16 @@ using tvm::script::ir_builder::details::Namer;
 TVM_STATIC_IR_FUNCTOR(Namer, vtable)
     .set_dispatch<tvm::relax::VarNode>([](const ffi::ObjectRef& node, 
ffi::String name) -> void {
       using tvm::relax::VarNode;
-      using tvm::relax::IdNode;
-      const VarNode* var = node.as<VarNode>();
-      IdNode* vid = const_cast<IdNode*>(var->vid.get());
-      vid->name_hint = name;
+      VarNode* var = const_cast<VarNode*>(node.as<VarNode>());
+      var->name_hint = name;
     });
 
 TVM_STATIC_IR_FUNCTOR(Namer, vtable)
     .set_dispatch<tvm::relax::DataflowVarNode>([](const ffi::ObjectRef& node,
                                                   ffi::String name) -> void {
       using tvm::relax::DataflowVarNode;
-      using tvm::relax::IdNode;
-      const DataflowVarNode* var = node.as<DataflowVarNode>();
-      IdNode* vid = const_cast<IdNode*>(var->vid.get());
-      vid->name_hint = name;
+      DataflowVarNode* var = 
const_cast<DataflowVarNode*>(node.as<DataflowVarNode>());
+      var->name_hint = name;
     });
 
 /////////////////////////////// Function ////////////////////////////////
diff --git a/src/relax/script/builder/utils.h b/src/relax/script/builder/utils.h
index 3a137b0ba4..f353465ee3 100644
--- a/src/relax/script/builder/utils.h
+++ b/src/relax/script/builder/utils.h
@@ -99,7 +99,7 @@ inline tvm::relax::SeqExpr GetSeqExprForBranch(const 
SeqExprFrame& frame, ffi::S
   TVM_FFI_ICHECK(!last_binding->var->IsInstance<tvm::relax::DataflowVarNode>())
       << "A non-dataflow var is expected in the last binding of '" << method 
<< "'.";
 
-  *var_name = last_binding->var->name_hint();
+  *var_name = last_binding->var->name_hint;
 
   // Step 3. Re-collect binding blocks to replace the last binding.
   ffi::Array<tvm::relax::BindingBlock> 
new_blocks(frame->binding_blocks.begin(),
@@ -107,8 +107,8 @@ inline tvm::relax::SeqExpr GetSeqExprForBranch(const 
SeqExprFrame& frame, ffi::S
   ffi::Array<tvm::relax::Binding> 
last_block_bindings(last_block->bindings.begin(),
                                                       
last_block->bindings.end() - 1);
 
-  tvm::relax::Var new_var = tvm::relax::Var(last_binding->var->name_hint() + 
output_var_suffix,
-                                            GetType(last_binding->var));
+  tvm::relax::Var new_var =
+      tvm::relax::Var(last_binding->var->name_hint + output_var_suffix, 
GetType(last_binding->var));
   tvm::relax::Expr body;
 
   const auto* var_binding = last_binding.as<tvm::relax::VarBindingNode>();
diff --git a/src/relax/script/printer/utils.h b/src/relax/script/printer/utils.h
index cced3c9705..b5d575666a 100644
--- a/src/relax/script/printer/utils.h
+++ b/src/relax/script/printer/utils.h
@@ -75,7 +75,7 @@ inline std::string ReprPrintRelax(const ffi::ObjectRef& obj, 
const PrinterConfig
 }
 
 inline IdDoc DefineVar(const relax::Var& var, const Frame& frame, const 
IRDocsifier& d) {
-  return d->Define(var, frame, var->name_hint().empty() ? "v" : 
var->name_hint());
+  return d->Define(var, frame, var->name_hint.empty() ? "v" : var->name_hint);
 }
 
 inline ffi::Optional<ExprDoc> TypeAsAnn(const relax::Var& v, const AccessPath& 
v_p,
diff --git a/src/relax/training/utils.cc b/src/relax/training/utils.cc
index 33376e6d58..a279972e95 100644
--- a/src/relax/training/utils.cc
+++ b/src/relax/training/utils.cc
@@ -167,7 +167,7 @@ class AppendLossMutator : private ExprMutator {
           << " while the corresponding type of parameter of loss function is " 
<< loss_param_ty
           << ", which is different.";
 
-      this->var_remap_[loss_param->vid] = backbone_ret;
+      this->var_remap_[loss_param] = backbone_ret;
     }
   }
 
@@ -190,8 +190,8 @@ class AppendLossMutator : private ExprMutator {
     for (int i = 0; i < num_backbone_outputs_; ++i) {
       auto var = backbone_return_arr_[i];
       if (other_outputs_var.count(var) == 0) {
-        auto new_var = DataflowVar(var->vid, GetType(var), var->span);
-        this->var_remap_[var->vid] = new_var;
+        auto new_var = DataflowVar(var->name_hint, GetType(var), var->span);
+        this->var_remap_[var] = new_var;
         backbone_return_arr_.Set(i, new_var);
       }
     }
diff --git a/src/relax/transform/bind_params.cc 
b/src/relax/transform/bind_params.cc
index 293537888d..04d5f33c66 100644
--- a/src/relax/transform/bind_params.cc
+++ b/src/relax/transform/bind_params.cc
@@ -92,7 +92,7 @@ std::tuple<ffi::Map<Var, Expr>, ffi::Map<tirx::Var, 
PrimExpr>> NormalizeBindings
   std::unordered_map<std::string, ffi::Array<relax::Var>> string_lookup;
   std::unordered_set<const relax::VarNode*> var_set;
   for (const auto& param : func->params) {
-    string_lookup[param->name_hint()].push_back(param);
+    string_lookup[param->name_hint].push_back(param);
     var_set.insert(param.get());
   }
 
@@ -105,7 +105,7 @@ std::tuple<ffi::Map<Var, Expr>, ffi::Map<tirx::Var, 
PrimExpr>> NormalizeBindings
       TVM_FFI_ICHECK(it != string_lookup.end())
           << "Function does not have parameter with name \"" << str << "\".  "
           << "Function parameters are named "
-          << func->params.Map([](const auto& param) { return 
param->name_hint(); });
+          << func->params.Map([](const auto& param) { return param->name_hint; 
});
       TVM_FFI_ICHECK_EQ(it->second.size(), 1)
           << "Function contains multiple parameters with name \"" << str << 
"\".  "
           << "The Relax variables " << it->second << " are all named \"" << 
str << "\"";
diff --git a/src/relax/transform/bundle_model_params.cc 
b/src/relax/transform/bundle_model_params.cc
index 8ddc5539e1..7aa4c8acbe 100644
--- a/src/relax/transform/bundle_model_params.cc
+++ b/src/relax/transform/bundle_model_params.cc
@@ -77,7 +77,7 @@ class ModelParamBundler : public ExprMutator {
   Expr VisitExpr_(const VarNode* op) override {
     auto var = ffi::GetRef<Var>(op);
     if (auto it = var_to_expr_.find(var); it != var_to_expr_.end()) {
-      return builder_->Emit((*it).second, op->name_hint());
+      return builder_->Emit((*it).second, op->name_hint);
     } else {
       return ExprMutator::VisitExpr_(op);
     }
diff --git a/src/relax/transform/canonicalize_bindings.cc 
b/src/relax/transform/canonicalize_bindings.cc
index 9ad3a6dda4..cc12031d5f 100644
--- a/src/relax/transform/canonicalize_bindings.cc
+++ b/src/relax/transform/canonicalize_bindings.cc
@@ -152,10 +152,10 @@ class SymbolicVarCanonicalizer : public ExprMutator {
 };
 
 struct CanonicalizationPlan {
-  ffi::Map<Id, Var> replace_usage;
-  ffi::Map<Id, Var> replace_binding;
-  std::unordered_set<Id, ffi::ObjectPtrHash, ffi::ObjectPtrEqual> 
bindings_to_remove;
-  ffi::Map<Id, Constant> inline_constant;
+  ffi::Map<Var, Var> replace_usage;
+  ffi::Map<Var, Var> replace_binding;
+  std::unordered_set<Var, ffi::ObjectPtrHash, ffi::ObjectPtrEqual> 
bindings_to_remove;
+  ffi::Map<Var, Constant> inline_constant;
 };
 
 /*! \brief Utility class to identify usage location
@@ -179,15 +179,15 @@ class CanonicalizePlanner : public ExprVisitor {
     // of trivial bindings, then we can replace it with a DataflowVar.
     for (auto var : visitor.defined_inside_dataflow_) {
       if (!var.as<DataflowVarNode>() && 
!visitor.used_outside_home_dataflow_.count(var)) {
-        DataflowVar new_var(var->name_hint(), GetType(var));
+        DataflowVar new_var(var->name_hint, GetType(var));
 
-        plan.replace_binding.Set(var->vid, new_var);
-        plan.replace_usage.Set(var->vid, new_var);
+        plan.replace_binding.Set(var, new_var);
+        plan.replace_usage.Set(var, new_var);
       }
     }
 
     for (const auto& [var, constant] : visitor.known_bound_to_constant_) {
-      plan.inline_constant.Set(var->vid, constant);
+      plan.inline_constant.Set(var, constant);
     }
 
     for (const auto& binding_iter : visitor.trivial_bindings_) {
@@ -200,7 +200,7 @@ class CanonicalizePlanner : public ExprVisitor {
         // non-trivial binding.
         bound_to = opt.value();
       }
-      while (auto opt = plan.replace_binding.Get(bound_to->vid)) {
+      while (auto opt = plan.replace_binding.Get(bound_to)) {
         // The variable we are binding to may have already been
         // replaced, if it fell into Case 4 (Var = DataflowVar).  In
         // that case, we check against its replacement instead.
@@ -217,8 +217,8 @@ class CanonicalizePlanner : public ExprVisitor {
         //
         // For these four cases, the trivial binding can be unwrapped,
         // using the bound variable directly at the point of use.
-        plan.replace_usage.Set(bound_var->vid, bound_to);
-        plan.bindings_to_remove.insert(bound_var->vid);
+        plan.replace_usage.Set(bound_var, bound_to);
+        plan.bindings_to_remove.insert(bound_var);
       } else {
         // Case 4b: Var = DataflowVar, where the Var is used somewhere
         //          outside the DataflowBlock containing the binding
@@ -227,9 +227,9 @@ class CanonicalizePlanner : public ExprVisitor {
         // use of a DataflowVar outside of a DataflowBlock.  Instead,
         // we replace in the opposite direction, replacing the binding
         // of the DataflowVar with a binding of the Var.
-        plan.replace_binding.Set(bound_to->vid, bound_var);
-        plan.replace_usage.Set(bound_to->vid, bound_var);
-        plan.bindings_to_remove.insert(bound_var->vid);
+        plan.replace_binding.Set(bound_to, bound_var);
+        plan.replace_usage.Set(bound_to, bound_var);
+        plan.bindings_to_remove.insert(bound_var);
       }
     }
 
@@ -433,14 +433,14 @@ class BindingCanonicalizer : public ExprMutator {
   explicit BindingCanonicalizer(CanonicalizationPlan plan) : plan_(plan) {}
 
   void VisitBinding(const Binding& binding) override {
-    if (!plan_.bindings_to_remove.count(binding->var->vid)) {
+    if (!plan_.bindings_to_remove.count(binding->var)) {
       ExprMutator::VisitBinding(binding);
     }
   }
 
   Var VisitVarDef(const Var& var) override {
     Var new_var = var;
-    while (auto opt = plan_.replace_binding.Get(new_var->vid)) {
+    while (auto opt = plan_.replace_binding.Get(new_var)) {
       new_var = opt.value();
     }
 
@@ -449,10 +449,10 @@ class BindingCanonicalizer : public ExprMutator {
 
   Expr VisitExpr_(const VarNode* var) override {
     Var new_var = ffi::GetRef<Var>(var);
-    while (auto opt = plan_.replace_usage.Get(new_var->vid)) {
+    while (auto opt = plan_.replace_usage.Get(new_var)) {
       new_var = opt.value();
     }
-    if (auto opt = plan_.inline_constant.Get(new_var->vid)) {
+    if (auto opt = plan_.inline_constant.Get(new_var)) {
       return VisitExpr(opt.value());
     }
 
diff --git a/src/relax/transform/convert_layout.cc 
b/src/relax/transform/convert_layout.cc
index fe38bbd3ae..28c30b708d 100644
--- a/src/relax/transform/convert_layout.cc
+++ b/src/relax/transform/convert_layout.cc
@@ -339,7 +339,7 @@ class LayoutConvertMutator : public ExprMutator {
     } else {
       Var new_var = builder_->EmitMatchCast(RewriteExpr(binding->value, 
input_layout), new_ty);
       var_layout_map_[binding->var] = input_layout;
-      this->var_remap_[binding->var->vid] = new_var;
+      this->var_remap_[binding->var] = new_var;
     }
   }
 
diff --git a/src/relax/transform/eliminate_common_subexpr.cc 
b/src/relax/transform/eliminate_common_subexpr.cc
index b2805bf9a2..0542d02910 100644
--- a/src/relax/transform/eliminate_common_subexpr.cc
+++ b/src/relax/transform/eliminate_common_subexpr.cc
@@ -141,7 +141,7 @@ class CommonSubexprEliminator : public ExprMutator {
               << "and occurrences of " << binding->var << " will be replaced 
with "
               << it->second[0];
       output_binding = VarBinding(binding->var, it->second[0]);
-      var_remap_.insert({binding->var->vid, it->second[0]});
+      var_remap_.insert({binding->var, it->second[0]});
       it->second.push_back(binding->var);
 
     } else {
diff --git a/src/relax/transform/expand_tuple_arguments.cc 
b/src/relax/transform/expand_tuple_arguments.cc
index 8294e2c0f0..fa37f9ca07 100644
--- a/src/relax/transform/expand_tuple_arguments.cc
+++ b/src/relax/transform/expand_tuple_arguments.cc
@@ -50,7 +50,7 @@ ffi::Optional<Function> ExpandParams(Function func) {
       ffi::Array<Expr> internal_tuple;
       for (size_t i = 0; i < ty->fields.size(); i++) {
         auto name = static_cast<const std::stringstream&>(std::stringstream()
-                                                          << 
param->name_hint() << "_" << i)
+                                                          << param->name_hint 
<< "_" << i)
                         .str();
         Var new_param(name, ty->fields[i]);
         internal_tuple.push_back(new_param);
diff --git a/src/relax/transform/fuse_ops.cc b/src/relax/transform/fuse_ops.cc
index dae9d0d36e..15d8493092 100644
--- a/src/relax/transform/fuse_ops.cc
+++ b/src/relax/transform/fuse_ops.cc
@@ -502,7 +502,7 @@ class FunctionCreator : public ExprMutator {
       TVM_FFI_ICHECK(!item_indices.empty());
       int param_idx = tuple_param_idx_[tuple_arg];
       Var param = params_[param_idx];
-      ffi::String param_name = params_[param_idx]->name_hint();
+      ffi::String param_name = params_[param_idx]->name_hint;
       TupleType param_ty = tuple_arg->ty.as_or_throw<TupleType>();
 
       ffi::Array<Expr> item_args;
@@ -530,7 +530,7 @@ class FunctionCreator : public ExprMutator {
           auto it = tuple_get_item_remap.find(tuple_get_item->tuple.get());
           if (it != tuple_get_item_remap.end()) {
             TVM_FFI_ICHECK(it->second.find(tuple_get_item->index) != 
it->second.end());
-            var_remap_[var_binding->var->vid] = 
it->second[tuple_get_item->index];
+            var_remap_[var_binding->var] = it->second[tuple_get_item->index];
             if (auto output_idx = GetOutputIndex(binding->var)) {
               outputs.Set(*output_idx, it->second[tuple_get_item->index]);
             }
@@ -545,7 +545,7 @@ class FunctionCreator : public ExprMutator {
         const auto* var_binding = binding.as<VarBindingNode>();
         TVM_FFI_ICHECK_NOTNULL(var_binding);
         Var output_var = builder_->EmitOutput(VisitExpr(var_binding->value));
-        var_remap_[var_binding->var->vid] = output_var;
+        var_remap_[var_binding->var] = output_var;
         outputs.Set(*output_idx, output_var);
       } else {
         // Case 2. It is an internal binding, add it to the binding list.
@@ -624,7 +624,7 @@ class FunctionCreator : public ExprMutator {
     if ((var == nullptr || defined_vars_.count(var) == 0) &&
         (lift_constant_ || !expr->IsInstance<ConstantNode>())) {
       ffi::String name = var != nullptr
-                             ? var->name_hint()
+                             ? var->name_hint
                              : ffi::String("param_" + 
std::to_string(n_param_for_const_++));
       Type param_ty = GetType(expr);
       if (!IsInlinableConstants(expr)) {
@@ -881,10 +881,10 @@ class OperatorFusor : public ExprMutator {
         // available in pending_tuple_get and tuple_get_indices_ respectively.
         for (const auto& var : pending_tuple_get[group]) {
           auto tuple_get = TupleGetItem(new_var, 
tuple_get_indices_[var.get()]);
-          var_remap_[var->vid] = builder_->Emit(tuple_get);
+          var_remap_[var] = builder_->Emit(tuple_get);
         }
       } else {
-        var_remap_[var_binding->var->vid] = new_var;
+        var_remap_[var_binding->var] = new_var;
       }
     }
     // Step 5. Finish the binding block generation.
@@ -931,8 +931,8 @@ class OperatorFusor : public ExprMutator {
           if (producer_group != cur_group) {
             for (Group* depgroup : group_deps_[producer_group]) {
               TVM_FFI_ICHECK(depgroup != cur_group)
-                  << "A cyclic dependency detected between the groups " << 
binding->var->name_hint()
-                  << " and " << used_var->name_hint() << " are in.";
+                  << "A cyclic dependency detected between the groups " << 
binding->var->name_hint
+                  << " and " << used_var->name_hint << " are in.";
             }
             group_deps_[cur_group].push_back(producer_group);
           }
@@ -1332,7 +1332,7 @@ class CompositeFunctionAnnotator : public ExprMutator {
     ffi::Array<Expr> params;
 
     for (auto v : func_node->params) {
-      Var new_v(v->name_hint(), GetType(v));
+      Var new_v(v->name_hint, GetType(v));
       param_vars.push_back(new_v);
       params.push_back(new_v);
     }
diff --git a/src/relax/transform/fuse_tir.cc b/src/relax/transform/fuse_tir.cc
index 7dff781df2..6aa6da3c1a 100644
--- a/src/relax/transform/fuse_tir.cc
+++ b/src/relax/transform/fuse_tir.cc
@@ -968,7 +968,7 @@ class FusedTIRConstructor : public ExprVisitor {
         << "All tuple parameters should be expanded before this point in 
FuseTIR.  "
         << "However, parameter " << relax_param << " has type " << ty;
 
-    auto name_hint = relax_param->name_hint();
+    auto name_hint = relax_param->name_hint;
 
     if (const auto* tensor = ty.as<TensorTypeNode>()) {
       // Case 1. The relax param is a Tensor, we directly create a tirx var 
and buffer
@@ -1263,8 +1263,8 @@ class TIRFuseMutator : public ExprMutator {
         }
       } else if (const auto* prim_value = ty.as<PrimTypeNode>()) {
         if (const auto* var = arg.as<VarNode>()) {
-          tir_vars.push_back(tirx::Var(var->name_hint(), 
tvm::PrimType(prim_value->dtype))
-                                 .as_or_throw<PrimExpr>());
+          tir_vars.push_back(
+              tirx::Var(var->name_hint, 
tvm::PrimType(prim_value->dtype)).as_or_throw<PrimExpr>());
         } else if (auto literal = arg.as<PrimExpr>()) {
           tir_vars.push_back(literal.value());
         } else {
diff --git a/src/relax/transform/gradient.cc b/src/relax/transform/gradient.cc
index 056f765ae9..f11d6af8c2 100644
--- a/src/relax/transform/gradient.cc
+++ b/src/relax/transform/gradient.cc
@@ -46,7 +46,7 @@ namespace relax {
 
 // We will use NestedMsg<Expr> to handle adjoint updates involving tuple 
handling
 using AdjointMsg = NestedMsg<Expr>;
-using VarIdSet = std::unordered_set<Id, ffi::ObjectPtrHash, 
ffi::ObjectPtrEqual>;
+using VarSet = std::unordered_set<Var, ffi::ObjectPtrHash, 
ffi::ObjectPtrEqual>;
 
 // Used in CallTIRWithGradCollector. call_tir -> call_tir_with_grad
 using CallTIRWithGradInfo = std::unordered_map<Call, Call, ffi::ObjectPtrHash, 
ffi::ObjectPtrEqual>;
@@ -63,7 +63,7 @@ class CallTIRWithGradEliminator : private ExprMutator {
    *
    * \param func The original function
    * \return The function with all start_checkpoint and end_checkpoint 
bindings removed, and a
-   * VarIdSet containing all checkpointed vars.
+   * VarSet containing all checkpointed vars.
    */
   static Function Transform(const Function& func) {
     return CallTIRWithGradEliminator().VisitExpr(func).as_or_throw<Function>();
@@ -109,14 +109,14 @@ class CheckpointCollector : private ExprMutator {
   }
 
   // checkpointed vars
-  VarIdSet checkpoints;
+  VarSet checkpoints;
   // mapping from vars that are wrapped in start_checkpoint or end_checkpoint 
to the original vars
-  std::unordered_map<Id, Var, ffi::ObjectPtrHash, ffi::ObjectPtrEqual> 
var_mapping;
+  std::unordered_map<Var, Var, ffi::ObjectPtrHash, ffi::ObjectPtrEqual> 
var_mapping;
 
  private:
   Expr VisitExpr_(const FunctionNode* func) final {
     for (auto var : func->params) {
-      checkpoints.insert(var->vid);
+      checkpoints.insert(var);
     }
 
     return ExprMutator::VisitExpr_(func);
@@ -137,13 +137,14 @@ class CheckpointCollector : private ExprMutator {
       bool all_inner_var_checkpointed = true;
       PostOrderVisit(var_binding->value, [this, 
&all_inner_var_checkpointed](const Expr& expr) {
         if (auto var = expr.as<VarNode>()) {
+          Var var_ref = ffi::GetRef<Var>(var);
           all_inner_var_checkpointed &=
-              (checkpoints.count(var->vid) != 0 || e_vars_.count(var->vid) != 
0);
+              (checkpoints.count(var_ref) != 0 || e_vars_.count(var_ref) != 0);
         }
       });
 
       if (all_inner_var_checkpointed) {
-        checkpoints.insert(var_binding->var->vid);
+        checkpoints.insert(var_binding->var);
       }
     }
 
@@ -165,17 +166,17 @@ class CheckpointCollector : private ExprMutator {
       // Add remapping from binding->var to new_var
       if (!binding->var.as<DataflowVarNode>() && 
var->IsInstance<DataflowVarNode>()) {
         // For output binding, emit a dummy binding
-        this->var_remap_[binding->var->vid] = builder_->EmitOutput(orig_var, 
orig_var->name_hint());
+        this->var_remap_[binding->var] = builder_->EmitOutput(orig_var, 
orig_var->name_hint);
       } else {
-        this->var_remap_[binding->var->vid] = orig_var;
+        this->var_remap_[binding->var] = orig_var;
       }
-      var_mapping[binding->var->vid] = orig_var;
+      var_mapping[binding->var] = orig_var;
 
       if (value->op.same_as(s_cp)) {
         // mark the original var to be checkpointed
-        checkpoints.insert(orig_var->vid);
+        checkpoints.insert(orig_var);
       } else if (value->op.same_as(e_cp)) {
-        e_vars_.insert(binding->var->vid);
+        e_vars_.insert(binding->var);
       }
     } else {
       ExprMutator::VisitBinding_(binding, value);
@@ -183,7 +184,7 @@ class CheckpointCollector : private ExprMutator {
   }
 
   // vars that are the output of end_checkpoint
-  VarIdSet e_vars_;
+  VarSet e_vars_;
 };
 
 /*!
@@ -205,7 +206,7 @@ class CheckpointGenerator : private ExprMutator {
    * checkpointed
    */
   CheckpointGenerator(const BlockBuilder& builder, const ffi::Array<Var>& 
orig_params,
-                      const DataflowBlock& forward_block, const VarIdSet& 
checkpoints)
+                      const DataflowBlock& forward_block, const VarSet& 
checkpoints)
       : builder_(builder) {
     // func params will always be checkpointed
     for (auto var : orig_params) {
@@ -217,7 +218,7 @@ class CheckpointGenerator : private ExprMutator {
       TVM_FFI_ICHECK(var_binding) << "Now only support VarBindingNode";
       auto var = var_binding->var;
       binding_map_.Set(var, var_binding->value);
-      if (checkpoints.count(var->vid)) {
+      if (checkpoints.count(var)) {
         checkpoint_map_.Set(var, var);
       }
     }
@@ -230,7 +231,7 @@ class CheckpointGenerator : private ExprMutator {
     if (it != checkpoint_map_.end()) {
       return std::make_pair((*it).second, new_value);
     }
-    auto new_var = builder_->Emit(new_value, var->name_hint() + "_cp");
+    auto new_var = builder_->Emit(new_value, var->name_hint + "_cp");
     checkpoint_map_.Set(var, new_var);
     return std::make_pair(new_var, new_value);
   }
@@ -249,7 +250,7 @@ class CheckpointGenerator : private ExprMutator {
     if (it != checkpoint_map_.end()) {
       return (*it).second;
     }
-    Var new_var = builder_->Emit(VisitExpr(binding_map_[var]), 
var->name_hint() + "_cp");
+    Var new_var = builder_->Emit(VisitExpr(binding_map_[var]), var->name_hint 
+ "_cp");
     checkpoint_map_.Set(var, new_var);
     return new_var;
   }
@@ -499,8 +500,8 @@ class BackwardBindingGenerator : private ExprVisitor {
     for (Var var : require_grads) {
       // var might be wrapped in start_checkpoint or end_checkpoint, so we 
should find the original
       // var first
-      if (cp_collector_.var_mapping.count(var->vid)) {
-        var = cp_collector_.var_mapping[var->vid];
+      if (cp_collector_.var_mapping.count(var)) {
+        var = cp_collector_.var_mapping[var];
       }
       // If the var don't have adjoint var, it do not contribute to the 
target. So its adjoint is
       // zeros
@@ -519,9 +520,9 @@ class BackwardBindingGenerator : private ExprVisitor {
   Var EmitAdjoint(const Var& source_var, const Expr& adjoint, bool is_output) {
     Var adjoint_var;
     if (is_output) {
-      adjoint_var = builder_->EmitOutput(adjoint, source_var->name_hint() + 
"_adjoint_out");
+      adjoint_var = builder_->EmitOutput(adjoint, source_var->name_hint + 
"_adjoint_out");
     } else {
-      adjoint_var = builder_->Emit(adjoint, source_var->name_hint() + 
"_adjoint");
+      adjoint_var = builder_->Emit(adjoint, source_var->name_hint + 
"_adjoint");
       adjoint_var_map_.Set(source_var, adjoint_var);
     }
     return adjoint_var;
@@ -762,21 +763,21 @@ class GradientMutator : private ExprMutator {
   static ffi::Array<Var> CheckAndMapRequireGrads(const ffi::Array<Var>& 
require_grads,
                                                  const ffi::Map<Var, Var>& 
var_map,
                                                  const ffi::String& func_name) 
{
-    VarIdSet var_set;
+    VarSet var_set;
     ffi::Array<Var> mapped_vars;
     for (const auto& var : require_grads) {
       auto it = var_map.find(var);
       TVM_FFI_ICHECK(it != var_map.end())
-          << "There is no Var named " << var->name_hint() << " in the function 
" << func_name;
-      TVM_FFI_ICHECK_EQ(var_set.count(var->vid), 0)
-          << "Var " << var->name_hint() << " appears more than once";
-      var_set.emplace(var->vid);
+          << "There is no Var named " << var->name_hint << " in the function " 
<< func_name;
+      TVM_FFI_ICHECK_EQ(var_set.count(var), 0)
+          << "Var " << var->name_hint << " appears more than once";
+      var_set.emplace(var);
       mapped_vars.push_back((*it).second);
 
       TVM_FFI_ICHECK(IsNestedTensorConditioned(GetType(var), 
IsFloatTensorType))
           << "Only Tensors of floating point dtype or Tuples of float "
              "Tensors can require gradients, but the Type of Var "
-          << var->name_hint() << " is " << GetType(var);
+          << var->name_hint << " is " << GetType(var);
     }
     return mapped_vars;
   }
diff --git a/src/relax/transform/gradient_simplifier.cc 
b/src/relax/transform/gradient_simplifier.cc
index 17d72c3ac2..13fda06faf 100644
--- a/src/relax/transform/gradient_simplifier.cc
+++ b/src/relax/transform/gradient_simplifier.cc
@@ -166,7 +166,7 @@ class GradientSimplifier : private ExprMutator {
     if (IsTransposeOp(prev_call_node)) {
       // rewrite rule #1: permute_dims(permute_dims(a)) -> a
       if (prev_call_node->args[0]->IsInstance<VarNode>()) {
-        var_remap_[binding->var->vid] = 
prev_call_node->args[0].as_or_throw<Var>();
+        var_remap_[binding->var] = prev_call_node->args[0].as_or_throw<Var>();
         return;
       } else {
         return reemit_and_return();
diff --git a/src/relax/transform/inline_functions.cc 
b/src/relax/transform/inline_functions.cc
index 4e03e83de8..a15d896457 100644
--- a/src/relax/transform/inline_functions.cc
+++ b/src/relax/transform/inline_functions.cc
@@ -129,7 +129,7 @@ class FunctionInliner : public ExprMutator {
       //
       // This implementation uses Option 4.
 
-      Var param_var(func->params[i]->name_hint(), args[i]->ty.as<Type>());
+      Var param_var(func->params[i]->name_hint, args[i]->ty.as<Type>());
       param_bindings.push_back(VarBinding(param_var, args[i]));
       param_map.Set(func->params[i], param_var);
     }
diff --git a/src/relax/transform/lambda_lift.cc 
b/src/relax/transform/lambda_lift.cc
index 2bdfb7178e..6a418ec815 100644
--- a/src/relax/transform/lambda_lift.cc
+++ b/src/relax/transform/lambda_lift.cc
@@ -96,7 +96,7 @@ class LambdaNameCollector : ExprVisitor {
       lifted_with_global_symbol_.insert({func, public_name});
     }
 
-    name_stack_.push_back(binding->var->name_hint());
+    name_stack_.push_back(binding->var->name_hint);
     lambda_location_.insert({func, name_stack_});
     ExprVisitor::VisitBinding_(binding, func);
     name_stack_.pop_back();
@@ -285,7 +285,7 @@ class LambdaLifter : public ExprMutator {
     ffi::Array<Var> typed_captured_vars;
     ffi::Map<Var, Expr> rebinding_map;
     for (auto free_var : captured_vars) {
-      Var var = Var(free_var->name_hint(), GetType(free_var), free_var->span);
+      Var var = Var(free_var->name_hint, GetType(free_var), free_var->span);
       typed_captured_vars.push_back(var);
       rebinding_map.Set(free_var, var);
     }
@@ -367,9 +367,10 @@ class LambdaLifter : public ExprMutator {
 
       // Call "relax.invoke_closure" to invoke closure
 
-      if (IsClosure(var) && builder_->LookupBinding(var).as<CallNode>()) {
+      auto bound_value = LookupBinding(var);
+      if (IsClosure(var) && bound_value.as<CallNode>()) {
         // if the original op was pure, we should use invoke_pure_closure
-        Call orig_call = 
builder_->LookupBinding(var).value().as_or_throw<Call>();
+        Call orig_call = bound_value.value().as_or_throw<Call>();
         bool is_pure = [&]() -> bool {
           if (auto op = orig_call->op.as<Op>()) {
             static const auto& purity_map = Op::GetAttrMap<bool>("FPurity");
@@ -422,7 +423,7 @@ class LambdaLifter : public ExprMutator {
       if (closures_.count(opt_var.value())) {
         return true;
       }
-      if (auto bound_value = builder_->LookupBinding(opt_var.value())) {
+      if (auto bound_value = LookupBinding(opt_var.value())) {
         val = bound_value.value();
       }
     }
diff --git a/src/relax/transform/lazy_transform_params.cc 
b/src/relax/transform/lazy_transform_params.cc
index b5a5918c4a..02fd26c05f 100644
--- a/src/relax/transform/lazy_transform_params.cc
+++ b/src/relax/transform/lazy_transform_params.cc
@@ -100,10 +100,10 @@ class LazyInputMutator : public ExprMutator {
         auto untyped = builder_->Emit(Call(Type::Missing(), plan_->fget_param,
                                            {
                                                
PrimExpr(IntImm::Int64(it->second)),
-                                               StringImm(var->name_hint()),
+                                               StringImm(var->name_hint),
                                            }),
-                                      var->name_hint() + "_untyped");
-        return builder_->EmitMatchCast(untyped, GetType(var), 
var->name_hint());
+                                      var->name_hint + "_untyped");
+        return builder_->EmitMatchCast(untyped, GetType(var), var->name_hint);
       }
     }
 
diff --git a/src/relax/transform/lift_transform_params.cc 
b/src/relax/transform/lift_transform_params.cc
index a5d295f09a..3907219968 100644
--- a/src/relax/transform/lift_transform_params.cc
+++ b/src/relax/transform/lift_transform_params.cc
@@ -106,7 +106,7 @@ struct BaseCollectInfo {
     }
 
     for (const auto& var : outputs) {
-      Var out_var(var->name_hint() + "_output", GetType(var));
+      Var out_var(var->name_hint + "_output", GetType(var));
       output_var_binding.push_back(VarBinding(out_var, var));
       output_exprs.push_back(out_var);
     }
@@ -280,7 +280,7 @@ struct LocalCollectInfo : public BaseCollectInfo {
       return global_outputs;
     }();
     for (const auto& var : compile_time_outputs) {
-      Var param_var(var->name_hint(), GetType(var));
+      Var param_var(var->name_hint, GetType(var));
       bindings.push_back(VarBinding(var, param_var));
       params.push_back(param_var);
     }
diff --git a/src/relax/transform/realize_vdevice.cc 
b/src/relax/transform/realize_vdevice.cc
index 2897c0e909..5e0a990b7d 100644
--- a/src/relax/transform/realize_vdevice.cc
+++ b/src/relax/transform/realize_vdevice.cc
@@ -363,9 +363,9 @@ class VDeviceTypeUpdater : ExprMutator {
         }();
 
         if (var->IsInstance<DataflowVarNode>()) {
-          var = DataflowVar(var->vid, new_ty, var->span);
+          var = DataflowVar(var->name_hint, new_ty, var->span);
         } else {
-          var = Var(var->vid, new_ty, var->span);
+          var = Var(var->name_hint, new_ty, var->span);
         }
       }
     }
diff --git a/src/relax/transform/rewrite_cuda_graph.cc 
b/src/relax/transform/rewrite_cuda_graph.cc
index 3b53e3ac3b..585ff6bdeb 100644
--- a/src/relax/transform/rewrite_cuda_graph.cc
+++ b/src/relax/transform/rewrite_cuda_graph.cc
@@ -137,8 +137,8 @@ class FuncBuilder : public ExprMutator {
     }
     // Set up the parameters
     for (const auto* input : inputs_) {
-      auto new_var = Var(input->name_hint(), 
VisitExprDepTypeField(input->ty.as_or_throw<Type>()));
-      var_remap_[input->vid] = new_var;
+      auto new_var = Var(input->name_hint, 
VisitExprDepTypeField(input->ty.as_or_throw<Type>()));
+      var_remap_[ffi::GetRef<Var>(input)] = new_var;
       params.push_back(new_var);
     }
     if (shape_expr) {
@@ -846,7 +846,7 @@ class CUDAGraphRewriter : public ExprMutator {
       LaunchSubgraph(op, subgraph_launches_[op->var.get()]);
     }
     if (auto it = var_redef_.find(op->var.get());
-        it != var_redef_.end() && !var_remap_.count(op->var->vid)) {
+        it != var_redef_.end() && !var_remap_.count(op->var)) {
       EmitRedef(op->var.get(), it->second);
       return;
     }
@@ -858,7 +858,8 @@ class CUDAGraphRewriter : public ExprMutator {
   }
 
   Expr VisitExpr_(const VarNode* op) final {
-    if (auto it = var_remap_.find(op->vid); it != var_remap_.end()) {
+    Var var = ffi::GetRef<Var>(op);
+    if (auto it = var_remap_.find(var); it != var_remap_.end()) {
       return it->second;
     }
     if (auto it = var_redef_.find(op); it != var_redef_.end()) {
@@ -870,8 +871,8 @@ class CUDAGraphRewriter : public ExprMutator {
   }
 
   Var EmitRedef(const VarNode* var, const Expr& redef) {
-    auto new_var = builder_->Emit(redef, var->name_hint());
-    var_remap_[var->vid] = new_var;
+    auto new_var = builder_->Emit(redef, var->name_hint);
+    var_remap_[ffi::GetRef<Var>(var)] = new_var;
     return new_var;
   }
 
diff --git a/src/relax/transform/specialize_primfunc_based_on_callsite.cc 
b/src/relax/transform/specialize_primfunc_based_on_callsite.cc
index b867ff967d..8445446e68 100644
--- a/src/relax/transform/specialize_primfunc_based_on_callsite.cc
+++ b/src/relax/transform/specialize_primfunc_based_on_callsite.cc
@@ -94,7 +94,7 @@ class SpecializeTIRCallArgs : ExprMutator {
       }
       ffi::String name;
       if (args[i]->IsInstance<relax::VarNode>()) {
-        name = args[i].as_or_throw<Var>()->name_hint();
+        name = args[i].as_or_throw<Var>()->name_hint;
       } else {
         name = std::string({static_cast<char>('A' + i)});
       }
diff --git a/src/relax/transform/to_mixed_precision.cc 
b/src/relax/transform/to_mixed_precision.cc
index ad2e285015..0764fd268f 100644
--- a/src/relax/transform/to_mixed_precision.cc
+++ b/src/relax/transform/to_mixed_precision.cc
@@ -279,11 +279,11 @@ class ToMixedPrecisionRewriter : public ExprMutator {
 
  private:
   Var GetRemapped(const Var& var) {
-    auto it = var_remap_.find(var->vid);
+    auto it = var_remap_.find(var);
     if (it != var_remap_.end()) {
       return it->second;
     } else {
-      if (fp16_input_names_.count(var->name_hint())) {
+      if (fp16_input_names_.count(var->name_hint)) {
         auto ty = GetType(var);
         if (auto tensor_ty = ty.as<TensorTypeNode>()) {
           VDevice vdev = VDevice();
@@ -291,8 +291,8 @@ class ToMixedPrecisionRewriter : public ExprMutator {
             vdev = tensor_ty->vdevice.value();
           }
           TensorType fp16_ty(tensor_ty->shape.value(), PrimType::Float(16), 
vdev, tensor_ty->span);
-          Var fp16_var(var->vid, fp16_ty, var->span);
-          var_remap_[var->vid] = fp16_var;
+          Var fp16_var(var->name_hint, fp16_ty, var->span);
+          var_remap_[var] = fp16_var;
           return fp16_var;
         }
       }
@@ -424,13 +424,13 @@ class ToMixedPrecisionRewriter : public ExprMutator {
     // If cur_var is not rewritten, we don't need to emit a new var
     if (!rewrite.same_as(cur_var)) {
       // Emit a new var, and update the var remap
-      var_remap_[var->vid] = builder_->Emit(rewrite);
+      var_remap_[var] = builder_->Emit(rewrite);
     }
   }
 
   Expr VisitVar_(const Var& var) {
     // We rewrite the remapped var to the original dtype
-    auto it = var_remap_.find(var->vid);
+    auto it = var_remap_.find(var);
     if (it != var_remap_.end()) {
       return RewriteExpr(it->second, NTypeFrom(var));
     }
@@ -577,7 +577,7 @@ class ToMixedPrecisionRewriter : public ExprMutator {
     }
     for (auto param : params_) {
       // remove the local version of params
-      auto it = var_remap_.find(param->vid);
+      auto it = var_remap_.find(param);
       if (it != var_remap_.end()) {
         var_remap_.erase(it);
       }
diff --git a/src/relax/transform/to_non_dataflow.cc 
b/src/relax/transform/to_non_dataflow.cc
index 700521eb9c..ae279e4cd5 100644
--- a/src/relax/transform/to_non_dataflow.cc
+++ b/src/relax/transform/to_non_dataflow.cc
@@ -34,8 +34,8 @@ class ToNonDFMutator : public ExprMutator {
  public:
   Var VisitVarDef(const Var& var) final {
     if (var.as<DataflowVarNode>()) {
-      Var new_var = Var(var->vid, GetType(var), var->span);
-      this->var_remap_[var->vid] = new_var;
+      Var new_var = Var(var->name_hint, GetType(var), var->span);
+      this->var_remap_[var] = new_var;
       return new_var;
     }
     return var;
diff --git a/src/relax/transform/update_param_type.cc 
b/src/relax/transform/update_param_type.cc
index 7ba696639f..dcebbd4e6f 100644
--- a/src/relax/transform/update_param_type.cc
+++ b/src/relax/transform/update_param_type.cc
@@ -52,7 +52,7 @@ class ParamTypeMutator : public ExprMutator {
     auto params = op->params.Map([this](Var param) {
       if (auto new_ty = ty_func_(param)) {
         auto new_param = WithType(param, new_ty.value());
-        var_remap_[param->vid] = new_param;
+        var_remap_[param] = new_param;
         return new_param;
       } else {
         return param;
diff --git a/src/relax/transform/utils.h b/src/relax/transform/utils.h
index 0163b93275..105dbff5e9 100644
--- a/src/relax/transform/utils.h
+++ b/src/relax/transform/utils.h
@@ -199,7 +199,7 @@ bool IsNestedTensor(const Expr& expr);
 // TODO(@bohan): implements some postorder function accepts a visitor closure
 class VarReplacer : public ExprMutator {
  public:
-  using VarMap = std::unordered_map<Id, Var, ffi::ObjectPtrHash, 
ffi::ObjectPtrEqual>;
+  using VarMap = std::unordered_map<Var, Var, ffi::ObjectPtrHash, 
ffi::ObjectPtrEqual>;
 
   explicit VarReplacer(const VarMap& var_remap) : var_remap_(var_remap) {}
 
@@ -211,7 +211,7 @@ class VarReplacer : public ExprMutator {
  private:
   Expr VisitExpr_(const VarNode* op) final {
     Var var = ffi::GetRef<Var>(op);
-    auto it = var_remap_.find(var->vid);
+    auto it = var_remap_.find(var);
     return it == var_remap_.end() ? var : it->second;
   }
 
@@ -269,7 +269,7 @@ class SymbolicVarRenewMutator : public ExprMutator, 
tirx::ExprMutator {
       Var new_param = this->VisitVarDef(param);
       params.push_back(new_param);
       if (!param.same_as(new_param)) {
-        var_remap_[param->vid] = new_param;
+        var_remap_[param] = new_param;
         all_params_unchanged = false;
       }
     }
@@ -303,16 +303,16 @@ class FunctionCopier : public SymbolicVarRenewMutator {
 
   Var VisitVarDef_(const DataflowVarNode* var) override {
     Var new_var = SymbolicVarRenewMutator::VisitVarDef_(var);
-    Var copied_var = DataflowVar(new_var->name_hint(), GetType(new_var), 
new_var->span);
-    var_remap_[var->vid] = copied_var;
+    Var copied_var = DataflowVar(new_var->name_hint, GetType(new_var), 
new_var->span);
+    var_remap_[ffi::GetRef<Var>(var)] = copied_var;
     relax_var_map_.Set(ffi::GetRef<Var>(var), copied_var);
     return copied_var;
   }
 
   Var VisitVarDef_(const VarNode* var) override {
     Var new_var = SymbolicVarRenewMutator::VisitVarDef_(var);
-    Var copied_var = Var(new_var->name_hint(), GetType(new_var), 
new_var->span);
-    var_remap_[var->vid] = copied_var;
+    Var copied_var = Var(new_var->name_hint, GetType(new_var), new_var->span);
+    var_remap_[ffi::GetRef<Var>(var)] = copied_var;
     relax_var_map_.Set(ffi::GetRef<Var>(var), copied_var);
     return copied_var;
   }
diff --git a/src/relax/utils.cc b/src/relax/utils.cc
index a8b148aa89..63f0d08636 100644
--- a/src/relax/utils.cc
+++ b/src/relax/utils.cc
@@ -49,7 +49,7 @@ class ExprBinder : public ExprMutator {
         Var new_param = this->VisitVarDef(param);
         params.push_back(new_param);
         if (!param.same_as(new_param)) {
-          this->var_remap_[param->vid] = new_param;
+          this->var_remap_[param] = new_param;
           all_params_unchanged = false;
         }
       }
diff --git a/tests/python/relax/test_expr_functor.py 
b/tests/python/relax/test_expr_functor.py
index cf004a879a..6a09ee8083 100644
--- a/tests/python/relax/test_expr_functor.py
+++ b/tests/python/relax/test_expr_functor.py
@@ -303,7 +303,7 @@ class ASTPostPrinterMutator(PyExprMutator):
         temp = self.with_type(new_var, new_value.ty)
         if not temp.same_as(new_var):
             new_var = temp
-            self.set_var_remap(binding.var.vid, new_var)
+            self.set_var_remap(binding.var, new_var)
 
         self.builder_.emit_normalized(VarBinding(new_var, new_value))
 
@@ -315,7 +315,7 @@ class ASTPostPrinterMutator(PyExprMutator):
         temp = self.with_type(new_var, binding.ty)
         if not temp.same_as(new_var):
             new_var = temp
-            self.set_var_remap(binding.var.vid, new_var)
+            self.set_var_remap(binding.var, new_var)
 
         self.log.add("MatchCast")
         self.builder_.emit_normalized(MatchCast(new_var, new_value, 
binding.ty))
diff --git a/tests/python/relax/test_transform_canonicalize_bindings.py 
b/tests/python/relax/test_transform_canonicalize_bindings.py
index 4650483546..17b4a466b8 100644
--- a/tests/python/relax/test_transform_canonicalize_bindings.py
+++ b/tests/python/relax/test_transform_canonicalize_bindings.py
@@ -1173,12 +1173,12 @@ def test_canonicalization_causes_ty_update():
     better type as a result.  If this happens, the
 
     In previous implementations, ExprMutator::ReEmitBinding defined a
-    remap for `binding->var->vid`, even if the derived class defined a
+    remap for `binding->var`, even if the derived class defined a
     replacement by overriding `VisitVarDef`.  If the derived class
     defines a new variable binding by overriding `VisitVarDef`, and
     also causes a variable replacement by overriding `VisitExpr` and
     returning a type with different type, then `ExprMutator`
-    must check for both `binding->var->vid` *AND* `new_var->vid`.  The
+    must check for both `binding->var` *AND* `new_var`.  The
     former may be present in the unmodified graph, and the latter may
     be produced by the derived class before delegating to the base
     class.

Reply via email to