jroesch commented on a change in pull request #5527:
URL: https://github.com/apache/incubator-tvm/pull/5527#discussion_r437069856



##########
File path: include/tvm/runtime/c_runtime_api.h
##########
@@ -514,16 +523,6 @@ TVM_DLL int TVMObjectTypeKey2Index(const char* type_key, 
unsigned* out_tindex);
  */
 TVM_DLL int TVMObjectFree(TVMObjectHandle obj);
 
-/*!

Review comment:
       Restore.

##########
File path: rust/macros/src/external.rs
##########
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+use proc_macro2::Span;
+use quote::quote;
+use std::env;
+use syn::parse::{Parse, ParseStream, Result};
+
+use syn::{FnArg, Generics, Ident, Lit, Meta, NestedMeta, Pat, ReturnType, 
TraitItemMethod, Type};
+
+struct External {
+    tvm_name: String,
+    ident: Ident,
+    generics: Generics,
+    inputs: Vec<FnArg>,
+    ret_type: ReturnType,
+}
+
+impl Parse for External {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let method: TraitItemMethod = input.parse()?;
+        assert_eq!(method.attrs.len(), 1);
+        let sig = method.sig;
+        let tvm_name = method.attrs[0].parse_meta()?;
+        let tvm_name = match tvm_name {
+            Meta::List(meta_list) => {
+                let name = meta_list.path.get_ident().expect("name");
+                assert_eq!(name.to_string(), "name".to_string());
+                match meta_list.nested.first() {
+                    Some(NestedMeta::Lit(Lit::Str(lit))) => lit.value(),
+                    _ => panic!(),
+                }
+            }
+            _ => panic!(),
+        };
+        assert_eq!(method.default, None);
+        assert!(method.semi_token != None);
+        let ident = sig.ident;
+        let generics = sig.generics;
+        let inputs = sig.inputs.iter().map(|param| param.clone()).collect();
+        let ret_type = sig.output;
+
+        Ok(External {
+            tvm_name,
+            ident,
+            generics,
+            inputs,
+            ret_type,
+        })
+    }
+}
+
+struct ExternalInput {
+    externs: Vec<External>,
+}
+
+impl Parse for ExternalInput {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let mut externs: Vec<External> = Vec::new();
+
+        loop {
+            if input.is_empty() {
+                break;
+            }
+            externs.push(input.parse()?);
+        }
+
+        Ok(ExternalInput { externs })
+    }
+}
+
+pub fn macro_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    let ext_input = syn::parse_macro_input!(input as ExternalInput);
+
+    let tvm_rt_crate = if env::var("CARGO_PKG_NAME").unwrap() == "tvm-rt" {

Review comment:
       Refactor to use helper. 

##########
File path: rust/tvm-rt/README.md
##########
@@ -0,0 +1,235 @@
+<!--- 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. -->
+
+# TVM Runtime Frontend Support
+
+This crate provides an idiomatic Rust API for 
[TVM](https://github.com/apache/incubator-tvm) runtime frontend. Currently this 
requires **Nightly Rust** and tested on `rustc 1.32.0-nightly`

Review comment:
       Most of these docs will live in the `tvm` crate. I cleaned up the 
current README.md and will add more back as the bindings stablize. 

##########
File path: rust/tvm-rt/src/function.rs
##########
@@ -0,0 +1,345 @@
+/*
+ * 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.
+ */
+
+//! This module provides an idiomatic Rust API for creating and working with 
TVM functions.
+//!
+//! For calling an already registered TVM function use [`function::Builder`]
+//! To register a TVM packed function from Rust side either
+//! use [`function::register`] or the macro [`register_global_func`].
+//!
+//! See the tests and examples repository for more examples.
+
+use lazy_static::lazy_static;
+use std::convert::TryFrom;
+use std::{
+    collections::BTreeMap,
+    ffi::{CStr, CString},
+    mem::{self, MaybeUninit},
+    os::raw::{c_char, c_int},
+    ptr, slice, str,
+    sync::Mutex,
+};
+
+pub use tvm_sys::{ffi, ArgValue, RetValue};
+
+use crate::errors::Error;
+
+use super::to_boxed_fn::ToBoxedFn;
+use super::to_function::{ToFunction, Typed};
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+lazy_static! {
+    static ref GLOBAL_FUNCTIONS: Mutex<BTreeMap<String, Option<Function>>> = {
+        let mut out_size = 0 as c_int;
+        let mut names_ptr = ptr::null_mut() as *mut *const c_char;
+        check_call!(ffi::TVMFuncListGlobalNames(
+            &mut out_size as *mut _,
+            &mut names_ptr as *mut _,
+        ));
+        let names_list = unsafe { slice::from_raw_parts(names_ptr, out_size as 
usize) };
+
+        let names_list: Vec<String> =
+            names_list
+            .iter()
+            .map(|&p| unsafe { CStr::from_ptr(p).to_str().unwrap().into() })
+            .collect();
+
+        // println!("{:?}", &names_list);
+
+        let names_list = names_list
+            .into_iter()
+            .map(|p| (p, None))
+            .collect();
+
+        Mutex::new(names_list)
+    };
+}
+
+/// Wrapper around TVM function handle which includes `is_global`
+/// indicating whether the function is global or not, and `is_cloned` showing
+/// not to drop a cloned function from Rust side.
+/// The value of these fields can be accessed through their respective methods.
+#[derive(Debug, Hash)]
+pub struct Function {
+    pub(crate) handle: ffi::TVMFunctionHandle,
+    // whether the registered function is global or not.
+    is_global: bool,
+    // whether the function has been cloned from frontend or not.
+    is_cloned: bool,
+}
+
+unsafe impl Send for Function {}
+unsafe impl Sync for Function {}
+
+impl Function {
+    pub(crate) fn new(handle: ffi::TVMFunctionHandle) -> Self {
+        Function {
+            handle,
+            is_global: false,
+            is_cloned: false,
+        }
+    }
+
+    /// For a given function, it returns a function by name.
+    pub fn get<S: AsRef<str>>(name: S) -> Option<&'static Function> {
+        let mut globals = GLOBAL_FUNCTIONS.lock().unwrap();
+        globals.get_mut(name.as_ref()).and_then(|maybe_func| {
+            if maybe_func.is_none() {
+                let name = CString::new(name.as_ref()).unwrap();
+                let mut handle = ptr::null_mut() as ffi::TVMFunctionHandle;
+                check_call!(ffi::TVMFuncGetGlobal(
+                    name.as_ptr() as *const c_char,
+                    &mut handle as *mut _
+                ));
+                maybe_func.replace(Function {
+                    handle,
+                    is_global: true,
+                    is_cloned: false,
+                });
+            }
+
+            unsafe {
+                mem::transmute::<Option<&Function>, Option<&'static 
Function>>(maybe_func.as_ref())
+            }
+        })
+    }
+
+    /// Returns the underlying TVM function handle.
+    pub fn handle(&self) -> ffi::TVMFunctionHandle {
+        self.handle
+    }
+
+    /// Returns `true` if the underlying TVM function is global and `false` 
otherwise.
+    pub fn is_global(&self) -> bool {
+        self.is_global
+    }
+
+    /// Returns `true` if the underlying TVM function has been cloned
+    /// from the frontend and `false` otherwise.
+    pub fn is_cloned(&self) -> bool {
+        self.is_cloned
+    }
+
+    /// Calls the function that created from `Builder`.
+    pub fn invoke<'a>(&self, arg_buf: Vec<ArgValue<'a>>) -> Result<RetValue> {
+        let num_args = arg_buf.len();
+        let (mut values, mut type_codes): (Vec<ffi::TVMValue>, 
Vec<ffi::TVMTypeCode>) =
+            arg_buf.iter().map(|arg| arg.to_tvm_value()).unzip();
+
+        let mut ret_val = unsafe { MaybeUninit::uninit().assume_init() };
+        let mut ret_type_code = 0i32;
+        check_call!(ffi::TVMFuncCall(
+            self.handle,
+            values.as_mut_ptr(),
+            type_codes.as_mut_ptr() as *mut i32,
+            num_args as c_int,
+            &mut ret_val as *mut _,
+            &mut ret_type_code as *mut _
+        ));
+
+        Ok(RetValue::from_tvm_value(ret_val, ret_type_code as u32))
+    }
+
+    pub fn to_boxed_fn<F: ?Sized>(&'static self) -> Box<F>
+    where
+        F: ToBoxedFn,
+    {
+        F::to_boxed_fn(self)
+    }
+}
+
+impl Clone for Function {
+    fn clone(&self) -> Function {
+        Self {
+            handle: self.handle,
+            is_global: self.is_global,
+            is_cloned: true,
+        }
+    }
+}
+
+impl Drop for Function {
+    fn drop(&mut self) {
+        if !self.is_global && !self.is_cloned {
+            check_call!(ffi::TVMFuncFree(self.handle));
+        }
+    }
+}
+
+impl From<Function> for RetValue {
+    fn from(func: Function) -> RetValue {
+        RetValue::FuncHandle(func.handle)
+    }
+}
+
+impl TryFrom<RetValue> for Function {
+    type Error = Error;
+
+    fn try_from(ret_value: RetValue) -> Result<Function> {
+        match ret_value {
+            RetValue::FuncHandle(handle) => Ok(Function::new(handle)),
+            _ => Err(Error::downcast(
+                format!("{:?}", ret_value),
+                "FunctionHandle",
+            )),
+        }
+    }
+}
+
+impl<'a> From<Function> for ArgValue<'a> {
+    fn from(func: Function) -> ArgValue<'a> {
+        ArgValue::FuncHandle(func.handle)
+    }
+}
+
+impl<'a> TryFrom<ArgValue<'a>> for Function {
+    type Error = Error;
+
+    fn try_from(arg_value: ArgValue<'a>) -> Result<Function> {
+        match arg_value {
+            ArgValue::FuncHandle(handle) => Ok(Function::new(handle)),
+            _ => Err(Error::downcast(
+                format!("{:?}", arg_value),
+                "FunctionHandle",
+            )),
+        }
+    }
+}
+
+impl<'a> TryFrom<&ArgValue<'a>> for Function {
+    type Error = Error;
+
+    fn try_from(arg_value: &ArgValue<'a>) -> Result<Function> {
+        match arg_value {
+            ArgValue::FuncHandle(handle) => Ok(Function::new(*handle)),
+            _ => Err(Error::downcast(
+                format!("{:?}", arg_value),
+                "FunctionHandle",
+            )),
+        }
+    }
+}
+
+/// Registers a Rust function with an arbitrary type signature in
+/// the TVM registry.
+///
+///
+/// A function is convertible if and only if its arguments and return types 
are convertible
+/// to and from TVM values respectively.
+///
+/// Use [`register_override`] if control of overriding existing global TVM 
function
+/// is required, this function will panic if a function is already registered.
+///
+/// ## Example
+///
+/// ```
+/// # use tvm_rt::{ArgValue, RetValue};
+/// # use tvm_rt::function::{Function, Result, register};
+///
+/// fn sum(x: i64, y: i64, z: i64) -> i64 {
+///     x + y + z
+/// }
+///
+/// register(sum, "mysum".to_owned()).unwrap();
+/// let func = Function::get("mysum").unwrap();
+/// let boxed_fn = func.to_boxed_fn::<dyn Fn(i64, i64, i64) -> Result<i64>>();
+/// let ret = boxed_fn(10, 20, 30).unwrap();
+/// assert_eq!(ret, 60);
+/// ```
+pub fn register<F, I, O, S: Into<String>>(f: F, name: S) -> Result<()>
+where
+    F: ToFunction<I, O>,
+    F: Typed<I, O>,
+{
+    register_override(f, name, false)
+}
+
+/// Register a function with explicit control over whether to override an 
existing registration or not.
+///
+/// See `register` for more details on how to use the registration API.
+pub fn register_override<F, I, O, S: Into<String>>(f: F, name: S, override_: 
bool) -> Result<()>
+where
+    F: ToFunction<I, O>,
+    F: Typed<I, O>,
+{
+    let func = f.to_function();
+    let name = name.into();
+    let mut globals = GLOBAL_FUNCTIONS.lock().unwrap();
+    // Not sure about this code
+    let handle = func.handle();
+    globals.insert(name.clone(), Some(func));
+    let name = CString::new(name)?;
+    check_call!(ffi::TVMFuncRegisterGlobal(
+        name.into_raw(),
+        handle,
+        override_ as c_int
+    ));
+
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::function::Function;
+
+    static CANARY: &str = "runtime.ModuleLoadFromFile";
+
+    // #[test]
+    // fn list_global_func() {
+    //     assert!(GLOBAL_FUNCTIONS.lock().unwrap().contains_key(CANARY));
+    // }
+
+    #[test]
+    fn get_fn() {
+        assert!(Function::get(CANARY).is_some());
+        assert!(Function::get("does not exists!").is_none());
+    }
+
+    #[test]
+    fn register_and_call_closure0() {
+        use crate::function;
+        use function::Result;
+
+        fn constfn() -> i64 {
+            return 10;
+        }
+
+        function::register_override(constfn, "constfn".to_owned(), 
true).unwrap();
+        let func = Function::get("constfn").unwrap();
+        let func = func.to_boxed_fn::<dyn Fn() -> Result<i32>>();
+        let ret = func().unwrap();
+        assert_eq!(ret, 10);
+    }
+
+    // #[test]
+    // fn register_and_call_closure1() {

Review comment:
       Turn back on.

##########
File path: rust/tvm-rt/src/object/object_ptr.rs
##########
@@ -0,0 +1,306 @@
+/*
+ * 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.
+ */
+
+use std::convert::TryFrom;
+use std::ffi::CString;
+use std::ptr::NonNull;
+
+use tvm_sys::ffi::{self, /* TVMObjectFree, */ TVMObjectRetain, 
TVMObjectTypeKey2Index};
+use tvm_sys::{ArgValue, RetValue};
+
+use crate::errors::Error;
+
+type Deleter<T> = unsafe extern "C" fn(object: *mut T) -> ();
+
+#[derive(Debug)]
+#[repr(C)]
+pub struct Object {
+    pub type_index: u32,
+    pub ref_count: i32,
+    pub fdeleter: Deleter<Object>,
+}
+
+unsafe extern "C" fn delete<T: IsObject>(object: *mut Object) {
+    let typed_object: *mut T = std::mem::transmute(object);
+    T::typed_delete(typed_object);
+}
+
+fn derived_from(child_type_index: u32, parent_type_index: u32) -> bool {
+    let mut is_derived = 0;
+    crate::check_call!(ffi::TVMObjectDerivedFrom(
+        child_type_index,
+        parent_type_index,
+        &mut is_derived
+    ));
+
+    if is_derived == 0 {
+        false
+    } else {
+        true
+    }
+}
+
+impl Object {
+    fn new(type_index: u32, deleter: Deleter<Object>) -> Object {
+        Object {
+            type_index,
+            // Note: do not touch this field directly again, this is
+            // a critical section, we write a 1 to the atomic which will now
+            // be managed by the C++ atomics.
+            // In the future we should probably use C-atomcis.
+            ref_count: 1,
+            fdeleter: deleter,
+        }
+    }
+
+    fn get_type_index<T: IsObject>() -> u32 {
+        let type_key = T::TYPE_KEY;
+        let cstring = CString::new(type_key).expect("type key must not contain 
null characters");
+        if type_key == "Object" {
+            return 0;
+        } else {
+            let mut index = 0;
+            unsafe {
+                let index_ptr = std::mem::transmute(&mut index);
+                if TVMObjectTypeKey2Index(cstring.as_ptr(), index_ptr) != 0 {
+                    panic!(crate::get_last_error())
+                }
+            }
+            return index;
+        }
+    }
+
+    pub fn base_object<T: IsObject>() -> Object {
+        let index = Object::get_type_index::<T>();
+        Object::new(index, delete::<T>)
+    }
+}
+
+pub unsafe trait IsObject {
+    const TYPE_KEY: &'static str;
+
+    fn as_object<'s>(&'s self) -> &'s Object;
+
+    unsafe extern "C" fn typed_delete(_object: *mut Self) {
+        // let object = Box::from_raw(object);

Review comment:
       Restore deallocation code.

##########
File path: rust/tvm-rt/src/object/object_ptr.rs
##########
@@ -0,0 +1,306 @@
+/*
+ * 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.
+ */
+
+use std::convert::TryFrom;
+use std::ffi::CString;
+use std::ptr::NonNull;
+
+use tvm_sys::ffi::{self, /* TVMObjectFree, */ TVMObjectRetain, 
TVMObjectTypeKey2Index};
+use tvm_sys::{ArgValue, RetValue};
+
+use crate::errors::Error;
+
+type Deleter<T> = unsafe extern "C" fn(object: *mut T) -> ();
+
+#[derive(Debug)]
+#[repr(C)]
+pub struct Object {
+    pub type_index: u32,
+    pub ref_count: i32,
+    pub fdeleter: Deleter<Object>,
+}
+
+unsafe extern "C" fn delete<T: IsObject>(object: *mut Object) {
+    let typed_object: *mut T = std::mem::transmute(object);
+    T::typed_delete(typed_object);
+}
+
+fn derived_from(child_type_index: u32, parent_type_index: u32) -> bool {
+    let mut is_derived = 0;
+    crate::check_call!(ffi::TVMObjectDerivedFrom(
+        child_type_index,
+        parent_type_index,
+        &mut is_derived
+    ));
+
+    if is_derived == 0 {
+        false
+    } else {
+        true
+    }
+}
+
+impl Object {
+    fn new(type_index: u32, deleter: Deleter<Object>) -> Object {
+        Object {
+            type_index,
+            // Note: do not touch this field directly again, this is
+            // a critical section, we write a 1 to the atomic which will now
+            // be managed by the C++ atomics.
+            // In the future we should probably use C-atomcis.
+            ref_count: 1,
+            fdeleter: deleter,
+        }
+    }
+
+    fn get_type_index<T: IsObject>() -> u32 {
+        let type_key = T::TYPE_KEY;
+        let cstring = CString::new(type_key).expect("type key must not contain 
null characters");
+        if type_key == "Object" {
+            return 0;
+        } else {
+            let mut index = 0;
+            unsafe {
+                let index_ptr = std::mem::transmute(&mut index);
+                if TVMObjectTypeKey2Index(cstring.as_ptr(), index_ptr) != 0 {
+                    panic!(crate::get_last_error())
+                }
+            }
+            return index;
+        }
+    }
+
+    pub fn base_object<T: IsObject>() -> Object {
+        let index = Object::get_type_index::<T>();
+        Object::new(index, delete::<T>)
+    }
+}
+
+pub unsafe trait IsObject {
+    const TYPE_KEY: &'static str;
+
+    fn as_object<'s>(&'s self) -> &'s Object;
+
+    unsafe extern "C" fn typed_delete(_object: *mut Self) {
+        // let object = Box::from_raw(object);
+        // drop(object)
+    }
+}
+
+unsafe impl IsObject for Object {
+    const TYPE_KEY: &'static str = "Object";
+
+    fn as_object<'s>(&'s self) -> &'s Object {
+        self
+    }
+}
+
+#[repr(C)]
+pub struct ObjectPtr<T> {
+    pub ptr: NonNull<T>,
+}
+
+impl ObjectPtr<Object> {
+    fn from_raw(object_ptr: *mut Object) -> Option<ObjectPtr<Object>> {
+        let non_null = NonNull::new(object_ptr);
+        non_null.map(|ptr| ObjectPtr { ptr })
+    }
+}
+
+impl<T> Clone for ObjectPtr<T> {
+    fn clone(&self) -> Self {
+        unsafe {
+            let raw_ptr = std::mem::transmute(self.ptr);
+            assert_eq!(TVMObjectRetain(raw_ptr), 0);
+            ObjectPtr { ptr: self.ptr }
+        }
+    }
+}
+

Review comment:
       Restore drop impl.

##########
File path: rust/tvm-rt/src/to_boxed_fn.rs
##########
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+//! This module provides an idiomatic Rust API for creating and working with 
TVM functions.
+//!
+//! For calling an already registered TVM function use [`function::Builder`]

Review comment:
       Update comment. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to