Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Table of Contents

packaging/python_packaging.rst
packaging/stubgen.rst
packaging/rust_stubgen.md
packaging/cpp_tooling.rst

.. toctree::
Expand Down
142 changes: 142 additions & 0 deletions docs/packaging/rust_stubgen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you under the Apache License, Version 2.0 (the -->
<!--- "License"); you may not use this file except in compliance -->
<!--- with the License. You may obtain a copy of the License at -->
<!--- -->
<!--- http://www.apache.org/licenses/LICENSE-2.0 -->
<!--- -->
<!--- Unless required by applicable law or agreed to in writing, -->
<!--- software distributed under the License is distributed on an -->
<!--- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -->
<!--- KIND, either express or implied. See the License for the -->
<!--- specific language governing permissions and limitations -->
<!--- under the License. -->

# Rust Stubgen Guide

```{note}
The Rust stub generation flow is currently experimental and may evolve.
```

This guide covers practical usage of `tvm-ffi-stubgen`: generation command, output crate, and how to call generated APIs.

## Generate a Stub Crate

Run from `3rdparty/tvm/3rdparty/tvm-ffi/rust`:

```bash
cargo run -p tvm-ffi-stubgen -- <OUT_DIR> \
--init-prefix testing \
--init-crate tvm-ffi-testing \
--dlls /abs/path/to/libtvm_ffi_testing.so \
--overwrite
```

### Arguments

- `OUT_DIR`: positional output directory
- `--dlls`: one or more dynamic libraries for reflection metadata (`;`-separated)
- `--init-prefix`: registry prefix filter (repeatable; see multi-prefix below)
- `--init-crate`: generated crate name
- `--tvm-ffi-path`: optional local path override for `tvm-ffi`
- `--overwrite`: overwrite non-empty output directory
- `--no-format`: skip the post-generation `cargo fmt` pass

By default, `tvm-ffi-stubgen` runs `cargo fmt` on the generated crate after emitting
`Cargo.toml`, `build.rs`, and Rust sources. Use `--no-format` only when you need to inspect
the raw generated text before formatting or when debugging generator output itself.

### Multi-Prefix Mode

`--init-prefix` can be specified multiple times to generate a single crate covering
several namespaces:

```bash
cargo run -p tvm-ffi-stubgen -- <OUT_DIR> \
--dlls "libtilelang_module.so;libtvm.so" \
--init-prefix tl --init-prefix ir --init-prefix tir --init-prefix script \
--init-crate tilelang-ffi \
--overwrite
```

With a single prefix the prefix is stripped and items land at the crate root.
With multiple prefixes no stripping occurs; each prefix becomes a top-level module
(`crate::tl::*`, `crate::ir::*`, etc.).

## Generated Output Layout

The output is a standalone Rust crate:

- `Cargo.toml`
- `src/lib.rs`
- `src/_tvm_ffi_stubgen_detail/functions.rs`
- `src/_tvm_ffi_stubgen_detail/types.rs`

`src/lib.rs` re-exports generated wrappers and provides:

```rust
pub fn load_library(path: &str) -> tvm_ffi::Result<tvm_ffi::Module>
```

## Using Generated Crate

Using the generated stubs is straightforward—simply load the runtime library, call exported functions, and work with generated object wrappers and subtyping as needed. The full process is shown in the following example, covering typical usage:

```rust
use tvm_ffi_testing as stub;

fn main() -> tvm_ffi::Result<()> {
// Load FFI library (required before any calls)
stub::load_library("/abs/path/to/libtvm_ffi_testing.so")?;

// Call a generated function with typed arguments
let y = stub::add_one(1)?;
assert_eq!(y, 2);

// Call a function via packed interface for dynamic signature
let _out = stub::echo(&[tvm_ffi::Any::from(1_i64)])?;

// Object constructor/method wrappers are resolved from type metadata.
let pair_obj = stub::TestIntPair::new(3, 4)?;
let pair: stub::TestIntPair = pair_obj
.try_into()
.map_err(|_| tvm_ffi::Error::new(tvm_ffi::TYPE_ERROR, "downcast failed", ""))?;
let sum_any = pair.sum(&[])?;
let sum: i64 = sum_any.try_into()?;
assert_eq!(sum, 7);

// Cxx inheritance sample: construct derived, view as base, then convert back.
let derived_obj = stub::TestCxxClassDerived::new(11, 7, 3.5, 1.25)?;
let base: stub::TestCxxClassBase = derived_obj.clone().into();
let base_obj: tvm_ffi::object::ObjectRef = base.clone().into();
let derived_again: stub::TestCxxClassDerived = base_obj.into();
assert_eq!(base.v_i64()?, 11);
assert_eq!(base.v_i32()?, 7);
assert!((derived_again.v_f64()? - 3.5).abs() < 1e-9);
assert!((derived_again.v_f32()? - 1.25).abs() < 1e-6);

// Use object-returning wrappers and ObjectRef-based APIs
let obj = stub::make_unregistered_object()?;
let count = stub::object_use_count(obj.clone())?;
assert!(count >= 1);

// Fallback wrapper can be built from ObjectRef directly
let _wrapped: stub::TestUnregisteredObject = obj.into();

Ok(())
}
```

- Load the library once before using the APIs. `load_library` keeps loaded modules alive
in an internal global storage so exported symbols remain valid for later calls.
- Generated functions support typed signatures when possible and fall back to `Any` for dynamic calling.
- Generated object method wrappers (including constructor `new`) are resolved via type metadata rather than global function lookup.
- Generated object-returning wrappers integrate with `ObjectRef` APIs and wrapper conversions.

## Related Docs

- Rust language guide: `guides/rust_lang_guide.md`
- Rust stubgen design details (implementation-oriented): `rust/tvm-ffi-stubgen/README.md`
20 changes: 20 additions & 0 deletions rust/.rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 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.

edition = "2021"
max_width = 100
use_small_heuristics = "Default"
2 changes: 1 addition & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
# under the License.

[workspace]
members = ["tvm-ffi", "tvm-ffi-sys", "tvm-ffi-macros"]
members = ["tvm-ffi", "tvm-ffi-sys", "tvm-ffi-macros", "tvm-ffi-stubgen"]

resolver = "2"
20 changes: 20 additions & 0 deletions rust/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 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.

[toolchain]
channel = "stable"
components = ["rustfmt", "clippy"]
3 changes: 1 addition & 2 deletions rust/tvm-ffi-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,4 @@ proc-macro = true
[dependencies]
proc-macro2 = "^1.0"
quote = "^1.0"
syn = { version = "1.0.48", features = ["full", "parsing", "extra-traits"] }
proc-macro-error = "^1.0"
syn = { version = "^2.0", features = ["full"] }
7 changes: 2 additions & 5 deletions rust/tvm-ffi-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,16 @@
*/

use proc_macro::TokenStream;
use proc_macro_error::proc_macro_error;

mod object_macros;
mod utils;

#[proc_macro_error]
#[proc_macro_derive(Object, attributes(type_key, type_index))]
pub fn derive_object(input: TokenStream) -> TokenStream {
TokenStream::from(object_macros::derive_object(input))
object_macros::derive_object(input)
}

#[proc_macro_error]
#[proc_macro_derive(ObjectRef, attributes(type_key, type_index))]
pub fn derive_object_ref(input: TokenStream) -> TokenStream {
TokenStream::from(object_macros::derive_object_ref(input))
object_macros::derive_object_ref(input)
}
Loading