This is an automated email from the ASF dual-hosted git repository.
junrushao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git
The following commit(s) were added to refs/heads/main by this push:
new f173692 doc: Function, Exception and Module (#394)
f173692 is described below
commit f1736924f80fe12b79752e1b0713dbb843eb01e6
Author: Junru Shao <[email protected]>
AuthorDate: Sat Jan 10 18:19:04 2026 -0800
doc: Function, Exception and Module (#394)
---
docs/concepts/any.rst | 2 +-
docs/concepts/func_module.rst | 564 +++++++++++++++++++++++++++++++++++++
docs/concepts/object_and_class.rst | 3 +
docs/concepts/tensor.rst | 2 +-
docs/index.rst | 1 +
5 files changed, 570 insertions(+), 2 deletions(-)
diff --git a/docs/concepts/any.rst b/docs/concepts/any.rst
index a32744f..efc6652 100644
--- a/docs/concepts/any.rst
+++ b/docs/concepts/any.rst
@@ -432,6 +432,6 @@ Further Reading
---------------
- **Object system**: :doc:`object_and_class` covers how TVM-FFI objects work,
including reference counting and type checking
+- **Function system**: :doc:`func_module` covers function calling conventions
and the global registry
- **C examples**: :doc:`../get_started/stable_c_abi` demonstrates working with
:cpp:class:`TVMFFIAny` directly in C
- **Tensor conversions**: :doc:`tensor` covers how tensors flow through
:cpp:class:`~tvm::ffi::Any` and :cpp:class:`~tvm::ffi::AnyView`
-- **Function calling**: :doc:`../guides/cpp_lang_guide` explains how functions
use :cpp:class:`~tvm::ffi::Any` for arguments and returns
diff --git a/docs/concepts/func_module.rst b/docs/concepts/func_module.rst
new file mode 100644
index 0000000..1024b91
--- /dev/null
+++ b/docs/concepts/func_module.rst
@@ -0,0 +1,564 @@
+.. 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.
+
+Function, Exception and Module
+==============================
+
+TVM-FFI provides a unified and ABI-stable calling convention that enables
+cross-language function calls between C++, Python, Rust, and other languages.
+Functions are first-class :doc:`TVM-FFI objects <object_and_class>`.
+
+This tutorial covers everything you need to know about defining, registering,
+and calling TVM-FFI functions, their exception handling, and working with
modules.
+
+Glossary
+--------
+
+TVM-FFI ABI. :cpp:type:`TVMFFISafeCallType`
+ A stable C calling convention where every function is represented by a
single signature,
+ which enables type-erased, cross-language function calls.
+ This calling convention is used across all TVM-FFI function calls at the ABI
boundary.
+ See :ref:`Stable C ABI <tvm_ffi_c_abi>` for a quick introduction.
+
+TVM-FFI Function. :py:class:`tvm_ffi.Function`,
:cpp:class:`tvm::ffi::FunctionObj`, :cpp:class:`tvm::ffi::Function`
+ A reference-counted function object and its managed reference, which wraps
any callable,
+ including language-agnostic functions and lambdas (C++, Python, Rust, etc.),
+ member functions, external C symbols, and other callable objects,
+ all sharing the same calling convention.
+
+TVM-FFI Module. :py:class:`tvm_ffi.Module`, :cpp:class:`tvm::ffi::ModuleObj`,
:cpp:class:`tvm::ffi::Module`
+ A namespace for a collection of functions, loaded from a shared library via
``dlopen`` (Linux, macOS) or ``LoadLibraryW`` (Windows),
+ or statically linked to the current executable.
+
+Global Functions and Registry. :py:func:`tvm_ffi.get_global_func` and
:py:func:`tvm_ffi.register_global_func`
+ A registry is a table that maps string names to
:cpp:class:`~tvm::ffi::Function` objects
+ and their metadata (name, docs, signatures, etc.) for cross-language access.
+ Functions in the registry are called **global functions**.
+
+Common Usage
+------------
+
+TVM-FFI C Symbols
+~~~~~~~~~~~~~~~~~
+
+**Shared library**. Use :c:macro:`TVM_FFI_DLL_EXPORT_TYPED_FUNC` to export
+a function as a C symbol that follows the TVM-FFI ABI:
+
+.. code-block:: cpp
+
+ static int AddTwo(int x) { return x + 2; }
+
+ TVM_FFI_DLL_EXPORT_TYPED_FUNC(/*ExportName=*/add_two, /*Function=*/AddTwo)
+
+This creates a C symbol ``__tvm_ffi_<ExportName>`` in the shared library,
+which can then be loaded and called via :py:func:`tvm_ffi.load_module`:
+
+.. code-block:: python
+
+ import tvm_ffi
+
+ mod = tvm_ffi.load_module("path/to/library.so")
+ result = mod.add_two(40) # -> 42
+
+**System library**. For symbols bundled in the same executable, use
:cpp:func:`TVMFFIEnvModRegisterSystemLibSymbol`
+to register each symbol during static initialization within a
:c:macro:`TVM_FFI_STATIC_INIT_BLOCK`.
+See :py:func:`tvm_ffi.system_lib` for a complete workflow.
+
+
+Global Functions
+~~~~~~~~~~~~~~~~
+
+**Register a global function**. In C++, use
:cpp:class:`tvm::ffi::reflection::GlobalDef` to
+register a function:
+
+.. code-block:: cpp
+
+ #include <tvm/ffi/tvm_ffi.h>
+
+ static int AddOne(int x) { return x + 1; }
+
+ TVM_FFI_STATIC_INIT_BLOCK() {
+ namespace refl = tvm::ffi::reflection;
+ refl::GlobalDef()
+ .def("my_ext.add_one", AddOne, "Add one to the input");
+ }
+
+The :c:macro:`TVM_FFI_STATIC_INIT_BLOCK` macro ensures that registration occurs
+during library initialization. The registered function is then accessible from
+Python by the name ``my_ext.add_one``.
+
+In Python, use the decorator :py:func:`tvm_ffi.register_global_func` to
register a global function:
+
+.. code-block:: python
+
+ import tvm_ffi
+
+ @tvm_ffi.register_global_func("my_ext.add_one")
+ def add_one(x: int) -> int:
+ return x + 1
+
+**Retrieve a global function**. After registration, functions are accessible
by name.
+
+In Python, use :py:func:`tvm_ffi.get_global_func` to retrieve a global
function:
+
+.. code-block:: python
+
+ import tvm_ffi
+
+ # Get a function from the global registry
+ add_one = tvm_ffi.get_global_func("my_ext.add_one")
+ result = add_one(41) # -> 42
+
+In C++, use :cpp:func:`tvm::ffi::Function::GetGlobal` or
:cpp:func:`tvm::ffi::Function::GetGlobalRequired`
+to retrieve a global function:
+
+.. code-block:: cpp
+
+ ffi::Function func = ffi::Function::GetGlobalRequired("my_ext.add_one");
+ int result = func(41); // -> 42
+
+
+Create Functions
+~~~~~~~~~~~~~~~~
+
+**From C++**. An :cpp:class:`tvm::ffi::Function` can be created via
:cpp:func:`tvm::ffi::Function::FromTyped`
+or :cpp:class:`tvm::ffi::TypedFunction`'s constructor.
+
+.. code-block:: cpp
+
+ // Create type-erased function: add_type_erased
+ ffi::Function add_type_erased = ffi::Function::FromTyped([](int x, int y) {
+ return x + y;
+ });
+
+ // Create a typed function: add_typed
+ ffi::TypedFunction<int(int, int)> add_typed = [](int x, int y) {
+ return x + y;
+ };
+
+ // Convert a typed function to a type-erased function
+ ffi::Function generic = add_typed;
+
+**From Python**. Any Python :py:class:`Callable <collections.abc.Callable>` is
automatically converted
+to a :py:class:`tvm_ffi.Function` at the ABI boundary. The example below
demonstrates that in ``my_ext.bind``:
+
+- The input ``func`` is automatically converted to a
:py:class:`tvm_ffi.Function`.
+- The returned lambda is also automatically converted to a
:py:class:`tvm_ffi.Function`.
+
+.. code-block:: python
+
+ import tvm_ffi
+
+ @tvm_ffi.register_global_func("my_ext.bind")
+ def bind(func, x):
+ assert isinstance(func, tvm_ffi.Function)
+ return lambda *args: func(x, *args) # converted to `tvm_ffi.Function`
+
+ def add_x_y(x, y):
+ return x + y
+
+ func_bind = tvm_ffi.get_global_func("my_ext.bind")
+ add_y = func_bind(add_x_y, 1) # bind x = 1
+ assert isinstance(add_y, tvm_ffi.Function)
+ print(add_y(2)) # -> 3
+
+
+:py:func:`tvm_ffi.convert` explicitly converts a Python callable to
:py:class:`tvm_ffi.Function`:
+
+.. code-block:: python
+
+ import tvm_ffi
+
+ def add(x, y):
+ return x + y
+
+ func_add = tvm_ffi.convert(add)
+ print(func_add(1, 2))
+
+
+Exception ABI
+-------------
+
+This section describes the exception handling contract in the TVM-FFI Stable C
ABI.
+Exceptions are first-class citizens in TVM-FFI, and this section specifies:
+
+- How to properly throw exceptions from a TVM-FFI ABI function
+- How to check for and propagate exceptions from a TVM-FFI ABI function
+
+TVM-FFI C ABI
+~~~~~~~~~~~~~
+
+All TVM-FFI functions ultimately conform to the :cpp:type:`TVMFFISafeCallType`
signature,
+which provides a stable C ABI for cross-language calls. The C calling
convention is defined as:
+
+.. code-block:: cpp
+
+ int tvm_ffi_c_abi(
+ void* handle, // Resource handle
+ const TVMFFIAny* args, // Input arguments (non-owning)
+ int32_t num_args, // Number of input arguments
+ TVMFFIAny* result // Output argument (owning, zero-initialized)
+ );
+
+**Input arguments**. The input arguments are passed as an array of
:cpp:class:`tvm::ffi::AnyView` values,
+specified by ``args`` and ``num_args``.
+
+**Output argument**. The output argument ``result`` is an owning
:cpp:type:`tvm::ffi::Any`
+that the caller must zero-initialize before the call.
+
+**Return value**. The ABI returns an **error code** that indicates:
+
+- ``0``: Success
+- ``-1``: Error occurred, retrievable with
:cpp:func:`TVMFFIErrorMoveFromRaised`
+- ``-2``: Very rare frontend error
+
+.. hint::
+ See :doc:`Any <any>` for more details on the semantics of
:cpp:type:`tvm::ffi::AnyView` and :cpp:type:`tvm::ffi::Any`.
+
+Retrieve Errors in C
+~~~~~~~~~~~~~~~~~~~~
+
+When a TVM-FFI function returns a non-zero code, it indicates that an error
occurred
+and a :cpp:class:`tvm::ffi::ErrorObj` is stored in thread-local storage (TLS).
+This section shows how to retrieve the error object and print the error
message and backtrace.
+
+.. note::
+
+ An :cpp:class:`~tvm::ffi::ErrorObj` is a :cpp:class:`~tvm::ffi::Object` with
a :cpp:class:`TVMFFIErrorCell` payload
+ as defined below:
+
+ .. code-block:: cpp
+
+ typedef struct {
+ TVMFFIByteArray kind; // Error type (e.g., "ValueError")
+ TVMFFIByteArray message; // Error message
+ TVMFFIByteArray backtrace; // Stack trace (most-recent call first)
+ void (*update_backtrace)(...); // Hook to append/replace backtrace
+ } TVMFFIErrorCell;
+
+**Print an Error**. The example code below shows how to print an error message
and backtrace.
+
+.. code-block:: cpp
+
+ #include <tvm/ffi/c_api.h>
+
+ void PrintError(TVMFFIObject* err) {
+ TVMFFIErrorCell* cell = (TVMFFIErrorCell*)((char*)err +
sizeof(TVMFFIObject));
+ fprintf(stderr, "%.*s: %.*s\n", (int)cell->kind.size, cell->kind.data,
(int)cell->message.size, cell->message.data);
+ if (cell->backtrace.size) {
+ fprintf(stderr, "Backtrace:\n%.*s\n", (int)cell->backtrace.size,
cell->backtrace.data);
+ }
+ }
+
+The payload of the error object is a :cpp:type:`TVMFFIErrorCell` structure
+containing the error kind, message, and backtrace. It can be accessed
+by skipping the :cpp:type:`TVMFFIObject` header using pointer arithmetic.
+
+**Retrieve the error object**. When the error code is ``-1``, the error object
is stored in TLS
+and can be retrieved with :cpp:func:`TVMFFIErrorMoveFromRaised`.
+
+.. code-block:: cpp
+
+ void HandleReturnCode(int rc) {
+ TVMFFIObject* err = NULL;
+ if (rc == 0) {
+ // Success
+ } else if (rc == -1) {
+ // Move the raised error from TLS (clears TLS slot)
+ TVMFFIErrorMoveFromRaised(&err); // now `err` owns the error object
+ if (err != NULL) {
+ PrintError(err); // print the error
+ TVMFFIObjectDecRef(err); // Release the error object
+ }
+ } else if (rc == -2) {
+ // Frontend (e.g., Python) already has an exception set.
+ // Do not fetch from TLS; consult the frontend's error mechanism.
+ }
+ }
+
+This function transfers ownership of the error object to the caller and clears
the TLS slot.
+You must call :cpp:func:`TVMFFIObjectDecRef` to release the object when done
to avoid memory leaks.
+
+**Rare frontend errors**. Error code ``-2`` is reserved for rare frontend
errors. It is returned only
+when the C API :cpp:func:`TVMFFIEnvCheckSignals` returns non-zero during
execution, indicating that
+the Python side has a pending signal requiring attention. In this case, the
caller should not fetch
+the error object from TLS but instead consult the frontend's error mechanism
to handle the exception.
+
+Raise Errors in C
+~~~~~~~~~~~~~~~~~
+
+As part of TVM-FFI's calling convention, returning ``-1`` indicates that an
error occurred
+and the error object is stored in the TLS slot. The error object can contain
arbitrary
+user-defined information, such as error messages, backtraces, or Python
frame-local variables.
+
+.. hint::
+ Compiler code generation may use similar patterns to raise errors in
generated code.
+
+The example below sets the TLS error and returns ``-1`` using
:cpp:func:`TVMFFIErrorSetRaisedFromCStr`:
+
+.. code-block:: cpp
+
+ #include <tvm/ffi/c_api.h>
+
+ int __tvm_ffi_my_kernel(void* handle, const TVMFFIAny* args,
+ int32_t num_args, TVMFFIAny* result) {
+ // Validate inputs
+ if (num_args < 2) {
+ TVMFFIErrorSetRaisedFromCStr("ValueError", "Expected at least 2
arguments");
+ return -1;
+ }
+ // ... kernel implementation ...
+ return 0;
+ }
+
+Alternatively, :cpp:func:`TVMFFIErrorSetRaisedFromCStrParts` accepts explicit
string lengths,
+which is useful when the error kind and message are not null-terminated.
+
+**Propagating errors**. For chains of generated calls, simply propagate return
codes—TLS carries
+the error details:
+
+.. code-block:: cpp
+
+ int outer_function(...) {
+ int err_code = 0;
+
+ err_code = inner_function(...);
+ if (err_code != 0) goto RAII; // Propagate error; TLS has the details
+
+ RAII:
+ // clean up owned resources
+ return err_code;
+ }
+
+Function
+--------
+
+Layout and ABI
+~~~~~~~~~~~~~~
+
+:cpp:class:`tvm::ffi::FunctionObj` stores two call pointers in
:cpp:class:`TVMFFIFunctionCell`:
+
+.. code-block:: cpp
+
+ typedef struct {
+ TVMFFISafeCallType safe_call;
+ void* cpp_call;
+ } TVMFFIFunctionCell;
+
+``safe_call`` is used for cross-ABI function calls: it intercepts exceptions
and stores them in TLS.
+``cpp_call`` is used within the same DSO, where exceptions are thrown directly
for better performance.
+
+.. important::
+
+ :cpp:func:`TVMFFIFunctionCall` is the idiomatic way to call a
:cpp:class:`tvm::ffi::FunctionObj` in C,
+ while ``safe_call`` or ``cpp_call`` remain low-level ABIs for fast access.
+
+**Conversion with Any**. Since :py:class:`tvm_ffi.Function` is a TVM-FFI
object, it follows the same
+conversion rules as any other TVM-FFI object. See :ref:`Object Conversion with
Any <object-conversion-with-any>` for details.
+
+
+Throw and Catch Errors
+~~~~~~~~~~~~~~~~~~~~~~
+
+TVM-FFI gracefully handles exceptions across language boundaries without
requiring manual
+error code management.
+
+.. important::
+ Stack traces from all languages are properly preserved and concatenated in
the TVM-FFI Stable C ABI.
+
+**Python**. In Python, raise native :py:class:`Exception <Exception>`
instances or derived classes.
+TVM-FFI catches these at the ABI boundary and converts them to
:cpp:class:`tvm::ffi::Error` objects.
+When C++ code calls into Python and a Python exception occurs, it propagates
back to C++ as a
+:cpp:class:`tvm::ffi::Error`, which C++ code can handle appropriately.
+
+**C++**. In C++, use :cpp:class:`tvm::ffi::Error` or the
:c:macro:`TVM_FFI_THROW` macro:
+
+.. code-block:: cpp
+
+ #include <tvm/ffi/error.h>
+
+ void ThrowError(int x) {
+ if (x < 0) {
+ TVM_FFI_THROW(ValueError) << "x must be non-negative, got " << x;
+ }
+ }
+
+The :c:macro:`TVM_FFI_THROW` macro captures the current file name, line
number, stack trace,
+and error message, then constructs a :cpp:class:`tvm::ffi::Error` object. At
the ABI boundary,
+this error is stored in TLS and the function returns ``-1`` per the
:cpp:type:`TVMFFISafeCallType`
+calling convention.
+
+.. hint::
+ A detailed implementation of such graceful handling behavior can be found
+ in :c:macro:`TVM_FFI_SAFE_CALL_BEGIN` / :c:macro:`TVM_FFI_SAFE_CALL_END`
macros.
+
+
+C Registry APIs
+~~~~~~~~~~~~~~~
+
+.. list-table::
+ :header-rows: 1
+ :widths: 40 60
+
+ * - C API
+ - Description
+ * - :cpp:func:`TVMFFIFunctionGetGlobal`
+ - Get a function by name; returns an owning handle.
+ * - :cpp:func:`TVMFFIFunctionSetGlobal`
+ - Register a function in the global registry.
+ * - :cpp:func:`TVMFFIFunctionCall`
+ - Call a function with the given arguments.
+
+Compiler developers commonly need to look up global functions in generated
code. Use
+:cpp:func:`TVMFFIFunctionGetGlobal` to retrieve a function by name, then call
it with :cpp:func:`TVMFFIFunctionCall`.
+The example below demonstrates how to look up and call a global function in C:
+
+.. code-block:: cpp
+
+ int LookupAndCall(const char* global_function_name, const TVMFFIAny* args,
int num_args, TVMFFIAny* result) {
+ TVMFFIObject* func = NULL;
+ int err_code;
+ if ((err_code = TVMFFIFunctionGetGlobal(global_function_name, &func)) !=
0)
+ goto RAII;
+ if ((err_code = TVMFFIFunctionCall(func, args, num_args, result)) != 0)
+ goto RAII;
+
+ RAII: // clean up owned resources
+ if (func != NULL) TVMFFIObjectDecRef(func);
+ return err_code;
+ }
+
+Modules
+-------
+
+A :py:class:`tvm_ffi.Module` is a namespace for a collection of functions that
can be loaded
+from a shared library or bundled with the current executable. Modules provide
namespace isolation
+and dynamic loading capabilities for TVM-FFI functions.
+
+Shared Library
+~~~~~~~~~~~~~~
+
+Shared library modules are loaded dynamically at runtime via ``dlopen``
(Linux, macOS) or
+``LoadLibraryW`` (Windows). This is the most common way to distribute and load
compiled functions.
+
+**Export functions from C++**. Use :c:macro:`TVM_FFI_DLL_EXPORT_TYPED_FUNC` to
export
+a function as a C symbol that follows the TVM-FFI ABI:
+
+.. code-block:: cpp
+
+ #include <tvm/ffi/tvm_ffi.h>
+
+ static int AddTwo(int x) { return x + 2; }
+
+ // Exports as symbol `__tvm_ffi_add_two`
+ TVM_FFI_DLL_EXPORT_TYPED_FUNC(add_two, AddTwo);
+
+**Load and call from Python**. Use :py:func:`tvm_ffi.load_module` to load the
shared library:
+
+.. code-block:: python
+
+ import tvm_ffi
+
+ # Load the shared library
+ mod = tvm_ffi.load_module("path/to/library.so")
+
+ # Access functions by name
+ result = mod.add_two(40) # -> 42
+
+ # Alternative: explicit function retrieval
+ func = mod.get_function("add_two")
+ result = func(40) # -> 42
+
+**Build and load from source**. For rapid prototyping,
:py:func:`tvm_ffi.cpp.load` compiles
+C++/CUDA source files and loads them as a module in one step:
+
+.. code-block:: python
+
+ import tvm_ffi.cpp
+
+ # Compile and load in one step
+ mod = tvm_ffi.cpp.load(
+ name="my_ops",
+ cpp_files="my_ops.cpp",
+ )
+ result = mod.add_two(40)
+
+Essentially, :py:func:`tvm_ffi.cpp.load` is a convenience function that
JIT-compiles the source
+files and loads the resulting library as a :py:class:`tvm_ffi.Module`.
+
+System Library
+~~~~~~~~~~~~~~
+
+System library modules contain symbols that are statically linked to the
current executable.
+
+This technique is useful when you want to simulate dynamic module loading
behavior but cannot
+or prefer not to use ``dlopen`` or ``LoadLibraryW`` (e.g., on iOS). Functions
are statically
+linked to the executable as a system library module. Symbols can be registered
via
+:cpp:func:`TVMFFIEnvModRegisterSystemLibSymbol` and looked up via
:py:func:`tvm_ffi.system_lib`.
+
+**Register symbols in C/C++**. Use
:cpp:func:`TVMFFIEnvModRegisterSystemLibSymbol` to register
+a symbol during static initialization:
+
+.. code-block:: cpp
+
+ #include <tvm/ffi/c_api.h>
+ #include <tvm/ffi/extra/c_env_api.h>
+
+ // A function following the TVM-FFI ABI
+ static int add_one_impl(void*, const TVMFFIAny* args, int32_t num_args,
TVMFFIAny* result) {
+ TVM_FFI_SAFE_CALL_BEGIN();
+ int64_t x = reinterpret_cast<const
tvm::ffi::AnyView*>(args)[0].cast<int64_t>();
+ reinterpret_cast<tvm::ffi::Any*>(result)[0] = x + 1;
+ TVM_FFI_SAFE_CALL_END();
+ }
+
+ // Register during static initialization
+ // The symbol name follows the convention `__tvm_ffi_<prefix>.<name>`
+ TVM_FFI_STATIC_INIT_BLOCK() {
+ TVMFFIEnvModRegisterSystemLibSymbol(
+ "__tvm_ffi_my_prefix.add_one",
+ reinterpret_cast<void*>(add_one_impl)
+ );
+ }
+
+**Access from Python**. Use :py:func:`tvm_ffi.system_lib` to get the system
library module:
+
+.. code-block:: python
+
+ import tvm_ffi
+
+ # Get system library with symbol prefix "my_prefix."
+ # This looks up symbols prefixed with `__tvm_ffi_my_prefix.`
+ mod = tvm_ffi.system_lib("my_prefix.")
+
+ # Call the registered function
+ func = mod.add_one # looks up `__tvm_ffi_my_prefix.add_one`
+ result = func(10) # -> 11
+
+.. note::
+
+ The system library is intended for statically linked symbols that exist for
the entire
+ program lifetime. For dynamic loading with the ability to unload, use
shared library modules instead.
+
+
+Further Reading
+---------------
+
+- :doc:`any`: How functions are stored in :cpp:class:`~tvm::ffi::Any`
containers
+- :doc:`object_and_class`: The object system that backs
:cpp:class:`~tvm::ffi::FunctionObj`
+- :doc:`../packaging/python_packaging`: Packaging functions for Python wheels
+- :doc:`abi_overview`: Low-level ABI details for the function calling
convention
diff --git a/docs/concepts/object_and_class.rst
b/docs/concepts/object_and_class.rst
index 6c25c79..7b3e5b0 100644
--- a/docs/concepts/object_and_class.rst
+++ b/docs/concepts/object_and_class.rst
@@ -308,6 +308,8 @@ C APIs are provided to manipulate the reference count of an
object:
- :cpp:func:`TVMFFIObjectDecRef` to decrease the strong reference count.
+.. _object-conversion-with-any:
+
Conversion between :cpp:class:`~tvm::ffi::Any`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -473,6 +475,7 @@ Further Reading
---------------
- :doc:`any`: How objects are stored in :cpp:class:`~tvm::ffi::Any` containers
+- :doc:`func_module`: Function objects and the global registry
- :doc:`tensor`: Tensor objects and DLPack interoperability
- :doc:`../packaging/python_packaging`: Packaging C++ objects for Python
- :doc:`abi_overview`: Low-level ABI details for the object system
diff --git a/docs/concepts/tensor.rst b/docs/concepts/tensor.rst
index fcdd1c4..22123f0 100644
--- a/docs/concepts/tensor.rst
+++ b/docs/concepts/tensor.rst
@@ -351,7 +351,7 @@ it does not include:
Conversion between :cpp:class:`TVMFFIAny`
-----------------------------------------
-At the stable C ABI boundary, TVM-FFI passes values using an "Any-like"
carrier—either
+At the stable C ABI boundary, TVM-FFI passes values using an "Any-like"
carrier - either
:cpp:class:`Any <tvm::ffi::Any>` (owning) or :cpp:class:`AnyView
<tvm::ffi::AnyView>` (non-owning).
These are 128-bit tagged unions derived from :cpp:class:`TVMFFIAny` that
contain:
diff --git a/docs/index.rst b/docs/index.rst
index dcef01f..ef2b2b8 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -62,6 +62,7 @@ Table of Contents
concepts/any.rst
concepts/object_and_class.rst
concepts/tensor.rst
+ concepts/func_module.rst
.. toctree::
:maxdepth: 1