You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/concepts/any.rst
+15-27Lines changed: 15 additions & 27 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,9 +27,7 @@ values of a wide variety of types, including primitives, objects, and strings.
27
27
Unlike ``std::any``, it is designed for zero-copy inter-language exchange without RTTI,
28
28
featuring a fixed 16-byte layout with built-in reference counting and ownership semantics.
29
29
30
-
This tutorial covers everything you need to know about :cpp:class:`~tvm::ffi::Any` and :cpp:class:`~tvm::ffi::AnyView`:
31
-
common usage patterns, ownership semantics, and memory layout.
32
-
30
+
This tutorial covers common usage patterns, ownership semantics, and memory layout.
33
31
34
32
Common Usage
35
33
------------
@@ -169,6 +167,8 @@ Compare with ``nullptr`` to check for ``None``:
169
167
}
170
168
171
169
170
+
.. _any-ownership:
171
+
172
172
Ownership
173
173
---------
174
174
@@ -195,8 +195,8 @@ The core distinction between :cpp:class:`tvm::ffi::Any` and
195
195
- Function inputs
196
196
- Return values, storage
197
197
198
-
Code Examples
199
-
~~~~~~~~~~~~~~
198
+
Examples
199
+
~~~~~~~~
200
200
201
201
:cpp:class:`~tvm::ffi::AnyView` is a lightweight, non-owning view. Copying it simply
202
202
copies 16 bytes with no reference count updates, making it ideal for passing arguments without overhead:
@@ -245,25 +245,9 @@ Destruction Semantics in C
245
245
246
246
In C, which lacks RAII, you must manually destroy :cpp:class:`~tvm::ffi::Any` objects
247
247
by calling :cpp:func:`TVMFFIObjectDecRef` for heap-allocated objects.
248
+
Destroying an :cpp:class:`~tvm::ffi::AnyView` is effectively a no-op - just clear its contents.
248
249
249
-
.. code-block:: cpp
250
-
251
-
void destroy_any(TVMFFIAny* any) {
252
-
if (any->type_index >= kTVMFFIStaticObjectBegin) {
253
-
// Decrement the reference count of the heap-allocated object
254
-
TVMFFIObjectDecRef(any->v_obj);
255
-
}
256
-
*any = (TVMFFIAny){0};
257
-
}
258
-
259
-
In contrast, destroying an :cpp:class:`~tvm::ffi::AnyView` is effectively a no-op - just clear its contents.
260
-
261
-
.. code-block:: cpp
262
-
263
-
void destroy_any_view(TVMFFIAny* any_view) {
264
-
*any_view = (TVMFFIAny){0};
265
-
}
266
-
250
+
See :ref:`abi-destruct-any` for C code examples.
267
251
268
252
Layout
269
253
------
@@ -310,6 +294,8 @@ It is effectively a layout-stable 16-byte tagged union.
310
294
* The first 4 bytes (:cpp:member:`TVMFFIAny::type_index`) serve as a tag identifying the stored type.
311
295
* 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.
312
296
297
+
.. _any-atomic-types:
298
+
313
299
Atomic Types
314
300
~~~~~~~~~~~~
315
301
@@ -371,6 +357,8 @@ Note that raw pointers like :c:struct:`DLTensor* <DLTensor>` and ``char*`` also
371
357
These pointers carry no ownership, so the caller must ensure the pointed-to data outlives
372
358
the :cpp:class:`~tvm::ffi::AnyView` or :cpp:class:`~tvm::ffi::Any`.
The payload of the error object is a :cpp:type:`TVMFFIErrorCell` structure
268
-
containing the error kind, message, and backtrace. It can be accessed
269
-
by skipping the :cpp:type:`TVMFFIObject` header using pointer arithmetic.
270
-
271
-
**Retrieve the error object**. When the error code is ``-1``, the error object is stored in TLS
272
-
and can be retrieved with :cpp:func:`TVMFFIErrorMoveFromRaised`.
233
+
This design is called a **packed function**, because it "packs" all arguments into a single array of type-erased :cpp:type:`tvm::ffi::AnyView`,
234
+
and further unifies calling convention across all languages without resorting to JIT compilation.
273
235
274
-
.. code-block:: cpp
275
-
276
-
void HandleReturnCode(int rc) {
277
-
TVMFFIObject* err = NULL;
278
-
if (rc == 0) {
279
-
// Success
280
-
} else if (rc == -1) {
281
-
// Move the raised error from TLS (clears TLS slot)
282
-
TVMFFIErrorMoveFromRaised(&err); // now `err` owns the error object
283
-
if (err != NULL) {
284
-
PrintError(err); // print the error
285
-
TVMFFIObjectDecRef(err); // Release the error object
286
-
}
287
-
} else if (rc == -2) {
288
-
// Frontend (e.g., Python) already has an exception set.
289
-
// Do not fetch from TLS; consult the frontend's error mechanism.
290
-
}
291
-
}
236
+
More specifically, this mechanism enables the following scenarios:
292
237
293
-
This function transfers ownership of the error object to the caller and clears the TLS slot.
294
-
You must call :cpp:func:`TVMFFIObjectDecRef` to release the object when done to avoid memory leaks.
238
+
- **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.
239
+
- **Static languages**. Metaprogramming techniques, such as C++ templates, are usually available to directly instantiate packed format on stack, saving the need for dynamic examination.
240
+
- **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.
295
241
296
-
**Rare frontend errors**. Error code ``-2`` is reserved for rare frontend errors. It is returned only
297
-
when the C API :cpp:func:`TVMFFIEnvCheckSignals` returns non-zero during execution, indicating that
298
-
the Python side has a pending signal requiring attention. In this case, the caller should not fetch
299
-
the error object from TLS but instead consult the frontend's error mechanism to handle the exception.
242
+
**Performance Implications**. This approach is in practice highly efficient in machine learning workloads.
300
243
301
-
Raise Errors in C
302
-
~~~~~~~~~~~~~~~~~
303
-
304
-
As part of TVM-FFI's calling convention, returning ``-1`` indicates that an error occurred
305
-
and the error object is stored in the TLS slot. The error object can contain arbitrary
306
-
user-defined information, such as error messages, backtraces, or Python frame-local variables.
307
-
308
-
.. hint::
309
-
Compiler code generation may use similar patterns to raise errors in generated code.
244
+
- In Python/C++ calls, we can get to microsecond level overhead, which is generally similar to overhead for eager mode;
245
+
- When both sides of calls are static languages, the overhead will go down to tens of nanoseconds.
310
246
311
-
The example below sets the TLS error and returns ``-1`` using :cpp:func:`TVMFFIErrorSetRaisedFromCStr`:
312
-
313
-
.. code-block:: cpp
314
-
315
-
#include <tvm/ffi/c_api.h>
316
-
317
-
int __tvm_ffi_my_kernel(void* handle, const TVMFFIAny* args,
318
-
int32_t num_args, TVMFFIAny* result) {
319
-
// Validate inputs
320
-
if (num_args < 2) {
321
-
TVMFFIErrorSetRaisedFromCStr("ValueError", "Expected at least 2 arguments");
0 commit comments