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 c78e8b4  doc: abi overview (#402)
c78e8b4 is described below

commit c78e8b4eefa076c457af97bd3930dd664aec71c3
Author: Junru Shao <[email protected]>
AuthorDate: Tue Jan 13 11:15:06 2026 -0800

    doc: abi overview (#402)
---
 docs/concepts/abi_overview.md        | 462 -----------------------------
 docs/concepts/abi_overview.rst       | 551 +++++++++++++++++++++++++++++++++++
 docs/concepts/any.rst                |  42 +--
 docs/concepts/func_module.rst        | 224 ++++----------
 docs/concepts/object_and_class.rst   | 112 ++-----
 docs/concepts/tensor.rst             | 150 ++--------
 docs/guides/compiler_integration.md  |   2 +-
 docs/index.rst                       |   2 +-
 examples/abi_overview/example_code.c | 292 +++++++++++++++++++
 include/tvm/ffi/c_api.h              |  16 +-
 10 files changed, 977 insertions(+), 876 deletions(-)

diff --git a/docs/concepts/abi_overview.md b/docs/concepts/abi_overview.md
deleted file mode 100644
index 573f7e4..0000000
--- a/docs/concepts/abi_overview.md
+++ /dev/null
@@ -1,462 +0,0 @@
-<!--- 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. -->
-
-# ABI Overview
-
-This section provides an overview of the ABI convention of TVM FFI. The ABI
-is designed around the following key principles:
-
-- **Stable C ABI:** Core ABI is defined on top of a stable C ABI.
-- **Minimal and efficient:** Keep things simple when possible and bring 
close-to-metal efficiency.
-- **Focus on machine learning systems:** while also ensuring reasonable 
extensibility.
-
-To explain the concepts in the following sections, we will write in 
**low-level C/C++ code** when possible,
-so the code itself illustrates the low-level semantics of how to work with the 
ABI convention.
-These can serve as references for how to build language bindings and compiler 
codegen for the ABI.
-
-```{note}
-The authoritative ABI specifications are defined in 
[tvm/ffi/c_api.h](https://github.com/apache/tvm-ffi/blob/main/include/tvm/ffi/c_api.h)
 for core ABI,
-and 
[tvm/ffi/extra/c_env_api.h](https://github.com/apache/tvm-ffi/blob/main/include/tvm/ffi/extra/c_env_api.h)
 for extra support features
-such as stream handling. This document provides explanations about design 
concepts and rationales.
-```
-
-## Simplified Example
-
-Before diving into details, it is helpful to review at a high level
-what happens when a function is called in TVM FFI ABI.
-One main design goal here is to represent all kinds of functions in a single
-unified C signature. Please review the following
-simplified code example that illustrates the key idea:
-
-```c++
-// simplified struct for TVMFFIAny
-typedef struct TVMFFIAny {
-  int32_t type_index;
-  uint32_t zero_padding;
-  // union values
-  union {
-    int64_t v_int64;       // integers
-    double v_float64;      // floating-point numbers
-    const char* v_c_str;   // raw C-string
-  };
-};
-
-// This is the signature of TVM FFI function ABI
-typedef int (*TVMFFISafeCallType)(
-   void* handle, const TVMFFIAny* args, int32_t num_args, TVMFFIAny* result
-);
-
-// An example function signature
-int MyFunc(const char* param0, int param1);
-
-// This is what MyFunc looks like when exposed through TVM FFI ABI
-int MyFuncTVMFFISafeCall(
-  void* handle, const TVMFFIAny* args, int32_t num_args, TVMFFIAny* result
-) {
-  assert(args[0].type_index == kTVMFFIRawStr);
-  assert(args[1].type_index == kTVMFFInt);
-  result->type_index = kTVMFFInt;
-  result->v_int64 = MyFunc(args[0].v_c_str, args[1].v_int64);
-  // return value indicates no error occurred
-  return 0;
-}
-
-// This is how we call the MyFuncTVMFFISafeCall
-// this can happen on the caller side in another language (e.g. python)
-int CallTVMFFISafeCall(const char* param0, int param1) {
-  // arguments on stack
-  TVMFFIAny args[2], result;
-  args[0].type_index = kTVMFFIRawStr;
-  args[0].v_c_str = param0;
-  args[1].type_index = kTVMFFInt;
-  args[1].v_int64 = param1;
-  result.type_index = kTVMFFINone;
-  // In this case we do not need handle
-  // handle is used to hold closure pointers
-  void* handle = nullptr;
-  int num_args = 2;
-  MyFuncTVMFFISafeCall(handle, args, num_args, &result);
-  return result.v_int64;
-}
-```
-
-At a high level, the `TVMFFISafeCallType` signature does the following things:
-
-- Arguments and return values are stored in structured `TVMFFIAny`
-  - Each value comes with a `type_index` to indicate its type
-  - Values are stored in union fields, depending on the specific type.
-- Caller can explicitly store the type index and value into
-  a stack of `TVMFFIAny`.
-- Callee can load the parameters from args and check their type indices.
-
-In this way, the same `TVMFFISafeCallType` can be used to represent any 
function
-that contains an arbitrary number of arguments and types that can be 
identified by `type_index`.
-Of course, this is a simplified example and we did not touch on specific 
details
-like Any value format and error handling. The following sections will provide 
a more systematic
-treatment of each of these specific topics.
-You can keep this example in mind as the overall picture and refine it as you 
read through
-the following sections.
-
-## TVMFFIAny Storage Format
-
-To start with, we need a mechanism to store the values that are passed across 
machine learning frameworks.
-It achieves this using a core data structure called TVMFFIAny.
-
-```c++
-typedef struct TVMFFIAny {
-  int32_t type_index;
-  union {  // 4 bytes
-    uint32_t zero_padding;
-    uint32_t small_str_len;
-  };
-  // union values
-  union {
-    int64_t v_int64;       // integers
-    double v_float64;      // floating-point numbers
-    void* v_ptr;           // typeless pointers
-    const char* v_c_str;   // raw C-string
-    TVMFFIObject* v_obj;   // ref counted objects
-    DLDataType v_dtype;    // data type
-    DLDevice v_device;     // device
-    char v_bytes[8];       // small string
-    ...
-  };
-} TVMFFIAny;
-```
-
-TVMFFIAny is a 16-byte C structure that follows the design principle of 
tagged-union:
-
-- `type_index` helps us identify the type being stored.
-- The value union part is designed to store the value:
-  - Small POD values (like integers and floats) are stored directly as 
"on-stack" values.
-  - `v_obj` can also point to a managed heap-allocated object, which we will 
discuss next.
-- The second field stores metadata for small strings.
-
-### Storing a POD Value
-
-There are many values that are plain-old-data types. In such cases, we store 
them directly
-on-stack in the value part of the TVMFFIAny. The following example shows how 
to store
-an int.
-
-```c++
-void SetIntValue(TVMFFIAny* any, int value) {
-  // must zero the entire space first
-  any->type_index = kTVMFFIInt;
-  any->zero_padding = 0;
-  any->v_int64 = value;
-}
-```
-
-:::{note}
-
-We **must zero the content that is not being used** by
-the current value type. The following example shows a common place
-where mistakes can be made when we forget to zero the value field
-on 32-bit platforms (where pointers only fill the 32-bit part of the value).
-
-```c++
-void SetOpaquePtrValue(TVMFFIAny* any, void* opaque_ptr) {
-  any->type_index = kTVMFFIOpaquePtr;
-  // must zero the padding
-  any->zero_padding = 0;
-  // the zeroing is needed for 32-bit platforms!
-  any->v_uint64 = 0;
-  any->v_ptr = opaque_ptr;
-}
-```
-
-**Rationale:** Such invariants allow us to directly compare
-and hash TVMFFIAny in bytes for quick equality checks without going through
-type index switching.
-:::
-
-(object-storage-format)=
-
-## Object Storage Format
-
-When TVMFFIAny points to a heap-allocated object (such as n-dimensional 
arrays),
-we adopt a unified object storage format, defined as follows:
-
-```c++
-typedef struct TVMFFIObject {
-  uint64_t combined_ref_count;
-  int32_t type_index;
-  uint32_t __padding;
-  union {
-    void (*deleter)(struct TVMFFIObject* self, int flags);
-    int64_t __ensure_align;
-  };
-} TVMFFIObject;
-```
-
-`TVMFFIObject` defines a common 24-byte intrusive header that all in-memory 
objects share:
-
-- `combined_ref_count` packs strong and weak reference counter of the object 
into a single 64bit field
-  - The lower 32bits stores the strong atomic reference counter:
-    `strong_ref_count = combined_ref_count & 0xFFFFFFFF`
-  - The higher 32bits stores the weak atomic reference counter:
-    `weak_ref_count = (combined_ref_count >> 32) & 0xFFFFFFFF`
-- `type_index` helps us identify the type being stored, which is consistent 
with `TVMFFIAny.type_index`.
-- `deleter` should be called when either the strong or weak ref counter goes 
to zero.
-  - The flags are set to indicate the event of either weak or strong going to 
zero, or both.
-  - When strong reference counter gets to zero, the deleter needs to call the 
destructor of the object.
-  - When weak reference counter gets to zero, the deleter needs to free the 
memory allocated by self.
-
-**Rationales:** There are several considerations when designing the data 
structure:
-
-- `type_index` enables runtime dynamic type checking and casting.
-- We introduce weak/strong ref counters so we can be compatible with systems 
that need weak pointers.
-- The weak ref counter is kept as 32-bit so we can pack the object header as 
24 bytes.
-- `deleter` ensures that objects allocated from one language/runtime can be 
safely deleted in another.
-
-The object format provides a unified way to manage object life-cycle and 
dynamic type casting
-for heap-allocated objects, including Shape, Tensor,
-Function, Array, Map and other custom objects.
-
-### DLPack Compatible Tensor
-
-We provide first-class support for DLPack raw unmanaged pointer support as 
well as a managed Tensor object that
-directly adopts the DLPack DLTensor layout. The overall layout of the Tensor 
object is as follows:
-
-```c++
-struct TensorObj: public ffi::Object, public DLTensor {
-};
-```
-
-That means we can read out the array buffer information from an `TVMFFIAny`
-in the following way:
-
-```c++
-DLTensor* ReadDLTensorPtr(const TVMFFIAny *value) {
-  if (value->type_index == kTVMFFIDLTensorPtr) {
-    return static_cast<DLTensor*>(value->v_ptr);
-  }
-  assert(value->type_index == kTVMFFITensor);
-  return reinterpret_cast<DLTensor*>(
-  reinterpret_cast<char*>(value->v_obj) + sizeof(TVMFFIObject));
-}
-```
-
-The above code can be used as a reference to implement compiler codegen for 
data.
-Note that the C++ API automatically handles such conversion.
-
-### Advanced: Dynamic Type Index
-
-The `TVMFFITypeIndex` defines a set of type indices. Each built-in type has a 
corresponding statically
-assigned type index that is defined in the enum. Static type indices should be 
sufficient for most
-library use cases.
-For advanced use cases we also support user-defined objects whose `type_index` 
are assigned at startup time
-by calling `TVMFFITypeGetOrAllocIndex` with a unique
-`type_key` string. This design allows us to enable decentralized extension of 
the objects as long as the `type_key`
-values are unique by appending namespace prefix to the key.
-
-## AnyView and Managed Any
-
-```{seealso}
-For a comprehensive tutorial on Any including ownership semantics, extraction 
methods,
-and common patterns, see {doc}`any`.
-```
-
-An `TVMFFIAny` can either be treated as a strongly managed value 
(corresponding to `ffi::Any` in C++),
-or an unmanaged value (corresponding to `ffi::AnyView` in C++).
-
-- For POD types, there is no difference between the two.
-- For object types, copying of AnyView should not change reference counters, 
while copying and deletion
-  of managed Any should result in increase and decrease of strong reference 
counters.
-- When we convert AnyView to Any, we will convert raw C string `const char*` 
and `const TVMFFIByteArray*`
-  into their managed counterparts (String and Bytes).
-- C API function `TVMFFIAnyViewToOwnedAny` is provided to perform such 
conversion.
-
-Unless the user is writing a compiler backend that needs low-level C style 
access, we encourage use of the
-C++ API to automatically manage conversion and casting between normal types 
and Any. The following code
-shows some example usage of the C++ API.
-
-```c++
-#include <tvm/ffi/any.h>
-
-void AnyExample() {
-  namespace ffi = tvm::ffi;
-  // Here is a managed any
-  ffi::Any value = "hello world";
-  // explicit cast to a specific type
-  ffi::String str_value = value.cast<ffi::String>();
-  // copy int to value
-  value = 1;
-  // copy into a view
-  ffi::AnyView view = value;
-  // cast view back to int
-  std::cout << "Value is " << view.cast<int>() << std::endl;
-}
-```
-
-`ffi::Any` can serve as a container type to hold managed values that can be 
recognized by the TVM FFI system.
-They can be composed with container structures such as `Map<String, Any>`, 
`Array<Any>` to represent various
-broad patterns in APIs that may appear in ML systems.
-
-## Function Calling Convention
-
-As discussed in the overview, we need to consider foreign function calls as 
first-class citizens. We adopt a single standard C function as follows:
-
-```c++
-typedef int (*TVMFFISafeCallType)(
-   void* handle, const TVMFFIAny* args, int32_t num_args, TVMFFIAny* result
-);
-```
-
-The handle contains the pointer to the function object itself, allowing us to 
support closures. args and num_args describe the input arguments and results 
store the return value. When args and results contain heap-managed objects, we 
expect the caller to own args and result.
-
-```{note}
-Before calling the function, caller must set `result->type_index` to be 
kTVMFFINone, or any type index that do not corresponds
-to an on-heap object.
-
-**Rationale:** Simplifies callee implementation as initial state of result can 
be viewed as managed Any.
-```
-
-We call this approach a packed function, as it provides a single signature to 
represent all functions in a "type-erased" way. It saves the need to declare 
and jit shim for each FFI function call while maintaining reasonable 
efficiency. This mechanism enables the following scenarios:
-
-- Calling from Dynamic Languages (e.g., Python): we provide a tvm_ffi binding 
that prepares the args based on dynamically examining Python arguments passed 
in.
-- Calling from Static Languages (e.g., C++): For static languages, we can 
leverage C++ templates to directly instantiate the arguments on the stack, 
saving the need for dynamic examination.
-- Dynamic language Callbacks: the signature enables us to easily bring dynamic 
language (Python) callbacks as ffi::Function, as we can take each argument and 
convert to the dynamic values.
-- Efficiency: In practice, we find this approach is sufficient for machine 
learning focused workloads. For example, we can get to microsecond level 
overhead for Python/C++ calls, which is generally similar to overhead for eager 
mode. When both sides of calls are static languages, the overhead will go down 
to tens of nanoseconds. As a side note, although we did not find it necessary, 
the signature still leaves room for link time optimization (LTO), when both 
sides are static languages wit [...]
-
-We support first-class Function objects that allow us to also pass 
function/closures from different places around, enabling cool usages such as 
quick python callback for prototyping, and dynamic Functor creation for 
driver-based kernel launching.
-
-## Error Handling
-
-Most TVM FFI C API calls, including `TVMFFISafeCallType` uses the return value 
to
-indicate whether an error happens. When an error happens during a function 
call,
-a non-zero value will be returned. The callee needs also to set the error 
through `TVMFFIErrorSetRaisedFromCStr` or `TVMFFIErrorSetRaised` API, which 
stores
-the error on a thread-local storage.
-
-```c++
-// Example function that raises an error
-int ErrorFunc(void* handle, const TVMFFIAny* args, int num_args, TVMFFIAny 
*result) {
-  const char* error_kind = "RuntimeError";
-  const char* error_msg = "error message";
-  // set the thread-local error state
-  TVMFFIErrorSetRaisedFromCStr(error_kind, error_msg);
-  return -1;
-}
-```
-
-The caller can retrieve the error from thread-local error storage
-using `TVMFFIErrorMoveFromRaised` function.
-The ABI stores Error also as a specific Object,
-the overall error object is stored as follows
-
-```c++
-/*!
- * \brief Error cell used in error object following header.
- */
-typedef struct {
-  /*! \brief The kind of the error. */
-  TVMFFIByteArray kind;
-  /*! \brief The message of the error. */
-  TVMFFIByteArray message;
-  /*!
-   * \brief The backtrace of the error.
-   *
-   * The backtrace is in the order of recent call first from the top of the 
stack
-   * to the bottom of the stack. This order makes it helpful for appending
-   * the extra backtrace to the end as we go up when error is propagated.
-   *
-   * When printing out, we encourage reverse the order of lines to make it
-   * align with python style.
-   */
-  TVMFFIByteArray backtrace;
-  /*!
-   * \brief Function handle to update the backtrace of the error.
-   * \param self The self object handle.
-   * \param backtrace The backtrace to update.
-   * \param update_mode The mode to update the backtrace,
-   *        can be either kTVMFFIBacktraceUpdateModeReplace, 
kTVMFFIBacktraceUpdateModeAppend.
-   */
-  void (*update_backtrace)(
-    TVMFFIObjectHandle self, const TVMFFIByteArray* backtrace, int32_t 
update_mode);
-} TVMFFIErrorCell;
-
-// error object
-class ErrorObj : public ffi::Object, public TVMFFIErrorCell {
-};
-```
-
-The error object stores kind, message and backtrace as string. When possible,
-we store the backtrace in the same format of python-style (see an example as 
follows):
-
-```text
-File "src/extension.cc", line 45, in void 
my_ffi_extension::RaiseError(tvm::ffi::String)
-```
-
-We provide C++ object `ffi::Error` that can be throwed as exception in c++ 
environment. When we encounter
-the C ABI boundary, we will catch the error and call `TVMFFIErrorSetRaised` to 
propagate the error
-to the caller safely.
-`TVMFFIErrorSetRaisedFromCStr` is a convenient method to set error directly 
from C string and can be useful in compiler
-backend construction to implement features such as assert.
-We also provide `TVMFFIErrorSetRaisedFromCStrParts` to concat reusable parts 
in the error message.
-
-**Rationales:** The error object contains minimal but sufficient information 
to reconstruct structured
-error in python side. We opt-for thread-local error state as it simplifies 
overall support.
-
-## String and Bytes
-
-The ABI supports strings and bytes as first-class citizens. A string can take 
multiple forms that are identified by
-its `type_index`.
-
-- `kTVMFFIRawStr`: raw C string terminated by `\0`.
-- `kTVMFFISmallStr`: small string, the length is stored in `small_str_len` and 
data is stored in `v_bytes`.
-- `kTVMFFIStr`: on-heap string object for strings that are longer than 7 
characters.
-
-The following code shows the layout of the on-heap string object.
-
-```c++
-// span-like data structure to store header and length
-typedef struct {
-  const char* data;
-  size_t size;
-} TVMFFIByteArray;
-
-// showcase the layout of the on-heap string.
-class StringObj : public ffi::Object, public TVMFFIByteArray {
-};
-```
-
-The following code shows how to read a string from `TVMFFIAny`
-
-```c++
-TVMFFIByteArray ReadString(const TVMFFIAny *value) {
-  TVMFFIByteArray ret;
-  if (value->type_index == kTVMFFIRawStr) {
-    ret.data = value->v_c_str;
-    ret.size = strlen(ret.data);
-  } else if (value->type_index == kTVMFFISmallStr) {
-    ret.data = value->v_bytes;
-    ret.size = value->small_str_len;
-  } else {
-    assert(value->type_index == kTVMFFIStr);
-    ret = *reinterpret_cast<TVMFFIByteArray*>(
-      reinterpret_cast<char*>(value->v_obj) + sizeof(TVMFFIObject));
-  }
-  return ret;
-}
-```
-
-Similarly, we have type indices to represent bytes. The C++ API provides 
classes
-`ffi::String` and `ffi::Bytes` to enable the automatic conversion of these 
values with Any storage format.
-
-**Rationales:** Separate string and bytes enable clear mappings from the 
Python side. Small string allows us to
-store short names on-stack. To favor 8-byte alignment (v_bytes) and keep 
things simple, we did not further
-pack characters into the `small_len` field.
diff --git a/docs/concepts/abi_overview.rst b/docs/concepts/abi_overview.rst
new file mode 100644
index 0000000..89848cc
--- /dev/null
+++ b/docs/concepts/abi_overview.rst
@@ -0,0 +1,551 @@
+..  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.
+
+ABI Overview
+============
+
+.. hint::
+
+    Authoritative ABI specifications are defined in
+
+    - C header `tvm/ffi/c_api.h 
<https://github.com/apache/tvm-ffi/blob/main/include/tvm/ffi/c_api.h>`_, which 
contains the core ABI, and
+    - C header `tvm/ffi/extra/c_env_api.h 
<https://github.com/apache/tvm-ffi/blob/main/include/tvm/ffi/extra/c_env_api.h>`_,
 which contains extra support features.
+
+The TVM-FFI ABI is designed around the following key principles:
+
+- **Minimal and efficient.** Keep things simple and deliver close-to-metal 
performance.
+- **Stability guarantee.** The ABI remains stable across compiler versions and 
is independent of host languages or frameworks.
+- **Expressive for machine learning.** Native support for tensors, shapes, and 
data types commonly used in ML workloads.
+- **Extensible.** The ABI supports user-defined types and features through a 
dynamic type registration system.
+
+This tutorial covers common concepts and usage patterns of the TVM-FFI ABI, 
with low-level C code examples for precise reference.
+
+.. important::
+  C code is used for clarity, precision and friendliness to compiler builders.
+  And C code can be readily translated into code generators such as LLVM IR 
builder.
+
+Any and AnyView
+---------------
+
+.. seealso::
+
+   :doc:`any` for :cpp:class:`~tvm::ffi::Any` and 
:cpp:class:`~tvm::ffi::AnyView` usage patterns.
+
+At the core of TVM-FFI is :cpp:class:`TVMFFIAny`, a 16-byte tagged union that 
can hold any value
+recognized by the FFI system. It enables type-erased value passing across 
language boundaries.
+
+.. dropdown:: C ABI Reference: :cpp:class:`TVMFFIAny`
+   :icon: code
+
+   .. literalinclude:: ../../include/tvm/ffi/c_api.h
+      :language: c
+      :start-after: [TVMFFIAny.begin]
+      :end-before: [TVMFFIAny.end]
+      :caption: tvm/ffi/c_api.h
+
+**Ownership.** :cpp:class:`TVMFFIAny` struct can represent either an owning or 
a borrowing reference.
+These two ownership patterns are formalized by the C++ wrapper classes 
:cpp:class:`~tvm::ffi::Any` and :cpp:class:`~tvm::ffi::AnyView`,
+which have identical memory layouts but different :ref:`ownership semantics 
<any-ownership>`:
+
+- **Owning:** :cpp:class:`tvm::ffi::Any` - reference-counted, manages object 
lifetime
+- **Borrowing:** :cpp:class:`tvm::ffi::AnyView` - non-owning view, caller must 
ensure validity
+
+.. note::
+   To convert a borrowing :cpp:class:`~tvm::ffi::AnyView` to an owning 
:cpp:class:`~tvm::ffi::Any`, use :cpp:func:`TVMFFIAnyViewToOwnedAny`.
+
+**Runtime Type Index.** The ``type_index`` field identifies what kind of value 
is stored:
+
+- :ref:`Atomic POD types <any-atomic-types>` (``type_index`` < 
:cpp:enumerator:`kTVMFFIStaticObjectBegin 
<TVMFFITypeIndex::kTVMFFIStaticObjectBegin>`):
+  Stored inline in the payload union without heap allocation or reference 
counting.
+- :ref:`Object types <any-heap-allocated-objects>` (``type_index`` >= 
:cpp:enumerator:`kTVMFFIStaticObjectBegin 
<TVMFFITypeIndex::kTVMFFIStaticObjectBegin>`):
+  Stored as pointers to heap-allocated, reference-counted TVM-FFI objects.
+
+.. important::
+   The TVM-FFI type index system does not rely on C++ RTTI.
+
+
+Construct Any
+~~~~~~~~~~~~~
+
+**From atomic POD types.** The following C code constructs a 
:cpp:class:`TVMFFIAny` from an integer:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Any_AnyView.FromInt_Float.begin]
+  :end-before: [Any_AnyView.FromInt_Float.end]
+
+Set the ``type_index`` from :cpp:enum:`TVMFFITypeIndex` and assign the 
corresponding payload field.
+
+.. important::
+
+   Always zero the ``zero_padding`` field and any unused bytes in the value 
union.
+   This invariant enables direct byte comparison and hashing of 
:cpp:class:`TVMFFIAny` values.
+
+**From object types.** The following C code constructs a 
:cpp:class:`TVMFFIAny` from a heap-allocated object:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Any_AnyView.FromObjectPtr.begin]
+  :end-before: [Any_AnyView.FromObjectPtr.end]
+
+When ``IS_OWNING_ANY`` is ``true`` (owning :cpp:class:`~tvm::ffi::Any`), this 
increments the object's reference count.
+
+.. _abi-destruct-any:
+
+Destruct Any
+~~~~~~~~~~~~
+
+The following C code destroys a :cpp:class:`TVMFFIAny`:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Any_AnyView.Destroy.begin]
+  :end-before: [Any_AnyView.Destroy.end]
+
+When ``IS_OWNING_ANY`` is ``true`` (owning :cpp:class:`~tvm::ffi::Any`), this 
decrements the object's reference count.
+
+Extract from Any
+~~~~~~~~~~~~~~~~
+
+**Extract an atomic POD.** The following C code extracts an integer or float 
from a :cpp:class:`TVMFFIAny`:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Any_AnyView.GetInt_Float.begin]
+  :end-before: [Any_AnyView.GetInt_Float.end]
+
+Implicit type conversion may occur. For example, when extracting a float from 
a :cpp:class:`TVMFFIAny`
+that holds an integer, the integer is cast to a float.
+
+**Extract a DLTensor.** A :c:struct:`DLTensor` may originate from either a raw 
pointer or a heap-allocated :cpp:class:`~tvm::ffi::TensorObj`:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Any_AnyView.GetDLTensor.begin]
+  :end-before: [Any_AnyView.GetDLTensor.end]
+
+**Extract a TVM-FFI object.** TVM-FFI objects are always heap-allocated and 
reference-counted,
+with ``type_index`` >= :cpp:enumerator:`kTVMFFIStaticObjectBegin 
<TVMFFITypeIndex::kTVMFFIStaticObjectBegin>`:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Any_AnyView.GetObject.begin]
+  :end-before: [Any_AnyView.GetObject.end]
+
+To take ownership of the returned value, increment the reference count via 
:cpp:func:`TVMFFIObjectIncRef`.
+Release ownership later via :cpp:func:`TVMFFIObjectDecRef`.
+
+.. _abi-object:
+
+Object
+------
+
+.. seealso::
+
+   :doc:`object_and_class` for the object system and reflection.
+
+TVM-FFI Object (:cpp:class:`TVMFFIObject`) is the cornerstone of TVM-FFI's 
stable yet extensible type system.
+
+.. dropdown:: C ABI Reference: :cpp:class:`TVMFFIObject`
+   :icon: code
+
+   .. literalinclude:: ../../include/tvm/ffi/c_api.h
+      :language: c
+      :start-after: [TVMFFIObject.begin]
+      :end-before: [TVMFFIObject.end]
+      :caption: tvm/ffi/c_api.h
+
+All TVM-FFI objects share these characteristics:
+
+- Heap-allocated and reference-counted
+- Layout-stable 24-byte header containing reference counts, type index, and 
deleter callback
+- Type index >= :cpp:enumerator:`kTVMFFIStaticObjectBegin 
<TVMFFITypeIndex::kTVMFFIStaticObjectBegin>`
+
+**Dynamic Type System.** Classes can be registered at runtime via 
:cpp:func:`TVMFFITypeGetOrAllocIndex`,
+with support for single inheritance. See :ref:`type-checking-and-casting` for 
usage details.
+
+A small **static section** between :cpp:enumerator:`kTVMFFIStaticObjectBegin 
<TVMFFITypeIndex::kTVMFFIStaticObjectBegin>`
+and :cpp:enumerator:`kTVMFFIDynObjectBegin 
<TVMFFITypeIndex::kTVMFFIDynObjectBegin>`
+is reserved for static object types, for example,
+
+- Strings (:cpp:enumerator:`kTVMFFIStr <TVMFFITypeIndex::kTVMFFIStr>`) and 
Bytes (:cpp:enumerator:`kTVMFFIBytes <TVMFFITypeIndex::kTVMFFIBytes>`): Section 
:ref:`abi-string-and-byte`
+- Errors (:cpp:enumerator:`kTVMFFIError <TVMFFITypeIndex::kTVMFFIError>`): 
Section :ref:`abi-exception`.
+- Functions (:cpp:enumerator:`kTVMFFIFunction 
<TVMFFITypeIndex::kTVMFFIFunction>`): Section :ref:`abi-function`.
+- Tensors (:cpp:enumerator:`kTVMFFITensor <TVMFFITypeIndex::kTVMFFITensor>`): 
Section :ref:`abi-tensor`.
+- Miscellaneous:
+  Modules (:cpp:enumerator:`kTVMFFIModule <TVMFFITypeIndex::kTVMFFIModule>`),
+  Arrays (:cpp:enumerator:`kTVMFFIArray <TVMFFITypeIndex::kTVMFFIArray>`),
+  Maps (:cpp:enumerator:`kTVMFFIMap <TVMFFITypeIndex::kTVMFFIMap>`),
+  Shapes (:cpp:enumerator:`kTVMFFIShape <TVMFFITypeIndex::kTVMFFIShape>`),
+  Opaque Python objects (:cpp:enumerator:`kTVMFFIOpaquePyObject 
<TVMFFITypeIndex::kTVMFFIOpaquePyObject>`).
+
+.. _abi-object-ownership:
+
+Ownership Management
+~~~~~~~~~~~~~~~~~~~~
+
+Ownership is managed via reference counting, which includes both strong and 
weak references.
+Two C APIs manage strong reference counting:
+
+- :cpp:func:`TVMFFIObjectIncRef`: Acquire strong ownership by incrementing the 
reference count
+- :cpp:func:`TVMFFIObjectDecRef`: Release strong ownership by decrementing the 
reference count
+
+The ``deleter`` callback (:cpp:member:`TVMFFIObject::deleter`) executes when 
the strong or weak count reaches zero with different flags.
+See :ref:`object-reference-counting` for details.
+
+**Move ownership from Any/AnyView.** The following C code transfers ownership 
from an owning :cpp:class:`~tvm::ffi::Any` to an object pointer:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Object.MoveFromAny.begin]
+  :end-before: [Object.MoveFromAny.end]
+
+Since :cpp:class:`~tvm::ffi::AnyView` is non-owning (``IS_OWNING_ANY`` is 
``false``),
+acquiring ownership requires explicitly incrementing the reference count.
+
+**Release ownership.** The following C code releases ownership of a TVM-FFI 
object:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :name: ABI.Object.Destroy
+  :start-after: [Object.Destroy.begin]
+  :end-before: [Object.Destroy.end]
+
+Inheritance Checking
+~~~~~~~~~~~~~~~~~~~~
+
+TVM-FFI models single inheritance as a tree where each node points to its 
parent.
+Each type has a unique type index, and the system tracks ancestors, 
inheritance depth, and other metadata.
+This information is available via :cpp:func:`TVMFFIGetTypeInfo`.
+
+The following C code checks whether a type is a subclass of another:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Object.IsInstance.begin]
+  :end-before: [Object.IsInstance.end]
+
+.. _abi-tensor:
+
+Tensor
+------
+
+.. seealso::
+
+   :doc:`tensor` for details about TVM-FFI tensors and DLPack interoperability.
+
+TVM-FFI provides :cpp:class:`tvm::ffi::TensorObj`, a DLPack-native tensor 
class that is also a standard TVM-FFI object.
+This means tensors can be managed using the same reference counting mechanisms 
as other objects.
+
+.. dropdown:: C ABI Reference: :cpp:class:`tvm::ffi::TensorObj`
+   :icon: code
+
+   .. code-block:: cpp
+    :caption: tvm/ffi/container/tensor.h
+
+     class TensorObj : public Object, public DLTensor {
+      // no other members besides those from Object and DLTensor
+     };
+
+
+Access Tensor Metadata
+~~~~~~~~~~~~~~~~~~~~~~
+
+The following C code obtains a :c:struct:`DLTensor` pointer from a 
:cpp:class:`~tvm::ffi::TensorObj`:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Tensor.AccessDLTensor.begin]
+  :end-before: [Tensor.AccessDLTensor.end]
+
+The :c:struct:`DLTensor` pointer provides access to shape, dtype, device, data 
pointer, and other tensor metadata.
+
+Construct Tensor
+~~~~~~~~~~~~~~~~
+
+**Zero-copy conversion.** The following C code constructs a 
:cpp:class:`~tvm::ffi::TensorObj` from a :c:struct:`DLManagedTensorVersioned`,
+which shares the underlying data buffer without allocating new memory.
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Tensor_FromDLPack.begin]
+  :end-before: [Tensor_FromDLPack.end]
+
+.. hint::
+   TVM-FFI's Python API automatically wraps framework tensors (e.g., 
:py:class:`torch.Tensor`) as :cpp:class:`~tvm::ffi::TensorObj`,
+   so manual conversion is typically unnecessary.
+
+**Allocate new memory.** Alternatively, if memory allocation is intended, the 
following C code constructs a :cpp:class:`~tvm::ffi::TensorObj` from a 
:c:struct:`DLTensor` pointer:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Tensor_Alloc.begin]
+  :end-before: [Tensor_Alloc.end]
+
+The ``prototype`` contains the shape, dtype, device, and other tensor metadata 
that will be used to allocate the new tensor.
+And the allocator, by default, is the framework's (e.g., PyTorch) allocator, 
which is automatically set when importing the framework.
+
+To override or explicitly look up the allocator, use 
:cpp:func:`TVMFFIEnvSetDLPackManagedTensorAllocator` and 
:cpp:func:`TVMFFIEnvGetDLPackManagedTensorAllocator`.
+
+.. warning::
+   In kernel library usecases, it is usually not recommended to dynamically 
allocate tensors inside a kernel, and instead always pre-allocate outputs,
+   and pass them as :cpp:class:`~tvm::ffi::TensorView` parameters. This 
approach
+
+   - avoids memory fragmentation and performance pitfalls,
+   - prevents CUDA graph incompatibilities on GPU, and
+   - allows the outer framework to control allocation policy (pools, device 
strategies, etc.).
+
+Destruct Tensor
+~~~~~~~~~~~~~~~
+
+As a standard TVM-FFI object, :cpp:class:`~tvm::ffi::TensorObj` follows the 
:ref:`standard destruction pattern <ABI.Object.Destroy>`.
+When the reference count reaches zero, the deleter callback 
(:cpp:member:`TVMFFIObject::deleter`) executes.
+
+Export Tensor to DLPack
+~~~~~~~~~~~~~~~~~~~~~~~
+
+To share a :cpp:class:`~tvm::ffi::TensorObj` with other frameworks, export it 
as a :c:struct:`DLManagedTensorVersioned`:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Tensor_ToDLPackVersioned.begin]
+  :end-before: [Tensor_ToDLPackVersioned.end]
+
+Note that the caller takes ownership of the returned 
:c:struct:`DLManagedTensorVersioned* <DLManagedTensorVersioned>`
+and must call its ``deleter`` to release the tensor.
+
+.. _abi-function:
+
+Function
+--------
+
+.. seealso::
+
+   :ref:`sec:function` for a detailed description of TVM-FFI functions.
+
+All functions in TVM-FFI follow a unified C calling convention that enables 
ABI-stable,
+type-erased, and cross-language function calls, defined by 
:cpp:type:`TVMFFISafeCallType`.
+
+**Calling convention.** The signature includes:
+
+- ``handle`` (``void*``): Optional resource handle passed to the callee; 
typically ``NULL`` for exported symbols
+- ``args`` (``TVMFFIAny*``) and ``num_args`` (``int``): Array of non-owning 
:cpp:class:`~tvm::ffi::AnyView` input arguments
+- ``result`` (``TVMFFIAny*``): Owning :cpp:class:`~tvm::ffi::Any` output value
+- Return value: ``0`` for success; ``-1`` or ``-2`` for errors (see 
:ref:`sec:exception`)
+
+See :ref:`sec:function-calling-convention` for more details.
+
+.. important::
+   The caller must zero-initialize the output argument ``result`` before the 
call.
+
+**Memory layout.** The :cpp:class:`~tvm::ffi::FunctionObj` stores call 
pointers after the object header.
+
+.. dropdown:: C ABI Reference: :cpp:class:`TVMFFIFunctionCell`
+   :icon: code
+
+   .. literalinclude:: ../../include/tvm/ffi/c_api.h
+      :language: c
+      :start-after: [TVMFFIFunctionCell.begin]
+      :end-before: [TVMFFIFunctionCell.end]
+      :caption: tvm/ffi/c_api.h
+
+Construct and Destroy
+~~~~~~~~~~~~~~~~~~~~~
+
+.. important::
+   Dynamic function creation is useful for passing lambdas or closures across 
language boundaries.
+
+The following C code constructs a :cpp:class:`~tvm::ffi::FunctionObj` from a 
:cpp:type:`TVMFFISafeCallType` and a ``deleter`` callback.
+The ``deleter`` cleans up resources owned by the function; for global symbols, 
it is typically ``NULL``.
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Function.Construct.begin]
+  :end-before: [Function.Construct.end]
+
+Release a :cpp:class:`~tvm::ffi::FunctionObj` using the :ref:`standard 
destruction pattern <ABI.Object.Destroy>`.
+
+Global Registry
+~~~~~~~~~~~~~~~
+
+**Retrieve a global function.** The following C code uses 
:cpp:func:`TVMFFIFunctionGetGlobal` to retrieve a function by name from the 
global registry:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Function.GetGlobal.begin]
+  :end-before: [Function.GetGlobal.end]
+
+.. note::
+   :cpp:func:`TVMFFIFunctionGetGlobal` returns an owning handle.
+   The caller must release it by calling :cpp:func:`TVMFFIObjectDecRef` when 
it's no longer needed.
+
+**Register a global function.** The following C code uses 
:cpp:func:`TVMFFIFunctionSetGlobal` to register a function by name in the 
global registry:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Function.SetGlobal.begin]
+  :end-before: [Function.SetGlobal.end]
+
+Call Function
+~~~~~~~~~~~~~
+
+The following C code invokes a :cpp:class:`~tvm::ffi::FunctionObj` with 
arguments:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Function.Call.begin]
+  :end-before: [Function.Call.end]
+
+
+.. _abi-exception:
+
+Exception
+---------
+
+.. seealso::
+
+   :ref:`sec:exception` for detailed exception handling patterns.
+
+Exceptions are a central part of TVM-FFI's ABI and calling convention.
+When errors occur, they are stored as objects with a 
:cpp:class:`TVMFFIErrorCell` payload.
+
+.. dropdown:: C ABI Reference: :cpp:class:`TVMFFIErrorCell`
+   :icon: code
+
+   .. literalinclude:: ../../include/tvm/ffi/c_api.h
+      :language: c
+      :start-after: [TVMFFIErrorCell.begin]
+      :end-before: [TVMFFIErrorCell.end]
+      :caption: tvm/ffi/c_api.h
+
+.. important::
+  Errors from all languages (e.g. Python, C++) will be properly translated 
into the TVM-FFI error object.
+
+
+Retrieve Error Object
+~~~~~~~~~~~~~~~~~~~~~
+
+When a function returns ``-1``, an error object is stored in thread-local 
storage (TLS).
+Retrieve it with :cpp:func:`TVMFFIErrorMoveFromRaised`, which returns a 
:cpp:class:`tvm::ffi::ErrorObj`:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Error.HandleReturnCode.begin]
+  :end-before: [Error.HandleReturnCode.end]
+
+This function transfers ownership to the caller and clears the TLS slot.
+Call :cpp:func:`TVMFFIObjectDecRef` when done to avoid memory leaks.
+
+**Frontend errors (-2).** Error code ``-2`` is reserved for frontend errors.
+It is returned when :cpp:func:`TVMFFIEnvCheckSignals` detects a pending Python 
signal.
+In this case, do not retrieve the error from TLS; instead, consult the 
frontend's error mechanism.
+
+.. admonition:: Print Error Message
+  :class: hint
+
+  The error payload is a :cpp:type:`TVMFFIErrorCell` structure containing the 
error kind, message, and backtrace.
+  Access it by skipping the :cpp:type:`TVMFFIObject` header via pointer 
arithmetic.
+
+  .. literalinclude:: ../../examples/abi_overview/example_code.c
+    :language: c
+    :start-after: [Error.Print.begin]
+    :end-before: [Error.Print.end]
+
+  This prints the error message along with its backtrace.
+
+Raise Exception
+~~~~~~~~~~~~~~~
+
+The following C code sets the TLS error and returns ``-1`` via 
:cpp:func:`TVMFFIErrorSetRaisedFromCStr`:
+
+.. literalinclude:: ../../examples/abi_overview/example_code.c
+  :language: c
+  :start-after: [Error.RaiseException.begin]
+  :end-before: [Error.RaiseException.end]
+
+For non-null-terminated strings, use 
:cpp:func:`TVMFFIErrorSetRaisedFromCStrParts`, which accepts explicit string 
lengths.
+
+.. note::
+   You rarely need to create a :cpp:class:`~tvm::ffi::ErrorObj` directly.
+   The C APIs :cpp:func:`TVMFFIErrorSetRaisedFromCStr` and 
:cpp:func:`TVMFFIErrorSetRaisedFromCStrParts` handle this internally.
+
+.. _abi-string-and-byte:
+
+🚧 String and Bytes
+-------------------
+
+.. warning::
+   This section is under construction.
+
+The ABI supports strings and bytes as first-class citizens. A string can take 
multiple forms that are identified by
+its ``type_index``.
+
+- ``kTVMFFIRawStr``: raw C string terminated by ``\0``.
+- ``kTVMFFISmallStr``: small string, the length is stored in ``small_str_len`` 
and data is stored in ``v_bytes``.
+- ``kTVMFFIStr``: on-heap string object for strings that are longer than 7 
characters.
+
+The following code shows the layout of the on-heap string object.
+
+.. code-block:: cpp
+
+  // span-like data structure to store header and length
+  typedef struct {
+    const char* data;
+    size_t size;
+  } TVMFFIByteArray;
+
+  // showcase the layout of the on-heap string.
+  class StringObj : public ffi::Object, public TVMFFIByteArray {
+  };
+
+
+The following code shows how to read a string from :cpp:class:`TVMFFIAny`
+
+.. code-block:: cpp
+
+  TVMFFIByteArray ReadString(const TVMFFIAny *value) {
+    TVMFFIByteArray ret;
+    if (value->type_index == kTVMFFIRawStr) {
+      ret.data = value->v_c_str;
+      ret.size = strlen(ret.data);
+    } else if (value->type_index == kTVMFFISmallStr) {
+      ret.data = value->v_bytes;
+      ret.size = value->small_str_len;
+    } else {
+      assert(value->type_index == kTVMFFIStr);
+      ret = *reinterpret_cast<TVMFFIByteArray*>(
+        reinterpret_cast<char*>(value->v_obj) + sizeof(TVMFFIObject));
+    }
+    return ret;
+  }
+
+
+Similarly, we have type indices to represent bytes. The C++ API provides 
classes
+:cpp:class:`~tvm::ffi::String` and :cpp:class:`~tvm::ffi::Bytes` to enable the 
automatic conversion of these values with Any storage format.
+
+**Rationales**. Separate string and bytes enable clear mappings from the 
Python side. Small string allows us to
+store short names on-stack. To favor 8-byte alignment (v_bytes) and keep 
things simple, we did not further
+pack characters into the ``small_len`` field.
+
+Further Reading
+---------------
+
+- :doc:`any`: High-level C++ usage of :cpp:class:`~tvm::ffi::Any` and 
:cpp:class:`~tvm::ffi::AnyView`
+- :doc:`object_and_class`: The object system and reflection
+- :doc:`tensor`: Tensor classes and DLPack interoperability
+- :doc:`func_module`: Functions, exceptions, and modules
+- :doc:`../get_started/stable_c_abi`: Quick introduction to the stable C ABI
diff --git a/docs/concepts/any.rst b/docs/concepts/any.rst
index efc6652..7f234ce 100644
--- a/docs/concepts/any.rst
+++ b/docs/concepts/any.rst
@@ -27,9 +27,7 @@ values of a wide variety of types, including primitives, 
objects, and strings.
 Unlike ``std::any``, it is designed for zero-copy inter-language exchange 
without RTTI,
 featuring a fixed 16-byte layout with built-in reference counting and 
ownership semantics.
 
-This tutorial covers everything you need to know about 
:cpp:class:`~tvm::ffi::Any` and :cpp:class:`~tvm::ffi::AnyView`:
-common usage patterns, ownership semantics, and memory layout.
-
+This tutorial covers common usage patterns, ownership semantics, and memory 
layout.
 
 Common Usage
 ------------
@@ -169,6 +167,8 @@ Compare with ``nullptr`` to check for ``None``:
    }
 
 
+.. _any-ownership:
+
 Ownership
 ---------
 
@@ -195,8 +195,8 @@ The core distinction between :cpp:class:`tvm::ffi::Any` and
      - Function inputs
      - Return values, storage
 
-Code Examples
-~~~~~~~~~~~~~~
+Examples
+~~~~~~~~
 
 :cpp:class:`~tvm::ffi::AnyView` is a lightweight, non-owning view. Copying it 
simply
 copies 16 bytes with no reference count updates, making it ideal for passing 
arguments without overhead:
@@ -245,25 +245,9 @@ Destruction Semantics in C
 
 In C, which lacks RAII, you must manually destroy :cpp:class:`~tvm::ffi::Any` 
objects
 by calling :cpp:func:`TVMFFIObjectDecRef` for heap-allocated objects.
+Destroying an :cpp:class:`~tvm::ffi::AnyView` is effectively a no-op - just 
clear its contents.
 
-.. code-block:: cpp
-
-   void destroy_any(TVMFFIAny* any) {
-     if (any->type_index >= kTVMFFIStaticObjectBegin) {
-       // Decrement the reference count of the heap-allocated object
-       TVMFFIObjectDecRef(any->v_obj);
-     }
-     *any = (TVMFFIAny){0};
-   }
-
-In contrast, destroying an :cpp:class:`~tvm::ffi::AnyView` is effectively a 
no-op - just clear its contents.
-
-.. code-block:: cpp
-
-   void destroy_any_view(TVMFFIAny* any_view) {
-     *any_view = (TVMFFIAny){0};
-   }
-
+See :ref:`abi-destruct-any` for C code examples.
 
 Layout
 ------
@@ -310,6 +294,8 @@ It is effectively a layout-stable 16-byte tagged union.
 * The first 4 bytes (:cpp:member:`TVMFFIAny::type_index`) serve as a tag 
identifying the stored type.
 * The last 8 bytes hold the actual value - either stored inline for atomic 
types (e.g., ``int64_t``, ``float64``, ``void*``) or as a pointer to a 
heap-allocated object.
 
+.. _any-atomic-types:
+
 Atomic Types
 ~~~~~~~~~~~~
 
@@ -371,6 +357,8 @@ Note that raw pointers like :c:struct:`DLTensor* 
<DLTensor>` and ``char*`` also
 These pointers carry no ownership, so the caller must ensure the pointed-to 
data outlives
 the :cpp:class:`~tvm::ffi::AnyView` or :cpp:class:`~tvm::ffi::Any`.
 
+.. _any-heap-allocated-objects:
+
 Heap-Allocated Objects
 ~~~~~~~~~~~~~~~~~~~~~~
 
@@ -431,7 +419,7 @@ inline using **small string optimization**, avoiding heap 
allocation entirely:
 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`
+- :doc:`object_and_class`: How TVM-FFI objects work, including reference 
counting and type checking
+- :doc:`func_module`: Function calling conventions and the global registry
+- :doc:`tensor`: How tensors flow through :cpp:class:`~tvm::ffi::Any` and 
:cpp:class:`~tvm::ffi::AnyView`
+- :doc:`abi_overview`: Low-level C ABI details for working with 
:cpp:class:`TVMFFIAny` directly
diff --git a/docs/concepts/func_module.rst b/docs/concepts/func_module.rst
index 1024b91..d354023 100644
--- a/docs/concepts/func_module.rst
+++ b/docs/concepts/func_module.rst
@@ -15,20 +15,20 @@
     specific language governing permissions and limitations
     under the License.
 
-Function, Exception and Module
-==============================
+Function 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.
+This tutorial covers defining, registering, and calling TVM-FFI functions,
+exception handling, and working with modules.
 
 Glossary
 --------
 
-TVM-FFI ABI. :cpp:type:`TVMFFISafeCallType`
+TVM-FFI ABI, or "Packed Function". :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.
@@ -190,17 +190,15 @@ to a :py:class:`tvm_ffi.Function` at the ABI boundary. 
The example below demonst
    print(func_add(1, 2))
 
 
-Exception ABI
--------------
+.. _sec:function:
 
-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:
+Function
+--------
 
-- How to properly throw exceptions from a TVM-FFI ABI function
-- How to check for and propagate exceptions from a TVM-FFI ABI function
+.. _sec:function-calling-convention:
 
-TVM-FFI C ABI
-~~~~~~~~~~~~~
+Calling Convention
+~~~~~~~~~~~~~~~~~~
 
 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:
@@ -220,147 +218,48 @@ 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.
 
+.. important::
+   The caller must zero-initialize the output argument ``result`` 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
+- **Error code 0**: Success
+- **Error code -1**: Error occurred, retrievable with 
:cpp:func:`TVMFFIErrorMoveFromRaised`
+- **Error code -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`.
+This design is called a **packed function**, because it "packs" all arguments 
into a single array of type-erased :cpp:type:`tvm::ffi::AnyView`,
+and further unifies calling convention across all languages without resorting 
to JIT compilation.
 
-.. 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.
-     }
-   }
+More specifically, this mechanism enables the following scenarios:
 
-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.
+- **Dynamic languages**. Well-optimized bindings are provided for, e.g. 
Python, to translate arguments into packed function format, and translate 
return value back to the host language.
+- **Static languages**. Metaprogramming techniques, such as C++ templates, are 
usually available to directly instantiate packed format on stack, saving the 
need for dynamic examination.
+- **Cross-language callbacks**. Language-agnostic 
:cpp:class:`tvm::ffi::Function` makes it easy to call between languages without 
depending on language-specific features such as GIL.
 
-**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.
+**Performance Implications**. This approach is in practice highly efficient in 
machine learning workloads.
 
-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.
+- In Python/C++ calls, we can get to microsecond level overhead, which is 
generally similar to overhead for eager mode;
+- When both sides of calls are static languages, the overhead will go down to 
tens of nanoseconds.
 
-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
+.. note::
+  Although we found it less necessary in practice, further link time 
optimization (LTO) is still theoretically possible
+  in scenarios where both sides are static languages with a known symbol and 
linked into a single binary.
+  In this case, the callee can be inlined into caller side and the stack 
argument memory can be passed into register passing.
 
-    RAII:
-     // clean up owned resources
-     return err_code;
-   }
-
-Function
---------
+.. _sec:function-layout:
 
 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``: Used for cross-ABI function calls; intercepts exceptions and 
stores them in TLS.
+- ``cpp_call``: Used within the same DSO; exceptions are thrown directly for 
better performance.
 
-``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.
+See :ref:`abi-function` for the C struct definition.
 
 .. important::
 
@@ -407,40 +306,35 @@ calling convention.
   in :c:macro:`TVM_FFI_SAFE_CALL_BEGIN` / :c:macro:`TVM_FFI_SAFE_CALL_END` 
macros.
 
 
-C Registry APIs
-~~~~~~~~~~~~~~~
+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`.
+See :ref:`abi-function` for C code examples.
 
-.. list-table::
-   :header-rows: 1
-   :widths: 40 60
+.. _sec:exception:
 
-   * - 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.
+Exception
+~~~~~~~~~
 
-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:
+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:
 
-.. code-block:: cpp
+- How to properly throw exceptions from a TVM-FFI ABI function
+- How to check for and propagate exceptions from a TVM-FFI ABI function
 
-   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;
-   }
+When a TVM-FFI function returns a non-zero code, an error occurred.
+An :cpp:class:`~tvm::ffi::ErrorObj` is stored in thread-local storage (TLS) 
and can be retrieved
+with :cpp:func:`TVMFFIErrorMoveFromRaised`.
+
+- **Error code -1:** Retrieve the error from TLS, print it, and release via 
:cpp:func:`TVMFFIObjectDecRef`.
+- **Error code -2:** A rare frontend error; consult the frontend's error 
mechanism instead of TLS.
+
+To raise an error, use :cpp:func:`TVMFFIErrorSetRaisedFromCStr` to set the TLS 
error and return ``-1``.
+For chains of calls, simply propagate return codes - TLS carries the error 
details.
+
+See :ref:`abi-exception` for C code examples.
+
+
+.. _sec:module:
 
 Modules
 -------
@@ -560,5 +454,5 @@ 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:`abi_overview`: Low-level C ABI details for functions and exceptions
 - :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 7b3e5b0..91ce14b 100644
--- a/docs/concepts/object_and_class.rst
+++ b/docs/concepts/object_and_class.rst
@@ -27,9 +27,7 @@ and :cpp:class:`tvm::ffi::ObjectRef`, which together form the 
foundation for:
 - **Reflection-based class exposure** across programming languages
 - **Serialization and deserialization** via reflection metadata
 
-This tutorial covers everything you need to know about defining, using, and 
extending
-TVM-FFI objects across languages.
-
+This tutorial covers defining, using, and extending TVM-FFI objects across 
languages.
 
 Glossary
 --------
@@ -253,106 +251,48 @@ and :cpp:func:`tvm::ffi::ObjectRef::get` to convert a 
managed reference to a raw
 ABI and Layout
 --------------
 
-Stable C Layout
-~~~~~~~~~~~~~~~
-
-All subclasses of :cpp:class:`tvm::ffi::Object` share a common 24-byte header 
(:cpp:class:`TVMFFIObject`):
-
-.. code-block:: cpp
-
-   typedef struct {
-     uint64_t combined_ref_count;  // Bytes 0-7: strong + weak ref counts
-     int32_t type_index;           // Bytes 8-11: runtime type identifier
-     uint32_t __padding;           // Bytes 12-15: alignment padding
-     void (*deleter)(void*, int);  // Bytes 16-23: destructor callback
-   } TVMFFIObject;
-
-
-It is designed with the following components:
-
-- Reference counting and deleter callback, which are used to manage the 
lifetime of the object;
-- Type index, which is used to interact with type registration system for type 
checking and casting.
-
-:cpp:class:`tvm::ffi::ObjectRef` and :cpp:class:`tvm::ffi::ObjectPtr` are 
smart pointers whose
-layout is equivalent to:
+**Stable C Layout**. All subclasses of :cpp:class:`tvm::ffi::Object` share a 
common 24-byte header (:cpp:class:`TVMFFIObject`)
+containing reference counts, type index, and a deleter callback.
+See :ref:`abi-object` for the C struct definition. 
:cpp:class:`tvm::ffi::ObjectRef` and :cpp:class:`tvm::ffi::ObjectPtr` are smart 
pointers
+equivalent to a single ``void*`` pointer.
 
-.. code-block:: cpp
-
-   struct { void* data; };
 
+.. _object-reference-counting:
 
 Reference Counting
 ~~~~~~~~~~~~~~~~~~
 
-**Deleter action**. When an object is managed by 
:cpp:class:`~tvm::ffi::ObjectRef`, the ``deleter`` callback is invoked:
+.. seealso::
 
-- When strong reference count reaches zero: the object's destructor is called.
-- When weak reference count reaches zero: the memory is freed.
-
-The flags in :cpp:enum:`TVMFFIObjectDeleterFlagBitMask` indicate which action 
to perform.
+   :ref:`abi-object-ownership` for C code examples.
 
 **Intrusive reference counting**. The reference count is stored directly in 
the object header, not in a separate control block.
-This design reduces memory overhead and improves cache locality. Specifically, 
the :cpp:member:`TVMFFIObject::combined_ref_count`
-field stores a 64-bit integer that packs both strong and weak reference counts:
+This design reduces memory overhead and improves cache locality. The 
:cpp:member:`TVMFFIObject::combined_ref_count`
+field packs both strong (lower 32 bits) and weak (upper 32 bits) reference 
counts in a single 64-bit integer.
 
-.. code-block:: cpp
+C APIs are provided to manipulate the reference count:
 
-   // Strong ref count: lower 32 bits
-   uint32_t strong_ref_count = combined_ref_count & 0xFFFFFFFF;
-   // Weak ref count: upper 32 bits
-   uint32_t weak_ref_count = (combined_ref_count >> 32) & 0xFFFFFFFF;
+- :cpp:func:`TVMFFIObjectIncRef` to increase the strong reference count
+- :cpp:func:`TVMFFIObjectDecRef` to decrease the strong reference count
 
-C APIs are provided to manipulate the reference count of an object:
+**Deleter**. When an object is managed by :cpp:class:`~tvm::ffi::ObjectRef`, 
the ``deleter`` callback is invoked:
 
-- :cpp:func:`TVMFFIObjectIncRef` to increase the strong reference count;
-- :cpp:func:`TVMFFIObjectDecRef` to decrease the strong reference count.
+- When strong reference count reaches zero: the object's destructor is called.
+- When weak reference count reaches zero: the memory is freed.
 
+The flags in :cpp:enum:`TVMFFIObjectDeleterFlagBitMask` indicate which action 
to perform.
 
 .. _object-conversion-with-any:
 
-Conversion between :cpp:class:`~tvm::ffi::Any`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-At the stable C ABI boundary, TVM-FFI passes values using :cpp:class:`Any 
<tvm::ffi::Any>` (owning)
-or :cpp:class:`AnyView <tvm::ffi::AnyView>` (non-owning). Object handles are 
stored in the
-:cpp:member:`TVMFFIAny::v_obj` field with a type index >= 
``kTVMFFIStaticObjectBegin``.
-
-**Any/AnyView to Object**. Extract an object handle from 
:cpp:class:`TVMFFIAny`:
-
-.. code-block:: cpp
-
-   // Converts Any/AnyView to Object handle (non-owning)
-   int AnyToObjectPtr(const TVMFFIAny* value, TVMFFIObject** out) {
-     if (value->type_index >= kTVMFFIStaticObjectBegin) {
-       *out = (TVMFFIObject*)(value->v_obj);
-       return SUCCESS;
-     }
-     return FAILURE;  // Not an object type
-   }
-
-**Object to AnyView**. Store an object handle into non-owning 
:cpp:class:`AnyView <tvm::ffi::AnyView>`:
+**Conversion with Any**. At the stable C ABI boundary, TVM-FFI passes values 
using :cpp:class:`Any <tvm::ffi::Any>`
+(owning) or :cpp:class:`AnyView <tvm::ffi::AnyView>` (non-owning). Object 
handles are stored in the
+:cpp:member:`TVMFFIAny::v_obj` field with a type index >= 
:cpp:enumerator:`kTVMFFIStaticObjectBegin 
<TVMFFITypeIndex::kTVMFFIStaticObjectBegin>`.
 
-.. code-block:: cpp
-
-   // Converts Object handle to AnyView (non-owning)
-   void ObjectToAnyView(TVMFFIObject* obj, int32_t type_index, TVMFFIAny* out) 
{
-     out->type_index = type_index;
-     out->zero_padding = 0;
-     out->v_obj = obj;
-   }
-
-**Object to Any**. Store an object handle into owning :cpp:class:`Any 
<tvm::ffi::Any>`.
-The function increments the reference count to take shared ownership.
-
-.. code-block:: cpp
-
-   // Converts Object handle to Any (owning, increments refcount)
-   void ObjectToAny(TVMFFIObject* obj, int32_t type_index, TVMFFIAny* out) {
-     ObjectToAnyView(obj, type_index, out);
-     TVMFFIObjectIncRef(obj);  // Take ownership
-   }
+See :ref:`abi-object-ownership` for C code examples demonstrating:
 
-Later, release ownership by calling :cpp:func:`TVMFFIObjectDecRef` on 
:cpp:member:`TVMFFIAny::v_obj`.
+- Extracting an object handle from :cpp:class:`TVMFFIAny`
+- Storing an object handle into :cpp:class:`~tvm::ffi::Any` or 
:cpp:class:`~tvm::ffi::AnyView`
+- Managing ownership via reference counting
 
 Object Type Registry
 --------------------
@@ -477,5 +417,5 @@ 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
+- :doc:`abi_overview`: Low-level C ABI details for the object system
+- :doc:`../packaging/python_packaging`: Packaging C++ objects for Python wheels
diff --git a/docs/concepts/tensor.rst b/docs/concepts/tensor.rst
index 22123f0..d7f9343 100644
--- a/docs/concepts/tensor.rst
+++ b/docs/concepts/tensor.rst
@@ -34,11 +34,7 @@ and minimal extensions for ownership management.
   they provide safer and more convenient abstractions over raw DLPack structs.
 
 
-This tutorial is organized as follows:
-
-* **Common Usage**: the most important tensor APIs, including allocation and 
stream handling.
-* **Tensor Classes**: what tensor types are provided and which one you should 
use.
-* **Conversion between TVMFFIAny**: how tensors flow across ABI boundaries.
+This tutorial covers common usage patterns, tensor classes, and how tensors 
flow across ABI boundaries.
 
 Glossary
 --------
@@ -351,142 +347,30 @@ 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
-: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:
-
-* a :cpp:member:`type_index <TVMFFIAny::type_index>` that indicates the type 
of the payload, and
-* a union payload that may contain:
-
-  * A1. Primitive values, such as integers, floats, enums, raw pointers, or
-  * A2. TVM-FFI object handles, which are reference-counted pointers.
+At the stable C ABI boundary, TVM-FFI passes values using :cpp:class:`Any 
<tvm::ffi::Any>` (owning)
+or :cpp:class:`AnyView <tvm::ffi::AnyView>` (non-owning). Tensors have two 
possible representations:
 
-Specifically for tensors stored in :cpp:class:`Any <tvm::ffi::Any>` or 
:cpp:class:`AnyView <tvm::ffi::AnyView>`,
-there are two possible representations:
+* **Non-owning:** :c:struct:`DLTensor* <DLTensor>` with type index 
:cpp:enumerator:`TVMFFITypeIndex::kTVMFFIDLTensorPtr`
+* **Owning:** :cpp:class:`TensorObj* <tvm::ffi::TensorObj>` with type index 
:cpp:enumerator:`TVMFFITypeIndex::kTVMFFITensor`
 
-* Non-owning views as A1 (primitive values), i.e. :c:struct:`DLTensor* 
<DLTensor>` whose type index is 
:cpp:enumerator:`TVMFFITypeIndex::kTVMFFIDLTensorPtr`.
-* Owning objects as A2 (TVM-FFI tensor object handles), i.e., 
:cpp:class:`TensorObj* <tvm::ffi::TensorObj>` whose type index is 
:cpp:enumerator:`TVMFFITypeIndex::kTVMFFITensor`.
-
-Therefore, when you see a tensor in :cpp:class:`Any <tvm::ffi::Any>` or 
:cpp:class:`AnyView <tvm::ffi::AnyView>`,
-first check its :cpp:member:`type_index <TVMFFIAny::type_index>` to determine 
whether it is a raw pointer or an object handle
-before converting it to the desired tensor type.
+When extracting a tensor from :cpp:class:`TVMFFIAny`, check the 
:cpp:member:`type_index <TVMFFIAny::type_index>`
+to determine the representation before conversion.
 
 .. important::
 
-  As a rule of thumb, an owning object can be converted to a non-owning view, 
but not vice versa.
-
-To Non-Owning Tensor
-~~~~~~~~~~~~~~~~~~~~
-
-This converts an owning :cpp:class:`Any <tvm::ffi::Any>` or non-owning 
:cpp:class:`AnyView <tvm::ffi::AnyView>` into a non-owning tensor.
-Two type indices can be converted to a non-owning tensor view:
-
-- :cpp:enumerator:`TVMFFITypeIndex::kTVMFFIDLTensorPtr`: the payload is a raw 
pointer :c:struct:`DLTensor* <DLTensor>`.
-- :cpp:enumerator:`TVMFFITypeIndex::kTVMFFITensor`: the payload is a TVM-FFI 
tensor object handle, from which you can extract the underlying 
:c:struct:`DLTensor` according to the layout defined in :ref:`Figure 1 
<fig:layout-tensor>`.
-
-The snippets below are plain C (C99-compatible) and assume the TVM-FFI C ABI 
definitions from
-``tvm/ffi/c_api.h`` are available.
-
-.. code-block:: cpp
-
-    // Converts Any/AnyView to DLTensor*
-    int AnyToDLTensorView(const TVMFFIAny* value, DLTensor** out) {
-      if (value->type_index == kTVMFFIDLTensorPtr) {
-        *out = (DLTensor*)value->v_ptr;
-        return SUCCESS;
-      }
-      if (value->type_index == kTVMFFITensor) {
-        // See Figure 1 for layout of tvm::ffi::TensorObj
-        TVMFFIObject* obj = value->v_obj;
-        *out = (DLTensor*)((char*)obj + sizeof(TVMFFIObject));
-        return SUCCESS;
-      }
-      return FAILURE;
-    }
-
-:cpp:class:`TensorView <tvm::ffi::TensorView>` can be constructed directly 
from the returned :c:struct:`DLTensor* <DLTensor>`.
-
-To Owning Tensor
-~~~~~~~~~~~~~~~~
-
-This converts an owning :cpp:class:`Any <tvm::ffi::Any>` or non-owning 
:cpp:class:`AnyView <tvm::ffi::AnyView>` into an owning :cpp:class:`TensorObj 
<tvm::ffi::TensorObj>`. Only type index 
:cpp:enumerator:`TVMFFITypeIndex::kTVMFFITensor` can be converted to an owning 
tensor because it contains a TVM-FFI tensor object handle. The conversion 
involves incrementing the reference count to take ownership.
-
-.. code-block:: cpp
-
-    // Converts Any/AnyView to TensorObj*
-    int AnyToOwnedTensor(const TVMFFIAny* value, TVMFFIObjectHandle* out) {
-      if (value->type_index == kTVMFFITensor) {
-        *out = (TVMFFIObjectHandle)value->v_obj;
-        return SUCCESS;
-      }
-      return FAILURE;
-    }
+  An owning tensor can be converted to a non-owning view, but not vice versa.
 
-The caller can obtain shared ownership by calling 
:cpp:func:`TVMFFIObjectIncRef` on the returned handle,
-and later release it with :cpp:func:`TVMFFIObjectDecRef`.
+See :ref:`abi-tensor` for C code examples demonstrating:
 
-From Owning Tensor
-~~~~~~~~~~~~~~~~~~
-
-This converts an owning :cpp:class:`TensorObj <tvm::ffi::TensorObj>` to an 
owning :cpp:class:`Any <tvm::ffi::Any>` or non-owning :cpp:class:`AnyView 
<tvm::ffi::AnyView>`. It sets the type index to 
:cpp:enumerator:`TVMFFITypeIndex::kTVMFFITensor` and stores the tensor object 
handle in the payload.
-
-.. code-block:: cpp
-
-    // Converts TensorObj* to AnyView
-    int TensorToAnyView(TVMFFIObjectHandle tensor, TVMFFIAny* out_any_view) {
-      out_any_view->type_index = kTVMFFITensor;
-      out_any_view->zero_padding = 0;
-      out_any_view->v_obj = (TVMFFIObject*)tensor;
-      return SUCCESS;
-    }
-
-    // Converts TensorObj* to Any
-    int TensorToAny(TVMFFIObjectHandle tensor, TVMFFIAny* out_any) {
-      TVMFFIAny any_view;
-      int ret = TensorToAnyView(tensor, &any_view);
-      if (ret != SUCCESS) {
-        return ret;
-      }
-      TVMFFIObjectIncRef(tensor);
-      *out_any = any_view;
-      return SUCCESS;
-    }
-
-The C API :cpp:func:`TVMFFIObjectIncRef` obtains shared ownership of the 
tensor into `out_any`. Later, release it with
-:cpp:func:`TVMFFIObjectDecRef` on its :cpp:member:`TVMFFIAny::v_obj` field.
-
-From Non-Owning Tensor
-~~~~~~~~~~~~~~~~~~~~~~
-
-This converts a non-owning :cpp:class:`TensorView <tvm::ffi::TensorView>` to 
non-owning :cpp:class:`AnyView <tvm::ffi::AnyView>`.
-It sets the type index to 
:cpp:enumerator:`TVMFFITypeIndex::kTVMFFIDLTensorPtr` and stores a raw pointer 
to :c:struct:`DLTensor* <DLTensor>` in the payload.
-
-.. warning::
-
-  Non-owning :c:struct:`DLTensor` or :cpp:class:`TensorView 
<tvm::ffi::TensorView>` can be converted to non-owning :cpp:class:`AnyView 
<tvm::ffi::AnyView>`, but cannot be converted to owning :cpp:class:`Any 
<tvm::ffi::Any>`.
-
-.. code-block:: cpp
-
-    // Converts DLTensor* to AnyView
-    int DLTensorToAnyView(DLTensor* tensor, TVMFFIAny* out) {
-      out->type_index = kTVMFFIDLTensorPtr;
-      out->zero_padding = 0;
-      out->v_ptr = tensor;
-      return SUCCESS;
-    }
-
-    // Converts TensorView to AnyView
-    int TensorViewToAnyView(const tvm::ffi::TensorView& tensor_view, 
TVMFFIAny* out) {
-      return DLTensorToAnyView(tensor_view.GetDLTensorPtr(), out);
-    }
+- Extracting a :c:struct:`DLTensor` pointer from :cpp:class:`TVMFFIAny`
+- Constructing a :cpp:class:`~tvm::ffi::TensorObj` from DLPack
+- Exporting a :cpp:class:`~tvm::ffi::TensorObj` to DLPack
 
 Further Reading
 ---------------
 
-- :cpp:class:`TensorObj <tvm::ffi::TensorObj>` and :cpp:class:`Tensor 
<tvm::ffi::Tensor>` are part of the standard TVM-FFI object system.
-  See :doc:`object_and_class` for a comprehensive guide, or :ref:`Object 
Storage Format <object-storage-format>` for low-level layout details.
-- :cpp:class:`AnyView <tvm::ffi::AnyView>` and :cpp:class:`Any 
<tvm::ffi::Any>` are part of the stable C ABI.
-  Tutorial :doc:`Stable C ABI<../get_started/stable_c_abi>` explains the ABI 
design at a high level,
-  and :doc:`ABI Overview <abi_overview>` shares details on the design.
-- DLPack specification can be found at :external+data-api:doc:`DLPack protocol 
<design_topics/data_interchange>`, and documentation at :external+dlpack:doc:`C 
API <c_api>` and :external+dlpack:doc:`Python API <python_spec>`.
-- Kernel library developers may also refer to 
:doc:`../guides/kernel_library_guide` and `FlashInfer 
<https://github.com/flashinfer-ai/flashinfer/>`_ for best practices on building 
high-performance kernel libraries with TVM-FFI.
+- :doc:`object_and_class`: The object system that backs 
:cpp:class:`~tvm::ffi::TensorObj`
+- :doc:`any`: How tensors are stored in :cpp:class:`~tvm::ffi::Any` containers
+- :doc:`abi_overview`: Low-level C ABI details for tensor conversion
+- :doc:`../guides/kernel_library_guide`: Best practices for building kernel 
libraries with TVM-FFI
+- :external+dlpack:doc:`DLPack C API <c_api>`: The underlying tensor 
interchange standard
diff --git a/docs/guides/compiler_integration.md 
b/docs/guides/compiler_integration.md
index 254df38..a9b816a 100644
--- a/docs/guides/compiler_integration.md
+++ b/docs/guides/compiler_integration.md
@@ -93,7 +93,7 @@ Some of the key takeaways include:
 - Use return value for error handling, set error via 
{cpp:func}`TVMFFIErrorSetRaisedFromCStr`
   or {cpp:func}`TVMFFIErrorSetRaisedFromCStrParts`.
 
-You can also check out the [ABI overview](../concepts/abi_overview.md) for a 
more complete guide.
+You can also check out the [ABI overview](../concepts/abi_overview.rst) for a 
more complete guide.
 
 ## Graph Compilers
 
diff --git a/docs/index.rst b/docs/index.rst
index ef2b2b8..31c5efe 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -58,7 +58,7 @@ Table of Contents
    :maxdepth: 1
    :caption: Concepts
 
-   concepts/abi_overview.md
+   concepts/abi_overview.rst
    concepts/any.rst
    concepts/object_and_class.rst
    concepts/tensor.rst
diff --git a/examples/abi_overview/example_code.c 
b/examples/abi_overview/example_code.c
new file mode 100644
index 0000000..049ea93
--- /dev/null
+++ b/examples/abi_overview/example_code.c
@@ -0,0 +1,292 @@
+/*
+ * 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.
+ */
+/*
+ * Example code for TVM-FFI ABI overview.
+ *
+ * Compilation command:
+ *
+ * ```bash
+ * gcc $(tvm-ffi-config --cflags)             \
+ *     $(tvm-ffi-config --ldflags)            \
+ *     $(tvm-ffi-config --libs)               \
+ *     -Wl,-rpath,$(tvm-ffi-config --libdir)  \
+ *     -o ./example_code
+ * ```
+ */
+// NOLINTBEGIN(modernize-deprecated-headers,modernize-use-nullptr)
+#include <assert.h>
+#include <dlpack/dlpack.h>
+#include <stdio.h>
+#include <string.h>
+#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/extra/c_env_api.h>
+
+int IS_OWNING_ANY = 1;
+
+// [Any_AnyView.FromInt_Float.begin]
+TVMFFIAny Any_AnyView_FromInt(int64_t value) {
+  TVMFFIAny any;
+  any.type_index = kTVMFFIInt;
+  any.zero_padding = 0;
+  any.v_int64 = value;
+  return any;
+}
+
+TVMFFIAny Any_AnyView_FromFloat(double value) {
+  TVMFFIAny any;
+  any.type_index = kTVMFFIFloat;
+  any.zero_padding = 0;
+  any.v_float64 = value;
+  return any;
+}
+// [Any_AnyView.FromInt_Float.end]
+
+// [Any_AnyView.FromObjectPtr.begin]
+TVMFFIAny Any_AnyView_FromObjectPtr(TVMFFIObject* obj) {
+  TVMFFIAny any;
+  assert(obj != NULL);
+  any.type_index = kTVMFFIObject;
+  any.zero_padding = 0;
+  any.v_obj = obj;
+  // Increment refcount if it's Any (owning) instead of AnyView (borrowing)
+  if (IS_OWNING_ANY) {
+    TVMFFIObjectIncRef(obj);
+  }
+  return any;
+}
+// [Any_AnyView.FromObjectPtr.end]
+
+// [Any_AnyView.Destroy.begin]
+void Any_AnyView_Destroy(TVMFFIAny* any) {
+  if (IS_OWNING_ANY) {
+    // Checks if `any` holds a heap-allocated object,
+    // and if so, decrements the reference count
+    if (any->type_index >= kTVMFFIStaticObjectBegin) {
+      TVMFFIObjectDecRef(any->v_obj);
+    }
+  }
+  *any = (TVMFFIAny){0};  // Clears the `any` struct
+}
+// [Any_AnyView.Destroy.end]
+
+// [Any_AnyView.GetInt_Float.begin]
+int64_t Any_AnyView_GetInt(const TVMFFIAny* any) {
+  if (any->type_index == kTVMFFIInt || any->type_index == kTVMFFIBool) {
+    return any->v_int64;
+  } else if (any->type_index == kTVMFFIFloat) {
+    return (int64_t)(any->v_float64);
+  }
+  assert(0);  // FAILED to read int
+  return 0;
+}
+
+double Any_AnyView_GetFloat(const TVMFFIAny* any) {
+  if (any->type_index == kTVMFFIInt || any->type_index == kTVMFFIBool) {
+    return (double)(any->v_int64);
+  } else if (any->type_index == kTVMFFIFloat) {
+    return any->v_float64;
+  }
+  assert(0);  // FAILED to read float
+  return 0.0;
+}
+// [Any_AnyView.GetInt_Float.end]
+
+// [Any_AnyView.GetDLTensor.begin]
+DLTensor* Any_AnyView_GetDLTensor(const TVMFFIAny* value) {
+  if (value->type_index == kTVMFFIDLTensorPtr) {
+    return (DLTensor*)(value->v_ptr);
+  } else if (value->type_index == kTVMFFITensor) {
+    return (DLTensor*)((char*)(value->v_obj) + sizeof(TVMFFIObject));
+  }
+  assert(0);  // FAILED to read DLTensor
+  return NULL;
+}
+// [Any_AnyView.GetDLTensor.end]
+
+// [Any_AnyView.GetObject.begin]
+TVMFFIObject* Any_AnyView_GetObject(const TVMFFIAny* value) {
+  if (value->type_index == kTVMFFINone) {
+    return NULL;  // Handling nullptr if needed
+  } else if (value->type_index >= kTVMFFIStaticObjectBegin) {
+    return value->v_obj;
+  }
+  assert(0);  // FAILED: not a TVM-FFI object
+  return NULL;
+}
+// [Any_AnyView.GetObject.end]
+
+// [Object.IsInstance.begin]
+int Object_IsInstance(int32_t sub_type_index, int32_t super_type_index, 
int32_t super_type_depth) {
+  const TVMFFITypeInfo* sub_type_info = NULL;
+  // Everything is a subclass of object.
+  if (sub_type_index == super_type_index) {
+    return 1;
+  }
+  // Invariance: parent index is always smaller than the child.
+  if (sub_type_index < super_type_index) {
+    return 0;
+  }
+  sub_type_info = TVMFFIGetTypeInfo(sub_type_index);
+  return sub_type_info->type_depth > super_type_depth &&
+         sub_type_info->type_ancestors[super_type_depth]->type_index == 
super_type_index;
+}
+// [Object.IsInstance.end]
+
+// [Object.MoveFromAny.begin]
+void Object_MoveFromAny(TVMFFIAny* any, TVMFFIObject** obj) {
+  assert(any->type_index >= kTVMFFIStaticObjectBegin);
+  *obj = any->v_obj;
+  (*any) = (TVMFFIAny){0};
+  if (!IS_OWNING_ANY) {
+    TVMFFIObjectIncRef(*obj);
+  }
+}
+// [Object.MoveFromAny.end]
+
+// [Object.Destroy.begin]
+void Object_Destroy(TVMFFIObject* obj) {
+  assert(obj != NULL);
+  TVMFFIObjectDecRef(obj);
+}
+// [Object.Destroy.end]
+
+// [Tensor.AccessDLTensor.begin]
+DLTensor* Tensor_AccessDLTensor(TVMFFIObject* tensor) {
+  assert(tensor != NULL);
+  return (DLTensor*)((char*)tensor + sizeof(TVMFFIObject));
+}
+// [Tensor.AccessDLTensor.end]
+
+// [Tensor_FromDLPack.begin]
+TVMFFIObject* Tensor_FromDLPack(DLManagedTensorVersioned* from) {
+  int err_code = 0;
+  TVMFFIObject* out = NULL;
+  err_code = TVMFFITensorFromDLPackVersioned(  //
+      from,                                    // input DLPack tensor
+      /*require_alignment=*/0,                 // no alignment requirement
+      /*require_contiguous=*/1,                // require contiguous tensor
+      (void**)(&out));
+  assert(err_code == 0);
+  return out;
+}
+// [Tensor_FromDLPack.end]
+
+// [Tensor_Alloc.begin]
+TVMFFIObject* Tensor_Alloc(DLTensor* prototype) {
+  int err_code = 0;
+  TVMFFIObject* out = NULL;
+  assert(prototype->data == NULL);
+  err_code = TVMFFIEnvTensorAlloc(prototype, (void**)(&out));
+  assert(err_code == 0);
+  return out;
+}
+// [Tensor_Alloc.end]
+
+// [Tensor_ToDLPackVersioned.begin]
+DLManagedTensorVersioned* Tensor_ToDLPackVersioned(TVMFFIObject* tensor) {
+  int err_code = 0;
+  DLManagedTensorVersioned* out = NULL;
+  err_code = TVMFFITensorToDLPackVersioned(tensor, &out);
+  assert(err_code == 0);
+  return out;
+}
+// [Tensor_ToDLPackVersioned.end]
+
+// [Function.Construct.begin]
+TVMFFIObject* Function_Construct(void* self, TVMFFISafeCallType safe_call,
+                                 void (*deleter)(void* self)) {
+  int err_code;
+  TVMFFIObject* out = NULL;
+  err_code = TVMFFIFunctionCreate(self, safe_call, deleter, (void**)(&out));
+  assert(err_code == 0);
+  return out;
+}
+// [Function.Construct.end]
+
+// [Function.Call.begin]
+int64_t CallFunction(TVMFFIObject* func, int64_t x, int64_t y) {
+  int err_code;
+  TVMFFIAny args[2];
+  TVMFFIAny result = (TVMFFIAny){0};
+  args[0] = Any_AnyView_FromInt(x);
+  args[1] = Any_AnyView_FromInt(y);
+  err_code = TVMFFIFunctionCall(func, args, 2, &result);
+  assert(err_code == 0);
+  return Any_AnyView_GetInt(&result);
+}
+// [Function.Call.end]
+
+// [Function.GetGlobal.begin]
+TVMFFIObject* Function_RetrieveGlobal(const char* name) {
+  TVMFFIObject* out = NULL;
+  TVMFFIByteArray name_byte_array = {name, strlen(name)};
+  int err_code = TVMFFIFunctionGetGlobal(&name_byte_array, (void**)(&out));
+  assert(err_code == 0);
+  return out;
+}
+// [Function.GetGlobal.end]
+
+// [Function.SetGlobal.begin]
+void Function_SetGlobal(const char* name, TVMFFIObject* func) {
+  TVMFFIByteArray name_byte_array = {name, strlen(name)};
+  int err_code = TVMFFIFunctionSetGlobal(&name_byte_array, func, 0);
+  assert(err_code == 0);
+}
+// [Function.SetGlobal.end]
+
+// [Error.Print.begin]
+void PrintError(TVMFFIObject* err) {
+  TVMFFIErrorCell* cell = (TVMFFIErrorCell*)((char*)err + 
sizeof(TVMFFIObject));
+  fprintf(stderr, "%.*s: %.*s\n",                        //
+          (int)cell->kind.size, cell->kind.data,         // e.g. "ValueError"
+          (int)cell->message.size, cell->message.data);  // e.g. "Expected at 
least 2 arguments"
+  if (cell->backtrace.size) {
+    fprintf(stderr, "Backtrace:\n%.*s\n", (int)cell->backtrace.size, 
cell->backtrace.data);
+  }
+}
+// [Error.Print.end]
+
+// [Error.HandleReturnCode.begin]
+void Error_HandleReturnCode(int rc) {
+  TVMFFIObject* err = NULL;
+  if (rc == -1) {
+    // Move the raised error from TLS (clears TLS slot)
+    TVMFFIErrorMoveFromRaised((void**)(&err));  // now `err` owns the error 
object
+    if (err != NULL) {
+      PrintError(err);  // print the error
+      // IMPORTANT: Release the error object, or gets memory leaks
+      TVMFFIObjectDecRef(err);
+    }
+  } else if (rc == -2) {
+    // Frontend (e.g., Python) already has an exception set.
+    // Do not fetch from TLS; consult the frontend's error mechanism.
+    return;
+  }
+}
+// [Error.HandleReturnCode.end]
+
+// [Error.RaiseException.begin]
+int Error_RaiseException(void* handle, const TVMFFIAny* args, int32_t 
num_args, TVMFFIAny* result) {
+  TVMFFIErrorSetRaisedFromCStr("ValueError", "Expected at least 2 arguments");
+  return -1;
+}
+// [Error.RaiseException.end]
+
+// NOLINTEND(modernize-deprecated-headers,modernize-use-nullptr)
+int main() { return 0; }
diff --git a/include/tvm/ffi/c_api.h b/include/tvm/ffi/c_api.h
index 4da0d4d..c02075b 100644
--- a/include/tvm/ffi/c_api.h
+++ b/include/tvm/ffi/c_api.h
@@ -62,7 +62,7 @@
 /*! \brief TVM FFI minor version. */
 #define TVM_FFI_VERSION_MINOR 1
 /*! \brief TVM FFI patch version. */
-#define TVM_FFI_VERSION_PATCH 8
+#define TVM_FFI_VERSION_PATCH 9
 // NOLINTEND(modernize-macro-to-enum)
 
 #ifdef __cplusplus
@@ -81,6 +81,7 @@ typedef struct {
   uint32_t patch;
 } TVMFFIVersion;
 
+// [TVMFFITypeIndex.begin]
 #ifdef __cplusplus
 enum TVMFFITypeIndex : int32_t {
 #else
@@ -184,6 +185,7 @@ typedef enum {
 #else
 } TVMFFITypeIndex;
 #endif
+// [TVMFFITypeIndex.end]
 
 /*! \brief Handle to Object from C API's pov */
 typedef void* TVMFFIObjectHandle;
@@ -218,6 +220,7 @@ typedef enum {
 } TVMFFIObjectDeleterFlagBitMask;
 #endif
 
+// [TVMFFIObject.begin]
 /*!
  * \brief C-based type of all FFI object header that allocates on heap.
  */
@@ -268,7 +271,9 @@ typedef struct {
   };
 #endif
 } TVMFFIObject;
+// [TVMFFIObject.end]
 
+// [TVMFFIAny.begin]
 /*!
  * \brief C-based type of all on stack Any value.
  *
@@ -321,7 +326,9 @@ typedef struct {
   };
 #endif
 } TVMFFIAny;
+// [TVMFFIAny.end]
 
+// [TVMFFIByteArray.begin]
 /*!
  * \brief Byte array data structure used by String and Bytes.
  *
@@ -349,6 +356,7 @@ typedef struct {
   /*! \brief The size of the data. */
   size_t size;
 } TVMFFIShapeCell;
+// [TVMFFIByteArray.end]
 
 /*!
  * \brief Mode to update the backtrace of the error.
@@ -366,6 +374,7 @@ typedef enum {
 } TVMFFIBacktraceUpdateMode;
 #endif
 
+// [TVMFFIErrorCell.begin]
 /*!
  * \brief Error cell used in error object following header.
  */
@@ -405,7 +414,9 @@ typedef struct {
    */
   TVMFFIObjectHandle extra_context;
 } TVMFFIErrorCell;
+// [TVMFFIErrorCell.end]
 
+// [TVMFFISafeCallType.begin]
 /*!
  * \brief Type that defines C-style safe call convention
  *
@@ -441,7 +452,9 @@ typedef struct {
  */
 typedef int (*TVMFFISafeCallType)(void* handle, const TVMFFIAny* args, int32_t 
num_args,
                                   TVMFFIAny* result);
+// [TVMFFISafeCallType.end]
 
+// [TVMFFIFunctionCell.begin]
 /*!
  * \brief Object cell for function object following header.
  */
@@ -462,6 +475,7 @@ typedef struct {
    */
   void* cpp_call;
 } TVMFFIFunctionCell;
+// [TVMFFIFunctionCell.end]
 
 /*!
  * \brief Object cell for opaque object following header.


Reply via email to