|
15 | 15 | specific language governing permissions and limitations |
16 | 16 | under the License. |
17 | 17 |
|
18 | | -Function, Exception and Module |
19 | | -============================== |
| 18 | +Function and Module |
| 19 | +=================== |
20 | 20 |
|
21 | 21 | TVM-FFI provides a unified and ABI-stable calling convention that enables |
22 | 22 | cross-language function calls between C++, Python, Rust, and other languages. |
23 | 23 | Functions are first-class :doc:`TVM-FFI objects <object_and_class>`. |
24 | 24 |
|
25 | | -This tutorial covers everything you need to know about defining, registering, |
26 | | -and calling TVM-FFI functions, their exception handling, and working with modules. |
| 25 | +This tutorial covers defining, registering, and calling TVM-FFI functions, |
| 26 | +exception handling, and working with modules. |
27 | 27 |
|
28 | 28 | Glossary |
29 | 29 | -------- |
@@ -190,17 +190,10 @@ to a :py:class:`tvm_ffi.Function` at the ABI boundary. The example below demonst |
190 | 190 | print(func_add(1, 2)) |
191 | 191 |
|
192 | 192 |
|
193 | | -Exception ABI |
194 | | -------------- |
| 193 | +.. _sec:function: |
195 | 194 |
|
196 | | -This section describes the exception handling contract in the TVM-FFI Stable C ABI. |
197 | | -Exceptions are first-class citizens in TVM-FFI, and this section specifies: |
198 | | - |
199 | | -- How to properly throw exceptions from a TVM-FFI ABI function |
200 | | -- How to check for and propagate exceptions from a TVM-FFI ABI function |
201 | | - |
202 | | -TVM-FFI C ABI |
203 | | -~~~~~~~~~~~~~ |
| 195 | +Function |
| 196 | +-------- |
204 | 197 |
|
205 | 198 | All TVM-FFI functions ultimately conform to the :cpp:type:`TVMFFISafeCallType` signature, |
206 | 199 | which provides a stable C ABI for cross-language calls. The C calling convention is defined as: |
@@ -229,138 +222,17 @@ that the caller must zero-initialize before the call. |
229 | 222 | .. hint:: |
230 | 223 | See :doc:`Any <any>` for more details on the semantics of :cpp:type:`tvm::ffi::AnyView` and :cpp:type:`tvm::ffi::Any`. |
231 | 224 |
|
232 | | -Retrieve Errors in C |
233 | | -~~~~~~~~~~~~~~~~~~~~ |
234 | | - |
235 | | -When a TVM-FFI function returns a non-zero code, it indicates that an error occurred |
236 | | -and a :cpp:class:`tvm::ffi::ErrorObj` is stored in thread-local storage (TLS). |
237 | | -This section shows how to retrieve the error object and print the error message and backtrace. |
238 | | - |
239 | | -.. note:: |
240 | | - |
241 | | - An :cpp:class:`~tvm::ffi::ErrorObj` is a :cpp:class:`~tvm::ffi::Object` with a :cpp:class:`TVMFFIErrorCell` payload |
242 | | - as defined below: |
243 | | - |
244 | | - .. code-block:: cpp |
245 | | -
|
246 | | - typedef struct { |
247 | | - TVMFFIByteArray kind; // Error type (e.g., "ValueError") |
248 | | - TVMFFIByteArray message; // Error message |
249 | | - TVMFFIByteArray backtrace; // Stack trace (most-recent call first) |
250 | | - void (*update_backtrace)(...); // Hook to append/replace backtrace |
251 | | - } TVMFFIErrorCell; |
252 | | -
|
253 | | -**Print an Error**. The example code below shows how to print an error message and backtrace. |
254 | | - |
255 | | -.. code-block:: cpp |
256 | | -
|
257 | | - #include <tvm/ffi/c_api.h> |
258 | | -
|
259 | | - void PrintError(TVMFFIObject* err) { |
260 | | - TVMFFIErrorCell* cell = (TVMFFIErrorCell*)((char*)err + sizeof(TVMFFIObject)); |
261 | | - fprintf(stderr, "%.*s: %.*s\n", (int)cell->kind.size, cell->kind.data, (int)cell->message.size, cell->message.data); |
262 | | - if (cell->backtrace.size) { |
263 | | - fprintf(stderr, "Backtrace:\n%.*s\n", (int)cell->backtrace.size, cell->backtrace.data); |
264 | | - } |
265 | | - } |
266 | | -
|
267 | | -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`. |
273 | | - |
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 | | - } |
292 | | -
|
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. |
295 | | - |
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. |
300 | | - |
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. |
310 | | - |
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"); |
322 | | - return -1; |
323 | | - } |
324 | | - // ... kernel implementation ... |
325 | | - return 0; |
326 | | - } |
327 | | -
|
328 | | -Alternatively, :cpp:func:`TVMFFIErrorSetRaisedFromCStrParts` accepts explicit string lengths, |
329 | | -which is useful when the error kind and message are not null-terminated. |
330 | | - |
331 | | -**Propagating errors**. For chains of generated calls, simply propagate return codes—TLS carries |
332 | | -the error details: |
333 | | - |
334 | | -.. code-block:: cpp |
335 | | -
|
336 | | - int outer_function(...) { |
337 | | - int err_code = 0; |
338 | | -
|
339 | | - err_code = inner_function(...); |
340 | | - if (err_code != 0) goto RAII; // Propagate error; TLS has the details |
341 | | -
|
342 | | - RAII: |
343 | | - // clean up owned resources |
344 | | - return err_code; |
345 | | - } |
346 | | -
|
347 | | -Function |
348 | | --------- |
| 225 | +.. _sec:function-layout: |
349 | 226 |
|
350 | 227 | Layout and ABI |
351 | 228 | ~~~~~~~~~~~~~~ |
352 | 229 |
|
353 | 230 | :cpp:class:`tvm::ffi::FunctionObj` stores two call pointers in :cpp:class:`TVMFFIFunctionCell`: |
354 | 231 |
|
355 | | -.. code-block:: cpp |
| 232 | +- ``safe_call``: Used for cross-ABI function calls; intercepts exceptions and stores them in TLS. |
| 233 | +- ``cpp_call``: Used within the same DSO; exceptions are thrown directly for better performance. |
356 | 234 |
|
357 | | - typedef struct { |
358 | | - TVMFFISafeCallType safe_call; |
359 | | - void* cpp_call; |
360 | | - } TVMFFIFunctionCell; |
361 | | -
|
362 | | -``safe_call`` is used for cross-ABI function calls: it intercepts exceptions and stores them in TLS. |
363 | | -``cpp_call`` is used within the same DSO, where exceptions are thrown directly for better performance. |
| 235 | +See :ref:`abi-function` for the C struct definition. |
364 | 236 |
|
365 | 237 | .. important:: |
366 | 238 |
|
@@ -407,40 +279,35 @@ calling convention. |
407 | 279 | in :c:macro:`TVM_FFI_SAFE_CALL_BEGIN` / :c:macro:`TVM_FFI_SAFE_CALL_END` macros. |
408 | 280 |
|
409 | 281 |
|
410 | | -C Registry APIs |
411 | | -~~~~~~~~~~~~~~~ |
| 282 | +Compiler developers commonly need to look up global functions in generated code. |
| 283 | +Use :cpp:func:`TVMFFIFunctionGetGlobal` to retrieve a function by name, then call it with :cpp:func:`TVMFFIFunctionCall`. |
| 284 | +See :ref:`abi-function` for C code examples. |
412 | 285 |
|
413 | | -.. list-table:: |
414 | | - :header-rows: 1 |
415 | | - :widths: 40 60 |
| 286 | +.. _sec:exception: |
416 | 287 |
|
417 | | - * - C API |
418 | | - - Description |
419 | | - * - :cpp:func:`TVMFFIFunctionGetGlobal` |
420 | | - - Get a function by name; returns an owning handle. |
421 | | - * - :cpp:func:`TVMFFIFunctionSetGlobal` |
422 | | - - Register a function in the global registry. |
423 | | - * - :cpp:func:`TVMFFIFunctionCall` |
424 | | - - Call a function with the given arguments. |
| 288 | +Exception |
| 289 | +~~~~~~~~~ |
425 | 290 |
|
426 | | -Compiler developers commonly need to look up global functions in generated code. Use |
427 | | -:cpp:func:`TVMFFIFunctionGetGlobal` to retrieve a function by name, then call it with :cpp:func:`TVMFFIFunctionCall`. |
428 | | -The example below demonstrates how to look up and call a global function in C: |
| 291 | +This section describes the exception handling contract in the TVM-FFI Stable C ABI. |
| 292 | +Exceptions are first-class citizens in TVM-FFI, and this section specifies: |
429 | 293 |
|
430 | | -.. code-block:: cpp |
| 294 | +- How to properly throw exceptions from a TVM-FFI ABI function |
| 295 | +- How to check for and propagate exceptions from a TVM-FFI ABI function |
431 | 296 |
|
432 | | - int LookupAndCall(const char* global_function_name, const TVMFFIAny* args, int num_args, TVMFFIAny* result) { |
433 | | - TVMFFIObject* func = NULL; |
434 | | - int err_code; |
435 | | - if ((err_code = TVMFFIFunctionGetGlobal(global_function_name, &func)) != 0) |
436 | | - goto RAII; |
437 | | - if ((err_code = TVMFFIFunctionCall(func, args, num_args, result)) != 0) |
438 | | - goto RAII; |
439 | | -
|
440 | | - RAII: // clean up owned resources |
441 | | - if (func != NULL) TVMFFIObjectDecRef(func); |
442 | | - return err_code; |
443 | | - } |
| 297 | +When a TVM-FFI function returns a non-zero code, an error occurred. |
| 298 | +An :cpp:class:`~tvm::ffi::ErrorObj` is stored in thread-local storage (TLS) and can be retrieved |
| 299 | +with :cpp:func:`TVMFFIErrorMoveFromRaised`. |
| 300 | + |
| 301 | +- **Error code -1:** Retrieve the error from TLS, print it, and release via :cpp:func:`TVMFFIObjectDecRef`. |
| 302 | +- **Error code -2:** A rare frontend error; consult the frontend's error mechanism instead of TLS. |
| 303 | + |
| 304 | +To raise an error, use :cpp:func:`TVMFFIErrorSetRaisedFromCStr` to set the TLS error and return ``-1``. |
| 305 | +For chains of calls, simply propagate return codes - TLS carries the error details. |
| 306 | + |
| 307 | +See :ref:`abi-exception` for C code examples. |
| 308 | + |
| 309 | + |
| 310 | +.. _sec:module: |
444 | 311 |
|
445 | 312 | Modules |
446 | 313 | ------- |
@@ -560,5 +427,5 @@ Further Reading |
560 | 427 |
|
561 | 428 | - :doc:`any`: How functions are stored in :cpp:class:`~tvm::ffi::Any` containers |
562 | 429 | - :doc:`object_and_class`: The object system that backs :cpp:class:`~tvm::ffi::FunctionObj` |
| 430 | +- :doc:`abi_overview`: Low-level C ABI details for functions and exceptions |
563 | 431 | - :doc:`../packaging/python_packaging`: Packaging functions for Python wheels |
564 | | -- :doc:`abi_overview`: Low-level ABI details for the function calling convention |
0 commit comments