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 200f4fd5cd [REFACTOR][NODE] Migrate ReprPrinter to tvm-ffi 
__ffi_repr__ mechanism (#19461)
200f4fd5cd is described below

commit 200f4fd5cdaa6f63bc65145e17d7f4a86370b905
Author: Tianqi Chen <[email protected]>
AuthorDate: Mon Apr 27 16:39:55 2026 -0400

    [REFACTOR][NODE] Migrate ReprPrinter to tvm-ffi __ffi_repr__ mechanism 
(#19461)
    
    This PR phases out the legacy `ReprPrinter` machinery
    (`include/tvm/node/repr_printer.h`, `src/node/repr_printer.cc`,
    `src/node/container_printing.cc`) by migrating ~169 dispatch sites to
    the tvm-ffi `__ffi_repr__` mechanism.
    
    **Summary of changes:**
    - All ~169 `TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable).set_dispatch<T>`
    sites have been migrated: most non-user-facing types now use the
    dataclass auto-default; user-facing types (`script/printer/`, `Trace`,
    DPL pattern syntax, Span/SequentialSpan/SourceName, `data_layout`
    Layout/BijectiveLayout, `target_kind`/`virtual_device`) retain custom
    hooks via `__ffi_repr__`.
    - Adds `include/tvm/node/repr.h` with `operator<<(ostream,
    ObjectRef/Any/Variant)` delegating to `ffi::ReprPrint`. Adds
    `src/node/repr.cc` with `Dump()` debug helpers and the `node.AsRepr` FFI
    for Python compatibility.
    - Note: null `ObjectRef` now reprs as `None` (Python-compatible) rather
    than `(nullptr)`; one test was updated for this
    (`test_tir_transform_vectorize.py::test_illegal_extent`). The
    auto-default uses the full type-key (e.g. `arith.ModularSet(...)`) per
    design direction.
    
    **Test results:**
    - all-platform-minimal: 75p/77s
    - tirx-base: 273p/2s
    - arith: 982p/1s/8xf (preexisting xfails)
    - ir+target: 236p/7s
    - tvmscript: 771p/1xf
    - tirx-transform+analysis: 374p/9xf/1xp
    - s_tir (excl. 2 broken test files): 1279p/36s/2xf + 15 preexisting CUDA
    sketch failures
    - relax: covered (no repr-pinned tests)
    - Pre-commit clean
---
 include/tvm/ir/expr.h                              |   2 +-
 include/tvm/node/functor.h                         |  28 ++-
 include/tvm/node/{repr_printer.h => repr.h}        |  56 ++----
 include/tvm/node/repr_printer.h                    | 116 +------------
 include/tvm/node/script_printer.h                  |   2 +-
 include/tvm/relax/exec_builder.h                   |   2 +-
 include/tvm/s_tir/meta_schedule/mutator.h          |   2 +
 src/arith/canonical_simplify.cc                    |  35 +---
 src/arith/const_int_bound.cc                       |  10 +-
 src/arith/int_constraints.cc                       |  22 +--
 src/arith/int_set.cc                               |   7 +-
 src/arith/iter_affine_map.cc                       |  19 +--
 src/arith/modular_set.cc                           |   8 +-
 src/arith/presburger_set.cc                        |  10 +-
 src/arith/rewrite_simplify.cc                      |  11 +-
 src/ir/env_func.cc                                 |   6 +-
 src/ir/expr.cc                                     |   6 +-
 src/ir/instrument.cc                               |   8 +-
 src/ir/op.cc                                       |   6 +-
 src/ir/source_map.cc                               |  60 ++++---
 src/ir/structural_equal.cc                         |   2 +-
 src/ir/structural_hash.cc                          |  30 +---
 src/ir/transform.cc                                |  57 +------
 src/node/container_printing.cc                     |  52 +-----
 src/node/repr.cc                                   |  97 +++++++++++
 src/node/repr_printer.cc                           | 189 +--------------------
 src/node/script_printer.cc                         |   9 +-
 src/relax/ir/dataflow_pattern.cc                   |  29 +++-
 src/relax/ir/emit_te.cc                            |   7 +-
 src/relax/ir/expr.cc                               |   2 -
 src/relax/ir/transform.cc                          |  18 +-
 src/s_tir/data_layout.cc                           |  26 +--
 src/s_tir/meta_schedule/arg_info.cc                |  24 ++-
 src/s_tir/meta_schedule/cost_model/cost_model.cc   |  15 +-
 .../feature_extractor/feature_extractor.cc         |  10 +-
 .../measure_callback/measure_callback.cc           |  10 +-
 src/s_tir/meta_schedule/mutator/mutator.cc         |   9 +-
 src/s_tir/meta_schedule/postproc/postproc.cc       |   9 +-
 .../meta_schedule/schedule_rule/schedule_rule.cc   |   9 +-
 src/s_tir/schedule/instruction.cc                  |  17 +-
 src/s_tir/schedule/trace.cc                        |   6 -
 src/script/printer/ir/utils.h                      |   2 +-
 src/script/printer/relax/utils.h                   |   2 +-
 src/script/printer/tirx/utils.h                    |   2 +-
 src/script/printer/utils.h                         |  26 ++-
 src/target/target.cc                               |   5 +-
 src/target/target_kind.cc                          |  11 +-
 src/target/virtual_device.cc                       |  63 +++----
 src/te/operation/compute_op.cc                     |   8 +-
 src/te/operation/extern_op.cc                      |   7 +-
 src/te/operation/placeholder_op.cc                 |   7 +-
 src/te/operation/scan_op.cc                        |   6 +-
 src/te/tensor.cc                                   |   6 +-
 src/tirx/ir/expr.cc                                |  11 +-
 src/tirx/ir/transform.cc                           |   9 +-
 .../tirx-transform/test_tir_transform_vectorize.py |   2 +-
 56 files changed, 362 insertions(+), 848 deletions(-)

diff --git a/include/tvm/ir/expr.h b/include/tvm/ir/expr.h
index 942595b53d..968bef0727 100644
--- a/include/tvm/ir/expr.h
+++ b/include/tvm/ir/expr.h
@@ -29,7 +29,7 @@
 #include <tvm/ir/source_map.h>
 #include <tvm/ir/type.h>
 #include <tvm/node/cast.h>
-#include <tvm/node/repr_printer.h>
+#include <tvm/node/repr.h>
 #include <tvm/node/script_printer.h>
 #include <tvm/runtime/object.h>
 
diff --git a/include/tvm/node/functor.h b/include/tvm/node/functor.h
index b9a4681532..3f0babdbd0 100644
--- a/include/tvm/node/functor.h
+++ b/include/tvm/node/functor.h
@@ -160,38 +160,32 @@ class NodeFunctor<R(const ObjectRef& n, Args...)> {
  * \brief Useful macro to set NodeFunctor dispatch in a global static field.
  *
  * \code
- *  // Use NodeFunctor to implement ReprPrinter similar to Visitor Pattern.
+ *  // Use NodeFunctor to implement TVMScriptPrinter similar to Visitor 
Pattern.
  *  // vtable allows easy patch of new Node types, without changing
- *  // interface of ReprPrinter.
+ *  // the interface of TVMScriptPrinter.
  *
- *  class ReprPrinter {
+ *  class TVMScriptPrinter {
  *   public:
- *    std::ostream& stream;
  *    // the dispatch function.
- *    void print(Expr e) {
- *      const static FType& f = *vtable();
- *      f(e, this);
+ *    static std::string Script(const ObjectRef& node, const PrinterConfig& 
cfg) {
+ *      return vtable()(node, cfg);
  *    }
- *
- *    using FType = NodeFunctor<void (const ObjectRef&, ReprPrinter* )>;
+ *    using FType = NodeFunctor<std::string(const ObjectRef&, const 
PrinterConfig&)>;
  *    // function to return global function table
  *    static FType& vtable();
  *  };
  *
  *  // in cpp/cc file
- *  ReprPrinter::FType& ReprPrinter::vtable() { // NOLINT(*)
+ *  TVMScriptPrinter::FType& TVMScriptPrinter::vtable() {
  *    static FType inst; return inst;
  *  }
  *
- *  TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
- *  .set_dispatch<Add>([](const ObjectRef& ref, ReprPrinter* p) {
- *    auto* n = static_cast<const Add*>(ref.get());
- *    p->print(n->a);
- *    p->stream << '+'
- *    p->print(n->b);
+ *  TVM_STATIC_IR_FUNCTOR(TVMScriptPrinter, vtable)
+ *  .set_dispatch<AddNode>([](const ObjectRef& ref, const PrinterConfig& cfg) {
+ *    auto* n = static_cast<const AddNode*>(ref.get());
+ *    return Script(n->a, cfg) + " + " + Script(n->b, cfg);
  *  });
  *
- *
  * \endcode
  *
  * \param ClsName The name of the class
diff --git a/include/tvm/node/repr_printer.h b/include/tvm/node/repr.h
similarity index 69%
copy from include/tvm/node/repr_printer.h
copy to include/tvm/node/repr.h
index 1c1f1ec380..48276df1ec 100644
--- a/include/tvm/node/repr_printer.h
+++ b/include/tvm/node/repr.h
@@ -17,41 +17,23 @@
  * under the License.
  */
 /*!
- * \file tvm/node/repr_printer.h
- * \brief Printer class to print repr string of each AST/IR nodes.
+ * \file tvm/node/repr.h
+ * \brief ostream operator<< for ObjectRef, Any, and Variant, delegating to
+ *        ffi::ReprPrint.  Also re-exports the Dump() debug helpers.
+ *
+ * Include this header wherever you need `os << some_objectref` and you are
+ * no longer pulling in the legacy repr_printer.h.
  */
-#ifndef TVM_NODE_REPR_PRINTER_H_
-#define TVM_NODE_REPR_PRINTER_H_
+#ifndef TVM_NODE_REPR_H_
+#define TVM_NODE_REPR_H_
 
+#include <tvm/ffi/extra/dataclass.h>
 #include <tvm/ffi/reflection/access_path.h>
-#include <tvm/node/functor.h>
-#include <tvm/node/script_printer.h>
+#include <tvm/runtime/object.h>
 
 #include <iostream>
-#include <string>
 
 namespace tvm {
-/*! \brief A printer class to print the AST/IR nodes. */
-class ReprPrinter {
- public:
-  /*! \brief The output stream */
-  std::ostream& stream;
-  /*! \brief The indentation level. */
-  int indent{0};
-
-  explicit ReprPrinter(std::ostream& stream)  // NOLINT(*)
-      : stream(stream) {}
-
-  /*! \brief The node to be printed. */
-  TVM_DLL void Print(const ObjectRef& node);
-  /*! \brief The node to be printed. */
-  TVM_DLL void Print(const ffi::Any& node);
-  /*! \brief Print indent to the stream */
-  TVM_DLL void PrintIndent();
-  // Allow registration to be printer.
-  using FType = NodeFunctor<void(const ObjectRef&, ReprPrinter*)>;
-  TVM_DLL static FType& vtable();
-};
 
 /*!
  * \brief Dump the node to stderr, used for debug purposes.
@@ -69,23 +51,21 @@ TVM_DLL void Dump(const runtime::Object* node);
 
 namespace tvm {
 namespace ffi {
-// default print function for all objects
-// provide in the runtime namespace as this is where objectref originally 
comes from.
+
+// ostream << ObjectRef  — delegates to ffi::ReprPrint
 inline std::ostream& operator<<(std::ostream& os, const ObjectRef& n) {  // 
NOLINT(*)
-  ReprPrinter(os).Print(n);
-  return os;
+  return os << ffi::ReprPrint(Any(n));
 }
 
-// default print function for any
+// ostream << Any — delegates to ffi::ReprPrint
 inline std::ostream& operator<<(std::ostream& os, const Any& n) {  // NOLINT(*)
-  ReprPrinter(os).Print(n);
-  return os;
+  return os << ffi::ReprPrint(n);
 }
 
+// ostream << Variant<...> — delegates to ffi::ReprPrint
 template <typename... V>
 inline std::ostream& operator<<(std::ostream& os, const ffi::Variant<V...>& n) 
{  // NOLINT(*)
-  ReprPrinter(os).Print(Any(n));
-  return os;
+  return os << ffi::ReprPrint(Any(n));
 }
 
 namespace reflection {
@@ -135,4 +115,4 @@ inline std::ostream& operator<<(std::ostream& os, const 
AccessPath& path) {
 }  // namespace reflection
 }  // namespace ffi
 }  // namespace tvm
-#endif  // TVM_NODE_REPR_PRINTER_H_
+#endif  // TVM_NODE_REPR_H_
diff --git a/include/tvm/node/repr_printer.h b/include/tvm/node/repr_printer.h
index 1c1f1ec380..71d19f5245 100644
--- a/include/tvm/node/repr_printer.h
+++ b/include/tvm/node/repr_printer.h
@@ -18,121 +18,13 @@
  */
 /*!
  * \file tvm/node/repr_printer.h
- * \brief Printer class to print repr string of each AST/IR nodes.
+ * \brief DEPRECATED: The legacy ReprPrinter has been replaced by
+ *        ffi::ReprPrint.  This header is kept as an empty shim;
+ *        include <tvm/node/repr.h> instead.
  */
 #ifndef TVM_NODE_REPR_PRINTER_H_
 #define TVM_NODE_REPR_PRINTER_H_
 
-#include <tvm/ffi/reflection/access_path.h>
-#include <tvm/node/functor.h>
-#include <tvm/node/script_printer.h>
+#include <tvm/node/repr.h>
 
-#include <iostream>
-#include <string>
-
-namespace tvm {
-/*! \brief A printer class to print the AST/IR nodes. */
-class ReprPrinter {
- public:
-  /*! \brief The output stream */
-  std::ostream& stream;
-  /*! \brief The indentation level. */
-  int indent{0};
-
-  explicit ReprPrinter(std::ostream& stream)  // NOLINT(*)
-      : stream(stream) {}
-
-  /*! \brief The node to be printed. */
-  TVM_DLL void Print(const ObjectRef& node);
-  /*! \brief The node to be printed. */
-  TVM_DLL void Print(const ffi::Any& node);
-  /*! \brief Print indent to the stream */
-  TVM_DLL void PrintIndent();
-  // Allow registration to be printer.
-  using FType = NodeFunctor<void(const ObjectRef&, ReprPrinter*)>;
-  TVM_DLL static FType& vtable();
-};
-
-/*!
- * \brief Dump the node to stderr, used for debug purposes.
- * \param node The input node
- */
-TVM_DLL void Dump(const runtime::ObjectRef& node);
-
-/*!
- * \brief Dump the node to stderr, used for debug purposes.
- * \param node The input node
- */
-TVM_DLL void Dump(const runtime::Object* node);
-
-}  // namespace tvm
-
-namespace tvm {
-namespace ffi {
-// default print function for all objects
-// provide in the runtime namespace as this is where objectref originally 
comes from.
-inline std::ostream& operator<<(std::ostream& os, const ObjectRef& n) {  // 
NOLINT(*)
-  ReprPrinter(os).Print(n);
-  return os;
-}
-
-// default print function for any
-inline std::ostream& operator<<(std::ostream& os, const Any& n) {  // NOLINT(*)
-  ReprPrinter(os).Print(n);
-  return os;
-}
-
-template <typename... V>
-inline std::ostream& operator<<(std::ostream& os, const ffi::Variant<V...>& n) 
{  // NOLINT(*)
-  ReprPrinter(os).Print(Any(n));
-  return os;
-}
-
-namespace reflection {
-
-inline std::ostream& operator<<(std::ostream& os, const AccessStep& step) {
-  namespace refl = ffi::reflection;
-  switch (step->kind) {
-    case refl::AccessKind::kAttr: {
-      os << '.' << step->key.cast<ffi::String>();
-      return os;
-    }
-    case refl::AccessKind::kArrayItem: {
-      os << "[" << step->key.cast<int64_t>() << "]";
-      return os;
-    }
-    case refl::AccessKind::kMapItem: {
-      os << "[" << step->key << "]";
-      return os;
-    }
-    case refl::AccessKind::kAttrMissing: {
-      os << ".<missing attr " << step->key.cast<ffi::String>() << "`>";
-      return os;
-    }
-    case refl::AccessKind::kArrayItemMissing: {
-      os << "[<missing item at " << step->key.cast<int64_t>() << ">]";
-      return os;
-    }
-    case refl::AccessKind::kMapItemMissing: {
-      os << "[<missing item at " << step->key << ">]";
-      return os;
-    }
-    default: {
-      TVM_FFI_THROW(InternalError) << "Unknown access step kind: " << 
static_cast<int>(step->kind);
-    }
-  }
-  return os;
-}
-
-inline std::ostream& operator<<(std::ostream& os, const AccessPath& path) {
-  ffi::Array<AccessStep> steps = path->ToSteps();
-  os << "<root>";
-  for (const auto& step : steps) {
-    os << step;
-  }
-  return os;
-}
-}  // namespace reflection
-}  // namespace ffi
-}  // namespace tvm
 #endif  // TVM_NODE_REPR_PRINTER_H_
diff --git a/include/tvm/node/script_printer.h 
b/include/tvm/node/script_printer.h
index c72ee10d2c..5eeab20107 100644
--- a/include/tvm/node/script_printer.h
+++ b/include/tvm/node/script_printer.h
@@ -161,7 +161,7 @@ class TVM_DLL PrinterConfig : public ObjectRef {
                                                 PrinterConfigNode);
 };
 
-/*! \brief Legacy behavior of ReprPrinter. */
+/*! \brief TVMScript-based printer for IR nodes. */
 class TVMScriptPrinter {
  public:
   /* Convert the object to TVMScript format */
diff --git a/include/tvm/relax/exec_builder.h b/include/tvm/relax/exec_builder.h
index 66bae5411a..f85b5af460 100644
--- a/include/tvm/relax/exec_builder.h
+++ b/include/tvm/relax/exec_builder.h
@@ -28,7 +28,7 @@
 #include <tvm/ffi/function.h>
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ir/expr.h>
-#include <tvm/node/repr_printer.h>
+#include <tvm/node/repr.h>
 #include <tvm/runtime/object.h>
 #include <tvm/runtime/vm/bytecode.h>
 #include <tvm/runtime/vm/executable.h>
diff --git a/include/tvm/s_tir/meta_schedule/mutator.h 
b/include/tvm/s_tir/meta_schedule/mutator.h
index 76f41f998f..42708dec57 100644
--- a/include/tvm/s_tir/meta_schedule/mutator.h
+++ b/include/tvm/s_tir/meta_schedule/mutator.h
@@ -162,6 +162,8 @@ class PyMutatorNode : public MutatorNode {
   FAsString f_as_string;
 
   static void RegisterReflection() {
+    namespace refl = tvm::ffi::reflection;
+    refl::ObjectDef<PyMutatorNode>();
     // `f_initialize_with_tune_context` is not registered
     // `f_apply` is not registered
     // `f_clone` is not registered
diff --git a/src/arith/canonical_simplify.cc b/src/arith/canonical_simplify.cc
index 68696b9de1..3d1d55269d 100644
--- a/src/arith/canonical_simplify.cc
+++ b/src/arith/canonical_simplify.cc
@@ -535,40 +535,7 @@ void SumExprNode::AddToSelf(const SumExpr& other, int64_t 
scale) {
   this->AddToSelf(other->base * scale);
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<SplitExprNode>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* op = static_cast<const SplitExprNode*>(node.get());
-      auto factor_str = [](int64_t f) {
-        return f == SplitExprNode::kPosInf ? std::string("+inf") : 
std::to_string(f);
-      };
-      p->stream << "split(";
-      p->Print(op->index);
-      p->stream << ", lower=" << factor_str(op->lower_factor)
-                << ", upper=" << factor_str(op->upper_factor) << ", scale=" << 
op->scale
-                << ", div_mode=";
-      switch (op->div_mode) {
-        // No "default", so that the compiler will emit a warning if more div 
modes are
-        // added that are not covered by the switch.
-        case kTruncDiv:
-          p->stream << "truncdiv";
-          break;
-        case kFloorDiv:
-          p->stream << "floordiv";
-          break;
-      }
-      p->stream << ')';
-    });
-
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<SumExprNode>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* op = static_cast<const SumExprNode*>(node.get());
-      p->stream << "sum(base=" << op->base;
-      for (const SplitExpr& s : op->args) {
-        p->stream << ", ";
-        p->Print(s);
-      }
-      p->stream << ')';
-    });
+// Pattern A (RM): auto-default repr from reflection for SplitExprNode and 
SumExprNode.
 
 // Sub-class RewriteSimplifier::Impl to take benefit of
 // rewriter for condition simplification etc.
diff --git a/src/arith/const_int_bound.cc b/src/arith/const_int_bound.cc
index 8e306ba966..4b0b57d84f 100644
--- a/src/arith/const_int_bound.cc
+++ b/src/arith/const_int_bound.cc
@@ -67,15 +67,7 @@ inline void PrintBoundValue(std::ostream& os, int64_t val) {
   }
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<ConstIntBoundNode>([](const ObjectRef& node, ReprPrinter* p) 
{
-      auto* op = static_cast<const ConstIntBoundNode*>(node.get());
-      p->stream << "ConstIntBound[";
-      PrintBoundValue(p->stream, op->min_value);
-      p->stream << ',';
-      PrintBoundValue(p->stream, op->max_value);
-      p->stream << ']';
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 // internal entry for const int bound
 struct ConstIntBoundAnalyzer::Entry {
diff --git a/src/arith/int_constraints.cc b/src/arith/int_constraints.cc
index 6d8e539357..be6dcdeb35 100644
--- a/src/arith/int_constraints.cc
+++ b/src/arith/int_constraints.cc
@@ -219,12 +219,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<IntGroupBoundsNode>([](const ObjectRef& node, ReprPrinter* 
p) {
-      auto* op = static_cast<const IntGroupBoundsNode*>(node.get());
-      p->stream << "IntGroupBounds(coef=" << op->coef << ", lower=" << 
op->lower
-                << ", equal=" << op->equal << ", upper=" << op->upper << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 IntConstraints::IntConstraints(ffi::Array<Var> variables, ffi::Map<Var, Range> 
ranges,
                                ffi::Array<PrimExpr> relations) {
@@ -255,12 +250,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<IntConstraintsNode>([](const ObjectRef& node, ReprPrinter* 
p) {
-      auto* op = static_cast<const IntConstraintsNode*>(node.get());
-      p->stream << "IntConstraints(" << op->variables << ", " << op->ranges << 
", " << op->relations
-                << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 IntConstraintsTransform::IntConstraintsTransform(IntConstraints src, 
IntConstraints dst,
                                                  ffi::Map<Var, PrimExpr> 
src_to_dst,
@@ -302,13 +292,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
                         });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<IntConstraintsTransformNode>([](const ObjectRef& node, 
ReprPrinter* p) {
-      auto* op = static_cast<const IntConstraintsTransformNode*>(node.get());
-      p->stream << "IntConstraintsTransform("
-                << "\n\t" << op->src << "\n\t" << op->dst << "\n\t" << 
op->src_to_dst << "\n\t"
-                << op->dst_to_src << "\n)";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 }  // namespace arith
 }  // namespace tvm
diff --git a/src/arith/int_set.cc b/src/arith/int_set.cc
index c8e1d73771..f48a1e1e26 100644
--- a/src/arith/int_set.cc
+++ b/src/arith/int_set.cc
@@ -1227,12 +1227,7 @@ ffi::Array<IntSet> EstimateRegionUpperBound(const 
ffi::Array<Range>& region,
   return result;
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<IntervalSetNode>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* op = static_cast<const IntervalSetNode*>(node.get());
-      p->stream << "IntervalSet"
-                << "[" << op->min_value << ", " << op->max_value << ']';
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
diff --git a/src/arith/iter_affine_map.cc b/src/arith/iter_affine_map.cc
index a4d5097167..d42114e579 100644
--- a/src/arith/iter_affine_map.cc
+++ b/src/arith/iter_affine_map.cc
@@ -61,11 +61,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
                         [](PrimExpr source, PrimExpr extent) { return 
IterMark(source, extent); });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<IterMarkNode>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* op = static_cast<const IterMarkNode*>(node.get());
-      p->stream << "IterMark(" << op->source << ", extent=" << op->extent << 
")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 IterSplitExpr::IterSplitExpr(IterMark source) {
   auto n = ffi::make_object<IterSplitExprNode>();
@@ -108,12 +104,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
   });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<IterSplitExprNode>([](const ObjectRef& node, ReprPrinter* p) 
{
-      auto* op = static_cast<const IterSplitExprNode*>(node.get());
-      p->stream << "IterSplit(" << op->source << ", lower_factor=" << 
op->lower_factor
-                << ", extent=" << op->extent << ", scale=" << op->scale << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 IterSumExpr::IterSumExpr(ffi::Array<IterSplitExpr> args, PrimExpr base) {
   auto n = ffi::make_object<IterSumExprNode>();
@@ -130,11 +121,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
   });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<IterSumExprNode>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* op = static_cast<const IterSumExprNode*>(node.get());
-      p->stream << "IterSum(" << op->args << ", " << op->base << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 /*!
  * \brief Collector that collects the outgoing split reference of each 
IterMark.
diff --git a/src/arith/modular_set.cc b/src/arith/modular_set.cc
index 9aaa81b1ba..9bc45bb77f 100644
--- a/src/arith/modular_set.cc
+++ b/src/arith/modular_set.cc
@@ -49,12 +49,8 @@ ModularSet::ModularSet(int64_t coeff, int64_t base) {
   data_ = std::move(node);
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<ModularSetNode>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* op = static_cast<const ModularSetNode*>(node.get());
-      p->stream << "ModularSet("
-                << "coeff=" << op->coeff << ", base=" << op->base << ')';
-    });
+// Pattern A (RM): auto-default repr from reflection produces
+// "arith.ModularSet(coeff=..., base=...)"
 
 ModularSet MakeModularSet(int64_t coeff, int64_t base) { return 
ModularSet(coeff, base); }
 
diff --git a/src/arith/presburger_set.cc b/src/arith/presburger_set.cc
index 3c7bd25d86..a62c34c525 100644
--- a/src/arith/presburger_set.cc
+++ b/src/arith/presburger_set.cc
@@ -260,15 +260,7 @@ IntSet EvalSet(const PrimExpr& e, const PresburgerSet& 
set) {
   return result;
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<PresburgerSetNode>([](const ObjectRef& node, ReprPrinter* p) 
{
-      auto set = node.as<PresburgerSetNode>();
-      TVM_FFI_ICHECK(ret) << "Unknown type:" << node->GetTypeKey();
-      p->stream << "{";
-      p->stream << set->GetVars() << ": ";
-      p->stream << node.as<PresburgerSetNode>()->GenerateConstraint();
-      p->stream << "}";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 #else  // defined(TVM_MLIR_VERSION) && TVM_MLIR_VERSION >= 150
 
diff --git a/src/arith/rewrite_simplify.cc b/src/arith/rewrite_simplify.cc
index cb96bb07f6..8649bf96be 100644
--- a/src/arith/rewrite_simplify.cc
+++ b/src/arith/rewrite_simplify.cc
@@ -2428,16 +2428,7 @@ RewriteSimplifier::RewriteSimplifier(Analyzer* parent) : 
impl_(new Impl(parent))
 
 RewriteSimplifier::~RewriteSimplifier() { delete impl_; }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<RewriteSimplifierStatsNode>([](const ObjectRef& node, 
ReprPrinter* p) {
-      auto* ptr = node.as<RewriteSimplifierStatsNode>();
-      p->stream << "RewriteSimplifierStats(nodes_visited = " << 
ptr->nodes_visited
-                << ", constraints_entered = " << ptr->constraints_entered
-                << ", rewrites_attempted = " << ptr->rewrites_attempted
-                << ", rewrites_performed = " << ptr->rewrites_performed
-                << ", max_recursive_depth = " << ptr->max_recursive_depth
-                << ", num_recursive_rewrites = " << 
ptr->num_recursive_rewrites << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 }  // namespace arith
 }  // namespace tvm
diff --git a/src/ir/env_func.cc b/src/ir/env_func.cc
index e90ea07a0b..5c64047c96 100644
--- a/src/ir/env_func.cc
+++ b/src/ir/env_func.cc
@@ -33,11 +33,7 @@ using ffi::Any;
 using ffi::Function;
 using ffi::PackedArgs;
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<EnvFuncNode>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* op = static_cast<const EnvFuncNode*>(node.get());
-      p->stream << "EnvFunc(" << op->name << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 ObjectPtr<Object> CreateEnvNode(const std::string& name) {
   auto f = tvm::ffi::Function::GetGlobal(name);
diff --git a/src/ir/expr.cc b/src/ir/expr.cc
index f9d6e0fc60..b5cc09e11d 100644
--- a/src/ir/expr.cc
+++ b/src/ir/expr.cc
@@ -225,10 +225,8 @@ TVM_FFI_STATIC_INIT_BLOCK() {
         ss << ref;
         return ss.str();
       });
-  refl::TypeAttrDef<GlobalVarNode>().def(
-      refl::type_attr::kRepr, [](GlobalVar gvar, ffi::Function) -> ffi::String 
{
-        return "I.GlobalVar(\"" + std::string(gvar->name_hint) + "\")";
-      });
+  // Note: kRepr for GlobalVarNode is registered in script/printer/ir/ir.cc
+  // via TVM_SCRIPT_REPR(GlobalVarNode, ReprPrintIR).
 }
 
 }  // namespace tvm
diff --git a/src/ir/instrument.cc b/src/ir/instrument.cc
index 8d1dd2ecf5..ced20ba1b1 100644
--- a/src/ir/instrument.cc
+++ b/src/ir/instrument.cc
@@ -25,7 +25,7 @@
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ir/instrument.h>
 #include <tvm/ir/transform.h>
-#include <tvm/node/repr_printer.h>
+#include <tvm/node/repr.h>
 
 #include <chrono>
 #include <stack>
@@ -190,11 +190,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<BasePassInstrumentNode>([](const ObjectRef& ref, 
ReprPrinter* p) {
-      auto* node = static_cast<const BasePassInstrumentNode*>(ref.get());
-      p->stream << node->name;
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 /*! \brief PassProfile stores profiling information for a given pass and its 
sub-passes. */
 struct PassProfile {
diff --git a/src/ir/op.cc b/src/ir/op.cc
index 3a260e886a..fb5751b63f 100644
--- a/src/ir/op.cc
+++ b/src/ir/op.cc
@@ -160,10 +160,6 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       .def("__data_from_json__", [](const ffi::String& name) -> Op { return 
Op::Get(name); });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<OpNode>([](const ObjectRef& ref, ReprPrinter* p) {
-      auto* node = static_cast<const OpNode*>(ref.get());
-      p->stream << "Op(" << node->name << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 }  // namespace tvm
diff --git a/src/ir/source_map.cc b/src/ir/source_map.cc
index 7b94890623..89842cfc05 100644
--- a/src/ir/source_map.cc
+++ b/src/ir/source_map.cc
@@ -20,6 +20,7 @@
  * \file source_map.cc
  * \brief The implementation of the source map data structure.
  */
+#include <tvm/ffi/extra/dataclass.h>
 #include <tvm/ffi/function.h>
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ir/source_map.h>
@@ -73,11 +74,15 @@ TVM_FFI_STATIC_INIT_BLOCK() {
   refl::GlobalDef().def("ir.SourceName", SourceName::Get);
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<SourceNameNode>([](const ObjectRef& ref, ReprPrinter* p) {
-      auto* node = static_cast<const SourceNameNode*>(ref.get());
-      p->stream << "SourceName(" << node->name << ", " << node << ")";
-    });
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  refl::TypeAttrDef<SourceNameNode>().def(
+      refl::type_attr::kRepr, [](SourceName sn, ffi::Function) -> ffi::String {
+        std::ostringstream os;
+        os << "SourceName(" << sn->name << ", " << static_cast<const 
void*>(sn.get()) << ")";
+        return os.str();
+      });
+}
 
 Span::Span(SourceName source_name, int line, int end_line, int column, int 
end_column) {
   auto n = ffi::make_object<SpanNode>();
@@ -150,25 +155,32 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       .def("ir.SequentialSpan", [](tvm::ffi::Array<Span> spans) { return 
SequentialSpan(spans); });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<SpanNode>([](const ObjectRef& ref, ReprPrinter* p) {
-      auto* node = static_cast<const SpanNode*>(ref.get());
-      p->stream << "Span(" << node->source_name << ", " << node->line << ", " 
<< node->end_line
-                << ", " << node->column << ", " << node->end_column << ")";
-    });
-
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<SequentialSpanNode>([](const ObjectRef& ref, ReprPrinter* p) 
{
-      auto* node = static_cast<const SequentialSpanNode*>(ref.get());
-
-      p->stream << "SequentailSpan([ ";
-      int index = 0;
-      const int last = node->spans.size() - 1;
-      while (index < last) {
-        p->stream << node->spans[index++] << ", ";
-      }
-      p->stream << node->spans[last] << " ])";
-    });
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  refl::TypeAttrDef<SpanNode>().def(
+      refl::type_attr::kRepr, [](Span span, ffi::Function fn_repr) -> 
ffi::String {
+        std::ostringstream os;
+        os << "Span(" << 
fn_repr(ffi::AnyView(span->source_name)).cast<ffi::String>() << ", "
+           << span->line << ", " << span->end_line << ", " << span->column << 
", "
+           << span->end_column << ")";
+        return os.str();
+      });
+  refl::TypeAttrDef<SequentialSpanNode>().def(
+      refl::type_attr::kRepr, [](SequentialSpan seq, ffi::Function fn_repr) -> 
ffi::String {
+        // Fix typo: was "SequentailSpan", now "SequentialSpan"
+        std::ostringstream os;
+        os << "SequentialSpan([ ";
+        const int last = static_cast<int>(seq->spans.size()) - 1;
+        for (int i = 0; i < last; ++i) {
+          os << fn_repr(ffi::AnyView(seq->spans[i])).cast<ffi::String>() << ", 
";
+        }
+        if (last >= 0) {
+          os << fn_repr(ffi::AnyView(seq->spans[last])).cast<ffi::String>();
+        }
+        os << " ])";
+        return os.str();
+      });
+}
 
 /*! \brief Construct a source from a string. */
 Source::Source(SourceName src_name, std::string source) {
diff --git a/src/ir/structural_equal.cc b/src/ir/structural_equal.cc
index 1d7cbd23d0..b8f80f4d57 100644
--- a/src/ir/structural_equal.cc
+++ b/src/ir/structural_equal.cc
@@ -25,7 +25,7 @@
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ir/module.h>
 #include <tvm/node/functor.h>
-#include <tvm/node/repr_printer.h>
+#include <tvm/node/repr.h>
 #include <tvm/node/script_printer.h>
 
 #include <optional>
diff --git a/src/ir/structural_hash.cc b/src/ir/structural_hash.cc
index ad74742e51..b875f86625 100644
--- a/src/ir/structural_hash.cc
+++ b/src/ir/structural_hash.cc
@@ -97,11 +97,7 @@ struct ReportNodeTrait {
 
 TVM_FFI_STATIC_INIT_BLOCK() { ReportNodeTrait::RegisterReflection(); }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<runtime::profiling::ReportNode>([](const ObjectRef& node, 
ReprPrinter* p) {
-      auto* op = static_cast<const 
runtime::profiling::ReportNode*>(node.get());
-      p->stream << op->AsTable();
-    });
+// Pattern A (RM): auto-default repr from reflection for ReportNode.
 
 struct CountNodeTrait {
   static void RegisterReflection() {
@@ -113,11 +109,7 @@ struct CountNodeTrait {
 
 TVM_FFI_STATIC_INIT_BLOCK() { CountNodeTrait::RegisterReflection(); }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<runtime::profiling::CountNode>([](const ObjectRef& node, 
ReprPrinter* p) {
-      auto* op = static_cast<const runtime::profiling::CountNode*>(node.get());
-      p->stream << op->GetTypeKey() << "(" << op->value << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection for CountNode.
 
 struct DurationNodeTrait {
   static void RegisterReflection() {
@@ -129,11 +121,7 @@ struct DurationNodeTrait {
 
 TVM_FFI_STATIC_INIT_BLOCK() { DurationNodeTrait::RegisterReflection(); }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<runtime::profiling::DurationNode>([](const ObjectRef& node, 
ReprPrinter* p) {
-      auto* op = static_cast<const 
runtime::profiling::DurationNode*>(node.get());
-      p->stream << op->GetTypeKey() << "(" << op->microseconds << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection for DurationNode.
 
 struct PercentNodeTrait {
   static void RegisterReflection() {
@@ -145,11 +133,7 @@ struct PercentNodeTrait {
 
 TVM_FFI_STATIC_INIT_BLOCK() { PercentNodeTrait::RegisterReflection(); }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<runtime::profiling::PercentNode>([](const ObjectRef& node, 
ReprPrinter* p) {
-      auto* op = static_cast<const 
runtime::profiling::PercentNode*>(node.get());
-      p->stream << op->GetTypeKey() << "(" << op->percent << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection for PercentNode.
 
 struct RatioNodeTrait {
   static void RegisterReflection() {
@@ -161,10 +145,6 @@ struct RatioNodeTrait {
 
 TVM_FFI_STATIC_INIT_BLOCK() { RatioNodeTrait::RegisterReflection(); }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<runtime::profiling::RatioNode>([](const ObjectRef& node, 
ReprPrinter* p) {
-      auto* op = static_cast<const runtime::profiling::RatioNode*>(node.get());
-      p->stream << op->GetTypeKey() << "(" << op->ratio << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection for RatioNode.
 
 }  // namespace tvm
diff --git a/src/ir/transform.cc b/src/ir/transform.cc
index 9d2dec6b1a..8c56f737d4 100644
--- a/src/ir/transform.cc
+++ b/src/ir/transform.cc
@@ -26,7 +26,7 @@
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ffi/rvalue_ref.h>
 #include <tvm/ir/transform.h>
-#include <tvm/node/repr_printer.h>
+#include <tvm/node/repr.h>
 #include <tvm/relax/expr.h>
 #include <tvm/runtime/device_api.h>
 
@@ -35,7 +35,6 @@
 namespace tvm {
 namespace transform {
 
-using tvm::ReprPrinter;
 using tvm::ffi::Any;
 
 TVM_REGISTER_PASS_CONFIG_OPTION("testing.immutable_module", Bool);
@@ -505,23 +504,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<PassInfoNode>([](const ObjectRef& ref, tvm::ReprPrinter* p) {
-      auto* node = static_cast<const PassInfoNode*>(ref.get());
-      p->stream << "The meta data of the pass - ";
-      p->stream << "pass name: " << node->name;
-      p->stream << ", opt_level: " << node->opt_level;
-      if (node->required.empty()) {
-        p->stream << ", required passes: []\n";
-      } else {
-        p->stream << ", required passes: ["
-                  << "\n";
-        for (const auto& it : node->required) {
-          p->stream << it << ", ";
-        }
-        p->stream << "]\n";
-      }
-    });
+// Pattern A (RM): auto-default repr from reflection for PassInfoNode.
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   PassContextNode::RegisterReflection();
@@ -545,13 +528,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
            [](Pass pass, ffi::RValueRef<IRModule> mod) { return 
pass(*std::move(mod)); });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<ModulePassNode>([](const ObjectRef& ref, ReprPrinter* p) {
-      auto* node = static_cast<const ModulePassNode*>(ref.get());
-      const PassInfo info = node->Info();
-      p->stream << "Run Module pass: " << info->name << " at the optimization 
level "
-                << info->opt_level;
-    });
+// Pattern A (RM): auto-default repr from reflection for ModulePassNode.
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
@@ -566,19 +543,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
   });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<SequentialNode>([](const ObjectRef& ref, ReprPrinter* p) {
-      auto* node = static_cast<const SequentialNode*>(ref.get());
-      const PassInfo info = node->Info();
-      p->stream << "Run Sequential pass: " << info->name << " at the 
optimization level "
-                << info->opt_level << ". ";
-      p->stream << "The passes will be executed are: [";
-      for (const auto& it : node->passes) {
-        const PassInfo pass_info = it->Info();
-        p->stream << pass_info->name << " ";
-      }
-      p->stream << "]";
-    });
+// Pattern A (RM): auto-default repr from reflection for SequentialNode.
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
@@ -602,19 +567,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<PassContextNode>([](const ObjectRef& ref, ReprPrinter* p) {
-      auto* node = static_cast<const PassContextNode*>(ref.get());
-      p->stream << "Pass context information: "
-                << "\n";
-      p->stream << "\topt_level: " << node->opt_level << "\n";
-
-      p->stream << "\trequired passes: " << node->required_pass << "\n";
-      p->stream << "\tdisabled passes: " << node->disabled_pass << "\n";
-      p->stream << "\tinstruments: " << node->instruments << "\n";
-
-      p->stream << "\tconfig: " << node->config << "\n";
-    });
+// Pattern A (RM): auto-default repr from reflection for PassContextNode.
 
 class PassContext::Internal {
  public:
diff --git a/src/node/container_printing.cc b/src/node/container_printing.cc
index b4773b2a81..3a6700c788 100644
--- a/src/node/container_printing.cc
+++ b/src/node/container_printing.cc
@@ -18,51 +18,9 @@
  */
 
 /*!
- * Printer implementation for containers
- * \file node/container_printint.cc
+ * \file node/container_printing.cc
+ * \brief DEPRECATED — tvm-ffi provides built-in repr for Array/Map/Shape.
+ *        The legacy ReprPrinter dispatches for containers are no longer 
needed.
  */
-#include <tvm/ffi/function.h>
-#include <tvm/node/cast.h>
-#include <tvm/node/functor.h>
-#include <tvm/node/repr_printer.h>
-
-namespace tvm {
-
-// Container printer
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<ffi::ArrayObj>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* op = static_cast<const ffi::ArrayObj*>(node.get());
-      p->stream << '[';
-      for (size_t i = 0; i < op->size(); ++i) {
-        if (i != 0) {
-          p->stream << ", ";
-        }
-        p->Print(op->at(i));
-      }
-      p->stream << ']';
-    });
-
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<ffi::MapObj>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* op = static_cast<const ffi::MapObj*>(node.get());
-      p->stream << '{';
-      for (auto it = op->begin(); it != op->end(); ++it) {
-        if (it != op->begin()) {
-          p->stream << ", ";
-        }
-        if (auto opt_str = it->first.as<ffi::String>()) {
-          p->stream << '\"' << opt_str.value() << "\": ";
-        } else {
-          p->Print(it->first);
-          p->stream << ": ";
-        }
-        p->Print(it->second);
-      }
-      p->stream << '}';
-    });
-
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<ffi::ShapeObj>([](const ObjectRef& node, ReprPrinter* p) {
-      p->stream << Downcast<ffi::Shape>(node);
-    });
-}  // namespace tvm
+// This file is intentionally empty. Container repr is handled by
+// ffi::ReprPrint (tvm-ffi/src/ffi/extra/dataclass.cc).
diff --git a/src/node/repr.cc b/src/node/repr.cc
new file mode 100644
index 0000000000..a194708c6d
--- /dev/null
+++ b/src/node/repr.cc
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*!
+ * \file node/repr.cc
+ * \brief Implements Dump helpers and FFI registration for ffi-repr-based 
printing.
+ *
+ * The legacy ReprPrinter has been replaced by ffi::ReprPrint.  This file:
+ *  - Implements the Dump() debug helpers (they call ffi::ReprPrint).
+ *  - Registers node.AsRepr (for backward Python compatibility) via 
ffi::ReprPrint.
+ *  - Registers __ffi_repr__ hooks for AccessPath and AccessStep.
+ */
+#include <tvm/ffi/extra/dataclass.h>
+#include <tvm/ffi/function.h>
+#include <tvm/ffi/reflection/access_path.h>
+#include <tvm/ffi/reflection/registry.h>
+#include <tvm/node/repr.h>
+#include <tvm/runtime/device_api.h>
+
+#include <sstream>
+
+namespace tvm {
+
+void Dump(const runtime::ObjectRef& n) { std::cerr << 
ffi::ReprPrint(ffi::Any(n)) << "\n"; }
+
+void Dump(const runtime::Object* n) { 
Dump(runtime::GetRef<runtime::ObjectRef>(n)); }
+
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  // node.AsRepr: backward-compatible Python entry point.
+  // Python's tvm.runtime._ffi_node_api sets __object_repr__ = AsRepr via 
init_ffi_api.
+  refl::GlobalDef().def("node.AsRepr",
+                        [](ffi::Any obj) -> ffi::String { return 
ffi::ReprPrint(obj); });
+  // Register __ffi_repr__ for AccessPath/AccessStep so that ffi.ReprPrint
+  // uses the concise "<root>.field[idx]" format.
+  //
+  // AccessStep: format one step fragment (e.g. ".field", "[0]", "[key]?").
+  refl::TypeAttrDef<ffi::reflection::AccessStepObj>().def(
+      refl::type_attr::kRepr,
+      [](ffi::reflection::AccessStep step, ffi::Function fn_repr) -> 
ffi::String {
+        using ffi::reflection::AccessKind;
+        std::ostringstream os;
+        switch (step->kind) {
+          case AccessKind::kAttr:
+            os << "." << step->key.cast<ffi::String>();
+            break;
+          case AccessKind::kArrayItem:
+            os << "[" << step->key.cast<int64_t>() << "]";
+            break;
+          case AccessKind::kMapItem:
+            os << "[" << fn_repr(step->key).cast<ffi::String>() << "]";
+            break;
+          case AccessKind::kAttrMissing:
+            os << "." << step->key.cast<ffi::String>() << "?";
+            break;
+          case AccessKind::kArrayItemMissing:
+            os << "[" << step->key.cast<int64_t>() << "]?";
+            break;
+          case AccessKind::kMapItemMissing:
+            os << "[" << fn_repr(step->key).cast<ffi::String>() << "]?";
+            break;
+        }
+        return os.str();
+      });
+  // AccessPath: recurse through parent via fn_repr rather than walking the
+  // linked list manually.  Root (no step) emits "<root>"; each non-root node
+  // prepends its parent's repr and appends the current step's repr.
+  refl::TypeAttrDef<ffi::reflection::AccessPathObj>().def(
+      refl::type_attr::kRepr,
+      [](ffi::reflection::AccessPath path, ffi::Function fn_repr) -> 
ffi::String {
+        if (!path->step.has_value()) {
+          // Root node: no parent, no step.
+          return "<root>";
+        }
+        std::ostringstream os;
+        os << fn_repr(path->parent.value()).cast<ffi::String>();
+        os << fn_repr(path->step.value()).cast<ffi::String>();
+        return os.str();
+      });
+}
+}  // namespace tvm
diff --git a/src/node/repr_printer.cc b/src/node/repr_printer.cc
index 142ad74eb5..de01909c8d 100644
--- a/src/node/repr_printer.cc
+++ b/src/node/repr_printer.cc
@@ -18,191 +18,8 @@
  */
 
 /*!
- * Printer utilities
  * \file node/repr_printer.cc
+ * \brief DEPRECATED — implementation moved to src/node/repr.cc.
  */
-#include <tvm/ffi/function.h>
-#include <tvm/ffi/reflection/access_path.h>
-#include <tvm/ffi/reflection/registry.h>
-#include <tvm/node/cast.h>
-#include <tvm/node/repr_printer.h>
-#include <tvm/runtime/device_api.h>
-
-#include <sstream>
-#include <vector>
-
-#include "../support/str_escape.h"
-
-namespace tvm {
-
-void ReprPrinter::Print(const ObjectRef& node) {
-  static const FType& f = vtable();
-  if (!node.defined()) {
-    stream << "(nullptr)";
-  } else {
-    if (f.can_dispatch(node)) {
-      f(node, this);
-    } else {
-      // default value, output type key and addr.
-      stream << node->GetTypeKey() << "(" << node.get() << ")";
-    }
-  }
-}
-
-void ReprPrinter::Print(const ffi::Any& node) {
-  switch (node.type_index()) {
-    case ffi::TypeIndex::kTVMFFINone: {
-      stream << "(nullptr)";
-      break;
-    }
-    case ffi::TypeIndex::kTVMFFIInt: {
-      stream << node.cast<int64_t>();
-      break;
-    }
-    case ffi::TypeIndex::kTVMFFIBool: {
-      stream << node.cast<bool>();
-      break;
-    }
-    case ffi::TypeIndex::kTVMFFIFloat: {
-      stream << node.cast<double>();
-      break;
-    }
-    case ffi::TypeIndex::kTVMFFIOpaquePtr: {
-      stream << node.cast<void*>();
-      break;
-    }
-    case ffi::TypeIndex::kTVMFFIDataType: {
-      stream << node.cast<DataType>();
-      break;
-    }
-    case ffi::TypeIndex::kTVMFFIDevice: {
-      runtime::operator<<(stream, node.cast<Device>());
-      break;
-    }
-    case ffi::TypeIndex::kTVMFFIObject: {
-      Print(node.cast<ObjectRef>());
-      break;
-    }
-    case ffi::TypeIndex::kTVMFFISmallStr:
-    case ffi::TypeIndex::kTVMFFIStr: {
-      ffi::String str = node.cast<ffi::String>();
-      stream << '"' << support::StrEscape(str.data(), str.size()) << '"';
-      break;
-    }
-    case ffi::TypeIndex::kTVMFFISmallBytes:
-    case ffi::TypeIndex::kTVMFFIBytes: {
-      ffi::Bytes bytes = node.cast<ffi::Bytes>();
-      stream << "b\"" << support::StrEscape(bytes.data(), bytes.size()) << '"';
-      break;
-    }
-    default: {
-      if (auto opt_obj = node.as<ObjectRef>()) {
-        Print(opt_obj.value());
-      } else {
-        stream << "Any(type_key=`" << node.GetTypeKey() << "`)";
-      }
-      break;
-    }
-  }
-}
-
-void ReprPrinter::PrintIndent() {
-  for (int i = 0; i < indent; ++i) {
-    stream << ' ';
-  }
-}
-
-ReprPrinter::FType& ReprPrinter::vtable() {
-  static FType inst;
-  return inst;
-}
-
-void Dump(const runtime::ObjectRef& n) { std::cerr << n << "\n"; }
-
-void Dump(const runtime::Object* n) { 
Dump(runtime::GetRef<runtime::ObjectRef>(n)); }
-
-namespace {
-/*!
- * \brief Format an AccessStep as a concise string fragment.
- *
- * For map keys, uses ffi.ReprPrint which dispatches to __ffi_repr__.
- */
-void FormatAccessStep(std::ostringstream& os, const 
ffi::reflection::AccessStep& step) {
-  using ffi::reflection::AccessKind;
-  static const ffi::Function repr_fn = 
ffi::Function::GetGlobal("ffi.ReprPrint").value();
-  switch (step->kind) {
-    case AccessKind::kAttr:
-      os << "." << step->key.cast<ffi::String>();
-      break;
-    case AccessKind::kArrayItem:
-      os << "[" << step->key.cast<int64_t>() << "]";
-      break;
-    case AccessKind::kMapItem:
-      os << "[" << repr_fn(step->key).cast<ffi::String>() << "]";
-      break;
-    case AccessKind::kAttrMissing:
-      os << "." << step->key.cast<ffi::String>() << "?";
-      break;
-    case AccessKind::kArrayItemMissing:
-      os << "[" << step->key.cast<int64_t>() << "]?";
-      break;
-    case AccessKind::kMapItemMissing:
-      os << "[" << repr_fn(step->key).cast<ffi::String>() << "]?";
-      break;
-  }
-}
-
-/*!
- * \brief Format an AccessPath as "<root>.field[idx]".
- */
-ffi::String FormatAccessPath(const ffi::reflection::AccessPath& path) {
-  std::vector<ffi::reflection::AccessStep> steps;
-  const ffi::reflection::AccessPathObj* cur = path.get();
-  while (cur->step.defined()) {
-    steps.push_back(cur->step.value());
-    cur = static_cast<const 
ffi::reflection::AccessPathObj*>(cur->parent.get());
-  }
-  std::ostringstream os;
-  os << "<root>";
-  for (auto it = steps.rbegin(); it != steps.rend(); ++it) {
-    FormatAccessStep(os, *it);
-  }
-  return os.str();
-}
-}  // namespace
-
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<ffi::reflection::AccessPathObj>([](const ObjectRef& node, 
ReprPrinter* p) {
-      p->stream << 
FormatAccessPath(Downcast<ffi::reflection::AccessPath>(node));
-    });
-
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<ffi::reflection::AccessStepObj>([](const ObjectRef& node, 
ReprPrinter* p) {
-      std::ostringstream os;
-      FormatAccessStep(os, Downcast<ffi::reflection::AccessStep>(node));
-      p->stream << os.str();
-    });
-
-TVM_FFI_STATIC_INIT_BLOCK() {
-  namespace refl = tvm::ffi::reflection;
-  refl::GlobalDef().def("node.AsRepr", [](ffi::Any obj) {
-    std::ostringstream os;
-    os << obj;
-    return os.str();
-  });
-  // Register __ffi_repr__ for AccessPath/AccessStep so that ffi.ReprPrint
-  // uses the concise "<root>.field[idx]" format instead of the dataclass repr.
-  refl::TypeAttrDef<ffi::reflection::AccessPathObj>().def(
-      refl::type_attr::kRepr,
-      [](ffi::reflection::AccessPath path, ffi::Function) -> ffi::String {
-        return FormatAccessPath(path);
-      });
-  refl::TypeAttrDef<ffi::reflection::AccessStepObj>().def(
-      refl::type_attr::kRepr,
-      [](ffi::reflection::AccessStep step, ffi::Function) -> ffi::String {
-        std::ostringstream os;
-        FormatAccessStep(os, step);
-        return os.str();
-      });
-}
-}  // namespace tvm
+// This file is intentionally empty. The legacy ReprPrinter has been removed.
+// See src/node/repr.cc for the replacement implementation.
diff --git a/src/node/script_printer.cc b/src/node/script_printer.cc
index 1774ba4b4b..2edf7860b5 100644
--- a/src/node/script_printer.cc
+++ b/src/node/script_printer.cc
@@ -16,11 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+#include <tvm/ffi/extra/dataclass.h>
 #include <tvm/ffi/function.h>
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ir/expr.h>
 #include <tvm/node/cast.h>
-#include <tvm/node/repr_printer.h>
+#include <tvm/node/repr.h>
 #include <tvm/node/script_printer.h>
 
 #include <algorithm>
@@ -39,10 +40,8 @@ TVMScriptPrinter::FType& TVMScriptPrinter::vtable() {
 std::string TVMScriptPrinter::Script(const ObjectRef& node,
                                      const ffi::Optional<PrinterConfig>& cfg) {
   if (!TVMScriptPrinter::vtable().can_dispatch(node)) {
-    std::ostringstream os;
-    ReprPrinter printer(os);
-    printer.Print(node);
-    return os.str();
+    // Fall back to ffi::ReprPrint for types not registered with 
TVMScriptPrinter.
+    return std::string(ffi::ReprPrint(ffi::Any(node)));
   }
   return TVMScriptPrinter::vtable()(node, cfg.value_or(PrinterConfig()));
 }
diff --git a/src/relax/ir/dataflow_pattern.cc b/src/relax/ir/dataflow_pattern.cc
index ce34f4705f..90a8379236 100644
--- a/src/relax/ir/dataflow_pattern.cc
+++ b/src/relax/ir/dataflow_pattern.cc
@@ -22,10 +22,13 @@
  * \brief The dataflow pattern language for Relax
  */
 
+#include <tvm/ffi/extra/dataclass.h>
 #include <tvm/ffi/reflection/registry.h>
+#include <tvm/node/repr.h>
 #include <tvm/relax/dataflow_pattern.h>
 #include <tvm/relax/dataflow_pattern_functor.h>
 
+#include <sstream>
 #include <stack>
 #include <string>
 
@@ -56,12 +59,26 @@ TVM_FFI_STATIC_INIT_BLOCK() {
   ConstantPatternNode::RegisterReflection();
 }
 
-#define RELAX_PATTERN_PRINTER_DEF(NODE_TYPE, REPR_LAMBDA)                 \
-  TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)                              \
-      .set_dispatch<NODE_TYPE>([](const ObjectRef& ref, ReprPrinter* p) { \
-        auto* node = static_cast<const NODE_TYPE*>(ref.get());            \
-        REPR_LAMBDA(p, node);                                             \
-      })
+// Helper used inside RELAX_PATTERN_PRINTER_DEF lambdas.
+// Mimics the ReprPrinter interface (p->stream, p->Print) so that existing
+// REPR_LAMBDA bodies compile without modification.
+struct PatternReprPrinterHelper {
+  std::ostringstream stream;
+  void Print(const ObjectRef& x) { stream << ffi::ReprPrint(ffi::Any(x)); }
+};
+
+#define RELAX_PATTERN_PRINTER_DEF(NODE_TYPE, REPR_LAMBDA)                      
                 \
+  TVM_FFI_STATIC_INIT_BLOCK() {                                                
                 \
+    namespace refl = tvm::ffi::reflection;                                     
                 \
+    refl::TypeAttrDef<NODE_TYPE>().def(refl::type_attr::kRepr,                 
                 \
+                                       [](ffi::ObjectRef ref, ffi::Function) 
-> ffi::String {   \
+                                         auto* node = static_cast<const 
NODE_TYPE*>(ref.get()); \
+                                         PatternReprPrinterHelper printer;     
                 \
+                                         auto* p = &printer;                   
                 \
+                                         REPR_LAMBDA(p, node);                 
                 \
+                                         return printer.stream.str();          
                 \
+                                       });                                     
                 \
+  }
 
 ExternFuncPattern::ExternFuncPattern(ffi::String global_symbol) {
   ObjectPtr<ExternFuncPatternNode> n = 
ffi::make_object<ExternFuncPatternNode>();
diff --git a/src/relax/ir/emit_te.cc b/src/relax/ir/emit_te.cc
index 004a856fd6..f5b0c4474d 100644
--- a/src/relax/ir/emit_te.cc
+++ b/src/relax/ir/emit_te.cc
@@ -29,12 +29,7 @@
 namespace tvm {
 namespace relax {
 
-// RXPlaceholderOpNode
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<RXPlaceholderOpNode>([](const ObjectRef& node, ReprPrinter* 
p) {
-      auto* op = static_cast<const RXPlaceholderOpNode*>(node.get());
-      p->stream << "rxplaceholder(" << op->name << ", " << op << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection for RXPlaceholderOpNode.
 
 TVM_FFI_STATIC_INIT_BLOCK() { RXPlaceholderOpNode::RegisterReflection(); }
 
diff --git a/src/relax/ir/expr.cc b/src/relax/ir/expr.cc
index 2fbd573a5f..e58f88f04a 100644
--- a/src/relax/ir/expr.cc
+++ b/src/relax/ir/expr.cc
@@ -27,8 +27,6 @@
 namespace tvm {
 namespace relax {
 
-using tvm::ReprPrinter;
-
 TVM_FFI_STATIC_INIT_BLOCK() {
   IdNode::RegisterReflection();
   CallNode::RegisterReflection();
diff --git a/src/relax/ir/transform.cc b/src/relax/ir/transform.cc
index 3fcdeeab83..49b32307f7 100644
--- a/src/relax/ir/transform.cc
+++ b/src/relax/ir/transform.cc
@@ -24,7 +24,7 @@
 #include <tvm/ffi/function.h>
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ffi/rvalue_ref.h>
-#include <tvm/node/repr_printer.h>
+#include <tvm/node/repr.h>
 #include <tvm/relax/analysis.h>
 #include <tvm/relax/expr_functor.h>
 #include <tvm/relax/struct_info_functor.h>
@@ -176,13 +176,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<FunctionPassNode>([](const ObjectRef& ref, ReprPrinter* p) {
-      auto* node = static_cast<const FunctionPassNode*>(ref.get());
-      const PassInfo info = node->Info();
-      p->stream << "Run Function pass: " << info->name << " at the 
optimization level "
-                << info->opt_level;
-    });
+// Pattern A (RM): auto-default repr from reflection for FunctionPassNode.
 
 class DataflowBlockPass;
 
@@ -399,13 +393,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<DataflowBlockPassNode>([](const ObjectRef& ref, ReprPrinter* 
p) {
-      auto* node = static_cast<const DataflowBlockPassNode*>(ref.get());
-      const PassInfo info = node->Info();
-      p->stream << "Run DataflowBlock pass: " << info->name << " at the 
optimization level "
-                << info->opt_level;
-    });
+// Pattern A (RM): auto-default repr from reflection for DataflowBlockPassNode.
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   FunctionPassNode::RegisterReflection();
diff --git a/src/s_tir/data_layout.cc b/src/s_tir/data_layout.cc
index bee4c2e31f..392fd7a874 100644
--- a/src/s_tir/data_layout.cc
+++ b/src/s_tir/data_layout.cc
@@ -289,11 +289,13 @@ int32_t Layout::FactorOf(const LayoutAxis& axis) const {
   return factor;
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<LayoutNode>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* l = static_cast<const LayoutNode*>(node.get());
-      p->stream << "Layout(" << l->name << ")";
-    });
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  refl::TypeAttrDef<LayoutNode>().def(refl::type_attr::kRepr,
+                                      [](Layout l, ffi::Function) -> 
ffi::String {
+                                        return "Layout(" + 
std::string(l->name) + ")";
+                                      });
+}
 
 inline bool GetStoreRule(ffi::Array<PrimExpr>* index_rule, 
ffi::Array<PrimExpr>* shape_rule,
                          const Layout& src_layout, const Layout& dst_layout) {
@@ -570,12 +572,14 @@ BijectiveLayout::BijectiveLayout(Layout src_layout, 
Layout dst_layout) {
   }
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<BijectiveLayoutNode>([](const ObjectRef& node, ReprPrinter* 
p) {
-      auto* b = static_cast<const BijectiveLayoutNode*>(node.get());
-      p->stream << "BijectiveLayout(" << b->src_layout.name() << "->" << 
b->dst_layout.name()
-                << ")";
-    });
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  refl::TypeAttrDef<BijectiveLayoutNode>().def(
+      refl::type_attr::kRepr, [](BijectiveLayout bl, ffi::Function) -> 
ffi::String {
+        return "BijectiveLayout(" + std::string(bl->src_layout.name()) + "->" +
+               std::string(bl->dst_layout.name()) + ")";
+      });
+}
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
diff --git a/src/s_tir/meta_schedule/arg_info.cc 
b/src/s_tir/meta_schedule/arg_info.cc
index f873f0a8a3..bcb2fd81c6 100644
--- a/src/s_tir/meta_schedule/arg_info.cc
+++ b/src/s_tir/meta_schedule/arg_info.cc
@@ -19,6 +19,8 @@
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/s_tir/transform.h>
 
+#include <sstream>
+
 #include "./utils.h"
 
 namespace tvm {
@@ -153,12 +155,22 @@ TensorInfo TensorInfo::FromJSON(const ObjectRef& 
json_obj) {
 
 /******** Repr ********/
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<TensorInfoNode>([](const ObjectRef& n, ReprPrinter* p) {
-      const auto* self = n.as<TensorInfoNode>();
-      TVM_FFI_ICHECK(self);
-      p->stream << "TensorInfo(\"" << self->dtype << "\", " << self->shape << 
")";
-    });
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  refl::TypeAttrDef<TensorInfoNode>().def(refl::type_attr::kRepr,
+                                          [](TensorInfo ti, ffi::Function) -> 
ffi::String {
+                                            std::ostringstream os;
+                                            os << "TensorInfo(\"" << ti->dtype 
<< "\", [";
+                                            bool first = true;
+                                            for (int64_t v : ti->shape) {
+                                              if (!first) os << ", ";
+                                              os << v;
+                                              first = false;
+                                            }
+                                            os << "])";
+                                            return os.str();
+                                          });
+}
 
 /******** FFI ********/
 TVM_FFI_STATIC_INIT_BLOCK() { TensorInfoNode::RegisterReflection(); }
diff --git a/src/s_tir/meta_schedule/cost_model/cost_model.cc 
b/src/s_tir/meta_schedule/cost_model/cost_model.cc
index 34d0cf5d77..6e9e8a464b 100644
--- a/src/s_tir/meta_schedule/cost_model/cost_model.cc
+++ b/src/s_tir/meta_schedule/cost_model/cost_model.cc
@@ -63,17 +63,16 @@ CostModel CostModel::PyCostModel(PyCostModelNode::FLoad 
f_load,        //
   return CostModel(n);
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<PyCostModelNode>([](const ObjectRef& n, ReprPrinter* p) {
-      const auto* self = n.as<PyCostModelNode>();
-      TVM_FFI_ICHECK(self);
-      PyCostModelNode::FAsString f_as_string = (*self).f_as_string;
-      TVM_FFI_ICHECK(f_as_string != nullptr) << "PyCostModel's AsString method 
not implemented!";
-      p->stream << f_as_string();
-    });
+// Pattern A (RM): auto-default repr from reflection.
+// Ensure the type index is allocated for Python registration to work.
+// (Previously, 
TVM_STATIC_IR_FUNCTOR(ReprPrinter).set_dispatch<PyCostModelNode> had
+// a side-effect of calling PyCostModelNode::RuntimeTypeIndex() which 
registered the type.)
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
+  // Trigger type index allocation for types that Python @register_object 
needs to find.
+  refl::ObjectDef<CostModelNode>();
+  refl::ObjectDef<PyCostModelNode>();
   refl::GlobalDef()
       .def_method("s_tir.meta_schedule.CostModelLoad", &CostModelNode::Load)
       .def_method("s_tir.meta_schedule.CostModelSave", &CostModelNode::Save)
diff --git a/src/s_tir/meta_schedule/feature_extractor/feature_extractor.cc 
b/src/s_tir/meta_schedule/feature_extractor/feature_extractor.cc
index e037231943..af8acc3d1c 100644
--- a/src/s_tir/meta_schedule/feature_extractor/feature_extractor.cc
+++ b/src/s_tir/meta_schedule/feature_extractor/feature_extractor.cc
@@ -40,15 +40,7 @@ FeatureExtractor FeatureExtractor::PyFeatureExtractor(
   return FeatureExtractor(n);
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<PyFeatureExtractorNode>([](const ObjectRef& n, ReprPrinter* 
p) {
-      const auto* self = n.as<PyFeatureExtractorNode>();
-      TVM_FFI_ICHECK(self);
-      PyFeatureExtractorNode::FAsString f_as_string = (*self).f_as_string;
-      TVM_FFI_ICHECK(f_as_string != nullptr)
-          << "PyFeatureExtractor's AsString method not implemented!";
-      p->stream << f_as_string();
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   FeatureExtractorNode::RegisterReflection();
diff --git a/src/s_tir/meta_schedule/measure_callback/measure_callback.cc 
b/src/s_tir/meta_schedule/measure_callback/measure_callback.cc
index 9f2a3056c2..38343338e6 100644
--- a/src/s_tir/meta_schedule/measure_callback/measure_callback.cc
+++ b/src/s_tir/meta_schedule/measure_callback/measure_callback.cc
@@ -50,15 +50,7 @@ ffi::Array<MeasureCallback, void> MeasureCallback::Default() 
{
   };
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<PyMeasureCallbackNode>([](const ObjectRef& n, ReprPrinter* 
p) {
-      const auto* self = n.as<PyMeasureCallbackNode>();
-      TVM_FFI_ICHECK(self);
-      PyMeasureCallbackNode::FAsString f_as_string = (*self).f_as_string;
-      TVM_FFI_ICHECK(f_as_string != nullptr)
-          << "PyMeasureCallback's AsString method not implemented!";
-      p->stream << f_as_string();
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   MeasureCallbackNode::RegisterReflection();
diff --git a/src/s_tir/meta_schedule/mutator/mutator.cc 
b/src/s_tir/meta_schedule/mutator/mutator.cc
index 8821a239b4..b4b46f8e2a 100644
--- a/src/s_tir/meta_schedule/mutator/mutator.cc
+++ b/src/s_tir/meta_schedule/mutator/mutator.cc
@@ -79,14 +79,7 @@ ffi::Map<Mutator, FloatImm> Mutator::DefaultHexagon() {
       {Mutator::MutateParallel(/*max_jobs_per_core=*/16), 
FloatImm(DataType::Float(64), 0.02)}};
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<PyMutatorNode>([](const ObjectRef& n, ReprPrinter* p) {
-      const auto* self = n.as<PyMutatorNode>();
-      TVM_FFI_ICHECK(self);
-      PyMutatorNode::FAsString f_as_string = (*self).f_as_string;
-      TVM_FFI_ICHECK(f_as_string != nullptr) << "PyMutator's AsString method 
not implemented!";
-      p->stream << f_as_string();
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   MutatorNode::RegisterReflection();
diff --git a/src/s_tir/meta_schedule/postproc/postproc.cc 
b/src/s_tir/meta_schedule/postproc/postproc.cc
index ac8f73f260..d9363b811c 100644
--- a/src/s_tir/meta_schedule/postproc/postproc.cc
+++ b/src/s_tir/meta_schedule/postproc/postproc.cc
@@ -111,14 +111,7 @@ ffi::Array<Postproc> Postproc::DefaultHexagon() {
   };
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<PyPostprocNode>([](const ObjectRef& n, ReprPrinter* p) {
-      const auto* self = n.as<PyPostprocNode>();
-      TVM_FFI_ICHECK(self);
-      PyPostprocNode::FAsString f_as_string = (*self).f_as_string;
-      TVM_FFI_ICHECK(f_as_string != nullptr) << "PyPostproc's AsString method 
not implemented!";
-      p->stream << f_as_string();
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   PostprocNode::RegisterReflection();
diff --git a/src/s_tir/meta_schedule/schedule_rule/schedule_rule.cc 
b/src/s_tir/meta_schedule/schedule_rule/schedule_rule.cc
index 04a9a99eba..57ca24e531 100644
--- a/src/s_tir/meta_schedule/schedule_rule/schedule_rule.cc
+++ b/src/s_tir/meta_schedule/schedule_rule/schedule_rule.cc
@@ -451,14 +451,7 @@ ffi::Array<ScheduleRule> ScheduleRule::DefaultARM(const 
ffi::String& type) {
       ScheduleRule::RandomComputeLocation());
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<PyScheduleRuleNode>([](const ObjectRef& n, ReprPrinter* p) {
-      const auto* self = n.as<PyScheduleRuleNode>();
-      TVM_FFI_ICHECK(self);
-      PyScheduleRuleNode::FAsString f_as_string = (*self).f_as_string;
-      TVM_FFI_ICHECK(f_as_string != nullptr) << "PyScheduleRule's AsString 
method not implemented!";
-      p->stream << f_as_string();
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   ScheduleRuleNode::RegisterReflection();
diff --git a/src/s_tir/schedule/instruction.cc 
b/src/s_tir/schedule/instruction.cc
index ef635462ab..a75ec7a333 100644
--- a/src/s_tir/schedule/instruction.cc
+++ b/src/s_tir/schedule/instruction.cc
@@ -79,8 +79,8 @@ ffi::String InstructionAsPythonRepr(const InstructionNode* 
self) {
     } else if (obj.as<IntImmNode>() || obj.as<FloatImmNode>()) {
       inputs.push_back(obj);
     } else if (const auto* expr = obj.as<PrimExprNode>()) {
-      PrimExpr new_expr = Substitute(
-          ffi::GetRef<PrimExpr>(expr), [](const Var& var) -> 
ffi::Optional<PrimExpr> {
+      PrimExpr new_expr =
+          Substitute(ffi::GetRef<PrimExpr>(expr), [](const Var& var) -> 
ffi::Optional<PrimExpr> {
             ObjectPtr<VarNode> new_var = ffi::make_object<VarNode>(*var.get());
             new_var->name_hint = "_";
             return Var(new_var);
@@ -103,10 +103,7 @@ ffi::String InstructionAsPythonRepr(const InstructionNode* 
self) {
 }
 }  // namespace
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<InstructionNode>([](const ObjectRef& obj, ReprPrinter* p) {
-      p->stream << InstructionAsPythonRepr(obj.as<InstructionNode>());
-    });
+// AC: kRepr already registered below in TVM_FFI_STATIC_INIT_BLOCK.
 
 /**************** FFI ****************/
 
@@ -119,10 +116,10 @@ TVM_FFI_STATIC_INIT_BLOCK() {
               ffi::Array<Any> outputs) -> Instruction {
              return Instruction(kind, inputs, attrs, outputs);
            });
-  refl::TypeAttrDef<InstructionNode>().def(
-      refl::type_attr::kRepr, [](Instruction inst, ffi::Function) -> 
ffi::String {
-        return InstructionAsPythonRepr(inst.get());
-      });
+  refl::TypeAttrDef<InstructionNode>().def(refl::type_attr::kRepr,
+                                           [](Instruction inst, ffi::Function) 
-> ffi::String {
+                                             return 
InstructionAsPythonRepr(inst.get());
+                                           });
 }
 
 }  // namespace s_tir
diff --git a/src/s_tir/schedule/trace.cc b/src/s_tir/schedule/trace.cc
index 17b169b857..fae3279e90 100644
--- a/src/s_tir/schedule/trace.cc
+++ b/src/s_tir/schedule/trace.cc
@@ -546,12 +546,6 @@ ffi::String TraceAsPythonRepr(const TraceNode* self) {
 }
 }  // namespace
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<TraceNode>([](const ObjectRef& obj, ReprPrinter* p) {
-      p->stream << TraceAsPythonRepr(obj.as<TraceNode>());
-      p->stream << std::flush;
-    });
-
 /**************** Instruction Registration ****************/
 
 struct EnterPostprocTraits : public UnpackedInstTraits<EnterPostprocTraits> {
diff --git a/src/script/printer/ir/utils.h b/src/script/printer/ir/utils.h
index 588e6066d9..6afc52cab7 100644
--- a/src/script/printer/ir/utils.h
+++ b/src/script/printer/ir/utils.h
@@ -59,7 +59,7 @@ class IRFrame : public Frame {
   TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(IRFrame, Frame, IRFrameNode);
 };
 
-/*! \brief Redirected method for the ReprPrinter */
+/*! \brief Redirected method for the ffi repr hook */
 inline std::string ReprPrintIR(const ObjectRef& obj, const PrinterConfig& cfg) 
{
   IRDocsifier d(cfg);
   With<IRFrame> f(d);
diff --git a/src/script/printer/relax/utils.h b/src/script/printer/relax/utils.h
index d6aa98fda7..9982d31da9 100644
--- a/src/script/printer/relax/utils.h
+++ b/src/script/printer/relax/utils.h
@@ -67,7 +67,7 @@ class RelaxFrame : public Frame {
   TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(RelaxFrame, Frame, 
RelaxFrameNode);
 };
 
-/*! \brief Redirected method for the ReprPrinter */
+/*! \brief Redirected method for the ffi repr hook */
 inline std::string ReprPrintRelax(const ObjectRef& obj, const PrinterConfig& 
cfg) {
   IRDocsifier d(cfg);
   With<RelaxFrame> f(d);
diff --git a/src/script/printer/tirx/utils.h b/src/script/printer/tirx/utils.h
index fb512769b0..3810b35715 100644
--- a/src/script/printer/tirx/utils.h
+++ b/src/script/printer/tirx/utils.h
@@ -166,7 +166,7 @@ inline ffi::Optional<Frame> FindLowestVarDef(const 
ObjectRef& var, const IRDocsi
   return std::nullopt;
 }
 
-/*! \brief Redirected method for the ReprPrinter */
+/*! \brief Redirected method for the ffi repr hook */
 inline std::string ReprPrintTIR(const ObjectRef& obj, const PrinterConfig& 
cfg) {
   IRDocsifier d(cfg);
   d->SetCommonPrefix(obj, [](const ObjectRef& obj) {
diff --git a/src/script/printer/utils.h b/src/script/printer/utils.h
index 78f12d4983..1ec51450e3 100644
--- a/src/script/printer/utils.h
+++ b/src/script/printer/utils.h
@@ -19,11 +19,14 @@
 #ifndef TVM_SCRIPT_PRINTER_UTILS_H_
 #define TVM_SCRIPT_PRINTER_UTILS_H_
 
+#include <tvm/ffi/extra/dataclass.h>
 #include <tvm/ffi/extra/json.h>
 #include <tvm/ffi/extra/serialization.h>
+#include <tvm/ffi/reflection/registry.h>
 #include <tvm/runtime/base.h>
 #include <tvm/script/printer/ir_docsifier.h>
 
+#include <sstream>
 #include <string>
 #include <unordered_set>
 #include <utility>
@@ -35,18 +38,25 @@ namespace tvm {
 namespace script {
 namespace printer {
 
-#define TVM_SCRIPT_REPR(ObjectType, Method)                   \
-  TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)                  \
-      .set_dispatch<ObjectType>(RedirectedReprPrinterMethod); \
-  TVM_STATIC_IR_FUNCTOR(TVMScriptPrinter, 
vtable).set_dispatch<ObjectType>(Method);
-
-inline void RedirectedReprPrinterMethod(const ObjectRef& obj, ReprPrinter* p) {
+#define TVM_SCRIPT_REPR(ObjectType, Method)                                    
                \
+  TVM_FFI_STATIC_INIT_BLOCK() {                                                
                \
+    namespace refl = tvm::ffi::reflection;                                     
                \
+    refl::TypeAttrDef<ObjectType>().def(refl::type_attr::kRepr,                
                \
+                                        [](ffi::ObjectRef obj, ffi::Function) 
-> ffi::String { \
+                                          return 
RedirectedReprPrinterMethod(obj);             \
+                                        });                                    
                \
+  }                                                                            
                \
+  TVM_STATIC_IR_FUNCTOR(TVMScriptPrinter, 
vtable).set_dispatch<ObjectType>(Method)
+
+inline std::string RedirectedReprPrinterMethod(const ObjectRef& obj) {
   try {
-    p->stream << TVMScriptPrinter::Script(obj, std::nullopt);
+    return TVMScriptPrinter::Script(obj, std::nullopt);
   } catch (const tvm::Error& e) {
     LOG(WARNING) << "TVMScript printer falls back to the basic address printer 
with the error:\n"
                  << e.what();
-    p->stream << obj->GetTypeKey() << '(' << obj.get() << ')';
+    std::ostringstream os;
+    os << obj->GetTypeKey() << '(' << obj.get() << ')';
+    return os.str();
   }
 }
 
diff --git a/src/target/target.cc b/src/target/target.cc
index 2c093ee73f..3a414d40ba 100644
--- a/src/target/target.cc
+++ b/src/target/target.cc
@@ -488,9 +488,6 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       [](Target target, ffi::Function) -> ffi::String { return target->str(); 
});
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<TargetNode>([](const ObjectRef& obj, ReprPrinter* p) {
-      p->stream << Downcast<Target>(obj)->str();
-    });
+// AC: kRepr already registered above at 
refl::TypeAttrDef<TargetNode>().def(kRepr, ...)
 
 }  // namespace tvm
diff --git a/src/target/target_kind.cc b/src/target/target_kind.cc
index 2fb5e17d5f..8d4cd50a9a 100644
--- a/src/target/target_kind.cc
+++ b/src/target/target_kind.cc
@@ -54,11 +54,12 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<TargetKindNode>([](const ObjectRef& obj, ReprPrinter* p) {
-      const TargetKind& kind = Downcast<TargetKind>(obj);
-      p->stream << kind->name;
-    });
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  refl::TypeAttrDef<TargetKindNode>().def(
+      refl::type_attr::kRepr,
+      [](TargetKind kind, ffi::Function) -> ffi::String { return kind->name; 
});
+}
 
 /**********  Registry-related code  **********/
 
diff --git a/src/target/virtual_device.cc b/src/target/virtual_device.cc
index cd9d8f4ead..c17298fdfd 100644
--- a/src/target/virtual_device.cc
+++ b/src/target/virtual_device.cc
@@ -26,45 +26,46 @@
 #include <tvm/runtime/device_api.h>
 #include <tvm/target/virtual_device.h>
 
+#include <sstream>
+
 namespace tvm {
 
 TVM_FFI_STATIC_INIT_BLOCK() { VirtualDeviceNode::RegisterReflection(); }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<VirtualDeviceNode>([](const ObjectRef& ref, ReprPrinter* p) {
-      auto* node = ref.as<VirtualDeviceNode>();
-      p->stream << "VirtualDevice(";
-      if (node->IsFullyUnconstrained()) {
-        p->stream << "?";
-      } else {
-        bool need_sep = false;
-        if (node->device_type() != kInvalidDeviceType) {
-          p->stream << "device_type=" << node->device_type();
-          need_sep = true;
-        }
-        if (node->virtual_device_id >= 0) {
-          if (need_sep) {
-            p->stream << ", ";
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  refl::TypeAttrDef<VirtualDeviceNode>().def(
+      refl::type_attr::kRepr, [](VirtualDevice vd, ffi::Function) -> 
ffi::String {
+        auto* node = vd.get();
+        std::ostringstream os;
+        os << "VirtualDevice(";
+        if (node->IsFullyUnconstrained()) {
+          os << "?";
+        } else {
+          bool need_sep = false;
+          if (node->device_type() != kInvalidDeviceType) {
+            os << "device_type=" << node->device_type();
+            need_sep = true;
           }
-          p->stream << "virtual_device_id=" << node->virtual_device_id;
-          need_sep = true;
-        }
-        if (node->target.defined()) {
-          if (need_sep) {
-            p->stream << ", ";
+          if (node->virtual_device_id >= 0) {
+            if (need_sep) os << ", ";
+            os << "virtual_device_id=" << node->virtual_device_id;
+            need_sep = true;
           }
-          p->stream << "target=" << node->target->str();
-          need_sep = true;
-        }
-        if (!node->memory_scope.empty()) {
-          if (need_sep) {
-            p->stream << ", ";
+          if (node->target.defined()) {
+            if (need_sep) os << ", ";
+            os << "target=" << node->target->str();
+            need_sep = true;
+          }
+          if (!node->memory_scope.empty()) {
+            if (need_sep) os << ", ";
+            os << "memory_scope='" << node->memory_scope << "'";
           }
-          p->stream << "memory_scope='" << node->memory_scope << "'";
         }
-      }
-      p->stream << ")";
-    });
+        os << ")";
+        return os.str();
+      });
+}
 
 VirtualDevice::VirtualDevice(int device_type_int, int virtual_device_id, 
Target target,
                              MemoryScope memory_scope) {
diff --git a/src/te/operation/compute_op.cc b/src/te/operation/compute_op.cc
index 40ac3232c3..9df3242eca 100644
--- a/src/te/operation/compute_op.cc
+++ b/src/te/operation/compute_op.cc
@@ -45,13 +45,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
   ComputeOpNode::RegisterReflection();
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<ComputeOpNode>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* op = static_cast<const ComputeOpNode*>(node.get());
-      p->stream << "compute(" << op->name << ", body=" << op->body << ", 
axis=" << op->axis
-                << ", reduce_axis=" << op->reduce_axis << ", tag=" << op->tag
-                << ", attrs=" << op->attrs << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 /// Verify if ComputeOp is valid with respect to Reduce operations.
 static void VerifyComputeOp(const ComputeOpNode* op);
diff --git a/src/te/operation/extern_op.cc b/src/te/operation/extern_op.cc
index 7149156091..b6b7c17691 100644
--- a/src/te/operation/extern_op.cc
+++ b/src/te/operation/extern_op.cc
@@ -33,12 +33,7 @@ using namespace tirx;
 
 TVM_FFI_STATIC_INIT_BLOCK() { ExternOpNode::RegisterReflection(); }
 
-// ExternOpNode
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<ExternOpNode>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* op = static_cast<const ExternOpNode*>(node.get());
-      p->stream << "extern(" << op->name << ", " << op << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 int ExternOpNode::num_outputs() const { return 
static_cast<int>(output_placeholders.size()); }
 
diff --git a/src/te/operation/placeholder_op.cc 
b/src/te/operation/placeholder_op.cc
index a063c83045..17f4791d76 100644
--- a/src/te/operation/placeholder_op.cc
+++ b/src/te/operation/placeholder_op.cc
@@ -31,12 +31,7 @@ namespace te {
 
 TVM_FFI_STATIC_INIT_BLOCK() { PlaceholderOpNode::RegisterReflection(); }
 
-// PlaceholderOpNode
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<PlaceholderOpNode>([](const ObjectRef& node, ReprPrinter* p) 
{
-      auto* op = static_cast<const PlaceholderOpNode*>(node.get());
-      p->stream << "placeholder(" << op->name << ", " << op << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 int PlaceholderOpNode::num_outputs() const { return 1; }
 
diff --git a/src/te/operation/scan_op.cc b/src/te/operation/scan_op.cc
index 09f464f466..bfee2b4222 100644
--- a/src/te/operation/scan_op.cc
+++ b/src/te/operation/scan_op.cc
@@ -32,11 +32,7 @@ using namespace tirx;
 
 TVM_FFI_STATIC_INIT_BLOCK() { ScanOpNode::RegisterReflection(); }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<ScanOpNode>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* op = static_cast<const ScanOpNode*>(node.get());
-      p->stream << "scan(" << op->name << ", " << op << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 int ScanOpNode::num_outputs() const { return static_cast<int>(update.size()); }
 
diff --git a/src/te/tensor.cc b/src/te/tensor.cc
index 031f2a0aba..fe521ea9c9 100644
--- a/src/te/tensor.cc
+++ b/src/te/tensor.cc
@@ -121,11 +121,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<TensorNode>([](const ObjectRef& node, ReprPrinter* p) {
-      auto* t = static_cast<const TensorNode*>(node.get());
-      p->stream << "Tensor(shape=" << t->shape << ", op.name=" << t->op->name 
<< ')';
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 // Other tensor ops.
 TVM_FFI_STATIC_INIT_BLOCK() {
diff --git a/src/tirx/ir/expr.cc b/src/tirx/ir/expr.cc
index f4130e70d6..a3f300a03b 100644
--- a/src/tirx/ir/expr.cc
+++ b/src/tirx/ir/expr.cc
@@ -84,15 +84,8 @@ TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
   refl::GlobalDef().def("tirx.convert",
                         [](ffi::Variant<PrimExpr, ffi::Array<PrimExpr>> expr) 
{ return expr; });
-  // Register __ffi_repr__ for Var/SizeVar so repr shows just the name
-  refl::TypeAttrDef<VarNode>().def(refl::type_attr::kRepr,
-                                   [](Var var, ffi::Function) -> ffi::String {
-                                     return std::string(var->name_hint);
-                                   });
-  refl::TypeAttrDef<SizeVarNode>().def(refl::type_attr::kRepr,
-                                       [](SizeVar var, ffi::Function) -> 
ffi::String {
-                                         return std::string(var->name_hint);
-                                       });
+  // Note: kRepr for VarNode/SizeVarNode is registered via TVM_SCRIPT_REPR in
+  // src/script/printer/tirx/expr.cc (-> ReprPrintTIR which delegates to 
TVMScriptPrinter).
 }
 
 #define TVM_DEFINE_BINOP_CONSTRUCTOR(Name)                                    \
diff --git a/src/tirx/ir/transform.cc b/src/tirx/ir/transform.cc
index f5a99c454c..3cf2f58008 100644
--- a/src/tirx/ir/transform.cc
+++ b/src/tirx/ir/transform.cc
@@ -24,7 +24,7 @@
 #include <tvm/ffi/function.h>
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ffi/rvalue_ref.h>
-#include <tvm/node/repr_printer.h>
+#include <tvm/node/repr.h>
 #include <tvm/tirx/transform.h>
 
 namespace tvm {
@@ -158,12 +158,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
       });
 }
 
-TVM_STATIC_IR_FUNCTOR(ReprPrinter, vtable)
-    .set_dispatch<PrimFuncPassNode>([](const ObjectRef& ref, ReprPrinter* p) {
-      auto* node = static_cast<const PrimFuncPassNode*>(ref.get());
-      const PassInfo info = node->Info();
-      p->stream << "PrimFuncPass(" << info->name << ", opt_level=" << 
info->opt_level << ")";
-    });
+// Pattern A (RM): auto-default repr from reflection.
 
 }  // namespace transform
 }  // namespace tirx
diff --git a/tests/python/tirx-transform/test_tir_transform_vectorize.py 
b/tests/python/tirx-transform/test_tir_transform_vectorize.py
index 02b82df6e4..ec38c4a975 100644
--- a/tests/python/tirx-transform/test_tir_transform_vectorize.py
+++ b/tests/python/tirx-transform/test_tir_transform_vectorize.py
@@ -499,7 +499,7 @@ def test_illegal_extent():
             for j in T.vectorized(n):
                 A[j] = 3
 
-    error_msg = "Failed to vectorize loop with extent n for target 
\\(nullptr\\)"
+    error_msg = "Failed to vectorize loop with extent n for target None"
     with pytest.raises(tvm.error.InternalError, match=error_msg):
         tvm.tirx.transform.VectorizeLoop()(Mod)
 

Reply via email to