Skip to content

Conversation

@Blealtan
Copy link

@Blealtan Blealtan commented Feb 7, 2026

This pull request introduces a new Rust crate, tvm-ffi-stubgen, to the workspace. The crate is a command-line tool for generating Rust stubs from tvm-ffi metadata. The implementation includes argument parsing, FFI helpers, metadata schema parsing, code generation logic, and the main entry point. The workspace is updated to include this new crate.

Notably, this PR can now automatically generate Rust binding for TVM and TileLang, enabling Rust code to manually assemble a legal TileLang AST.

Relation with existing Python stubgen

This Rust stubgen only supports a Rust equivalent to the "STUB_INIT is on" mode in the existing Python stubgen, which yields a compilable Rust crate that wraps a TVM-FFI DLL.

Addition of the tvm-ffi-stubgen crate

  • Added tvm-ffi-stubgen to the workspace members in Cargo.toml, ensuring it is built as part of the Rust workspace.
  • Created tvm-ffi-stubgen/Cargo.toml with dependencies, package metadata, and bin target for the new stub generator.

Core implementation of the stub generator

  • Implemented command-line interface parsing in cli.rs using clap, supporting options for output directory, DLLs, prefix, and other configuration.
  • Developed FFI utilities in ffi.rs for loading DLLs, querying global function/type names, and extracting type information from the loaded libraries.
  • Defined internal data models for Rust type and function representations in model.rs, supporting code generation from metadata.
  • Added schema parsing in schema.rs to interpret type schemas from JSON metadata.
  • Provided the main library logic in lib.rs, orchestrating argument parsing, FFI calls, filtering, module construction, and writing generated files.
  • Implemented the binary entry point in main.rs to parse arguments and invoke the main logic.

Integration test

It tests the functionality through loading, compiling and running a crate that invokes add_one from libtvm_ffi_testing.so. The testing is yet to be fully automated; it now relies on human to supply the correct paths.

Non-stubgen changes

High-level rationale

The non-stubgen changes make the Rust FFI runtime capable of:

  • carrying dynamic object type information for ObjectRef and wrapper types,
  • supporting richer typed argument passing and higher-arity typed functions,
  • providing core containers (Map) and AnyValue for typed signatures,
  • enabling reflection-backed field access for generated wrappers.

File-by-file details

Expand...

rust/tvm-ffi-macros/src/object_macros.rs

  • Added a special AnyCompatible implementation for ObjectRef so it uses the dynamic type_index stored in the object header, not the container type_index.
  • Expanded the non-ObjectRef AnyCompatible implementation to allow type hierarchy checks via TVMFFIGetTypeInfo ancestors.
  • Rationale: ObjectRef can hold any concrete object; copying and casting must reflect the runtime type. The ancestor walk allows derived objects to satisfy base-typed arguments without forcing an exact type_index match.

rust/tvm-ffi/src/any.rs

  • Introduced AnyValue as a managed Any wrapper for typed signatures.
  • Added Any::as_raw_ffi_any to support AnyCompatible conversions.
  • Rationale: stubgen-generated APIs need a stable Any-compatible value type for typed calls and return values.

rust/tvm-ffi/src/type_traits.rs

  • Added AnyCompatible for AnyValue using TVMFFIAnyViewToOwnedAny for copy semantics.
  • Rationale: allows AnyValue to participate in the same Any-compatible conversions as other typed values.

rust/tvm-ffi/src/object_wrapper.rs

  • New runtime module for stubgen-generated object wrappers.
  • Provides ObjectWrapper trait, reflection-backed FieldGetter, and AnyCompatible for wrappers.
  • FieldGetter resolves field offsets/getters via TVMFFIGetTypeInfo and supports typed conversions via TryFrom.
  • Rationale: the generated wrappers need shared runtime support for field access and Any conversions without emitting large per-type code.

rust/tvm-ffi/src/macros.rs

  • Added define_object_wrapper! macro to declare stubgen wrapper types that implement ObjectWrapper and Any conversions.
  • Extended into_typed_fn macro arity from 8 to 12 arguments.
  • Rationale: generated stubs require a minimal wrapper macro and wider typed function coverage.

rust/tvm-ffi/src/function_internal.rs

  • Added IntoArgHolder implementations for ObjectRef, AnyValue, DLDevice, DLDataType, Array, Map, Option, and ObjectWrapper types.
  • Added ArgIntoRef implementations for the same (where appropriate).
  • Extended tuple handling to 12 arguments for both IntoArgHolderTuple and TupleAsPackedArgs.
  • Rationale: typed function calls must accept these value categories without extra manual wrapping.

rust/tvm-ffi/src/collections/map.rs

  • New Map<K, V> object wrapper with FFI-backed operations: new, len/is_empty, contains_key, get/get_optional/get_or, and iterator.
  • Implements AnyCompatible and TryFrom for Any/AnyView.
  • Rationale: Map is required by TVM APIs and is emitted by stubgen, so the runtime needs a stable, typed Map wrapper.

rust/tvm-ffi/src/collections/mod.rs

  • Added map module export.

rust/tvm-ffi/src/error.rs

  • Added From for Error.
  • Rationale: simplifies conversion paths in typed APIs where Infallible can appear.

rust/tvm-ffi/src/lib.rs

  • Exported AnyValue, Map, and ObjectWrapper, and added object_wrapper module.
  • Rationale: makes new runtime types available to stubgen output and user code.

rust/Cargo.toml

  • Added tvm-ffi-stubgen to the Rust workspace members.
  • Rationale: enables building the stubgen tool alongside the FFI crates.

This PR is vibed with GPT-5.2-Codex through Copilot and then reviewed by human.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Blealtan, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the Rust ecosystem within TVM by introducing a dedicated stub generator. This new tool automates the creation of Rust FFI bindings, allowing developers to seamlessly integrate TVM's C++ functions and types into Rust projects. By generating a compilable Rust crate that wraps TVM-FFI DLLs, it streamlines the process of calling TVM functions from Rust, improving developer experience and reducing manual FFI boilerplate.

Highlights

  • New Rust Stub Generator: Introduces tvm-ffi-stubgen, a new Rust crate and command-line tool designed to generate Rust stubs from tvm-ffi metadata.
  • FFI Integration: The generator supports creating compilable Rust crates that wrap TVM-FFI DLLs, mirroring the "STUB_INIT is on" mode of the existing Python stubgen.
  • Core Logic: Implements robust argument parsing using clap, FFI helpers for dynamic library loading and metadata querying, internal data models for Rust types, and schema parsing for type information.
  • Code Generation: Includes comprehensive logic for generating Cargo.toml, lib.rs, functions.rs, and types.rs files for the new stub crate.
  • Workspace Integration: The new tvm-ffi-stubgen crate has been added to the main Rust workspace.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • rust/Cargo.toml
    • Added "tvm-ffi-stubgen" to the workspace members.
  • rust/tvm-ffi-stubgen/Cargo.toml
    • New file defining the tvm-ffi-stubgen package.
    • Specifies dependencies: clap, libloading, serde, serde_json, and tvm-ffi.
    • Configures the binary target tvm-ffi-stubgen.
  • rust/tvm-ffi-stubgen/src/cli.rs
    • New file implementing command-line argument parsing using clap.
    • Defines arguments for output directory, DLL paths, initialization prefixes, crate names, TVM-FFI path, and overwrite options.
  • rust/tvm-ffi-stubgen/src/ffi.rs
    • New file containing FFI utility functions.
    • Includes Array struct for handling TVM-FFI objects with reference counting.
    • Provides functions to load DLLs, list global function names, list registered type keys, query array size and items, get type info, and retrieve global function metadata.
  • rust/tvm-ffi-stubgen/src/generate.rs
    • New file containing the core code generation logic.
    • Functions for building type maps, function entries, type entries, and module structures.
    • Includes rendering logic for Cargo.toml, lib.rs, functions.rs, and types.rs for the generated stub crate.
    • Implements identifier sanitization and PascalCase conversion.
  • rust/tvm-ffi-stubgen/src/lib.rs
    • New file serving as the library entry point for the stub generator.
    • Orchestrates the overall stub generation process, including loading DLLs, filtering functions and types, building models, and writing generated files.
  • rust/tvm-ffi-stubgen/src/main.rs
    • New file, the main binary entry point for the tvm-ffi-stubgen command-line tool.
    • Parses arguments and calls the run function from lib.rs.
  • rust/tvm-ffi-stubgen/src/model.rs
    • New file defining data structures for representing Rust types (RustType, RustTypeKind), function signatures (FunctionSig), and generated code elements (FunctionGen, MethodGen, TypeGen, ModuleNode).
  • rust/tvm-ffi-stubgen/src/schema.rs
    • New file for parsing type schemas.
    • Defines TypeSchema and TypeSchemaJson for deserializing type metadata from JSON.
    • Provides functions to extract and parse type schemas.
  • rust/tvm-ffi-stubgen/src/tests.rs
    • New file containing integration tests for the tvm-ffi-stubgen.
    • Includes logic to resolve testing DLLs, create unique temporary directories, write integration test code, and run generated tests using cargo test.
  • rust/tvm-ffi-stubgen/src/utils.rs
    • New file with utility functions.
    • Includes functions for normalizing prefixes, ensuring output directory existence, locating the default tvm-ffi path, and calculating relative paths.
Activity
  • The pull request was initially "vibed" (generated) using GPT-5.2-CodeX via Copilot.
  • The generated code was subsequently reviewed and refined by a human.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is a great addition, introducing a Rust stub generator which will improve the development experience for Rust users of tvm-ffi. The implementation is comprehensive, covering CLI parsing, FFI interaction, code generation, and robust integration testing. The code is well-structured and follows good Rust practices. I have a couple of suggestions for improvement, mainly around reducing code duplication by leveraging existing types from tvm-ffi and simplifying some of the string manipulation logic.

Comment on lines 28 to 134
#[repr(C)]
#[derive(Debug)]
pub(crate) struct Array {
handle: TVMFFIObjectHandle,
}

extern "C" {
fn TVMFFIObjectIncRef(handle: TVMFFIObjectHandle) -> i32;
fn TVMFFIObjectDecRef(handle: TVMFFIObjectHandle) -> i32;
}

impl Clone for Array {
fn clone(&self) -> Self {
unsafe {
TVMFFIObjectIncRef(self.handle);
}
Self { handle: self.handle }
}
}

impl Drop for Array {
fn drop(&mut self) {
unsafe {
TVMFFIObjectDecRef(self.handle);
}
}
}

unsafe impl tvm_ffi::type_traits::AnyCompatible for Array {
fn type_str() -> String {
"ffi.Array".to_string()
}

unsafe fn copy_to_any_view(src: &Self, data: &mut TVMFFIAny) {
data.type_index = TVMFFITypeIndex::kTVMFFIArray as i32;
data.small_str_len = 0;
data.data_union.v_obj = src.handle as *mut tvm_ffi::tvm_ffi_sys::TVMFFIObject;
}

unsafe fn move_to_any(src: Self, data: &mut TVMFFIAny) {
data.type_index = TVMFFITypeIndex::kTVMFFIArray as i32;
data.small_str_len = 0;
data.data_union.v_obj = src.handle as *mut tvm_ffi::tvm_ffi_sys::TVMFFIObject;
}

unsafe fn check_any_strict(data: &TVMFFIAny) -> bool {
data.type_index == TVMFFITypeIndex::kTVMFFIArray as i32
}

unsafe fn copy_from_any_view_after_check(data: &TVMFFIAny) -> Self {
let handle = data.data_union.v_obj as TVMFFIObjectHandle;
TVMFFIObjectIncRef(handle);
Self { handle }
}

unsafe fn move_from_any_after_check(data: &mut TVMFFIAny) -> Self {
let handle = data.data_union.v_obj as TVMFFIObjectHandle;
Self { handle }
}

unsafe fn try_cast_from_any_view(data: &TVMFFIAny) -> Result<Self, ()> {
if data.type_index == TVMFFITypeIndex::kTVMFFIArray as i32 {
Ok(Self::copy_from_any_view_after_check(data))
} else {
Err(())
}
}
}

impl ArgIntoRef for Array {
type Target = Array;
fn to_ref(&self) -> &Self::Target {
self
}
}

impl<'a> ArgIntoRef for &'a Array {
type Target = Array;
fn to_ref(&self) -> &Self::Target {
self
}
}

impl IntoArgHolder for Array {
type Target = Array;
fn into_arg_holder(self) -> Self::Target {
self
}
}

impl<'a> IntoArgHolder for &'a Array {
type Target = &'a Array;
fn into_arg_holder(self) -> Self::Target {
self
}
}

impl TryFrom<Any> for Array {
type Error = Error;
fn try_from(value: Any) -> Result<Self, Self::Error> {
if let Some(ret) = value.try_as::<Array>() {
Ok(ret)
} else {
Err(Error::new(TYPE_ERROR, "Expected ffi.Array", ""))
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The local Array struct and its implementations (Clone, Drop, AnyCompatible, TryFrom<Any>, etc.) appear to duplicate functionality that should already be present in the tvm-ffi crate's tvm_ffi::Array. Using the existing tvm_ffi::Array would significantly reduce code duplication, simplify this module, and improve maintainability.

The tvm-ffi crate is already a dependency, so you should be able to use tvm_ffi::Array directly. This would also allow you to remove the helper functions array_size and array_get_item, as tvm_ffi::Array likely provides an iterator or similar methods for element access.

For example, list_registered_type_keys could be simplified to something like:

pub(crate) fn list_registered_type_keys() -> FfiResult<Vec<String>> {
    let get_keys = Function::get_global("ffi.GetRegisteredTypeKeys")?;
    let keys_any = get_keys.call_tuple_with_len::<0, _>(())?;
    let keys: tvm_ffi::Array<tvm_ffi::String> = keys_any.try_into()?;
    Ok(keys.iter().map(|s| s.as_str().to_string()).collect())
}

Comment on lines 237 to 243
let mut remainder = full_name;
if !prefix.is_empty() && remainder.starts_with(prefix) {
remainder = &remainder[prefix.len()..];
} else if !prefix.is_empty() && remainder.starts_with(prefix.trim_end_matches('.')) {
remainder = &remainder[prefix.trim_end_matches('.').len()..];
remainder = remainder.trim_start_matches('.');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for stripping the prefix in split_name is a bit complex. Since the prefix is normalized to always end with a . (if not empty) in lib.rs, the else if branch seems redundant. You can simplify this logic using strip_prefix, which also makes the intent clearer.

    let remainder = if !prefix.is_empty() {
        full_name.strip_prefix(prefix).unwrap_or(full_name)
    } else {
        full_name
    };

@Blealtan Blealtan marked this pull request as ready for review February 9, 2026 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants