From 4aa86d06491ca78d3577e867cc16aa6fb0989760 Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Wed, 17 Feb 2021 21:01:22 -0800 Subject: [PATCH 1/3] Make TVM Cargo installable Rewrite the Rust Module API and change some imports causing crashes. This commit also updates the docs to remove outdated information. Fixes for version bump Update build.rs to use new tvm-build version Tweak build.rs to use release version of tvm-build Add docs Add Readme for tvm-sys crate. Fix Cargo verisions for pre-release Add README Move generated code to OUT_DIR Fix path Add descp for tvm-sys Tweak versions for publishing Tweak versions for publishing Add README for tvm-graph-rt Conform to Apache branding guidelines Fix caps Add header Remove warning Format Clean up build Turn docs back on Tweak CI WIP Remove CI changes --- rust/tvm-graph-rt/Cargo.toml | 6 +- rust/tvm-graph-rt/README.md | 20 +++ rust/tvm-macros/Cargo.toml | 2 +- rust/tvm-macros/README.md | 20 +++ rust/tvm-rt/Cargo.toml | 10 +- rust/tvm-rt/src/ndarray.rs | 4 +- rust/tvm-sys/Cargo.toml | 11 +- rust/tvm-sys/README.md | 28 ++++ rust/tvm-sys/build.rs | 136 +++++++++++++------ rust/tvm-sys/src/byte_array.rs | 6 +- rust/tvm-sys/src/lib.rs | 2 +- rust/tvm/Cargo.toml | 6 +- rust/tvm/README.md | 6 +- tests/python/topi/python/test_topi_sparse.py | 3 +- tests/scripts/task_python_docs.sh | 5 +- tests/scripts/task_rust.sh | 14 +- 16 files changed, 200 insertions(+), 79 deletions(-) create mode 100644 rust/tvm-graph-rt/README.md create mode 100644 rust/tvm-macros/README.md create mode 100644 rust/tvm-sys/README.md 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/python/topi/python/test_topi_sparse.py b/tests/python/topi/python/test_topi_sparse.py index 500384b23f2a..6db1b2aabfcd 100644 --- a/tests/python/topi/python/test_topi_sparse.py +++ b/tests/python/topi/python/test_topi_sparse.py @@ -25,7 +25,6 @@ import tvm.contrib.sparse as tvmsp from collections import namedtuple import time -import scipy.sparse as sp import tvm.testing _sparse_dense_implement = { @@ -248,6 +247,8 @@ def test_dense(): def test_sparse_dense_csr(): + import scipy.sparse as sp + M, N, K, density = 1, 17, 47, 0.2 X_np = np.random.randn(M, K).astype("float32") W_sp_np = sp.random(N, K, density=density, format="csr", dtype="float32") diff --git a/tests/scripts/task_python_docs.sh b/tests/scripts/task_python_docs.sh index 1eb75be830c3..459b680daeb1 100755 --- a/tests/scripts/task_python_docs.sh +++ b/tests/scripts/task_python_docs.sh @@ -74,8 +74,7 @@ cd .. # Rust doc cd rust -# Temp disable rust doc build -# cargo doc --workspace --no-deps +cargo doc --workspace --no-deps cd .. # Prepare the doc dir @@ -85,7 +84,7 @@ rm -f _docs/.buildinfo mkdir -p _docs/api mv docs/doxygen/html _docs/api/doxygen mv jvm/core/target/site/apidocs _docs/api/javadoc -# mv rust/target/doc _docs/api/rust +mv rust/target/doc _docs/api/rust mv web/dist/docs _docs/api/typedoc echo "Start creating the docs tarball.." 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 From 1c39912506fdf20a566a3ef85637903c64558e23 Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Thu, 1 Apr 2021 12:14:02 -0700 Subject: [PATCH 2/3] Disable docs --- tests/scripts/task_python_docs.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/scripts/task_python_docs.sh b/tests/scripts/task_python_docs.sh index 459b680daeb1..1eb75be830c3 100755 --- a/tests/scripts/task_python_docs.sh +++ b/tests/scripts/task_python_docs.sh @@ -74,7 +74,8 @@ cd .. # Rust doc cd rust -cargo doc --workspace --no-deps +# Temp disable rust doc build +# cargo doc --workspace --no-deps cd .. # Prepare the doc dir @@ -84,7 +85,7 @@ rm -f _docs/.buildinfo mkdir -p _docs/api mv docs/doxygen/html _docs/api/doxygen mv jvm/core/target/site/apidocs _docs/api/javadoc -mv rust/target/doc _docs/api/rust +# mv rust/target/doc _docs/api/rust mv web/dist/docs _docs/api/typedoc echo "Start creating the docs tarball.." From 476c5a6036a8383860f8fa98fbdd68407442c1e9 Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Wed, 7 Apr 2021 09:40:32 -0700 Subject: [PATCH 3/3] Fix --- tests/python/topi/python/test_topi_sparse.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/python/topi/python/test_topi_sparse.py b/tests/python/topi/python/test_topi_sparse.py index 6db1b2aabfcd..500384b23f2a 100644 --- a/tests/python/topi/python/test_topi_sparse.py +++ b/tests/python/topi/python/test_topi_sparse.py @@ -25,6 +25,7 @@ import tvm.contrib.sparse as tvmsp from collections import namedtuple import time +import scipy.sparse as sp import tvm.testing _sparse_dense_implement = { @@ -247,8 +248,6 @@ def test_dense(): def test_sparse_dense_csr(): - import scipy.sparse as sp - M, N, K, density = 1, 17, 47, 0.2 X_np = np.random.randn(M, K).astype("float32") W_sp_np = sp.random(N, K, density=density, format="csr", dtype="float32")