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

tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new cf3c5aff50 [REFACTOR][IR] Move dialect and codegen registrations to 
their owners (#20368)
cf3c5aff50 is described below

commit cf3c5aff50190ded12f68bb0e7658b50f1f66687
Author: Tianqi Chen <[email protected]>
AuthorDate: Thu Sep 17 07:10:17 2026 -0400

    [REFACTOR][IR] Move dialect and codegen registrations to their owners 
(#20368)
    
    Move TIRX-specific intrinsic-lowering registration and TensorMapType out
    of core IR, and place runtime-module JSON callbacks beside their codegen
    serializers. Remove unused higher-layer includes from IR
    implementations.
    
    TensorMapType now belongs to `tvm::tirx` in `tvm/tirx/type.h`, with
    `tirx.TensorMapType` registration and `tvm.tirx.TensorMapType` Python
    binding. Intrinsic-lowering registration uses
    `tirx.RegisterOpLowerIntrinsic` and `tvm.tirx.register_intrin_lowering`.
    
    GlobalVar calls use shared `ir.Call` construction, which preserves known
    declared return types while leaving argument-dependent results for
    normalization. Explicit constructor return-type overrides remain
    supported. Host/device lowering preserves declared results and converts
    void at external and packed-call ABI boundaries. Remove
    `tvm.tirx.call_tir` and use ordinary function-call syntax in TVMScript.
    Relax's distinct `call_tir` operator is unchanged.
---
 include/tvm/ir/type.h                              | 24 --------
 include/tvm/tirx/script/builder/ir.h               |  3 +-
 include/tvm/tirx/type.h                            | 56 +++++++++++++++++++
 python/tvm/ir/__init__.py                          |  2 +-
 python/tvm/ir/expr.py                              | 42 +++++++-------
 python/tvm/ir/op.py                                | 37 -------------
 python/tvm/ir/type.py                              | 17 ------
 python/tvm/target/intrin.py                        |  3 +-
 python/tvm/tirx/__init__.py                        |  3 +-
 python/tvm/tirx/op.py                              | 59 +++++++++++++-------
 python/tvm/tirx/script/parser/parser.py            | 10 +---
 python/tvm/tirx/type.py                            | 40 ++++++++++++++
 src/ir/expr.cc                                     |  2 -
 src/ir/module.cc                                   | 15 -----
 src/ir/op.cc                                       |  7 ---
 src/ir/transform.cc                                |  1 -
 src/ir/type.cc                                     | 30 ----------
 src/target/build_common.h                          |  3 +-
 src/target/codegen.cc                              | 17 ++++++
 src/target/llvm/codegen_llvm.cc                    |  5 +-
 src/target/source/codegen_c.cc                     |  7 ++-
 src/tirx/ir/type.cc                                | 64 ++++++++++++++++++++++
 src/tirx/op/op.cc                                  | 12 +++-
 src/tirx/script/printer/expr.cc                    | 15 ++++-
 src/tirx/script/printer/ir.cc                      |  3 +-
 src/tirx/transform/split_host_device.cc            |  8 +--
 tests/python/tirx-base/test_tir_constructor.py     |  1 -
 tests/python/tvmscript/test_tvmscript_roundtrip.py |  5 +-
 28 files changed, 283 insertions(+), 208 deletions(-)

diff --git a/include/tvm/ir/type.h b/include/tvm/ir/type.h
index cc4fb6a711..efdd1c384b 100644
--- a/include/tvm/ir/type.h
+++ b/include/tvm/ir/type.h
@@ -179,29 +179,5 @@ class FuncType : public Type {
   TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(FuncType, Type, FuncTypeNode);
 };
 
-/*!
- * \brief The type of tensor map.
- * \sa TensorMapType
- */
-class TensorMapTypeNode : public TypeNode {
- public:
-  static void RegisterReflection() {
-    namespace refl = tvm::ffi::reflection;
-    refl::ObjectDef<TensorMapTypeNode>();
-  }
-  TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.TensorMapType", TensorMapTypeNode, 
TypeNode);
-};
-
-/*!
- * \brief Managed reference to TensorMapTypeNode.
- * \sa TensorMapTypeNode
- */
-class TensorMapType : public Type {
- public:
-  TVM_DLL TensorMapType(Span span = Span());
-
-  TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(TensorMapType, Type, 
TensorMapTypeNode);
-};
-
 }  // namespace tvm
 #endif  // TVM_IR_TYPE_H_
diff --git a/include/tvm/tirx/script/builder/ir.h 
b/include/tvm/tirx/script/builder/ir.h
index 57a8d1098f..9e3e78a88c 100644
--- a/include/tvm/tirx/script/builder/ir.h
+++ b/include/tvm/tirx/script/builder/ir.h
@@ -28,6 +28,7 @@
 #include <tvm/tirx/op.h>
 #include <tvm/tirx/script/builder/frame.h>
 #include <tvm/tirx/tile_primitive.h>
+#include <tvm/tirx/type.h>
 
 namespace tvm {
 namespace script {
@@ -524,7 +525,7 @@ inline Var Handle(ffi::Optional<PrimType> dtype = 
std::nullopt,
   return tvm::tirx::Var("", type_annotation);
 }
 
-inline Var TensorMap() { return tvm::tirx::Var("", 
PointerType(TensorMapType())); }
+inline Var TensorMap() { return tvm::tirx::Var("", 
PointerType(tvm::tirx::TensorMapType())); }
 
 #define TVM_TIRX_IR_BUILDER_DEF_DTYPE_CAST(FuncName, DType)                    
  \
   inline PrimExpr FuncName(ffi::Optional<PrimExpr> expr = std::nullopt) {      
  \
diff --git a/include/tvm/tirx/type.h b/include/tvm/tirx/type.h
new file mode 100644
index 0000000000..20906eeabc
--- /dev/null
+++ b/include/tvm/tirx/type.h
@@ -0,0 +1,56 @@
+/*
+ * 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 tvm/tirx/type.h
+ * \brief Types specific to TIRX.
+ */
+#ifndef TVM_TIRX_TYPE_H_
+#define TVM_TIRX_TYPE_H_
+
+#include <tvm/ir/type.h>
+
+namespace tvm::tirx {
+
+/*!
+ * \brief The type of tensor map.
+ * \sa TensorMapType
+ */
+class TensorMapTypeNode : public TypeNode {
+ public:
+  static void RegisterReflection() {
+    namespace refl = tvm::ffi::reflection;
+    refl::ObjectDef<TensorMapTypeNode>();
+  }
+  TVM_FFI_DECLARE_OBJECT_INFO_FINAL("tirx.TensorMapType", TensorMapTypeNode, 
TypeNode);
+};
+
+/*!
+ * \brief Managed reference to TensorMapTypeNode.
+ * \sa TensorMapTypeNode
+ */
+class TensorMapType : public Type {
+ public:
+  TVM_DLL TensorMapType(Span span = Span());
+
+  TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(TensorMapType, Type, 
TensorMapTypeNode);
+};
+
+}  // namespace tvm::tirx
+#endif  // TVM_TIRX_TYPE_H_
diff --git a/python/tvm/ir/__init__.py b/python/tvm/ir/__init__.py
index a02e77cdd6..af039e0b9c 100644
--- a/python/tvm/ir/__init__.py
+++ b/python/tvm/ir/__init__.py
@@ -53,6 +53,6 @@ from . import prim
 from .function import BaseFunc, CallingConv
 from .global_info import GlobalInfo
 from .module import IRModule
-from .op import Op, register_intrin_lowering, register_op_attr
+from .op import Op, register_op_attr
 
 from tvm_ffi import Array, Map
diff --git a/python/tvm/ir/expr.py b/python/tvm/ir/expr.py
index 2ae414f758..993d13bfc5 100644
--- a/python/tvm/ir/expr.py
+++ b/python/tvm/ir/expr.py
@@ -16,8 +16,6 @@
 # under the License.
 """Common expressions data structures in the IR."""
 
-from numbers import Number
-
 import tvm_ffi
 
 import tvm
@@ -107,23 +105,7 @@ class GlobalVar(Expr):
         call: Expr
             A call taking the variable as a function.
         """
-        from .type import PointerType
-
-        def is_tir_arg(x):
-            return (
-                isinstance(x, Number)
-                or is_prim_expr(x)
-                or (isinstance(x, Expr) and isinstance(x.ty, PointerType))
-            )
-
-        if args and all(is_tir_arg(x) for x in args):
-            return tvm.tirx.call_tir(self, *args)
-
-        if all(isinstance(x, Expr) for x in args):
-            return Call(self, args)
-
-        arg_types = [type(x) for x in args]
-        raise RuntimeError(f"Do not know how to handle GlobalVar.__call__ for 
types {arg_types}")
+        return Call(self, args)
 
 
 class ExprOperand:
@@ -467,7 +449,12 @@ class TensorLoad(_CallableExprWithOp):
 
 @tvm_ffi.register_object("ir.Call")
 class Call(_CallableExprWithOp):
-    """Core function call node."""
+    """Core function call node.
+
+    When ``ret_ty`` is omitted, use the callee signature's declared return type
+    if available, or a missing type otherwise. Argument-dependent signatures
+    retain a missing type for subsequent normalization.
+    """
 
     op: Expr
     args: list[Expr]
@@ -487,14 +474,25 @@ class Call(_CallableExprWithOp):
         # pylint: disable=import-outside-toplevel
         from .attrs import DictAttrs
         from .op import Op
-        from .type import PointerType, PrimType, Type
+        from .type import PointerType, PrimType, TupleType, Type
 
         if isinstance(op, str):
             op = Op.get(op)
         if attrs is not None and isinstance(attrs, dict):
             attrs = DictAttrs(attrs)
         if ret_ty is None:
-            ret_ty = Type.missing()
+            # Reuse a declared signature without invoking dialect-specific 
inference.
+            signature = getattr(op, "ty", None)
+            ret_ty = getattr(signature, "ret_type", None)
+            if not isinstance(ret_ty, Type):
+                ret_ty = getattr(signature, "ret", None)
+                # Rich signatures may specialize their result using arguments.
+                # Reuse only fixed shared scalar, pointer, or void results 
here.
+                is_fixed_result = isinstance(ret_ty, PrimType | PointerType) 
or (
+                    isinstance(ret_ty, TupleType) and not ret_ty.fields
+                )
+                if not is_fixed_result or getattr(signature, "derive_func", 
None) is not None:
+                    ret_ty = Type.missing()
         if isinstance(ret_ty, str) and ret_ty == "handle":
             ret_ty = PointerType(PrimType("void"))
         elif ret_ty is not None and not isinstance(ret_ty, Type):
diff --git a/python/tvm/ir/op.py b/python/tvm/ir/op.py
index 02034fdf99..e1de79d9e1 100644
--- a/python/tvm/ir/op.py
+++ b/python/tvm/ir/op.py
@@ -187,40 +187,3 @@ def register_op_attr(op_name, attr_key, value=None, 
level=10):
         return v
 
     return _register(value) if value is not None else _register
-
-
-def register_intrin_lowering(
-    op_name,
-    target,
-    *,
-    f=None,
-    level=10,
-):
-    """Register Op lowering function
-
-    Parameters
-    ----------
-    op_name : str
-        The op name
-
-    target : str
-        The target string for given intrinsic lowering function
-
-    f : function, optional
-        The function to be registered.
-
-    level : int
-        The priority level
-
-    Returns
-    -------
-    fregister : function
-        Register op lowering function if f is not specified.
-    """
-
-    def _register(f):
-        """internal register function"""
-        _ffi_api.RegisterOpLowerIntrinsic(op_name, f, target, level)
-        return f
-
-    return _register(f) if f is not None else _register
diff --git a/python/tvm/ir/type.py b/python/tvm/ir/type.py
index 015232963e..d9f6dbc707 100644
--- a/python/tvm/ir/type.py
+++ b/python/tvm/ir/type.py
@@ -160,20 +160,3 @@ class FuncType(Type):
             arg_types,
             ret_type,
         )
-
-
-@tvm_ffi.register_object("ir.TensorMapType")
-class TensorMapType(Type):
-    """TensorMapType used in the low-level TIR.
-
-    Parameters
-    ----------
-    span : tvm.ir.Span
-        The span information.
-    """
-
-    def __init__(self, span=None):
-        self.__init_handle_by_constructor__(
-            _ffi_api.TensorMapType,
-            span,  # pylint: disable=no-member
-        )
diff --git a/python/tvm/target/intrin.py b/python/tvm/target/intrin.py
index 70c9a5fc2f..11ec8c33cc 100644
--- a/python/tvm/target/intrin.py
+++ b/python/tvm/target/intrin.py
@@ -16,8 +16,7 @@
 # under the License.
 """Target dependent intrinsic registration."""
 
-from tvm.ir import register_intrin_lowering
-from tvm.tirx import call_pure_extern
+from tvm.tirx import call_pure_extern, register_intrin_lowering
 
 
 def _rule_float_suffix(op):
diff --git a/python/tvm/tirx/__init__.py b/python/tvm/tirx/__init__.py
index e524a749c9..af4b7161b0 100644
--- a/python/tvm/tirx/__init__.py
+++ b/python/tvm/tirx/__init__.py
@@ -35,6 +35,7 @@ from .buffer import (
     decl_buffer,
     is_buffer_var,
 )
+from .type import TensorMapType
 from .expr import convert
 from .expr import Var, Reduce, FloatImm, IntImm, StringImm, Cast
 from .expr import Add, Sub, Mul, Div, Mod, FloorDiv, FloorMod
@@ -57,7 +58,7 @@ from .tile_primitive import DispatchContext, LambdaExpr, 
TilePrimitiveCall
 
 from .function import PrimFunc, TensorIntrin, IndexMap
 
-from .op import call_packed_lowered, call_cpacked_lowered, call_tir
+from .op import call_packed_lowered, call_cpacked_lowered, 
register_intrin_lowering
 from .op import call_packed, call_cpacked, call_intrin, call_pure_extern, 
call_extern
 from .op import call_llvm_intrin, call_llvm_pure_intrin, all, any, min_value, 
max_value, trace
 from .op import tvm_stack_alloca, tvm_stack_make_shape, tvm_stack_make_array
diff --git a/python/tvm/tirx/op.py b/python/tvm/tirx/op.py
index cfb875befc..1b321c9c28 100644
--- a/python/tvm/tirx/op.py
+++ b/python/tvm/tirx/op.py
@@ -29,12 +29,12 @@ from tvm.ir import Call, Expr, ExprWithOp, Op, PointerType, 
PrimType, TensorLoad
 from tvm.ir.base import Span
 from tvm.ir.prim import clz as clz
 from tvm.ir.prim import max_value, min_value
-from tvm.ir.type import TensorMapType
 from tvm.runtime import const
 
 from . import _ffi_api
 from .buffer import Buffer, buffer_data, is_buffer_var
 from .expr import BufferLoad, CommReducer, ExprOp, IntImm, Var
+from .type import TensorMapType
 
 tir = tirx  # alias for backward compat with upstream tir.convert() calls
 
@@ -50,6 +50,43 @@ _DEVICE_INTRIN_PREFIX_TO_NAMESPACE = {
 }
 
 
+def register_intrin_lowering(
+    op_name,
+    target,
+    *,
+    f=None,
+    level=10,
+):
+    """Register Op lowering function
+
+    Parameters
+    ----------
+    op_name : str
+        The op name
+
+    target : str
+        The target string for given intrinsic lowering function
+
+    f : function, optional
+        The function to be registered.
+
+    level : int
+        The priority level
+
+    Returns
+    -------
+    fregister : function
+        Register op lowering function if f is not specified.
+    """
+
+    def _register(f):
+        """internal register function"""
+        _ffi_api.RegisterOpLowerIntrinsic(op_name, f, target, level)
+        return f
+
+    return _register(f) if f is not None else _register
+
+
 def _canonical_device_intrin_name(func_name: str) -> str:
     """Return the canonical registry name for statically registered device 
intrinsics."""
 
@@ -547,26 +584,6 @@ def undef():
     return call_intrin("int32", "tirx.undef")
 
 
-def call_tir(global_var: tvm.ir.GlobalVar, *args):
-    """Performs a call into another PrimFunc in the same IRModule
-
-    Returns
-    -------
-    call : Expr
-        The call expression.
-    """
-    assert isinstance(global_var, tvm.ir.GlobalVar)
-    args = tuple(_reject_buffer_region(arg, "call_tir") for arg in args)
-
-    dtype = "void"
-    if global_var.ty is not None:
-        ret_ty = global_var.ty.ret
-        if isinstance(ret_ty, tvm.ir.PrimType):
-            dtype = ret_ty
-
-    return Call(op=global_var, args=args, ret_ty=dtype)
-
-
 def start_profile_intrinsic(id):
     """Start profile intrinsic.
     Parameters
diff --git a/python/tvm/tirx/script/parser/parser.py 
b/python/tvm/tirx/script/parser/parser.py
index 607988a4d1..e9c9aafc43 100644
--- a/python/tvm/tirx/script/parser/parser.py
+++ b/python/tvm/tirx/script/parser/parser.py
@@ -1032,14 +1032,8 @@ def visit_expr_stmt(self: Parser, node: doc.Expr) -> 
None:
     elif isinstance(res, int | bool):
         T.evaluate(tvm.tirx.const(res))
     elif isinstance(res, tvm.ir.Call) and not tvm.ir.is_prim_expr(res):
-        if isinstance(res.op, tvm.ir.GlobalVar) and res.ty.is_missing():
-            # GlobalVar calls with a missing return type are ambiguous, as 
each IR has a
-            # different function Call representation. Convert to the TIR 
representation.
-            T.evaluate(tvm.tirx.call_tir(res.op, *res.args))
-        else:
-            # Pointer-valued TIR calls are general Expr rather than Expr,
-            # but are still valid standalone Evaluate statements.
-            T.evaluate(res)
+        # Non-primitive calls are still valid standalone Evaluate statements.
+        T.evaluate(res)
     elif isinstance(res, str):
         # Ignore docstrings
         pass
diff --git a/python/tvm/tirx/type.py b/python/tvm/tirx/type.py
new file mode 100644
index 0000000000..7b03990e89
--- /dev/null
+++ b/python/tvm/tirx/type.py
@@ -0,0 +1,40 @@
+# 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.
+"""Types specific to TIRX."""
+
+import tvm_ffi
+
+from tvm.ir import Type
+
+from . import _ffi_api
+
+
+@tvm_ffi.register_object("tirx.TensorMapType")
+class TensorMapType(Type):
+    """TensorMapType used in the low-level TIR.
+
+    Parameters
+    ----------
+    span : tvm.ir.Span
+        The span information.
+    """
+
+    def __init__(self, span=None):
+        self.__init_handle_by_constructor__(
+            _ffi_api.TensorMapType,
+            span,  # pylint: disable=no-member
+        )
diff --git a/src/ir/expr.cc b/src/ir/expr.cc
index 0f4502271c..cf2ffbeb26 100644
--- a/src/ir/expr.cc
+++ b/src/ir/expr.cc
@@ -21,7 +21,6 @@
  * \file src/ir/expr.cc
  * \brief The expression AST nodes for the common IR infra.
  */
-#include <tvm/arith/analyzer.h>
 #include <tvm/ffi/extra/structural_mutate.h>
 #include <tvm/ffi/extra/structural_visit.h>
 #include <tvm/ffi/function.h>
@@ -32,7 +31,6 @@
 #include <tvm/ir/prim/expr.h>
 #include <tvm/ir/prim/op.h>
 #include <tvm/ir/type.h>
-#include <tvm/te/tensor.h>
 
 #include <cmath>
 #include <utility>
diff --git a/src/ir/module.cc b/src/ir/module.cc
index 49f607f6c5..f2b63bf937 100644
--- a/src/ir/module.cc
+++ b/src/ir/module.cc
@@ -22,15 +22,12 @@
  */
 #include <tvm/ffi/cast.h>
 #include <tvm/ffi/container/variant.h>
-#include <tvm/ffi/extra/base64.h>
-#include <tvm/ffi/extra/module.h>
 #include <tvm/ffi/extra/structural_equal.h>
 #include <tvm/ffi/function.h>
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ffi/rvalue_ref.h>
 #include <tvm/ir/module.h>
 #include <tvm/ir/unique_name_supply.h>
-#include <tvm/target/codegen.h>
 
 #include <algorithm>
 #include <fstream>
@@ -258,18 +255,6 @@ IRModule IRModule::FromExpr(const Expr& expr,
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
-  refl::TypeAttrDef<ffi::ModuleObj>()
-      .def("__data_to_json__",
-           [](const ffi::ModuleObj* node) {
-             std::string bytes = 
codegen::SerializeModuleToBytes(ffi::GetRef<ffi::Module>(node),
-                                                                 
/*export_dso*/ false);
-             return ffi::Base64Encode(ffi::Bytes(bytes));
-           })
-      .def("__data_from_json__", [](const ffi::String& base64_bytes) {
-        ffi::Bytes bytes = ffi::Base64Decode(base64_bytes);
-        ffi::Module rtmod = codegen::DeserializeModuleFromBytes(bytes.operator 
std::string());
-        return rtmod;
-      });
   refl::GlobalDef()
       .def("ir.Module_Clone",
            [](IRModule mod) -> IRModule {
diff --git a/src/ir/op.cc b/src/ir/op.cc
index 4b50f06ab1..8f7897e502 100644
--- a/src/ir/op.cc
+++ b/src/ir/op.cc
@@ -27,7 +27,6 @@
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ir/op.h>
 #include <tvm/ir/type.h>
-#include <tvm/tirx/op_attr_types.h>
 
 #include <memory>
 
@@ -57,7 +56,6 @@ TVM_FFI_STATIC_INIT_BLOCK() { 
ArgumentInfoNode::RegisterReflection(); }
 using ffi::Any;
 using ffi::Function;
 using ffi::PackedArgs;
-using tirx::FLowerIntrinsic;
 
 using OpRegistry = AttrRegistry<OpRegEntry, Op>;
 
@@ -182,11 +180,6 @@ TVM_FFI_STATIC_INIT_BLOCK() {
              } else {
                reg.set_attr(attr_key, value, plevel);
              }
-           })
-      .def("ir.RegisterOpLowerIntrinsic",
-           [](ffi::String name, ffi::Function f, ffi::String target, int 
plevel) {
-             tvm::OpRegEntry::RegisterOrGet(name).set_attr<FLowerIntrinsic>(
-                 target + ".FLowerIntrinsic", f, plevel);
            });
   // override OpNode to use name as the repr
 }
diff --git a/src/ir/transform.cc b/src/ir/transform.cc
index 42a5741b0c..d33895c997 100644
--- a/src/ir/transform.cc
+++ b/src/ir/transform.cc
@@ -28,7 +28,6 @@
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ffi/rvalue_ref.h>
 #include <tvm/ir/transform.h>
-#include <tvm/relax/expr.h>
 #include <tvm/runtime/device_api.h>
 #include <tvm/runtime/logging.h>
 
diff --git a/src/ir/type.cc b/src/ir/type.cc
index dd28b98e55..6e0ce2ab8d 100644
--- a/src/ir/type.cc
+++ b/src/ir/type.cc
@@ -229,18 +229,6 @@ TVMFFIAny 
TupleTypeMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
-TVMFFIAny TensorMapTypeVisit(ffi::StructuralVisitorObj*, ffi::AnyView) 
noexcept {
-  return ffi::AnyView(nullptr).CopyToTVMFFIAny();
-}
-
-TVMFFIAny TensorMapTypeMutate(ffi::StructuralMutatorObj*, ffi::AnyView) 
noexcept {
-  return ffi::Unchanged().CopyToTVMFFIAny();
-}
-
-TVMFFIAny TensorMapTypeMaybeInplaceMutate(ffi::StructuralMutatorObj*, 
ffi::AnyView) noexcept {
-  return ffi::Unchanged().CopyToTVMFFIAny();
-}
-
 }  // namespace
 
 Type Type::Missing() {
@@ -428,22 +416,4 @@ TVM_FFI_STATIC_INIT_BLOCK() {
 
 TupleType TupleType::Empty() { return TupleType(ffi::Array<Type>()); }
 
-TensorMapType::TensorMapType(Span span) : Type(ffi::UnsafeInit{}) {
-  ffi::ObjectPtr<TensorMapTypeNode> n = ffi::make_object<TensorMapTypeNode>();
-  n->span = std::move(span);
-  data_ = std::move(n);
-}
-
-TVM_FFI_STATIC_INIT_BLOCK() {
-  namespace refl = tvm::ffi::reflection;
-  TensorMapTypeNode::RegisterReflection();
-  refl::TypeAttrDef<TensorMapTypeNode>()
-      .attr(refl::type_attr::kStructuralVisit, 
reinterpret_cast<void*>(&TensorMapTypeVisit))
-      .attr(refl::type_attr::kStructuralMutate, 
reinterpret_cast<void*>(&TensorMapTypeMutate))
-      .attr(refl::type_attr::kStructuralMaybeInplaceMutate,
-            reinterpret_cast<void*>(&TensorMapTypeMaybeInplaceMutate));
-
-  refl::GlobalDef().def("ir.TensorMapType", [](Span span) { return 
TensorMapType(span); });
-}
-
 }  // namespace tvm
diff --git a/src/target/build_common.h b/src/target/build_common.h
index aba8f0796d..a12cca310a 100644
--- a/src/target/build_common.h
+++ b/src/target/build_common.h
@@ -32,6 +32,7 @@
 #include <tvm/tirx/function.h>
 #include <tvm/tirx/op.h>
 #include <tvm/tirx/stmt.h>
+#include <tvm/tirx/type.h>
 
 #include <string>
 
@@ -64,7 +65,7 @@ inline ffi::Map<ffi::String, runtime::FunctionInfo> 
ExtractFuncInfo(const IRModu
         if (type == nullptr) {
           return false;
         }
-        return type->element_type.as<TensorMapTypeNode>() != nullptr;
+        return type->element_type.as<tirx::TensorMapTypeNode>() != nullptr;
       };
       arg_extra_tags.push_back(is_tensormap(f->params[i]) ? 
runtime::ArgExtraTags::kTensorMap
                                                           : 
runtime::ArgExtraTags::kNone);
diff --git a/src/target/codegen.cc b/src/target/codegen.cc
index 4437667c70..de16eb1e02 100644
--- a/src/target/codegen.cc
+++ b/src/target/codegen.cc
@@ -21,6 +21,7 @@
  * \file codegen.cc
  * \brief Common utilities to generated C style code.
  */
+#include <tvm/ffi/extra/base64.h>
 #include <tvm/ffi/extra/module.h>
 #include <tvm/ffi/function.h>
 #include <tvm/ffi/reflection/registry.h>
@@ -347,6 +348,22 @@ TVM_FFI_STATIC_INIT_BLOCK() {
   refl::GlobalDef().def("target.Build", Build);
 }
 
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  refl::TypeAttrDef<ffi::ModuleObj>()
+      .def("__data_to_json__",
+           [](const ffi::ModuleObj* node) {
+             std::string bytes = 
codegen::SerializeModuleToBytes(ffi::GetRef<ffi::Module>(node),
+                                                                 
/*export_dso*/ false);
+             return ffi::Base64Encode(ffi::Bytes(bytes));
+           })
+      .def("__data_from_json__", [](const ffi::String& base64_bytes) {
+        ffi::Bytes bytes = ffi::Base64Decode(base64_bytes);
+        ffi::Module rtmod = codegen::DeserializeModuleFromBytes(bytes.operator 
std::string());
+        return rtmod;
+      });
+}
+
 // Export a few auxiliary function to the runtime namespace.
 TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
diff --git a/src/target/llvm/codegen_llvm.cc b/src/target/llvm/codegen_llvm.cc
index f70a4f9ed9..92892ec7bf 100644
--- a/src/target/llvm/codegen_llvm.cc
+++ b/src/target/llvm/codegen_llvm.cc
@@ -82,6 +82,7 @@
 #include <tvm/runtime/device_api.h>
 #include <tvm/runtime/logging.h>
 #include <tvm/tirx/op.h>
+#include <tvm/tirx/type.h>
 
 #include <algorithm>
 #include <functional>
@@ -643,14 +644,14 @@ llvm::Type* CodeGenLLVM::GetLLVMType(const Type& type) 
const {
       if (PrimType(primtype->dtype).IsVoid()) {
         return t_void_p_;
       }
-    } else if (ptr->element_type->IsInstance<TensorMapTypeNode>()) {
+    } else if (ptr->element_type->IsInstance<tirx::TensorMapTypeNode>()) {
       return llvmGetPointerTo(t_tvm_tensormap_, 0);
     }
     // TODO(tvm-team) consider put storage scope into the pointer type.
     return llvmGetPointerTo(GetLLVMType(ptr->element_type), 
GetGlobalAddressSpace());
   } else if (IsVoidType(type)) {
     return t_void_;
-  } else if (type->IsInstance<TensorMapTypeNode>()) {
+  } else if (type->IsInstance<tirx::TensorMapTypeNode>()) {
     return t_tvm_tensormap_;
   } else {
     TVM_FFI_THROW(InternalError) << "Type " << type << " does not have a 
corresponding LLVM Type";
diff --git a/src/target/source/codegen_c.cc b/src/target/source/codegen_c.cc
index 5086dbe0aa..4cca7b6255 100644
--- a/src/target/source/codegen_c.cc
+++ b/src/target/source/codegen_c.cc
@@ -25,6 +25,7 @@
 #include <tvm/arith/analyzer.h>
 #include <tvm/ffi/cast.h>
 #include <tvm/ir/unique_name_supply.h>
+#include <tvm/tirx/type.h>
 
 #include <cctype>
 #include <iomanip>
@@ -99,7 +100,7 @@ void CodeGenC::PrintFunctionSignature(const ffi::String& 
function_name, const Pr
 
     auto is_tensormap_ptr = [&]() -> bool {
       if (auto* ptr = v->ty.as<PointerTypeNode>()) {
-        return ptr->element_type.as<TensorMapTypeNode>();
+        return ptr->element_type.as<tirx::TensorMapTypeNode>();
       }
       return false;
     };
@@ -112,7 +113,7 @@ void CodeGenC::PrintFunctionSignature(const ffi::String& 
function_name, const Pr
     bool no_alias = func->HasNonzeroAttr(tirx::attr::kNoAlias);
     bool is_handle = v->ty.as<PointerTypeNode>();
     auto* ptr = v->ty.as<PointerTypeNode>();
-    if (ptr && ptr->element_type.as<TensorMapTypeNode>()) {
+    if (ptr && ptr->element_type.as<tirx::TensorMapTypeNode>()) {
       is_handle = false;
     }
     if (no_alias && is_handle) {
@@ -799,7 +800,7 @@ void CodeGenC::Dispatch_(const CallNode* op, std::ostream& 
os) {  // NOLINT(*)
             << "Builtin address_of() expects the argument to be a TensorLoad 
or Var, but "
             << "received argument " << op->args[0];
         if (auto* ptr = var->ty.as<PointerTypeNode>()) {
-          if (ptr->element_type.as<TensorMapTypeNode>()) {
+          if (ptr->element_type.as<tirx::TensorMapTypeNode>()) {
             os << "((unsigned long long)(&(";
             this->PrintExpr(op->args[0], os);
             os << ")))";
diff --git a/src/tirx/ir/type.cc b/src/tirx/ir/type.cc
new file mode 100644
index 0000000000..704c60a40f
--- /dev/null
+++ b/src/tirx/ir/type.cc
@@ -0,0 +1,64 @@
+/*
+ * 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 tirx/ir/type.cc
+ * \brief Types specific to TIRX.
+ */
+#include <tvm/ffi/extra/structural_mutate.h>
+#include <tvm/ffi/extra/structural_visit.h>
+#include <tvm/ffi/reflection/registry.h>
+#include <tvm/tirx/type.h>
+
+namespace tvm::tirx {
+namespace {
+
+TVMFFIAny TensorMapTypeVisit(ffi::StructuralVisitorObj*, ffi::AnyView) 
noexcept {
+  return ffi::AnyView(nullptr).CopyToTVMFFIAny();
+}
+
+TVMFFIAny TensorMapTypeMutate(ffi::StructuralMutatorObj*, ffi::AnyView) 
noexcept {
+  return ffi::Unchanged().CopyToTVMFFIAny();
+}
+
+TVMFFIAny TensorMapTypeMaybeInplaceMutate(ffi::StructuralMutatorObj*, 
ffi::AnyView) noexcept {
+  return ffi::Unchanged().CopyToTVMFFIAny();
+}
+
+}  // namespace
+
+TensorMapType::TensorMapType(Span span) : Type(ffi::UnsafeInit{}) {
+  ffi::ObjectPtr<TensorMapTypeNode> n = ffi::make_object<TensorMapTypeNode>();
+  n->span = std::move(span);
+  data_ = std::move(n);
+}
+
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  TensorMapTypeNode::RegisterReflection();
+  refl::TypeAttrDef<TensorMapTypeNode>()
+      .attr(refl::type_attr::kStructuralVisit, 
reinterpret_cast<void*>(&TensorMapTypeVisit))
+      .attr(refl::type_attr::kStructuralMutate, 
reinterpret_cast<void*>(&TensorMapTypeMutate))
+      .attr(refl::type_attr::kStructuralMaybeInplaceMutate,
+            reinterpret_cast<void*>(&TensorMapTypeMaybeInplaceMutate));
+
+  refl::GlobalDef().def("tirx.TensorMapType", [](Span span) { return 
TensorMapType(span); });
+}
+
+}  // namespace tvm::tirx
diff --git a/src/tirx/op/op.cc b/src/tirx/op/op.cc
index 828568d16f..8c521b88c3 100644
--- a/src/tirx/op/op.cc
+++ b/src/tirx/op/op.cc
@@ -34,6 +34,7 @@
 #include <tvm/tirx/builtin.h>
 #include <tvm/tirx/op.h>
 #include <tvm/tirx/op_attr_types.h>
+#include <tvm/tirx/type.h>
 #include <tvm/tirx/var.h>
 
 #include <cmath>
@@ -104,7 +105,7 @@ Type GetType(const PrimExpr& expr) {
 
       if (auto var = address_of->args[0].as<Var>()) {
         if (auto* ptr = var.value()->ty.as<PointerTypeNode>()) {
-          if (ptr->element_type.as<TensorMapTypeNode>()) {
+          if (ptr->element_type.as<tirx::TensorMapTypeNode>()) {
             return PrimType::UInt(64);
           }
         }
@@ -149,6 +150,15 @@ PrimExpr break_loop(Span span) {
       .as_or_throw<PrimExpr>();
 }
 
+TVM_FFI_STATIC_INIT_BLOCK() {
+  namespace refl = tvm::ffi::reflection;
+  refl::GlobalDef().def("tirx.RegisterOpLowerIntrinsic",
+                        [](ffi::String name, ffi::Function f, ffi::String 
target, int plevel) {
+                          
OpRegEntry::RegisterOrGet(name).set_attr<tirx::FLowerIntrinsic>(
+                              target + ".FLowerIntrinsic", f, plevel);
+                        });
+}
+
 TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
   refl::GlobalDef()
diff --git a/src/tirx/script/printer/expr.cc b/src/tirx/script/printer/expr.cc
index c9174c2040..ab29eb1623 100644
--- a/src/tirx/script/printer/expr.cc
+++ b/src/tirx/script/printer/expr.cc
@@ -19,6 +19,7 @@
 #include <tvm/ir/prim/builtin.h>
 #include <tvm/te/operation.h>
 #include <tvm/tirx/builtin.h>
+#include <tvm/tirx/type.h>
 
 #include "./utils.h"
 
@@ -72,7 +73,7 @@ ExprDoc PrintVarCreation(const tirx::Var& var, const 
AccessPath& var_p, const IR
                           kwargs_keys, kwargs_values);
         }
       }
-    } else if (ptr_type->element_type->IsInstance<TensorMapTypeNode>()) {
+    } else if (ptr_type->element_type->IsInstance<tirx::TensorMapTypeNode>()) {
       rhs = TIR(d, "TensorMap")->Call({}, {}, {});
     }
   } else {
@@ -348,6 +349,16 @@ Doc PrintTIRCall(Call call, AccessPath call_p, IRDocsifier 
d) {
                                 "types, but got "
                              << call->ty;
   };
+  auto get_call_return_type_doc = [&]() -> ExprDoc {
+    if (call->ty.IsMissing()) {
+      return IdDoc("tvm")->Attr("ir")->Attr("Type")->Attr("missing")->Call({});
+    }
+    if (call_prim_type || call->ty.as<PointerTypeNode>()) {
+      return get_call_type_doc(call_p->Attr("ty"));
+    }
+    // Annotation spellings such as None for an empty tuple are not type 
values.
+    return d->AddMetadata(call->ty);
+  };
   if (call->attrs.defined()) {
     ffi::Array<ExprDoc> call_args;
     int n_args = call->args.size();
@@ -358,7 +369,7 @@ Doc PrintTIRCall(Call call, AccessPath call_p, IRDocsifier 
d) {
     ExprDoc op_doc = call->op.as<Op>()
                          ? LiteralDoc::Str(call->op.as<Op>().value()->name, 
call_p->Attr("op"))
                          : d->AsDoc<ExprDoc>(call->op, call_p->Attr("op"));
-    ExprDoc ret_ty_doc = get_call_type_doc(call_p->Attr("ty"));
+    ExprDoc ret_ty_doc = get_call_return_type_doc();
     return TIR(d, "Call")->Call(
         {op_doc, ListDoc(call_args)}, {"attrs", "ret_ty"},
         {d->AsDoc<ExprDoc>(call->attrs, call_p->Attr("attrs")), ret_ty_doc});
diff --git a/src/tirx/script/printer/ir.cc b/src/tirx/script/printer/ir.cc
index 462382864a..e0aa4f8202 100644
--- a/src/tirx/script/printer/ir.cc
+++ b/src/tirx/script/printer/ir.cc
@@ -17,6 +17,7 @@
  * under the License.
  */
 #include <tvm/target/target.h>
+#include <tvm/tirx/type.h>
 
 #include "./utils.h"
 
@@ -89,7 +90,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
           }
           element_type = LiteralDoc::DataType(prim_type->dtype,  //
                                               
ty_p->Attr("element_type")->Attr("dtype"));
-        } else if (ty->element_type.as<TensorMapTypeNode>()) {
+        } else if (ty->element_type.as<tirx::TensorMapTypeNode>()) {
           return TIR(d, "TensorMap")->Call({});
         } else {
           element_type = d->AsDoc<ExprDoc>(ty->element_type, 
ty_p->Attr("element_type"));
diff --git a/src/tirx/transform/split_host_device.cc 
b/src/tirx/transform/split_host_device.cc
index 9804f2d610..51acd55019 100644
--- a/src/tirx/transform/split_host_device.cc
+++ b/src/tirx/transform/split_host_device.cc
@@ -337,8 +337,7 @@ class HostDeviceSplitter : public StmtExprMutator {
                                       assert_success});
 
     } else {
-      return Evaluate(
-          Call(PrimType::Void(), kernel_symbol_global, 
call_args).as_or_throw<PrimExpr>());
+      return Evaluate(Call(kernel_ret_type, kernel_symbol_global, call_args));
     }
   }
 
@@ -811,7 +810,8 @@ class DeviceKernelMutator : public StmtExprMutator {
         for (const Expr& arg : node->args) {
           args.push_back(arg);
         }
-        return Call(node->ty, builtin::call_extern(), args);
+        Type ret_ty = IsVoidType(node->ty) ? PrimType::Void() : node->ty;
+        return Call(ret_ty, builtin::call_extern(), args);
       }
     }
 
@@ -856,7 +856,7 @@ class DeviceKernelMutator : public StmtExprMutator {
                               .as_or_throw<PrimExpr>());
     }
 
-    PrimType node_ty = node->ty.as_or_throw<PrimType>();
+    PrimType node_ty = IsVoidType(node->ty) ? PrimType::Void() : 
node->ty.as_or_throw<PrimType>();
     PrimType ret_ty = node_ty.IsVoid() ? PrimType::Int(32) : node_ty;
 
     return Call(ret_ty, builtin::tvm_call_packed(), 
call_args).as_or_throw<PrimExpr>();
diff --git a/tests/python/tirx-base/test_tir_constructor.py 
b/tests/python/tirx-base/test_tir_constructor.py
index 05e91a2591..88d0f36964 100644
--- a/tests/python/tirx-base/test_tir_constructor.py
+++ b/tests/python/tirx-base/test_tir_constructor.py
@@ -217,7 +217,6 @@ def test_buffer_region_call_wrappers_reject():
         lambda: tvm.tirx.call_cpacked("consume", region, 0),
         lambda: tvm.tirx.call_packed_lowered("consume", region),
         lambda: tvm.tirx.call_cpacked_lowered("consume", region, 0),
-        lambda: tvm.tirx.call_tir(tvm.ir.GlobalVar("callee"), region),
         lambda: tvm.tirx.trace([region]),
         lambda: T.evaluate(region),
     ]
diff --git a/tests/python/tvmscript/test_tvmscript_roundtrip.py 
b/tests/python/tvmscript/test_tvmscript_roundtrip.py
index b55d5279f9..e41755a9a2 100644
--- a/tests/python/tvmscript/test_tvmscript_roundtrip.py
+++ b/tests/python/tvmscript/test_tvmscript_roundtrip.py
@@ -3003,10 +3003,7 @@ def subroutine_call_without_arguments():
     class mod:
         @T.prim_func(s_tir=True)
         def main():
-            # Should be equivalent to the bare "mod.subroutine()", but
-            # that relies on `GlobalVar.__call__` returning the
-            # correct IR type.
-            tirx.call_tir(mod.subroutine)
+            mod.subroutine()
 
         @T.prim_func(s_tir=True)
         def subroutine():

Reply via email to