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-tirx.git
The following commit(s) were added to refs/heads/main by this push:
new 5b297717d [REFACTOR][RUST] Load libtvm_tirx_rust through the backend
autoload (#51)
5b297717d is described below
commit 5b297717d18e38caa17d92b5ab1c29c1d61383ce
Author: Linzhang Li <[email protected]>
AuthorDate: Sat Oct 24 20:11:57 2026 -0400
[REFACTOR][RUST] Load libtvm_tirx_rust through the backend autoload (#51)
`import tvm_tirx` no longer loads `libtvm_tirx_rust` from `base.py`,
right after the compiler
library. A built-in backend, `tvm_tirx.backend.rust`, loads it instead,
through the backend
autoload, as the CUDA, Metal and ROCm backends load their runtime
libraries. The library is still
built by the TIRx build (`TVM_TIRX_USE_RUST`); moving it into a package
of its own is left for
later.
---
cmake/modules/Rust.cmake | 5 +-
docs/reference/python/backend.rst | 1 +
.../python/{backend.rst => backend.rust.rst} | 32 ++----------
docs/reference/python/index.rst | 3 ++
python/tvm_tirx/backend/_autoload_backends.py | 1 +
python/tvm_tirx/backend/rust/__init__.py | 45 +++++++++++++++++
python/tvm_tirx/base.py | 22 ---------
rust/examples/define_ops.rs | 4 +-
rust/src/lib.rs | 15 +++---
tests/python/ir/test_op_rust.py | 57 ++++++++++++++++------
10 files changed, 108 insertions(+), 77 deletions(-)
diff --git a/cmake/modules/Rust.cmake b/cmake/modules/Rust.cmake
index cc28707a0..66e260220 100644
--- a/cmake/modules/Rust.cmake
+++ b/cmake/modules/Rust.cmake
@@ -16,8 +16,9 @@
# under the License.
# The crate in rust/, built by cargo as a shared library and installed as
libtvm_tirx_rust next to
-# libtvm_tirx_compiler. `import tvm_tirx` loads it after the compiler library
and registers the
-# operators it defines (python/tvm_tirx/base.py), as loading the compiler
registers the C++ ones.
+# libtvm_tirx_compiler. `import tvm_tirx` loads it through the backend
autoload and registers the
+# operators it defines (python/tvm_tirx/backend/rust), as loading the compiler
registers the C++
+# ones.
#
# TVM_TIRX_USE_RUST is ON, OFF or AUTO. AUTO builds the library when it can:
not on Windows, with
# the crate's sources (a source distribution has none), with a cargo at least
as new as the
diff --git a/docs/reference/python/backend.rst
b/docs/reference/python/backend.rst
index d5a7352bc..40a7f6da3 100644
--- a/docs/reference/python/backend.rst
+++ b/docs/reference/python/backend.rst
@@ -32,6 +32,7 @@ Namespaces
* :doc:`tvm_tirx.backend.metal <backend.metal>`
* :doc:`tvm_tirx.backend.opencl <backend.opencl>`
* :doc:`tvm_tirx.backend.rocm <backend.rocm>`
+* :doc:`tvm_tirx.backend.rust <backend.rust>`
* :doc:`tvm_tirx.backend.trn <backend.trn>`
* :doc:`tvm_tirx.backend.vulkan <backend.vulkan>`
* :doc:`tvm_tirx.backend.webgpu <backend.webgpu>`
diff --git a/docs/reference/python/backend.rst
b/docs/reference/python/backend.rust.rst
similarity index 55%
copy from docs/reference/python/backend.rst
copy to docs/reference/python/backend.rust.rst
index d5a7352bc..549a6b24d 100644
--- a/docs/reference/python/backend.rst
+++ b/docs/reference/python/backend.rust.rst
@@ -15,34 +15,13 @@
specific language governing permissions and limitations
under the License.
-tvm_tirx.backend
-================
+tvm_tirx.backend.rust
+=====================
-.. automodule:: tvm_tirx.backend
+.. automodule:: tvm_tirx.backend.rust
:no-members:
-.. currentmodule:: tvm_tirx.backend
-
-Namespaces
-----------
-
-* :doc:`tvm_tirx.backend.cuda <backend.cuda>`
-* :doc:`tvm_tirx.backend.adreno <backend.adreno>`
-* :doc:`tvm_tirx.backend.hexagon <backend.hexagon>`
-* :doc:`tvm_tirx.backend.metal <backend.metal>`
-* :doc:`tvm_tirx.backend.opencl <backend.opencl>`
-* :doc:`tvm_tirx.backend.rocm <backend.rocm>`
-* :doc:`tvm_tirx.backend.trn <backend.trn>`
-* :doc:`tvm_tirx.backend.vulkan <backend.vulkan>`
-* :doc:`tvm_tirx.backend.webgpu <backend.webgpu>`
-* :doc:`tvm_tirx.backend.config <backend.config>`
-
-.. toctree::
- :hidden:
- :maxdepth: 1
- :titlesonly:
-
- backend.config
+.. currentmodule:: tvm_tirx.backend.rust
Functions
---------
@@ -50,5 +29,4 @@ Functions
.. autosummary::
:toctree: generated/
- is_loaded
- load
+ register_backend
diff --git a/docs/reference/python/index.rst b/docs/reference/python/index.rst
index 162d3a8d3..9b5d0a919 100644
--- a/docs/reference/python/index.rst
+++ b/docs/reference/python/index.rst
@@ -102,6 +102,8 @@ Namespaces
- Trainium NKI intrinsic wrappers.
* - :doc:`tvm_tirx.backend.trn.transform <backend.trn.transform>`
- Trainium-specific lowering passes.
+ * - :doc:`tvm_tirx.backend.rust <backend.rust>`
+ - Operators written in Rust.
* - :doc:`tvm_tirx.support <support>`
- Build tools and system utilities.
* - :doc:`tvm_tirx.testing <testing>`
@@ -147,6 +149,7 @@ Namespaces
tvm_tirx.backend.trn <backend.trn>
tvm_tirx.backend.trn.op <backend.trn.op>
tvm_tirx.backend.trn.transform <backend.trn.transform>
+ tvm_tirx.backend.rust <backend.rust>
tvm_tirx.support <support>
tvm_tirx.testing <testing>
diff --git a/python/tvm_tirx/backend/_autoload_backends.py
b/python/tvm_tirx/backend/_autoload_backends.py
index ad333064e..5b8e2d2f8 100644
--- a/python/tvm_tirx/backend/_autoload_backends.py
+++ b/python/tvm_tirx/backend/_autoload_backends.py
@@ -34,6 +34,7 @@ _BUILTIN_BACKENDS = (
"webgpu",
"hexagon",
"adreno",
+ "rust",
)
_AUTO_LOAD_DONE = False
diff --git a/python/tvm_tirx/backend/rust/__init__.py
b/python/tvm_tirx/backend/rust/__init__.py
new file mode 100644
index 000000000..13aebfd49
--- /dev/null
+++ b/python/tvm_tirx/backend/rust/__init__.py
@@ -0,0 +1,45 @@
+# 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.
+"""Rust-owned backend hooks."""
+
+from pathlib import Path
+
+import tvm_ffi
+from tvm_ffi.libinfo import load_lib_ctypes
+
+from tvm_tirx.base import _LOADED_LIBS
+
+
+def register_backend():
+ """Register Rust-owned Python semantics."""
+ runtime_dir = Path(_LOADED_LIBS["tvm_runtime"]._name).resolve().parent
+ try:
+ _LOADED_LIBS["tvm_tirx_rust"] = load_lib_ctypes(
+ package="tvm_tirx",
+ target_name="tvm_tirx_rust",
+ extra_lib_paths=[runtime_dir],
+ mode="RTLD_LOCAL",
+ )
+ except (OSError, FileNotFoundError, RuntimeError):
+ pass
+ else:
+ # Unlike a runtime sidecar, the library registers its operators only
when asked to.
+
tvm_ffi.load_module(_LOADED_LIBS["tvm_tirx_rust"]._name).tirx_register_ops()
+ return None
+
+
+__all__ = ["register_backend"]
diff --git a/python/tvm_tirx/base.py b/python/tvm_tirx/base.py
index 1cfdc9249..af8665125 100644
--- a/python/tvm_tirx/base.py
+++ b/python/tvm_tirx/base.py
@@ -18,12 +18,9 @@
# pylint: disable=invalid-name, import-outside-toplevel
"""Base library for TVM."""
-import ctypes
import os
-import sys
from pathlib import Path
-import tvm_ffi
from tvm_ffi import libinfo as tvm_ffi_libinfo
from tvm_ffi.libinfo import load_lib_ctypes
@@ -64,25 +61,6 @@ if not _RUNTIME_ONLY:
"tvm_tirx", "tvm_tirx_compiler", "RTLD_LOCAL",
extra_lib_paths=_lib_paths
)
-# The operators written in Rust are in libtvm_tirx_rust, when the build has it
-# (``TVM_TIRX_USE_RUST``). They are registered now, as loading the compiler
library registered the
-# C++ ones, before any module looks for them; ``TVM_TIRX_RUST_LIBRARY`` names
another build of the
-# library to load instead, such as ``rust/target/debug/libtvm_tirx.so``.
Otherwise only the one next
-# to the compiler library is loaded, which the same build made: one found
elsewhere, such as on
-# ``LD_LIBRARY_PATH``, may be another build's.
-if not _RUNTIME_ONLY:
- _rust_lib_path = os.environ.get("TVM_TIRX_RUST_LIBRARY")
- if not _rust_lib_path:
- _rust_lib_path =
Path(_LOADED_LIBS["tvm_tirx_compiler"]._name).resolve().parent / (
- "libtvm_tirx_rust.dylib" if sys.platform == "darwin" else
"libtvm_tirx_rust.so"
- )
- if not _rust_lib_path.is_file():
- _rust_lib_path = None
- if _rust_lib_path:
- # The handle keeps the library, and the hooks it registers, loaded for
the process.
- _LOADED_LIBS["tvm_tirx_rust"] = ctypes.CDLL(str(_rust_lib_path),
ctypes.RTLD_LOCAL)
- tvm_ffi.load_module(str(_rust_lib_path)).tirx_register_ops()
-
if _RUNTIME_ONLY:
from tvm_ffi import registry as _tvm_ffi_registry
diff --git a/rust/examples/define_ops.rs b/rust/examples/define_ops.rs
index 814a02516..7bea93fc3 100644
--- a/rust/examples/define_ops.rs
+++ b/rust/examples/define_ops.rs
@@ -21,8 +21,8 @@
//!
//! `cargo build --example define_ops` builds this as
`target/debug/examples/libdefine_ops.so`,
//! which exports `tirx_register_ops`, as every library built on `tvm_tirx`
does.
-//! `tests/python/ir/test_op_rust.py` loads it in place of `libtvm_tirx_rust`
-//! (`TVM_TIRX_RUST_LIBRARY`), so `import tvm_tirx` registers these operators.
As a C++ operator
+//! `tests/python/ir/test_op_rust.py` loads it and calls `tirx_register_ops`,
as `import tvm_tirx`
+//! does for `libtvm_tirx_rust`, which registers these operators. As a C++
operator
//! does, each has its TVMScript builder written in Python, which the test
writes:
//! `T.example_clamp(i, n)`, `T.example_mark()`, `T.example_hint(i,
attrs={..})` and
//! `with T.example_loop(n) as j:`.
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index c4d377153..1706935ef 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -138,14 +138,13 @@
//!
//! The crate is also built as a shared library, which the TIRx build installs
as
//! `libtvm_tirx_rust` next to `libtvm_tirx_compiler` when it finds a cargo as
new as the crate's
-//! `rust-version` (`TVM_TIRX_USE_RUST`, `AUTO` by default). `import tvm_tirx`
loads it
-//! after the compiler library and calls `tirx_register_ops`, so the operators
the crate defines
-//! are registered before any Python module looks for them, as the C++ ones
are: `Op.get` finds
-//! them, a `Call` built in Python runs their hooks, and the TVMScript printer
prints their calls.
-//! As for a C++ operator, the TVMScript builder of an operator (`T.<name>`
for `tirx.<name>`) is a
-//! Python function written for it. Set `TVM_TIRX_RUST_LIBRARY` to the path of
another build of the
-//! library, such as `rust/target/debug/libtvm_tirx.so` from `cargo build`, to
load that one
-//! instead. `examples/define_ops.rs` builds a library of its own the same way.
+//! `rust-version` (`TVM_TIRX_USE_RUST`, `AUTO` by default). `import tvm_tirx`
loads it through
+//! the backend autoload (`tvm_tirx.backend.rust`) and calls
`tirx_register_ops`, so the operators
+//! the crate defines are registered by the time the import returns, as the
C++ ones are: `Op.get`
+//! finds them, a `Call` built in Python runs their hooks, and the TVMScript
printer prints their
+//! calls. As for a C++ operator, the TVMScript builder of an operator
(`T.<name>` for
+//! `tirx.<name>`) is a Python function written for it.
`examples/define_ops.rs` builds a library
+//! of its own the same way, which
`tvm_ffi.load_module(path).tirx_register_ops()` registers.
//!
//! # Defining a type in Rust
//!
diff --git a/tests/python/ir/test_op_rust.py b/tests/python/ir/test_op_rust.py
index f740e4c1a..a40da764a 100644
--- a/tests/python/ir/test_op_rust.py
+++ b/tests/python/ir/test_op_rust.py
@@ -14,11 +14,11 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-"""Operators defined in Rust are registered on ``import tvm_tirx``, as C++
ones are.
+"""Operators defined in Rust are registered when their library is loaded, and
used as C++ ones are.
-``rust/examples/define_ops.rs`` is built by cargo and loaded in place of
libtvm_tirx_rust
-(``TVM_TIRX_RUST_LIBRARY``) by a fresh interpreter, which then uses its
operators as it uses C++
-ones: their TVMScript builders are Python functions written for them.
+``rust/examples/define_ops.rs`` is built by cargo and loaded by a fresh
interpreter, as
+``import tvm_tirx`` loads libtvm_tirx_rust, which then uses its operators as
it uses C++ ones:
+their TVMScript builders are Python functions written for them.
"""
import os
@@ -26,6 +26,7 @@ import re
import shutil
import subprocess
import sys
+import tempfile
import textwrap
from pathlib import Path
@@ -65,26 +66,49 @@ def define_ops_library():
[cargo, "build", "--manifest-path", str(manifest), "--example",
"define_ops"], check=True
)
target_dir = Path(os.environ.get("CARGO_TARGET_DIR", RUST_DIR / "target"))
- name = "libdefine_ops.dylib" if sys.platform == "darwin" else
"libdefine_ops.so"
+ name = {"darwin": "libdefine_ops.dylib", "win32": "define_ops.dll"}.get(
+ sys.platform, "libdefine_ops.so"
+ )
return target_dir / "debug" / "examples" / name
def run_python(code, library):
- """Run `code` in a fresh interpreter that loads `library` as
libtvm_tirx_rust."""
- env = dict(os.environ, TVM_TIRX_RUST_LIBRARY=str(library))
- result = subprocess.run(
- [sys.executable, "-c", textwrap.dedent(code)],
- env=env,
- capture_output=True,
- text=True,
- check=False,
- )
+ """Run `code` in a fresh interpreter, with the path of `library` as its
argument.
+
+ The code runs from a file: TVMScript reads the source of the functions it
parses, which Python
+ keeps for code passed with `-c` only from 3.13 on.
+ """
+ with tempfile.TemporaryDirectory() as tmp:
+ script = Path(tmp) / "script.py"
+ script.write_text(textwrap.dedent(code))
+ result = subprocess.run(
+ [sys.executable, str(script), str(library)],
+ capture_output=True,
+ text=True,
+ check=False,
+ )
assert result.returncode == 0, result.stderr
-def test_rust_operators_are_registered_on_import(define_ops_library):
+def test_library_is_loaded_on_import():
+ """``import tvm_tirx`` loads libtvm_tirx_rust, when the build has it, as a
backend."""
+ from tvm_ffi.libinfo import _find_library_by_basename # pylint:
disable=import-outside-toplevel
+
+ from tvm_tirx.base import _LOADED_LIBS # pylint:
disable=import-outside-toplevel
+
+ runtime_dir = Path(_LOADED_LIBS["tvm_runtime"]._name).resolve().parent
+ try:
+ _find_library_by_basename("tvm_tirx", "tvm_tirx_rust",
extra_lib_paths=[runtime_dir])
+ except RuntimeError:
+ pytest.skip("the build has no libtvm_tirx_rust (TVM_TIRX_USE_RUST)")
+ assert "tvm_tirx_rust" in _LOADED_LIBS
+
+
+def test_rust_operators_are_registered_by_their_library(define_ops_library):
run_python(
"""
+ import sys
+
import tvm_ffi
import tvm_tirx
from tvm_tirx import ir
@@ -92,7 +116,8 @@ def
test_rust_operators_are_registered_on_import(define_ops_library):
from tvm_tirx.script.ir_builder.base import auto_emit_void_call
from tvm_tirx.tirx.script import ir_builder
- # Registered on import, with the doc comment and signature the Rust
definition gives.
+ # Registered by the library, with the doc comment and signature the
Rust definition gives.
+ tvm_ffi.load_module(sys.argv[1]).tirx_register_ops()
clamp = ir.Op.get("tirx.example_clamp")
assert clamp.doc == "Clamp an index to the range `[0, bound)`."
assert [arg.name for arg in clamp.args_info] == ["index", "bound"]