diff --git a/rust/tvm-graph-rt/Cargo.toml b/rust/tvm-graph-rt/Cargo.toml index 5c492393a75e..c8db44eadf9b 100644 --- a/rust/tvm-graph-rt/Cargo.toml +++ b/rust/tvm-graph-rt/Cargo.toml @@ -17,7 +17,7 @@ [package] name = "tvm-graph-rt" -version = "0.1.0" +version = "0.1.0-alpha" license = "Apache-2.0" description = "A static graph executor for TVM." repository = "https://github.com/apache/tvm" @@ -38,8 +38,8 @@ nom = "5.0" num_cpus = "1.10" serde = { version = "^1.0", features = ["derive"] } serde_json = "^1.0" -tvm-sys = { version = "0.1", path = "../tvm-sys" } -tvm-macros = { version = "0.1", path = "../tvm-macros" } +tvm-sys = { version = "0.1.1-alpha", path = "../tvm-sys" } +tvm-macros = { version = "0.1.1-alpha", path = "../tvm-macros" } [target.'cfg(not(any(target_arch = "wasm32", target_env = "sgx")))'.dependencies] libloading = "0.5" diff --git a/rust/tvm-graph-rt/README.md b/rust/tvm-graph-rt/README.md new file mode 100644 index 000000000000..f1355b042882 --- /dev/null +++ b/rust/tvm-graph-rt/README.md @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + +# tvm-graph-rt + +An implementation of TVM's graph runtime in Rust. See `tvm` crate for more documentation. diff --git a/rust/tvm-macros/Cargo.toml b/rust/tvm-macros/Cargo.toml index 37275d6a941e..4300cb3f1dcb 100644 --- a/rust/tvm-macros/Cargo.toml +++ b/rust/tvm-macros/Cargo.toml @@ -17,7 +17,7 @@ [package] name = "tvm-macros" -version = "0.1.1" +version = "0.1.1-alpha" license = "Apache-2.0" description = "Procedural macros of the TVM crate." repository = "https://github.com/apache/tvm" diff --git a/rust/tvm-macros/README.md b/rust/tvm-macros/README.md new file mode 100644 index 000000000000..8a7c4b301524 --- /dev/null +++ b/rust/tvm-macros/README.md @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + +# tvm-macros + +The procedural macro implementations for TVM crates, see `tvm` crate for more documentation. diff --git a/rust/tvm-rt/Cargo.toml b/rust/tvm-rt/Cargo.toml index 13c05373f6b6..eb49558ec6ce 100644 --- a/rust/tvm-rt/Cargo.toml +++ b/rust/tvm-rt/Cargo.toml @@ -17,7 +17,7 @@ [package] name = "tvm-rt" -version = "0.1.0" +version = "0.1.0-alpha" license = "Apache-2.0" description = "Rust bindings for the TVM runtime API." repository = "https://github.com/apache/tvm" @@ -30,22 +30,22 @@ edition = "2018" [features] default = ["dynamic-linking"] -dynamic-linking = ["tvm-sys/bindings"] -static-linking = [] +dynamic-linking = ["tvm-sys/dynamic-linking"] +static-linking = ["tvm-sys/static-linking"] blas = ["ndarray/blas"] [dependencies] thiserror = "^1.0" ndarray = "0.12" num-traits = "0.2" -tvm-macros = { version = "0.1", path = "../tvm-macros" } +tvm-macros = { version = "0.1.1-alpha", path = "../tvm-macros" } paste = "0.1" mashup = "0.1" once_cell = "^1.3.1" memoffset = "0.5.6" [dependencies.tvm-sys] -version = "0.1" +version = "0.1.1-alpha" default-features = false path = "../tvm-sys/" diff --git a/rust/tvm-rt/src/ndarray.rs b/rust/tvm-rt/src/ndarray.rs index 4c48ce50b4f3..0e2d2830615f 100644 --- a/rust/tvm-rt/src/ndarray.rs +++ b/rust/tvm-rt/src/ndarray.rs @@ -287,7 +287,7 @@ impl NDArray { check_call!(ffi::TVMArrayCopyFromBytes( self.as_raw_dltensor(), data.as_ptr() as *mut _, - data.len() * mem::size_of::() + (data.len() * mem::size_of::()) as _, )); } @@ -296,7 +296,7 @@ impl NDArray { check_call!(ffi::TVMArrayCopyToBytes( self.as_raw_dltensor(), data.as_ptr() as *mut _, - self.size(), + self.size() as _, )); } diff --git a/rust/tvm-sys/Cargo.toml b/rust/tvm-sys/Cargo.toml index 2952aa4938d7..c7ee98fc455a 100644 --- a/rust/tvm-sys/Cargo.toml +++ b/rust/tvm-sys/Cargo.toml @@ -17,14 +17,16 @@ [package] name = "tvm-sys" -version = "0.1.0" +version = "0.1.1-alpha" authors = ["TVM Contributors"] license = "Apache-2.0" edition = "2018" +description = "Low level bindings to TVM's cross language API." [features] -default = [] -bindings = [] +default = ["dynamic-linking"] +static-linking = [] +dynamic-linking = [] [dependencies] thiserror = "^1.0" @@ -33,5 +35,6 @@ ndarray = "0.12" enumn = "^0.1" [build-dependencies] -bindgen = { version="0.51", default-features=false } +bindgen = { version="0.57", default-features = false, features = ["runtime"] } anyhow = "^1.0" +tvm-build = "0.1" diff --git a/rust/tvm-sys/README.md b/rust/tvm-sys/README.md new file mode 100644 index 000000000000..735a9431aa33 --- /dev/null +++ b/rust/tvm-sys/README.md @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + +# tvm-sys + +The low level bindings to TVM's C APIs for interacting with the runtime, +the cross-language object system, and packed function API. + +These will generate bindings to TVM, if you set `TVM_HOME` variable before +building it will instruct the bindings to use your source tree, if not the +crate will use `tvm-build` in order to build a sandboxed version of the library. + +This feature is intended to simplify the installation for brand new TVM users +by trying to automate the build process as much as possible. diff --git a/rust/tvm-sys/build.rs b/rust/tvm-sys/build.rs index 159023463e8d..d80bd9598246 100644 --- a/rust/tvm-sys/build.rs +++ b/rust/tvm-sys/build.rs @@ -19,65 +19,113 @@ extern crate bindgen; -use std::env; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; +use tvm_build::BuildConfig; + +/// The necessary information for detecting a TVM installation. +struct TVMInstall { + source_path: PathBuf, + build_path: PathBuf, +} + +/// Find the TVM install using the provided path. +fn find_using_tvm_path>(tvm_path: P) -> Result { + Ok(TVMInstall { + source_path: tvm_path.as_ref().into(), + build_path: tvm_path.as_ref().into(), + }) +} + +#[allow(unused)] +fn if_unset, V: AsRef>(k: K, v: V) -> Result<()> { + match std::env::var(k.as_ref()) { + Ok(other) if other != "" => { + println!( + "cargo:warning=Using existing environment variable setting {:?}={:?}", + k.as_ref(), + v.as_ref() + ); + } + _ => std::env::set_var(k, v), + } + + Ok(()) +} + +/// Find a TVM installation using TVM build by either first installing or detecting. +fn find_using_tvm_build() -> Result { + let mut build_config = BuildConfig::default(); + build_config.repository = Some("https://github.com/apache/tvm".to_string()); + build_config.branch = Some(option_env!("TVM_BRANCH").unwrap_or("main").into()); + let build_result = tvm_build::build(build_config)?; + let source_path = build_result.revision.source_path(); + let build_path = build_result.revision.build_path(); + Ok(TVMInstall { + source_path, + build_path, + }) +} fn main() -> Result<()> { - let tvm_home = option_env!("TVM_HOME") - .map::, _>(|s: &str| Ok(str::to_string(s))) - .unwrap_or_else(|| { - let crate_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .canonicalize() - .with_context(|| { - format!( - "failed to cannonicalize() CARGO_MANIFEST_DIR={}", - env!("CARGO_MANIFEST_DIR") - ) - })?; - - Ok(crate_dir - .parent() - .with_context(|| { - format!( - "failed to find parent of CARGO_MANIFEST_DIR={}", - env!("CARGO_MANIFEST_DIR") - ) - })? - .parent() - .with_context(|| { - format!( - "failed to find the parent of the parent of CARGO MANIFEST_DIR={}", - env!("CARGO_MANIFEST_DIR") - ) - })? - .to_str() - .context("failed to convert to strings")? - .to_string()) - })?; - - if cfg!(feature = "bindings") { - println!("cargo:rerun-if-env-changed=TVM_HOME"); + let TVMInstall { + source_path, + build_path, + } = match option_env!("TVM_HOME") { + Some(tvm_path) if tvm_path != "" => find_using_tvm_path(tvm_path), + _ => find_using_tvm_build(), + }?; + + // If the TVM_HOME environment variable changed, the LLVM_CONFIG_PATH environment variable + // changed, the build directory or headers have changed we need to rebuild the Rust bindings. + println!("cargo:rerun-if-env-changed=TVM_HOME"); + println!("cargo:rerun-if-env-changed=LLVM_CONFIG_PATH"); + println!("cargo:rerun-if-changed={}", build_path.display()); + println!("cargo:rerun-if-changed={}/include", source_path.display()); + + if cfg!(feature = "static-linking") { + println!("cargo:rustc-link-lib=static=tvm"); + // TODO(@jroesch): move this to tvm-build as library_path? + println!( + "cargo:rustc-link-search=native={}/build", + build_path.display() + ); + } + + if cfg!(feature = "dynamic-linking") { println!("cargo:rustc-link-lib=dylib=tvm"); - println!("cargo:rustc-link-search=native={}/build", tvm_home); + println!( + "cargo:rustc-link-search=native={}/build", + build_path.display() + ); } + let runtime_api = source_path.join("include/tvm/runtime/c_runtime_api.h"); + let backend_api = source_path.join("include/tvm/runtime/c_backend_api.h"); + let source_path = source_path.display().to_string(); + let dlpack_include = format!("-I{}/3rdparty/dlpack/include/", source_path); + let tvm_include = format!("-I{}/include/", source_path); + + let out_file = PathBuf::from(std::env::var("OUT_DIR")?).join("c_runtime_api.rs"); + // @see rust-bindgen#550 for `blacklist_type` bindgen::Builder::default() - .header(format!("{}/include/tvm/runtime/c_runtime_api.h", tvm_home)) - .header(format!("{}/include/tvm/runtime/c_backend_api.h", tvm_home)) - .clang_arg(format!("-I{}/3rdparty/dlpack/include/", tvm_home)) - .clang_arg(format!("-I{}/include/", tvm_home)) + .header(runtime_api.display().to_string()) + .header(backend_api.display().to_string()) + .clang_arg(dlpack_include) + .clang_arg(tvm_include) .blacklist_type("max_align_t") .layout_tests(false) .derive_partialeq(true) .derive_eq(true) .derive_default(true) .generate() - .map_err(|()| anyhow::anyhow!("failed to generate bindings"))? - .write_to_file(PathBuf::from("src/c_runtime_api.rs")) - .context("failed to write bindings")?; + .map_err(|()| { + anyhow::anyhow!("bindgen failed to generate the Rust bindings for the C API") + })? + .write_to_file(out_file) + .context("failed to write the generated Rust binding to disk")?; Ok(()) } diff --git a/rust/tvm-sys/src/byte_array.rs b/rust/tvm-sys/src/byte_array.rs index 0f027713ede0..4b005abee7ef 100644 --- a/rust/tvm-sys/src/byte_array.rs +++ b/rust/tvm-sys/src/byte_array.rs @@ -41,12 +41,12 @@ pub struct ByteArray { impl ByteArray { /// Gets the underlying byte-array pub fn data(&self) -> &'static [u8] { - unsafe { std::slice::from_raw_parts(self.array.data as *const u8, self.array.size) } + unsafe { std::slice::from_raw_parts(self.array.data as *const u8, self.array.size as _) } } /// Gets the length of the underlying byte-array pub fn len(&self) -> usize { - self.array.size + self.array.size as _ } /// Converts the underlying byte-array to `Vec` @@ -66,7 +66,7 @@ impl> From for ByteArray { ByteArray { array: TVMByteArray { data: arg.as_ptr() as *const c_char, - size: arg.len(), + size: arg.len() as _, }, } } diff --git a/rust/tvm-sys/src/lib.rs b/rust/tvm-sys/src/lib.rs index 8ed6f37f5f48..f874e672bb66 100644 --- a/rust/tvm-sys/src/lib.rs +++ b/rust/tvm-sys/src/lib.rs @@ -32,7 +32,7 @@ pub mod ffi { use std::os::raw::{c_char, c_int, c_void}; - include!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/c_runtime_api.rs")); + include!(concat!(env!("OUT_DIR"), "/c_runtime_api.rs")); pub type BackendPackedCFunc = extern "C" fn( args: *const TVMValue, diff --git a/rust/tvm/Cargo.toml b/rust/tvm/Cargo.toml index 9438f340f78f..ca32226e0ac5 100644 --- a/rust/tvm/Cargo.toml +++ b/rust/tvm/Cargo.toml @@ -17,7 +17,7 @@ [package] name = "tvm" -version = "0.1.0" +version = "0.1.1-alpha" license = "Apache-2.0" description = "Rust frontend support for TVM" repository = "https://github.com/apache/tvm" @@ -36,7 +36,7 @@ blas = ["ndarray/blas"] python = ["pyo3"] [dependencies.tvm-rt] -version = "0.1" +version = "0.1.0-alpha" default-features = false path = "../tvm-rt/" @@ -46,7 +46,7 @@ anyhow = "^1.0" lazy_static = "1.1" ndarray = "0.12" num-traits = "0.2" -tvm-macros = { version = "*", path = "../tvm-macros/" } +tvm-macros = { version = "0.1.1-alpha", path = "../tvm-macros/" } paste = "0.1" mashup = "0.1" once_cell = "^1.3.1" diff --git a/rust/tvm/README.md b/rust/tvm/README.md index b518f93195b7..b1bb4687679e 100644 --- a/rust/tvm/README.md +++ b/rust/tvm/README.md @@ -17,7 +17,7 @@ # TVM -This crate provides an idiomatic Rust API for [TVM](https://github.com/apache/tvm). +This crate provides an idiomatic Rust API for [Apache TVM](https://github.com/apache/tvm). The code works on **Stable Rust** and is tested against `rustc 1.47`. You can find the API Documentation [here](https://tvm.apache.org/docs/api/rust/tvm/index.html). @@ -52,3 +52,7 @@ and usage is welcome and encouraged. If you want to discuss design issues check Please follow the TVM [install](https://tvm.apache.org/docs/install/index.html) instructions, `export TVM_HOME=/path/to/tvm` and add `libtvm_runtime` to your `LD_LIBRARY_PATH`. *Note:* To run the end-to-end examples and tests, `tvm` and `topi` need to be added to your `PYTHONPATH` or it's automatic via an Anaconda environment when it is installed individually. + +### Disclaimers + +*Apache TVM is a top level project from the Apache software foundation. Please refer to the official Apache TVM website for Apache source releases. Apache TVM, Apache, the Apache feather, and the Apache TVM project logo are either trademarks or registered trademarks of the Apache Software Foundation.* diff --git a/tests/scripts/task_rust.sh b/tests/scripts/task_rust.sh index c40585b62b47..9ddd1f2b5a4b 100755 --- a/tests/scripts/task_rust.sh +++ b/tests/scripts/task_rust.sh @@ -45,18 +45,16 @@ cargo fmt -- --check cd $RUST_DIR/tvm-sys # First we test w/o the bindings feature on. cargo build -cargo test --tests +cargo test --features static-linking --tests # Second we test w/ the bindings feature on. -cargo build --features bindings -cargo test --features bindings --tests +cargo build --features dynamic-linking +cargo test --features dynamic-linking --tests # Next we test the runtime API. cd $RUST_DIR/tvm-rt - # Build and run the tests. -cargo build -cargo test --tests +cargo test # Next we test the graph executor crate. cd $RUST_DIR/tvm-graph-rt @@ -89,11 +87,10 @@ cd - # and compiler bindings. cd $RUST_DIR/tvm -cargo test --tests -- --test-threads=1 +cargo test # run basic tests on cpu cd tests/basics -cargo build --features cpu cargo run --features cpu # uncomment when have more CI resources # cargo build --features gpu @@ -101,6 +98,7 @@ cargo run --features cpu # fi cd - +# TODO(@jroesch): I believe this is no longer true, refactor in follow up PR. # run callback tests separately: https://discuss.tvm.ai/t/are-global-functions-need-to-be-accessed-in-separate-processes/1075 cd tests/callback cargo build