You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tvm.apache.org by tq...@apache.org on 2025/03/12 13:25:25 UTC
(tvm) branch refactor-s0 updated (70bbe36645 -> dbf7c372f3)
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a change to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
discard 70bbe36645 fix lint and minor compile
discard 16603265fc lint
discard 3bb75d4cb1 Fix deleter to match latest
discard cc061117c0 Pass compile
discard 6127599320 clang format
discard 66f993be83 pass expr first stab
discard 130f39e5f8 Pass all runtime
discard 5df4dec4b2 pass object and runtime compile
discard 6c60fd30e1 Use specific import via using and not do global namespace
discard 5d3d586e94 pass basic disco session compile
discard ad6d5fbdf0 pass ndarray compile
discard 8e7df79382 pass packed func compile
discard 5315cb9a3f Fix NDArray compile
discard 6c16f46c9a Move over container on new object
discard b9e1fd8d25 First step, pass string compile
discard 02127d4039 CMake links ok
discard ac79f9e8cf [REFACTOR] Migration starting pt
discard acd5e5ca26 Fix ffi
discard 8503919cdb [FFI] Enhance the deleter and reflection to be aware of padding
discard 68f2cead2c Cleanup and focus on the integrated version
discard 695ac175b9 [FFI] bool, device, tensor, raw str
discard 61249b46e7 [FFI] Initial reflection support
discard 00fd84b40d [FFI] Global function registry
discard f29e76f9f4 [FFI] Formalize SafeCall handling
discard 63c86e58a6 [FFI] Initial map support
discard d5b942f94e [FFI] Optional Support
discard 3076b1f260 [FFI] Introduce String support
discard 98e38b3368 [FFI][REFACTOR] Cleanup naming convention and lint
discard 38ffb87768 [FFI] Introduce array support
discard 67986d3369 [FFI] Improve Any coverage to include ObjectPtr
discard dfbf0e587e [FFI] Function support
discard 1ae001e701 [FFI] Any support
discard 89ccf3f967 [FFI] Object type hierachy cast and check support
discard 2a461fa42f [FFI] Add libbacktrace support
discard 45f4ff950f [FFI] ObjectRef based Error
discard efd253828e [FFI] Enable ObjectPtr and tests
discard ea2549e0a8 [FFI] Initial step to create ffi from the original tvm source
discard 5b3ff33039 [FFI] Initial Code Commit
add cf6e61b285 [Relax] Allow ingesting Upsample module from torch.export either using Size or Scale Factor argument (#17721)
add 6e8c367fd7 [Relax] Add torch exported program ingestion capability for Tensor.detach(), Tensor.copy_, and aten.lift_fresh_copy (#17723)
add e74f7eee32 [Relax] Add support to ingest Tensor.expand_as() (#17724)
add 6afa62cbe1 Fix typos in comments and strings (#17736)
add 148737b1ff [IR] Compact Functor vtable (#17731)
add 36a92a4ae5 Remove Google Analytics (#17734)
add 37fe645f94 [Relax] Ingest Tensor.clamp from torch export (#17725)
add 363ebb453b [BF16] Support ndarray.asnumpy() to bfloat16 tensor natively using ml_dtypes (#17739)
add 64dae9de9b [Relax][PyTorch] Support elu, celu, selu ops for ExportedProgram importer (#17738)
add 6b89f9537c [Codegen][CUDA] Fix codegen of cast among vector bfloat16, fp8 and fp4 (#17741)
add cd92392d34 [Refactor] Clean up Relay references in the codebase (#17733)
new 73ea198a2a [FFI] Initial Code Commit
new af35e25d38 [FFI] Initial step to create ffi from the original tvm source
new 1ab674c646 [FFI] Enable ObjectPtr and tests
new 8c2a94802d [FFI] ObjectRef based Error
new d3280ddb40 [FFI] Add libbacktrace support
new d3145e864a [FFI] Object type hierachy cast and check support
new 4d03e51f29 [FFI] Any support
new 854abf58be [FFI] Function support
new 9a5cca4645 [FFI] Improve Any coverage to include ObjectPtr
new 40b5ad587e [FFI] Introduce array support
new a2b5e9a726 [FFI][REFACTOR] Cleanup naming convention and lint
new 344ab18437 [FFI] Introduce String support
new 441ceae16e [FFI] Optional Support
new 514a798b2c [FFI] Initial map support
new 36f8f8f2b0 [FFI] Formalize SafeCall handling
new 463425f90b [FFI] Global function registry
new 27ffeda002 [FFI] Initial reflection support
new 7b7b1e7f1c [FFI] bool, device, tensor, raw str
new 4472f6ec3b Cleanup and focus on the integrated version
new 661cf0bd48 [FFI] Enhance the deleter and reflection to be aware of padding
new 238342a200 Fix ffi
new 969b2192b3 [REFACTOR] Migration starting pt
new dce37a83b3 CMake links ok
new 84d7aaa81b First step, pass string compile
new b8a65dbab0 Move over container on new object
new e4dd9cc031 Fix NDArray compile
new 24fce43a82 pass packed func compile
new 125da2e573 pass ndarray compile
new d263641f10 pass basic disco session compile
new 8b0ac0deb6 Use specific import via using and not do global namespace
new fb62f9034d pass object and runtime compile
new fb524a6935 Pass all runtime
new 958938ee34 pass expr first stab
new eb0199921a clang format
new a16c253f0e Pass compile
new 69b4611a64 Fix deleter to match latest
new ffc1ec0ebd lint
new dbf7c372f3 fix lint and minor compile
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (70bbe36645)
\
N -- N -- N refs/heads/refactor-s0 (dbf7c372f3)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 38 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
apps/ios_rpc/tvmrpc/RPCServer.mm | 30 +--
apps/ios_rpc/tvmrpc/TVMRuntime.mm | 2 +-
cmake/config.cmake | 6 +-
cmake/modules/CUDA.cmake | 6 +-
cmake/modules/contrib/ArmComputeLib.cmake | 2 +-
cmake/modules/contrib/BNNS.cmake | 3 +-
cmake/modules/contrib/DNNL.cmake | 7 +-
cmake/modules/contrib/Mrvl.cmake | 5 -
cmake/modules/contrib/TensorRT.cmake | 2 +-
docs/arch/index.rst | 8 +-
docs/arch/pass_infra.rst | 18 +-
docs/conf.py | 1 -
docs/contribute/document.rst | 2 +-
include/tvm/arith/iter_affine_map.h | 2 +-
include/tvm/ir/expr.h | 4 +-
include/tvm/ir/transform.h | 10 +-
include/tvm/ir/type.h | 4 +-
include/tvm/ir/type_functor.h | 1 +
include/tvm/meta_schedule/builder.h | 4 +-
include/tvm/node/functor.h | 28 ++-
include/tvm/relax/dataflow_pattern.h | 2 +
include/tvm/relax/dataflow_pattern_functor.h | 2 +-
include/tvm/relax/expr.h | 4 +-
include/tvm/relax/expr_functor.h | 3 +-
include/tvm/relax/op_attr_types.h | 2 +-
include/tvm/relax/struct_info_functor.h | 1 +
include/tvm/relax/type.h | 2 +-
include/tvm/target/virtual_device.h | 4 +-
include/tvm/tir/analysis.h | 2 +-
include/tvm/tir/block_dependence_info.h | 4 +-
include/tvm/tir/builtin.h | 2 +-
include/tvm/tir/expr_functor.h | 1 +
include/tvm/tir/op.h | 2 +-
include/tvm/tir/stmt_functor.h | 1 +
include/tvm/topi/transform.h | 4 +-
python/gen_requirements.py | 6 +-
python/setup.py | 6 -
python/tvm/__init__.py | 2 +-
python/tvm/contrib/cutlass/build.py | 4 +-
python/tvm/contrib/mrvl.py | 2 +-
.../msc/framework/tensorflow/frontend/translate.py | 2 +-
.../msc/framework/torch/frontend/translate.py | 2 +-
python/tvm/ir/transform.py | 2 +-
python/tvm/ir/type.py | 2 +-
python/tvm/meta_schedule/builder/builder.py | 4 +-
python/tvm/relax/dpl/pattern.py | 5 +-
.../frontend/torch/base_fx_graph_translator.py | 116 +++++++++++-
.../frontend/torch/exported_program_translator.py | 52 +++++-
python/tvm/relax/frontend/torch/fx_translator.py | 4 +-
python/tvm/runtime/ndarray.py | 7 +-
python/tvm/testing/utils.py | 42 -----
python/tvm/tir/analysis/analysis.py | 4 -
rust/tvm-rt/src/module.rs | 2 +-
src/ir/attr_functor.h | 1 +
src/ir/transform.cc | 2 +-
src/node/structural_equal.cc | 4 +-
src/relax/backend/contrib/cutlass/codegen.cc | 2 +-
src/relax/ir/dataflow_pattern.cc | 3 +-
src/relax/ir/dataflow_pattern_functor.cc | 2 +-
src/relax/ir/py_expr_functor.cc | 3 +
src/relax/ir/transform.cc | 1 -
src/runtime/const_loader_module.cc | 5 +-
src/runtime/contrib/dnnl/dnnl.cc | 1 -
src/runtime/contrib/dnnl/dnnl_json_runtime.cc | 2 +-
src/runtime/contrib/mrvl/mrvl_hw_runtime.cc | 4 +-
src/runtime/contrib/mrvl/mrvl_runtime.cc | 4 +-
src/runtime/contrib/tensorrt/tensorrt_builder.cc | 4 +-
src/runtime/contrib/tensorrt/tensorrt_ops.cc | 4 +-
src/runtime/contrib/tensorrt/tensorrt_ops.h | 7 +-
src/support/scalars.h | 4 +-
src/target/llvm/codegen_hexagon.cc | 2 +-
src/target/llvm/codegen_llvm.cc | 2 +-
src/target/source/codegen_cuda.cc | 58 ++++--
src/target/source/literal/cuda_half_t.h | 151 ++++++++++-----
tests/lint/cpplint.sh | 1 -
.../python/codegen/test_target_codegen_cuda_fp8.py | 49 ++++-
tests/python/codegen/test_target_codegen_llvm.py | 43 +----
.../contrib/test_hexagon/test_meta_schedule.py | 138 +-------------
.../contrib/test_hexagon/test_relax_integration.py | 2 +
tests/python/relax/test_from_exported_to_cuda.py | 205 ++++++++++++++++++++-
.../relax/test_frontend_from_exported_program.py | 185 ++++++++++++++++++-
tests/python/relax/test_frontend_from_fx.py | 64 +++++--
.../test_transform_merge_composite_functions.py | 3 -
tests/python/tvmscript/test_tvmscript_roundtrip.py | 3 +-
web/emcc/tvmjs_support.cc | 2 +-
web/emcc/wasm_runtime.cc | 2 +-
86 files changed, 931 insertions(+), 477 deletions(-)
(tvm) 14/38: [FFI] Initial map support
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 514a798b2c659c1117a97607d3dd93881246db56
Author: tqchen <ti...@gmail.com>
AuthorDate: Fri Sep 13 20:05:09 2024 -0400
[FFI] Initial map support
---
ffi/include/tvm/ffi/any.h | 74 +-
ffi/include/tvm/ffi/base_details.h | 16 +
ffi/include/tvm/ffi/c_api.h | 2 +-
ffi/include/tvm/ffi/container/map.h | 1364 +++++++++++++++++++++++++++++++++++
ffi/include/tvm/ffi/type_traits.h | 4 +
ffi/tests/example/test_map.cc | 153 ++++
6 files changed, 1606 insertions(+), 7 deletions(-)
diff --git a/ffi/include/tvm/ffi/any.h b/ffi/include/tvm/ffi/any.h
index b7181b2297..225bbfd21b 100644
--- a/ffi/include/tvm/ffi/any.h
+++ b/ffi/include/tvm/ffi/any.h
@@ -57,7 +57,11 @@ class AnyView {
/*!
* \brief Reset any view to None
*/
- void reset() { data_.type_index = TypeIndex::kTVMFFINone; }
+ void reset() {
+ data_.type_index = TypeIndex::kTVMFFINone;
+ // invariance: always set the union padding part to 0
+ data_.v_int64 = 0;
+ }
/*!
* \brief Swap this array with another Object
* \param other The other Object
@@ -68,12 +72,18 @@ class AnyView {
/*! \return the internal type index */
int32_t type_index() const { return data_.type_index; }
// default constructors
- AnyView() { data_.type_index = TypeIndex::kTVMFFINone; }
+ AnyView() {
+ data_.type_index = TypeIndex::kTVMFFINone;
+ data_.v_int64 = 0;
+ }
~AnyView() = default;
// constructors from any view
AnyView(const AnyView&) = default;
AnyView& operator=(const AnyView&) = default;
- AnyView(AnyView&& other) : data_(other.data_) { other.data_.type_index = TypeIndex::kTVMFFINone; }
+ AnyView(AnyView&& other) : data_(other.data_) {
+ other.data_.type_index = TypeIndex::kTVMFFINone;
+ other.data_.v_int64 = 0;
+ }
AnyView& operator=(AnyView&& other) {
// copy-and-swap idiom
AnyView(std::move(other)).swap(*this); // NOLINT(*)
@@ -161,6 +171,7 @@ class Any {
details::ObjectUnsafe::DecRefObjectInAny(&data_);
}
data_.type_index = TVMFFITypeIndex::kTVMFFINone;
+ data_.v_int64 = 0;
}
/*!
* \brief Swap this array with another Object
@@ -172,7 +183,10 @@ class Any {
/*! \return the internal type index */
int32_t type_index() const { return data_.type_index; }
// default constructors
- Any() { data_.type_index = TypeIndex::kTVMFFINone; }
+ Any() {
+ data_.type_index = TypeIndex::kTVMFFINone;
+ data_.v_int64 = 0;
+ }
~Any() { this->reset(); }
// constructors from Any
Any(const Any& other) : data_(other.data_) {
@@ -180,7 +194,10 @@ class Any {
details::ObjectUnsafe::IncRefObjectInAny(&data_);
}
}
- Any(Any&& other) : data_(other.data_) { other.data_.type_index = TypeIndex::kTVMFFINone; }
+ Any(Any&& other) : data_(other.data_) {
+ other.data_.type_index = TypeIndex::kTVMFFINone;
+ other.data_.v_int64 = 0;
+ }
Any& operator=(const Any& other) {
// copy-and-swap idiom
Any(other).swap(*this); // NOLINT(*)
@@ -242,9 +259,12 @@ class Any {
void MoveToTVMFFIAny(TVMFFIAny* result) {
*result = data_;
data_.type_index = TypeIndex::kTVMFFINone;
+ data_.v_int64 = 0;
}
- friend class details::AnyUnsafe;
+ friend struct details::AnyUnsafe;
+ friend struct AnyHash;
+ friend struct AnyEqual;
};
// layout assert to ensure we can freely cast between the two types
@@ -280,6 +300,48 @@ struct AnyUnsafe : public ObjectUnsafe {
};
} // namespace details
+/*! \brief String-aware ObjectRef equal functor */
+struct AnyHash {
+ /*!
+ * \brief Calculate the hash code of an Any
+ * \param a The given Any
+ * \return Hash code of a, string hash for strings and pointer address otherwise.
+ */
+ size_t operator()(const Any& src) const {
+ uint64_t val_hash = [&]() -> uint64_t {
+ if (src.data_.type_index == TypeIndex::kTVMFFIStr) {
+ const StringObj* src_str = details::AnyUnsafe::ConvertAfterCheck<const StringObj*>(src);
+ return details::StableHashBytes(src_str->data, src_str->size);
+ } else {
+ return std::hash<int64_t>()(src.data_.v_int64);
+ }
+ }();
+ return static_cast<size_t>(details::StableHashCombine(src.data_.type_index, val_hash));
+ }
+};
+
+/*! \brief String-aware Any hash functor */
+struct AnyEqual {
+ /*!
+ * \brief Check if the two Any are equal
+ * \param lhs left operand.
+ * \param rhs right operand
+ * \return String equality if both are strings, pointer address equality otherwise.
+ */
+ bool operator()(const Any& lhs, const Any& rhs) const {
+ if (lhs.data_.type_index != rhs.data_.type_index) return false;
+ // byte equivalence
+ if (lhs.data_.v_int64 == rhs.data_.v_int64) return true;
+ // specialy handle string hash
+ if (lhs.data_.type_index == TypeIndex::kTVMFFIStr) {
+ const StringObj* lhs_str = details::AnyUnsafe::ConvertAfterCheck<const StringObj*>(lhs);
+ const StringObj* rhs_str = details::AnyUnsafe::ConvertAfterCheck<const StringObj*>(rhs);
+ return String::memncmp(lhs_str->data, rhs_str->data, lhs_str->size, rhs_str->size) == 0;
+ }
+ return false;
+ }
+};
+
// Downcast an object
// NOTE: the implementation is put in here to avoid cyclic dependency
// with the
diff --git a/ffi/include/tvm/ffi/base_details.h b/ffi/include/tvm/ffi/base_details.h
index c817a6d817..cf825fdea7 100644
--- a/ffi/include/tvm/ffi/base_details.h
+++ b/ffi/include/tvm/ffi/base_details.h
@@ -134,6 +134,22 @@ void for_each(const F& f, Args&&... args) { // NOLINT(*)
for_each_dispatcher<sizeof...(Args) == 0, 0, F>::run(f, std::forward<Args>(args)...);
}
+/*!
+ * \brief hash an object and combines uint64_t key with previous keys
+ *
+ * This hash function is stable across platforms.
+ *
+ * \param key The left operand.
+ * \param value The right operand.
+ * \return the combined result.
+ */
+template <typename T, std::enable_if_t<std::is_convertible<T, uint64_t>::value, bool> = true>
+TVM_FFI_INLINE uint64_t StableHashCombine(uint64_t key, const T& value) {
+ // XXX: do not use std::hash in this function. This hash must be stable
+ // across different platforms and std::hash is implementation dependent.
+ return key ^ (uint64_t(value) + 0x9e3779b9 + (key << 6) + (key >> 2));
+}
+
/*!
* \brief Hash the binary bytes
* \param data The data pointer
diff --git a/ffi/include/tvm/ffi/c_api.h b/ffi/include/tvm/ffi/c_api.h
index 5c40fedda8..20579bbe06 100644
--- a/ffi/include/tvm/ffi/c_api.h
+++ b/ffi/include/tvm/ffi/c_api.h
@@ -78,7 +78,7 @@ typedef enum {
kTVMFFIStaticObjectBegin = 64,
kTVMFFIObject = 64,
kTVMFFIArray = 65,
- kTVMFFIDict = 66,
+ kTVMFFIMap = 66,
kTVMFFIError = 67,
kTVMFFIFunc = 68,
kTVMFFIStr = 69,
diff --git a/ffi/include/tvm/ffi/container/map.h b/ffi/include/tvm/ffi/container/map.h
new file mode 100644
index 0000000000..81957c13d0
--- /dev/null
+++ b/ffi/include/tvm/ffi/container/map.h
@@ -0,0 +1,1364 @@
+/*
+ * 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.
+ */
+
+/*!
+ * \file tvm/ffi/container/map.h
+ * \brief Runtime Map container types.
+ */
+#ifndef TVM_FFI_CONTAINER_MAP_H_
+#define TVM_FFI_CONTAINER_MAP_H_
+
+#include <tvm/ffi/any.h>
+#include <tvm/ffi/container/container_details.h>
+#include <tvm/ffi/memory.h>
+#include <tvm/ffi/object.h>
+
+#include <algorithm>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+
+namespace tvm {
+namespace ffi {
+
+#if TVM_FFI_DEBUG_WITH_ABI_CHANGE
+#define TVM_FFI_MAP_FAIL_IF_CHANGED() \
+ TVM_FFI_ICHECK(state_marker == self->state_marker) << "Concurrent modification of the Map";
+#else
+#define TVM_FFI_MAP_FAIL_IF_CHANGED()
+#endif // TVM_FFI_DEBUG_WITH_ABI_CHANGE
+
+/*! \brief Shared content of all specializations of hash map */
+class MapNode : public Object {
+ public:
+ /*! \brief Type of the keys in the hash map */
+ using key_type = Any;
+ /*! \brief Type of the values in the hash map */
+ using mapped_type = Any;
+ /*! \brief Type of value stored in the hash map */
+ using KVType = std::pair<Any, Any>;
+ /*! \brief Iterator class */
+ class iterator;
+
+ static_assert(std::is_standard_layout<KVType>::value, "KVType is not standard layout");
+ static_assert(sizeof(KVType) == 32, "sizeof(KVType) incorrect");
+
+ static constexpr const int32_t _type_index = TypeIndex::kTVMFFIMap;
+ static constexpr const char* _type_key = "object.Map";
+ static const constexpr bool _type_final = true;
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(MapNode, Object);
+
+ /*!
+ * \brief Number of elements in the SmallMapNode
+ * \return The result
+ */
+ size_t size() const { return size_; }
+ /*!
+ * \brief Count the number of times a key exists in the hash map
+ * \param key The indexing key
+ * \return The result, 0 or 1
+ */
+ size_t count(const key_type& key) const;
+ /*!
+ * \brief Index value associated with a key, throw exception if the key does not exist
+ * \param key The indexing key
+ * \return The const reference to the value
+ */
+ const mapped_type& at(const key_type& key) const;
+ /*!
+ * \brief Index value associated with a key, throw exception if the key does not exist
+ * \param key The indexing key
+ * \return The mutable reference to the value
+ */
+ mapped_type& at(const key_type& key);
+ /*! \return begin iterator */
+ iterator begin() const;
+ /*! \return end iterator */
+ iterator end() const;
+ /*!
+ * \brief Index value associated with a key
+ * \param key The indexing key
+ * \return The iterator of the entry associated with the key, end iterator if not exists
+ */
+ iterator find(const key_type& key) const;
+ /*!
+ * \brief Erase the entry associated with the iterator
+ * \param position The iterator
+ */
+ void erase(const iterator& position);
+ /*!
+ * \brief Erase the entry associated with the key, do nothing if not exists
+ * \param key The indexing key
+ */
+ void erase(const key_type& key) { erase(find(key)); }
+
+ class iterator {
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = int64_t;
+ using value_type = KVType;
+ using pointer = KVType*;
+ using reference = KVType&;
+/*! \brief Default constructor */
+#if TVM_FFI_DEBUG_WITH_ABI_CHANGE
+ iterator() : state_marker(0), index(0), self(nullptr) {}
+#else
+ iterator() : index(0), self(nullptr) {}
+#endif // TVM_FFI_DEBUG_WITH_ABI_CHANGE
+ /*! \brief Compare iterators */
+ bool operator==(const iterator& other) const {
+ TVM_FFI_MAP_FAIL_IF_CHANGED()
+ return index == other.index && self == other.self;
+ }
+ /*! \brief Compare iterators */
+ bool operator!=(const iterator& other) const { return !(*this == other); }
+ /*! \brief De-reference iterators */
+ pointer operator->() const;
+ /*! \brief De-reference iterators */
+ reference operator*() const {
+ TVM_FFI_MAP_FAIL_IF_CHANGED()
+ return *((*this).operator->());
+ }
+ /*! \brief Prefix self increment, e.g. ++iter */
+ iterator& operator++();
+ /*! \brief Prefix self decrement, e.g. --iter */
+ iterator& operator--();
+ /*! \brief Suffix self increment */
+ iterator operator++(int) {
+ TVM_FFI_MAP_FAIL_IF_CHANGED()
+ iterator copy = *this;
+ ++(*this);
+ return copy;
+ }
+ /*! \brief Suffix self decrement */
+ iterator operator--(int) {
+ TVM_FFI_MAP_FAIL_IF_CHANGED()
+ iterator copy = *this;
+ --(*this);
+ return copy;
+ }
+
+ protected:
+#if TVM_FFI_DEBUG_WITH_ABI_CHANGE
+ uint64_t state_marker;
+ /*! \brief Construct by value */
+ iterator(uint64_t index, const MapNode* self)
+ : state_marker(self->state_marker), index(index), self(self) {}
+
+#else
+ iterator(uint64_t index, const MapNode* self) : index(index), self(self) {}
+#endif // TVM_FFI_DEBUG_WITH_ABI_CHANGE
+ /*! \brief The position on the array */
+ uint64_t index;
+ /*! \brief The container it points to */
+ const MapNode* self;
+
+ friend class DenseMapNode;
+ friend class SmallMapNode;
+ };
+ /*!
+ * \brief Create an empty container
+ * \return The object created
+ */
+ static inline ObjectPtr<MapNode> Empty();
+
+ protected:
+#if TVM_FFI_DEBUG_WITH_ABI_CHANGE
+ uint64_t state_marker;
+#endif // TVM_FFI_DEBUG_WITH_ABI_CHANGE
+ /*!
+ * \brief Create the map using contents from the given iterators.
+ * \param first Begin of iterator
+ * \param last End of iterator
+ * \tparam IterType The type of iterator
+ * \return ObjectPtr to the map created
+ */
+ template <typename IterType>
+ static inline ObjectPtr<Object> CreateFromRange(IterType first, IterType last);
+ /*!
+ * \brief InsertMaybeReHash an entry into the given hash map
+ * \param kv The entry to be inserted
+ * \param map The pointer to the map, can be changed if re-hashing happens
+ */
+ static inline void InsertMaybeReHash(const KVType& kv, ObjectPtr<Object>* map);
+ /*!
+ * \brief Create an empty container with elements copying from another SmallMapNode
+ * \param from The source container
+ * \return The object created
+ */
+ static inline ObjectPtr<MapNode> CopyFrom(MapNode* from);
+ /*! \brief number of slots minus 1 */
+ uint64_t slots_;
+ /*! \brief number of entries in the container */
+ uint64_t size_;
+ // Reference class
+ template <typename, typename, typename>
+ friend class Map;
+};
+
+/*! \brief A specialization of small-sized hash map */
+class SmallMapNode : public MapNode,
+ public details::InplaceArrayBase<SmallMapNode, MapNode::KVType> {
+ private:
+ static constexpr uint64_t kInitSize = 2;
+ static constexpr uint64_t kMaxSize = 4;
+
+ public:
+ using MapNode::iterator;
+ using MapNode::KVType;
+
+ /*! \brief Defaults to the destructor of InplaceArrayBase */
+ ~SmallMapNode() = default;
+ /*!
+ * \brief Count the number of times a key exists in the SmallMapNode
+ * \param key The indexing key
+ * \return The result, 0 or 1
+ */
+ size_t count(const key_type& key) const { return find(key).index < size_; }
+ /*!
+ * \brief Index value associated with a key, throw exception if the key does not exist
+ * \param key The indexing key
+ * \return The const reference to the value
+ */
+ const mapped_type& at(const key_type& key) const {
+ iterator itr = find(key);
+ if (itr.index >= size_) {
+ TVM_FFI_THROW(IndexError) << "key is not in Map";
+ }
+ return itr->second;
+ }
+ /*!
+ * \brief Index value associated with a key, throw exception if the key does not exist
+ * \param key The indexing key
+ * \return The mutable reference to the value
+ */
+ mapped_type& at(const key_type& key) {
+ iterator itr = find(key);
+ if (itr.index >= size_) {
+ TVM_FFI_THROW(IndexError) << "key is not in Map";
+ }
+ return itr->second;
+ }
+ /*! \return begin iterator */
+ iterator begin() const { return iterator(0, this); }
+ /*! \return end iterator */
+ iterator end() const { return iterator(size_, this); }
+ /*!
+ * \brief Index value associated with a key
+ * \param key The indexing key
+ * \return The iterator of the entry associated with the key, end iterator if not exists
+ */
+ iterator find(const key_type& key) const {
+ KVType* ptr = static_cast<KVType*>(AddressOf(0));
+ for (uint64_t i = 0; i < size_; ++i, ++ptr) {
+ if (AnyEqual()(ptr->first, key)) {
+ return iterator(i, this);
+ }
+ }
+ return iterator(size_, this);
+ }
+ /*!
+ * \brief Erase the entry associated with the iterator
+ * \param position The iterator
+ */
+ void erase(const iterator& position) { Erase(position.index); }
+
+ private:
+ /*!
+ * \brief Remove a position in SmallMapNode
+ * \param index The position to be removed
+ */
+ void Erase(const uint64_t index) {
+ if (index >= size_) {
+ return;
+ }
+ KVType* begin = static_cast<KVType*>(AddressOf(0));
+ KVType* last = begin + (size_ - 1);
+ if (index + 1 == size_) {
+ last->first.Any::~Any();
+ last->second.Any::~Any();
+ } else {
+ *(begin + index) = std::move(*last);
+ }
+ size_ -= 1;
+ }
+ /*!
+ * \brief Create an empty container
+ * \param n Number of empty slots
+ * \return The object created
+ */
+ static ObjectPtr<SmallMapNode> Empty(uint64_t n = kInitSize) {
+ using ::tvm::ffi::make_inplace_array_object;
+ ObjectPtr<SmallMapNode> p = make_inplace_array_object<SmallMapNode, KVType>(n);
+ p->size_ = 0;
+ p->slots_ = n;
+ return p;
+ }
+ /*!
+ * \brief Create an empty container initialized with a given range
+ * \param n Number of empty slots
+ * \param first begin of iterator
+ * \param last end of iterator
+ * \tparam IterType The type of iterator
+ * \return The object created
+ */
+ template <typename IterType>
+ static ObjectPtr<SmallMapNode> CreateFromRange(uint64_t n, IterType first, IterType last) {
+ ObjectPtr<SmallMapNode> p = Empty(n);
+ KVType* ptr = static_cast<KVType*>(p->AddressOf(0));
+ for (; first != last; ++first, ++p->size_) {
+ new (ptr++) KVType(*first);
+ }
+ return p;
+ }
+ /*!
+ * \brief Create an empty container with elements copying from another SmallMapNode
+ * \param from The source container
+ * \return The object created
+ */
+ static ObjectPtr<SmallMapNode> CopyFrom(SmallMapNode* from) {
+ KVType* first = static_cast<KVType*>(from->AddressOf(0));
+ KVType* last = first + from->size_;
+ return CreateFromRange(from->size_, first, last);
+ }
+ /*!
+ * \brief InsertMaybeReHash an entry into the given hash map
+ * \param kv The entry to be inserted
+ * \param map The pointer to the map, can be changed if re-hashing happens
+ */
+ static void InsertMaybeReHash(const KVType& kv, ObjectPtr<Object>* map) {
+ SmallMapNode* map_node = static_cast<SmallMapNode*>(map->get());
+ iterator itr = map_node->find(kv.first);
+ if (itr.index < map_node->size_) {
+ itr->second = kv.second;
+ return;
+ }
+ if (map_node->size_ < map_node->slots_) {
+ KVType* ptr = static_cast<KVType*>(map_node->AddressOf(map_node->size_));
+ new (ptr) KVType(kv);
+ ++map_node->size_;
+ return;
+ }
+ uint64_t next_size = std::max(map_node->slots_ * 2, uint64_t(kInitSize));
+ next_size = std::min(next_size, uint64_t(kMaxSize));
+ TVM_FFI_ICHECK_GT(next_size, map_node->slots_);
+ ObjectPtr<Object> new_map = CreateFromRange(next_size, map_node->begin(), map_node->end());
+ InsertMaybeReHash(kv, &new_map);
+ *map = std::move(new_map);
+ }
+ /*!
+ * \brief Increment the pointer
+ * \param index The pointer to be incremented
+ * \return The increased pointer
+ */
+ uint64_t IncItr(uint64_t index) const { return index + 1 < size_ ? index + 1 : size_; }
+ /*!
+ * \brief Decrement the pointer
+ * \param index The pointer to be decremented
+ * \return The decreased pointer
+ */
+ uint64_t DecItr(uint64_t index) const { return index > 0 ? index - 1 : size_; }
+ /*!
+ * \brief De-reference the pointer
+ * \param index The pointer to be dereferenced
+ * \return The result
+ */
+ KVType* DeRefItr(uint64_t index) const { return static_cast<KVType*>(AddressOf(index)); }
+ /*! \brief A size function used by InplaceArrayBase */
+ uint64_t GetSize() const { return size_; }
+
+ protected:
+ friend class MapNode;
+ friend class DenseMapNode;
+ friend class details::InplaceArrayBase<SmallMapNode, MapNode::KVType>;
+};
+
+/*! \brief A specialization of hash map that implements the idea of array-based hash map.
+ * Another reference implementation can be found [1].
+ *
+ * A. Overview
+ *
+ * DenseMapNode did several improvements over traditional separate chaining hash,
+ * in terms of cache locality, memory footprints and data organization.
+ *
+ * A1. Implicit linked list. For better cache locality, instead of using linked list
+ * explicitly for each bucket, we store list data into a single array that spans contiguously
+ * in memory, and then carefully design access patterns to make sure most of them fall into
+ * a single cache line.
+ *
+ * A2. 1-byte metadata. There is only 1 byte overhead for each slot in the array to indexing and
+ * traversal. This can be divided in 3 parts.
+ * 1) Reserved code: (0b11111111)_2 indicates a slot is empty; (0b11111110)_2 indicates protected,
+ * which means the slot is empty but not allowed to be written.
+ * 2) If not empty or protected, the highest bit is used to indicate whether data in the slot is
+ * head of a linked list.
+ * 3) The rest 7 bits are used as the "next pointer" (i.e. pointer to the next element). On 64-bit
+ * architecture, an ordinary pointer can take up to 8 bytes, which is not acceptable overhead when
+ * dealing with 16-byte ObjectRef pairs. Based on a commonly noticed fact that the lists are
+ * relatively short (length <= 3) in hash maps, we follow [1]'s idea that only allows the pointer to
+ * be one of the 126 possible values, i.e. if the next element of i-th slot is (i + x)-th element,
+ * then x must be one of the 126 pre-defined values.
+ *
+ * A3. Data blocking. We organize the array in the way that every 16 elements forms a data block.
+ * The 16-byte metadata of those 16 elements are stored together, followed by the real data, i.e.
+ * 16 key-value pairs.
+ *
+ * B. Implementation details
+ *
+ * B1. Power-of-2 table size and Fibonacci Hashing. We use power-of-two as table size to avoid
+ * modulo for more efficient arithmetics. To make the hash-to-slot mapping distribute more evenly,
+ * we use the Fibonacci Hashing [2] trick.
+ *
+ * B2. Traverse a linked list in the array.
+ * 1) List head. Assume Fibonacci Hashing maps a given key to slot i, if metadata at slot i
+ * indicates that it is list head, then we found the head; otherwise the list is empty. No probing
+ * is done in this procedure. 2) Next element. To find the next element of a non-empty slot i, we
+ * look at the last 7 bits of the metadata at slot i. If they are all zeros, then it is the end of
+ * list; otherwise, we know that the next element is (i + candidates[the-last-7-bits]).
+ *
+ * B3. InsertMaybeReHash an element. Following B2, we first traverse the linked list to see if this
+ * element is in the linked list, and if not, we put it at the end by probing the next empty
+ * position in one of the 126 candidate positions. If the linked list does not even exist, but the
+ * slot for list head has been occupied by another linked list, we should find this intruder another
+ * place.
+ *
+ * B4. Quadratic probing with triangle numbers. In open address hashing, it is provable that probing
+ * with triangle numbers can traverse power-of-2-sized table [3]. In our algorithm, we follow the
+ * suggestion in [1] that also use triangle numbers for "next pointer" as well as sparing for list
+ * head.
+ *
+ * [1] https://github.com/skarupke/flat_hash_map
+ * [2] https://programmingpraxis.com/2018/06/19/fibonacci-hash/
+ * [3] https://fgiesen.wordpress.com/2015/02/22/triangular-numbers-mod-2n/
+ */
+class DenseMapNode : public MapNode {
+ private:
+ /*! \brief The number of elements in a memory block */
+ static constexpr int kBlockCap = 16;
+ /*! \brief Maximum load factor of the hash map */
+ static constexpr double kMaxLoadFactor = 0.99;
+ /*! \brief Binary representation of the metadata of an empty slot */
+ static constexpr uint8_t kEmptySlot = uint8_t(0b11111111);
+ /*! \brief Binary representation of the metadata of a protected slot */
+ static constexpr uint8_t kProtectedSlot = uint8_t(0b11111110);
+ /*! \brief Number of probing choices available */
+ static constexpr int kNumJumpDists = 126;
+ /*! \brief Head of the implicit linked list */
+ struct ListNode;
+ /*! \brief POD type of a block of memory */
+ struct Block {
+ uint8_t bytes[kBlockCap + kBlockCap * sizeof(KVType)];
+ };
+ static_assert(sizeof(Block) == kBlockCap * (sizeof(KVType) + 1), "sizeof(Block) incorrect");
+ static_assert(std::is_standard_layout<Block>::value, "Block is not standard layout");
+
+ public:
+ using MapNode::iterator;
+
+ /*!
+ * \brief Destroy the DenseMapNode
+ */
+ ~DenseMapNode() { this->Reset(); }
+ /*! \return The number of elements of the key */
+ size_t count(const key_type& key) const { return !Search(key).IsNone(); }
+ /*!
+ * \brief Index value associated with a key, throw exception if the key does not exist
+ * \param key The indexing key
+ * \return The const reference to the value
+ */
+ const mapped_type& at(const key_type& key) const { return At(key); }
+ /*!
+ * \brief Index value associated with a key, throw exception if the key does not exist
+ * \param key The indexing key
+ * \return The mutable reference to the value
+ */
+ mapped_type& at(const key_type& key) { return At(key); }
+ /*!
+ * \brief Index value associated with a key
+ * \param key The indexing key
+ * \return The iterator of the entry associated with the key, end iterator if not exists
+ */
+ iterator find(const key_type& key) const {
+ ListNode node = Search(key);
+ return node.IsNone() ? end() : iterator(node.index, this);
+ }
+ /*!
+ * \brief Erase the entry associated with the iterator
+ * \param position The iterator
+ */
+ void erase(const iterator& position) {
+ uint64_t index = position.index;
+ if (position.self != nullptr && index <= this->slots_) {
+ Erase(ListNode(index, this));
+ }
+ }
+ /*! \return begin iterator */
+ iterator begin() const {
+ if (slots_ == 0) {
+ return iterator(0, this);
+ }
+ for (uint64_t index = 0; index <= slots_; ++index) {
+ if (!ListNode(index, this).IsEmpty()) {
+ return iterator(index, this);
+ }
+ }
+ return iterator(slots_ + 1, this);
+ }
+ /*! \return end iterator */
+ iterator end() const { return slots_ == 0 ? iterator(0, this) : iterator(slots_ + 1, this); }
+
+ private:
+ /*!
+ * \brief Search for the given key
+ * \param key The key
+ * \return ListNode that associated with the key
+ */
+ ListNode Search(const key_type& key) const {
+ if (this->size_ == 0) {
+ return ListNode();
+ }
+ for (ListNode iter = GetListHead(AnyHash()(key)); !iter.IsNone(); iter.MoveToNext(this)) {
+ if (AnyEqual()(key, iter.Key())) {
+ return iter;
+ }
+ }
+ return ListNode();
+ }
+ /*!
+ * \brief Search for the given key, throw exception if not exists
+ * \param key The key
+ * \return ListNode that associated with the key
+ */
+ mapped_type& At(const key_type& key) const {
+ ListNode iter = Search(key);
+ if (iter.IsNone()) {
+ TVM_FFI_THROW(IndexError) << "key is not in Map";
+ }
+ return iter.Val();
+ }
+ /*!
+ * \brief Try to insert a key, or do nothing if already exists
+ * \param key The indexing key
+ * \param result The linked-list entry found or just constructed
+ * \return A boolean, indicating if actual insertion happens
+ */
+ bool TryInsert(const key_type& key, ListNode* result) {
+ if (slots_ == 0) {
+ return false;
+ }
+ // required that `iter` to be the head of a linked list through which we can iterator
+ ListNode iter = IndexFromHash(AnyHash()(key));
+ // `iter` can be: 1) empty; 2) body of an irrelevant list; 3) head of the relevant list
+ // Case 1: empty
+ if (iter.IsEmpty()) {
+ iter.NewHead(KVType(key, Any(nullptr)));
+ this->size_ += 1;
+ *result = iter;
+ return true;
+ }
+ // Case 2: body of an irrelevant list
+ if (!iter.IsHead()) {
+ // we move the elements around and construct the single-element linked list
+ return IsFull() ? false : TrySpareListHead(iter, key, result);
+ }
+ // Case 3: head of the relevant list
+ // we iterate through the linked list until the end
+ // make sure `iter` is the previous element of `next`
+ ListNode next = iter;
+ do {
+ // find equal item, do not insert
+ if (AnyEqual()(key, next.Key())) {
+ *result = next;
+ return true;
+ }
+ // make sure `iter` is the previous element of `next`
+ iter = next;
+ } while (next.MoveToNext(this));
+ // `iter` is the tail of the linked list
+ // always check capacity before insertion
+ if (IsFull()) {
+ return false;
+ }
+ // find the next empty slot
+ uint8_t jump;
+ if (!iter.GetNextEmpty(this, &jump, result)) {
+ return false;
+ }
+ result->NewTail(KVType(key, Any(nullptr)));
+ // link `iter` to `empty`, and move forward
+ iter.SetJump(jump);
+ this->size_ += 1;
+ return true;
+ }
+ /*!
+ * \brief Spare an entry to be the head of a linked list.
+ * As described in B3, during insertion, it is possible that the entire linked list does not
+ * exist, but the slot of its head has been occupied by other linked lists. In this case, we need
+ * to spare the slot by moving away the elements to another valid empty one to make insertion
+ * possible.
+ * \param target The given entry to be spared
+ * \param key The indexing key
+ * \param result The linked-list entry constructed as the head
+ * \return A boolean, if actual insertion happens
+ */
+ bool TrySpareListHead(ListNode target, const key_type& key, ListNode* result) {
+ // `target` is not the head of the linked list
+ // move the original item of `target` (if any)
+ // and construct new item on the position `target`
+ // To make `target` empty, we
+ // 1) find `w` the previous element of `target` in the linked list
+ // 2) copy the linked list starting from `r = target`
+ // 3) paste them after `w`
+ // read from the linked list after `r`
+ ListNode r = target;
+ // write to the tail of `w`
+ ListNode w = target.FindPrev(this);
+ // after `target` is moved, we disallow writing to the slot
+ bool is_first = true;
+ uint8_t r_meta, jump;
+ ListNode empty;
+ do {
+ // `jump` describes how `w` is jumped to `empty`
+ // rehash if there is no empty space after `w`
+ if (!w.GetNextEmpty(this, &jump, &empty)) {
+ return false;
+ }
+ // move `r` to `empty`
+ empty.NewTail(std::move(r.Data()));
+ // clear the metadata of `r`
+ r_meta = r.Meta();
+ if (is_first) {
+ is_first = false;
+ r.SetProtected();
+ } else {
+ r.SetEmpty();
+ }
+ // link `w` to `empty`, and move forward
+ w.SetJump(jump);
+ w = empty;
+ // move `r` forward as well
+ } while (r.MoveToNext(this, r_meta));
+ // finally we have done moving the linked list
+ // fill data_ into `target`
+ target.NewHead(KVType(key, Any(nullptr)));
+ this->size_ += 1;
+ *result = target;
+ return true;
+ }
+ /*!
+ * \brief Remove a ListNode
+ * \param iter The node to be removed
+ */
+ void Erase(const ListNode& iter) {
+ this->size_ -= 1;
+ if (!iter.HasNext()) {
+ // `iter` is the last
+ if (!iter.IsHead()) {
+ // cut the link if there is any
+ iter.FindPrev(this).SetJump(0);
+ }
+ iter.Data().KVType::~KVType();
+ iter.SetEmpty();
+ } else {
+ ListNode last = iter, prev = iter;
+ for (last.MoveToNext(this); last.HasNext(); prev = last, last.MoveToNext(this)) {
+ }
+ iter.Data() = std::move(last.Data());
+ last.SetEmpty();
+ prev.SetJump(0);
+ }
+ }
+ /*! \brief Clear the container to empty, release all entries and memory acquired */
+ void Reset() {
+ uint64_t n_blocks = CalcNumBlocks(this->slots_);
+ for (uint64_t bi = 0; bi < n_blocks; ++bi) {
+ uint8_t* meta_ptr = data_[bi].bytes;
+ KVType* data_ptr = reinterpret_cast<KVType*>(data_[bi].bytes + kBlockCap);
+ for (int j = 0; j < kBlockCap; ++j, ++meta_ptr, ++data_ptr) {
+ uint8_t& meta = *meta_ptr;
+ if (meta != uint8_t(kProtectedSlot) && meta != uint8_t(kEmptySlot)) {
+ meta = uint8_t(kEmptySlot);
+ data_ptr->KVType::~KVType();
+ }
+ }
+ }
+ ReleaseMemory();
+ }
+ /*! \brief Release the memory acquired by the container without deleting its entries stored inside
+ */
+ void ReleaseMemory() {
+ delete[] data_;
+ data_ = nullptr;
+ slots_ = 0;
+ size_ = 0;
+ fib_shift_ = 63;
+ }
+ /*!
+ * \brief Create an empty container
+ * \param fib_shift The fib shift provided
+ * \param n_slots Number of slots required, should be power-of-two
+ * \return The object created
+ */
+ static ObjectPtr<DenseMapNode> Empty(uint32_t fib_shift, uint64_t n_slots) {
+ TVM_FFI_ICHECK_GT(n_slots, uint64_t(SmallMapNode::kMaxSize));
+ ObjectPtr<DenseMapNode> p = make_object<DenseMapNode>();
+ uint64_t n_blocks = CalcNumBlocks(n_slots - 1);
+ Block* block = p->data_ = new Block[n_blocks];
+ p->slots_ = n_slots - 1;
+ p->size_ = 0;
+ p->fib_shift_ = fib_shift;
+ for (uint64_t i = 0; i < n_blocks; ++i, ++block) {
+ std::fill(block->bytes, block->bytes + kBlockCap, uint8_t(kEmptySlot));
+ }
+ return p;
+ }
+ /*!
+ * \brief Create an empty container with elements copying from another DenseMapNode
+ * \param from The source container
+ * \return The object created
+ */
+ static ObjectPtr<DenseMapNode> CopyFrom(DenseMapNode* from) {
+ ObjectPtr<DenseMapNode> p = make_object<DenseMapNode>();
+ uint64_t n_blocks = CalcNumBlocks(from->slots_);
+ p->data_ = new Block[n_blocks];
+ p->slots_ = from->slots_;
+ p->size_ = from->size_;
+ p->fib_shift_ = from->fib_shift_;
+ for (uint64_t bi = 0; bi < n_blocks; ++bi) {
+ uint8_t* meta_ptr_from = from->data_[bi].bytes;
+ KVType* data_ptr_from = reinterpret_cast<KVType*>(from->data_[bi].bytes + kBlockCap);
+ uint8_t* meta_ptr_to = p->data_[bi].bytes;
+ KVType* data_ptr_to = reinterpret_cast<KVType*>(p->data_[bi].bytes + kBlockCap);
+ for (int j = 0; j < kBlockCap;
+ ++j, ++meta_ptr_from, ++data_ptr_from, ++meta_ptr_to, ++data_ptr_to) {
+ uint8_t& meta = *meta_ptr_to = *meta_ptr_from;
+ TVM_FFI_ICHECK(meta != kProtectedSlot);
+ if (meta != uint8_t(kEmptySlot)) {
+ new (data_ptr_to) KVType(*data_ptr_from);
+ }
+ }
+ }
+ return p;
+ }
+ /*!
+ * \brief InsertMaybeReHash an entry into the given hash map
+ * \param kv The entry to be inserted
+ * \param map The pointer to the map, can be changed if re-hashing happens
+ */
+ static void InsertMaybeReHash(const KVType& kv, ObjectPtr<Object>* map) {
+ DenseMapNode* map_node = static_cast<DenseMapNode*>(map->get());
+ ListNode iter;
+ // Try to insert. If succeed, we simply return
+ if (map_node->TryInsert(kv.first, &iter)) {
+ iter.Val() = kv.second;
+ return;
+ }
+ TVM_FFI_ICHECK_GT(map_node->slots_, uint64_t(SmallMapNode::kMaxSize));
+ // Otherwise, start rehash
+ ObjectPtr<Object> p = Empty(map_node->fib_shift_ - 1, map_node->slots_ * 2 + 2);
+ // Insert the given `kv` into the new hash map
+ InsertMaybeReHash(kv, &p);
+ uint64_t n_blocks = CalcNumBlocks(map_node->slots_);
+ // Then Insert data from the original block.
+ for (uint64_t bi = 0; bi < n_blocks; ++bi) {
+ uint8_t* meta_ptr = map_node->data_[bi].bytes;
+ KVType* data_ptr = reinterpret_cast<KVType*>(map_node->data_[bi].bytes + kBlockCap);
+ for (int j = 0; j < kBlockCap; ++j, ++meta_ptr, ++data_ptr) {
+ uint8_t& meta = *meta_ptr;
+ if (meta != uint8_t(kProtectedSlot) && meta != uint8_t(kEmptySlot)) {
+ meta = uint8_t(kEmptySlot);
+ KVType kv = std::move(*data_ptr);
+ InsertMaybeReHash(kv, &p);
+ }
+ }
+ }
+ map_node->ReleaseMemory();
+ *map = p;
+ }
+ /*!
+ * \brief Check whether the hash table is full
+ * \return A boolean indicating whether hash table is full
+ */
+ bool IsFull() const { return size_ + 1 > (slots_ + 1) * kMaxLoadFactor; }
+ /*!
+ * \brief Increment the pointer
+ * \param index The pointer to be incremented
+ * \return The increased pointer
+ */
+ uint64_t IncItr(uint64_t index) const {
+ for (++index; index <= slots_; ++index) {
+ if (!ListNode(index, this).IsEmpty()) {
+ return index;
+ }
+ }
+ return slots_ + 1;
+ }
+ /*!
+ * \brief Decrement the pointer
+ * \param index The pointer to be decremented
+ * \return The decreased pointer
+ */
+ uint64_t DecItr(uint64_t index) const {
+ while (index != 0) {
+ index -= 1;
+ if (!ListNode(index, this).IsEmpty()) {
+ return index;
+ }
+ }
+ return slots_ + 1;
+ }
+ /*!
+ * \brief De-reference the pointer
+ * \param index The pointer to be dereferenced
+ * \return The result
+ */
+ KVType* DeRefItr(uint64_t index) const { return &ListNode(index, this).Data(); }
+ /*! \brief Construct from hash code */
+ ListNode IndexFromHash(uint64_t hash_value) const {
+ return ListNode(FibHash(hash_value, fib_shift_), this);
+ }
+ /*! \brief Construct from hash code if the position is head of list */
+ ListNode GetListHead(uint64_t hash_value) const {
+ ListNode node = IndexFromHash(hash_value);
+ return node.IsHead() ? node : ListNode();
+ }
+ /*! \brief Construct the number of blocks in the hash table */
+ static uint64_t CalcNumBlocks(uint64_t n_slots_m1) {
+ uint64_t n_slots = n_slots_m1 > 0 ? n_slots_m1 + 1 : 0;
+ return (n_slots + kBlockCap - 1) / kBlockCap;
+ }
+ /*!
+ * \brief Calculate the power-of-2 table size given the lower-bound of required capacity.
+ * \param cap The lower-bound of the required capacity
+ * \param fib_shift The result shift for Fibonacci Hashing
+ * \param n_slots The result number of slots
+ */
+ static void CalcTableSize(uint64_t cap, uint32_t* fib_shift, uint64_t* n_slots) {
+ uint32_t shift = 64;
+ uint64_t slots = 1;
+ for (uint64_t c = cap; c; c >>= 1) {
+ shift -= 1;
+ slots <<= 1;
+ }
+ TVM_FFI_ICHECK_GT(slots, cap);
+ if (slots < cap * 2) {
+ *fib_shift = shift - 1;
+ *n_slots = slots << 1;
+ } else {
+ *fib_shift = shift;
+ *n_slots = slots;
+ }
+ }
+ /*!
+ * \brief Fibonacci Hashing, maps a hash code to an index in a power-of-2-sized table.
+ * See also: https://programmingpraxis.com/2018/06/19/fibonacci-hash/.
+ * \param hash_value The raw hash value
+ * \param fib_shift The shift in Fibonacci Hashing
+ * \return An index calculated using Fibonacci Hashing
+ */
+ static uint64_t FibHash(uint64_t hash_value, uint32_t fib_shift) {
+ constexpr uint64_t coeff = 11400714819323198485ull;
+ return (coeff * hash_value) >> fib_shift;
+ }
+ /*! \brief The implicit in-place linked list used to index a chain */
+ struct ListNode {
+ /*! \brief Construct None */
+ ListNode() : index(0), block(nullptr) {}
+ /*! \brief Construct from position */
+ ListNode(uint64_t index, const DenseMapNode* self)
+ : index(index), block(self->data_ + (index / kBlockCap)) {}
+ /*! \brief Metadata on the entry */
+ uint8_t& Meta() const { return *(block->bytes + index % kBlockCap); }
+ /*! \brief Data on the entry */
+ KVType& Data() const {
+ return *(reinterpret_cast<KVType*>(block->bytes + kBlockCap +
+ (index % kBlockCap) * sizeof(KVType)));
+ }
+ /*! \brief Key on the entry */
+ key_type& Key() const { return Data().first; }
+ /*! \brief Value on the entry */
+ mapped_type& Val() const { return Data().second; }
+ /*! \brief If the entry is head of linked list */
+ bool IsHead() const { return (Meta() & 0b10000000) == 0b00000000; }
+ /*! \brief If the entry is none */
+ bool IsNone() const { return block == nullptr; }
+ /*! \brief If the entry is empty slot */
+ bool IsEmpty() const { return Meta() == uint8_t(kEmptySlot); }
+ /*! \brief If the entry is protected slot */
+ bool IsProtected() const { return Meta() == uint8_t(kProtectedSlot); }
+ /*! \brief Set the entry to be empty */
+ void SetEmpty() const { Meta() = uint8_t(kEmptySlot); }
+ /*! \brief Set the entry to be protected */
+ void SetProtected() const { Meta() = uint8_t(kProtectedSlot); }
+ /*! \brief Set the entry's jump to its next entry */
+ void SetJump(uint8_t jump) const { (Meta() &= 0b10000000) |= jump; }
+ /*! \brief Construct a head of linked list in-place */
+ void NewHead(KVType v) const {
+ Meta() = 0b00000000;
+ new (&Data()) KVType(std::move(v));
+ }
+ /*! \brief Construct a tail of linked list in-place */
+ void NewTail(KVType v) const {
+ Meta() = 0b10000000;
+ new (&Data()) KVType(std::move(v));
+ }
+ /*! \brief If the entry has next entry on the linked list */
+ bool HasNext() const { return NextProbeLocation(Meta() & 0b01111111) != 0; }
+ /*! \brief Move the entry to the next entry on the linked list */
+ bool MoveToNext(const DenseMapNode* self, uint8_t meta) {
+ uint64_t offset = NextProbeLocation(meta & 0b01111111);
+ if (offset == 0) {
+ index = 0;
+
+ block = nullptr;
+ return false;
+ }
+ index = (index + offset) & (self->slots_);
+ block = self->data_ + (index / kBlockCap);
+ return true;
+ }
+ /*! \brief Move the entry to the next entry on the linked list */
+ bool MoveToNext(const DenseMapNode* self) { return MoveToNext(self, Meta()); }
+ /*! \brief Get the previous entry on the linked list */
+ ListNode FindPrev(const DenseMapNode* self) const {
+ // start from the head of the linked list, which must exist
+ ListNode next = self->IndexFromHash(AnyHash()(Key()));
+ // `prev` is always the previous item of `next`
+ ListNode prev = next;
+ for (next.MoveToNext(self); index != next.index; prev = next, next.MoveToNext(self)) {
+ }
+ return prev;
+ }
+ /*! \brief Get the next empty jump */
+ bool GetNextEmpty(const DenseMapNode* self, uint8_t* jump, ListNode* result) const {
+ for (uint8_t idx = 1; idx < kNumJumpDists; ++idx) {
+ ListNode candidate((index + NextProbeLocation(idx)) & (self->slots_), self);
+ if (candidate.IsEmpty()) {
+ *jump = idx;
+ *result = candidate;
+ return true;
+ }
+ }
+ return false;
+ }
+ /*! \brief Index on the real array */
+ uint64_t index;
+ /*! \brief Pointer to the actual block */
+ Block* block;
+ };
+
+ protected:
+ /*! \brief fib shift in Fibonacci Hashing */
+ uint32_t fib_shift_;
+ /*! \brief array of data blocks */
+ Block* data_;
+ static uint64_t NextProbeLocation(size_t index) {
+ /* clang-format off */
+ /*! \brief Candidates of probing distance */
+ static const uint64_t kNextProbeLocation[kNumJumpDists] {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ // Quadratic probing with triangle numbers. See also:
+ // 1) https://en.wikipedia.org/wiki/Quadratic_probing
+ // 2) https://fgiesen.wordpress.com/2015/02/22/triangular-numbers-mod-2n/
+ // 3) https://github.com/skarupke/flat_hash_map
+ 21, 28, 36, 45, 55, 66, 78, 91, 105, 120,
+ 136, 153, 171, 190, 210, 231, 253, 276, 300, 325,
+ 351, 378, 406, 435, 465, 496, 528, 561, 595, 630,
+ 666, 703, 741, 780, 820, 861, 903, 946, 990, 1035,
+ 1081, 1128, 1176, 1225, 1275, 1326, 1378, 1431, 1485, 1540,
+ 1596, 1653, 1711, 1770, 1830, 1891, 1953, 2016, 2080, 2145,
+ 2211, 2278, 2346, 2415, 2485, 2556, 2628,
+ // larger triangle numbers
+ 8515, 19110, 42778, 96141, 216153,
+ 486591, 1092981, 2458653, 5532801, 12442566,
+ 27993903, 62983476, 141717030, 318844378, 717352503,
+ 1614057336, 3631522476, 8170957530, 18384510628, 41364789378,
+ 93070452520, 209408356380, 471168559170, 1060128894105, 2385289465695,
+ 5366898840628, 12075518705635, 27169915244790, 61132312065111, 137547689707000,
+ 309482283181501, 696335127828753, 1566753995631385, 3525196511162271, 7931691992677701,
+ 17846306936293605, 40154190677507445, 90346928918121501, 203280589587557251,
+ 457381325854679626, 1029107982097042876, 2315492959180353330, 5209859154120846435,
+ };
+ /* clang-format on */
+ return kNextProbeLocation[index];
+ }
+ friend class MapNode;
+};
+
+#define TVM_DISPATCH_MAP(base, var, body) \
+ { \
+ using TSmall = SmallMapNode*; \
+ using TDense = DenseMapNode*; \
+ uint64_t slots = base->slots_; \
+ if (slots <= SmallMapNode::kMaxSize) { \
+ TSmall var = static_cast<TSmall>(base); \
+ body; \
+ } else { \
+ TDense var = static_cast<TDense>(base); \
+ body; \
+ } \
+ }
+
+#define TVM_DISPATCH_MAP_CONST(base, var, body) \
+ { \
+ using TSmall = const SmallMapNode*; \
+ using TDense = const DenseMapNode*; \
+ uint64_t slots = base->slots_; \
+ if (slots <= SmallMapNode::kMaxSize) { \
+ TSmall var = static_cast<TSmall>(base); \
+ body; \
+ } else { \
+ TDense var = static_cast<TDense>(base); \
+ body; \
+ } \
+ }
+
+inline MapNode::iterator::pointer MapNode::iterator::operator->() const {
+ TVM_FFI_MAP_FAIL_IF_CHANGED()
+ TVM_DISPATCH_MAP_CONST(self, p, { return p->DeRefItr(index); });
+}
+
+inline MapNode::iterator& MapNode::iterator::operator++() {
+ TVM_FFI_MAP_FAIL_IF_CHANGED()
+ TVM_DISPATCH_MAP_CONST(self, p, {
+ index = p->IncItr(index);
+ return *this;
+ });
+}
+
+inline MapNode::iterator& MapNode::iterator::operator--() {
+ TVM_FFI_MAP_FAIL_IF_CHANGED()
+ TVM_DISPATCH_MAP_CONST(self, p, {
+ index = p->DecItr(index);
+ return *this;
+ });
+}
+
+inline size_t MapNode::count(const key_type& key) const {
+ TVM_DISPATCH_MAP_CONST(this, p, { return p->count(key); });
+}
+
+inline const MapNode::mapped_type& MapNode::at(const MapNode::key_type& key) const {
+ TVM_DISPATCH_MAP_CONST(this, p, { return p->at(key); });
+}
+
+inline MapNode::mapped_type& MapNode::at(const MapNode::key_type& key) {
+ TVM_DISPATCH_MAP(this, p, { return p->at(key); });
+}
+
+inline MapNode::iterator MapNode::begin() const {
+ TVM_DISPATCH_MAP_CONST(this, p, { return p->begin(); });
+}
+
+inline MapNode::iterator MapNode::end() const {
+ TVM_DISPATCH_MAP_CONST(this, p, { return p->end(); });
+}
+
+inline MapNode::iterator MapNode::find(const MapNode::key_type& key) const {
+ TVM_DISPATCH_MAP_CONST(this, p, { return p->find(key); });
+}
+
+inline void MapNode::erase(const MapNode::iterator& position) {
+ TVM_DISPATCH_MAP(this, p, { return p->erase(position); });
+}
+
+#undef TVM_DISPATCH_MAP
+#undef TVM_DISPATCH_MAP_CONST
+
+inline ObjectPtr<MapNode> MapNode::Empty() { return SmallMapNode::Empty(); }
+
+inline ObjectPtr<MapNode> MapNode::CopyFrom(MapNode* from) {
+ if (from->slots_ <= SmallMapNode::kMaxSize) {
+ return SmallMapNode::CopyFrom(static_cast<SmallMapNode*>(from));
+ } else {
+ return DenseMapNode::CopyFrom(static_cast<DenseMapNode*>(from));
+ }
+}
+
+template <typename IterType>
+inline ObjectPtr<Object> MapNode::CreateFromRange(IterType first, IterType last) {
+ int64_t _cap = std::distance(first, last);
+ if (_cap < 0) {
+ return SmallMapNode::Empty();
+ }
+ uint64_t cap = static_cast<uint64_t>(_cap);
+ if (cap < SmallMapNode::kMaxSize) {
+ return SmallMapNode::CreateFromRange(cap, first, last);
+ }
+ uint32_t fib_shift;
+ uint64_t n_slots;
+ DenseMapNode::CalcTableSize(cap, &fib_shift, &n_slots);
+ ObjectPtr<Object> obj = DenseMapNode::Empty(fib_shift, n_slots);
+ for (; first != last; ++first) {
+ KVType kv(*first);
+ DenseMapNode::InsertMaybeReHash(kv, &obj);
+ }
+ return obj;
+}
+
+inline void MapNode::InsertMaybeReHash(const KVType& kv, ObjectPtr<Object>* map) {
+ constexpr uint64_t kSmallMapMaxSize = SmallMapNode::kMaxSize;
+ MapNode* base = static_cast<MapNode*>(map->get());
+#if TVM_FFI_DEBUG_WITH_ABI_CHANGE
+ base->state_marker++;
+#endif // TVM_FFI_DEBUG_WITH_ABI_CHANGE
+ if (base->slots_ < kSmallMapMaxSize) {
+ SmallMapNode::InsertMaybeReHash(kv, map);
+ } else if (base->slots_ == kSmallMapMaxSize) {
+ if (base->size_ < base->slots_) {
+ SmallMapNode::InsertMaybeReHash(kv, map);
+ } else {
+ ObjectPtr<Object> new_map = MapNode::CreateFromRange(base->begin(), base->end());
+ DenseMapNode::InsertMaybeReHash(kv, &new_map);
+ *map = std::move(new_map);
+ }
+ } else {
+ DenseMapNode::InsertMaybeReHash(kv, map);
+ }
+}
+
+template <>
+inline ObjectPtr<MapNode> make_object<>() = delete;
+
+/*!
+ * \brief Map container of NodeRef->NodeRef in DSL graph.
+ * Map implements copy on write semantics, which means map is mutable
+ * but copy will happen when array is referenced in more than two places.
+ *
+ * operator[] only provide const acces, use Set to mutate the content.
+ * \tparam K The key NodeRef type.
+ * \tparam V The value NodeRef type.
+ */
+template <typename K, typename V,
+ typename = typename std::enable_if_t<(std::is_same_v<K, Any> || TypeTraits<K>::enabled) &&
+ (std::is_same_v<V, Any> || TypeTraits<V>::enabled)>>
+class Map : public ObjectRef {
+ public:
+ using key_type = K;
+ using mapped_type = V;
+ class iterator;
+ /*!
+ * \brief default constructor
+ */
+ Map() { data_ = MapNode::Empty(); }
+ /*!
+ * \brief move constructor
+ * \param other source
+ */
+ Map(Map<K, V>&& other) { data_ = std::move(other.data_); }
+ /*!
+ * \brief copy constructor
+ * \param other source
+ */
+ Map(const Map<K, V>& other) : ObjectRef(other.data_) {}
+ /*!
+ * \brief copy assign operator
+ * \param other The source of assignment
+ * \return reference to self.
+ */
+ Map<K, V>& operator=(Map<K, V>&& other) {
+ data_ = std::move(other.data_);
+ return *this;
+ }
+ /*!
+ * \brief move assign operator
+ * \param other The source of assignment
+ * \return reference to self.
+ */
+ Map<K, V>& operator=(const Map<K, V>& other) {
+ data_ = other.data_;
+ return *this;
+ }
+ /*!
+ * \brief constructor from pointer
+ * \param n the container pointer
+ */
+ explicit Map(ObjectPtr<Object> n) : ObjectRef(n) {}
+ /*!
+ * \brief constructor from iterator
+ * \param begin begin of iterator
+ * \param end end of iterator
+ * \tparam IterType The type of iterator
+ */
+ template <typename IterType>
+ Map(IterType begin, IterType end) {
+ data_ = MapNode::CreateFromRange(begin, end);
+ }
+ /*!
+ * \brief constructor from initializer list
+ * \param init The initalizer list
+ */
+ Map(std::initializer_list<std::pair<K, V>> init) {
+ data_ = MapNode::CreateFromRange(init.begin(), init.end());
+ }
+ /*!
+ * \brief constructor from unordered_map
+ * \param init The unordered_map
+ */
+ template <typename Hash, typename Equal>
+ Map(const std::unordered_map<K, V, Hash, Equal>& init) { // NOLINT(*)
+ data_ = MapNode::CreateFromRange(init.begin(), init.end());
+ }
+ /*!
+ * \brief Read element from map.
+ * \param key The key
+ * \return the corresonding element.
+ */
+ const V at(const K& key) const {
+ return details::AnyUnsafe::ConvertAfterCheck<V>(GetMapNode()->at(key));
+ }
+ /*!
+ * \brief Read element from map.
+ * \param key The key
+ * \return the corresonding element.
+ */
+ const V operator[](const K& key) const { return this->at(key); }
+ /*! \return The size of the array */
+ size_t size() const {
+ MapNode* n = GetMapNode();
+ return n == nullptr ? 0 : n->size();
+ }
+ /*! \return The number of elements of the key */
+ size_t count(const K& key) const {
+ MapNode* n = GetMapNode();
+ return n == nullptr ? 0 : GetMapNode()->count(key);
+ }
+ /*! \return whether array is empty */
+ bool empty() const { return size() == 0; }
+ /*! \brief Release reference to all the elements */
+ void clear() {
+ MapNode* n = GetMapNode();
+ if (n != nullptr) {
+ data_ = MapNode::Empty();
+ }
+ }
+ /*!
+ * \brief set the Map.
+ * \param key The index key.
+ * \param value The value to be setted.
+ */
+ void Set(const K& key, const V& value) {
+ CopyOnWrite();
+ MapNode::InsertMaybeReHash(MapNode::KVType(key, value), &data_);
+ }
+ /*! \return begin iterator */
+ iterator begin() const { return iterator(GetMapNode()->begin()); }
+ /*! \return end iterator */
+ iterator end() const { return iterator(GetMapNode()->end()); }
+ /*! \return find the key and returns the associated iterator */
+ iterator find(const K& key) const { return iterator(GetMapNode()->find(key)); }
+ /*! \return The value associated with the key, NullOpt if not found */
+ Optional<V> Get(const K& key) const {
+ MapNode::iterator iter = GetMapNode()->find(key);
+ if (iter == GetMapNode()->end()) {
+ return std::nullopt;
+ }
+ return details::AnyUnsafe::ConvertAfterCheck<V>(iter->second);
+ }
+ void erase(const K& key) { CopyOnWrite()->erase(key); }
+
+ /*!
+ * \brief copy on write semantics
+ * Do nothing if current handle is the unique copy of the array.
+ * Otherwise make a new copy of the array to ensure the current handle
+ * hold a unique copy.
+ *
+ * \return Handle to the internal node container(which guarantees to be unique)
+ */
+ MapNode* CopyOnWrite() {
+ if (data_.get() == nullptr) {
+ data_ = MapNode::Empty();
+ } else if (!data_.unique()) {
+ data_ = MapNode::CopyFrom(GetMapNode());
+ }
+ return GetMapNode();
+ }
+ /*! \brief specify container node */
+ using ContainerType = MapNode;
+
+ /*! \brief Iterator of the hash map */
+ class iterator {
+ public:
+ using iterator_category = std::bidirectional_iterator_tag;
+ using difference_type = int64_t;
+ using value_type = const std::pair<K, V>;
+ using pointer = value_type*;
+ using reference = value_type;
+
+ iterator() : itr() {}
+
+ /*! \brief Compare iterators */
+ bool operator==(const iterator& other) const { return itr == other.itr; }
+ /*! \brief Compare iterators */
+ bool operator!=(const iterator& other) const { return itr != other.itr; }
+ /*! \brief De-reference iterators is not allowed */
+ pointer operator->() const = delete;
+ /*! \brief De-reference iterators */
+ reference operator*() const {
+ auto& kv = *itr;
+ return std::make_pair(details::AnyUnsafe::ConvertAfterCheck<K>(kv.first),
+ details::AnyUnsafe::ConvertAfterCheck<V>(kv.second));
+ }
+ /*! \brief Prefix self increment, e.g. ++iter */
+ iterator& operator++() {
+ ++itr;
+ return *this;
+ }
+ /*! \brief Suffix self increment */
+ iterator operator++(int) {
+ iterator copy = *this;
+ ++(*this);
+ return copy;
+ }
+
+ private:
+ iterator(const MapNode::iterator& itr) // NOLINT(*)
+ : itr(itr) {}
+
+ template <typename, typename, typename>
+ friend class Map;
+
+ MapNode::iterator itr;
+ };
+
+ private:
+ /*! \brief Return data_ as type of pointer of MapNode */
+ MapNode* GetMapNode() const { return static_cast<MapNode*>(data_.get()); }
+};
+
+/*!
+ * \brief Merge two Maps.
+ * \param lhs the first Map to merge.
+ * \param rhs the second Map to merge.
+ * @return The merged Array. Original Maps are kept unchanged.
+ */
+template <typename K, typename V,
+ typename = typename std::enable_if_t<(std::is_same_v<K, Any> || TypeTraits<K>::enabled) &&
+ (std::is_same_v<V, Any> || TypeTraits<V>::enabled)>>
+inline Map<K, V> Merge(Map<K, V> lhs, const Map<K, V>& rhs) {
+ for (const auto& p : rhs) {
+ lhs.Set(p.first, p.second);
+ }
+ return std::move(lhs);
+}
+
+} // namespace ffi
+} // namespace tvm
+
+#endif // TVM_FFI_CONTAINER_MAP_H_
diff --git a/ffi/include/tvm/ffi/type_traits.h b/ffi/include/tvm/ffi/type_traits.h
index 9fc38f7354..68c83bc0df 100644
--- a/ffi/include/tvm/ffi/type_traits.h
+++ b/ffi/include/tvm/ffi/type_traits.h
@@ -211,6 +211,10 @@ template <>
struct TypeTraits<void*> : public TypeTraitsBase {
static TVM_FFI_INLINE void CopyToAnyView(void* src, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFIOpaquePtr;
+ // maintain padding zero in 32bit platform
+ if constexpr (sizeof(void*) != sizeof(int64_t)) {
+ result->v_int64 = 0;
+ }
result->v_ptr = src;
}
diff --git a/ffi/tests/example/test_map.cc b/ffi/tests/example/test_map.cc
new file mode 100644
index 0000000000..ee265a34ec
--- /dev/null
+++ b/ffi/tests/example/test_map.cc
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+#include <gtest/gtest.h>
+#include <tvm/ffi/container/map.h>
+
+#include "./testing_object.h"
+
+namespace {
+
+using namespace tvm::ffi;
+using namespace tvm::ffi::testing;
+
+TEST(Map, Basic) {
+ Map<TInt, int> map0;
+ TInt k0(0);
+ map0.Set(k0, 1);
+
+ EXPECT_EQ(map0.size(), 1);
+
+ map0.Set(k0, 2);
+ EXPECT_EQ(map0.size(), 1);
+
+ auto it = map0.find(k0);
+ EXPECT_TRUE(it != map0.end());
+ EXPECT_EQ((*it).second, 2);
+}
+
+TEST(Map, PODKey) {
+ Map<Any, Any> map0;
+
+ // int as key
+ map0.Set(1, 2);
+ // float key is different
+ map0.Set(1.1, 3);
+ EXPECT_EQ(map0.size(), 2);
+
+ auto it = map0.find(1.1);
+ EXPECT_TRUE(it != map0.end());
+ EXPECT_EQ((*it).second.operator int(), 3);
+}
+
+TEST(Map, Object) {
+ TInt x(1);
+ TInt z(100);
+ TInt zz(1000);
+ Map<TInt, TInt> dict{{x, z}, {z, zz}};
+ EXPECT_EQ(dict.size(), 2);
+ EXPECT_TRUE(dict[x].same_as(z));
+ EXPECT_TRUE(dict.count(z));
+ EXPECT_TRUE(!dict.count(zz));
+}
+
+TEST(Map, Str) {
+ TInt x(1);
+ TInt z(100);
+ Map<String, TInt> dict{{"x", z}, {"z", z}};
+ EXPECT_EQ(dict.size(), 2);
+ EXPECT_TRUE(dict["x"].same_as(z));
+}
+
+TEST(Map, Mutate) {
+ TInt x(1);
+ TInt z(100);
+ TInt zz(1000);
+ Map<TInt, TInt> dict{{x, z}, {z, zz}};
+
+ EXPECT_TRUE(dict[x].same_as(z));
+ dict.Set(x, zz);
+ auto dict2 = dict;
+ EXPECT_EQ(dict2.count(z), 1);
+ dict.Set(zz, x);
+ EXPECT_EQ(dict2.count(zz), 0);
+ EXPECT_EQ(dict.count(zz), 1);
+
+ auto it = dict.find(zz);
+ EXPECT_TRUE(it != dict.end() && (*it).second.same_as(x));
+
+ it = dict2.find(zz);
+ EXPECT_TRUE(it == dict2.end());
+}
+
+TEST(Map, Clear) {
+ TInt x(1);
+ TInt z(100);
+ Map<TInt, TInt> dict{{x, z}, {z, z}};
+ EXPECT_EQ(dict.size(), 2);
+ dict.clear();
+ EXPECT_EQ(dict.size(), 0);
+}
+
+TEST(Map, Insert) {
+ auto check = [](const Map<String, int64_t>& result,
+ std::unordered_map<std::string, int64_t> expected) {
+ EXPECT_EQ(result.size(), expected.size());
+ for (const auto& kv : result) {
+ EXPECT_TRUE(expected.count(kv.first));
+ EXPECT_EQ(expected[kv.first], kv.second);
+ expected.erase(kv.first);
+ }
+ };
+ Map<String, int64_t> result;
+ std::unordered_map<std::string, int64_t> expected;
+ char key = 'a';
+ int64_t val = 1;
+ for (int i = 0; i < 26; ++i, ++key, ++val) {
+ std::string s(1, key);
+ result.Set(s, val);
+ expected[s] = val;
+ check(result, expected);
+ }
+}
+
+TEST(Map, Erase) {
+ auto check = [](const Map<String, int64_t>& result,
+ std::unordered_map<std::string, int64_t> expected) {
+ EXPECT_EQ(result.size(), expected.size());
+ for (const auto& kv : result) {
+ EXPECT_TRUE(expected.count(kv.first));
+ EXPECT_EQ(expected[kv.first], kv.second);
+ expected.erase(kv.first);
+ }
+ };
+ Map<String, int64_t> map{{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}, {"e", 5}};
+ std::unordered_map<std::string, int64_t> stl;
+ std::transform(map.begin(), map.end(), std::inserter(stl, stl.begin()),
+ [](auto&& p) { return std::make_pair(p.first, p.second); });
+ for (char c = 'a'; c <= 'e'; ++c) {
+ Map<String, int64_t> result = map;
+ std::unordered_map<std::string, int64_t> expected(stl);
+ std::string key(1, c);
+ result.erase(key);
+ expected.erase(key);
+ check(result, expected);
+ }
+}
+
+} // namespace
(tvm) 16/38: [FFI] Global function registry
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 463425f90b32f9de3f7c882eb376f4e340345720
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Sep 15 16:06:14 2024 -0400
[FFI] Global function registry
---
ffi/include/tvm/ffi/any.h | 20 --------
ffi/include/tvm/ffi/c_api.h | 36 +++++++++++++-
ffi/include/tvm/ffi/cast.h | 80 +++++++++++++++++++++++++++++++
ffi/include/tvm/ffi/function.h | 98 +++++++++++++++++++++++++++++++++++--
ffi/include/tvm/ffi/object.h | 12 ++++-
ffi/src/ffi/function.cc | 106 ++++++++++++++++++++++++++++++++++++++++-
ffi/src/ffi/object.cc | 16 ++++---
ffi/src/ffi/traceback.cc | 4 --
ffi/tests/cpp/test_function.cc | 16 +++++++
ffi/tests/cpp/test_string.cc | 1 +
10 files changed, 350 insertions(+), 39 deletions(-)
diff --git a/ffi/include/tvm/ffi/any.h b/ffi/include/tvm/ffi/any.h
index 225bbfd21b..f7073d20e4 100644
--- a/ffi/include/tvm/ffi/any.h
+++ b/ffi/include/tvm/ffi/any.h
@@ -341,26 +341,6 @@ struct AnyEqual {
return false;
}
};
-
-// Downcast an object
-// NOTE: the implementation is put in here to avoid cyclic dependency
-// with the
-template <typename SubRef, typename BaseRef,
- typename = std::enable_if_t<std::is_base_of_v<ObjectRef, BaseRef>>>
-TVM_FFI_INLINE SubRef Downcast(BaseRef ref) {
- if (ref.defined()) {
- if (!ref->template IsInstance<typename SubRef::ContainerType>()) {
- TVM_FFI_THROW(TypeError) << "Downcast from " << ref->GetTypeKey() << " to "
- << SubRef::ContainerType::_type_key << " failed.";
- }
- } else {
- if (!SubRef::_type_is_nullable) {
- TVM_FFI_THROW(TypeError) << "Downcast from nullptr to not nullable reference of "
- << SubRef::ContainerType::_type_key;
- }
- }
- return details::ObjectUnsafe::DowncastRefNoCheck<SubRef>(std::move(ref));
-}
} // namespace ffi
} // namespace tvm
#endif // TVM_FFI_ANY_H_
diff --git a/ffi/include/tvm/ffi/c_api.h b/ffi/include/tvm/ffi/c_api.h
index e6d3449ac3..c580410581 100644
--- a/ffi/include/tvm/ffi/c_api.h
+++ b/ffi/include/tvm/ffi/c_api.h
@@ -188,6 +188,41 @@ typedef struct {
typedef int (*TVMFFISafeCallType)(void* func, int32_t num_args, const TVMFFIAny* args,
TVMFFIAny* result);
+/*!
+ * \brief Create a FFIFunc by passing in callbacks from C callback.
+ *
+ * The registered function then can be pulled by the backend by the name.
+ *
+ * \param self The resource handle of the C callback.
+ * \param safe_call The C callback implementation
+ * \param deleter deleter to recycle
+ * \param out The output of the function.
+ * \return 0 when success, nonzero when failure happens
+ */
+TVM_FFI_DLL int TVMFFIFuncCreate(void* self, TVMFFISafeCallType safe_call,
+ void (*deleter)(void* self), TVMFFIObjectHandle* out);
+
+/*!
+ * \brief Register the function to runtime's global table.
+ *
+ * The registered function then can be pulled by the backend by the name.
+ *
+ * \param name The name of the function.
+ * \param f The function to be registered.
+ * \param override Whether allow override already registered function.
+ * \return 0 when success, nonzero when failure happens
+ */
+TVM_FFI_DLL int TVMFFIFuncSetGlobal(const char* name, TVMFFIObjectHandle f, int override);
+
+/*!
+ * \brief Get a global function.
+ *
+ * \param name The name of the function.
+ * \param out the result function pointer, NULL if it does not exist.
+ * \return 0 when success, nonzero when failure happens
+ */
+TVM_FFI_DLL int TVMFFIFuncGetGlobal(const char* name, TVMFFIObjectHandle* out);
+
/*!
* \brief Free an object handle by decreasing reference
* \param obj The object handle.
@@ -211,7 +246,6 @@ TVM_FFI_DLL void TVMFFIMoveFromLastError(TVMFFIAny* result);
*
* \param error_view The error in format of any view.
* It can be an object, or simply a raw c_str.
- * \note
*/
TVM_FFI_DLL void TVMFFISetLastError(const TVMFFIAny* error_view);
diff --git a/ffi/include/tvm/ffi/cast.h b/ffi/include/tvm/ffi/cast.h
new file mode 100644
index 0000000000..3cdb620d22
--- /dev/null
+++ b/ffi/include/tvm/ffi/cast.h
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+/*!
+ * \file tvm/ffi/cast.h
+ * \brief Value casting support
+ */
+#ifndef TVM_FFI_CAST_H_
+#define TVM_FFI_CAST_H_
+
+#include <tvm/ffi/error.h>
+#include <tvm/ffi/object.h>
+
+namespace tvm {
+namespace ffi {
+/*!
+ * \brief Get a reference type from a raw object ptr type
+ *
+ * It is always important to get a reference type
+ * if we want to return a value as reference or keep
+ * the object alive beyond the scope of the function.
+ *
+ * \param ptr The object pointer
+ * \tparam RefType The reference type
+ * \tparam ObjectType The object type
+ * \return The corresponding RefType
+ */
+template <typename RefType, typename ObjectType>
+inline RefType GetRef(const ObjectType* ptr) {
+ static_assert(std::is_base_of_v<typename RefType::ContainerType, ObjectType>,
+ "Can only cast to the ref of same container type");
+ if (!RefType::_type_is_nullable) {
+ TVM_FFI_ICHECK_NOTNULL(ptr);
+ }
+ return RefType(details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(
+ const_cast<Object*>(static_cast<const Object*>(ptr))));
+}
+
+/*!
+ * \brief Downcast a base reference type to a more specific type.
+ *
+ * \param ref The input reference
+ * \return The corresponding SubRef.
+ * \tparam SubRef The target specific reference type.
+ * \tparam BaseRef the current reference type.
+ */
+template <typename SubRef, typename BaseRef,
+ typename = std::enable_if_t<std::is_base_of_v<ObjectRef, BaseRef>>>
+inline SubRef Downcast(BaseRef ref) {
+ if (ref.defined()) {
+ if (!ref->template IsInstance<typename SubRef::ContainerType>()) {
+ TVM_FFI_THROW(TypeError) << "Downcast from " << ref->GetTypeKey() << " to "
+ << SubRef::ContainerType::_type_key << " failed.";
+ }
+ } else {
+ if (!SubRef::_type_is_nullable) {
+ TVM_FFI_THROW(TypeError) << "Downcast from nullptr to not nullable reference of "
+ << SubRef::ContainerType::_type_key;
+ }
+ }
+ return details::ObjectUnsafe::DowncastRefNoCheck<SubRef>(std::move(ref));
+}
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_CAST_H_
diff --git a/ffi/include/tvm/ffi/function.h b/ffi/include/tvm/ffi/function.h
index cb4cb2b937..680758159a 100644
--- a/ffi/include/tvm/ffi/function.h
+++ b/ffi/include/tvm/ffi/function.h
@@ -246,11 +246,11 @@ class Function : public ObjectRef {
public:
/*! \brief Constructor from null */
Function(std::nullptr_t) : ObjectRef(nullptr) {} // NOLINT(*)
- /*!
- * \brief Constructing a packed function from a callable type
- * whose signature is consistent with `PackedFunc`
- * \param packed_call The packed function signature
- */
+ /*!
+ * \brief Constructing a packed function from a callable type
+ * whose signature is consistent with `PackedFunc`
+ * \param packed_call The packed function signature
+ */
template <typename TCallable>
static Function FromPacked(TCallable packed_call) {
static_assert(
@@ -299,6 +299,31 @@ class Function : public ObjectRef {
func.data_ = make_object<details::ExternCFunctionObjImpl>(self, safe_call, deleter);
return func;
}
+ /*!
+ * \brief Get global function by name
+ * \param name The function name
+ * \return The global function.
+ */
+ static Function GetGlobal(const char* name) {
+ TVMFFIObjectHandle handle;
+ TVM_FFI_CHECK_SAFE_CALL(TVMFFIFuncGetGlobal(name, &handle));
+ if (handle != nullptr) {
+ return Function(
+ details::ObjectUnsafe::ObjectPtrFromOwned<Object>(static_cast<Object*>(handle)));
+ } else {
+ return Function();
+ }
+ }
+ /*!
+ * \brief Set global function by name
+ * \param name The name of the function
+ * \param func The function
+ * \param override Whether to override when there is duplication.
+ */
+ static void SetGlobal(const char* name, Function func, bool override = false) {
+ TVM_FFI_CHECK_SAFE_CALL(
+ TVMFFIFuncSetGlobal(name, details::ObjectUnsafe::GetHeader(func.get()), override));
+ }
/*!
* \brief Constructing a packed function from a normal function.
*
@@ -367,8 +392,71 @@ class Function : public ObjectRef {
bool operator!=(std::nullptr_t) const { return data_ != nullptr; }
TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS(Function, ObjectRef, FunctionObj);
+
+ class Registry;
};
+/*! \brief Registry for global function */
+class Function::Registry {
+ public:
+ /*! \brief constructor */
+ explicit Registry(const char* name) : name_(name) {}
+ /*!
+ * \brief set the body of the function to the given function.
+ * Note that this will ignore default arg values and always require all arguments to be
+ * provided.
+ *
+ * \code
+ *
+ * int multiply(int x, int y) {
+ * return x * y;
+ * }
+ *
+ * TVM_REGISTER_GLOBAL("multiply")
+ * .set_body_typed(multiply); // will have type int(int, int)
+ *
+ * // will have type int(int, int)
+ * TVM_REGISTER_GLOBAL("sub")
+ * .set_body_typed([](int a, int b) -> int { return a - b; });
+ *
+ * \endcode
+ *
+ * \param f The function to forward to.
+ * \tparam FLambda The signature of the function.
+ */
+ template <typename FLambda>
+ Registry& set_body_typed(FLambda f) {
+ return Register(Function::FromUnpacked(f));
+ }
+
+ protected:
+ /*!
+ * \brief set the body of the function to be f
+ * \param f The body of the function.
+ */
+ Registry& Register(Function f) {
+ Function::SetGlobal(name_, f);
+ return *this;
+ }
+
+ /*! \brief name of the function */
+ const char* name_;
+};
+
+#define TVM_FFI_FUNC_REG_VAR_DEF \
+ static TVM_FFI_ATTRIBUTE_UNUSED ::tvm::ffi::Function::Registry& __mk_##TVMFFI
+
+/*!
+ * \brief Register a function globally.
+ * \code
+ * TVM_FFI_REGISTER_GLOBAL("MyAdd")
+ * .set_body_typed([](int a, int b) {
+ * return a + b;
+ * });
+ * \endcode
+ */
+#define TVM_FFI_REGISTER_GLOBAL(OpName) \
+ TVM_FFI_STR_CONCAT(TVM_FFI_FUNC_REG_VAR_DEF, __COUNTER__) = ::tvm::ffi::Function::Registry(OpName)
} // namespace ffi
} // namespace tvm
#endif // TVM_FFI_FUNCTION_H_
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index dbb940ab45..6a202ba1f5 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -545,6 +545,13 @@ struct ObjectUnsafe {
return const_cast<TVMFFIObject*>(&(src->header_));
}
+ template <typename T>
+ static TVM_FFI_INLINE ObjectPtr<T> ObjectPtrFromOwned(Object* raw_ptr) {
+ tvm::ffi::ObjectPtr<T> ptr;
+ ptr.data_ = raw_ptr;
+ return ptr;
+ }
+
// Create ObjectPtr from unknowned ptr
template <typename T>
static TVM_FFI_INLINE ObjectPtr<T> ObjectPtrFromUnowned(Object* raw_ptr) {
@@ -556,7 +563,10 @@ struct ObjectUnsafe {
return tvm::ffi::ObjectPtr<T>(reinterpret_cast<Object*>(obj_ptr));
}
- // Interactions with Any system
+ static TVM_FFI_INLINE void DecRefObjectHandle(TVMFFIObjectHandle handle) {
+ reinterpret_cast<Object*>(handle)->DecRef();
+ }
+
static TVM_FFI_INLINE void DecRefObjectInAny(TVMFFIAny* src) {
reinterpret_cast<Object*>(src->v_obj)->DecRef();
}
diff --git a/ffi/src/ffi/function.cc b/ffi/src/ffi/function.cc
index 5da4e0178b..f306af09f6 100644
--- a/ffi/src/ffi/function.cc
+++ b/ffi/src/ffi/function.cc
@@ -22,9 +22,14 @@
*/
#include <tvm/ffi/any.h>
#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/cast.h>
+#include <tvm/ffi/container/array.h>
#include <tvm/ffi/error.h>
+#include <tvm/ffi/function.h>
#include <tvm/ffi/string.h>
+#include <unordered_map>
+
namespace tvm {
namespace ffi {
@@ -49,10 +54,99 @@ class SafeCallContext {
Any last_error_;
};
+/*!
+ * \brief Global function table.
+ *
+
+ * \note We do not use mutex to guard updating of GlobalFunctionTable
+ *
+ * The assumption is that updating of GlobalFunctionTable will be done
+ * in the main thread during initialization or loading, or
+ * explicitly locked from the caller.
+ *
+ * Then the followup code will leverage the information
+ */
+class GlobalFunctionTable {
+ public:
+ void Update(const String& name, Function func, bool can_override) {
+ if (table_.count(name)) {
+ if (!can_override) {
+ TVM_FFI_THROW(RuntimeError) << "Global Function `" << name << "` is already registered";
+ }
+ }
+ table_[name] = new Function(func);
+ }
+
+ bool Remove(const String& name) {
+ auto it = table_.find(name);
+ if (it == table_.end()) return false;
+ table_.erase(it);
+ return true;
+ }
+
+ const Function* Get(const String& name) {
+ auto it = table_.find(name);
+ if (it == table_.end()) return nullptr;
+ return it->second;
+ }
+
+ Array<String> ListNames() const {
+ Array<String> names;
+ names.reserve(table_.size());
+ for (const auto& kv : table_) {
+ names.push_back(kv.first);
+ }
+ return names;
+ }
+
+ static GlobalFunctionTable* Global() {
+ // We deliberately create a new instance via raw new
+ // This is because GlobalFunctionTable can contain callbacks into
+ // the host language (Python) and the resource can become invalid
+ // indeterministic order of destruction and forking.
+ // The resources will only be recycled during program exit.
+ static GlobalFunctionTable* inst = new GlobalFunctionTable();
+ return inst;
+ }
+
+ private:
+ // deliberately track function pointer without recycling
+ // to avoid
+ std::unordered_map<String, Function*> table_;
+};
} // namespace ffi
} // namespace tvm
-extern "C" {
+int TVMFFIFuncCreate(void* self, TVMFFISafeCallType safe_call, void (*deleter)(void* self),
+ TVMFFIObjectHandle* out) {
+ using namespace tvm::ffi;
+ TVM_FFI_SAFE_CALL_BEGIN();
+ Function func = Function::FromExternC(self, safe_call, deleter);
+ *out = details::ObjectUnsafe::MoveTVMFFIObjectPtrFromObjectRef(&func);
+ TVM_FFI_SAFE_CALL_END();
+}
+
+int TVMFFIFuncSetGlobal(const char* name, TVMFFIObjectHandle f, int override) {
+ using namespace tvm::ffi;
+ TVM_FFI_SAFE_CALL_BEGIN();
+ GlobalFunctionTable::Global()->Update(name, GetRef<Function>(static_cast<FunctionObj*>(f)),
+ override != 0);
+ TVM_FFI_SAFE_CALL_END();
+}
+
+int TVMFFIFuncGetGlobal(const char* name, TVMFFIObjectHandle* out) {
+ using namespace tvm::ffi;
+ TVM_FFI_SAFE_CALL_BEGIN();
+ const Function* fp = GlobalFunctionTable::Global()->Get(name);
+ if (fp != nullptr) {
+ Function func(*fp);
+ *out = details::ObjectUnsafe::MoveTVMFFIObjectPtrFromObjectRef(&func);
+ } else {
+ *out = nullptr;
+ }
+ TVM_FFI_SAFE_CALL_END();
+}
+
void TVMFFISetLastError(const TVMFFIAny* error_view) {
tvm::ffi::SafeCallContext::ThreadLocal()->SetLastError(error_view);
}
@@ -60,4 +154,12 @@ void TVMFFISetLastError(const TVMFFIAny* error_view) {
void TVMFFIMoveFromLastError(TVMFFIAny* result) {
tvm::ffi::SafeCallContext::ThreadLocal()->MoveFromLastError(result);
}
-}
+
+TVM_FFI_REGISTER_GLOBAL("tvm_ffi.GlobalFunctionRemove")
+ .set_body_typed([](const tvm::ffi::String& name) -> bool {
+ return tvm::ffi::GlobalFunctionTable::Global()->Remove(name);
+ });
+
+TVM_FFI_REGISTER_GLOBAL("tvm_ffi.GlobalFunctionListNames").set_body_typed([]() {
+ return tvm::ffi::GlobalFunctionTable::Global()->ListNames();
+});
diff --git a/ffi/src/ffi/object.cc b/ffi/src/ffi/object.cc
index d0782d1b50..8941753df8 100644
--- a/ffi/src/ffi/object.cc
+++ b/ffi/src/ffi/object.cc
@@ -34,12 +34,13 @@ namespace tvm {
namespace ffi {
/*!
- * \brief Type context that manages the type hierarchy information.
+ * \brief Global registry that manages
*
- * \note We do not use mutex to guard updating of TypeContext
+ * \note We do not use mutex to guard updating of TypeTable
*
- * The assumption is that updating of TypeContext will be done
- * in the main thread during initialization or loading.
+ * The assumption is that updating of TypeTable will be done
+ * in the main thread during initialization or loading, or
+ * explicitly locked from the caller.
*
* Then the followup code will leverage the information
*/
@@ -223,7 +224,11 @@ class TypeTable {
} // namespace ffi
} // namespace tvm
-extern "C" {
+int TVMFFIObjectFree(TVMFFIObjectHandle handle) {
+ TVM_FFI_SAFE_CALL_BEGIN();
+ tvm::ffi::details::ObjectUnsafe::DecRefObjectHandle(handle);
+ TVM_FFI_SAFE_CALL_END();
+}
int32_t TVMFFIGetOrAllocTypeIndex(const char* type_key, int32_t static_type_index,
int32_t type_depth, int32_t num_child_slots,
@@ -240,4 +245,3 @@ const TVMFFITypeInfo* TVMFFIGetTypeInfo(int32_t type_index) {
return tvm::ffi::TypeTable::Global()->GetTypeInfo(type_index);
TVM_FFI_LOG_EXCEPTION_CALL_END(TVMFFIGetTypeInfo);
}
-} // extern "C"
diff --git a/ffi/src/ffi/traceback.cc b/ffi/src/ffi/traceback.cc
index 70de055719..6de3efe7ca 100644
--- a/ffi/src/ffi/traceback.cc
+++ b/ffi/src/ffi/traceback.cc
@@ -149,15 +149,12 @@ __attribute__((constructor)) void install_signal_handler(void) {
} // namespace ffi
} // namespace tvm
-extern "C" {
const char* TVMFFITraceback(const char*, const char*, int) {
static thread_local std::string traceback_str;
traceback_str = ::tvm::ffi::Traceback();
return traceback_str.c_str();
}
-} // extern "C"
#else
-extern "C" {
// fallback implementation simply print out the last trace
const char* TVMFFITraceback(const char* filename, const char* func, int lineno) {
static thread_local std::string traceback_str;
@@ -167,6 +164,5 @@ const char* TVMFFITraceback(const char* filename, const char* func, int lineno)
traceback_str = traceback_stream.str();
return traceback_str.c_str();
}
-} // extern "C"
#endif // TVM_FFI_USE_LIBBACKTRACE
#endif // _MSC_VER
diff --git a/ffi/tests/cpp/test_function.cc b/ffi/tests/cpp/test_function.cc
index 99e7819b23..367e613665 100644
--- a/ffi/tests/cpp/test_function.cc
+++ b/ffi/tests/cpp/test_function.cc
@@ -18,6 +18,7 @@
*/
#include <gtest/gtest.h>
#include <tvm/ffi/any.h>
+#include <tvm/ffi/container/array.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/memory.h>
@@ -112,4 +113,19 @@ TEST(Func, FromUnpacked) {
},
::tvm::ffi::Error);
}
+
+TEST(Func, Global) {
+ Function::SetGlobal("testing.add1",
+ Function::FromUnpacked([](const int32_t& a) -> int { return a + 1; }));
+ Function fadd1 = Function::GetGlobal("testing.add1");
+ int b = fadd1(1);
+ EXPECT_EQ(b, 2);
+ Function fnot_exist = Function::GetGlobal("testing.not_existing_func");
+ EXPECT_TRUE(fnot_exist == nullptr);
+
+ Array<String> names = Function::GetGlobal("tvm_ffi.GlobalFunctionListNames")();
+
+ EXPECT_TRUE(std::find(names.begin(), names.end(), "testing.add1") != names.end());
+}
+
} // namespace
diff --git a/ffi/tests/cpp/test_string.cc b/ffi/tests/cpp/test_string.cc
index 23310824a6..04348292b7 100644
--- a/ffi/tests/cpp/test_string.cc
+++ b/ffi/tests/cpp/test_string.cc
@@ -18,6 +18,7 @@
*/
#include <gtest/gtest.h>
#include <tvm/ffi/any.h>
+#include <tvm/ffi/cast.h>
#include <tvm/ffi/string.h>
namespace {
(tvm) 11/38: [FFI][REFACTOR] Cleanup naming convention and lint
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit a2b5e9a726c5b811b60d71b6488556afe566b1ef
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Sep 8 14:29:23 2024 -0400
[FFI][REFACTOR] Cleanup naming convention and lint
---
ffi/CMakeLists.txt | 2 +-
ffi/include/tvm/ffi/any.h | 29 +++---
.../tvm/ffi/{internal_utils.h => base_details.h} | 23 ++---
ffi/include/tvm/ffi/c_api.h | 3 +-
ffi/include/tvm/ffi/container/array.h | 18 ++--
.../ffi/container/{base.h => container_details.h} | 17 ++--
ffi/include/tvm/ffi/container/optional.h | 0
ffi/include/tvm/ffi/error.h | 13 +--
ffi/include/tvm/ffi/function.h | 81 ++++++++-------
ffi/include/tvm/ffi/function_details.h | 104 +++++++++----------
ffi/include/tvm/ffi/object.h | 12 +--
ffi/include/tvm/ffi/type_traits.h | 69 ++++++-------
ffi/scripts/run_tests.sh | 6 +-
ffi/src/ffi/traceback.h | 2 +-
ffi/tests/example/test_error.cc | 33 +++----
ffi/tests/example/test_function.cc | 110 ++++++++++-----------
ffi/tests/example/testing_object.h | 8 +-
tests/lint/cpplint.sh | 1 +
18 files changed, 249 insertions(+), 282 deletions(-)
diff --git a/ffi/CMakeLists.txt b/ffi/CMakeLists.txt
index 7c830f8a0f..89429d253b 100644
--- a/ffi/CMakeLists.txt
+++ b/ffi/CMakeLists.txt
@@ -39,7 +39,7 @@ target_link_libraries(tvm_ffi INTERFACE dlpack_header)
target_compile_features(tvm_ffi INTERFACE cxx_std_17)
target_include_directories(tvm_ffi INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
-if (TVM_FFI_USE_LIBBRACKTRACE)
+if (TVM_FFI_ALLOW_DYN_TYPE)
message(STATUS "Setting C++ macro TVM_FFI_ALLOW_DYN_TYPE - 1")
target_compile_definitions(tvm_ffi INTERFACE TVM_FFI_ALLOW_DYN_TYPE=1)
else()
diff --git a/ffi/include/tvm/ffi/any.h b/ffi/include/tvm/ffi/any.h
index 9912c45f12..3fad36eacc 100644
--- a/ffi/include/tvm/ffi/any.h
+++ b/ffi/include/tvm/ffi/any.h
@@ -26,6 +26,9 @@
#include <tvm/ffi/c_api.h>
#include <tvm/ffi/type_traits.h>
+#include <string>
+#include <utility>
+
namespace tvm {
namespace ffi {
@@ -62,9 +65,7 @@ class AnyView {
std::swap(data_, other.data_);
}
/*! \return the internal type index */
- int32_t type_index() const {
- return data_.type_index;
- }
+ int32_t type_index() const { return data_.type_index; }
// default constructors
AnyView() { data_.type_index = TypeIndex::kTVMFFINone; }
~AnyView() = default;
@@ -80,7 +81,7 @@ class AnyView {
// constructor from general types
template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
AnyView(const T& other) { // NOLINT(*)
- TypeTraits<T>::ConvertToAnyView(other, &data_);
+ TypeTraits<T>::CopyToAnyView(other, &data_);
}
template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
AnyView& operator=(const T& other) { // NOLINT(*)
@@ -91,12 +92,12 @@ class AnyView {
template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
std::optional<T> TryAs() const {
- return TypeTraits<T>::TryConvertFromAnyView(&data_);
+ return TypeTraits<T>::TryCopyFromAnyView(&data_);
}
template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
operator T() const {
- std::optional<T> opt = TypeTraits<T>::TryConvertFromAnyView(&data_);
+ std::optional<T> opt = TypeTraits<T>::TryCopyFromAnyView(&data_);
if (opt.has_value()) {
return std::move(*opt);
}
@@ -132,7 +133,7 @@ namespace details {
*/
TVM_FFI_INLINE void InplaceConvertAnyViewToAny(TVMFFIAny* data,
[[maybe_unused]] size_t extra_any_bytes = 0) {
- // TODO: string conversion.
+ // TODO(tqchen): string conversion.
if (data->type_index >= TVMFFITypeIndex::kTVMFFIStaticObjectBegin) {
details::ObjectUnsafe::IncRefObjectInAny(data);
}
@@ -165,9 +166,7 @@ class Any {
std::swap(data_, other.data_);
}
/*! \return the internal type index */
- int32_t type_index() const {
- return data_.type_index;
- }
+ int32_t type_index() const { return data_.type_index; }
// default constructors
Any() { data_.type_index = TypeIndex::kTVMFFINone; }
~Any() { this->reset(); }
@@ -189,7 +188,9 @@ class Any {
return *this;
}
// convert from/to AnyView
- Any(const AnyView& other) : data_(other.data_) { details::InplaceConvertAnyViewToAny(&data_); }
+ explicit Any(const AnyView& other) : data_(other.data_) {
+ details::InplaceConvertAnyViewToAny(&data_);
+ }
Any& operator=(const AnyView& other) {
// copy-and-swap idiom
Any(other).swap(*this); // NOLINT(*)
@@ -210,12 +211,12 @@ class Any {
}
template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
std::optional<T> TryAs() const {
- return TypeTraits<T>::TryConvertFromAnyView(&data_);
+ return TypeTraits<T>::TryCopyFromAnyView(&data_);
}
template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
operator T() const {
- std::optional<T> opt = TypeTraits<T>::TryConvertFromAnyView(&data_);
+ std::optional<T> opt = TypeTraits<T>::TryCopyFromAnyView(&data_);
if (opt.has_value()) {
return std::move(*opt);
}
@@ -254,7 +255,7 @@ struct AnyUnsafe : public ObjectUnsafe {
template <typename T>
static TVM_FFI_INLINE T ConvertAfterCheck(const Any& ref) {
if constexpr (!std::is_same_v<T, Any>) {
- return TypeTraits<T>::ConvertFromAnyViewAfterCheck(&(ref.data_));
+ return TypeTraits<T>::CopyFromAnyViewAfterCheck(&(ref.data_));
} else {
return ref;
}
diff --git a/ffi/include/tvm/ffi/internal_utils.h b/ffi/include/tvm/ffi/base_details.h
similarity index 86%
rename from ffi/include/tvm/ffi/internal_utils.h
rename to ffi/include/tvm/ffi/base_details.h
index ffe1dd9e1a..c44ad0608a 100644
--- a/ffi/include/tvm/ffi/internal_utils.h
+++ b/ffi/include/tvm/ffi/base_details.h
@@ -18,10 +18,12 @@
*/
/*!
* \file tvm/ffi/base_details.h
- * \brief Internal use utilities
+ * \brief Internal detail utils that can be used by files in tvm/ffi.
+ * \note details header are for internal use only
+ * and not to be directly used by user.
*/
-#ifndef TVM_FFI_INTERNAL_UTILS_H_
-#define TVM_FFI_INTERNAL_UTILS_H_
+#ifndef TVM_FFI_BASE_DETAILS_H_
+#define TVM_FFI_BASE_DETAILS_H_
#include <tvm/ffi/c_api.h>
@@ -74,21 +76,20 @@
* \param TypeName The class typename.
*/
#define TVM_FFI_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName) \
- TypeName(const TypeName& other) = default; \
- TypeName(TypeName&& other) = default; \
- TypeName& operator=(const TypeName& other) = default; \
+ TypeName(const TypeName& other) = default; \
+ TypeName(TypeName&& other) = default; \
+ TypeName& operator=(const TypeName& other) = default; \
TypeName& operator=(TypeName&& other) = default;
namespace tvm {
namespace ffi {
-
namespace details {
/********** Atomic Operations *********/
TVM_FFI_INLINE int32_t AtomicIncrementRelaxed(int32_t* ptr) {
#ifdef _MSC_VER
- return _InterlockedIncrement(reinterpret_cast<volatile long*>(ptr)) - 1;
+ return _InterlockedIncrement(reinterpret_cast<volatile long*>(ptr)) - 1; // NOLINT(*)
#else
return __atomic_fetch_add(ptr, 1, __ATOMIC_RELAXED);
#endif
@@ -96,7 +97,7 @@ TVM_FFI_INLINE int32_t AtomicIncrementRelaxed(int32_t* ptr) {
TVM_FFI_INLINE int32_t AtomicDecrementRelAcq(int32_t* ptr) {
#ifdef _MSC_VER
- return _InterlockedDecrement(reinterpret_cast<volatile long*>(ptr)) + 1;
+ return _InterlockedDecrement(reinterpret_cast<volatile long*>(ptr)) + 1; // NOLINT(*)
#else
return __atomic_fetch_sub(ptr, 1, __ATOMIC_ACQ_REL);
#endif
@@ -106,7 +107,7 @@ TVM_FFI_INLINE int32_t AtomicLoadRelaxed(const int32_t* ptr) {
int32_t* raw_ptr = const_cast<int32_t*>(ptr);
#ifdef _MSC_VER
// simply load the variable ptr out
- return (reinterpret_cast<const volatile long*>(raw_ptr))[0];
+ return (reinterpret_cast<const volatile long*>(raw_ptr))[0]; // NOLINT(*)
#else
return __atomic_load_n(raw_ptr, __ATOMIC_RELAXED);
#endif
@@ -135,4 +136,4 @@ void for_each(const F& f, Args&&... args) { // NOLINT(*)
} // namespace details
} // namespace ffi
} // namespace tvm
-#endif // TVM_FFI_INTERNAL_UTILS_H_
+#endif // TVM_FFI_BASE_DETAILS_H_
diff --git a/ffi/include/tvm/ffi/c_api.h b/ffi/include/tvm/ffi/c_api.h
index 659ee6cd83..5c40fedda8 100644
--- a/ffi/include/tvm/ffi/c_api.h
+++ b/ffi/include/tvm/ffi/c_api.h
@@ -178,7 +178,8 @@ typedef struct {
* It returns non-zero value if there is an error.
* When error happens, the exception object will be stored in result.
*/
-typedef int (*TVMFFISafeCallType)(void* func, int32_t num_args, const TVMFFIAny* args, TVMFFIAny* result);
+typedef int (*TVMFFISafeCallType)(void* func, int32_t num_args, const TVMFFIAny* args,
+ TVMFFIAny* result);
#ifdef __cplusplus
} // TVM_FFI_EXTERN_C
diff --git a/ffi/include/tvm/ffi/container/array.h b/ffi/include/tvm/ffi/container/array.h
index 2ce946a120..d4e3a88641 100644
--- a/ffi/include/tvm/ffi/container/array.h
+++ b/ffi/include/tvm/ffi/container/array.h
@@ -27,12 +27,12 @@
#define TVM_FFI_CONTAINER_ARRAY_H_
#include <tvm/ffi/any.h>
-#include <tvm/ffi/container/base.h>
+#include <tvm/ffi/container/container_details.h>
#include <tvm/ffi/memory.h>
#include <tvm/ffi/object.h>
#include <algorithm>
-#include <memory>
+#include <string>
#include <type_traits>
#include <utility>
#include <vector>
@@ -41,7 +41,7 @@ namespace tvm {
namespace ffi {
/*! \brief array node content in array */
-class ArrayNode : public Object, public InplaceArrayBase<ArrayNode, Any> {
+class ArrayNode : public Object, public details::InplaceArrayBase<ArrayNode, Any> {
public:
/*! \return The size of the array */
size_t size() const { return this->size_; }
@@ -391,8 +391,8 @@ class Array : public ObjectRef {
static T convert(const Any& n) { return details::AnyUnsafe::ConvertAfterCheck<T>(n); }
};
- using iterator = IterAdapter<ValueConverter, const Any*>;
- using reverse_iterator = ReverseIterAdapter<ValueConverter, const Any*>;
+ using iterator = details::IterAdapter<ValueConverter, const Any*>;
+ using reverse_iterator = details::ReverseIterAdapter<ValueConverter, const Any*>;
/*! \return begin iterator */
iterator begin() const { return iterator(GetArrayNode()->begin()); }
@@ -946,7 +946,7 @@ inline constexpr bool use_default_type_traits_v<Array<T>> = false;
template <typename T>
struct TypeTraits<Array<T>> : public TypeTraitsBase {
- static TVM_FFI_INLINE void ConvertToAnyView(const Array<T>& src, TVMFFIAny* result) {
+ static TVM_FFI_INLINE void CopyToAnyView(const Array<T>& src, TVMFFIAny* result) {
TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetTVMFFIObjectPtrFromObjectRef(src);
result->type_index = obj_ptr->type_index;
result->v_obj = obj_ptr;
@@ -992,13 +992,13 @@ struct TypeTraits<Array<T>> : public TypeTraitsBase {
}
}
- static TVM_FFI_INLINE Array<T> ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ static TVM_FFI_INLINE Array<T> CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
if (src->type_index == TypeIndex::kTVMFFINone) return Array<T>(nullptr);
return Array<T>(details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(src->v_obj));
}
- static TVM_FFI_INLINE std::optional<Array<T>> TryConvertFromAnyView(const TVMFFIAny* src) {
- if (CheckAnyView(src)) return ConvertFromAnyViewAfterCheck(src);
+ static TVM_FFI_INLINE std::optional<Array<T>> TryCopyFromAnyView(const TVMFFIAny* src) {
+ if (CheckAnyView(src)) return CopyFromAnyViewAfterCheck(src);
return std::nullopt;
}
diff --git a/ffi/include/tvm/ffi/container/base.h b/ffi/include/tvm/ffi/container/container_details.h
similarity index 95%
rename from ffi/include/tvm/ffi/container/base.h
rename to ffi/include/tvm/ffi/container/container_details.h
index bfeeacd59b..9df7758524 100644
--- a/ffi/include/tvm/ffi/container/base.h
+++ b/ffi/include/tvm/ffi/container/container_details.h
@@ -18,21 +18,21 @@
*/
/*!
- * \file tvm/ffi/container/base.h
- * \brief Base utilities for common POD(plain old data) container types.
+ * \file tvm/ffi/container/container_details.h
+ * \brief Common utilities for container types.
*/
-#ifndef TVM_FFI_CONTAINER_BASE_H_
-#define TVM_FFI_CONTAINER_BASE_H_
+#ifndef TVM_FFI_CONTAINER_CONTAINER_DETAILS_H_
+#define TVM_FFI_CONTAINER_CONTAINER_DETAILS_H_
#include <tvm/ffi/memory.h>
#include <tvm/ffi/object.h>
-#include <algorithm>
-#include <initializer_list>
+#include <type_traits>
#include <utility>
namespace tvm {
namespace ffi {
+namespace details {
/*!
* \brief Base template for classes with array like memory layout.
*
@@ -46,7 +46,7 @@ namespace ffi {
*
* \code
* // Example usage of the template to define a simple array wrapper
- * class ArrayNode : public InplaceArrayBase<ArrayNode, Elem> {
+ * class ArrayNode : public tvm::ffi::details::InplaceArrayBase<ArrayNode, Elem> {
* public:
* // Wrap EmplaceInit to initialize the elements
* template <typename Iterator>
@@ -263,6 +263,7 @@ class ReverseIterAdapter {
private:
TIter iter_;
};
+} // namespace details
} // namespace ffi
} // namespace tvm
-#endif // TVM_FFI_CONTAINER_BASE_H_
+#endif // TVM_FFI_CONTAINER_CONTAINER_DETAILS_H_
diff --git a/ffi/include/tvm/ffi/container/optional.h b/ffi/include/tvm/ffi/container/optional.h
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/ffi/include/tvm/ffi/error.h b/ffi/include/tvm/ffi/error.h
index f894166ef3..1f703f1fac 100644
--- a/ffi/include/tvm/ffi/error.h
+++ b/ffi/include/tvm/ffi/error.h
@@ -24,8 +24,8 @@
#ifndef TVM_FFI_ERROR_H_
#define TVM_FFI_ERROR_H_
+#include <tvm/ffi/base_details.h>
#include <tvm/ffi/c_api.h>
-#include <tvm/ffi/internal_utils.h>
#include <tvm/ffi/memory.h>
#include <tvm/ffi/object.h>
@@ -33,6 +33,7 @@
#include <memory>
#include <sstream>
#include <string>
+#include <utility>
/*!
* \brief Macro defines whether we enable libbacktrace
@@ -55,7 +56,7 @@ namespace ffi {
/*!
* \brief Error object class.
*/
-class ErrorObj: public Object {
+class ErrorObj : public Object {
public:
/*! \brief The error kind */
std::string kind;
@@ -76,9 +77,7 @@ class ErrorObj: public Object {
* \brief Managed reference to ErrorObj
* \sa Error Object
*/
-class Error :
- public ObjectRef,
- public std::exception {
+class Error : public ObjectRef, public std::exception {
public:
Error(std::string kind, std::string message, std::string backtrace) {
std::ostringstream what;
@@ -91,9 +90,7 @@ class Error :
data_ = std::move(n);
}
- const char* what() const noexcept(true) override {
- return get()->what_str.c_str();
- }
+ const char* what() const noexcept(true) override { return get()->what_str.c_str(); }
TVM_FFI_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(Error, ObjectRef, ErrorObj);
};
diff --git a/ffi/include/tvm/ffi/function.h b/ffi/include/tvm/ffi/function.h
index 81b0d8fab2..e2adee03c0 100644
--- a/ffi/include/tvm/ffi/function.h
+++ b/ffi/include/tvm/ffi/function.h
@@ -23,12 +23,15 @@
#ifndef TVM_FFI_FUNCTION_H_
#define TVM_FFI_FUNCTION_H_
-#include <tvm/ffi/c_api.h>
-#include <tvm/ffi/internal_utils.h>
#include <tvm/ffi/any.h>
+#include <tvm/ffi/base_details.h>
+#include <tvm/ffi/c_api.h>
#include <tvm/ffi/error.h>
#include <tvm/ffi/function_details.h>
+#include <string>
+#include <utility>
+
namespace tvm {
namespace ffi {
@@ -38,7 +41,7 @@ namespace ffi {
*/
class FunctionObj : public Object {
public:
- typedef void (*FCall)(const FunctionObj*, int32_t, const AnyView* , Any*);
+ typedef void (*FCall)(const FunctionObj*, int32_t, const AnyView*, Any*);
/*! \brief A C++ style call implementation */
FCall call;
/*! \brief A C API compatible call with exception catching. */
@@ -61,15 +64,14 @@ class FunctionObj : public Object {
static int32_t SafeCall(void* func, int32_t num_args, const TVMFFIAny* args, TVMFFIAny* result) {
FunctionObj* self = static_cast<FunctionObj*>(func);
try {
- self->call(self, num_args, reinterpret_cast<const AnyView*>(args), reinterpret_cast<Any*>(result));
+ self->call(self, num_args, reinterpret_cast<const AnyView*>(args),
+ reinterpret_cast<Any*>(result));
return 0;
} catch (const tvm::ffi::Error& err) {
Any(std::move(err)).MoveToTVMFFIAny(result);
return 1;
} catch (const std::runtime_error& err) {
- Any(
- tvm::ffi::Error("RuntimeError", err.what(), "")
- ).MoveToTVMFFIAny(result);
+ Any(tvm::ffi::Error("RuntimeError", err.what(), "")).MoveToTVMFFIAny(result);
return 1;
}
TVM_FFI_UNREACHABLE();
@@ -94,8 +96,7 @@ class FunctionObjImpl : public FunctionObj {
* \brief Derived object class for constructing PackedFuncObj.
* \param callable The type-erased callable object.
*/
- explicit FunctionObjImpl(TCallable callable)
- : callable_(callable) {
+ explicit FunctionObjImpl(TCallable callable) : callable_(callable) {
this->call = Call;
this->safe_call = SafeCall;
}
@@ -116,11 +117,10 @@ class FunctionObjImpl : public FunctionObj {
*/
template <typename Derived>
struct RedirectCallToSafeCall {
- static void Call(const FunctionObj* func, int32_t num_args, const AnyView* args , Any* rv) {
+ static void Call(const FunctionObj* func, int32_t num_args, const AnyView* args, Any* rv) {
Derived* self = static_cast<Derived*>(const_cast<FunctionObj*>(func));
- int ret_code = self->RedirectSafeCall(
- num_args, reinterpret_cast<const TVMFFIAny*>(args),
- reinterpret_cast<TVMFFIAny*>(rv));
+ int ret_code = self->RedirectSafeCall(num_args, reinterpret_cast<const TVMFFIAny*>(args),
+ reinterpret_cast<TVMFFIAny*>(rv));
if (ret_code != 0) {
if (std::optional<tvm::ffi::Error> err = rv->TryAs<tvm::ffi::Error>()) {
throw std::move(*err);
@@ -139,23 +139,21 @@ struct RedirectCallToSafeCall {
/*!
* \brief FunctionObj specialization that leverages C-style callback definitions.
*/
-class ExternCFunctionObjImpl :
- public FunctionObj,
- public RedirectCallToSafeCall<ExternCFunctionObjImpl> {
+class ExternCFunctionObjImpl : public FunctionObj,
+ public RedirectCallToSafeCall<ExternCFunctionObjImpl> {
public:
using RedirectCallToSafeCall<ExternCFunctionObjImpl>::SafeCall;
ExternCFunctionObjImpl(void* self, TVMFFISafeCallType safe_call, void (*deleter)(void* self))
- : self_(self), safe_call_(safe_call), deleter_(deleter) {
+ : self_(self), safe_call_(safe_call), deleter_(deleter) {
this->call = RedirectCallToSafeCall<ExternCFunctionObjImpl>::Call;
this->safe_call = RedirectCallToSafeCall<ExternCFunctionObjImpl>::SafeCall;
}
- ~ExternCFunctionObjImpl() {
- deleter_(self_);
- }
+ ~ExternCFunctionObjImpl() { deleter_(self_); }
- TVM_FFI_INLINE int32_t RedirectSafeCall(int32_t num_args, const TVMFFIAny* args, TVMFFIAny* rv) const {
+ TVM_FFI_INLINE int32_t RedirectSafeCall(int32_t num_args, const TVMFFIAny* args,
+ TVMFFIAny* rv) const {
return safe_call_(self_, num_args, args, rv);
}
@@ -168,19 +166,18 @@ class ExternCFunctionObjImpl :
/*!
* \brief FunctionObj specialization that wraps an external function.
*/
-class ImportedFunctionObjImpl :
- public FunctionObj,
- public RedirectCallToSafeCall<ImportedFunctionObjImpl> {
+class ImportedFunctionObjImpl : public FunctionObj,
+ public RedirectCallToSafeCall<ImportedFunctionObjImpl> {
public:
using RedirectCallToSafeCall<ImportedFunctionObjImpl>::SafeCall;
- explicit ImportedFunctionObjImpl(ObjectPtr<Object> data)
- : data_(data) {
+ explicit ImportedFunctionObjImpl(ObjectPtr<Object> data) : data_(data) {
this->call = RedirectCallToSafeCall<ImportedFunctionObjImpl>::Call;
this->safe_call = RedirectCallToSafeCall<ImportedFunctionObjImpl>::SafeCall;
}
- TVM_FFI_INLINE int32_t RedirectSafeCall(int32_t num_args, const TVMFFIAny* args, TVMFFIAny* rv) const {
+ TVM_FFI_INLINE int32_t RedirectSafeCall(int32_t num_args, const TVMFFIAny* args,
+ TVMFFIAny* rv) const {
FunctionObj* func = const_cast<FunctionObj*>(static_cast<const FunctionObj*>(data_.get()));
return func->safe_call(func, num_args, args, rv);
}
@@ -192,12 +189,12 @@ class ImportedFunctionObjImpl :
// Helper class to set packed arguments
class PackedArgsSetter {
public:
- PackedArgsSetter(AnyView* args) : args_(args) {}
+ explicit PackedArgsSetter(AnyView* args) : args_(args) {}
// NOTE: setter needs to be very carefully designed
// such that we do not have temp variable conversion(eg. convert from lvalue to rvalue)
// that is why we need T&& and std::forward here
- template<typename T>
+ template <typename T>
TVM_FFI_INLINE void operator()(size_t i, T&& value) const {
args_[i].operator=(std::forward<T>(value));
}
@@ -215,17 +212,17 @@ class Function : public ObjectRef {
public:
/*! \brief Constructor from null */
Function(std::nullptr_t) : ObjectRef(nullptr) {} // NOLINT(*)
- /*!
- * \brief Constructing a packed function from a callable type
- * whose signature is consistent with `PackedFunc`
- * \param packed_call The packed function signature
- */
+ /*!
+ * \brief Constructing a packed function from a callable type
+ * whose signature is consistent with `PackedFunc`
+ * \param packed_call The packed function signature
+ */
template <typename TCallable>
static Function FromPacked(TCallable packed_call) {
static_assert(
- std::is_convertible_v<TCallable, std::function<void(int32_t, const AnyView*, Any*)>>,
- "tvm::ffi::Function::FromPacked requires input function signature to match packed func format"
- );
+ std::is_convertible_v<TCallable, std::function<void(int32_t, const AnyView*, Any*)>>,
+ "tvm::ffi::Function::FromPacked requires input function signature to match packed func "
+ "format");
using ObjType = typename details::FunctionObjImpl<TCallable>;
Function func;
func.data_ = make_object<ObjType>(std::forward<TCallable>(packed_call));
@@ -261,7 +258,8 @@ class Function : public ObjectRef {
* \param deleter The deleter to release the resource of self.
* \return The created function.
*/
- static Function FromExternC(void* self, TVMFFISafeCallType safe_call, void (*deleter)(void* self)) {
+ static Function FromExternC(void* self, TVMFFISafeCallType safe_call,
+ void (*deleter)(void* self)) {
// the other function coems from a different library
Function func;
func.data_ = make_object<details::ExternCFunctionObjImpl>(self, safe_call, deleter);
@@ -276,7 +274,8 @@ class Function : public ObjectRef {
static Function FromUnpacked(TCallable callable) {
using FuncInfo = details::FunctionInfo<TCallable>;
auto call_packed = [callable](int32_t num_args, const AnyView* args, Any* rv) -> void {
- details::unpack_call<typename FuncInfo::RetType, FuncInfo::num_args>(nullptr, callable, num_args, args, rv);
+ details::unpack_call<typename FuncInfo::RetType, FuncInfo::num_args>(nullptr, callable,
+ num_args, args, rv);
};
return FromPacked(call_packed);
}
@@ -326,7 +325,7 @@ class Function : public ObjectRef {
* \param rv The return value.
*/
TVM_FFI_INLINE void CallPacked(int32_t num_args, const AnyView* args, Any* result) const {
- static_cast<FunctionObj*>(data_.get())->CallPacked(num_args, args, result);
+ static_cast<FunctionObj*>(data_.get())->CallPacked(num_args, args, result);
}
/*! \return Whether the packed function is nullptr */
bool operator==(std::nullptr_t) const { return data_ == nullptr; }
@@ -338,4 +337,4 @@ class Function : public ObjectRef {
} // namespace ffi
} // namespace tvm
-#endif // TVM_FFI_OBJECT_H_
+#endif // TVM_FFI_FUNCTION_H_
diff --git a/ffi/include/tvm/ffi/function_details.h b/ffi/include/tvm/ffi/function_details.h
index f22258029d..56b7ca7df3 100644
--- a/ffi/include/tvm/ffi/function_details.h
+++ b/ffi/include/tvm/ffi/function_details.h
@@ -23,40 +23,36 @@
#ifndef TVM_FFI_FUNCTION_DETAILS_H_
#define TVM_FFI_FUNCTION_DETAILS_H_
-#include <tvm/ffi/c_api.h>
-#include <tvm/ffi/internal_utils.h>
#include <tvm/ffi/any.h>
+#include <tvm/ffi/base_details.h>
+#include <tvm/ffi/c_api.h>
#include <tvm/ffi/error.h>
+#include <string>
+#include <tuple>
+#include <utility>
+
namespace tvm {
namespace ffi {
namespace details {
template <typename Type>
struct Type2Str {
- static std::string v() {
- return TypeTraitsNoCR<Type>::TypeStr();
- }
+ static std::string v() { return TypeTraitsNoCR<Type>::TypeStr(); }
};
template <>
struct Type2Str<Any> {
- static const char* v() {
- return "Any";
- }
+ static const char* v() { return "Any"; }
};
template <>
struct Type2Str<AnyView> {
- static const char* v() {
- return "AnyView";
- }
+ static const char* v() { return "AnyView"; }
};
template <>
struct Type2Str<void> {
- static const char* v() {
- return "void";
- }
+ static const char* v() { return "void"; }
};
template <typename ArgType>
@@ -70,24 +66,22 @@ struct Arg2Str {
os << i << ": " << Type2Str<Arg>::v();
}
template <size_t... I>
- static TVM_FFI_INLINE void Run(std::ostream &os, std::index_sequence<I...>) {
+ static TVM_FFI_INLINE void Run(std::ostream& os, std::index_sequence<I...>) {
using TExpander = int[];
(void)TExpander{0, (Apply<I>(os), 0)...};
}
};
template <typename T>
-static constexpr bool ArgSupported = (
- std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>, Any> ||
- std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>, AnyView> ||
- TypeTraitsNoCR<T>::enabled
-);
+static constexpr bool ArgSupported =
+ (std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>, Any> ||
+ std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>, AnyView> ||
+ TypeTraitsNoCR<T>::enabled);
// NOTE: return type can only support non-reference managed returns
template <typename T>
-static constexpr bool RetSupported = (
- std::is_same_v<T, Any> || std::is_void_v<T> || TypeTraits<T>::enabled
-);
+static constexpr bool RetSupported =
+ (std::is_same_v<T, Any> || std::is_void_v<T> || TypeTraits<T>::enabled);
template <typename R, typename... Args>
struct FuncFunctorImpl {
@@ -113,9 +107,9 @@ template <typename T>
struct FunctionInfoHelper;
template <typename T, typename R, typename... Args>
-struct FunctionInfoHelper<R (T::*)(Args...)>: FuncFunctorImpl<R, Args...> {};
+struct FunctionInfoHelper<R (T::*)(Args...)> : FuncFunctorImpl<R, Args...> {};
template <typename T, typename R, typename... Args>
-struct FunctionInfoHelper<R (T::*)(Args...) const>: FuncFunctorImpl<R, Args...> {};
+struct FunctionInfoHelper<R (T::*)(Args...) const> : FuncFunctorImpl<R, Args...> {};
/*!
* \brief Template class to get function signature of a function or functor.
@@ -133,15 +127,15 @@ struct FunctionInfo<R (*)(Args...)> : FuncFunctorImpl<R, Args...> {};
/*! \brief Using static function to output TypedPackedFunc signature */
typedef std::string (*FGetFuncSignature)();
-template<typename T>
+template <typename T>
TVM_FFI_INLINE std::optional<T> TryAs(AnyView arg) {
return arg.TryAs<T>();
}
-template<>
+template <>
TVM_FFI_INLINE std::optional<Any> TryAs<Any>(AnyView arg) {
return Any(arg);
}
-template<>
+template <>
TVM_FFI_INLINE std::optional<AnyView> TryAs<AnyView>(AnyView arg) {
return arg;
}
@@ -159,14 +153,10 @@ class MovableArgValueWithContext {
* \param f_sig Pointer to static function outputting signature of the function being called.
* named.
*/
- TVM_FFI_INLINE MovableArgValueWithContext(
- const AnyView* args, int32_t arg_index,
- const std::string* optional_name,
- FGetFuncSignature f_sig)
- : args_(args),
- arg_index_(arg_index),
- optional_name_(optional_name),
- f_sig_(f_sig) {}
+ TVM_FFI_INLINE MovableArgValueWithContext(const AnyView* args, int32_t arg_index,
+ const std::string* optional_name,
+ FGetFuncSignature f_sig)
+ : args_(args), arg_index_(arg_index), optional_name_(optional_name), f_sig_(f_sig) {}
template <typename Type>
TVM_FFI_INLINE operator Type() {
@@ -175,12 +165,11 @@ class MovableArgValueWithContext {
if (opt.has_value()) {
return std::move(*opt);
}
- TVM_FFI_THROW(TypeError)
- << "Mismatched type on argument #" << arg_index_ << " when calling: `"
- << (optional_name_ == nullptr ? "" : *optional_name_)
- << (f_sig_ == nullptr ? "" : (*f_sig_)()) << "`. Expected `"
- << Type2Str<Type>::v() << "` but got `"
- << TypeIndex2TypeKey(args_[arg_index_].type_index()) << "`";
+ TVM_FFI_THROW(TypeError) << "Mismatched type on argument #" << arg_index_ << " when calling: `"
+ << (optional_name_ == nullptr ? "" : *optional_name_)
+ << (f_sig_ == nullptr ? "" : (*f_sig_)()) << "`. Expected `"
+ << Type2Str<Type>::v() << "` but got `"
+ << TypeIndex2TypeKey(args_[arg_index_].type_index()) << "`";
}
private:
@@ -193,8 +182,8 @@ class MovableArgValueWithContext {
template <typename R, int nleft, int index, typename F>
struct unpack_call_dispatcher {
template <typename... Args>
- TVM_FFI_INLINE static void run(const std::string* optional_name, FGetFuncSignature f_sig, const F& f,
- int32_t num_args, const AnyView* args, Any* rv,
+ TVM_FFI_INLINE static void run(const std::string* optional_name, FGetFuncSignature f_sig,
+ const F& f, int32_t num_args, const AnyView* args, Any* rv,
Args&&... unpacked_args) {
// construct a movable argument value
// which allows potential move of argument to the input of F.
@@ -207,9 +196,8 @@ struct unpack_call_dispatcher {
template <typename R, int index, typename F>
struct unpack_call_dispatcher<R, 0, index, F> {
template <typename... Args>
- TVM_FFI_INLINE static void run(const std::string*, FGetFuncSignature, const F& f,
- int32_t, const AnyView*, Any* rv,
- Args&&... unpacked_args) {
+ TVM_FFI_INLINE static void run(const std::string*, FGetFuncSignature, const F& f, int32_t,
+ const AnyView*, Any* rv, Args&&... unpacked_args) {
using RetType = decltype(f(std::forward<Args>(unpacked_args)...));
if constexpr (std::is_same_v<RetType, R>) {
*rv = f(std::forward<Args>(unpacked_args)...);
@@ -222,25 +210,25 @@ struct unpack_call_dispatcher<R, 0, index, F> {
template <int index, typename F>
struct unpack_call_dispatcher<void, 0, index, F> {
template <typename... Args>
- TVM_FFI_INLINE static void run(const std::string* optional_name, FGetFuncSignature f_sig, const F& f,
- int32_t num_args, const AnyView* args, Any* rv,
- Args&&... unpacked_args) {
+ TVM_FFI_INLINE static void run(const std::string* optional_name, FGetFuncSignature f_sig,
+ const F& f, int32_t num_args, const AnyView* args, Any* rv,
+ Args&&... unpacked_args) {
f(std::forward<Args>(unpacked_args)...);
}
};
template <typename R, int nargs, typename F>
-TVM_FFI_INLINE void unpack_call(const std::string* optional_name, const F& f,
- int32_t num_args, const AnyView* args, Any* rv) {
+TVM_FFI_INLINE void unpack_call(const std::string* optional_name, const F& f, int32_t num_args,
+ const AnyView* args, Any* rv) {
using FuncInfo = FunctionInfo<F>;
FGetFuncSignature f_sig = FuncInfo::Sig;
- static_assert(FuncInfo::unpacked_supported, "The function signature cannot support unpacked call");
+ static_assert(FuncInfo::unpacked_supported,
+ "The function signature cannot support unpacked call");
if (nargs != num_args) {
- TVM_FFI_THROW(TypeError)
- << "Mismatched number of arguments when calling: `"
- << (optional_name == nullptr ? "" : *optional_name)
- << (f_sig == nullptr ? "" : (*f_sig)()) << "`. Expected "
- << nargs << " but got " << num_args << " arguments";
+ TVM_FFI_THROW(TypeError) << "Mismatched number of arguments when calling: `"
+ << (optional_name == nullptr ? "" : *optional_name)
+ << (f_sig == nullptr ? "" : (*f_sig)()) << "`. Expected " << nargs
+ << " but got " << num_args << " arguments";
}
unpack_call_dispatcher<R, nargs, 0, F>::run(optional_name, f_sig, f, num_args, args, rv);
}
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 13de74fa07..9e218899d7 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -23,8 +23,8 @@
#ifndef TVM_FFI_OBJECT_H_
#define TVM_FFI_OBJECT_H_
+#include <tvm/ffi/base_details.h>
#include <tvm/ffi/c_api.h>
-#include <tvm/ffi/internal_utils.h>
#include <type_traits>
#include <utility>
@@ -336,7 +336,6 @@ class ObjectPtr {
friend ObjectPtr<BaseType> GetObjectPtr(ObjType* ptr);
};
-
// Forward declaration, to prevent circular includes.
template <typename T>
class Optional;
@@ -469,9 +468,9 @@ inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
* \param TypeName The name of the current type.
* \param ParentType The name of the ParentType
*/
-#define TVM_FFI_DECLARE_STATIC_OBJECT_INFO(TypeName, ParentType) \
- TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType); \
- static int32_t RuntimeTypeIndex() { return TypeName::_type_index; }\
+#define TVM_FFI_DECLARE_STATIC_OBJECT_INFO(TypeName, ParentType) \
+ TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType); \
+ static int32_t RuntimeTypeIndex() { return TypeName::_type_index; } \
TVM_FFI_OBJECT_STATIC_DEFS(TypeName, ParentType)
/*!
@@ -499,10 +498,9 @@ inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
*/
#define TVM_FFI_DECLARE_FINAL_OBJECT_INFO(TypeName, ParentType) \
static const constexpr int _type_child_slots = 0; \
- static const constexpr bool _type_final = true; \
+ static const constexpr bool _type_final = true; \
TVM_FFI_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType)
-
/*
* \brief Define object reference methods.
* \param TypeName The object type name
diff --git a/ffi/include/tvm/ffi/type_traits.h b/ffi/include/tvm/ffi/type_traits.h
index be46639642..9fc38f7354 100644
--- a/ffi/include/tvm/ffi/type_traits.h
+++ b/ffi/include/tvm/ffi/type_traits.h
@@ -23,12 +23,13 @@
#ifndef TVM_FFI_TYPE_TRAITS_H_
#define TVM_FFI_TYPE_TRAITS_H_
+#include <tvm/ffi/base_details.h>
#include <tvm/ffi/c_api.h>
#include <tvm/ffi/error.h>
-#include <tvm/ffi/internal_utils.h>
#include <tvm/ffi/object.h>
#include <optional>
+#include <string>
#include <type_traits>
namespace tvm {
@@ -72,11 +73,11 @@ inline std::string TypeIndex2TypeKey(int32_t type_index) {
*
* We need to implement the following conversion functions
*
- * - void ConvertToAnyView(const T& src, TVMFFIAny* result);
+ * - void CopyToAnyView(const T& src, TVMFFIAny* result);
*
* Convert a value to AnyView
*
- * - std::optional<T> TryConvertFromAnyView(const TVMFFIAny* src);
+ * - std::optional<T> TryCopyFromAnyView(const TVMFFIAny* src);
*
* Try convert AnyView to a value type.
*/
@@ -99,7 +100,7 @@ struct TypeTraitsBase {
static constexpr bool enabled = true;
// get mismatched type when result mismatches the trait.
- // this function is called after TryConvertFromAnyView fails
+ // this function is called after TryCopyFromAnyView fails
// to get more detailed type information in runtime
// especially when the error involves nested container type
static TVM_FFI_INLINE std::string GetMismatchTypeInfo(const TVMFFIAny* source) {
@@ -110,7 +111,7 @@ struct TypeTraitsBase {
// None
template <>
struct TypeTraits<std::nullptr_t> : public TypeTraitsBase {
- static TVM_FFI_INLINE void ConvertToAnyView(const std::nullptr_t&, TVMFFIAny* result) {
+ static TVM_FFI_INLINE void CopyToAnyView(const std::nullptr_t&, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFINone;
// invariant: the pointer field also equals nullptr
// this will simplify the recovery of nullable object from the any
@@ -124,7 +125,7 @@ struct TypeTraits<std::nullptr_t> : public TypeTraitsBase {
result->v_int64 = 0;
}
- static TVM_FFI_INLINE std::optional<std::nullptr_t> TryConvertFromAnyView(const TVMFFIAny* src) {
+ static TVM_FFI_INLINE std::optional<std::nullptr_t> TryCopyFromAnyView(const TVMFFIAny* src) {
if (src->type_index == TypeIndex::kTVMFFINone) {
return nullptr;
}
@@ -135,7 +136,7 @@ struct TypeTraits<std::nullptr_t> : public TypeTraitsBase {
return src->type_index == TypeIndex::kTVMFFINone;
}
- static TVM_FFI_INLINE std::nullptr_t ConvertFromAnyViewAfterCheck(const TVMFFIAny*) {
+ static TVM_FFI_INLINE std::nullptr_t CopyFromAnyViewAfterCheck(const TVMFFIAny*) {
return nullptr;
}
@@ -145,16 +146,14 @@ struct TypeTraits<std::nullptr_t> : public TypeTraitsBase {
// Integer POD values
template <typename Int>
struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> : public TypeTraitsBase {
- static TVM_FFI_INLINE void ConvertToAnyView(const Int& src, TVMFFIAny* result) {
+ static TVM_FFI_INLINE void CopyToAnyView(const Int& src, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFIInt;
result->v_int64 = static_cast<int64_t>(src);
}
- static TVM_FFI_INLINE void MoveToAny(Int src, TVMFFIAny* result) {
- ConvertToAnyView(src, result);
- }
+ static TVM_FFI_INLINE void MoveToAny(Int src, TVMFFIAny* result) { CopyToAnyView(src, result); }
- static TVM_FFI_INLINE std::optional<Int> TryConvertFromAnyView(const TVMFFIAny* src) {
+ static TVM_FFI_INLINE std::optional<Int> TryCopyFromAnyView(const TVMFFIAny* src) {
if (src->type_index == TypeIndex::kTVMFFIInt) {
return std::make_optional<Int>(src->v_int64);
}
@@ -165,7 +164,7 @@ struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> : public TypeT
return src->type_index == TypeIndex::kTVMFFIInt;
}
- static TVM_FFI_INLINE int ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ static TVM_FFI_INLINE int CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
return static_cast<Int>(src->v_int64);
}
@@ -176,16 +175,14 @@ struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> : public TypeT
template <typename Float>
struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>>
: public TypeTraitsBase {
- static TVM_FFI_INLINE void ConvertToAnyView(const Float& src, TVMFFIAny* result) {
+ static TVM_FFI_INLINE void CopyToAnyView(const Float& src, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFIFloat;
result->v_float64 = static_cast<double>(src);
}
- static TVM_FFI_INLINE void MoveToAny(Float src, TVMFFIAny* result) {
- ConvertToAnyView(src, result);
- }
+ static TVM_FFI_INLINE void MoveToAny(Float src, TVMFFIAny* result) { CopyToAnyView(src, result); }
- static TVM_FFI_INLINE std::optional<Float> TryConvertFromAnyView(const TVMFFIAny* src) {
+ static TVM_FFI_INLINE std::optional<Float> TryCopyFromAnyView(const TVMFFIAny* src) {
if (src->type_index == TypeIndex::kTVMFFIFloat) {
return std::make_optional<Float>(src->v_float64);
} else if (src->type_index == TypeIndex::kTVMFFIInt) {
@@ -198,7 +195,7 @@ struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>>
return src->type_index == TypeIndex::kTVMFFIFloat || src->type_index == TypeIndex::kTVMFFIInt;
}
- static TVM_FFI_INLINE Float ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ static TVM_FFI_INLINE Float CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
if (src->type_index == TypeIndex::kTVMFFIFloat) {
return static_cast<Float>(src->v_float64);
} else {
@@ -212,16 +209,14 @@ struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>>
// void*
template <>
struct TypeTraits<void*> : public TypeTraitsBase {
- static TVM_FFI_INLINE void ConvertToAnyView(void* src, TVMFFIAny* result) {
+ static TVM_FFI_INLINE void CopyToAnyView(void* src, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFIOpaquePtr;
result->v_ptr = src;
}
- static TVM_FFI_INLINE void MoveToAny(void* src, TVMFFIAny* result) {
- ConvertToAnyView(src, result);
- }
+ static TVM_FFI_INLINE void MoveToAny(void* src, TVMFFIAny* result) { CopyToAnyView(src, result); }
- static TVM_FFI_INLINE std::optional<void*> TryConvertFromAnyView(const TVMFFIAny* src) {
+ static TVM_FFI_INLINE std::optional<void*> TryCopyFromAnyView(const TVMFFIAny* src) {
if (src->type_index == TypeIndex::kTVMFFIOpaquePtr) {
return std::make_optional<void*>(src->v_ptr);
}
@@ -236,9 +231,7 @@ struct TypeTraits<void*> : public TypeTraitsBase {
src->type_index == TypeIndex::kTVMFFINone;
}
- static TVM_FFI_INLINE void* ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
- return src->v_ptr;
- }
+ static TVM_FFI_INLINE void* CopyFromAnyViewAfterCheck(const TVMFFIAny* src) { return src->v_ptr; }
static TVM_FFI_INLINE std::string TypeStr() { return "void*"; }
};
@@ -250,7 +243,7 @@ struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef
: public TypeTraitsBase {
using ContainerType = typename TObjRef::ContainerType;
- static TVM_FFI_INLINE void ConvertToAnyView(const TObjRef& src, TVMFFIAny* result) {
+ static TVM_FFI_INLINE void CopyToAnyView(const TObjRef& src, TVMFFIAny* result) {
TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetTVMFFIObjectPtrFromObjectRef(src);
result->type_index = obj_ptr->type_index;
result->v_obj = obj_ptr;
@@ -268,14 +261,14 @@ struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef
(src->type_index == kTVMFFINone && TObjRef::_type_is_nullable);
}
- static TVM_FFI_INLINE TObjRef ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ static TVM_FFI_INLINE TObjRef CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
if constexpr (TObjRef::_type_is_nullable) {
if (src->type_index == kTVMFFINone) return TObjRef(nullptr);
}
return TObjRef(details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(src->v_obj));
}
- static TVM_FFI_INLINE std::optional<TObjRef> TryConvertFromAnyView(const TVMFFIAny* src) {
+ static TVM_FFI_INLINE std::optional<TObjRef> TryCopyFromAnyView(const TVMFFIAny* src) {
if (src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
if (details::IsObjectInstance<ContainerType>(src->type_index)) {
return TObjRef(details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(src->v_obj));
@@ -293,7 +286,7 @@ struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef
// Traits for ObjectPtr
template <typename T>
struct TypeTraits<ObjectPtr<T>> : public TypeTraitsBase {
- static TVM_FFI_INLINE void ConvertToAnyView(const ObjectPtr<T>& src, TVMFFIAny* result) {
+ static TVM_FFI_INLINE void CopyToAnyView(const ObjectPtr<T>& src, TVMFFIAny* result) {
TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetTVMFFIObjectPtrFromObjectPtr(src);
result->type_index = obj_ptr->type_index;
result->v_obj = obj_ptr;
@@ -310,12 +303,12 @@ struct TypeTraits<ObjectPtr<T>> : public TypeTraitsBase {
details::IsObjectInstance<T>(src->type_index);
}
- static TVM_FFI_INLINE ObjectPtr<T> ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ static TVM_FFI_INLINE ObjectPtr<T> CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
return details::ObjectUnsafe::ObjectPtrFromUnowned<T>(src->v_obj);
}
- static TVM_FFI_INLINE std::optional<ObjectPtr<T>> TryConvertFromAnyView(const TVMFFIAny* src) {
- if (CheckAnyView(src)) return ConvertFromAnyViewAfterCheck(src);
+ static TVM_FFI_INLINE std::optional<ObjectPtr<T>> TryCopyFromAnyView(const TVMFFIAny* src) {
+ if (CheckAnyView(src)) return CopyFromAnyViewAfterCheck(src);
return std::nullopt;
}
@@ -326,7 +319,7 @@ struct TypeTraits<ObjectPtr<T>> : public TypeTraitsBase {
template <typename TObject>
struct TypeTraits<const TObject*, std::enable_if_t<std::is_base_of_v<Object, TObject>>>
: public TypeTraitsBase {
- static TVM_FFI_INLINE void ConvertToAnyView(const TObject* src, TVMFFIAny* result) {
+ static TVM_FFI_INLINE void CopyToAnyView(const TObject* src, TVMFFIAny* result) {
TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetHeader(src);
result->type_index = obj_ptr->type_index;
result->v_obj = obj_ptr;
@@ -345,12 +338,12 @@ struct TypeTraits<const TObject*, std::enable_if_t<std::is_base_of_v<Object, TOb
details::IsObjectInstance<TObject>(src->type_index);
}
- static TVM_FFI_INLINE const TObject* ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ static TVM_FFI_INLINE const TObject* CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
return reinterpret_cast<const TObject*>(src->v_obj);
}
- static TVM_FFI_INLINE std::optional<const TObject*> TryConvertFromAnyView(const TVMFFIAny* src) {
- if (CheckAnyView(src)) return ConvertFromAnyViewAfterCheck(src);
+ static TVM_FFI_INLINE std::optional<const TObject*> TryCopyFromAnyView(const TVMFFIAny* src) {
+ if (CheckAnyView(src)) return CopyFromAnyViewAfterCheck(src);
return std::nullopt;
}
diff --git a/ffi/scripts/run_tests.sh b/ffi/scripts/run_tests.sh
index 0d4efe6bf2..8219afc90d 100755
--- a/ffi/scripts/run_tests.sh
+++ b/ffi/scripts/run_tests.sh
@@ -1,11 +1,11 @@
#!/bin/bash
set -euxo pipefail
-HEADER_ONLY=OFF
-BUILD_TYPE=RelWithDebInfo
+TVM_FFI_ALLOW_DYN_TYPE=ON
+BUILD_TYPE=Release
rm -rf build/CMakeFiles build/CMakeCache.txt
-cmake -G Ninja -S . -B build -DTVM_FFI_ALLOW_DYN_TYPE=${HEADER_ONLY} -DTVM_FFI_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
+cmake -G Ninja -S . -B build -DTVM_FFI_ALLOW_DYN_TYPE=${TVM_FFI_ALLOW_DYN_TYPE} -DTVM_FFI_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
-DTVM_FFI_BUILD_REGISTRY=ON \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
cmake --build build --parallel 16 --clean-first --config ${BUILD_TYPE} --target tvm_ffi_tests
diff --git a/ffi/src/ffi/traceback.h b/ffi/src/ffi/traceback.h
index e4ba2a589d..d2584081a7 100644
--- a/ffi/src/ffi/traceback.h
+++ b/ffi/src/ffi/traceback.h
@@ -130,4 +130,4 @@ struct TracebackStorage {
} // namespace ffi
} // namespace tvm
-#endif
+#endif // TVM_FFI_TRACEBACK_H_
diff --git a/ffi/tests/example/test_error.cc b/ffi/tests/example/test_error.cc
index bf43f97ad5..0e8ce28b44 100644
--- a/ffi/tests/example/test_error.cc
+++ b/ffi/tests/example/test_error.cc
@@ -23,25 +23,24 @@ namespace {
using namespace tvm::ffi;
-void ThrowRuntimeError() {
- TVM_FFI_THROW(RuntimeError)
- << "test0";
-}
+void ThrowRuntimeError() { TVM_FFI_THROW(RuntimeError) << "test0"; }
TEST(Error, Traceback) {
- EXPECT_THROW({
- try {
- ThrowRuntimeError();
- } catch (const Error& error) {
- EXPECT_EQ(error->message, "test0");
- EXPECT_EQ(error->kind, "RuntimeError");
- std::string what = error.what();
- EXPECT_NE(what.find("line"), std::string::npos);
- EXPECT_NE(what.find("ThrowRuntimeError"), std::string::npos);
- EXPECT_NE(what.find("RuntimeError: test0"), std::string::npos);
- throw;
- }
- }, ::tvm::ffi::Error);
+ EXPECT_THROW(
+ {
+ try {
+ ThrowRuntimeError();
+ } catch (const Error& error) {
+ EXPECT_EQ(error->message, "test0");
+ EXPECT_EQ(error->kind, "RuntimeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("line"), std::string::npos);
+ EXPECT_NE(what.find("ThrowRuntimeError"), std::string::npos);
+ EXPECT_NE(what.find("RuntimeError: test0"), std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
}
TEST(CheckError, Traceback) {
diff --git a/ffi/tests/example/test_function.cc b/ffi/tests/example/test_function.cc
index 46c69c3739..99e7819b23 100644
--- a/ffi/tests/example/test_function.cc
+++ b/ffi/tests/example/test_function.cc
@@ -18,8 +18,8 @@
*/
#include <gtest/gtest.h>
#include <tvm/ffi/any.h>
-#include <tvm/ffi/memory.h>
#include <tvm/ffi/function.h>
+#include <tvm/ffi/memory.h>
#include "./testing_object.h"
@@ -29,95 +29,87 @@ using namespace tvm::ffi;
using namespace tvm::ffi::testing;
TEST(Func, FromPacked) {
- Function fadd1 = Function::FromPacked(
- [](int32_t num_args, const AnyView* args, Any* rv) {
- EXPECT_EQ(num_args, 1);
- int32_t a = args[0];
- *rv = a + 1;
- }
- );
+ Function fadd1 = Function::FromPacked([](int32_t num_args, const AnyView* args, Any* rv) {
+ EXPECT_EQ(num_args, 1);
+ int32_t a = args[0];
+ *rv = a + 1;
+ });
int b = fadd1(1);
EXPECT_EQ(b, 2);
- Function fadd2 = Function::FromPacked(
- [](int32_t num_args, const AnyView* args, Any* rv) {
- EXPECT_EQ(num_args, 1);
- TInt a = args[0];
- EXPECT_EQ(a.use_count(), 2);
- *rv = a->value + 1;
- }
- );
+ Function fadd2 = Function::FromPacked([](int32_t num_args, const AnyView* args, Any* rv) {
+ EXPECT_EQ(num_args, 1);
+ TInt a = args[0];
+ EXPECT_EQ(a.use_count(), 2);
+ *rv = a->value + 1;
+ });
EXPECT_EQ(fadd2(TInt(12)).operator int(), 13);
}
TEST(Func, FromUnpacked) {
// try decution
- Function fadd1 = Function::FromUnpacked(
- [](const int32_t& a) -> int { return a + 1; }
- );
+ Function fadd1 = Function::FromUnpacked([](const int32_t& a) -> int { return a + 1; });
int b = fadd1(1);
EXPECT_EQ(b, 2);
- // convert that triggers error
+ // convert that triggers error
EXPECT_THROW(
{
try {
- fadd1(1.1);
+ fadd1(1.1);
} catch (const Error& error) {
EXPECT_EQ(error->kind, "TypeError");
- EXPECT_STREQ(
- error->message.c_str(),
- "Mismatched type on argument #0 when calling: `(0: int) -> int`. "
- "Expected `int` but got `float`");
+ EXPECT_STREQ(error->message.c_str(),
+ "Mismatched type on argument #0 when calling: `(0: int) -> int`. "
+ "Expected `int` but got `float`");
throw;
}
},
::tvm::ffi::Error);
- // convert that triggers error
- EXPECT_THROW(
+ // convert that triggers error
+ EXPECT_THROW(
{
try {
- fadd1();
+ fadd1();
} catch (const Error& error) {
EXPECT_EQ(error->kind, "TypeError");
- EXPECT_STREQ(
- error->message.c_str(),
- "Mismatched number of arguments when calling: `(0: int) -> int`. "
- "Expected 1 but got 0 arguments");
+ EXPECT_STREQ(error->message.c_str(),
+ "Mismatched number of arguments when calling: `(0: int) -> int`. "
+ "Expected 1 but got 0 arguments");
throw;
}
},
::tvm::ffi::Error);
// try decution
- Function fpass_and_return = Function::FromUnpacked(
- [](TInt x, int value, AnyView z) -> Function {
- EXPECT_EQ(x.use_count(), 2);
- EXPECT_EQ(x->value, value);
- if (auto opt = z.TryAs<int>()) {
- EXPECT_EQ(value, *opt);
- }
- return Function::FromUnpacked([value](int x) -> int { return x + value; });
- },
- "fpass_and_return");
- TInt a(11);
- Function fret = fpass_and_return(std::move(a), 11, 11);
- EXPECT_EQ(fret(12).operator int(), 23);
+ Function fpass_and_return = Function::FromUnpacked(
+ [](TInt x, int value, AnyView z) -> Function {
+ EXPECT_EQ(x.use_count(), 2);
+ EXPECT_EQ(x->value, value);
+ if (auto opt = z.TryAs<int>()) {
+ EXPECT_EQ(value, *opt);
+ }
+ return Function::FromUnpacked([value](int x) -> int { return x + value; });
+ },
+ "fpass_and_return");
+ TInt a(11);
+ Function fret = fpass_and_return(std::move(a), 11, 11);
+ EXPECT_EQ(fret(12).operator int(), 23);
- EXPECT_THROW(
- {
- try {
- fpass_and_return();
- } catch (const Error& error) {
- EXPECT_EQ(error->kind, "TypeError");
- EXPECT_STREQ(error->message.c_str(),
- "Mismatched number of arguments when calling: "
- "`fpass_and_return(0: test.Int, 1: int, 2: AnyView) -> object.Function`. "
- "Expected 3 but got 0 arguments");
- throw;
- }
- },
- ::tvm::ffi::Error);
+ EXPECT_THROW(
+ {
+ try {
+ fpass_and_return();
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ EXPECT_STREQ(error->message.c_str(),
+ "Mismatched number of arguments when calling: "
+ "`fpass_and_return(0: test.Int, 1: int, 2: AnyView) -> object.Function`. "
+ "Expected 3 but got 0 arguments");
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
}
} // namespace
diff --git a/ffi/tests/example/testing_object.h b/ffi/tests/example/testing_object.h
index 00cc58b7b7..e660b2751e 100644
--- a/ffi/tests/example/testing_object.h
+++ b/ffi/tests/example/testing_object.h
@@ -51,9 +51,7 @@ class TIntObj : public TNumberObj {
class TInt : public TNumber {
public:
- explicit TInt(int64_t value) {
- data_ = make_object<TIntObj>(value);
- }
+ explicit TInt(int64_t value) { data_ = make_object<TIntObj>(value); }
TVM_FFI_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(TInt, TNumber, TIntObj);
};
@@ -70,9 +68,7 @@ class TFloatObj : public TNumberObj {
class TFloat : public TNumber {
public:
- explicit TFloat(double value) {
- data_ = make_object<TFloatObj>(value);
- }
+ explicit TFloat(double value) { data_ = make_object<TFloatObj>(value); }
TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS(TFloat, TNumber, TFloatObj);
};
diff --git a/tests/lint/cpplint.sh b/tests/lint/cpplint.sh
index 84065e17b0..e49c6801ad 100755
--- a/tests/lint/cpplint.sh
+++ b/tests/lint/cpplint.sh
@@ -19,6 +19,7 @@
set -e
echo "Running 2 cpplints..."
+python3 3rdparty/dmlc-core/scripts/lint.py --quiet tvm cpp ffi/include ffi/src
python3 3rdparty/dmlc-core/scripts/lint.py --quiet tvm cpp \
include src \
examples/extension/src examples/graph_executor/src \
(tvm) 38/38: fix lint and minor compile
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit dbf7c372f3663bc400f41cf54f0c9e93940ffa40
Author: tqchen <ti...@gmail.com>
AuthorDate: Mon Mar 10 20:59:54 2025 -0400
fix lint and minor compile
---
ffi/cmake/Utils/AddLibbacktrace.cmake | 18 ++++++++----------
ffi/tests/cpp/test_string.cc | 2 +-
ffi/tests/cpp/testing_object.h | 2 +-
python/tvm/_ffi/libinfo.py | 2 ++
src/relax/backend/contrib/cutlass/codegen.cc | 2 +-
src/runtime/debug_compile.cc | 2 +-
tests/lint/check_file_type.py | 3 +++
web/Makefile | 3 ++-
8 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/ffi/cmake/Utils/AddLibbacktrace.cmake b/ffi/cmake/Utils/AddLibbacktrace.cmake
index a837ca7b3f..d534bd1e6c 100644
--- a/ffi/cmake/Utils/AddLibbacktrace.cmake
+++ b/ffi/cmake/Utils/AddLibbacktrace.cmake
@@ -13,14 +13,15 @@ function(_libbacktrace_compile)
file(MAKE_DIRECTORY ${_libbacktrace_prefix}/include)
file(MAKE_DIRECTORY ${_libbacktrace_prefix}/lib)
+
ExternalProject_Add(project_libbacktrace
PREFIX libbacktrace
SOURCE_DIR ${_libbacktrace_source}
BINARY_DIR ${_libbacktrace_prefix}
CONFIGURE_COMMAND
- ${_libbacktrace_source}/configure
+ "${_libbacktrace_source}/configure"
"--prefix=${_libbacktrace_prefix}"
- "--with-pic"
+ --with-pic
"CC=${_cmake_c_compiler}"
"CPP=${_cmake_c_compiler} -E"
"CFLAGS=${CMAKE_C_FLAGS}"
@@ -28,14 +29,11 @@ function(_libbacktrace_compile)
"NM=${CMAKE_NM}"
"STRIP=${CMAKE_STRIP}"
"--host=${MACHINE_NAME}"
- BUILD_COMMAND make -j
- BUILD_BYPRODUCTS ${_libbacktrace_prefix}/lib/libbacktrace.a ${_libbacktrace_prefix}/include/backtrace.h
- INSTALL_DIR ${_libbacktrace_prefix}
- INSTALL_COMMAND make install
- LOG_CONFIGURE ON
- LOG_INSTALL ON
- LOG_BUILD ON
- LOG_OUTPUT_ON_FAILURE ON
+ INSTALL_DIR ${_libbacktrace_prefix}
+ BUILD_COMMAND make
+ INSTALL_COMMAND make install
+ BUILD_BYPRODUCTS "${_libbacktrace_prefix}/lib/libbacktrace.a"
+ "${_libbacktrace_prefix}/include/backtrace.h"
)
ExternalProject_Add_Step(project_libbacktrace checkout DEPENDERS configure DEPENDEES download)
set_target_properties(project_libbacktrace PROPERTIES EXCLUDE_FROM_ALL TRUE)
diff --git a/ffi/tests/cpp/test_string.cc b/ffi/tests/cpp/test_string.cc
index 04348292b7..7be184cafb 100644
--- a/ffi/tests/cpp/test_string.cc
+++ b/ffi/tests/cpp/test_string.cc
@@ -263,4 +263,4 @@ TEST(String, Concat) {
EXPECT_EQ(res4.compare("helloworld"), 0);
EXPECT_EQ(res5.compare("worldhello"), 0);
}
-} // namespace
\ No newline at end of file
+} // namespace
diff --git a/ffi/tests/cpp/testing_object.h b/ffi/tests/cpp/testing_object.h
index cd6055ec1f..daba742c74 100644
--- a/ffi/tests/cpp/testing_object.h
+++ b/ffi/tests/cpp/testing_object.h
@@ -88,4 +88,4 @@ class TFloat : public TNumber {
} // namespace testing
} // namespace ffi
} // namespace tvm
-#endif // TVM_FFI_TESTING_OBJECT_H_
\ No newline at end of file
+#endif // TVM_FFI_TESTING_OBJECT_H_
diff --git a/python/tvm/_ffi/libinfo.py b/python/tvm/_ffi/libinfo.py
index 3bbc588f45..2dcf29ca72 100644
--- a/python/tvm/_ffi/libinfo.py
+++ b/python/tvm/_ffi/libinfo.py
@@ -222,11 +222,13 @@ def find_include_path(name=None, search_path=None, optional=False):
dmlc_include_path = []
else:
tvm_include_path = [os.path.join(p, "include") for p in header_path]
+ tvm_ffi_include_path = [os.path.join(p, "ffi/include") for p in header_path]
dlpack_include_path = [os.path.join(p, "dlpack/include") for p in header_path]
dmlc_include_path = [os.path.join(p, "dmlc-core/include") for p in header_path]
# try to find include path
include_found = [p for p in tvm_include_path if os.path.exists(p) and os.path.isdir(p)]
+ include_found += [p for p in tvm_ffi_include_path if os.path.exists(p) and os.path.isdir(p)]
include_found += [p for p in dlpack_include_path if os.path.exists(p) and os.path.isdir(p)]
include_found += [p for p in dmlc_include_path if os.path.exists(p) and os.path.isdir(p)]
diff --git a/src/relax/backend/contrib/cutlass/codegen.cc b/src/relax/backend/contrib/cutlass/codegen.cc
index c1452c20ac..48bc733755 100644
--- a/src/relax/backend/contrib/cutlass/codegen.cc
+++ b/src/relax/backend/contrib/cutlass/codegen.cc
@@ -95,7 +95,7 @@ class CodegenResult : public ObjectRef {
data_ = std::move(n);
}
- TVM_DEFINE_OBJECT_REF_METHODS(CodegenResult, ObjectRef, CodegenResultNode)
+ TVM_DEFINE_OBJECT_REF_METHODS(CodegenResult, ObjectRef, CodegenResultNode);
};
TVM_REGISTER_NODE_TYPE(CodegenResultNode);
diff --git a/src/runtime/debug_compile.cc b/src/runtime/debug_compile.cc
index 96d220307a..ee59a37131 100644
--- a/src/runtime/debug_compile.cc
+++ b/src/runtime/debug_compile.cc
@@ -42,4 +42,4 @@ String Test() {
}
} // namespace debug
-} // namespace tvm
\ No newline at end of file
+} // namespace tvm
diff --git a/tests/lint/check_file_type.py b/tests/lint/check_file_type.py
index 119cc2ea78..a567664788 100644
--- a/tests/lint/check_file_type.py
+++ b/tests/lint/check_file_type.py
@@ -175,6 +175,9 @@ def filename_allowed(name):
if name.startswith("3rdparty"):
return True
+ if name.startswith("ffi/3rdparty"):
+ return True
+
if name in ALLOW_SPECIFIC_FILE:
return True
diff --git a/web/Makefile b/web/Makefile
index 5abd72b598..e9d1375fc7 100644
--- a/web/Makefile
+++ b/web/Makefile
@@ -18,7 +18,8 @@
TVM_ROOT=$(realpath $(shell dirname $(firstword $(MAKEFILE_LIST))))/../
INCLUDE_FLAGS = -I$(TVM_ROOT) -I$(TVM_ROOT)/include\
- -I$(TVM_ROOT)/3rdparty/dlpack/include -I$(TVM_ROOT)/3rdparty/dmlc-core/include\
+ -I$(TVM_ROOT)/ffi/include\
+ -I$(TVM_ROOT)/ffi/3rdparty/dlpack/include -I$(TVM_ROOT)/3rdparty/dmlc-core/include\
-I$(TVM_ROOT)/3rdparty/compiler-rt -I$(TVM_ROOT)/3rdparty/picojson
.PHONY: clean all rmtypedep preparetest
(tvm) 18/38: [FFI] bool, device, tensor, raw str
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 7b7b1e7f1c5a7e34afaa843d3042815a70993d2c
Author: tqchen <tq...@gmail.com>
AuthorDate: Sun Dec 29 07:38:59 2024 +0800
[FFI] bool, device, tensor, raw str
---
ffi/include/tvm/ffi/c_api.h | 2 +-
ffi/include/tvm/ffi/function_details.h | 7 +-
ffi/include/tvm/ffi/reflection.h | 24 ++--
ffi/include/tvm/ffi/string.h | 26 +++++
ffi/include/tvm/ffi/type_traits.h | 196 +++++++++++++++++++++++++++++++--
ffi/src/ffi/object.cc | 2 +-
ffi/tests/cpp/test_any.cc | 129 ++++++++++++++++++++++
ffi/tests/cpp/test_function.cc | 4 +
ffi/tests/cpp/test_reflection.cc | 6 +-
9 files changed, 365 insertions(+), 31 deletions(-)
diff --git a/ffi/include/tvm/ffi/c_api.h b/ffi/include/tvm/ffi/c_api.h
index 18c44e40da..5a626d03c0 100644
--- a/ffi/include/tvm/ffi/c_api.h
+++ b/ffi/include/tvm/ffi/c_api.h
@@ -358,7 +358,7 @@ TVM_FFI_DLL int TVMFFIRegisterTypeField(int32_t type_index, const TVMFFIFieldInf
* \brief Register type method information for rutnime reflection.
* \param type_index The type index
* \param info The method info to be registered.
- * \return 0 when success, nonzero when failure happens
+ * \return 0 when success, nonzero when failure happens
*/
TVM_FFI_DLL int TVMFFIRegisterTypeMethod(int32_t type_index, const TVMFFIMethodInfo* info);
diff --git a/ffi/include/tvm/ffi/function_details.h b/ffi/include/tvm/ffi/function_details.h
index 0f6701e509..90d842d000 100644
--- a/ffi/include/tvm/ffi/function_details.h
+++ b/ffi/include/tvm/ffi/function_details.h
@@ -161,7 +161,7 @@ class MovableArgValueWithContext {
template <typename Type>
TVM_FFI_INLINE operator Type() {
using TypeWithoutCR = std::remove_const_t<std::remove_reference_t<Type>>;
- std::optional<TypeWithoutCR> opt = TryAs<TypeWithoutCR>(args_[arg_index_]);
+ std::optional<TypeWithoutCR> opt = TryAs<TypeWithoutCR>(args_[arg_index_]);
if (opt.has_value()) {
return std::move(*opt);
}
@@ -210,9 +210,8 @@ struct unpack_call_dispatcher<R, 0, index, F> {
template <int index, typename F>
struct unpack_call_dispatcher<void, 0, index, F> {
template <typename... Args>
- TVM_FFI_INLINE static void run(const std::string*, FGetFuncSignature ,
- const F& , int32_t , const AnyView* , Any* ,
- Args&&... unpacked_args) {
+ TVM_FFI_INLINE static void run(const std::string*, FGetFuncSignature, const F&, int32_t,
+ const AnyView*, Any*, Args&&... unpacked_args) {
f(std::forward<Args>(unpacked_args)...);
}
};
diff --git a/ffi/include/tvm/ffi/reflection.h b/ffi/include/tvm/ffi/reflection.h
index 51573d1bc2..3e6a1ecc37 100644
--- a/ffi/include/tvm/ffi/reflection.h
+++ b/ffi/include/tvm/ffi/reflection.h
@@ -52,7 +52,7 @@ struct Type2FieldStaticTypeIndex<T, std::enable_if_t<TypeTraits<T>::enabled>> {
* \returns The byteoffset
*/
template <typename Class, typename T>
-inline int64_t GetFieldByteOffset(T Class::*field_ptr) {
+inline int64_t GetFieldByteOffset(T Class::* field_ptr) {
return reinterpret_cast<int64_t>(&(static_cast<Class*>(nullptr)->*field_ptr));
}
@@ -61,13 +61,13 @@ class ReflectionDef {
explicit ReflectionDef(int32_t type_index) : type_index_(type_index) {}
template <typename Class, typename T>
- ReflectionDef& def_readonly(const char* name, T Class::*field_ptr) {
+ ReflectionDef& def_readonly(const char* name, T Class::* field_ptr) {
RegisterField(name, field_ptr, true);
return *this;
}
template <typename Class, typename T>
- ReflectionDef& def_readwrite(const char* name, T Class::*field_ptr) {
+ ReflectionDef& def_readwrite(const char* name, T Class::* field_ptr) {
RegisterField(name, field_ptr, false);
return *this;
}
@@ -76,7 +76,7 @@ class ReflectionDef {
private:
template <typename Class, typename T>
- void RegisterField(const char* name, T Class::*field_ptr, bool readonly) {
+ void RegisterField(const char* name, T Class::* field_ptr, bool readonly) {
TVMFFIFieldInfo info;
info.name = name;
info.field_static_type_index = Type2FieldStaticTypeIndex<T>::value;
@@ -126,23 +126,19 @@ inline const TVMFFIFieldInfo* GetReflectionFieldInfo(const char* type_key, const
*/
class ReflectionFieldGetter {
public:
- explicit ReflectionFieldGetter(const TVMFFIFieldInfo* field_info) : field_info_(field_info) {
- }
+ explicit ReflectionFieldGetter(const TVMFFIFieldInfo* field_info) : field_info_(field_info) {}
- Any operator()(const Object* obj_ptr) const {
+ Any operator()(const Object* obj_ptr) const {
Any result;
const void* addr = reinterpret_cast<const char*>(obj_ptr) + field_info_->byte_offset;
- TVM_FFI_CHECK_SAFE_CALL(field_info_->getter(const_cast<void*>(addr), reinterpret_cast<TVMFFIAny*>(&result)));
+ TVM_FFI_CHECK_SAFE_CALL(
+ field_info_->getter(const_cast<void*>(addr), reinterpret_cast<TVMFFIAny*>(&result)));
return result;
}
- Any operator()(const ObjectPtr<Object>& obj_ptr) const {
- return operator()(obj_ptr.get());
- }
+ Any operator()(const ObjectPtr<Object>& obj_ptr) const { return operator()(obj_ptr.get()); }
- Any operator()(const ObjectRef& obj) const {
- return operator()(obj.get());
- }
+ Any operator()(const ObjectRef& obj) const { return operator()(obj.get()); }
private:
const TVMFFIFieldInfo* field_info_;
diff --git a/ffi/include/tvm/ffi/string.h b/ffi/include/tvm/ffi/string.h
index 7cbed2f7e8..debd361e7d 100644
--- a/ffi/include/tvm/ffi/string.h
+++ b/ffi/include/tvm/ffi/string.h
@@ -28,6 +28,7 @@
#include <tvm/ffi/error.h>
#include <tvm/ffi/memory.h>
#include <tvm/ffi/object.h>
+#include <tvm/ffi/type_traits.h>
#include <cstddef>
#include <cstring>
@@ -305,6 +306,30 @@ class String : public ObjectRef {
friend struct AnyEqual;
};
+template <>
+inline constexpr bool use_default_type_traits_v<String> = false;
+
+// specialize to enable implicit conversion from const char*
+template <>
+struct TypeTraits<String> : public ObjectRefTypeTraitsBase<String> {
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFIRawStr) return true;
+ return ObjectRefTypeTraitsBase<String>::CheckAnyView(src);
+ }
+
+ static TVM_FFI_INLINE String CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFIRawStr) {
+ return String(src->v_c_str);
+ }
+ return ObjectRefTypeTraitsBase<String>::CopyFromAnyViewAfterCheck(src);
+ }
+
+ static TVM_FFI_INLINE std::optional<String> TryCopyFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFIRawStr) return String(src->v_c_str);
+ return ObjectRefTypeTraitsBase<String>::TryCopyFromAnyView(src);
+ }
+};
+
inline String operator+(const String& lhs, const String& rhs) {
size_t lhs_size = lhs.size();
size_t rhs_size = rhs.size();
@@ -421,6 +446,7 @@ inline int String::memncmp(const char* lhs, const char* rhs, size_t lhs_count, s
return 0;
}
}
+
} // namespace ffi
} // namespace tvm
diff --git a/ffi/include/tvm/ffi/type_traits.h b/ffi/include/tvm/ffi/type_traits.h
index cf88eb7319..69b3738be0 100644
--- a/ffi/include/tvm/ffi/type_traits.h
+++ b/ffi/include/tvm/ffi/type_traits.h
@@ -44,6 +44,8 @@ inline std::string TypeIndex2TypeKey(int32_t type_index) {
switch (type_index) {
case TypeIndex::kTVMFFINone:
return "None";
+ case TypeIndex::kTVMFFIBool:
+ return "bool";
case TypeIndex::kTVMFFIInt:
return "int";
case TypeIndex::kTVMFFIFloat:
@@ -154,14 +156,14 @@ struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> : public TypeT
static TVM_FFI_INLINE void MoveToAny(Int src, TVMFFIAny* result) { CopyToAnyView(src, result); }
static TVM_FFI_INLINE std::optional<Int> TryCopyFromAnyView(const TVMFFIAny* src) {
- if (src->type_index == TypeIndex::kTVMFFIInt) {
+ if (src->type_index == TypeIndex::kTVMFFIInt || src->type_index == TypeIndex::kTVMFFIBool) {
return std::make_optional<Int>(src->v_int64);
}
return std::nullopt;
}
static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
- return src->type_index == TypeIndex::kTVMFFIInt;
+ return src->type_index == TypeIndex::kTVMFFIInt || src->type_index == TypeIndex::kTVMFFIBool;
}
static TVM_FFI_INLINE int CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
@@ -171,6 +173,36 @@ struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> : public TypeT
static TVM_FFI_INLINE std::string TypeStr() { return "int"; }
};
+// Bool type, allow implicit casting from int
+template <>
+struct TypeTraits<bool> : public TypeTraitsBase {
+ static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIBool;
+
+ static TVM_FFI_INLINE void CopyToAnyView(const bool& src, TVMFFIAny* result) {
+ result->type_index = TypeIndex::kTVMFFIBool;
+ result->v_int64 = static_cast<int64_t>(src);
+ }
+
+ static TVM_FFI_INLINE void MoveToAny(bool src, TVMFFIAny* result) { CopyToAnyView(src, result); }
+
+ static TVM_FFI_INLINE std::optional<bool> TryCopyFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFIInt || src->type_index == TypeIndex::kTVMFFIBool) {
+ return std::make_optional<bool>(static_cast<bool>(src->v_int64));
+ }
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ return src->type_index == TypeIndex::kTVMFFIInt || src->type_index == TypeIndex::kTVMFFIBool;
+ }
+
+ static TVM_FFI_INLINE bool CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ return static_cast<bool>(src->v_int64);
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return "bool"; }
+};
+
// Float POD values
template <typename Float>
struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>>
@@ -187,14 +219,16 @@ struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>>
static TVM_FFI_INLINE std::optional<Float> TryCopyFromAnyView(const TVMFFIAny* src) {
if (src->type_index == TypeIndex::kTVMFFIFloat) {
return std::make_optional<Float>(src->v_float64);
- } else if (src->type_index == TypeIndex::kTVMFFIInt) {
+ } else if (src->type_index == TypeIndex::kTVMFFIInt ||
+ src->type_index == TypeIndex::kTVMFFIBool) {
return std::make_optional<Float>(src->v_int64);
}
return std::nullopt;
}
static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
- return src->type_index == TypeIndex::kTVMFFIFloat || src->type_index == TypeIndex::kTVMFFIInt;
+ return src->type_index == TypeIndex::kTVMFFIFloat || src->type_index == TypeIndex::kTVMFFIInt ||
+ src->type_index == TypeIndex::kTVMFFIBool;
}
static TVM_FFI_INLINE Float CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
@@ -244,11 +278,154 @@ struct TypeTraits<void*> : public TypeTraitsBase {
static TVM_FFI_INLINE std::string TypeStr() { return "void*"; }
};
+// DataType
+template <>
+struct TypeTraits<DLDataType> : public TypeTraitsBase {
+ static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIDataType;
+
+ static TVM_FFI_INLINE void CopyToAnyView(const DLDataType& src, TVMFFIAny* result) {
+ result->type_index = TypeIndex::kTVMFFIDataType;
+ result->v_dtype = src;
+ }
+
+ static TVM_FFI_INLINE void MoveToAny(DLDataType src, TVMFFIAny* result) {
+ result->type_index = TypeIndex::kTVMFFIDataType;
+ result->v_dtype = src;
+ }
+
+ static TVM_FFI_INLINE std::optional<DLDataType> TryCopyFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFIDataType) {
+ return src->v_dtype;
+ }
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ return src->type_index == TypeIndex::kTVMFFIDataType;
+ }
+
+ static TVM_FFI_INLINE DLDataType CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ return src->v_dtype;
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return "DataType"; }
+};
+
+// Device
+template <>
+struct TypeTraits<DLDevice> : public TypeTraitsBase {
+ static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIDevice;
+
+ static TVM_FFI_INLINE void CopyToAnyView(const DLDevice& src, TVMFFIAny* result) {
+ result->type_index = TypeIndex::kTVMFFIDevice;
+ result->v_device = src;
+ }
+
+ static TVM_FFI_INLINE void MoveToAny(DLDevice src, TVMFFIAny* result) {
+ result->type_index = TypeIndex::kTVMFFIDevice;
+ result->v_device = src;
+ }
+
+ static TVM_FFI_INLINE std::optional<DLDevice> TryCopyFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFIDevice) {
+ return src->v_device;
+ }
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ return src->type_index == TypeIndex::kTVMFFIDevice;
+ }
+
+ static TVM_FFI_INLINE DLDevice CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ return src->v_device;
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return "Device"; }
+};
+
+// DLTensor*, requirement: not nullable, do not retain ownership
+template <>
+struct TypeTraits<DLTensor*> : public TypeTraitsBase {
+ static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIDLTensorPtr;
+
+ static TVM_FFI_INLINE void CopyToAnyView(DLTensor* src, TVMFFIAny* result) {
+ TVM_FFI_ICHECK_NOTNULL(src);
+ result->type_index = TypeIndex::kTVMFFIDLTensorPtr;
+ result->v_ptr = src;
+ }
+
+ static TVM_FFI_INLINE void MoveToAny(DLTensor* src, TVMFFIAny* result) {
+ TVM_FFI_THROW(RuntimeError)
+ << "DLTensor* cannot be held in Any as it does not retain ownership, use NDArray instead";
+ }
+
+ static TVM_FFI_INLINE std::optional<DLTensor*> TryCopyFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFIDLTensorPtr) {
+ return static_cast<DLTensor*>(src->v_ptr);
+ }
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ return src->type_index == TypeIndex::kTVMFFIDLTensorPtr;
+ }
+
+ static TVM_FFI_INLINE DLTensor* CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ return static_cast<DLTensor*>(src->v_ptr);
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return "DLTensor*"; }
+};
+
+// const char*, requirement: not nullable, do not retain ownership
+template <>
+struct TypeTraits<const char*> : public TypeTraitsBase {
+ static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIRawStr;
+
+ static TVM_FFI_INLINE void CopyToAnyView(const char* src, TVMFFIAny* result) {
+ TVM_FFI_ICHECK_NOTNULL(src);
+ result->type_index = TypeIndex::kTVMFFIRawStr;
+ result->v_c_str = src;
+ }
+
+ static TVM_FFI_INLINE void MoveToAny(const char* src, TVMFFIAny* result) {
+ TVM_FFI_THROW(RuntimeError)
+ << "const char* cannot be held in Any as it does not retain ownership, use NDArray instead";
+ }
+
+ static TVM_FFI_INLINE std::optional<const char*> TryCopyFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFIDLTensorPtr) {
+ return static_cast<const char*>(src->v_c_str);
+ }
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ return src->type_index == TypeIndex::kTVMFFIRawStr;
+ }
+
+ static TVM_FFI_INLINE const char* CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ return static_cast<const char*>(src->v_ptr);
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return "const char*"; }
+};
+
+template <int N>
+struct TypeTraits<char[N]> : public TypeTraitsBase {
+ // NOTE: only enable implicit conversion into AnyView
+ static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIRawStr;
+
+ static TVM_FFI_INLINE void CopyToAnyView(const char src[N], TVMFFIAny* result) {
+ result->type_index = TypeIndex::kTVMFFIRawStr;
+ result->v_c_str = src;
+ }
+};
+
// Traits for ObjectRef
template <typename TObjRef>
-struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef> &&
- use_default_type_traits_v<TObjRef>>>
- : public TypeTraitsBase {
+struct ObjectRefTypeTraitsBase : public TypeTraitsBase {
static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIObject;
using ContainerType = typename TObjRef::ContainerType;
@@ -292,6 +469,11 @@ struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef
static TVM_FFI_INLINE std::string TypeStr() { return ContainerType::_type_key; }
};
+template <typename TObjRef>
+struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef> &&
+ use_default_type_traits_v<TObjRef>>>
+ : public ObjectRefTypeTraitsBase<TObjRef> {};
+
// Traits for ObjectPtr
template <typename T>
struct TypeTraits<ObjectPtr<T>> : public TypeTraitsBase {
diff --git a/ffi/src/ffi/object.cc b/ffi/src/ffi/object.cc
index a0fe306e78..1cf37ac448 100644
--- a/ffi/src/ffi/object.cc
+++ b/ffi/src/ffi/object.cc
@@ -172,7 +172,7 @@ class TypeTable {
return it->second;
}
- Entry* GetTypeEntry(int32_t type_index) {
+ Entry* GetTypeEntry(int32_t type_index) {
Entry* entry = nullptr;
if (type_index >= 0 && static_cast<size_t>(type_index) < type_table_.size()) {
entry = type_table_[type_index].get();
diff --git a/ffi/tests/cpp/test_any.cc b/ffi/tests/cpp/test_any.cc
index 2cb3e8b95b..1fd312ecd2 100644
--- a/ffi/tests/cpp/test_any.cc
+++ b/ffi/tests/cpp/test_any.cc
@@ -60,6 +60,37 @@ TEST(Any, Int) {
EXPECT_EQ(view0.CopyToTVMFFIAny().v_int64, 2);
}
+TEST(Any, bool) {
+ AnyView view0;
+ std::optional<bool> opt_v0 = view0.TryAs<bool>();
+ EXPECT_TRUE(!opt_v0.has_value());
+
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] bool v0 = view0;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("Cannot convert from type `None` to `bool`"), std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+
+ AnyView view1 = true;
+ EXPECT_EQ(view1.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIBool);
+ EXPECT_EQ(view1.CopyToTVMFFIAny().v_int64, 1);
+
+ int32_t int_v1 = view1;
+ EXPECT_EQ(int_v1, 1);
+
+ bool v1 = false;
+ view0 = v1;
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIBool);
+ EXPECT_EQ(view0.CopyToTVMFFIAny().v_int64, 0);
+}
+
TEST(Any, Float) {
AnyView view0;
EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
@@ -94,6 +125,104 @@ TEST(Any, Float) {
EXPECT_EQ(view0.CopyToTVMFFIAny().v_float64, 2);
}
+TEST(Any, DataType) {
+ AnyView view0;
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
+
+ std::optional<DLDataType> opt_v0 = view0.TryAs<DLDataType>();
+ EXPECT_TRUE(!opt_v0.has_value());
+
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] DLDataType v0 = view0;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("Cannot convert from type `None` to `DataType`"), std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+
+ DLDataType dtype{kDLFloat, 32, 1};
+
+ AnyView view1_dtype = dtype;
+ DLDataType dtype_v1 = view1_dtype;
+ EXPECT_EQ(dtype_v1.code, kDLFloat);
+ EXPECT_EQ(dtype_v1.bits, 32);
+ EXPECT_EQ(dtype_v1.lanes, 1);
+
+ Any view2 = DLDataType{kDLInt, 16, 2};
+ TVMFFIAny ffi_v2;
+ view2.MoveToTVMFFIAny(&ffi_v2);
+ EXPECT_EQ(ffi_v2.type_index, TypeIndex::kTVMFFIDataType);
+ EXPECT_EQ(ffi_v2.v_dtype.code, kDLInt);
+ EXPECT_EQ(ffi_v2.v_dtype.bits, 16);
+ EXPECT_EQ(ffi_v2.v_dtype.lanes, 2);
+}
+
+TEST(Any, Device) {
+ AnyView view0;
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
+
+ std::optional<DLDevice> opt_v0 = view0.TryAs<DLDevice>();
+ EXPECT_TRUE(!opt_v0.has_value());
+
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] DLDevice v0 = view0;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("Cannot convert from type `None` to `Device`"), std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+
+ DLDevice device{kDLCUDA, 1};
+
+ AnyView view1_device = device;
+ DLDevice dtype_v1 = view1_device;
+ EXPECT_EQ(dtype_v1.device_type, kDLCUDA);
+ EXPECT_EQ(dtype_v1.device_id, 1);
+
+ Any view2 = DLDevice{kDLCPU, 0};
+ TVMFFIAny ffi_v2;
+ view2.MoveToTVMFFIAny(&ffi_v2);
+ EXPECT_EQ(ffi_v2.type_index, TypeIndex::kTVMFFIDevice);
+ EXPECT_EQ(ffi_v2.v_device.device_type, kDLCPU);
+ EXPECT_EQ(ffi_v2.v_device.device_id, 0);
+}
+
+TEST(Any, DLTensor) {
+ AnyView view0;
+
+ std::optional<DLTensor*> opt_v0 = view0.TryAs<DLTensor*>();
+ EXPECT_TRUE(!opt_v0.has_value());
+
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] DLTensor* v0 = view0;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("Cannot convert from type `None` to `DLTensor*`"), std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+
+ DLTensor dltensor;
+
+ AnyView view1_dl = &dltensor;
+ DLTensor* dl_v1 = view1_dl;
+ EXPECT_EQ(dl_v1, &dltensor);
+}
+
TEST(Any, Object) {
AnyView view0;
EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
diff --git a/ffi/tests/cpp/test_function.cc b/ffi/tests/cpp/test_function.cc
index 367e613665..ff098d70c9 100644
--- a/ffi/tests/cpp/test_function.cc
+++ b/ffi/tests/cpp/test_function.cc
@@ -112,6 +112,10 @@ TEST(Func, FromUnpacked) {
}
},
::tvm::ffi::Error);
+
+ Function fconcact =
+ Function::FromUnpacked([](const String& a, const String& b) -> String { return a + b; });
+ EXPECT_EQ(fconcact("abc", "def").operator String(), "abcdef");
}
TEST(Func, Global) {
diff --git a/ffi/tests/cpp/test_reflection.cc b/ffi/tests/cpp/test_reflection.cc
index c7901a4ca3..9de0015009 100644
--- a/ffi/tests/cpp/test_reflection.cc
+++ b/ffi/tests/cpp/test_reflection.cc
@@ -20,6 +20,7 @@
#include <gtest/gtest.h>
#include <tvm/ffi/object.h>
#include <tvm/ffi/reflection.h>
+
#include "./testing_object.h"
namespace {
@@ -38,12 +39,9 @@ TEST(Reflection, GetFieldByteOffset) {
EXPECT_EQ(details::GetFieldByteOffset(&A::y), 12);
}
-
TEST(Reflection, FieldGetter) {
ObjectRef a = TInt(10);
- details::ReflectionFieldGetter getter(
- details::GetReflectionFieldInfo("test.Int", "value")
- );
+ details::ReflectionFieldGetter getter(details::GetReflectionFieldInfo("test.Int", "value"));
EXPECT_EQ(getter(a).operator int(), 10);
}
} // namespace
(tvm) 13/38: [FFI] Optional Support
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 441ceae16e686c78141311c949a2ba8fdfd8f8b2
Author: tqchen <ti...@gmail.com>
AuthorDate: Fri Sep 13 15:20:58 2024 -0400
[FFI] Optional Support
---
ffi/include/tvm/ffi/any.h | 17 +-
ffi/include/tvm/ffi/container/optional.h | 300 +++++++++++++++++++++++++++++++
ffi/include/tvm/ffi/object.h | 2 +-
ffi/tests/example/test_any.cc | 22 +--
ffi/tests/example/test_optional.cc | 107 +++++++++++
5 files changed, 431 insertions(+), 17 deletions(-)
diff --git a/ffi/include/tvm/ffi/any.h b/ffi/include/tvm/ffi/any.h
index 031038d4fc..b7181b2297 100644
--- a/ffi/include/tvm/ffi/any.h
+++ b/ffi/include/tvm/ffi/any.h
@@ -112,12 +112,12 @@ class AnyView {
* \return The underlying supporting data of any view
* \note This function is used only for testing purposes.
*/
- TVMFFIAny AsTVMFFIAny() const { return data_; }
+ TVMFFIAny CopyToTVMFFIAny() const { return data_; }
/*!
* \return Create an AnyView from TVMFFIAny
* \param data the underlying ffi data.
*/
- static AnyView FromTVMFFIAny(TVMFFIAny data) {
+ static AnyView CopyFromTVMFFIAny(TVMFFIAny data) {
AnyView view;
view.data_ = data;
return view;
@@ -142,7 +142,10 @@ TVM_FFI_INLINE void InplaceConvertAnyViewToAny(TVMFFIAny* data,
} // namespace details
/*!
- * \brief
+ * \brief Managed Any that takes strong reference to a value.
+ *
+ * \note Develooper invariance: the TVMFFIAny data_
+ * in the Any can be safely used in AnyView.
*/
class Any {
protected:
@@ -198,7 +201,7 @@ class Any {
return *this;
}
/*! \brief Any can be converted to AnyView in zero cost. */
- operator AnyView() { return AnyView::FromTVMFFIAny(data_); }
+ operator AnyView() { return AnyView::CopyFromTVMFFIAny(data_); }
// constructor from general types
template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
Any(T other) { // NOLINT(*)
@@ -227,10 +230,14 @@ class Any {
TVM_FFI_UNREACHABLE();
}
+ bool operator==(std::nullptr_t) const { return data_.type_index == TypeIndex::kTVMFFINone; }
+
+ bool operator!=(std::nullptr_t) const { return data_.type_index != TypeIndex::kTVMFFINone; }
+
// FFI related operations
/*!
* Move the current data to FFI any
- * \parma result the output to nmove to
+ * \param result the output to nmove to
*/
void MoveToTVMFFIAny(TVMFFIAny* result) {
*result = data_;
diff --git a/ffi/include/tvm/ffi/container/optional.h b/ffi/include/tvm/ffi/container/optional.h
new file mode 100644
index 0000000000..5f08db0a5b
--- /dev/null
+++ b/ffi/include/tvm/ffi/container/optional.h
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ */
+
+/*!
+ * \file tvm/ffi/container/optional.h
+ * \brief Runtime Optional container types.
+ */
+#ifndef TVM_FFI_CONTAINER_OPTIONAL_H_
+#define TVM_FFI_CONTAINER_OPTIONAL_H_
+
+#include <tvm/ffi/any.h>
+#include <tvm/ffi/object.h>
+
+#include <optional>
+
+namespace tvm {
+namespace ffi {
+
+/*!
+ * \brief Optional that is backed by Any
+ *
+ * nullptr will be treated as NullOpt
+ *
+ * \tparam T any value will be treated as
+ */
+template <typename T>
+class Optional<T, std::enable_if_t<!std::is_base_of_v<ObjectRef, T>>> {
+ public:
+ static_assert(!std::is_same_v<std::nullptr_t, T>, "Optional<nullptr> is not well defined");
+ // default constructors.
+ Optional() = default;
+ Optional(const Optional<T>& other) : data_(other.data_) {}
+ Optional(Optional<T>&& other) : data_(std::move(other.data_)) {}
+ Optional<T>& operator=(const Optional<T>& other) {
+ data_ = other.data_;
+ return *this;
+ }
+ Optional<T>& operator=(Optional<T>&& other) {
+ data_ = std::move(other.data_);
+ return *this;
+ }
+ // normal value handling.
+ Optional(T other) // NOLINT(*)
+ : data_(std::move(other)) {}
+ Optional<T>& operator=(T other) {
+ data_ = std::move(other);
+ return *this;
+ }
+ // nullptr handling.
+ // disallow implicit conversion as 0 can be implicitly converted to nullptr_t
+ explicit Optional(std::nullptr_t) {}
+ Optional<T>& operator=(std::nullptr_t) {
+ data_ = std::nullopt;
+ return *this;
+ }
+ /*!
+ * \return A not-null container value in the optional.
+ * \note This function performs not-null checking.
+ */
+ T value() const {
+ if (!data_.has_value()) {
+ TVM_FFI_THROW(RuntimeError) << "Back optional access";
+ }
+ return *data_;
+ }
+ /*!
+ * \return A not-null container value in the optional.
+ * \note This function performs not-null checking.
+ */
+ T value_or(T default_value) const { return data_.value_or(default_value); }
+
+ /*! \return Whether the container is not nullptr.*/
+ explicit operator bool() const { return data_.has_value(); }
+
+ bool has_value() const { return data_.has_value(); }
+
+ bool operator==(const Optional<T>& other) const { return data_ == other.data_; }
+
+ bool operator!=(const Optional<T>& other) const { return data_ != other.data_; }
+
+ template <typename U>
+ bool operator==(const U& other) const {
+ return data_ == other;
+ }
+
+ template <typename U>
+ bool operator!=(const U& other) const {
+ return data_ != other;
+ }
+
+ // operator overloadings with nullptr
+ bool operator==(std::nullptr_t) const { return !data_.has_value(); }
+ bool operator!=(std::nullptr_t) const { return data_.has_value(); }
+
+ // helper function to move out value
+ T&& MoveValueNoCheck() { return std::move(*data_); }
+ // helper function to copy out value
+ T CopyValueNoCheck() const { return *data_; }
+
+ private:
+ std::optional<T> data_;
+};
+
+/*!
+ * \brief Specialization of Optional for ObjectRef.
+ *
+ * In such cases, nullptr is treated as NullOpt.
+ * This specialization reduces the storage cost of
+ * Optional for ObjectRef.
+ *
+ * \tparam T The original ObjectRef.
+ */
+template <typename T>
+class Optional<T, std::enable_if_t<std::is_base_of_v<ObjectRef, T>>> : public ObjectRef {
+ public:
+ using ContainerType = typename T::ContainerType;
+ static_assert(std::is_base_of<ObjectRef, T>::value, "Optional is only defined for ObjectRef.");
+ // default constructors.
+ Optional() = default;
+ Optional(const Optional<T>& other) : ObjectRef(other.data_) {}
+ Optional(Optional<T>&& other) : ObjectRef(std::move(other.data_)) {}
+ Optional<T>& operator=(const Optional<T>& other) {
+ data_ = other.data_;
+ return *this;
+ }
+ Optional<T>& operator=(Optional<T>&& other) {
+ data_ = std::move(other.data_);
+ return *this;
+ }
+ /*!
+ * \brief Construct from an ObjectPtr
+ * whose type already matches the ContainerType.
+ * \param ptr
+ */
+ explicit Optional(ObjectPtr<Object> ptr) : ObjectRef(ptr) {}
+ /*! \brief Nullopt handling */
+ Optional(std::nullopt_t) {} // NOLINT(*)
+ // nullptr handling.
+ // disallow implicit conversion as 0 can be implicitly converted to nullptr_t
+ explicit Optional(std::nullptr_t) {}
+ Optional<T>& operator=(std::nullptr_t) {
+ data_ = nullptr;
+ return *this;
+ }
+ // normal value handling.
+ Optional(T other) // NOLINT(*)
+ : ObjectRef(std::move(other)) {}
+ Optional<T>& operator=(T other) {
+ ObjectRef::operator=(std::move(other));
+ return *this;
+ }
+ // delete the int constructor
+ // since Optional<Integer>(0) is ambiguious
+ // 0 can be implicitly casted to nullptr_t
+ explicit Optional(int val) = delete;
+ Optional<T>& operator=(int val) = delete;
+ // helper function to move out value
+ T&& MoveOutValueNoCheck() { return T(std::move(data_)); }
+ /*!
+ * \return A not-null container value in the optional.
+ * \note This function performs not-null checking.
+ */
+ T value() const {
+ if (data_ == nullptr) {
+ TVM_FFI_THROW(RuntimeError) << "Bad optional access";
+ }
+ return T(data_);
+ }
+ /*!
+ * \return The internal object pointer with container type of T.
+ * \note This function do not perform not-null checking.
+ */
+ const ContainerType* get() const { return static_cast<ContainerType*>(data_.get()); }
+ /*!
+ * \return The contained value if the Optional is not null
+ * otherwise return the default_value.
+ */
+ T value_or(T default_value) const { return data_ != nullptr ? T(data_) : default_value; }
+
+ /*! \return Whether the container is not nullptr.*/
+ explicit operator bool() const { return *this != nullptr; }
+ /*! \return Whether the container is not nullptr */
+ bool has_value() const { return *this != nullptr; }
+
+ // operator overloadings
+ bool operator==(std::nullptr_t) const { return data_ == nullptr; }
+ bool operator!=(std::nullptr_t) const { return data_ != nullptr; }
+ auto operator==(const Optional<T>& other) const {
+ // support case where sub-class returns a symbolic ref type.
+ using RetType = decltype(value() == other.value());
+ if (same_as(other)) return RetType(true);
+ if (*this != nullptr && other != nullptr) {
+ return value() == other.value();
+ } else {
+ // one of them is nullptr.
+ return RetType(false);
+ }
+ }
+ auto operator!=(const Optional<T>& other) const {
+ // support case where sub-class returns a symbolic ref type.
+ using RetType = decltype(value() != other.value());
+ if (same_as(other)) return RetType(false);
+ if (*this != nullptr && other != nullptr) {
+ return value() != other.value();
+ } else {
+ // one of them is nullptr.
+ return RetType(true);
+ }
+ }
+ auto operator==(const T& other) const {
+ using RetType = decltype(value() == other);
+ if (same_as(other)) return RetType(true);
+ if (*this != nullptr) return value() == other;
+ return RetType(false);
+ }
+ auto operator!=(const T& other) const { return !(*this == other); }
+ template <typename U>
+ auto operator==(const U& other) const {
+ using RetType = decltype(value() == other);
+ if (*this == nullptr) return RetType(false);
+ return value() == other;
+ }
+ template <typename U>
+ auto operator!=(const U& other) const {
+ using RetType = decltype(value() != other);
+ if (*this == nullptr) return RetType(true);
+ return value() != other;
+ }
+ static constexpr bool _type_is_nullable = true;
+
+ // helper function to move out value
+ T&& MoveValueNoCheck() { return T(std::move(data_)); }
+ // helper function to copy out value
+ T CopyValueNoCheck() const { return T(data_); }
+};
+
+template <typename T>
+inline constexpr bool use_default_type_traits_v<Optional<T>> = false;
+
+template <typename T>
+struct TypeTraits<Optional<T>> : public TypeTraitsBase {
+ static TVM_FFI_INLINE void CopyToAnyView(const Optional<T>& src, TVMFFIAny* result) {
+ if (src.has_value()) {
+ TypeTraits<T>::CopyToAnyView(src.CopyValueNoCheck(), result);
+ } else {
+ TypeTraits<std::nullptr_t>::CopyToAnyView(nullptr, result);
+ }
+ }
+
+ static TVM_FFI_INLINE void MoveToAny(Optional<T> src, TVMFFIAny* result) {
+ if (src.has_value()) {
+ TypeTraits<T>::MoveToAny(src.MoveValueNoCheck(), result);
+ } else {
+ TypeTraits<std::nullptr_t>::CopyToAnyView(nullptr, result);
+ }
+ }
+
+ static TVM_FFI_INLINE std::string GetMismatchTypeInfo(const TVMFFIAny* src) {
+ return TypeTraits<T>::GetMismatchTypeInfo(src);
+ }
+
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFINone) return true;
+ return TypeTraits<T>::CheckAnyView(src);
+ }
+
+ static TVM_FFI_INLINE Optional<T> CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFINone) return Optional<T>(nullptr);
+ return TypeTraits<T>::CopyFromAnyViewAfterCheck(src);
+ }
+
+ static TVM_FFI_INLINE std::optional<Optional<T>> TryCopyFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFINone) return Optional<T>(nullptr);
+ return TypeTraits<T>::TryCopyFromAnyView(src);
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() {
+ return "Optional<" + TypeTraits<T>::TypeStr() + ">";
+ }
+};
+
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_CONTAINER_OPTIONAL_H_
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 120a735c82..07170dce8f 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -352,7 +352,7 @@ class ObjectPtr {
};
// Forward declaration, to prevent circular includes.
-template <typename T>
+template <typename T, typename = void>
class Optional;
/*! \brief Base class of all object reference */
diff --git a/ffi/tests/example/test_any.cc b/ffi/tests/example/test_any.cc
index 02d0ad6a23..36d4783dc5 100644
--- a/ffi/tests/example/test_any.cc
+++ b/ffi/tests/example/test_any.cc
@@ -29,7 +29,7 @@ using namespace tvm::ffi::testing;
TEST(Any, Int) {
AnyView view0;
- EXPECT_EQ(view0.AsTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
std::optional<int64_t> opt_v0 = view0.TryAs<int64_t>();
EXPECT_TRUE(!opt_v0.has_value());
@@ -48,21 +48,21 @@ TEST(Any, Int) {
::tvm::ffi::Error);
AnyView view1 = 1;
- EXPECT_EQ(view1.AsTVMFFIAny().type_index, TypeIndex::kTVMFFIInt);
- EXPECT_EQ(view1.AsTVMFFIAny().v_int64, 1);
+ EXPECT_EQ(view1.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIInt);
+ EXPECT_EQ(view1.CopyToTVMFFIAny().v_int64, 1);
int32_t int_v1 = view1;
EXPECT_EQ(int_v1, 1);
int64_t v1 = 2;
view0 = v1;
- EXPECT_EQ(view0.AsTVMFFIAny().type_index, TypeIndex::kTVMFFIInt);
- EXPECT_EQ(view0.AsTVMFFIAny().v_int64, 2);
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIInt);
+ EXPECT_EQ(view0.CopyToTVMFFIAny().v_int64, 2);
}
TEST(Any, Float) {
AnyView view0;
- EXPECT_EQ(view0.AsTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
std::optional<double> opt_v0 = view0.TryAs<double>();
EXPECT_TRUE(!opt_v0.has_value());
@@ -85,18 +85,18 @@ TEST(Any, Float) {
EXPECT_EQ(float_v1, 1);
AnyView view2 = 2.2;
- EXPECT_EQ(view2.AsTVMFFIAny().type_index, TypeIndex::kTVMFFIFloat);
- EXPECT_EQ(view2.AsTVMFFIAny().v_float64, 2.2);
+ EXPECT_EQ(view2.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIFloat);
+ EXPECT_EQ(view2.CopyToTVMFFIAny().v_float64, 2.2);
float v1 = 2;
view0 = v1;
- EXPECT_EQ(view0.AsTVMFFIAny().type_index, TypeIndex::kTVMFFIFloat);
- EXPECT_EQ(view0.AsTVMFFIAny().v_float64, 2);
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIFloat);
+ EXPECT_EQ(view0.CopyToTVMFFIAny().v_float64, 2);
}
TEST(Any, Object) {
AnyView view0;
- EXPECT_EQ(view0.AsTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
// int object is not nullable
std::optional<TInt> opt_v0 = view0.TryAs<TInt>();
diff --git a/ffi/tests/example/test_optional.cc b/ffi/tests/example/test_optional.cc
new file mode 100644
index 0000000000..10d9189c23
--- /dev/null
+++ b/ffi/tests/example/test_optional.cc
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+#include <gtest/gtest.h>
+#include <tvm/ffi/any.h>
+#include <tvm/ffi/container/array.h>
+#include <tvm/ffi/container/optional.h>
+#include <tvm/ffi/memory.h>
+
+#include "./testing_object.h"
+
+namespace {
+
+using namespace tvm::ffi;
+using namespace tvm::ffi::testing;
+
+TEST(Optional, TInt) {
+ Optional<TInt> x;
+ Optional<TInt> y = TInt(11);
+ static_assert(sizeof(Optional<TInt>) == sizeof(ObjectRef));
+
+ EXPECT_TRUE(!x.has_value());
+ EXPECT_TRUE(x == nullptr);
+ EXPECT_EQ(x.value_or(TInt(12))->value, 12);
+
+ EXPECT_TRUE(y.has_value());
+ EXPECT_TRUE(y != nullptr);
+ EXPECT_EQ(y.value_or(TInt(12))->value, 11);
+}
+
+TEST(Optional, double) {
+ Optional<double> x;
+ Optional<double> y = 11.0;
+ static_assert(sizeof(Optional<double>) > sizeof(ObjectRef));
+
+ EXPECT_TRUE(!x.has_value());
+ EXPECT_TRUE(x == nullptr);
+ EXPECT_EQ(x.value_or(12), 12);
+ EXPECT_TRUE(x != 12);
+
+ EXPECT_TRUE(y.has_value());
+ EXPECT_TRUE(y != nullptr);
+ EXPECT_EQ(y.value_or(12), 11);
+ EXPECT_TRUE(y == 11);
+ EXPECT_TRUE(y != 12);
+}
+
+TEST(Optional, AnyConvert_int) {
+ Optional<int> opt_v0 = 1;
+ EXPECT_EQ(opt_v0.value(), 1);
+ EXPECT_TRUE(opt_v0 != nullptr);
+
+ AnyView view0 = opt_v0;
+ EXPECT_EQ(view0.operator int(), 1);
+
+ Any any1;
+ Optional<int> opt_v1 = any1;
+
+ EXPECT_TRUE(opt_v1 == nullptr);
+}
+
+TEST(Optional, AnyConvert_Array) {
+ AnyView view0;
+ Array<Array<TNumber>> arr_nested = {{}, {TInt(1), TFloat(2)}};
+ view0 = arr_nested;
+
+ Optional<Array<Array<TNumber>>> opt_arr = view0;
+ EXPECT_EQ(arr_nested.use_count(), 2);
+
+ Optional<Array<Array<TNumber>>> arr1 = view0;
+ EXPECT_EQ(arr_nested.use_count(), 3);
+ EXPECT_EQ(arr1.value()[1][1].as<TFloatObj>()->value, 2);
+
+ Any any1;
+ Optional<Array<Array<TNumber>>> arr2 = any1;
+ EXPECT_TRUE(arr2 == nullptr);
+
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] Optional<Array<Array<int>>> arr2 = view0;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("to `Optional<Array<Array<int>>>`"), std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+}
+
+} // namespace
(tvm) 25/38: Move over container on new object
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit b8a65dbab039d0d3060802aeb54e2add27b7286a
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 11:25:30 2025 -0400
Move over container on new object
---
include/tvm/runtime/container/array.h | 13 +++---
include/tvm/runtime/container/map.h | 4 +-
include/tvm/runtime/object.h | 78 +++++++++++++++++++++++++----------
src/runtime/debug_compile.cc | 4 ++
4 files changed, 70 insertions(+), 29 deletions(-)
diff --git a/include/tvm/runtime/container/array.h b/include/tvm/runtime/container/array.h
index ba8fdfac55..7513a1299a 100644
--- a/include/tvm/runtime/container/array.h
+++ b/include/tvm/runtime/container/array.h
@@ -121,7 +121,7 @@ class ArrayNode : public Object, public InplaceArrayBase<ArrayNode, ObjectRef> {
static constexpr const uint32_t _type_index = TypeIndex::kRuntimeArray;
static constexpr const char* _type_key = "Array";
- TVM_DECLARE_FINAL_OBJECT_INFO(ArrayNode, Object);
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(ArrayNode, Object);
private:
/*! \return Size of initialized memory, used by InplaceArrayBase. */
@@ -903,14 +903,15 @@ inline Array<T> Concat(Array<T> lhs, const Array<T>& rhs) {
return std::move(lhs);
}
+} // namespace runtime
+
+namespace ffi{
// Specialize make_object<ArrayNode> to make sure it is correct.
template <>
-inline ObjectPtr<ArrayNode> make_object() {
- return ArrayNode::Empty();
+inline ObjectPtr<tvm::runtime::ArrayNode> make_object() {
+ return tvm::runtime::ArrayNode::Empty();
+}
}
-
-} // namespace runtime
-
// expose the functions to the root namespace.
using runtime::Array;
using runtime::ArrayNode;
diff --git a/include/tvm/runtime/container/map.h b/include/tvm/runtime/container/map.h
index 1e4015aa23..a24d61d4de 100644
--- a/include/tvm/runtime/container/map.h
+++ b/include/tvm/runtime/container/map.h
@@ -68,7 +68,7 @@ class MapNode : public Object {
static constexpr const uint32_t _type_index = runtime::TypeIndex::kRuntimeMap;
static constexpr const char* _type_key = "Map";
- TVM_DECLARE_FINAL_OBJECT_INFO(MapNode, Object);
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(MapNode, Object);
/*!
* \brief Number of elements in the SmallMapNode
@@ -187,7 +187,7 @@ class MapNode : public Object {
static constexpr const uint32_t _type_index = runtime::TypeIndex::kRuntimeMap;
static constexpr const char* _type_key = "Map";
- TVM_DECLARE_FINAL_OBJECT_INFO(MapNode, Object);
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(MapNode, Object);
/*!
* \brief Number of elements in the SmallMapNode
diff --git a/include/tvm/runtime/object.h b/include/tvm/runtime/object.h
index c2b3681bc4..8fc296519f 100644
--- a/include/tvm/runtime/object.h
+++ b/include/tvm/runtime/object.h
@@ -30,6 +30,8 @@
namespace tvm {
namespace runtime {
+template<typename T>
+class Optional;
using namespace tvm::ffi;
/*!
@@ -38,27 +40,27 @@ using namespace tvm::ffi;
* the constant, but still able to use enum.
*/
enum TypeIndex : int32_t {
- // Standard static index assignments,
- // Frontends can take benefit of these constants.
- kRuntimeString = TVMFFITypeIndex::kTVMFFIStr,
- kRuntimeMap = TVMFFITypeIndex::kTVMFFIMap,
- kRuntimeArray = TVMFFITypeIndex::kTVMFFIArray,
- /*! \brief runtime::Module. */
- kRuntimeModule = TVMFFITypeIndex::kTVMFFIRuntimeModule,
- /*! \brief runtime::NDArray. */
- kRuntimeNDArray = TVMFFITypeIndex::kTVMFFINDArray,
- /*! \brief runtime::ShapeTuple. */
- kRuntimeShapeTuple = TVMFFITypeIndex::kTVMFFIShapeTuple,
- // Extra builtin static index here
- kCustomStaticIndex = TVMFFITypeIndex::kTVMFFIStaticObjectEnd,
- /*! \brief runtime::PackedFunc. */
- kRuntimePackedFunc = kCustomStaticIndex + 1,
- /*! \brief runtime::DRef for disco distributed runtime */
- kRuntimeDiscoDRef = kCustomStaticIndex + 2,
- /*! \brief runtime::RPCObjectRef */
- kRuntimeRPCObjectRef = kCustomStaticIndex + 3,
- // static assignments that may subject to change.
- kStaticIndexEnd
+ // Standard static index assignments,
+ // Frontends can take benefit of these constants.
+ kRuntimeString = TVMFFITypeIndex::kTVMFFIStr,
+ kRuntimeMap = TVMFFITypeIndex::kTVMFFIMap,
+ kRuntimeArray = TVMFFITypeIndex::kTVMFFIArray,
+ /*! \brief runtime::Module. */
+ kRuntimeModule = TVMFFITypeIndex::kTVMFFIRuntimeModule,
+ /*! \brief runtime::NDArray. */
+ kRuntimeNDArray = TVMFFITypeIndex::kTVMFFINDArray,
+ /*! \brief runtime::ShapeTuple. */
+ kRuntimeShapeTuple = TVMFFITypeIndex::kTVMFFIShapeTuple,
+ // Extra builtin static index here
+ kCustomStaticIndex = TVMFFITypeIndex::kTVMFFIStaticObjectEnd,
+ /*! \brief runtime::PackedFunc. */
+ kRuntimePackedFunc = kCustomStaticIndex + 1,
+ /*! \brief runtime::DRef for disco distributed runtime */
+ kRuntimeDiscoDRef = kCustomStaticIndex + 2,
+ /*! \brief runtime::RPCObjectRef */
+ kRuntimeRPCObjectRef = kCustomStaticIndex + 3,
+ // static assignments that may subject to change.
+ kStaticIndexEnd,
};
class ObjectRef : public tvm::ffi::ObjectRef {
@@ -68,6 +70,29 @@ class ObjectRef : public tvm::ffi::ObjectRef {
/*! \brief Constructor from existing object ptr */
explicit ObjectRef(ObjectPtr<Object> data) : tvm::ffi::ObjectRef(data) {}
+ using tvm::ffi::ObjectRef::as;
+
+ /*!
+ * \brief Try to downcast the ObjectRef to a
+ * Optional<T> of the requested type.
+ *
+ * The function will return a NullOpt if the cast failed.
+ *
+ * if (Optional<Add> opt = node_ref.as<Add>()) {
+ * // This is an add node
+ * }
+ *
+ * \note While this method is declared in <tvm/runtime/object.h>,
+ * the implementation is in <tvm/runtime/container/optional.h> to
+ * prevent circular includes. This additional include file is only
+ * required in compilation units that uses this method.
+ *
+ * \tparam ObjectRefType the target type, must be a subtype of ObjectRef
+ */
+ template <typename ObjectRefType,
+ typename = std::enable_if_t<std::is_base_of_v<ObjectRef, ObjectRefType>>>
+ inline Optional<ObjectRefType> as() const;
+
protected:
/*! \return return a mutable internal ptr, can be used by sub-classes. */
Object* get_mutable() const { return data_.get(); }
@@ -99,6 +124,7 @@ class ObjectRef : public tvm::ffi::ObjectRef {
static ObjectPtr<ObjectType> GetDataPtr(const ObjectRef& ref) {
return ObjectPtr<ObjectType>(ref.data_.data_);
}
+
// friend classes.
friend struct ObjectPtrHash;
friend class TVMRetValue;
@@ -106,6 +132,16 @@ class ObjectRef : public tvm::ffi::ObjectRef {
friend class ObjectInternal;
};
+/*
+ * \brief Define the default copy/move constructor and assign operator
+ * \param TypeName The class typename.
+ */
+#define TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName& other) = default; \
+ TypeName(TypeName&& other) = default; \
+ TypeName& operator=(const TypeName& other) = default; \
+ TypeName& operator=(TypeName&& other) = default;
+
#define TVM_DECLARE_FINAL_OBJECT_INFO TVM_FFI_DECLARE_FINAL_OBJECT_INFO
#define TVM_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS TVM_FFI_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS
diff --git a/src/runtime/debug_compile.cc b/src/runtime/debug_compile.cc
index 5f9f6b10d1..214302d80f 100644
--- a/src/runtime/debug_compile.cc
+++ b/src/runtime/debug_compile.cc
@@ -22,6 +22,10 @@
* \brief File used for debug migration
*/
#include <tvm/runtime/container/string.h>
+#include <tvm/runtime/container/optional.h>
+#include <tvm/runtime/container/array.h>
+#include <tvm/runtime/container/map.h>
+#include <tvm/runtime/container/variant.h>
namespace tvm {
namespace debug {
(tvm) 27/38: pass packed func compile
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 24fce43a82e8a1a513145649c1b815353117f6f4
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 11:50:32 2025 -0400
pass packed func compile
---
include/tvm/runtime/module.h | 2 +-
include/tvm/runtime/object.h | 2 ++
include/tvm/runtime/packed_func.h | 21 ++++++++++-----------
src/runtime/debug_compile.cc | 1 +
4 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/include/tvm/runtime/module.h b/include/tvm/runtime/module.h
index 308c936d62..c4763c2d75 100644
--- a/include/tvm/runtime/module.h
+++ b/include/tvm/runtime/module.h
@@ -257,7 +257,7 @@ class TVM_DLL ModuleNode : public Object {
static constexpr const char* _type_key = "runtime.Module";
// NOTE: ModuleNode can still be sub-classed
//
- TVM_DECLARE_FINAL_OBJECT_INFO(ModuleNode, Object);
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(ModuleNode, Object);
protected:
friend class Module;
diff --git a/include/tvm/runtime/object.h b/include/tvm/runtime/object.h
index 8fc296519f..b84cf1914b 100644
--- a/include/tvm/runtime/object.h
+++ b/include/tvm/runtime/object.h
@@ -145,6 +145,8 @@ class ObjectRef : public tvm::ffi::ObjectRef {
#define TVM_DECLARE_FINAL_OBJECT_INFO TVM_FFI_DECLARE_FINAL_OBJECT_INFO
#define TVM_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS TVM_FFI_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS
+#define TVM_DEFINE_OBJECT_REF_METHODS TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS
+
} // namespace runtime
} // namespace tvm
#endif // TVM_RUNTIME_OBJECT_H_
diff --git a/include/tvm/runtime/packed_func.h b/include/tvm/runtime/packed_func.h
index 07a6848bfe..804fd3b98e 100644
--- a/include/tvm/runtime/packed_func.h
+++ b/include/tvm/runtime/packed_func.h
@@ -79,7 +79,8 @@ class PackedFuncObj : public Object {
static constexpr const uint32_t _type_index = TypeIndex::kRuntimePackedFunc;
static constexpr const char* _type_key = "runtime.PackedFunc";
- TVM_DECLARE_FINAL_OBJECT_INFO(PackedFuncObj, Object);
+ static const constexpr bool _type_final = true;
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(PackedFuncObj, Object);
protected:
/*!
@@ -640,14 +641,14 @@ class TVMPODValue_ {
return Module(ObjectPtr<Object>(nullptr));
}
TVM_CHECK_TYPE_CODE(type_code_, kTVMModuleHandle);
- return Module(ObjectPtr<Object>(static_cast<Object*>(value_.v_handle)));
+ return Module(details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(static_cast<Object*>(value_.v_handle)));
}
operator PackedFunc() const {
if (type_code_ == kTVMNullptr) {
return PackedFunc(ObjectPtr<Object>(nullptr));
}
TVM_CHECK_TYPE_CODE(type_code_, kTVMPackedFuncHandle);
- return PackedFunc(ObjectPtr<Object>(static_cast<Object*>(value_.v_handle)));
+ return PackedFunc(details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(static_cast<Object*>(value_.v_handle)));
}
operator Device() const {
TVM_CHECK_TYPE_CODE(type_code_, kDLDevice);
@@ -1195,12 +1196,10 @@ class TVMRetValue : public TVMPODValue_CRTP_<TVMRetValue> {
}
}
void SwitchToObject(int type_code, ObjectPtr<Object> other) {
- if (other.data_ != nullptr) {
+ if (other != nullptr) {
this->Clear();
type_code_ = type_code;
- // move the handle out
- value_.v_handle = other.data_;
- other.data_ = nullptr;
+ value_.v_handle = details::ObjectUnsafe::MoveTVMFFIObjectPtrFromObjectPtr(&other);
} else {
SwitchToPOD(kTVMNullptr);
value_.v_handle = nullptr;
@@ -1214,18 +1213,18 @@ class TVMRetValue : public TVMPODValue_CRTP_<TVMRetValue> {
delete ptr<std::string>();
break;
case kTVMPackedFuncHandle:
- static_cast<Object*>(value_.v_handle)->DecRef();
+ details::ObjectUnsafe::DecRefObjectHandle(static_cast<Object*>(value_.v_handle));
break;
case kTVMNDArrayHandle: {
NDArray::FFIDecRef(static_cast<TVMArrayHandle>(value_.v_handle));
break;
}
case kTVMModuleHandle: {
- static_cast<Object*>(value_.v_handle)->DecRef();
+ details::ObjectUnsafe::DecRefObjectHandle(static_cast<Object*>(value_.v_handle));
break;
}
case kTVMObjectHandle: {
- static_cast<Object*>(value_.v_handle)->DecRef();
+ details::ObjectUnsafe::DecRefObjectHandle(static_cast<Object*>(value_.v_handle));
break;
}
}
@@ -2439,7 +2438,7 @@ inline TVMMovableArgValue_::operator T() const {
if (type_code_ == kTVMObjectRValueRefArg) {
auto** ref = static_cast<Object**>(value_.v_handle);
if (ObjectTypeChecker<T>::Check(*ref)) {
- return T(ObjectPtr<Object>::MoveFromRValueRefArg(ref));
+ return T(details::ObjectUnsafe::MoveObjectPtrFromRValueRef<Object>(ref));
}
}
// fallback
diff --git a/src/runtime/debug_compile.cc b/src/runtime/debug_compile.cc
index 67a508dde5..e04c0b0003 100644
--- a/src/runtime/debug_compile.cc
+++ b/src/runtime/debug_compile.cc
@@ -27,6 +27,7 @@
#include <tvm/runtime/container/map.h>
#include <tvm/runtime/container/variant.h>
#include <tvm/runtime/ndarray.h>
+#include <tvm/runtime/packed_func.h>
namespace tvm {
namespace debug {
(tvm) 26/38: Fix NDArray compile
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit e4dd9cc031650280b926670a476cebb8bdf9bc56
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 11:35:09 2025 -0400
Fix NDArray compile
---
ffi/include/tvm/ffi/object.h | 2 +-
include/tvm/runtime/container/shape_tuple.h | 2 +-
include/tvm/runtime/ndarray.h | 21 +++++++++++----------
src/runtime/debug_compile.cc | 1 +
4 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 4cb26f1c2b..a6f85be999 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -95,7 +95,7 @@ TVM_FFI_INLINE bool IsObjectInstance(int32_t object_type_index);
* Which will automatically populate the type_index and deleter of the object.
*/
class Object {
- private:
+ protected:
/*! \brief header field that is the common prefix of all objects */
TVMFFIObject header_;
diff --git a/include/tvm/runtime/container/shape_tuple.h b/include/tvm/runtime/container/shape_tuple.h
index 1fb6248cc2..6532fe0fc9 100644
--- a/include/tvm/runtime/container/shape_tuple.h
+++ b/include/tvm/runtime/container/shape_tuple.h
@@ -48,7 +48,7 @@ class ShapeTupleObj : public Object {
static constexpr const uint32_t _type_index = runtime::TypeIndex::kRuntimeShapeTuple;
static constexpr const char* _type_key = "runtime.ShapeTuple";
- TVM_DECLARE_FINAL_OBJECT_INFO(ShapeTupleObj, Object);
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(ShapeTupleObj, Object);
private:
/*! \brief ShapeTuple object which is moved from std::vector container. */
diff --git a/include/tvm/runtime/ndarray.h b/include/tvm/runtime/ndarray.h
index fef61a7531..bd209f2c4c 100644
--- a/include/tvm/runtime/ndarray.h
+++ b/include/tvm/runtime/ndarray.h
@@ -308,7 +308,7 @@ class NDArray::Container : public Object, public NDArray::ContainerBase {
/*! \brief default constructor */
Container() {
// Initialize the type index.
- type_index_ = Container::RuntimeTypeIndex();
+ header_.type_index = Container::RuntimeTypeIndex();
dl_tensor.data = nullptr;
dl_tensor.ndim = 0;
dl_tensor.shape = nullptr;
@@ -318,7 +318,7 @@ class NDArray::Container : public Object, public NDArray::ContainerBase {
Container(void* data, ShapeTuple shape, DLDataType dtype, Device dev) {
// Initialize the type index.
- type_index_ = Container::RuntimeTypeIndex();
+ header_.type_index = Container::RuntimeTypeIndex();
dl_tensor.data = data;
shape_ = std::move(shape);
dl_tensor.ndim = static_cast<int>(shape_.size());
@@ -332,19 +332,20 @@ class NDArray::Container : public Object, public NDArray::ContainerBase {
* \brief Set the deleter field.
* \param deleter The deleter.
*/
- void SetDeleter(FDeleter deleter) { deleter_ = deleter; }
+ void SetDeleter(void (*deleter)(void* self)) { header_.deleter = deleter; }
// Expose DecRef and IncRef as public function
// NOTE: they are only for developer purposes only.
- using Object::DecRef;
- using Object::IncRef;
+ // using Object::DecRef;
+ // using Object::IncRef;
// Information for object protocol.
static constexpr const uint32_t _type_index = TypeIndex::kRuntimeNDArray;
static constexpr const uint32_t _type_child_slots = 0;
static constexpr const uint32_t _type_child_slots_can_overflow = true;
static constexpr const char* _type_key = "runtime.NDArray";
- TVM_DECLARE_BASE_OBJECT_INFO(NDArray::Container, Object);
+ static const constexpr bool _type_final = true;
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(NDArray::Container, Object);
protected:
friend class RPCWrappedFunc;
@@ -439,14 +440,14 @@ inline TVMArrayHandle NDArray::FFIGetHandle(const ObjectRef& nd) {
return ptr;
}
-inline void NDArray::FFIDecRef(TVMArrayHandle handle) {
- static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle))->DecRef();
-}
-
inline Object* TVMArrayHandleToObjectHandle(TVMArrayHandle handle) {
return static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle));
}
+inline void NDArray::FFIDecRef(TVMArrayHandle handle) {
+ details::ObjectUnsafe::DecRefObjectHandle(TVMArrayHandleToObjectHandle(handle));
+}
+
/*! \brief Magic number for NDArray file */
constexpr uint64_t kTVMNDArrayMagic = 0xDD5E40F096B4A13F;
diff --git a/src/runtime/debug_compile.cc b/src/runtime/debug_compile.cc
index 214302d80f..67a508dde5 100644
--- a/src/runtime/debug_compile.cc
+++ b/src/runtime/debug_compile.cc
@@ -26,6 +26,7 @@
#include <tvm/runtime/container/array.h>
#include <tvm/runtime/container/map.h>
#include <tvm/runtime/container/variant.h>
+#include <tvm/runtime/ndarray.h>
namespace tvm {
namespace debug {
(tvm) 08/38: [FFI] Function support
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 854abf58be7c604019bd051ede43d6638dc762f8
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Aug 18 17:14:29 2024 -0400
[FFI] Function support
Co-authored-by: Junru Shao <ju...@apache.org>
---
ffi/include/tvm/ffi/any.h | 30 ++-
ffi/include/tvm/ffi/c_api.h | 16 ++
ffi/include/tvm/ffi/function.h | 341 +++++++++++++++++++++++++++++++++
ffi/include/tvm/ffi/function_details.h | 262 +++++++++++++++++++++++++
ffi/include/tvm/ffi/internal_utils.h | 26 ++-
ffi/include/tvm/ffi/type_traits.h | 9 +-
ffi/src/ffi/traceback.h | 6 +
ffi/tests/example/test_any.cc | 1 -
ffi/tests/example/test_function.cc | 123 ++++++++++++
ffi/tests/example/testing_object.h | 82 ++++++++
10 files changed, 886 insertions(+), 10 deletions(-)
diff --git a/ffi/include/tvm/ffi/any.h b/ffi/include/tvm/ffi/any.h
index 1568fe98b2..6f2935f8a6 100644
--- a/ffi/include/tvm/ffi/any.h
+++ b/ffi/include/tvm/ffi/any.h
@@ -55,6 +55,10 @@ class AnyView {
void swap(AnyView& other) { // NOLINT(*)
std::swap(data_, other.data_);
}
+ /*! \return the internal type index */
+ int32_t type_index() const {
+ return data_.type_index;
+ }
// default constructors
AnyView() { data_.type_index = TypeIndex::kTVMFFINone; }
~AnyView() = default;
@@ -88,7 +92,7 @@ class AnyView {
operator T() const {
std::optional<T> opt = TypeTraits<T>::TryConvertFromAnyView(&data_);
if (opt.has_value()) {
- return std::move(opt.value());
+ return std::move(*opt);
}
TVM_FFI_THROW(TypeError) << "Cannot convert from type `" << TypeIndex2TypeKey(data_.type_index)
<< "` to `" << TypeTraits<T>::TypeStr() << "`";
@@ -110,9 +114,6 @@ class AnyView {
}
};
-// layout assert to ensure we can freely cast between the two types
-static_assert(sizeof(AnyView) == sizeof(TVMFFIAny));
-
namespace details {
/*!
* \brief Helper function to inplace convert any view to any.
@@ -155,7 +156,10 @@ class Any {
void swap(Any& other) { // NOLINT(*)
std::swap(data_, other.data_);
}
-
+ /*! \return the internal type index */
+ int32_t type_index() const {
+ return data_.type_index;
+ }
// default constructors
Any() { data_.type_index = TypeIndex::kTVMFFINone; }
~Any() { this->reset(); }
@@ -205,13 +209,27 @@ class Any {
operator T() const {
std::optional<T> opt = TypeTraits<T>::TryConvertFromAnyView(&data_);
if (opt.has_value()) {
- return std::move(opt.value());
+ return std::move(*opt);
}
TVM_FFI_THROW(TypeError) << "Cannot convert from type `" << TypeIndex2TypeKey(data_.type_index)
<< "` to `" << TypeTraits<T>::TypeStr() << "`";
}
+
+ // FFI related operations
+ /*!
+ * Move the current data to FFI any
+ * \parma result the output to nmove to
+ */
+ void MoveToTVMFFIAny(TVMFFIAny* result) {
+ *result = data_;
+ data_.type_index = TypeIndex::kTVMFFINone;
+ }
};
+// layout assert to ensure we can freely cast between the two types
+static_assert(sizeof(AnyView) == sizeof(TVMFFIAny));
+static_assert(sizeof(Any) == sizeof(TVMFFIAny));
+
} // namespace ffi
} // namespace tvm
#endif // TVM_FFI_ANY_H_
diff --git a/ffi/include/tvm/ffi/c_api.h b/ffi/include/tvm/ffi/c_api.h
index e0597d0dda..65341feb83 100644
--- a/ffi/include/tvm/ffi/c_api.h
+++ b/ffi/include/tvm/ffi/c_api.h
@@ -164,6 +164,22 @@ typedef struct {
const int32_t* type_acenstors;
} TVMFFITypeInfo;
+/*!
+ * \brief Type that defines C-style safe call convention
+ *
+ * Safe call explicitly catches exception on function boundary.
+ *
+ * \param func The function handle
+ * \param num_args Number if input arguments
+ * \param args The input arguments to the call.
+ * \param result Store output result
+ *
+ * \return The call return 0 if call is successful.
+ * It returns non-zero value if there is an error.
+ * When error happens, the exception object will be stored in result.
+ */
+typedef int (*TVMFFISafeCallType)(void* func, int32_t num_args, const TVMFFIAny* args, TVMFFIAny* result);
+
#ifdef __cplusplus
} // TVM_FFI_EXTERN_C
#endif
diff --git a/ffi/include/tvm/ffi/function.h b/ffi/include/tvm/ffi/function.h
new file mode 100644
index 0000000000..81b0d8fab2
--- /dev/null
+++ b/ffi/include/tvm/ffi/function.h
@@ -0,0 +1,341 @@
+/*
+ * 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.
+ */
+/*!
+ * \file tvm/ffi/function.h
+ * \brief A managed function in the TVM FFI.
+ */
+#ifndef TVM_FFI_FUNCTION_H_
+#define TVM_FFI_FUNCTION_H_
+
+#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/internal_utils.h>
+#include <tvm/ffi/any.h>
+#include <tvm/ffi/error.h>
+#include <tvm/ffi/function_details.h>
+
+namespace tvm {
+namespace ffi {
+
+/*!
+ * \brief Object container class that backs ffi::Function
+ * \note Do not use this function directly, use ffi::Function
+ */
+class FunctionObj : public Object {
+ public:
+ typedef void (*FCall)(const FunctionObj*, int32_t, const AnyView* , Any*);
+ /*! \brief A C++ style call implementation */
+ FCall call;
+ /*! \brief A C API compatible call with exception catching. */
+ TVMFFISafeCallType safe_call;
+
+ TVM_FFI_INLINE void CallPacked(int32_t num_args, const AnyView* args, Any* result) const {
+ this->call(this, num_args, args, result);
+ }
+
+ static constexpr const uint32_t _type_index = TypeIndex::kTVMFFIFunc;
+ static constexpr const char* _type_key = "object.Function";
+
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(FunctionObj, Object);
+
+ protected:
+ /*! \brief Make default constructor protected. */
+ FunctionObj() {}
+
+ // Implementing safe call style
+ static int32_t SafeCall(void* func, int32_t num_args, const TVMFFIAny* args, TVMFFIAny* result) {
+ FunctionObj* self = static_cast<FunctionObj*>(func);
+ try {
+ self->call(self, num_args, reinterpret_cast<const AnyView*>(args), reinterpret_cast<Any*>(result));
+ return 0;
+ } catch (const tvm::ffi::Error& err) {
+ Any(std::move(err)).MoveToTVMFFIAny(result);
+ return 1;
+ } catch (const std::runtime_error& err) {
+ Any(
+ tvm::ffi::Error("RuntimeError", err.what(), "")
+ ).MoveToTVMFFIAny(result);
+ return 1;
+ }
+ TVM_FFI_UNREACHABLE();
+ }
+
+ friend class Function;
+};
+
+namespace details {
+/*!
+ * \brief Derived object class for constructing FunctionObj backed by a TCallable
+ *
+ * This is a helper class that
+ */
+template <typename TCallable>
+class FunctionObjImpl : public FunctionObj {
+ public:
+ using TStorage = typename std::remove_cv<typename std::remove_reference<TCallable>::type>::type;
+ /*! \brief The type of derived object class */
+ using TSelf = FunctionObjImpl<TCallable>;
+ /*!
+ * \brief Derived object class for constructing PackedFuncObj.
+ * \param callable The type-erased callable object.
+ */
+ explicit FunctionObjImpl(TCallable callable)
+ : callable_(callable) {
+ this->call = Call;
+ this->safe_call = SafeCall;
+ }
+
+ private:
+ // implementation of call
+ static void Call(const FunctionObj* func, int32_t num_args, const AnyView* args, Any* result) {
+ (static_cast<const TSelf*>(func))->callable_(num_args, args, result);
+ }
+
+ /*! \brief Type-erased filed for storing callable object*/
+ mutable TStorage callable_;
+};
+
+/*!
+ * \brief Base class to provide a common implementation to redirect call to safecall
+ * \tparam Derived The derived class in CRTP-idiom
+ */
+template <typename Derived>
+struct RedirectCallToSafeCall {
+ static void Call(const FunctionObj* func, int32_t num_args, const AnyView* args , Any* rv) {
+ Derived* self = static_cast<Derived*>(const_cast<FunctionObj*>(func));
+ int ret_code = self->RedirectSafeCall(
+ num_args, reinterpret_cast<const TVMFFIAny*>(args),
+ reinterpret_cast<TVMFFIAny*>(rv));
+ if (ret_code != 0) {
+ if (std::optional<tvm::ffi::Error> err = rv->TryAs<tvm::ffi::Error>()) {
+ throw std::move(*err);
+ } else {
+ TVM_FFI_THROW(RuntimeError) << "Error encountered when calling a tvm::ffi::Function";
+ }
+ }
+ }
+
+ static int32_t SafeCall(void* func, int32_t num_args, const TVMFFIAny* args, TVMFFIAny* rv) {
+ Derived* self = reinterpret_cast<Derived*>(func);
+ return self->RedirectSafeCall(num_args, args, rv);
+ }
+};
+
+/*!
+ * \brief FunctionObj specialization that leverages C-style callback definitions.
+ */
+class ExternCFunctionObjImpl :
+ public FunctionObj,
+ public RedirectCallToSafeCall<ExternCFunctionObjImpl> {
+ public:
+ using RedirectCallToSafeCall<ExternCFunctionObjImpl>::SafeCall;
+
+ ExternCFunctionObjImpl(void* self, TVMFFISafeCallType safe_call, void (*deleter)(void* self))
+ : self_(self), safe_call_(safe_call), deleter_(deleter) {
+ this->call = RedirectCallToSafeCall<ExternCFunctionObjImpl>::Call;
+ this->safe_call = RedirectCallToSafeCall<ExternCFunctionObjImpl>::SafeCall;
+ }
+
+ ~ExternCFunctionObjImpl() {
+ deleter_(self_);
+ }
+
+ TVM_FFI_INLINE int32_t RedirectSafeCall(int32_t num_args, const TVMFFIAny* args, TVMFFIAny* rv) const {
+ return safe_call_(self_, num_args, args, rv);
+ }
+
+ private:
+ void* self_;
+ TVMFFISafeCallType safe_call_;
+ void (*deleter_)(void* self);
+};
+
+/*!
+ * \brief FunctionObj specialization that wraps an external function.
+ */
+class ImportedFunctionObjImpl :
+ public FunctionObj,
+ public RedirectCallToSafeCall<ImportedFunctionObjImpl> {
+ public:
+ using RedirectCallToSafeCall<ImportedFunctionObjImpl>::SafeCall;
+
+ explicit ImportedFunctionObjImpl(ObjectPtr<Object> data)
+ : data_(data) {
+ this->call = RedirectCallToSafeCall<ImportedFunctionObjImpl>::Call;
+ this->safe_call = RedirectCallToSafeCall<ImportedFunctionObjImpl>::SafeCall;
+ }
+
+ TVM_FFI_INLINE int32_t RedirectSafeCall(int32_t num_args, const TVMFFIAny* args, TVMFFIAny* rv) const {
+ FunctionObj* func = const_cast<FunctionObj*>(static_cast<const FunctionObj*>(data_.get()));
+ return func->safe_call(func, num_args, args, rv);
+ }
+
+ private:
+ ObjectPtr<Object> data_;
+};
+
+// Helper class to set packed arguments
+class PackedArgsSetter {
+ public:
+ PackedArgsSetter(AnyView* args) : args_(args) {}
+
+ // NOTE: setter needs to be very carefully designed
+ // such that we do not have temp variable conversion(eg. convert from lvalue to rvalue)
+ // that is why we need T&& and std::forward here
+ template<typename T>
+ TVM_FFI_INLINE void operator()(size_t i, T&& value) const {
+ args_[i].operator=(std::forward<T>(value));
+ }
+
+ private:
+ AnyView* args_;
+};
+} // namespace details
+
+/*!
+ * \brief ffi::Function is a type-erased function.
+ * The arguments are passed by packed format.
+ */
+class Function : public ObjectRef {
+ public:
+ /*! \brief Constructor from null */
+ Function(std::nullptr_t) : ObjectRef(nullptr) {} // NOLINT(*)
+ /*!
+ * \brief Constructing a packed function from a callable type
+ * whose signature is consistent with `PackedFunc`
+ * \param packed_call The packed function signature
+ */
+ template <typename TCallable>
+ static Function FromPacked(TCallable packed_call) {
+ static_assert(
+ std::is_convertible_v<TCallable, std::function<void(int32_t, const AnyView*, Any*)>>,
+ "tvm::ffi::Function::FromPacked requires input function signature to match packed func format"
+ );
+ using ObjType = typename details::FunctionObjImpl<TCallable>;
+ Function func;
+ func.data_ = make_object<ObjType>(std::forward<TCallable>(packed_call));
+ return func;
+ }
+ /*!
+ * \brief Import a possibly externally defined function to this dll
+ * \param other Function defined in another dynamic library.
+ *
+ * \note This function will redirect the call to safe_call in other.
+ * It will try to detect if the function is already from the same DLL
+ * and directly return the original function if so.
+ *
+ * \return The imported function.
+ */
+ static Function ImportFromExternDLL(Function other) {
+ const FunctionObj* other_func = static_cast<const FunctionObj*>(other.get());
+ // the other function comes from the same dll, no action needed
+ if (other_func->safe_call == FunctionObj::SafeCall ||
+ other_func->safe_call == details::ImportedFunctionObjImpl::SafeCall ||
+ other_func->safe_call == details::ExternCFunctionObjImpl::SafeCall) {
+ return other;
+ }
+ // the other function coems from a different library
+ Function func;
+ func.data_ = make_object<details::ImportedFunctionObjImpl>(std::move(other.data_));
+ return func;
+ }
+ /*!
+ * \brief Create ffi::Function from a C style callbacks.
+ * \param self Resource handle to the function
+ * \param safe_call The safe_call definition in C.
+ * \param deleter The deleter to release the resource of self.
+ * \return The created function.
+ */
+ static Function FromExternC(void* self, TVMFFISafeCallType safe_call, void (*deleter)(void* self)) {
+ // the other function coems from a different library
+ Function func;
+ func.data_ = make_object<details::ExternCFunctionObjImpl>(self, safe_call, deleter);
+ return func;
+ }
+ /*!
+ * \brief Constructing a packed function from a normal function.
+ *
+ * \param callable the internal container of packed function.
+ */
+ template <typename TCallable>
+ static Function FromUnpacked(TCallable callable) {
+ using FuncInfo = details::FunctionInfo<TCallable>;
+ auto call_packed = [callable](int32_t num_args, const AnyView* args, Any* rv) -> void {
+ details::unpack_call<typename FuncInfo::RetType, FuncInfo::num_args>(nullptr, callable, num_args, args, rv);
+ };
+ return FromPacked(call_packed);
+ }
+ /*!
+ * \brief Constructing a packed function from a normal function.
+ *
+ * \param callable the internal container of packed function.
+ * \param name optional name attacked to the function.
+ */
+ template <typename TCallable>
+ static Function FromUnpacked(TCallable callable, std::string name) {
+ using FuncInfo = details::FunctionInfo<TCallable>;
+ auto call_packed = [callable, name](int32_t num_args, const AnyView* args, Any* rv) -> void {
+ details::unpack_call<typename FuncInfo::RetType, FuncInfo::num_args>(&name, callable,
+ num_args, args, rv);
+ };
+ return FromPacked(call_packed);
+ }
+ /*!
+ * \brief Call function by directly passing in unpacked arguments.
+ *
+ * \param args Arguments to be passed.
+ * \tparam Args arguments to be passed.
+ *
+ * \code
+ * // Example code on how to call packed function
+ * void CallFFIFunction(tvm::ffi::Function f) {
+ * // call like normal functions by pass in arguments
+ * // return value is automatically converted back
+ * int rvalue = f(1, 2.0);
+ * }
+ * \endcode
+ */
+ template <typename... Args>
+ TVM_FFI_INLINE Any operator()(Args&&... args) const {
+ const int kNumArgs = sizeof...(Args);
+ const int kArraySize = kNumArgs > 0 ? kNumArgs : 1;
+ AnyView args_pack[kArraySize];
+ details::for_each(details::PackedArgsSetter(args_pack), std::forward<Args>(args)...);
+ Any result;
+ static_cast<FunctionObj*>(data_.get())->CallPacked(kNumArgs, args_pack, &result);
+ return result;
+ }
+ /*!
+ * \brief Call the function in packed format.
+ * \param args The arguments
+ * \param rv The return value.
+ */
+ TVM_FFI_INLINE void CallPacked(int32_t num_args, const AnyView* args, Any* result) const {
+ static_cast<FunctionObj*>(data_.get())->CallPacked(num_args, args, result);
+ }
+ /*! \return Whether the packed function is nullptr */
+ bool operator==(std::nullptr_t) const { return data_ == nullptr; }
+ /*! \return Whether the packed function is not nullptr */
+ bool operator!=(std::nullptr_t) const { return data_ != nullptr; }
+
+ TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS(Function, ObjectRef, FunctionObj);
+};
+
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_OBJECT_H_
diff --git a/ffi/include/tvm/ffi/function_details.h b/ffi/include/tvm/ffi/function_details.h
new file mode 100644
index 0000000000..f22258029d
--- /dev/null
+++ b/ffi/include/tvm/ffi/function_details.h
@@ -0,0 +1,262 @@
+/*
+ * 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.
+ */
+/*!
+ * \file tvm/ffi/function_details.h
+ * \brief Implements the funciton signature reflection
+ */
+#ifndef TVM_FFI_FUNCTION_DETAILS_H_
+#define TVM_FFI_FUNCTION_DETAILS_H_
+
+#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/internal_utils.h>
+#include <tvm/ffi/any.h>
+#include <tvm/ffi/error.h>
+
+namespace tvm {
+namespace ffi {
+namespace details {
+
+template <typename Type>
+struct Type2Str {
+ static std::string v() {
+ return TypeTraitsNoCR<Type>::TypeStr();
+ }
+};
+
+template <>
+struct Type2Str<Any> {
+ static const char* v() {
+ return "Any";
+ }
+};
+
+template <>
+struct Type2Str<AnyView> {
+ static const char* v() {
+ return "AnyView";
+ }
+};
+template <>
+struct Type2Str<void> {
+ static const char* v() {
+ return "void";
+ }
+};
+
+template <typename ArgType>
+struct Arg2Str {
+ template <size_t i>
+ static TVM_FFI_INLINE void Apply(std::ostream& os) {
+ using Arg = std::tuple_element_t<i, ArgType>;
+ if constexpr (i != 0) {
+ os << ", ";
+ }
+ os << i << ": " << Type2Str<Arg>::v();
+ }
+ template <size_t... I>
+ static TVM_FFI_INLINE void Run(std::ostream &os, std::index_sequence<I...>) {
+ using TExpander = int[];
+ (void)TExpander{0, (Apply<I>(os), 0)...};
+ }
+};
+
+template <typename T>
+static constexpr bool ArgSupported = (
+ std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>, Any> ||
+ std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>, AnyView> ||
+ TypeTraitsNoCR<T>::enabled
+);
+
+// NOTE: return type can only support non-reference managed returns
+template <typename T>
+static constexpr bool RetSupported = (
+ std::is_same_v<T, Any> || std::is_void_v<T> || TypeTraits<T>::enabled
+);
+
+template <typename R, typename... Args>
+struct FuncFunctorImpl {
+ using FType = R(Args...);
+ using ArgType = std::tuple<Args...>;
+ using RetType = R;
+ /*! \brief total number of arguments*/
+ static constexpr size_t num_args = sizeof...(Args);
+ /*! \brief Whether this function can be converted to ffi::Function via FromUnpacked */
+ static constexpr bool unpacked_supported = (ArgSupported<Args> && ...) && (RetSupported<R>);
+
+ static TVM_FFI_INLINE std::string Sig() {
+ using IdxSeq = std::make_index_sequence<sizeof...(Args)>;
+ std::ostringstream ss;
+ ss << "(";
+ Arg2Str<std::tuple<Args...>>::Run(ss, IdxSeq{});
+ ss << ") -> " << Type2Str<R>::v();
+ return ss.str();
+ }
+};
+
+template <typename T>
+struct FunctionInfoHelper;
+
+template <typename T, typename R, typename... Args>
+struct FunctionInfoHelper<R (T::*)(Args...)>: FuncFunctorImpl<R, Args...> {};
+template <typename T, typename R, typename... Args>
+struct FunctionInfoHelper<R (T::*)(Args...) const>: FuncFunctorImpl<R, Args...> {};
+
+/*!
+ * \brief Template class to get function signature of a function or functor.
+ * \tparam T The function/functor type.
+ * \note We need a decltype redirection because this helps lambda types.
+ */
+template <typename T>
+struct FunctionInfo : FunctionInfoHelper<decltype(&T::operator())> {};
+
+template <typename R, typename... Args>
+struct FunctionInfo<R(Args...)> : FuncFunctorImpl<R, Args...> {};
+template <typename R, typename... Args>
+struct FunctionInfo<R (*)(Args...)> : FuncFunctorImpl<R, Args...> {};
+
+/*! \brief Using static function to output TypedPackedFunc signature */
+typedef std::string (*FGetFuncSignature)();
+
+template<typename T>
+TVM_FFI_INLINE std::optional<T> TryAs(AnyView arg) {
+ return arg.TryAs<T>();
+}
+template<>
+TVM_FFI_INLINE std::optional<Any> TryAs<Any>(AnyView arg) {
+ return Any(arg);
+}
+template<>
+TVM_FFI_INLINE std::optional<AnyView> TryAs<AnyView>(AnyView arg) {
+ return arg;
+}
+
+/*!
+ * \brief Auxilary argument value with context for error reporting
+ */
+class MovableArgValueWithContext {
+ public:
+ /*!
+ * \brief move constructor from another return value.
+ * \param args The argument list
+ * \param arg_index In a function call, this argument is at index arg_index (0-indexed).
+ * \param optional_name Name of the function being called. Can be nullptr if the function is not.
+ * \param f_sig Pointer to static function outputting signature of the function being called.
+ * named.
+ */
+ TVM_FFI_INLINE MovableArgValueWithContext(
+ const AnyView* args, int32_t arg_index,
+ const std::string* optional_name,
+ FGetFuncSignature f_sig)
+ : args_(args),
+ arg_index_(arg_index),
+ optional_name_(optional_name),
+ f_sig_(f_sig) {}
+
+ template <typename Type>
+ TVM_FFI_INLINE operator Type() {
+ using TypeWithoutCR = std::remove_const_t<std::remove_reference_t<Type>>;
+ std::optional<TypeWithoutCR> opt = TryAs<TypeWithoutCR>(args_[arg_index_]);
+ if (opt.has_value()) {
+ return std::move(*opt);
+ }
+ TVM_FFI_THROW(TypeError)
+ << "Mismatched type on argument #" << arg_index_ << " when calling: `"
+ << (optional_name_ == nullptr ? "" : *optional_name_)
+ << (f_sig_ == nullptr ? "" : (*f_sig_)()) << "`. Expected `"
+ << Type2Str<Type>::v() << "` but got `"
+ << TypeIndex2TypeKey(args_[arg_index_].type_index()) << "`";
+ }
+
+ private:
+ const AnyView* args_;
+ int32_t arg_index_;
+ const std::string* optional_name_;
+ FGetFuncSignature f_sig_;
+};
+
+template <typename R, int nleft, int index, typename F>
+struct unpack_call_dispatcher {
+ template <typename... Args>
+ TVM_FFI_INLINE static void run(const std::string* optional_name, FGetFuncSignature f_sig, const F& f,
+ int32_t num_args, const AnyView* args, Any* rv,
+ Args&&... unpacked_args) {
+ // construct a movable argument value
+ // which allows potential move of argument to the input of F.
+ unpack_call_dispatcher<R, nleft - 1, index + 1, F>::run(
+ optional_name, f_sig, f, num_args, args, rv, std::forward<Args>(unpacked_args)...,
+ MovableArgValueWithContext(args, index, optional_name, f_sig));
+ }
+};
+
+template <typename R, int index, typename F>
+struct unpack_call_dispatcher<R, 0, index, F> {
+ template <typename... Args>
+ TVM_FFI_INLINE static void run(const std::string*, FGetFuncSignature, const F& f,
+ int32_t, const AnyView*, Any* rv,
+ Args&&... unpacked_args) {
+ using RetType = decltype(f(std::forward<Args>(unpacked_args)...));
+ if constexpr (std::is_same_v<RetType, R>) {
+ *rv = f(std::forward<Args>(unpacked_args)...);
+ } else {
+ *rv = R(f(std::forward<Args>(unpacked_args)...));
+ }
+ }
+};
+
+template <int index, typename F>
+struct unpack_call_dispatcher<void, 0, index, F> {
+ template <typename... Args>
+ TVM_FFI_INLINE static void run(const std::string* optional_name, FGetFuncSignature f_sig, const F& f,
+ int32_t num_args, const AnyView* args, Any* rv,
+ Args&&... unpacked_args) {
+ f(std::forward<Args>(unpacked_args)...);
+ }
+};
+
+template <typename R, int nargs, typename F>
+TVM_FFI_INLINE void unpack_call(const std::string* optional_name, const F& f,
+ int32_t num_args, const AnyView* args, Any* rv) {
+ using FuncInfo = FunctionInfo<F>;
+ FGetFuncSignature f_sig = FuncInfo::Sig;
+ static_assert(FuncInfo::unpacked_supported, "The function signature cannot support unpacked call");
+ if (nargs != num_args) {
+ TVM_FFI_THROW(TypeError)
+ << "Mismatched number of arguments when calling: `"
+ << (optional_name == nullptr ? "" : *optional_name)
+ << (f_sig == nullptr ? "" : (*f_sig)()) << "`. Expected "
+ << nargs << " but got " << num_args << " arguments";
+ }
+ unpack_call_dispatcher<R, nargs, 0, F>::run(optional_name, f_sig, f, num_args, args, rv);
+}
+
+template <typename FType>
+struct unpack_call_by_signature {};
+
+template <typename R, typename... Args>
+struct unpack_call_by_signature<R(Args...)> {
+ template <typename F>
+ TVM_FFI_INLINE static void run(const F& f, int32_t num_args, const AnyView* args, Any* rv) {
+ unpack_call<R, sizeof...(Args)>(nullptr, f, num_args, args, rv);
+ }
+};
+
+} // namespace details
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_FUNCTION_DETAILS_H_
diff --git a/ffi/include/tvm/ffi/internal_utils.h b/ffi/include/tvm/ffi/internal_utils.h
index 900b7fd2e2..ffe1dd9e1a 100644
--- a/ffi/include/tvm/ffi/internal_utils.h
+++ b/ffi/include/tvm/ffi/internal_utils.h
@@ -17,8 +17,8 @@
* under the License.
*/
/*!
- * \file tvm/ffi/internal_utils.h
- * \brief Utility functions and macros for internal use
+ * \file tvm/ffi/base_details.h
+ * \brief Internal use utilities
*/
#ifndef TVM_FFI_INTERNAL_UTILS_H_
#define TVM_FFI_INTERNAL_UTILS_H_
@@ -26,6 +26,7 @@
#include <tvm/ffi/c_api.h>
#include <cstddef>
+#include <utility>
#if defined(_MSC_VER)
#define TVM_FFI_INLINE __forceinline
@@ -110,6 +111,27 @@ TVM_FFI_INLINE int32_t AtomicLoadRelaxed(const int32_t* ptr) {
return __atomic_load_n(raw_ptr, __ATOMIC_RELAXED);
#endif
}
+
+// for each iterator
+template <bool stop, std::size_t I, typename F>
+struct for_each_dispatcher {
+ template <typename T, typename... Args>
+ static void run(const F& f, T&& value, Args&&... args) { // NOLINT(*)
+ f(I, std::forward<T>(value));
+ for_each_dispatcher<sizeof...(Args) == 0, (I + 1), F>::run(f, std::forward<Args>(args)...);
+ }
+};
+
+template <std::size_t I, typename F>
+struct for_each_dispatcher<true, I, F> {
+ static void run(const F&) {} // NOLINT(*)
+};
+
+template <typename F, typename... Args>
+void for_each(const F& f, Args&&... args) { // NOLINT(*)
+ for_each_dispatcher<sizeof...(Args) == 0, 0, F>::run(f, std::forward<Args>(args)...);
+}
+
} // namespace details
} // namespace ffi
} // namespace tvm
diff --git a/ffi/include/tvm/ffi/type_traits.h b/ffi/include/tvm/ffi/type_traits.h
index 0a1d7d9941..18c7ab2a35 100644
--- a/ffi/include/tvm/ffi/type_traits.h
+++ b/ffi/include/tvm/ffi/type_traits.h
@@ -52,6 +52,13 @@ struct TypeTraits {
static constexpr bool enabled = false;
};
+/*!
+ * \brief TypeTraits that removes const and reference keywords.
+ * \tparam T the original type
+ */
+template <typename T>
+using TypeTraitsNoCR = TypeTraits<std::remove_const_t<std::remove_reference_t<T>>>;
+
// Integer POD values
template <typename Int>
struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> {
@@ -153,7 +160,7 @@ inline std::string TypeIndex2TypeKey(int32_t type_index) {
case TypeIndex::kTVMFFIInt:
return "int";
case TypeIndex::kTVMFFIFloat:
- return "double";
+ return "float";
case TypeIndex::kTVMFFIOpaquePtr:
return "void*";
case TypeIndex::kTVMFFIDataType:
diff --git a/ffi/src/ffi/traceback.h b/ffi/src/ffi/traceback.h
index 2bc9e523a7..e4ba2a589d 100644
--- a/ffi/src/ffi/traceback.h
+++ b/ffi/src/ffi/traceback.h
@@ -57,6 +57,12 @@ inline bool ShouldExcludeFrame(const char* filename, const char* symbol) {
if (strstr(filename, "include/tvm/ffi/error.h")) {
return true;
}
+ if (strstr(filename, "include/tvm/ffi/function_details.h")) {
+ return true;
+ }
+ if (strstr(filename, "include/tvm/ffi/function.h")) {
+ return true;
+ }
if (strstr(filename, "src/ffi/traceback.cc")) {
return true;
}
diff --git a/ffi/tests/example/test_any.cc b/ffi/tests/example/test_any.cc
index 55ef21405e..791ecfe68c 100644
--- a/ffi/tests/example/test_any.cc
+++ b/ffi/tests/example/test_any.cc
@@ -122,7 +122,6 @@ TEST(Any, Object) {
} catch (const Error& error) {
EXPECT_EQ(error->kind, "TypeError");
std::string what = error.what();
- std::cout << what;
EXPECT_NE(what.find("Cannot convert from type `test.Int` to `test.Float`"),
std::string::npos);
throw;
diff --git a/ffi/tests/example/test_function.cc b/ffi/tests/example/test_function.cc
new file mode 100644
index 0000000000..46c69c3739
--- /dev/null
+++ b/ffi/tests/example/test_function.cc
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+#include <gtest/gtest.h>
+#include <tvm/ffi/any.h>
+#include <tvm/ffi/memory.h>
+#include <tvm/ffi/function.h>
+
+#include "./testing_object.h"
+
+namespace {
+
+using namespace tvm::ffi;
+using namespace tvm::ffi::testing;
+
+TEST(Func, FromPacked) {
+ Function fadd1 = Function::FromPacked(
+ [](int32_t num_args, const AnyView* args, Any* rv) {
+ EXPECT_EQ(num_args, 1);
+ int32_t a = args[0];
+ *rv = a + 1;
+ }
+ );
+ int b = fadd1(1);
+ EXPECT_EQ(b, 2);
+
+ Function fadd2 = Function::FromPacked(
+ [](int32_t num_args, const AnyView* args, Any* rv) {
+ EXPECT_EQ(num_args, 1);
+ TInt a = args[0];
+ EXPECT_EQ(a.use_count(), 2);
+ *rv = a->value + 1;
+ }
+ );
+ EXPECT_EQ(fadd2(TInt(12)).operator int(), 13);
+}
+
+TEST(Func, FromUnpacked) {
+ // try decution
+ Function fadd1 = Function::FromUnpacked(
+ [](const int32_t& a) -> int { return a + 1; }
+ );
+ int b = fadd1(1);
+ EXPECT_EQ(b, 2);
+
+ // convert that triggers error
+ EXPECT_THROW(
+ {
+ try {
+ fadd1(1.1);
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ EXPECT_STREQ(
+ error->message.c_str(),
+ "Mismatched type on argument #0 when calling: `(0: int) -> int`. "
+ "Expected `int` but got `float`");
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+
+ // convert that triggers error
+ EXPECT_THROW(
+ {
+ try {
+ fadd1();
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ EXPECT_STREQ(
+ error->message.c_str(),
+ "Mismatched number of arguments when calling: `(0: int) -> int`. "
+ "Expected 1 but got 0 arguments");
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+
+ // try decution
+ Function fpass_and_return = Function::FromUnpacked(
+ [](TInt x, int value, AnyView z) -> Function {
+ EXPECT_EQ(x.use_count(), 2);
+ EXPECT_EQ(x->value, value);
+ if (auto opt = z.TryAs<int>()) {
+ EXPECT_EQ(value, *opt);
+ }
+ return Function::FromUnpacked([value](int x) -> int { return x + value; });
+ },
+ "fpass_and_return");
+ TInt a(11);
+ Function fret = fpass_and_return(std::move(a), 11, 11);
+ EXPECT_EQ(fret(12).operator int(), 23);
+
+ EXPECT_THROW(
+ {
+ try {
+ fpass_and_return();
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ EXPECT_STREQ(error->message.c_str(),
+ "Mismatched number of arguments when calling: "
+ "`fpass_and_return(0: test.Int, 1: int, 2: AnyView) -> object.Function`. "
+ "Expected 3 but got 0 arguments");
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+}
+} // namespace
diff --git a/ffi/tests/example/testing_object.h b/ffi/tests/example/testing_object.h
new file mode 100644
index 0000000000..00cc58b7b7
--- /dev/null
+++ b/ffi/tests/example/testing_object.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#ifndef TVM_FFI_TESTING_OBJECT_H_
+#define TVM_FFI_TESTING_OBJECT_H_
+#include <tvm/ffi/memory.h>
+#include <tvm/ffi/object.h>
+
+namespace tvm {
+namespace ffi {
+namespace testing {
+
+class TNumberObj : public Object {
+ public:
+ // declare as one slot, with float as overflow
+ static constexpr uint32_t _type_child_slots = 1;
+ static constexpr const char* _type_key = "test.Number";
+ TVM_FFI_DECLARE_BASE_OBJECT_INFO(TNumberObj, Object);
+};
+
+class TNumber : public ObjectRef {
+ public:
+ TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS(TNumber, ObjectRef, TNumberObj);
+};
+
+class TIntObj : public TNumberObj {
+ public:
+ int64_t value;
+
+ TIntObj(int64_t value) : value(value) {}
+
+ static constexpr const char* _type_key = "test.Int";
+ TVM_FFI_DECLARE_FINAL_OBJECT_INFO(TIntObj, TNumberObj);
+};
+
+class TInt : public TNumber {
+ public:
+ explicit TInt(int64_t value) {
+ data_ = make_object<TIntObj>(value);
+ }
+
+ TVM_FFI_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(TInt, TNumber, TIntObj);
+};
+
+class TFloatObj : public TNumberObj {
+ public:
+ double value;
+
+ TFloatObj(double value) : value(value) {}
+
+ static constexpr const char* _type_key = "test.Float";
+ TVM_FFI_DECLARE_FINAL_OBJECT_INFO(TFloatObj, TNumberObj);
+};
+
+class TFloat : public TNumber {
+ public:
+ explicit TFloat(double value) {
+ data_ = make_object<TFloatObj>(value);
+ }
+
+ TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS(TFloat, TNumber, TFloatObj);
+};
+} // namespace testing
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_TESTING_OBJECT_H_
\ No newline at end of file
(tvm) 17/38: [FFI] Initial reflection support
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 27ffeda002f8c28c2ec966d1b6e5c789cd10a8b8
Author: tqchen <tq...@gmail.com>
AuthorDate: Fri Dec 27 08:02:30 2024 +0800
[FFI] Initial reflection support
---
ffi/cmake/Utils/CxxWarning.cmake | 2 +-
ffi/include/tvm/ffi/c_api.h | 209 +++++++++++++++++++++++++--------
ffi/include/tvm/ffi/function_details.h | 6 +-
ffi/include/tvm/ffi/object.h | 31 +----
ffi/include/tvm/ffi/reflection.h | 182 ++++++++++++++++++++++++++++
ffi/include/tvm/ffi/type_traits.h | 9 ++
ffi/src/ffi/object.cc | 49 +++++++-
ffi/tests/cpp/test_reflection.cc | 49 ++++++++
ffi/tests/cpp/testing_object.h | 5 +-
9 files changed, 455 insertions(+), 87 deletions(-)
diff --git a/ffi/cmake/Utils/CxxWarning.cmake b/ffi/cmake/Utils/CxxWarning.cmake
index 50ee5b616d..efa9781e55 100644
--- a/ffi/cmake/Utils/CxxWarning.cmake
+++ b/ffi/cmake/Utils/CxxWarning.cmake
@@ -1,7 +1,7 @@
function(add_cxx_warning target_name)
# GNU, Clang, or AppleClang
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
- target_compile_options(${target_name} PRIVATE "-Werror" "-Wall" "-Wextra" "-Wpedantic")
+ target_compile_options(${target_name} PRIVATE "-Werror" "-Wall" "-Wextra" "-Wpedantic" "-Wno-unused-parameter")
return()
endif()
# MSVC
diff --git a/ffi/include/tvm/ffi/c_api.h b/ffi/include/tvm/ffi/c_api.h
index c580410581..18c44e40da 100644
--- a/ffi/include/tvm/ffi/c_api.h
+++ b/ffi/include/tvm/ffi/c_api.h
@@ -51,27 +51,40 @@ enum TVMFFITypeIndex : int32_t {
#else
typedef enum {
#endif
- // [Section] On-stack POD Types: [0, kTVMFFIStaticObjectBegin)
+ // [Section] On-stack POD and special types: [0, kTVMFFIStaticObjectBegin)
// N.B. `kTVMFFIRawStr` is a string backed by a `\0`-terminated char array,
// which is not owned by TVMFFIAny. It is required that the following
// invariant holds:
// - `Any::type_index` is never `kTVMFFIRawStr`
// - `AnyView::type_index` can be `kTVMFFIRawStr`
+ //
+ // NOTE: kTVMFFIAny is a root type of everything
+ // we include it so TypeIndex captures all possible runtime values.
+ // `kTVMFFIAny` code will never appear in Any::type_index.
+ // However, it may appear in field annotations during reflection.
+ //
+ kTVMFFIAny = -1,
kTVMFFINone = 0,
kTVMFFIInt = 1,
- kTVMFFIFloat = 2,
- kTVMFFIOpaquePtr = 3,
- kTVMFFIDataType = 4,
- kTVMFFIDevice = 5,
- kTVMFFIRawStr = 6,
+ kTVMFFIBool = 2,
+ kTVMFFIFloat = 3,
+ kTVMFFIOpaquePtr = 4,
+ kTVMFFIDataType = 5,
+ kTVMFFIDevice = 6,
+ kTVMFFIDLTensorPtr = 7,
+ kTVMFFIRawStr = 8,
// [Section] Static Boxed: [kTVMFFIStaticObjectBegin, kTVMFFIDynObjectBegin)
+ // roughly order in terms of their ptential dependencies
kTVMFFIStaticObjectBegin = 64,
kTVMFFIObject = 64,
- kTVMFFIArray = 65,
- kTVMFFIMap = 66,
- kTVMFFIError = 67,
- kTVMFFIFunc = 68,
- kTVMFFIStr = 69,
+ kTVMFFIStr = 65,
+ kTVMFFIError = 66,
+ kTVMFFIFunc = 67,
+ kTVMFFIArray = 68,
+ kTVMFFIMap = 69,
+ kTVMFFIShapeTuple = 70,
+ kTVMFFINDArray = 71,
+ kTVMFFIRuntimeModule = 72,
// [Section] Dynamic Boxed: [kTVMFFIDynObjectBegin, +oo)
// kTVMFFIDynObject is used to indicate that the type index
// is dynamic and needs to be looked up at runtime
@@ -113,7 +126,10 @@ typedef struct TVMFFIAny {
* \note The type index of Object and Any are shared in FFI.
*/
int32_t type_index;
- /*! \brief length for on-stack Any object, such as small-string */
+ /*!
+ * \brief length for on-stack Any object, such as small-string
+ * \note This field is reserved for future compact.
+ */
int32_t small_len;
union { // 8 bytes
int64_t v_int64; // integers
@@ -134,6 +150,99 @@ typedef struct {
const char* bytes;
} TVMFFIByteArray;
+/*!
+ * \brief Type that defines C-style safe call convention
+ *
+ * Safe call explicitly catches exception on function boundary.
+ *
+ * \param self The function handle
+ * \param num_args Number if input arguments
+ * \param args The input arguments to the call.
+ * \param result Store output result
+ *
+ * \return The call return 0 if call is successful.
+ * It returns non-zero value if there is an error.
+ *
+ * Possible return error of the API functions:
+ * * 0: success
+ * * -1: error happens, can be retrieved by TVMFFIGetLastError
+ * * -2: a frontend error occurred and recorded in the frontend.
+ *
+ * \note We decided to leverage TVMFFIGetLastError and TVMFFISetLastError
+ * for C function error propagation. This design choice, while
+ * introducing a dependency for TLS runtime, simplifies error
+ * propgation in chains of calls in compiler codegen.
+ * As we do not need to propagate error through argument but simply
+ * set them in the runtime environment.
+ */
+typedef int (*TVMFFISafeCallType)(void* self, int32_t num_args, const TVMFFIAny* args,
+ TVMFFIAny* result);
+
+/*!
+ * \brief Getter that can take address of a field and set the result.
+ * \param field The raw address of the field.
+ * \param result Stores the result.
+ */
+typedef int (*TVMFFIFieldGetter)(void* field, TVMFFIAny* result);
+
+/*!
+ * \brief Getter that can take address of a field and set to value.
+ * \param field The raw address of the field.
+ * \param value The value to set.
+ */
+typedef int (*TVMFFIFieldSetter)(void* field, const TVMFFIAny* value);
+
+/*!
+ * \brief Information support for optional object reflection.
+ */
+typedef struct {
+ /*! \brief The name of the field. */
+ const char* name;
+ /*!
+ * \brief Records the static type kind of the field.
+ *
+ * Possible values:
+ *
+ * - TVMFFITypeIndex::kTVMFFIObject for general objects
+ * - The value is nullable when kTVMFFIObject is chosen
+ * - static object type kinds such as Map, Dict, String
+ * - POD type index
+ * - TVMFFITypeIndex::kTVMFFIAny if we don't have specialized info
+ * about the field.
+ *
+ * \note This information is helpful in designing serializer
+ * of the field. As it helps to narrow down the type of the
+ * object. It also helps to provide opportunities to enable
+ * short-cut access to the field.
+ */
+ int32_t field_static_type_index;
+ /*!
+ * \brief Mark whether field is readonly.
+ */
+ int32_t readonly;
+ /*!
+ * \brief Byte offset of the field.
+ */
+ int64_t byte_offset;
+ /*! \brief The getter to access the field. */
+ TVMFFIFieldGetter getter;
+ /*! \brief The setter to access the field. */
+ TVMFFIFieldSetter setter;
+} TVMFFIFieldInfo;
+
+/*!
+ * \brief Method information that can appear in reflection table.
+ */
+typedef struct {
+ /*! \brief The name of the field. */
+ const char* name;
+ /*!
+ * \brief The method wrapped as Function
+ * \note The first argument to the method is always the self.
+ */
+ TVMFFIObjectHandle method;
+} TVMFFIMethodInfo;
+
/*!
* \brief Runtime type information for object type checking.
*/
@@ -155,38 +264,27 @@ typedef struct {
* hieracy stays as a tree
*/
const int32_t* type_acenstors;
+ /*! \brief number of reflection accessible fields. */
+ int32_t num_fields;
+ /*! \brief number of reflection acccesible methods. */
+ int32_t num_methods;
+ /*! \brief The reflection field information. */
+ TVMFFIFieldInfo* fields;
+ /*! \brief The reflection method. */
+ TVMFFIMethodInfo* methods;
} TVMFFITypeInfo;
//------------------------------------------------------------
// Section: User APIs to interact with the FFI
//------------------------------------------------------------
/*!
- * \brief Type that defines C-style safe call convention
- *
- * Safe call explicitly catches exception on function boundary.
- *
- * \param func The function handle
- * \param num_args Number if input arguments
- * \param args The input arguments to the call.
- * \param result Store output result
- *
- * \return The call return 0 if call is successful.
- * It returns non-zero value if there is an error.
- *
- * Possible return error of the API functions:
- * * 0: success
- * * -1: error happens, can be retrieved by TVMFFIGetLastError
- * * -2: a frontend error occurred and recorded in the frontend.
- *
- * \note We decided to leverage TVMFFIGetLastError and TVMFFISetLastError
- * for C function error propagation. This design choice, while
- * introducing a dependency for TLS runtime, simplifies error
- * propgation in chains of calls in compiler codegen.
- * As we do not need to propagate error through argument but simply
- * set them in the runtime environment.
+ * \brief Free an object handle by decreasing reference
+ * \param obj The object handle.
+ * \note Internally we decrease the reference counter of the object.
+ * The object will be freed when every reference to the object are removed.
+ * \return 0 when success, nonzero when failure happens
*/
-typedef int (*TVMFFISafeCallType)(void* func, int32_t num_args, const TVMFFIAny* args,
- TVMFFIAny* result);
+TVM_FFI_DLL int TVMFFIObjectFree(TVMFFIObjectHandle obj);
/*!
* \brief Create a FFIFunc by passing in callbacks from C callback.
@@ -223,15 +321,6 @@ TVM_FFI_DLL int TVMFFIFuncSetGlobal(const char* name, TVMFFIObjectHandle f, int
*/
TVM_FFI_DLL int TVMFFIFuncGetGlobal(const char* name, TVMFFIObjectHandle* out);
-/*!
- * \brief Free an object handle by decreasing reference
- * \param obj The object handle.
- * \note Internally we decrease the reference counter of the object.
- * The object will be freed when every reference to the object are removed.
- * \return 0 when success, nonzero when failure happens
- */
-TVM_FFI_DLL int TVMFFIObjectFree(TVMFFIObjectHandle obj);
-
/*!
* \brief Move the last error from the environment to result.
*
@@ -249,6 +338,30 @@ TVM_FFI_DLL void TVMFFIMoveFromLastError(TVMFFIAny* result);
*/
TVM_FFI_DLL void TVMFFISetLastError(const TVMFFIAny* error_view);
+/*!
+ * \brief Convert type key to type index.
+ * \param type_key The key of the type.
+ * \param out_tindex the corresponding type index.
+ * \return 0 when success, nonzero when failure happens
+ */
+TVM_FFI_DLL int TVMFFITypeKey2Index(const char* type_key, int32_t* out_tindex);
+
+/*!
+ * \brief Register type field information for rutnime reflection.
+ * \param type_index The type index
+ * \param info The field info to be registered.
+ * \return 0 when success, nonzero when failure happens
+ */
+TVM_FFI_DLL int TVMFFIRegisterTypeField(int32_t type_index, const TVMFFIFieldInfo* info);
+
+/*!
+ * \brief Register type method information for rutnime reflection.
+ * \param type_index The type index
+ * \param info The method info to be registered.
+ * \return 0 when success, nonzero when failure happens
+ */
+TVM_FFI_DLL int TVMFFIRegisterTypeMethod(int32_t type_index, const TVMFFIMethodInfo* info);
+
//------------------------------------------------------------
// Section: Backend noexcept functions for internal use
//
@@ -292,13 +405,13 @@ TVM_FFI_DLL int32_t TVMFFIGetOrAllocTypeIndex(const char* type_key, int32_t stat
int32_t type_depth, int32_t num_child_slots,
int32_t child_slots_can_overflow,
int32_t parent_type_index);
+
/*!
* \brief Get dynamic type info by type index.
*
* \param type_index The type index
* \param result The output type information
- *
- * \return 0 when success, nonzero when failure happens
+ * \return The type info
*/
TVM_FFI_DLL const TVMFFITypeInfo* TVMFFIGetTypeInfo(int32_t type_index);
diff --git a/ffi/include/tvm/ffi/function_details.h b/ffi/include/tvm/ffi/function_details.h
index 56b7ca7df3..0f6701e509 100644
--- a/ffi/include/tvm/ffi/function_details.h
+++ b/ffi/include/tvm/ffi/function_details.h
@@ -161,7 +161,7 @@ class MovableArgValueWithContext {
template <typename Type>
TVM_FFI_INLINE operator Type() {
using TypeWithoutCR = std::remove_const_t<std::remove_reference_t<Type>>;
- std::optional<TypeWithoutCR> opt = TryAs<TypeWithoutCR>(args_[arg_index_]);
+ std::optional<TypeWithoutCR> opt = TryAs<TypeWithoutCR>(args_[arg_index_]);
if (opt.has_value()) {
return std::move(*opt);
}
@@ -210,8 +210,8 @@ struct unpack_call_dispatcher<R, 0, index, F> {
template <int index, typename F>
struct unpack_call_dispatcher<void, 0, index, F> {
template <typename... Args>
- TVM_FFI_INLINE static void run(const std::string* optional_name, FGetFuncSignature f_sig,
- const F& f, int32_t num_args, const AnyView* args, Any* rv,
+ TVM_FFI_INLINE static void run(const std::string*, FGetFuncSignature ,
+ const F& , int32_t , const AnyView* , Any* ,
Args&&... unpacked_args) {
f(std::forward<Args>(unpacked_args)...);
}
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 6a202ba1f5..70b68f6fc6 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -39,7 +39,7 @@ using TypeInfo = TVMFFITypeInfo;
namespace details {
// Helper to perform
// unsafe operations related to object
-struct ObjectUnsafe;
+class ObjectUnsafe;
/*!
* Check if the type_index is an instance of TargetObjectType.
@@ -445,32 +445,6 @@ inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
static int32_t RuntimeTypeIndex() { return TypeName::_type_index; } \
TVM_FFI_OBJECT_STATIC_DEFS(TypeName, ParentType)
-/*!
- * \brief helper macro to declare a base object type that can be inherited.
- * \param TypeName The name of the current type.
- * \param ParentType The name of the ParentType
- */
-#define TVM_FFI_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType) \
- TVM_FFI_OBJECT_STATIC_DEFS(TypeName, ParentType); \
- static int32_t _GetOrAllocRuntimeTypeIndex() { \
- static int32_t tindex = TVMFFIGetOrAllocTypeIndex( \
- TypeName::_type_key, -1, TypeName::_type_depth, TypeName::_type_child_slots, \
- TypeName::_type_child_slots_can_overflow, ParentType::_GetOrAllocRuntimeTypeIndex()); \
- return tindex; \
- } \
- static int32_t RuntimeTypeIndex() { return _GetOrAllocRuntimeTypeIndex(); } \
- static inline int32_t _type_index = _GetOrAllocRuntimeTypeIndex()
-
-/*!
- * \brief helper macro to declare type information in a final class.
- * \param TypeName The name of the current type.
- * \param ParentType The name of the ParentType
- */
-#define TVM_FFI_DECLARE_FINAL_OBJECT_INFO(TypeName, ParentType) \
- static const constexpr int _type_child_slots = 0; \
- static const constexpr bool _type_final = true; \
- TVM_FFI_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType)
-
/*
* \brief Define object reference methods.
* \param TypeName The object type name
@@ -539,7 +513,8 @@ TVM_FFI_INLINE bool IsObjectInstance(int32_t object_type_index) {
* \note These functions are only supposed to be used by internal
* implementations and not external users of the tvm::ffi
*/
-struct ObjectUnsafe {
+class ObjectUnsafe {
+ public:
// NOTE: get ffi header from an object
static TVM_FFI_INLINE TVMFFIObject* GetHeader(const Object* src) {
return const_cast<TVMFFIObject*>(&(src->header_));
diff --git a/ffi/include/tvm/ffi/reflection.h b/ffi/include/tvm/ffi/reflection.h
new file mode 100644
index 0000000000..51573d1bc2
--- /dev/null
+++ b/ffi/include/tvm/ffi/reflection.h
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+/*!
+ * \file tvm/ffi/reflection.h
+ * \brief Base reflection support to access object fields.
+ */
+#ifndef TVM_FFI_REFLECTION_H_
+#define TVM_FFI_REFLECTION_H_
+
+#include <tvm/ffi/any.h>
+#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/function.h>
+#include <tvm/ffi/type_traits.h>
+
+namespace tvm {
+namespace ffi {
+namespace details {
+
+template <typename T, typename = void>
+struct Type2FieldStaticTypeIndex {
+ static constexpr int32_t value = TypeIndex::kTVMFFIAny;
+};
+
+template <typename T>
+struct Type2FieldStaticTypeIndex<T, std::enable_if_t<TypeTraits<T>::enabled>> {
+ static constexpr int32_t value = TypeTraits<T>::field_static_type_index;
+};
+
+/*!
+ * \brief Get the byte offset of a class member field.
+ *
+ * \tparam The original class.
+ * \tparam T the field type.
+ *
+ * \param field_ptr A class member pointer
+ * \returns The byteoffset
+ */
+template <typename Class, typename T>
+inline int64_t GetFieldByteOffset(T Class::*field_ptr) {
+ return reinterpret_cast<int64_t>(&(static_cast<Class*>(nullptr)->*field_ptr));
+}
+
+class ReflectionDef {
+ public:
+ explicit ReflectionDef(int32_t type_index) : type_index_(type_index) {}
+
+ template <typename Class, typename T>
+ ReflectionDef& def_readonly(const char* name, T Class::*field_ptr) {
+ RegisterField(name, field_ptr, true);
+ return *this;
+ }
+
+ template <typename Class, typename T>
+ ReflectionDef& def_readwrite(const char* name, T Class::*field_ptr) {
+ RegisterField(name, field_ptr, false);
+ return *this;
+ }
+
+ operator int32_t() const { return type_index_; }
+
+ private:
+ template <typename Class, typename T>
+ void RegisterField(const char* name, T Class::*field_ptr, bool readonly) {
+ TVMFFIFieldInfo info;
+ info.name = name;
+ info.field_static_type_index = Type2FieldStaticTypeIndex<T>::value;
+ // store byte offset and setter, getter
+ // so the same setter can be reused for all the same type
+ info.byte_offset = GetFieldByteOffset<Class, T>(field_ptr);
+ info.readonly = readonly;
+ info.getter = FieldGetter<T>;
+ info.setter = FieldSetter<T>;
+ TVM_FFI_CHECK_SAFE_CALL(TVMFFIRegisterTypeField(type_index_, &info));
+ }
+
+ template <typename T>
+ static int FieldGetter(void* field, TVMFFIAny* result) {
+ TVM_FFI_SAFE_CALL_BEGIN();
+ Any(*reinterpret_cast<T*>(field)).MoveToTVMFFIAny(result);
+ TVM_FFI_SAFE_CALL_END();
+ }
+
+ template <typename T>
+ static int FieldSetter(void* field, const TVMFFIAny* value) {
+ TVM_FFI_SAFE_CALL_BEGIN();
+ *reinterpret_cast<T*>(field) = AnyView::CopyFromTVMFFIAny(*value);
+ TVM_FFI_SAFE_CALL_END();
+ }
+
+ int32_t type_index_;
+};
+
+/*!
+ * \brief helper function to get reflection field info by type key and field name
+ */
+inline const TVMFFIFieldInfo* GetReflectionFieldInfo(const char* type_key, const char* field_name) {
+ int32_t type_index;
+ TVM_FFI_CHECK_SAFE_CALL(TVMFFITypeKey2Index(type_key, &type_index));
+ const TypeInfo* info = TVMFFIGetTypeInfo(type_index);
+ for (int32_t i = 0; i < info->num_fields; ++i) {
+ if (std::strcmp(info->fields[i].name, field_name) == 0) {
+ return &(info->fields[i]);
+ }
+ }
+ TVM_FFI_THROW(RuntimeError) << "Cannot find field " << field_name << " in " << type_key;
+}
+
+/*!
+ * \brief helper wrapper class to obtain a getter.
+ */
+class ReflectionFieldGetter {
+ public:
+ explicit ReflectionFieldGetter(const TVMFFIFieldInfo* field_info) : field_info_(field_info) {
+ }
+
+ Any operator()(const Object* obj_ptr) const {
+ Any result;
+ const void* addr = reinterpret_cast<const char*>(obj_ptr) + field_info_->byte_offset;
+ TVM_FFI_CHECK_SAFE_CALL(field_info_->getter(const_cast<void*>(addr), reinterpret_cast<TVMFFIAny*>(&result)));
+ return result;
+ }
+
+ Any operator()(const ObjectPtr<Object>& obj_ptr) const {
+ return operator()(obj_ptr.get());
+ }
+
+ Any operator()(const ObjectRef& obj) const {
+ return operator()(obj.get());
+ }
+
+ private:
+ const TVMFFIFieldInfo* field_info_;
+};
+
+} // namespace details
+
+/*!
+ * \brief helper macro to declare a base object type that can be inherited.
+ * \param TypeName The name of the current type.
+ * \param ParentType The name of the ParentType
+ */
+#define TVM_FFI_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType) \
+ TVM_FFI_OBJECT_STATIC_DEFS(TypeName, ParentType); \
+ static int32_t _GetOrAllocRuntimeTypeIndex() { \
+ static int32_t tindex = TVMFFIGetOrAllocTypeIndex( \
+ TypeName::_type_key, -1, TypeName::_type_depth, TypeName::_type_child_slots, \
+ TypeName::_type_child_slots_can_overflow, ParentType::_GetOrAllocRuntimeTypeIndex()); \
+ return tindex; \
+ } \
+ static int32_t RuntimeTypeIndex() { return _GetOrAllocRuntimeTypeIndex(); } \
+ static inline int32_t _type_index = \
+ ::tvm::ffi::details::ReflectionDef(_GetOrAllocRuntimeTypeIndex())
+
+/*!
+ * \brief helper macro to declare type information in a final class.
+ * \param TypeName The name of the current type.
+ * \param ParentType The name of the ParentType
+ */
+#define TVM_FFI_DECLARE_FINAL_OBJECT_INFO(TypeName, ParentType) \
+ static const constexpr int _type_child_slots = 0; \
+ static const constexpr bool _type_final = true; \
+ TVM_FFI_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType)
+
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_REFLECTION_H_
diff --git a/ffi/include/tvm/ffi/type_traits.h b/ffi/include/tvm/ffi/type_traits.h
index a46d04ad83..cf88eb7319 100644
--- a/ffi/include/tvm/ffi/type_traits.h
+++ b/ffi/include/tvm/ffi/type_traits.h
@@ -107,6 +107,8 @@ struct TypeTraitsBase {
// None
template <>
struct TypeTraits<std::nullptr_t> : public TypeTraitsBase {
+ static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFINone;
+
static TVM_FFI_INLINE void CopyToAnyView(const std::nullptr_t&, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFINone;
// invariant: the pointer field also equals nullptr
@@ -142,6 +144,8 @@ struct TypeTraits<std::nullptr_t> : public TypeTraitsBase {
// Integer POD values
template <typename Int>
struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> : public TypeTraitsBase {
+ static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIInt;
+
static TVM_FFI_INLINE void CopyToAnyView(const Int& src, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFIInt;
result->v_int64 = static_cast<int64_t>(src);
@@ -171,6 +175,8 @@ struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> : public TypeT
template <typename Float>
struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>>
: public TypeTraitsBase {
+ static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIFloat;
+
static TVM_FFI_INLINE void CopyToAnyView(const Float& src, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFIFloat;
result->v_float64 = static_cast<double>(src);
@@ -205,6 +211,8 @@ struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>>
// void*
template <>
struct TypeTraits<void*> : public TypeTraitsBase {
+ static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIOpaquePtr;
+
static TVM_FFI_INLINE void CopyToAnyView(void* src, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFIOpaquePtr;
// maintain padding zero in 32bit platform
@@ -241,6 +249,7 @@ template <typename TObjRef>
struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef> &&
use_default_type_traits_v<TObjRef>>>
: public TypeTraitsBase {
+ static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIObject;
using ContainerType = typename TObjRef::ContainerType;
static TVM_FFI_INLINE void CopyToAnyView(const TObjRef& src, TVMFFIAny* result) {
diff --git a/ffi/src/ffi/object.cc b/ffi/src/ffi/object.cc
index 8941753df8..a0fe306e78 100644
--- a/ffi/src/ffi/object.cc
+++ b/ffi/src/ffi/object.cc
@@ -52,6 +52,8 @@ class TypeTable {
std::string type_key_data;
/*! \brief acenstor information */
std::vector<int32_t> type_acenstors_data;
+ /*! \brief type fields informaton */
+ std::vector<TVMFFIFieldInfo> type_fields_data;
// NOTE: the indices in [index, index + num_reserved_slots) are
// reserved for the child-class of this type.
/*! \brief Total number of slots reserved for the type and its children. */
@@ -87,6 +89,11 @@ class TypeTable {
this->type_key = this->type_key_data.c_str();
this->type_key_hash = std::hash<std::string>()(this->type_key_data);
this->type_acenstors = type_acenstors_data.data();
+ // initialize the reflection information
+ this->num_fields = 0;
+ this->num_methods = 0;
+ this->fields = nullptr;
+ this->methods = nullptr;
}
};
@@ -165,13 +172,23 @@ class TypeTable {
return it->second;
}
- const TypeInfo* GetTypeInfo(int32_t type_index) {
- const TypeInfo* info = nullptr;
+ Entry* GetTypeEntry(int32_t type_index) {
+ Entry* entry = nullptr;
if (type_index >= 0 && static_cast<size_t>(type_index) < type_table_.size()) {
- info = type_table_[type_index].get();
+ entry = type_table_[type_index].get();
}
- TVM_FFI_ICHECK(info != nullptr) << "Cannot find type info for type_index=" << type_index;
- return info;
+ TVM_FFI_ICHECK(entry != nullptr) << "Cannot find type info for type_index=" << type_index;
+ return entry;
+ }
+
+ void RegisterTypeField(int32_t type_index, const TVMFFIFieldInfo* info) {
+ Entry* entry = GetTypeEntry(type_index);
+ TVMFFIFieldInfo field_data = *info;
+ field_data.name = this->CopyString(info->name);
+ entry->type_fields_data.push_back(field_data);
+ // refresh ptr as the data can change
+ entry->fields = entry->type_fields_data.data();
+ entry->num_fields = static_cast<int32_t>(entry->type_fields_data.size());
}
void Dump(int min_children_count) {
@@ -217,9 +234,17 @@ class TypeTable {
-1);
}
+ const char* CopyString(const char* c_str) {
+ std::unique_ptr<std::string> val = std::make_unique<std::string>(c_str);
+ const char* c_val = val->c_str();
+ string_pool_.emplace_back(std::move(val));
+ return c_val;
+ }
+
int32_t type_counter_{TypeIndex::kTVMFFIDynObjectBegin};
std::vector<std::unique_ptr<Entry>> type_table_;
std::unordered_map<std::string, int32_t> type_key2index_;
+ std::vector<std::unique_ptr<std::string>> string_pool_;
};
} // namespace ffi
} // namespace tvm
@@ -230,6 +255,18 @@ int TVMFFIObjectFree(TVMFFIObjectHandle handle) {
TVM_FFI_SAFE_CALL_END();
}
+int TVMFFITypeKey2Index(const char* type_key, int32_t* out_tindex) {
+ TVM_FFI_SAFE_CALL_BEGIN();
+ out_tindex[0] = tvm::ffi::TypeTable::Global()->TypeKey2Index(type_key);
+ TVM_FFI_SAFE_CALL_END();
+}
+
+int TVMFFIRegisterTypeField(int32_t type_index, const TVMFFIFieldInfo* info) {
+ TVM_FFI_SAFE_CALL_BEGIN();
+ tvm::ffi::TypeTable::Global()->RegisterTypeField(type_index, info);
+ TVM_FFI_SAFE_CALL_END();
+}
+
int32_t TVMFFIGetOrAllocTypeIndex(const char* type_key, int32_t static_type_index,
int32_t type_depth, int32_t num_child_slots,
int32_t child_slots_can_overflow, int32_t parent_type_index) {
@@ -242,6 +279,6 @@ int32_t TVMFFIGetOrAllocTypeIndex(const char* type_key, int32_t static_type_inde
const TVMFFITypeInfo* TVMFFIGetTypeInfo(int32_t type_index) {
TVM_FFI_LOG_EXCEPTION_CALL_BEGIN();
- return tvm::ffi::TypeTable::Global()->GetTypeInfo(type_index);
+ return tvm::ffi::TypeTable::Global()->GetTypeEntry(type_index);
TVM_FFI_LOG_EXCEPTION_CALL_END(TVMFFIGetTypeInfo);
}
diff --git a/ffi/tests/cpp/test_reflection.cc b/ffi/tests/cpp/test_reflection.cc
new file mode 100644
index 0000000000..c7901a4ca3
--- /dev/null
+++ b/ffi/tests/cpp/test_reflection.cc
@@ -0,0 +1,49 @@
+
+/*
+ * 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.
+ */
+#include <gtest/gtest.h>
+#include <tvm/ffi/object.h>
+#include <tvm/ffi/reflection.h>
+#include "./testing_object.h"
+
+namespace {
+
+using namespace tvm::ffi;
+using namespace tvm::ffi::testing;
+
+struct A {
+ ObjectRef obj;
+ int32_t x;
+ int32_t y;
+};
+
+TEST(Reflection, GetFieldByteOffset) {
+ EXPECT_EQ(details::GetFieldByteOffset(&A::x), 8);
+ EXPECT_EQ(details::GetFieldByteOffset(&A::y), 12);
+}
+
+
+TEST(Reflection, FieldGetter) {
+ ObjectRef a = TInt(10);
+ details::ReflectionFieldGetter getter(
+ details::GetReflectionFieldInfo("test.Int", "value")
+ );
+ EXPECT_EQ(getter(a).operator int(), 10);
+}
+} // namespace
diff --git a/ffi/tests/cpp/testing_object.h b/ffi/tests/cpp/testing_object.h
index e660b2751e..a4d6b1353f 100644
--- a/ffi/tests/cpp/testing_object.h
+++ b/ffi/tests/cpp/testing_object.h
@@ -19,8 +19,10 @@
#ifndef TVM_FFI_TESTING_OBJECT_H_
#define TVM_FFI_TESTING_OBJECT_H_
+
#include <tvm/ffi/memory.h>
#include <tvm/ffi/object.h>
+#include <tvm/ffi/reflection.h>
namespace tvm {
namespace ffi {
@@ -46,7 +48,8 @@ class TIntObj : public TNumberObj {
TIntObj(int64_t value) : value(value) {}
static constexpr const char* _type_key = "test.Int";
- TVM_FFI_DECLARE_FINAL_OBJECT_INFO(TIntObj, TNumberObj);
+
+ TVM_FFI_DECLARE_FINAL_OBJECT_INFO(TIntObj, TNumberObj).def_readonly("value", &TIntObj::value);
};
class TInt : public TNumber {
(tvm) 02/38: [FFI] Initial step to create ffi from the original tvm source
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit af35e25d38627c192e92a427e147eeae3c76c8c2
Author: tqchen <ti...@gmail.com>
AuthorDate: Tue Aug 6 13:16:38 2024 -0400
[FFI] Initial step to create ffi from the original tvm source
---
ffi/CMakeLists.txt | 2 +-
ffi/include | 1 -
ffi/include/tvm/ffi/c_ffi_abi.h | 134 +++++++++++++++++++++
ffi/include/tvm/ffi/internal_utils.h | 77 ++++++++++++
ffi/include/tvm/ffi/memory.h | 0
ffi/include/tvm/ffi/object.h | 223 +++++++++++++++++++++++++++++++++++
ffi/src | 1 -
ffi/src/ffi/registry.cc | 1 +
ffi/tests/example/CMakeLists.txt | 21 ++++
ffi/tests/example/test_c_ffi_abi.cc | 12 ++
ffi/tests/example/test_object.cc | 12 ++
11 files changed, 481 insertions(+), 3 deletions(-)
diff --git a/ffi/CMakeLists.txt b/ffi/CMakeLists.txt
index 8710a0c377..532922bf14 100644
--- a/ffi/CMakeLists.txt
+++ b/ffi/CMakeLists.txt
@@ -73,6 +73,6 @@ if (${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME})
enable_testing()
message(STATUS "Enable Testing")
include(cmake/Utils/AddGoogleTest.cmake)
- add_subdirectory(tests/cpp/)
+ add_subdirectory(tests/example/)
endif()
endif ()
diff --git a/ffi/include b/ffi/include
deleted file mode 120000
index f5030fe889..0000000000
--- a/ffi/include
+++ /dev/null
@@ -1 +0,0 @@
-../include
\ No newline at end of file
diff --git a/ffi/include/tvm/ffi/c_ffi_abi.h b/ffi/include/tvm/ffi/c_ffi_abi.h
new file mode 100644
index 0000000000..ccba3f58df
--- /dev/null
+++ b/ffi/include/tvm/ffi/c_ffi_abi.h
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+/*
+ * \file tvm/ffi/c_ffi_abi.h
+ * \brief This file defines
+ */
+#ifndef TVM_FFI_C_FFI_ABI_H_
+#define TVM_FFI_C_FFI_ABI_H_
+
+#include <dlpack/dlpack.h>
+#include <stdint.h>
+
+#if !defined(TVM_FFI_DLL) && defined(__EMSCRIPTEN__)
+#include <emscripten/emscripten.h>
+#define TVM_FFI_API EMSCRIPTEN_KEEPALIVE
+#endif
+#if !defined(TVM_FFI_DLL) && defined(_MSC_VER)
+#ifdef TVM_FFI_EXPORTS
+#define TVM_FFI_DLL __declspec(dllexport)
+#else
+#define TVM_FFI_DLL __declspec(dllimport)
+#endif
+#endif
+#ifndef TVM_FFI_DLL
+#define TVM_FFI_DLL __attribute__((visibility("default")))
+#endif
+
+#ifndef TVM_FFI_ALLOW_DYN_TYPE
+#define TVM_FFI_ALLOW_DYN_TYPE 0
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+enum class TVMFFITypeIndex : int32_t {
+#else
+typedef enum {
+#endif
+ // [Section] On-stack POD Types: [0, kTVMFFIStaticObjectBegin)
+ // N.B. `kTVMFFIRawStr` is a string backed by a `\0`-terminated char array,
+ // which is not owned by TVMFFIAny. It is required that the following
+ // invariant holds:
+ // - `Any::type_index` is never `kTVMFFIRawStr`
+ // - `AnyView::type_index` can be `kTVMFFIRawStr`
+ kTVMFFINone = 0,
+ kTVMFFIInt = 1,
+ kTVMFFIFloat = 2,
+ kTVMFFIOpaquePtr = 3,
+ kTVMFFIDataType = 4,
+ kTVMFFIDevice = 5,
+ kTVMFFIRawStr = 6,
+ // [Section] Static Boxed: [kTVMFFIStaticObjectBegin, kTVMFFIDynObjectBegin)
+ kTVMFFIStaticObjectBegin = 64,
+ kTVMFFIObject = 64,
+ kTVMFFIList = 65,
+ kTVMFFIDict = 66,
+ kTVMFFIError = 67,
+ kTVMFFIFunc = 68,
+ kTVMFFIStr = 69,
+ // [Section] Dynamic Boxed: [kTVMFFIDynObjectBegin, +oo)
+ kTVMFFIDynObjectBegin = 128,
+#ifdef __cplusplus
+};
+#else
+} TVMFFITypeIndex;
+#endif
+
+/*!
+ * \brief C-based type of all FFI object types that allocates on heap.
+ * \note TVMFFIObject and TVMFFIAny share the common type_index_ header
+ */
+typedef struct TVMFFIObject {
+ /*!
+ * \brief type index of the object.
+ * \note The type index of Object and Any are shared in FFI.
+ */
+ int32_t type_index;
+ /*! \brief Reference counter of the object. */
+ int32_t ref_counter;
+ /*! \brief Deleter to be invoked when reference counter goes to zero. */
+ void (*deleter)(struct TVMFFIObject* self);
+} TVMFFIObject;
+
+/*!
+ * \brief C-based type of all on stack Any value.
+ *
+ * Any value can hold on stack values like int,
+ * as well as reference counted pointers to object.
+ */
+typedef struct TVMFFIAny {
+ /*!
+ * \brief type index of the object.
+ * \note The type index of Object and Any are shared in FFI.
+ */
+ int32_t type_index;
+ /*! \brief length for on-stack Any object, such as small-string */
+ int32_t small_len;
+ union { // 8 bytes
+ int64_t v_int64; // integers
+ double v_float64; // floating-point numbers
+ void* v_ptr; // typeless pointers
+ const char* v_c_str; // raw C-string
+ TVMFFIObject* v_obj; // ref counted objects
+ DLDataType v_dtype; // data type
+ DLDevice v_device; // device
+ char v_bytes[8]; // small string
+ char32_t v_char32[2]; // small UCS4 string and Unicode
+ };
+} TVMFFIAny;
+
+#ifdef __cplusplus
+} // TVM_FFI_EXTERN_C
+#endif
+
+#endif // TVM_FFI_C_FFI_ABI_H_
diff --git a/ffi/include/tvm/ffi/internal_utils.h b/ffi/include/tvm/ffi/internal_utils.h
new file mode 100644
index 0000000000..53d08c364c
--- /dev/null
+++ b/ffi/include/tvm/ffi/internal_utils.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+/*!
+ * \file tvm/ffi/internal_utils.h
+ * \brief Utility functions and macros for internal use, not meant for
+ */
+#ifndef TVM_FFI_INTERNAL_UTILS_H_
+#define TVM_FFI_INTERNAL_UTILS_H_
+
+#include <tvm/ffi/c_ffi_abi.h>
+
+#include <cstddef>
+
+#if defined(_MSC_VER)
+#define TVM_FFI_INLINE __forceinline
+#else
+#define TVM_FFI_INLINE inline __attribute__((always_inline))
+#endif
+
+#if defined(_MSC_VER)
+#define TVM_FFI_UNREACHABLE() __assume(false)
+#else
+#define TVM_FFI_UNREACHABLE() __builtin_unreachable()
+#endif
+
+namespace tvm {
+namespace ffi {
+
+namespace details {
+
+/********** Atomic Operations *********/
+
+TVM_FFI_INLINE int32_t AtomicIncrementRelaxed(int32_t* ptr) {
+#ifdef _MSC_VER
+ return _InterlockedIncrement(reinterpret_cast<volatile long*>(ptr)) - 1;
+#else
+ return __atomic_fetch_add(ptr, 1, __ATOMIC_RELAXED);
+#endif
+}
+
+TVM_FFI_INLINE int32_t AtomicDecrementRelAcq(int32_t* ptr) {
+#ifdef _MSC_VER
+ return _InterlockedDecrement(reinterpret_cast<volatile long*>(ptr)) + 1;
+#else
+ return __atomic_fetch_sub(ptr, 1, __ATOMIC_ACQ_REL);
+#endif
+}
+
+TVM_FFI_INLINE int32_t AtomicLoadRelaxed(const int32_t* ptr) {
+ int32_t* raw_ptr = const_cast<int32_t*>(ptr);
+#ifdef _MSC_VER
+ // simply load the variable ptr out
+ return (reinterpret_cast<const volatile long*>(raw_ptr))[0];
+#else
+ return __atomic_load_n(raw_ptr, __ATOMIC_RELAXED);
+#endif
+}
+} // namespace details
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_INTERNAL_UTILS_H_
diff --git a/ffi/include/tvm/ffi/memory.h b/ffi/include/tvm/ffi/memory.h
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
new file mode 100644
index 0000000000..90e78defae
--- /dev/null
+++ b/ffi/include/tvm/ffi/object.h
@@ -0,0 +1,223 @@
+/*
+ * 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.
+ */
+/*!
+ * \file tvm/ffi/object.h
+ * \brief A managed object in the TVM FFI.
+ */
+#ifndef TVM_FFI_OBJECT_H_
+#define TVM_FFI_OBJECT_H_
+
+#include <tvm/ffi/c_ffi_abi.h>
+#include <tvm/ffi/internal_utils.h>
+
+namespace tvm {
+namespace ffi {
+
+namespace details {
+// forward declare object internal
+struct ObjectInternal;
+} // namespace details
+
+class Object : protected TVMFFIObject {
+ public:
+ Object() {
+ TVMFFIObject::ref_counter = 0;
+ TVMFFIObject::deleter = nullptr;
+ }
+
+ private:
+ /*! \brief decreas*/
+ void IncRef() { details::AtomicIncrementRelaxed(&(this->ref_counter)); }
+
+ void DecRef() {
+ if (details::AtomicDecrementRelAcq(&(this->ref_counter)) == 1) {
+ if (this->deleter != nullptr) {
+ this->deleter(this);
+ }
+ }
+ }
+
+ /*!
+ * \return The usage count of the cell.
+ * \note We use stl style naming to be consistent with known API in shared_ptr.
+ */
+ int32_t use_count() const { return details::AtomicLoadRelaxed(&(this->ref_counter)); }
+
+ // friend classes
+ template <typename>
+ friend class ObjectPtr;
+ friend class tvm::ffi::details::ObjectInternal;
+};
+
+/*!
+ * \brief A custom smart pointer for Object.
+ * \tparam T the content data type.
+ * \sa make_object
+ */
+template <typename T>
+class ObjectPtr {
+ public:
+ /*! \brief default constructor */
+ ObjectPtr() {}
+ /*! \brief default constructor */
+ ObjectPtr(std::nullptr_t) {} // NOLINT(*)
+ /*!
+ * \brief copy constructor
+ * \param other The value to be moved
+ */
+ ObjectPtr(const ObjectPtr<T>& other) // NOLINT(*)
+ : ObjectPtr(other.data_) {}
+ /*!
+ * \brief copy constructor
+ * \param other The value to be moved
+ */
+ template <typename U>
+ ObjectPtr(const ObjectPtr<U>& other) // NOLINT(*)
+ : ObjectPtr(other.data_) {
+ static_assert(std::is_base_of<T, U>::value,
+ "can only assign of child class ObjectPtr to parent");
+ }
+ /*!
+ * \brief move constructor
+ * \param other The value to be moved
+ */
+ ObjectPtr(ObjectPtr<T>&& other) // NOLINT(*)
+ : data_(other.data_) {
+ other.data_ = nullptr;
+ }
+ /*!
+ * \brief move constructor
+ * \param other The value to be moved
+ */
+ template <typename Y>
+ ObjectPtr(ObjectPtr<Y>&& other) // NOLINT(*)
+ : data_(other.data_) {
+ static_assert(std::is_base_of<T, Y>::value,
+ "can only assign of child class ObjectPtr to parent");
+ other.data_ = nullptr;
+ }
+ /*! \brief destructor */
+ ~ObjectPtr() { this->reset(); }
+ /*!
+ * \brief Swap this array with another Object
+ * \param other The other Object
+ */
+ void swap(ObjectPtr<T>& other) { // NOLINT(*)
+ std::swap(data_, other.data_);
+ }
+ /*!
+ * \return Get the content of the pointer
+ */
+ T* get() const { return static_cast<T*>(data_); }
+ /*!
+ * \return The pointer
+ */
+ T* operator->() const { return get(); }
+ /*!
+ * \return The reference
+ */
+ T& operator*() const { // NOLINT(*)
+ return *get();
+ }
+ /*!
+ * \brief copy assignment
+ * \param other The value to be assigned.
+ * \return reference to self.
+ */
+ ObjectPtr<T>& operator=(const ObjectPtr<T>& other) { // NOLINT(*)
+ // takes in plane operator to enable copy elison.
+ // copy-and-swap idiom
+ ObjectPtr(other).swap(*this); // NOLINT(*)
+ return *this;
+ }
+ /*!
+ * \brief move assignment
+ * \param other The value to be assigned.
+ * \return reference to self.
+ */
+ ObjectPtr<T>& operator=(ObjectPtr<T>&& other) { // NOLINT(*)
+ // copy-and-swap idiom
+ ObjectPtr(std::move(other)).swap(*this); // NOLINT(*)
+ return *this;
+ }
+ /*!
+ * \brief nullptr check
+ * \return result of comparison of internal pointer with nullptr.
+ */
+ explicit operator bool() const { return get() != nullptr; }
+ /*! \brief reset the content of ptr to be nullptr */
+ void reset() {
+ if (data_ != nullptr) {
+ data_->DecRef();
+ data_ = nullptr;
+ }
+ }
+ /*! \return The use count of the ptr, for debug purposes */
+ int use_count() const { return data_ != nullptr ? data_->use_count() : 0; }
+ /*! \return whether the reference is unique */
+ bool unique() const { return data_ != nullptr && data_->use_count() == 1; }
+ /*! \return Whether two ObjectPtr do not equal each other */
+ bool operator==(const ObjectPtr<T>& other) const { return data_ == other.data_; }
+ /*! \return Whether two ObjectPtr equals each other */
+ bool operator!=(const ObjectPtr<T>& other) const { return data_ != other.data_; }
+ /*! \return Whether the pointer is nullptr */
+ bool operator==(std::nullptr_t null) const { return data_ == nullptr; }
+ /*! \return Whether the pointer is not nullptr */
+ bool operator!=(std::nullptr_t null) const { return data_ != nullptr; }
+
+ private:
+ /*! \brief internal pointer field */
+ Object* data_{nullptr};
+ /*!
+ * \brief constructor from Object
+ * \param data The data pointer
+ */
+ explicit ObjectPtr(Object* data) : data_(data) {
+ if (data != nullptr) {
+ data_->IncRef();
+ }
+ }
+ /*!
+ * \brief Move an ObjectPtr from an RValueRef argument.
+ * \param ref The rvalue reference.
+ * \return the moved result.
+ */
+ static ObjectPtr<T> MoveFromRValueRefArg(Object** ref) {
+ ObjectPtr<T> ptr;
+ ptr.data_ = *ref;
+ *ref = nullptr;
+ return ptr;
+ }
+ // friend classes
+ friend class Object;
+ friend class ObjectRef;
+ friend class tvm::ffi::details::ObjectInternal;
+ template <typename RelayRefType, typename ObjType>
+ friend RelayRefType GetRef(const ObjType* ptr);
+ template <typename BaseType, typename ObjType>
+ friend ObjectPtr<BaseType> GetObjectPtr(ObjType* ptr);
+};
+
+namespace details {
+/*! \brief Namespace to internally manipulate object class. */
+struct ObjectInternal {};
+} // namespace details
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_OBJECT_H_
diff --git a/ffi/src b/ffi/src
deleted file mode 120000
index 5cd551cf26..0000000000
--- a/ffi/src
+++ /dev/null
@@ -1 +0,0 @@
-../src
\ No newline at end of file
diff --git a/ffi/src/ffi/registry.cc b/ffi/src/ffi/registry.cc
new file mode 100644
index 0000000000..698f81c2b6
--- /dev/null
+++ b/ffi/src/ffi/registry.cc
@@ -0,0 +1 @@
+namespace tvm {}
\ No newline at end of file
diff --git a/ffi/tests/example/CMakeLists.txt b/ffi/tests/example/CMakeLists.txt
new file mode 100644
index 0000000000..a78e3ee01a
--- /dev/null
+++ b/ffi/tests/example/CMakeLists.txt
@@ -0,0 +1,21 @@
+file(GLOB _test_sources "${CMAKE_CURRENT_SOURCE_DIR}/test*.cc")
+add_executable(
+ tvm_ffi_tests
+ EXCLUDE_FROM_ALL
+ ${_test_sources}
+)
+set_target_properties(
+ tvm_ffi_tests PROPERTIES
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+ CXX_EXTENSIONS OFF
+ MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
+ ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
+)
+add_cxx_warning(tvm_ffi_tests)
+add_sanitizer_address(tvm_ffi_tests)
+target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi)
+
+add_googletest(tvm_ffi_tests)
diff --git a/ffi/tests/example/test_c_ffi_abi.cc b/ffi/tests/example/test_c_ffi_abi.cc
new file mode 100644
index 0000000000..66f2dcf739
--- /dev/null
+++ b/ffi/tests/example/test_c_ffi_abi.cc
@@ -0,0 +1,12 @@
+#include <gtest/gtest.h>
+#include <tvm/ffi/c_ffi_abi.h>
+
+namespace {
+
+TEST(ABIHeaderAlignment, Default) {
+ TVMFFIObject value;
+ value.type_index = 10;
+ EXPECT_EQ(reinterpret_cast<TVMFFIAny*>(&value)->type_index, 10);
+}
+
+} // namespace
diff --git a/ffi/tests/example/test_object.cc b/ffi/tests/example/test_object.cc
new file mode 100644
index 0000000000..95b7d0717f
--- /dev/null
+++ b/ffi/tests/example/test_object.cc
@@ -0,0 +1,12 @@
+#include <gtest/gtest.h>
+#include <tvm/ffi/object.h>
+
+namespace {
+
+using namespace tvm::ffi;
+
+TEST(Object, Default) {
+ // Object* x = new Object();
+}
+
+} // namespace
(tvm) 32/38: Pass all runtime
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit fb524a693549cd96b37dcb66412e959e44922f96
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 17:45:16 2025 -0400
Pass all runtime
---
CMakeLists.txt | 6 +++---
cmake/modules/contrib/CUTLASS.cmake | 2 ++
ffi/include/tvm/ffi/cast.h | 15 +++++++++++++++
ffi/include/tvm/ffi/object.h | 20 ++++----------------
include/tvm/runtime/container/array.h | 2 +-
include/tvm/runtime/memory/memory_manager.h | 4 ++--
include/tvm/runtime/packed_func.h | 3 ++-
include/tvm/runtime/relax_vm/vm.h | 2 --
src/runtime/memory/memory_manager.cc | 18 +++++++++++-------
src/runtime/minrpc/rpc_reference.h | 12 +++++++-----
src/runtime/ndarray.cc | 4 ++--
src/runtime/relax_vm/kv_state.h | 5 +----
src/runtime/relax_vm/lm_support.cc | 1 -
src/runtime/relax_vm/paged_kv_cache.cc | 1 -
src/runtime/relax_vm/rnn_state.cc | 4 ++--
src/runtime/rpc/rpc_module.cc | 2 +-
src/runtime/rpc/rpc_session.h | 3 ++-
17 files changed, 55 insertions(+), 49 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b630f12b06..4f3dc2c9e2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -753,9 +753,9 @@ string(APPEND PROJECT_CONFIG_CONTENT
"include(\"\${CMAKE_CURRENT_LIST_DIR}/${PROJECT_NAME}Targets.cmake\")")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/temp_config_file.cmake" ${PROJECT_CONFIG_CONTENT})
-install(EXPORT ${PROJECT_NAME}Targets
- NAMESPACE ${PROJECT_NAME}::
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
+# install(EXPORT ${PROJECT_NAME}Targets
+# NAMESPACE ${PROJECT_NAME}::
+# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
# Create config for find_package()
configure_package_config_file(
diff --git a/cmake/modules/contrib/CUTLASS.cmake b/cmake/modules/contrib/CUTLASS.cmake
index 75e7f68c88..de3bc860f9 100644
--- a/cmake/modules/contrib/CUTLASS.cmake
+++ b/cmake/modules/contrib/CUTLASS.cmake
@@ -33,6 +33,8 @@ if(USE_CUDA AND USE_CUTLASS)
${PROJECT_SOURCE_DIR}/3rdparty/cutlass_fpA_intB_gemm
${PROJECT_SOURCE_DIR}/3rdparty/cutlass_fpA_intB_gemm/cutlass/include
)
+ target_link_libraries(fpA_intB_gemm_tvm PRIVATE tvm_ffi_header)
+
set(CUTLASS_FPA_INTB_RUNTIME_SRCS "")
list(APPEND CUTLASS_FPA_INTB_RUNTIME_SRCS src/runtime/contrib/cutlass/weight_preprocess.cc)
add_library(fpA_intB_cutlass_objs OBJECT ${CUTLASS_FPA_INTB_RUNTIME_SRCS})
diff --git a/ffi/include/tvm/ffi/cast.h b/ffi/include/tvm/ffi/cast.h
index 3cdb620d22..c7365a2f45 100644
--- a/ffi/include/tvm/ffi/cast.h
+++ b/ffi/include/tvm/ffi/cast.h
@@ -51,6 +51,21 @@ inline RefType GetRef(const ObjectType* ptr) {
const_cast<Object*>(static_cast<const Object*>(ptr))));
}
+/*!
+ * \brief Get an object ptr type from a raw object ptr.
+ *
+ * \param ptr The object pointer
+ * \tparam BaseType The reference type
+ * \tparam ObjectType The object type
+ * \return The corresponding RefType
+ */
+template <typename BaseType, typename ObjectType>
+inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr) {
+ static_assert(std::is_base_of<BaseType, ObjectType>::value,
+ "Can only cast to the ref of same container type");
+ return details::ObjectUnsafe::ObjectPtrFromUnowned<BaseType>(ptr);
+}
+
/*!
* \brief Downcast a base reference type to a more specific type.
*
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 01dca88878..d63b4efbd7 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -320,10 +320,6 @@ class ObjectPtr {
template <typename>
friend class ObjectPtr;
friend class tvm::ffi::details::ObjectUnsafe;
- template <typename RelayRefType, typename ObjType>
- friend RelayRefType GetRef(const ObjType* ptr);
- template <typename BaseType, typename ObjType>
- friend ObjectPtr<BaseType> GetObjectPtr(ObjType* ptr);
};
// Forward declaration, to prevent circular includes.
@@ -410,17 +406,6 @@ class ObjectRef {
friend class tvm::ffi::details::ObjectUnsafe;
};
-/*!
- * \brief Get an object ptr type from a raw object ptr.
- *
- * \param ptr The object pointer
- * \tparam BaseType The reference type
- * \tparam ObjectType The object type
- * \return The corresponding RefType
- */
-template <typename BaseType, typename ObjectType>
-inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
-
/*! \brief ObjectRef hash functor */
struct ObjectPtrHash {
size_t operator()(const ObjectRef& a) const { return operator()(a.data_); }
@@ -673,12 +658,15 @@ class ObjectUnsafe {
return ptr;
}
+ static TVM_FFI_INLINE Object** GetObjectRValueRefValue(ObjectRef* ref) {
+ return const_cast<Object**>(&(ref->data_.data_));
+ }
+
// legacy APIs to support migration and can be moved later
static TVM_FFI_INLINE void LegacyClearObjectPtrAfterMove(ObjectRef* src) {
src->data_.data_ = nullptr;
}
};
-
} // namespace details
} // namespace ffi
} // namespace tvm
diff --git a/include/tvm/runtime/container/array.h b/include/tvm/runtime/container/array.h
index 7513a1299a..f4e825f44f 100644
--- a/include/tvm/runtime/container/array.h
+++ b/include/tvm/runtime/container/array.h
@@ -247,7 +247,7 @@ class ArrayNode : public Object, public InplaceArrayBase<ArrayNode, ObjectRef> {
friend class Array;
// To specialize make_object<ArrayNode>
- friend ObjectPtr<ArrayNode> make_object<>();
+ friend ObjectPtr<ArrayNode> tvm::ffi::make_object<>();
};
/*! \brief Helper struct for type-checking
diff --git a/include/tvm/runtime/memory/memory_manager.h b/include/tvm/runtime/memory/memory_manager.h
index 754d7b4fe3..a88d05603f 100644
--- a/include/tvm/runtime/memory/memory_manager.h
+++ b/include/tvm/runtime/memory/memory_manager.h
@@ -171,10 +171,10 @@ class StorageObj : public Object {
String scope = "global");
/*! \brief The deleter for an NDArray when allocated from underlying storage. */
- static void ScopedDeleter(Object* ptr);
+ static void ScopedDeleter(void* ptr);
/*! \brief The deleter for an NDArray when allocated from underlying storage. */
- static void Deleter(Object* ptr);
+ static void Deleter(void* ptr);
~StorageObj() {
if (allocator) {
diff --git a/include/tvm/runtime/packed_func.h b/include/tvm/runtime/packed_func.h
index 168798149a..912a15e976 100644
--- a/include/tvm/runtime/packed_func.h
+++ b/include/tvm/runtime/packed_func.h
@@ -2183,7 +2183,8 @@ inline void TVMArgsSetter::SetObject(size_t i, T&& value) const {
// Final fallback, if the ObjectRef has no special cases that must
// be expressed within the TVMRetValue.
if constexpr (std::is_rvalue_reference_v<decltype(value)>) {
- values_[i].v_handle = const_cast<Object**>(&(value.data_.data_));
+ // values_[i].v_handle = const_cast<Object**>(&(value.data_.data_));
+ values_[i].v_handle = ffi::details::ObjectUnsafe::GetObjectRValueRefValue(&value);
type_codes_[i] = kTVMObjectRValueRefArg;
} else {
// value.data_.data_;
diff --git a/include/tvm/runtime/relax_vm/vm.h b/include/tvm/runtime/relax_vm/vm.h
index 7bf716ae50..3d2bbc130e 100644
--- a/include/tvm/runtime/relax_vm/vm.h
+++ b/include/tvm/runtime/relax_vm/vm.h
@@ -76,7 +76,6 @@ class VMClosureObj : public Object {
*/
PackedFunc impl;
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const char* _type_key = "relax.vm.Closure";
TVM_DECLARE_FINAL_OBJECT_INFO(VMClosureObj, Object);
};
@@ -108,7 +107,6 @@ class VMClosure : public ObjectRef {
*/
class VMExtensionNode : public Object {
protected:
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const char* _type_key = "runtime.VMExtension";
TVM_DECLARE_BASE_OBJECT_INFO(VMExtensionNode, Object);
};
diff --git a/src/runtime/memory/memory_manager.cc b/src/runtime/memory/memory_manager.cc
index a4b8e15943..fb10b7724b 100644
--- a/src/runtime/memory/memory_manager.cc
+++ b/src/runtime/memory/memory_manager.cc
@@ -34,7 +34,7 @@ namespace tvm {
namespace runtime {
namespace memory {
-static void BufferDeleter(Object* obj) {
+static void BufferDeleter(void* obj) {
auto* ptr = static_cast<NDArray::Container*>(obj);
ICHECK(ptr->manager_ctx != nullptr);
Buffer* buffer = reinterpret_cast<Buffer*>(ptr->manager_ctx);
@@ -50,7 +50,7 @@ Storage::Storage(Buffer buffer, Allocator* allocator) {
data_ = std::move(n);
}
-void StorageObj::Deleter(Object* obj) {
+void StorageObj::Deleter(void* obj) {
auto* ptr = static_cast<NDArray::Container*>(obj);
// When invoking AllocNDArray we don't own the underlying allocation
// and should not delete the buffer, but instead let it be reclaimed
@@ -62,7 +62,8 @@ void StorageObj::Deleter(Object* obj) {
// We decrement the object allowing for the buffer to release our
// reference count from allocation.
StorageObj* storage = reinterpret_cast<StorageObj*>(ptr->manager_ctx);
- storage->DecRef();
+ // storage->DecRef();
+ tvm::ffi::details::ObjectUnsafe::DecRefObjectHandle(storage);
delete ptr;
}
@@ -84,13 +85,14 @@ inline size_t GetDataAlignment(const DLTensor& arr) {
return align;
}
-void StorageObj::ScopedDeleter(Object* obj) {
+void StorageObj::ScopedDeleter(void* obj) {
auto* ptr = static_cast<NDArray::Container*>(obj);
StorageObj* storage = reinterpret_cast<StorageObj*>(ptr->manager_ctx);
// Let the device handle proper cleanup of view
storage->allocator->FreeView(ptr->dl_tensor.device, ptr->dl_tensor.data);
- storage->DecRef();
+ // storage->DecRef();
+ tvm::ffi::details::ObjectUnsafe::DecRefObjectHandle(storage);
delete ptr;
}
@@ -105,7 +107,8 @@ NDArray StorageObj::AllocNDArrayScoped(int64_t offset, ShapeTuple shape, DLDataT
container->dl_tensor.byte_offset = offset;
container->SetDeleter(StorageObj::ScopedDeleter);
size_t needed_size = DeviceAPI::Get(this->buffer.device)->GetDataSize(container->dl_tensor);
- this->IncRef();
+ // this->IncRef();
+ tvm::ffi::details::ObjectUnsafe::IncRefObjectHandle(this);
container->manager_ctx = reinterpret_cast<void*>(this);
NDArray ret(GetObjectPtr<Object>(container));
// RAII in effect, now run the check.
@@ -125,7 +128,8 @@ NDArray StorageObj::AllocNDArray(int64_t offset, ShapeTuple shape, DLDataType dt
container->SetDeleter(StorageObj::Deleter);
size_t needed_size = DeviceAPI::Get(this->buffer.device)->GetDataSize(container->dl_tensor);
- this->IncRef();
+ // this->IncRef();
+ tvm::ffi::details::ObjectUnsafe::IncRefObjectHandle(this);
// The manager context pointer must continue to point to the storage object
// which owns the backing memory, and keeps track of the reference count.
//
diff --git a/src/runtime/minrpc/rpc_reference.h b/src/runtime/minrpc/rpc_reference.h
index bb9c5d3c86..ff3c9f22fd 100644
--- a/src/runtime/minrpc/rpc_reference.h
+++ b/src/runtime/minrpc/rpc_reference.h
@@ -25,10 +25,12 @@
#define TVM_RUNTIME_MINRPC_RPC_REFERENCE_H_
namespace tvm {
-namespace runtime {
-
+namespace ffi {
// Forward declare TVM Object to use `Object*` in RPC protocol.
-// class Object;
+class Object;
+} // namespace ffi
+
+namespace runtime {
/*! \brief The current RPC procotol version. */
constexpr const char* kRPCProtocolVer = "0.8.0";
@@ -206,7 +208,7 @@ struct RPCReference {
num_bytes_ += sizeof(T) * num;
}
- void WriteObject(Object* obj) { num_bytes_ += channel_->GetObjectBytes(obj); }
+ void WriteObject(ffi::Object* obj) { num_bytes_ += channel_->GetObjectBytes(obj); }
void ThrowError(RPCServerStatus status) { channel_->ThrowError(status); }
@@ -383,7 +385,7 @@ struct RPCReference {
break;
}
case kTVMObjectHandle: {
- channel->WriteObject(static_cast<Object*>(value.v_handle));
+ channel->WriteObject(static_cast<ffi::Object*>(value.v_handle));
break;
}
default: {
diff --git a/src/runtime/ndarray.cc b/src/runtime/ndarray.cc
index 32f11a9e8a..63087fda6f 100644
--- a/src/runtime/ndarray.cc
+++ b/src/runtime/ndarray.cc
@@ -104,7 +104,7 @@ struct NDArray::Internal {
static void DefaultDeleter(void* ptr_obj) {
auto* ptr = static_cast<NDArray::Container*>(ptr_obj);
if (ptr->manager_ctx != nullptr) {
- details::ObjectUnsafe::DecRefObjectHandle(
+ ffi::details::ObjectUnsafe::DecRefObjectHandle(
static_cast<NDArray::Container*>(ptr->manager_ctx)
);
} else if (ptr->dl_tensor.data != nullptr) {
@@ -179,7 +179,7 @@ struct NDArray::Internal {
}
// Delete dlpack object.
static void NDArrayDLPackDeleter(DLManagedTensor* tensor) {
- details::ObjectUnsafe::DecRefObjectHandle(static_cast<NDArray::Container*>(tensor->manager_ctx));
+ ffi::details::ObjectUnsafe::DecRefObjectHandle(static_cast<NDArray::Container*>(tensor->manager_ctx));
delete tensor;
}
};
diff --git a/src/runtime/relax_vm/kv_state.h b/src/runtime/relax_vm/kv_state.h
index 1e530b41ec..14e17d26b7 100644
--- a/src/runtime/relax_vm/kv_state.h
+++ b/src/runtime/relax_vm/kv_state.h
@@ -103,9 +103,8 @@ class KVStateObj : public Object {
*/
virtual void EndForward() = 0;
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const char* _type_key = "relax.vm.KVState";
- TVM_DECLARE_BASE_OBJECT_INFO(KVStateObj, Object)
+ TVM_DECLARE_BASE_OBJECT_INFO(KVStateObj, Object);
};
class KVState : public ObjectRef {
@@ -293,7 +292,6 @@ class AttentionKVCacheObj : public KVStateObj {
*/
virtual void DebugSetKV(int64_t seq_id, int64_t start_pos, NDArray k_data, NDArray v_data) = 0;
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const char* _type_key = "relax.vm.AttentionKVCache";
TVM_DECLARE_BASE_OBJECT_INFO(AttentionKVCacheObj, KVStateObj);
};
@@ -337,7 +335,6 @@ class RNNStateObj : public KVStateObj {
*/
virtual NDArray DebugGet(int64_t layer_id, int64_t state_id, int64_t seq_id) = 0;
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const char* _type_key = "relax.vm.RNNState";
TVM_DECLARE_BASE_OBJECT_INFO(RNNStateObj, KVStateObj);
};
diff --git a/src/runtime/relax_vm/lm_support.cc b/src/runtime/relax_vm/lm_support.cc
index 95dca0c6d5..4af4f42db4 100644
--- a/src/runtime/relax_vm/lm_support.cc
+++ b/src/runtime/relax_vm/lm_support.cc
@@ -225,7 +225,6 @@ class AttentionKVCacheLegacyObj : public Object {
this->fill_count += value->shape[0];
}
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const char* _type_key = "relax.vm.AttentionKVCacheLegacy";
TVM_DECLARE_FINAL_OBJECT_INFO(AttentionKVCacheLegacyObj, Object);
};
diff --git a/src/runtime/relax_vm/paged_kv_cache.cc b/src/runtime/relax_vm/paged_kv_cache.cc
index 6b68bb1b1a..0011fa28d1 100644
--- a/src/runtime/relax_vm/paged_kv_cache.cc
+++ b/src/runtime/relax_vm/paged_kv_cache.cc
@@ -1600,7 +1600,6 @@ class PagedAttentionKVCacheObj : public AttentionKVCacheObj {
ICHECK(false) << "DebugSetKV for PageAttentionKVCache not implemented yet.";
}
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const char* _type_key = "relax.vm.PagedAttentionKVCache";
TVM_DECLARE_FINAL_OBJECT_INFO(PagedAttentionKVCacheObj, AttentionKVCacheObj);
diff --git a/src/runtime/relax_vm/rnn_state.cc b/src/runtime/relax_vm/rnn_state.cc
index 16fe6791b8..c823cfbb98 100644
--- a/src/runtime/relax_vm/rnn_state.cc
+++ b/src/runtime/relax_vm/rnn_state.cc
@@ -454,8 +454,8 @@ class RNNStateImpObj : public RNNStateObj {
}
public:
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
- static constexpr const char* _type_key = "relax.vm.RNNStateImp";
+
+ static constexpr const char* _type_key = "relax.vm.RNNStateImp";
TVM_DECLARE_FINAL_OBJECT_INFO(RNNStateImpObj, RNNStateObj);
};
diff --git a/src/runtime/rpc/rpc_module.cc b/src/runtime/rpc/rpc_module.cc
index a696005ab8..57f2771501 100644
--- a/src/runtime/rpc/rpc_module.cc
+++ b/src/runtime/rpc/rpc_module.cc
@@ -41,7 +41,7 @@ namespace tvm {
namespace runtime {
// deleter of RPC remote array
-static void RemoteNDArrayDeleter(Object* obj) {
+static void RemoteNDArrayDeleter(void* obj) {
auto* ptr = static_cast<NDArray::Container*>(obj);
RemoteSpace* space = static_cast<RemoteSpace*>(ptr->dl_tensor.data);
if (ptr->manager_ctx != nullptr) {
diff --git a/src/runtime/rpc/rpc_session.h b/src/runtime/rpc/rpc_session.h
index f01b571b25..9bb4f31946 100644
--- a/src/runtime/rpc/rpc_session.h
+++ b/src/runtime/rpc/rpc_session.h
@@ -320,7 +320,8 @@ class RPCObjectRefObj : public Object {
static constexpr const uint32_t _type_index = TypeIndex::kRuntimeRPCObjectRef;
static constexpr const char* _type_key = "runtime.RPCObjectRef";
- TVM_DECLARE_FINAL_OBJECT_INFO(RPCObjectRefObj, Object);
+ static const constexpr bool _type_final = true;
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(RPCObjectRefObj, Object);
private:
// The object handle
(tvm) 06/38: [FFI] Object type hierachy cast and check support
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit d3145e864a3a87b92c3d6e98d83a59354095abed
Author: tqchen <ti...@gmail.com>
AuthorDate: Wed Aug 14 14:53:14 2024 -0400
[FFI] Object type hierachy cast and check support
Co-authored-by: Junru Shao <ju...@apache.org>
---
ffi/include/tvm/ffi/c_api.h | 26 +++-
ffi/include/tvm/ffi/error.h | 2 +-
ffi/include/tvm/ffi/object.h | 201 +++++++++++++++++++++--------
ffi/src/ffi/object.cc | 265 +++++++++++++++++++++------------------
ffi/tests/example/test_error.cc | 1 -
ffi/tests/example/test_object.cc | 93 +++++++++++++-
6 files changed, 410 insertions(+), 178 deletions(-)
diff --git a/ffi/include/tvm/ffi/c_api.h b/ffi/include/tvm/ffi/c_api.h
index 2616ae1648..e0597d0dda 100644
--- a/ffi/include/tvm/ffi/c_api.h
+++ b/ffi/include/tvm/ffi/c_api.h
@@ -85,8 +85,7 @@ typedef enum {
// [Section] Dynamic Boxed: [kTVMFFIDynObjectBegin, +oo)
// kTVMFFIDynObject is used to indicate that the type index
// is dynamic and needs to be looked up at runtime
- kTVMFFIDynObject = 128,
- kTVMFFIDynObjectBegin = 129
+ kTVMFFIDynObjectBegin = 128
#ifdef __cplusplus
};
#else
@@ -142,6 +141,29 @@ typedef struct {
const char* bytes;
} TVMFFIByteArray;
+/*!
+ * \brief Runtime type information for object type checking.
+ */
+typedef struct {
+ /*!
+ *\brief The runtime type index,
+ * It can be allocated during runtime if the type is dynamic.
+ */
+ int32_t type_index;
+ /*! \brief number of parent types in the type hierachy. */
+ int32_t type_depth;
+ /*! \brief the unique type key to identify the type. */
+ const char* type_key;
+ /*! \brief Cached hash value of the type key, used for consistent structural hashing. */
+ uint64_t type_key_hash;
+ /*!
+ * \brief type_acenstors[depth] stores the type_index of the acenstors at depth level
+ * \note To keep things simple, we do not allow multiple inheritance so the
+ * hieracy stays as a tree
+ */
+ const int32_t* type_acenstors;
+} TVMFFITypeInfo;
+
#ifdef __cplusplus
} // TVM_FFI_EXTERN_C
#endif
diff --git a/ffi/include/tvm/ffi/error.h b/ffi/include/tvm/ffi/error.h
index 250206ac8b..f894166ef3 100644
--- a/ffi/include/tvm/ffi/error.h
+++ b/ffi/include/tvm/ffi/error.h
@@ -95,7 +95,7 @@ class Error :
return get()->what_str.c_str();
}
- TVM_FFI_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(Error, ObjectRef, ErrorObj)
+ TVM_FFI_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(Error, ObjectRef, ErrorObj);
};
namespace details {
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 03ba7d7dbf..5a6b552e1c 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -33,10 +33,44 @@ namespace tvm {
namespace ffi {
using TypeIndex = TVMFFITypeIndex;
+using TypeInfo = TVMFFITypeInfo;
namespace details {
// forward declare object internal
struct ObjectInternal;
+
+// Code section that depends on dynamic components
+#if TVM_FFI_ALLOW_DYN_TYPE
+/*!
+ * \brief Initialize the type info during runtime.
+ *
+ * When the function is first time called for a type,
+ * it will register the type to the type table in the runtime.
+ *
+ * If the static_tindex is non-negative, the function will
+ * allocate a runtime type index.
+ * Otherwise, we will populate the type table and return the static index.
+ *
+ * \param type_key The type key.
+ * \param static_type_index Static type index if any, can be -1, which means this is a dynamic index
+ * \param num_child_slots Number of slots reserved for its children.
+ * \param child_slots_can_overflow Whether to allow child to overflow the slots.
+ * \param parent_type_index Parent type index, pass in -1 if it is root.
+ *
+ * \return The allocated type index
+ */
+TVM_FFI_DLL int32_t ObjectGetOrAllocTypeIndex(const char* type_key, int32_t static_type_index,
+ int32_t type_depth, int32_t num_child_slots,
+ bool child_slots_can_overflow,
+ int32_t parent_type_index);
+
+/*!
+ * \brief Get Type information from type index.
+ * \param type_index The type index
+ * \return The type information
+ */
+TVM_FFI_DLL const TypeInfo* ObjectGetTypeInfo(int32_t type_index);
+#endif // TVM_FFI_ALLOW_DYN_TYPE
} // namespace details
/*!
@@ -89,6 +123,47 @@ class Object {
header_.deleter = nullptr;
}
+ /*!
+ * Check if the object is an instance of TargetType.
+ * \tparam TargetType The target type to be checked.
+ * \return Whether the target type is true.
+ */
+ template <typename TargetType>
+ bool IsInstance() const {
+ // Everything is a subclass of object.
+ if constexpr (std::is_same<TargetType, Object>::value) return true;
+
+ if constexpr (TargetType::_type_final) {
+ // if the target type is a final type
+ // then we only need to check the equivalence.
+ return header_.type_index == TargetType::RuntimeTypeIndex();
+ }
+
+ // if target type is a non-leaf type
+ // Check if type index falls into the range of reserved slots.
+ int32_t target_type_index = TargetType::RuntimeTypeIndex();
+ int32_t begin = target_type_index;
+ // The condition will be optimized by constant-folding.
+ if constexpr (TargetType::_type_child_slots != 0) {
+ int32_t end = begin + TargetType::_type_child_slots;
+ if (header_.type_index >= begin && header_.type_index < end) return true;
+ } else {
+ if (header_.type_index == begin) return true;
+ }
+ if (!TargetType::_type_child_slots_can_overflow) return false;
+ // Invariance: parent index is always smaller than the child.
+ if (header_.type_index < target_type_index) return false;
+ // Do a runtime lookup of type information
+#if TVM_FFI_ALLOW_DYN_TYPE
+ // the function checks that the info exists
+ const TypeInfo* type_info = details::ObjectGetTypeInfo(header_.type_index);
+ return (type_info->type_depth > TargetType::_type_depth &&
+ type_info->type_acenstors[TargetType::_type_depth] == target_type_index);
+#else
+ return false;
+#endif
+ }
+
// Information about the object
static constexpr const char* _type_key = "runtime.Object";
@@ -96,11 +171,10 @@ class Object {
static constexpr bool _type_final = false;
static constexpr uint32_t _type_child_slots = 0;
static constexpr bool _type_child_slots_can_overflow = true;
- // NOTE: the following field is not type index of Object
- // but was intended to be used by sub-classes as default value.
- // The type index of Object is TypeIndex::kRoot
+ // NOTE: static type index field of the class
static constexpr int32_t _type_index = TypeIndex::kTVMFFIObject;
-
+ // the static type depth of the class
+ static constexpr int32_t _type_depth = 0;
// The following functions are provided by macro
// TVM_FFI_DECLARE_BASE_OBJECT_INFO and TVM_DECLARE_FINAL_OBJECT_INFO
/*!
@@ -110,7 +184,6 @@ class Object {
static int32_t RuntimeTypeIndex() { return TypeIndex::kTVMFFIObject; }
/*!
* \brief Internal function to get or allocate a runtime index.
- * \note
*/
static int32_t _GetOrAllocRuntimeTypeIndex() { return TypeIndex::kTVMFFIObject; }
@@ -342,7 +415,13 @@ class ObjectRef {
* \tparam ObjectType the target type, must be a subtype of Object
*/
template <typename ObjectType, typename = std::enable_if_t<std::is_base_of_v<Object, ObjectType>>>
- inline const ObjectType* as() const;
+ const ObjectType* as() const {
+ if (data_ != nullptr && data_->IsInstance<ObjectType>()) {
+ return static_cast<ObjectType*>(data_.get());
+ } else {
+ return nullptr;
+ }
+ }
/*! \brief type indicate the container type. */
using ContainerType = Object;
@@ -377,14 +456,30 @@ inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
* \param TypeName The name of the current type.
* \param ParentType The name of the ParentType
*/
-#define TVM_FFI_OBJECT_STATIC_CHECKS(TypeName, ParentType) \
+#define TVM_FFI_OBJECT_STATIC_DEFS(TypeName, ParentType) \
+ static constexpr int32_t _type_depth = ParentType::_type_depth + 1; \
static_assert(!ParentType::_type_final, "ParentType marked as final"); \
static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
TypeName::_type_child_slots < ParentType::_type_child_slots, \
"Need to set _type_child_slots when parent specifies it."); \
static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
TypeName::_type_child_slots < ParentType::_type_child_slots, \
- "Need to set _type_child_slots when parent specifies it.");
+ "Need to set _type_child_slots when parent specifies it.")
+
+// If dynamic type is enabled, we still need to register the runtime type of parent
+#if TVM_FFI_ALLOW_DYN_TYPE
+#define TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType) \
+ static int32_t _GetOrAllocRuntimeTypeIndex() { \
+ static int32_t tindex = ::tvm::ffi::details::ObjectGetOrAllocTypeIndex( \
+ TypeName::_type_key, TypeName::_type_index, TypeName::_type_depth, \
+ TypeName::_type_child_slots, TypeName::_type_child_slots_can_overflow, \
+ ParentType::_GetOrAllocRuntimeTypeIndex()); \
+ return tindex; \
+ } \
+ static inline int32_t _register_type_index = _GetOrAllocRuntimeTypeIndex()
+#else
+#define TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType)
+#endif
/*!
* \brief Helper macro to declare a object that comes with static type index.
@@ -392,25 +487,38 @@ inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
* \param ParentType The name of the ParentType
*/
#define TVM_FFI_DECLARE_STATIC_OBJECT_INFO(TypeName, ParentType) \
- TVM_FFI_OBJECT_STATIC_CHECKS(TypeName, ParentType) \
- static int32_t RuntimeTypeIndex() { return TypeName::_type_index; }
+ TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType); \
+ static int32_t RuntimeTypeIndex() { return TypeName::_type_index; }\
+ TVM_FFI_OBJECT_STATIC_DEFS(TypeName, ParentType)
/*!
* \brief helper macro to declare a base object type that can be inherited.
* \param TypeName The name of the current type.
* \param ParentType The name of the ParentType
*/
-#define TVM_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType) \
- static_assert(TVM_FFI_ALLOW_DYN_TYPE, \
- "Dynamic object depend on TVM_FFI_ALLOW_DYN_TYPE cd set to 1"); \
- TVM_FFI_OBJECT_STATIC_CHECKS(TypaName, ParentType) \
- static inline int32_t _type_index = _GetOrAllocRuntimeTypeIndex(); \
- static int32_t RuntimeTypeIndex() { return TypeName::_type_index; } \
- static int32_t _GetOrAllocRuntimeTypeIndex() { \
- return ::tvm::ffi::details::ObjectGetOrAllocTypeIndex( \
- TypeName::_type_key, -1, ParentType::_GetOrAllocRuntimeTypeIndex(), \
- TypeName::_type_child_slots, TypeName::_type_child_slots_can_overflow); \
- }
+#define TVM_FFI_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType) \
+ static_assert(TVM_FFI_ALLOW_DYN_TYPE, \
+ "Dynamic object depend on TVM_FFI_ALLOW_DYN_TYPE cd set to 1"); \
+ TVM_FFI_OBJECT_STATIC_DEFS(TypeName, ParentType); \
+ static int32_t _GetOrAllocRuntimeTypeIndex() { \
+ static int32_t tindex = ::tvm::ffi::details::ObjectGetOrAllocTypeIndex( \
+ TypeName::_type_key, -1, TypeName::_type_depth, TypeName::_type_child_slots, \
+ TypeName::_type_child_slots_can_overflow, ParentType::_GetOrAllocRuntimeTypeIndex()); \
+ return tindex; \
+ } \
+ static int32_t RuntimeTypeIndex() { return _GetOrAllocRuntimeTypeIndex(); } \
+ static inline int32_t _type_index = _GetOrAllocRuntimeTypeIndex()
+
+/*!
+ * \brief helper macro to declare type information in a final class.
+ * \param TypeName The name of the current type.
+ * \param ParentType The name of the ParentType
+ */
+#define TVM_FFI_DECLARE_FINAL_OBJECT_INFO(TypeName, ParentType) \
+ static const constexpr int _type_child_slots = 0; \
+ static const constexpr bool _type_final = true; \
+ TVM_FFI_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType)
+
/*
* \brief Define object reference methods.
@@ -424,7 +532,7 @@ inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
TVM_FFI_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName) \
const ObjectName* operator->() const { return static_cast<const ObjectName*>(data_.get()); } \
const ObjectName* get() const { return operator->(); } \
- using ContainerType = ObjectName;
+ using ContainerType = ObjectName
/*
* \brief Define object reference methods that is not nullable.
@@ -439,9 +547,25 @@ inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
const ObjectName* operator->() const { return static_cast<const ObjectName*>(data_.get()); } \
const ObjectName* get() const { return operator->(); } \
static constexpr bool _type_is_nullable = false; \
- using ContainerType = ObjectName;
+ using ContainerType = ObjectName
namespace details {
+
+// auxiliary class to enable static type info table at depth
+template <int depth>
+struct TypeInfoAtDepth : public TypeInfo {
+ /*! \brief extra type acenstors fields */
+ int32_t _type_acenstors[depth];
+
+ TypeInfoAtDepth(const char* type_key, int32_t static_type_index) {
+ this->type_key = type_key;
+ this->type_key_hash = 0;
+ this->type_index = static_type_index;
+ this->type_depth = depth;
+ this->type_acenstors = _type_acenstors;
+ }
+};
+
/*!
* \brief Namespace to internally manipulate object class.
* \note These functions are only supposed to be used by internal
@@ -469,37 +593,6 @@ struct ObjectInternal {
}
};
-// Code section that depends on dynamic components
-#if TVM_FFI_ALLOW_DYN_TYPE
-/*!
- * \brief Get the type index using type key.
- *
- * When the function is first time called for a type,
- * it will register the type to the type table in the runtime.
- * If the static_tindex is TypeIndex::kDynamic, the function will
- * allocate a runtime type index.
- * Otherwise, we will populate the type table and return the static index.
- *
- * \param type_key the type key.
- * \param static_tindex Static type index if any, can be -1, which means this is a dynamic index
- * \param parent_tindex The index of the parent.
- * \param type_child_slots Number of slots reserved for its children.
- * \param type_child_slots_can_overflow Whether to allow child to overflow the slots.
- *
- * \return The allocated type index
- */
-TVM_FFI_DLL int ObjectGetOrAllocTypeIndex(const char* type_key, int32_t static_tindex,
- int32_t parent_tindex, int32_t type_child_slots,
- bool type_child_slots_can_overflow);
-
-/*!
- * \brief Check whether child type is derived from parent type.
- * \param child_type_index The candidate child type index.
- * \param parent_type_index The candidate parent type index.
- * \return the Check result.
- */
-TVM_FFI_DLL bool ObjectDerivedFrom(int32_t child_type_index, int32_t parent_type_index);
-#endif // TVM_FFI_ALLOW_DYN_TYPE
} // namespace details
} // namespace ffi
} // namespace tvm
diff --git a/ffi/src/ffi/object.cc b/ffi/src/ffi/object.cc
index ac8e04ebb9..f4e2ff3e30 100644
--- a/ffi/src/ffi/object.cc
+++ b/ffi/src/ffi/object.cc
@@ -23,6 +23,7 @@
#include <tvm/ffi/c_api.h>
#include <tvm/ffi/error.h>
+#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
@@ -31,26 +32,6 @@
namespace tvm {
namespace ffi {
-/*! \brief Type information */
-struct TypeInfo {
- /*! \brief The current index. */
- int32_t index{0};
- /*! \brief Index of the parent in the type hierarchy */
- int32_t parent_index{0};
- // NOTE: the indices in [index, index + num_reserved_slots) are
- // reserved for the child-class of this type.
- /*! \brief Total number of slots reserved for the type and its children. */
- int32_t num_slots{0};
- /*! \brief number of allocated child slots. */
- int32_t allocated_slots{0};
- /*! \brief Whether child can overflow. */
- bool child_slots_can_overflow{true};
- /*! \brief name of the type. */
- std::string name;
- /*! \brief hash of the name */
- size_t name_hash{0};
-};
-
/*!
* \brief Type context that manages the type hierarchy information.
*
@@ -61,150 +42,196 @@ struct TypeInfo {
*
* Then the followup code will leverage the information
*/
-class TypeContext {
+class TypeTable {
public:
- // NOTE: this is a relatively slow path for child checking
- // Most types are already checked by the fast-path via reserved slot checking.
- bool DerivedFrom(int32_t child_tindex, int32_t parent_tindex) {
- // invariance: child's type index is always bigger than its parent.
- if (child_tindex < parent_tindex) return false;
- if (child_tindex == parent_tindex) return true;
- TVM_FFI_ICHECK_LT(child_tindex, type_table_.size());
- while (child_tindex > parent_tindex) {
- child_tindex = type_table_[child_tindex].parent_index;
+ /*! \brief Type information */
+ struct Entry : public TypeInfo {
+ /*! \brief stored type key */
+ std::string type_key_data;
+ /*! \brief acenstor information */
+ std::vector<int32_t> type_acenstors_data;
+ // NOTE: the indices in [index, index + num_reserved_slots) are
+ // reserved for the child-class of this type.
+ /*! \brief Total number of slots reserved for the type and its children. */
+ int32_t num_slots;
+ /*! \brief number of allocated child slots. */
+ int32_t allocated_slots;
+ /*! \brief Whether child can overflow. */
+ bool child_slots_can_overflow{true};
+
+ Entry(int32_t type_index, int32_t type_depth, std::string type_key, int32_t num_slots,
+ bool child_slots_can_overflow, const Entry* parent) {
+ // setup fields in the class
+ this->type_key_data = std::move(type_key);
+ this->num_slots = num_slots;
+ this->allocated_slots = 1;
+ this->child_slots_can_overflow = child_slots_can_overflow;
+ // set up type acenstors information
+ if (type_depth != 0) {
+ TVM_FFI_ICHECK_NOTNULL(parent);
+ TVM_FFI_ICHECK_EQ(type_depth, parent->type_depth + 1);
+ type_acenstors_data.resize(type_depth);
+ // copy over parent's type information
+ for (int32_t i = 0; i < parent->type_depth; ++i) {
+ type_acenstors_data[i] = parent->type_acenstors[i];
+ }
+ // set last type information to be parent
+ type_acenstors_data[parent->type_depth] = parent->type_index;
+ }
+ // initialize type info: no change to type_key and type_acenstors fields
+ // after this line
+ this->type_index = type_index;
+ this->type_depth = type_depth;
+ this->type_key = this->type_key_data.c_str();
+ this->type_key_hash = std::hash<std::string>()(this->type_key_data);
+ this->type_acenstors = type_acenstors_data.data();
}
- return child_tindex == parent_tindex;
- }
+ };
- int32_t GetOrAllocRuntimeTypeIndex(const std::string& skey, int32_t static_tindex,
- int32_t parent_tindex, int32_t num_child_slots,
- bool child_slots_can_overflow) {
- auto it = type_key2index_.find(skey);
+ int32_t GetOrAllocTypeIndex(std::string type_key, int32_t static_type_index, int32_t type_depth,
+ int32_t num_child_slots, bool child_slots_can_overflow,
+ int32_t parent_type_index) {
+ auto it = type_key2index_.find(type_key);
if (it != type_key2index_.end()) {
- return it->second;
+ return type_table_[it->second]->type_index;
}
- // try to allocate from parent's type table.
- TVM_FFI_ICHECK_LT(parent_tindex, type_table_.size())
- << " skey=" << skey << ", static_index=" << static_tindex;
- TypeInfo& pinfo = type_table_[parent_tindex];
- TVM_FFI_ICHECK_EQ(pinfo.index, parent_tindex);
+ // get parent's entry
+ Entry* parent = [&]() -> Entry* {
+ if (parent_type_index < 0) return nullptr;
+ // try to allocate from parent's type table.
+ TVM_FFI_ICHECK_LT(parent_type_index, type_table_.size())
+ << " type_key=" << type_key << ", static_index=" << static_type_index;
+ return type_table_[parent_type_index].get();
+ }();
+
+ // get allocated index
+ int32_t allocated_tindex = [&]() {
+ // Step 0: static allocation
+ if (static_type_index >= 0) {
+ TVM_FFI_ICHECK_LT(static_type_index, type_table_.size());
+ TVM_FFI_ICHECK(type_table_[static_type_index] == nullptr)
+ << "Conflicting static index " << static_type_index << " between "
+ << type_table_[static_type_index]->type_key << " and " << type_key;
+ return static_type_index;
+ }
+ TVM_FFI_ICHECK_NOTNULL(parent);
+ int num_slots = num_child_slots + 1;
+ if (parent->allocated_slots + num_slots <= parent->num_slots) {
+ // allocate the slot from parent's reserved pool
+ int32_t allocated_tindex = parent->type_index + parent->allocated_slots;
+ // update parent's state
+ parent->allocated_slots += num_slots;
+ return allocated_tindex;
+ }
+ // Step 2: allocate from overflow
+ TVM_FFI_ICHECK(parent->child_slots_can_overflow)
+ << "Reach maximum number of sub-classes for " << parent->type_key;
+ // allocate new entries.
+ int32_t allocated_tindex = type_counter_;
+ type_counter_ += num_slots;
+ TVM_FFI_ICHECK_LE(type_table_.size(), type_counter_);
+ type_table_.reserve(type_counter_);
+ // resize type table
+ while (static_cast<int32_t>(type_table_.size()) < type_counter_) {
+ type_table_.emplace_back(nullptr);
+ }
+ return allocated_tindex;
+ }();
// if parent cannot overflow, then this class cannot.
- if (!pinfo.child_slots_can_overflow) {
+ if (parent != nullptr && !(parent->child_slots_can_overflow)) {
child_slots_can_overflow = false;
}
-
// total number of slots include the type itself.
- int32_t num_slots = num_child_slots + 1;
- int32_t allocated_tindex;
-
- if (static_tindex > 0) {
- // statically assigned type
- allocated_tindex = static_tindex;
- TVM_FFI_ICHECK_LT(static_tindex, type_table_.size());
- TVM_FFI_ICHECK_EQ(type_table_[allocated_tindex].allocated_slots, 0U)
- << "Conflicting static index " << static_tindex << " between "
- << type_table_[allocated_tindex].name << " and " << skey;
- } else if (pinfo.allocated_slots + num_slots <= pinfo.num_slots) {
- // allocate the slot from parent's reserved pool
- allocated_tindex = parent_tindex + pinfo.allocated_slots;
- // update parent's state
- pinfo.allocated_slots += num_slots;
- } else {
- TVM_FFI_ICHECK(pinfo.child_slots_can_overflow)
- << "Reach maximum number of sub-classes for " << pinfo.name;
- // allocate new entries.
- allocated_tindex = type_counter_;
- type_counter_ += num_slots;
- TVM_FFI_ICHECK_LE(type_table_.size(), type_counter_);
- type_table_.resize(type_counter_, TypeInfo());
+
+ if (parent != nullptr) {
+ TVM_FFI_ICHECK_GT(allocated_tindex, parent->type_index);
}
- TVM_FFI_ICHECK_GT(allocated_tindex, parent_tindex);
- // initialize the slot.
- type_table_[allocated_tindex].index = allocated_tindex;
- type_table_[allocated_tindex].parent_index = parent_tindex;
- type_table_[allocated_tindex].num_slots = num_slots;
- type_table_[allocated_tindex].allocated_slots = 1;
- type_table_[allocated_tindex].child_slots_can_overflow = child_slots_can_overflow;
- type_table_[allocated_tindex].name = skey;
- type_table_[allocated_tindex].name_hash = std::hash<std::string>()(skey);
+
+ type_table_[allocated_tindex] =
+ std::make_unique<Entry>(allocated_tindex, type_depth, type_key, num_child_slots + 1,
+ child_slots_can_overflow, parent);
// update the key2index mapping.
- type_key2index_[skey] = allocated_tindex;
+ type_key2index_[type_key] = allocated_tindex;
return allocated_tindex;
}
- const std::string& TypeIndex2Key(int32_t tindex) {
- if (tindex != 0) {
- // always return the right type key for root
- // for non-root type nodes, allocated slots should not equal 0
- TVM_FFI_ICHECK(tindex < static_cast<int32_t>(type_table_.size()) &&
- type_table_[tindex].allocated_slots != 0)
- << "Unknown type index " << tindex;
- }
- return type_table_[tindex].name;
- }
-
- size_t TypeIndex2KeyHash(int32_t tindex) {
- TVM_FFI_ICHECK(tindex < static_cast<int32_t>(type_table_.size()) &&
- type_table_[tindex].allocated_slots != 0)
- << "Unknown type index " << tindex;
- return type_table_[tindex].name_hash;
+ int32_t TypeKey2Index(const std::string& type_key) {
+ auto it = type_key2index_.find(type_key);
+ TVM_FFI_ICHECK(it != type_key2index_.end()) << "Cannot find type " << type_key;
+ return it->second;
}
- int32_t TypeKey2Index(const std::string& skey) {
- auto it = type_key2index_.find(skey);
- TVM_FFI_ICHECK(it != type_key2index_.end()) << "Cannot find type " << skey;
- return it->second;
+ const TypeInfo* GetTypeInfo(int32_t type_index) {
+ const TypeInfo* info = nullptr;
+ if (type_index >= 0 && static_cast<size_t>(type_index) < type_table_.size()) {
+ info = type_table_[type_index].get();
+ }
+ TVM_FFI_ICHECK(info != nullptr) << "Cannot find type info for type_index=" << type_index;
+ return info;
}
void Dump(int min_children_count) {
std::vector<int> num_children(type_table_.size(), 0);
// reverse accumulation so we can get total counts in a bottom-up manner.
for (auto it = type_table_.rbegin(); it != type_table_.rend(); ++it) {
- if (it->index != 0) {
- num_children[it->parent_index] += num_children[it->index] + 1;
+ const Entry* ptr = it->get();
+ if (ptr != nullptr && ptr->type_depth != 0) {
+ int parent_index = ptr->type_acenstors[ptr->type_depth - 1];
+ num_children[parent_index] += num_children[ptr->type_index] + 1;
}
}
- for (const auto& info : type_table_) {
- if (info.index != 0 && num_children[info.index] >= min_children_count) {
- std::cerr << '[' << info.index << "] " << info.name
- << "\tparent=" << type_table_[info.parent_index].name
- << "\tnum_child_slots=" << info.num_slots - 1
- << "\tnum_children=" << num_children[info.index] << std::endl;
+ for (const auto& ptr : type_table_) {
+ if (ptr != nullptr && num_children[ptr->type_index] >= min_children_count) {
+ std::cerr << '[' << ptr->type_index << "]\t" << ptr->type_key;
+ if (ptr->type_depth != 0) {
+ int32_t parent_index = ptr->type_acenstors[ptr->type_depth - 1];
+ std::cerr << "\tparent=" << type_table_[parent_index]->type_key;
+ } else {
+ std::cerr << "\tparent=root";
+ }
+ std::cerr << "\tnum_child_slots=" << ptr->num_slots - 1
+ << "\tnum_children=" << num_children[ptr->type_index] << std::endl;
}
}
}
- static TypeContext* Global() {
- static TypeContext inst;
+ static TypeTable* Global() {
+ static TypeTable inst;
return &inst;
}
private:
- TypeContext() {
- type_table_.resize(TypeIndex::kTVMFFIDynObjectBegin, TypeInfo());
- type_table_[0].name = "runtime.Object";
+ TypeTable() {
+ type_table_.reserve(TypeIndex::kTVMFFIDynObjectBegin);
+ for (int32_t i = 0; i < TypeIndex::kTVMFFIDynObjectBegin; ++i) {
+ type_table_.emplace_back(nullptr);
+ }
+ // initialize the entry for object
+ this->GetOrAllocTypeIndex(Object::_type_key, Object::_type_index, Object::_type_depth,
+ Object::_type_child_slots, Object::_type_child_slots_can_overflow,
+ -1);
}
int32_t type_counter_{TypeIndex::kTVMFFIDynObjectBegin};
- std::vector<TypeInfo> type_table_;
+ std::vector<std::unique_ptr<Entry>> type_table_;
std::unordered_map<std::string, int32_t> type_key2index_;
};
namespace details {
-int32_t ObjectGetOrAllocTypeIndex(const char* type_key, int32_t static_tindex,
- int32_t parent_tindex, int32_t type_child_slots,
- bool type_child_slots_can_overflow) {
- return tvm::ffi::TypeContext::Global()->GetOrAllocRuntimeTypeIndex(
- type_key, static_tindex, parent_tindex, type_child_slots, type_child_slots_can_overflow != 0);
+int32_t ObjectGetOrAllocTypeIndex(const char* type_key, int32_t static_type_index,
+ int32_t type_depth, int32_t num_child_slots,
+ bool child_slots_can_overflow, int32_t parent_index) {
+ return tvm::ffi::TypeTable::Global()->GetOrAllocTypeIndex(type_key, static_type_index, type_depth,
+ num_child_slots,
+ child_slots_can_overflow, parent_index);
}
-bool ObjectDerivedFrom(int32_t child_type_index, int32_t parent_type_index) {
- return static_cast<int>(
- tvm::ffi::TypeContext::Global()->DerivedFrom(child_type_index, parent_type_index));
+const TypeInfo* ObjectGetTypeInfo(int32_t type_index) {
+ return tvm::ffi::TypeTable::Global()->GetTypeInfo(type_index);
}
} // namespace details
} // namespace ffi
diff --git a/ffi/tests/example/test_error.cc b/ffi/tests/example/test_error.cc
index 4f208b6999..015551c953 100644
--- a/ffi/tests/example/test_error.cc
+++ b/ffi/tests/example/test_error.cc
@@ -33,7 +33,6 @@ TEST(CheckError, Traceback) {
TVM_FFI_ICHECK_GT(2, 3);
} catch (const Error& error) {
EXPECT_EQ(error->kind, "InternalError");
- std::cout << error.what();
std::string what = error.what();
EXPECT_NE(what.find("line"), std::string::npos);
EXPECT_NE(what.find("2 > 3"), std::string::npos);
diff --git a/ffi/tests/example/test_object.cc b/ffi/tests/example/test_object.cc
index 959246fda0..4395fccf52 100644
--- a/ffi/tests/example/test_object.cc
+++ b/ffi/tests/example/test_object.cc
@@ -6,11 +6,55 @@ namespace {
using namespace tvm::ffi;
-class IntObj : public Object {
+class NumberObj : public Object {
+ public:
+ // declare as one slot, with float as overflow
+ static constexpr uint32_t _type_child_slots = 1;
+ static constexpr const char* _type_key = "test.Number";
+ TVM_FFI_DECLARE_BASE_OBJECT_INFO(NumberObj, Object);
+};
+
+class Number : public ObjectRef {
+ public:
+ TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS(Number, ObjectRef, NumberObj);
+};
+
+class IntObj : public NumberObj {
public:
int64_t value;
IntObj(int64_t value) : value(value) {}
+
+ static constexpr const char* _type_key = "test.Int";
+ TVM_FFI_DECLARE_FINAL_OBJECT_INFO(IntObj, NumberObj);
+};
+
+class Int : public Number {
+ public:
+ explicit Int(int64_t value) {
+ data_ = make_object<IntObj>(value);
+ }
+
+ TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS(Int, Number, IntObj);
+};
+
+class FloatObj : public NumberObj {
+ public:
+ double value;
+
+ FloatObj(double value) : value(value) {}
+
+ static constexpr const char* _type_key = "test.Float";
+ TVM_FFI_DECLARE_FINAL_OBJECT_INFO(FloatObj, NumberObj);
+};
+
+class Float : public Number {
+ public:
+ explicit Float(double value) {
+ data_ = make_object<FloatObj>(value);
+ }
+
+ TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS(Float, Number, FloatObj);
};
TEST(Object, RefCounter) {
@@ -32,4 +76,51 @@ TEST(Object, RefCounter) {
EXPECT_EQ(c->value, 11);
}
+TEST(Object, TypeInfo) {
+ const TypeInfo* info = tvm::ffi::details::ObjectGetTypeInfo(IntObj::RuntimeTypeIndex());
+ EXPECT_TRUE(info != nullptr);
+ EXPECT_EQ(info->type_index, IntObj::RuntimeTypeIndex());
+ EXPECT_EQ(info->type_depth, 2);
+ EXPECT_EQ(info->type_acenstors[0], Object::_type_index);
+ EXPECT_EQ(info->type_acenstors[1], NumberObj::_type_index);
+ EXPECT_GE(info->type_index, TypeIndex::kTVMFFIDynObjectBegin);
+}
+
+TEST(Object, InstanceCheck) {
+ ObjectPtr<Object> a = make_object<IntObj>(11);
+ ObjectPtr<Object> b = make_object<FloatObj>(11);
+
+ EXPECT_TRUE(a->IsInstance<Object>());
+ EXPECT_TRUE(a->IsInstance<NumberObj>());
+ EXPECT_TRUE(a->IsInstance<IntObj>());
+ EXPECT_TRUE(!a->IsInstance<FloatObj>());
+
+ EXPECT_TRUE(a->IsInstance<Object>());
+ EXPECT_TRUE(b->IsInstance<NumberObj>());
+ EXPECT_TRUE(!b->IsInstance<IntObj>());
+ EXPECT_TRUE(b->IsInstance<FloatObj>());
+}
+
+TEST(ObjectRef, as) {
+ ObjectRef a = Int(10);
+ ObjectRef b = Float(20);
+ // nullable object
+ ObjectRef c(nullptr);
+
+ EXPECT_TRUE(a.as<IntObj>() != nullptr);
+ EXPECT_TRUE(a.as<FloatObj>() == nullptr);
+ EXPECT_TRUE(a.as<NumberObj>() != nullptr);
+
+ EXPECT_TRUE(b.as<IntObj>() == nullptr);
+ EXPECT_TRUE(b.as<FloatObj>() != nullptr);
+ EXPECT_TRUE(b.as<NumberObj>() != nullptr);
+
+ EXPECT_TRUE(c.as<IntObj>() == nullptr);
+ EXPECT_TRUE(c.as<FloatObj>() == nullptr);
+ EXPECT_TRUE(c.as<NumberObj>() == nullptr);
+
+ EXPECT_EQ(a.as<IntObj>()->value, 10);
+ EXPECT_EQ(b.as<FloatObj>()->value, 20);
+}
+
} // namespace
(tvm) 22/38: [REFACTOR] Migration starting pt
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 969b2192b3abca8abb457c0f1b38cb2cc6cf80b9
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 08:40:15 2025 -0400
[REFACTOR] Migration starting pt
---
CMakeLists.txt | 3 ++
cmake/modules/Logging.cmake | 87 --------------------------------------------
ffi/CMakeLists.txt | 34 ++++++++---------
ffi/tests/cpp/CMakeLists.txt | 2 +-
4 files changed, 21 insertions(+), 105 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a86bc4cc33..5509408d95 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -131,6 +131,7 @@ include_directories(SYSTEM ${DMLC_PATH})
include_directories(SYSTEM ${RANG_PATH})
include_directories(SYSTEM ${COMPILER_RT_PATH})
include_directories(SYSTEM ${PICOJSON_PATH})
+add_subdirectory(ffi)
# initial variables
set(TVM_LINKER_LIBS "")
@@ -611,6 +612,8 @@ endif()
target_link_libraries(tvm PRIVATE ${TVM_RUNTIME_LINKER_LIBS})
target_link_libraries(tvm_runtime PRIVATE ${TVM_RUNTIME_LINKER_LIBS})
+target_link_directories(tvm PRIVATE tvm_ffi)
+target_link_directories(tvm_runtime PRIVATE tvm_ffi)
if(BUILD_FOR_HEXAGON AND DEFINED USE_HEXAGON_GTEST AND EXISTS ${USE_HEXAGON_GTEST})
include(FetchContent)
diff --git a/cmake/modules/Logging.cmake b/cmake/modules/Logging.cmake
index ae4b561186..2b092f665e 100644
--- a/cmake/modules/Logging.cmake
+++ b/cmake/modules/Logging.cmake
@@ -27,90 +27,3 @@ if(USE_CUSTOM_LOGGING)
target_compile_definitions(tvm PUBLIC TVM_LOG_CUSTOMIZE=1)
target_compile_definitions(tvm_runtime PUBLIC TVM_LOG_CUSTOMIZE=1)
endif()
-
-add_library(libbacktrace STATIC IMPORTED)
-
-set(LIBBACKTRACE_INCLUDE_DIR NOTFOUND)
-set(LIBBACKTRACE_STATIC_LIBRARY NOTFOUND)
-set(LIBBACKTRACE_FOUND NO)
-
-macro(__find_libbacktrace)
- find_path(LIBBACKTRACE_INCLUDE_DIR backtrace.h)
- find_library(LIBBACKTRACE_STATIC_LIBRARY libbacktrace.a)
- find_package_handle_standard_args(LIBBACKTRACE REQUIRED_VARS
- LIBBACKTRACE_STATIC_LIBRARY LIBBACKTRACE_INCLUDE_DIR)
-endmacro()
-
-macro(__find_libbacktrace_from PATH)
- find_path(LIBBACKTRACE_INCLUDE_DIR backtrace.h
- PATHS ${PATH}
- PATH_SUFFIXES include
- NO_CMAKE_SYSTEM_PATH
- NO_SYSTEM_ENVIRONMENT_PATH
- )
- find_library(LIBBACKTRACE_STATIC_LIBRARY libbacktrace.a
- PATHS ${PATH}
- PATH_SUFFIXES lib
- NO_CMAKE_SYSTEM_PATH
- NO_SYSTEM_ENVIRONMENT_PATH
- )
- find_package_handle_standard_args(LIBBACKTRACE REQUIRED_VARS
- LIBBACKTRACE_STATIC_LIBRARY LIBBACKTRACE_INCLUDE_DIR)
-endmacro()
-
-macro(__compile_libbacktrace)
- message(STATUS "Building libbacktrace from 3rdparty/libbacktrace")
- include(cmake/libs/Libbacktrace.cmake)
- add_dependencies(libbacktrace project_libbacktrace)
- set(LIBBACKTRACE_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/libbacktrace/include)
- set(LIBBACKTRACE_STATIC_LIBRARY ${CMAKE_CURRENT_BINARY_DIR}/libbacktrace/lib/libbacktrace.a)
- add_dependencies(tvm_runtime_objs libbacktrace)
- set(LIBBACKTRACE_FOUND YES)
-endmacro()
-
-if(USE_LIBBACKTRACE STREQUAL "AUTO")
- __find_libbacktrace()
- if(NOT LIBBACKTRACE_FOUND AND (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Darwin"))
- __compile_libbacktrace()
- endif()
-elseif(USE_LIBBACKTRACE STREQUAL "COMPILE")
- __compile_libbacktrace()
-elseif("${USE_LIBBACKTRACE}" MATCHES ${IS_TRUE_PATTERN})
- __find_libbacktrace()
- if(NOT LIBBACKTRACE_FOUND)
- message(SEND_ERROR "libbacktrace not found. (Set USE_LIBBACKTRACE to COMPILE if you want to build with the submodule at 3rdparty/libbacktrace.)")
- endif()
-elseif("${USE_LIBBACKTRACE}" MATCHES ${IS_FALSE_PATTERN})
-else()
- # Treat USE_LIBBACKTRACE as path to libbacktrace
- message(STATUS "Using libbacktrace from ${USE_LIBBACKTRACE}")
- __find_libbacktrace_from(${USE_LIBBACKTRACE})
- if(NOT LIBBACKTRACE_FOUND)
- message(SEND_ERROR "libbacktrace not found from ${USE_LIBBACKTRACE}.")
- endif()
-endif()
-
-set_property(TARGET libbacktrace
- PROPERTY IMPORTED_LOCATION ${LIBBACKTRACE_STATIC_LIBRARY})
-
-function(configure_backtrace TARGET)
- if(LIBBACKTRACE_FOUND)
- get_target_property(target_type ${TARGET} TYPE)
- if(target_type MATCHES "EXECUTABLE|(STATIC|SHARED|MODULE)_LIBRARY")
- target_link_libraries(${TARGET} PRIVATE libbacktrace)
- endif()
- target_include_directories(${TARGET} PRIVATE ${LIBBACKTRACE_INCLUDE_DIR})
- target_compile_definitions(${TARGET} PRIVATE TVM_USE_LIBBACKTRACE=1)
- else()
- target_compile_definitions(${TARGET} PRIVATE TVM_USE_LIBBACKTRACE=0)
- endif()
-
- if(BACKTRACE_ON_SEGFAULT)
- target_compile_definitions(${TARGET} PRIVATE TVM_BACKTRACE_ON_SEGFAULT)
- endif()
-endfunction()
-
-configure_backtrace(tvm)
-configure_backtrace(tvm_runtime)
-configure_backtrace(tvm_objs)
-configure_backtrace(tvm_runtime_objs)
diff --git a/ffi/CMakeLists.txt b/ffi/CMakeLists.txt
index b327c6667a..1aa05110a7 100644
--- a/ffi/CMakeLists.txt
+++ b/ffi/CMakeLists.txt
@@ -15,7 +15,7 @@ option(TVM_FFI_BUILD_REGISTRY
targets linked against `tvm_ffi` will allow objects that comes with non-pre-defined type indices, \
as well as getting full stacktrace during debugging. \
so that the object hierarchy could expand without limitation. \
- This will require the downstream targets to link against target `tvm_ffi_registry` to be effective."
+ This will require the downstream targets to link against target `tvm_ffi` to be effective."
OFF
)
option(TVM_FFI_USE_LIBBRACKTRACE "Enable libbacktrace" ON)
@@ -38,16 +38,16 @@ target_link_libraries(tvm_ffi INTERFACE dlpack_header)
target_compile_features(tvm_ffi INTERFACE cxx_std_17)
target_include_directories(tvm_ffi INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
-########## Target: `tvm_ffi_registry` ##########
+########## Target: `tvm_ffi` ##########
if (TVM_FFI_BUILD_REGISTRY)
- add_library(tvm_ffi_registry_objs OBJECT
+ add_library(tvm_ffi_objs OBJECT
"${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/traceback.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/object.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/function.cc"
)
set_target_properties(
- tvm_ffi_registry_objs PROPERTIES
+ tvm_ffi_objs PROPERTIES
POSITION_INDEPENDENT_CODE ON
CXX_STANDARD 17
CXX_EXTENSIONS OFF
@@ -56,34 +56,34 @@ if (TVM_FFI_BUILD_REGISTRY)
VISIBILITY_INLINES_HIDDEN ON
PREFIX "lib"
)
- add_cxx_warning(tvm_ffi_registry_objs)
- target_link_libraries(tvm_ffi_registry_objs PRIVATE dlpack_header)
- target_include_directories(tvm_ffi_registry_objs PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
+ add_cxx_warning(tvm_ffi_objs)
+ target_link_libraries(tvm_ffi_objs PRIVATE dlpack_header)
+ target_include_directories(tvm_ffi_objs PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
if (TVM_FFI_USE_LIBBRACKTRACE)
message(STATUS "Setting C++ macro TVM_FFI_USE_LIBBRACKTRACE - 1")
- target_compile_definitions(tvm_ffi_registry_objs PRIVATE TVM_FFI_USE_LIBBRACKTRACE=1)
+ target_compile_definitions(tvm_ffi_objs PRIVATE TVM_FFI_USE_LIBBRACKTRACE=1)
else()
message(STATUS "Setting C++ macro TVM_FFI_USE_LIBBRACKTRACE - 0")
- target_compile_definitions(tvm_ffi_registry_objs PRIVATE TVM_FFI_USE_LIBBRACKTRACE=0)
+ target_compile_definitions(tvm_ffi_objs PRIVATE TVM_FFI_USE_LIBBRACKTRACE=0)
endif()
if (TVM_FFI_BACKTRACE_ON_SEGFAULT)
message(STATUS "Setting C++ macro TVM_FFI_BACKTRACE_ON_SEGFAULT - 1")
- target_compile_definitions(tvm_ffi_registry_objs PRIVATE TVM_FFI_BACKTRACE_ON_SEGFAULT=1)
+ target_compile_definitions(tvm_ffi_objs PRIVATE TVM_FFI_BACKTRACE_ON_SEGFAULT=1)
else()
message(STATUS "Setting C++ macro TVM_FFI_BACKTRACE_ON_SEGFAULT - 0")
- target_compile_definitions(tvm_ffi_registry_objs PRIVATE TVM_FFI_BACKTRACE_ON_SEGFAULT=0)
+ target_compile_definitions(tvm_ffi_objs PRIVATE TVM_FFI_BACKTRACE_ON_SEGFAULT=0)
endif()
- add_target_from_obj(tvm_ffi_registry tvm_ffi_registry_objs)
+ add_target_from_obj(tvm_ffi tvm_ffi_objs)
if (TARGET libbacktrace)
- target_link_libraries(tvm_ffi_registry_objs PRIVATE libbacktrace)
- target_link_libraries(tvm_ffi_registry_shared PRIVATE libbacktrace)
- target_link_libraries(tvm_ffi_registry_static PRIVATE libbacktrace)
+ target_link_libraries(tvm_ffi_objs PRIVATE libbacktrace)
+ target_link_libraries(tvm_ffi_shared PRIVATE libbacktrace)
+ target_link_libraries(tvm_ffi_static PRIVATE libbacktrace)
endif ()
- install(TARGETS tvm_ffi_registry_static DESTINATION "lib/")
- install(TARGETS tvm_ffi_registry_shared DESTINATION "lib/")
+ install(TARGETS tvm_ffi_static DESTINATION "lib/")
+ install(TARGETS tvm_ffi_shared DESTINATION "lib/")
endif (TVM_FFI_BUILD_REGISTRY)
########## Adding tests ##########
diff --git a/ffi/tests/cpp/CMakeLists.txt b/ffi/tests/cpp/CMakeLists.txt
index 62543756b9..54dae7177d 100644
--- a/ffi/tests/cpp/CMakeLists.txt
+++ b/ffi/tests/cpp/CMakeLists.txt
@@ -19,7 +19,7 @@ add_sanitizer_address(tvm_ffi_tests)
target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi)
if (TVM_FFI_BUILD_REGISTRY)
- target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi_registry_shared)
+ target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi_shared)
endif()
add_googletest(tvm_ffi_tests)
(tvm) 34/38: clang format
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit eb0199921adfefe55d43b952e7d136bb4da071ec
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 18:14:45 2025 -0400
clang format
---
ffi/include/tvm/ffi/cast.h | 2 +-
ffi/include/tvm/ffi/object.h | 33 +++++++++++------------
ffi/include/tvm/ffi/reflection.h | 10 +++----
include/tvm/runtime/container/array.h | 4 +--
include/tvm/runtime/container/map.h | 4 +--
include/tvm/runtime/memory.h | 2 +-
include/tvm/runtime/object.h | 49 +++++++++++++++++------------------
include/tvm/runtime/packed_func.h | 6 +++--
src/runtime/debug_compile.cc | 12 ++++-----
src/runtime/ndarray.cc | 11 ++++----
src/runtime/object.cc | 3 ++-
src/runtime/relax_vm/rnn_state.cc | 3 +--
12 files changed, 68 insertions(+), 71 deletions(-)
diff --git a/ffi/include/tvm/ffi/cast.h b/ffi/include/tvm/ffi/cast.h
index c7365a2f45..3d97d3a217 100644
--- a/ffi/include/tvm/ffi/cast.h
+++ b/ffi/include/tvm/ffi/cast.h
@@ -62,7 +62,7 @@ inline RefType GetRef(const ObjectType* ptr) {
template <typename BaseType, typename ObjectType>
inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr) {
static_assert(std::is_base_of<BaseType, ObjectType>::value,
- "Can only cast to the ref of same container type");
+ "Can only cast to the ref of same container type");
return details::ObjectUnsafe::ObjectPtrFromUnowned<BaseType>(ptr);
}
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index b2189fb7f5..b9e5df51d1 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -160,7 +160,7 @@ class Object {
*/
static int32_t _GetOrAllocRuntimeTypeIndex() { return TypeIndex::kTVMFFIObject; }
- private:
+ private:
/*! \brief increase reference count */
void IncRef() { details::AtomicIncrementRelaxed(&(header_.ref_counter)); }
@@ -426,21 +426,20 @@ struct ObjectPtrEqual {
}
};
-
// If dynamic type is enabled, we still need to register the runtime type of parent
-#define TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType) \
- static constexpr int32_t _type_depth = ParentType::_type_depth + 1; \
- static int32_t _GetOrAllocRuntimeTypeIndex() { \
+#define TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType) \
+ static constexpr int32_t _type_depth = ParentType::_type_depth + 1; \
+ static int32_t _GetOrAllocRuntimeTypeIndex() { \
static_assert(!ParentType::_type_final, "ParentType marked as final"); \
static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
TypeName::_type_child_slots < ParentType::_type_child_slots, \
"Need to set _type_child_slots when parent specifies it."); \
- static int32_t tindex = TVMFFIGetOrAllocTypeIndex( \
- TypeName::_type_key, TypeName::_type_index, TypeName::_type_depth, \
- TypeName::_type_child_slots, TypeName::_type_child_slots_can_overflow, \
- ParentType::_GetOrAllocRuntimeTypeIndex()); \
- return tindex; \
- } \
+ static int32_t tindex = TVMFFIGetOrAllocTypeIndex( \
+ TypeName::_type_key, TypeName::_type_index, TypeName::_type_depth, \
+ TypeName::_type_child_slots, TypeName::_type_child_slots_can_overflow, \
+ ParentType::_GetOrAllocRuntimeTypeIndex()); \
+ return tindex; \
+ } \
static inline int32_t _register_type_index = _GetOrAllocRuntimeTypeIndex()
/*!
@@ -452,7 +451,6 @@ struct ObjectPtrEqual {
static int32_t RuntimeTypeIndex() { return TypeName::_type_index; } \
TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType);
-
/*
* \brief Define object reference methods.
* \param TypeName The object type name
@@ -505,14 +503,13 @@ struct ObjectPtrEqual {
* \param ObjectName The type name of the object.
*/
#define TVM_FFI_DEFINE_MUTABLE_NOTNULLABLE_OBJECT_REF_METHODS(TypeName, ParentType, ObjectName) \
- explicit TypeName(::tvm::ffi::ObjectPtr<::tvm::ffi::Object> n) : ParentType(n) {} \
- TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName); \
- ObjectName* operator->() const { return static_cast<ObjectName*>(data_.get()); } \
- ObjectName* get() const { return operator->(); } \
- static constexpr bool _type_is_nullable = false; \
+ explicit TypeName(::tvm::ffi::ObjectPtr<::tvm::ffi::Object> n) : ParentType(n) {} \
+ TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName); \
+ ObjectName* operator->() const { return static_cast<ObjectName*>(data_.get()); } \
+ ObjectName* get() const { return operator->(); } \
+ static constexpr bool _type_is_nullable = false; \
using ContainerType = ObjectName;
-
namespace details {
template <typename TargetType>
TVM_FFI_INLINE bool IsObjectInstance(int32_t object_type_index) {
diff --git a/ffi/include/tvm/ffi/reflection.h b/ffi/include/tvm/ffi/reflection.h
index d154522b9f..b29b3d919f 100644
--- a/ffi/include/tvm/ffi/reflection.h
+++ b/ffi/include/tvm/ffi/reflection.h
@@ -155,11 +155,11 @@ class ReflectionFieldGetter {
*/
#define TVM_FFI_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType) \
static constexpr int32_t _type_depth = ParentType::_type_depth + 1; \
- static int32_t _GetOrAllocRuntimeTypeIndex() { \
- static_assert(!ParentType::_type_final, "ParentType marked as final"); \
- static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
- TypeName::_type_child_slots < ParentType::_type_child_slots, \
- "Need to set _type_child_slots when parent specifies it."); \
+ static int32_t _GetOrAllocRuntimeTypeIndex() { \
+ static_assert(!ParentType::_type_final, "ParentType marked as final"); \
+ static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
+ TypeName::_type_child_slots < ParentType::_type_child_slots, \
+ "Need to set _type_child_slots when parent specifies it."); \
static int32_t tindex = TVMFFIGetOrAllocTypeIndex( \
TypeName::_type_key, -1, TypeName::_type_depth, TypeName::_type_child_slots, \
TypeName::_type_child_slots_can_overflow, ParentType::_GetOrAllocRuntimeTypeIndex()); \
diff --git a/include/tvm/runtime/container/array.h b/include/tvm/runtime/container/array.h
index f4e825f44f..d601309dbe 100644
--- a/include/tvm/runtime/container/array.h
+++ b/include/tvm/runtime/container/array.h
@@ -905,13 +905,13 @@ inline Array<T> Concat(Array<T> lhs, const Array<T>& rhs) {
} // namespace runtime
-namespace ffi{
+namespace ffi {
// Specialize make_object<ArrayNode> to make sure it is correct.
template <>
inline ObjectPtr<tvm::runtime::ArrayNode> make_object() {
return tvm::runtime::ArrayNode::Empty();
}
-}
+} // namespace ffi
// expose the functions to the root namespace.
using runtime::Array;
using runtime::ArrayNode;
diff --git a/include/tvm/runtime/container/map.h b/include/tvm/runtime/container/map.h
index a24d61d4de..7daaf238c1 100644
--- a/include/tvm/runtime/container/map.h
+++ b/include/tvm/runtime/container/map.h
@@ -1251,8 +1251,8 @@ inline void MapNode::InsertMaybeReHash(const KVType& kv, ObjectPtr<Object>* map)
}
}
-//template <>
-//inline ObjectPtr<MapNode> make_object<>() = delete;
+// template <>
+// inline ObjectPtr<MapNode> make_object<>() = delete;
#endif
diff --git a/include/tvm/runtime/memory.h b/include/tvm/runtime/memory.h
index 8429650421..04b4a1e7d4 100644
--- a/include/tvm/runtime/memory.h
+++ b/include/tvm/runtime/memory.h
@@ -28,8 +28,8 @@
namespace tvm {
namespace runtime {
-using tvm::ffi::make_object;
using tvm::ffi::make_inplace_array_object;
+using tvm::ffi::make_object;
} // namespace runtime
} // namespace tvm
diff --git a/include/tvm/runtime/object.h b/include/tvm/runtime/object.h
index d956eebf1f..1edfae6abb 100644
--- a/include/tvm/runtime/object.h
+++ b/include/tvm/runtime/object.h
@@ -23,15 +23,15 @@
#ifndef TVM_RUNTIME_OBJECT_H_
#define TVM_RUNTIME_OBJECT_H_
-#include <tvm/ffi/object.h>
#include <tvm/ffi/cast.h>
+#include <tvm/ffi/object.h>
#include <tvm/ffi/reflection.h>
#include <tvm/runtime/c_runtime_api.h>
namespace tvm {
namespace runtime {
-template<typename T>
+template <typename T>
class Optional;
using tvm::ffi::Object;
@@ -40,9 +40,8 @@ using tvm::ffi::ObjectPtrEqual;
using tvm::ffi::ObjectPtrHash;
using tvm::ffi::Downcast;
-using tvm::ffi::GetRef;
using tvm::ffi::GetObjectPtr;
-
+using tvm::ffi::GetRef;
/*!
* \brief Namespace for the list of type index.
@@ -173,19 +172,19 @@ class ObjectRef : public tvm::ffi::ObjectRef {
*
* \endcode
*/
-#define TVM_DEFINE_OBJECT_REF_COW_METHOD(ObjectName) \
-static_assert(ObjectName::_type_final, \
- "TVM's CopyOnWrite may only be used for " \
- "Object types that are declared as final, " \
- "using the TVM_DECLARE_FINAL_OBJECT_INFO macro."); \
-ObjectName* CopyOnWrite() { \
- ICHECK(data_ != nullptr); \
- if (!data_.unique()) { \
- auto n = make_object<ObjectName>(*(operator->())); \
- ObjectPtr<Object>(std::move(n)).swap(data_); \
- } \
- return static_cast<ObjectName*>(data_.get()); \
-}
+#define TVM_DEFINE_OBJECT_REF_COW_METHOD(ObjectName) \
+ static_assert(ObjectName::_type_final, \
+ "TVM's CopyOnWrite may only be used for " \
+ "Object types that are declared as final, " \
+ "using the TVM_DECLARE_FINAL_OBJECT_INFO macro."); \
+ ObjectName* CopyOnWrite() { \
+ ICHECK(data_ != nullptr); \
+ if (!data_.unique()) { \
+ auto n = make_object<ObjectName>(*(operator->())); \
+ ObjectPtr<Object>(std::move(n)).swap(data_); \
+ } \
+ return static_cast<ObjectName*>(data_.get()); \
+ }
/*
* \brief Define object reference methods.
@@ -194,13 +193,12 @@ ObjectName* CopyOnWrite() { \
* \param ObjectName The type name of the object.
*/
#define TVM_DEFINE_OBJECT_REF_METHODS_WITHOUT_DEFAULT_CONSTRUCTOR(TypeName, ParentType, \
- ObjectName) \
-explicit TypeName(::tvm::runtime::ObjectPtr<::tvm::runtime::Object> n) : ParentType(n) {} \
-TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName); \
-const ObjectName* operator->() const { return static_cast<const ObjectName*>(data_.get()); } \
-const ObjectName* get() const { return operator->(); } \
-using ContainerType = ObjectName;
-
+ ObjectName) \
+ explicit TypeName(::tvm::runtime::ObjectPtr<::tvm::runtime::Object> n) : ParentType(n) {} \
+ TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName); \
+ const ObjectName* operator->() const { return static_cast<const ObjectName*>(data_.get()); } \
+ const ObjectName* get() const { return operator->(); } \
+ using ContainerType = ObjectName;
#define TVM_DECLARE_BASE_OBJECT_INFO TVM_FFI_DECLARE_BASE_OBJECT_INFO
#define TVM_DECLARE_FINAL_OBJECT_INFO TVM_FFI_DECLARE_FINAL_OBJECT_INFO
@@ -208,7 +206,8 @@ using ContainerType = ObjectName;
#define TVM_DEFINE_MUTABLE_OBJECT_REF_METHODS TVM_DEFINE_MUTABLE_NULLABLE_OBJECT_REF_METHODS
#define TVM_DEFINE_OBJECT_REF_METHODS TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS
-#define TVM_DEFINE_MUTABLE_NOTNULLABLE_OBJECT_REF_METHODS TVM_FFI_DEFINE_MUTABLE_NOTNULLABLE_OBJECT_REF_METHODS
+#define TVM_DEFINE_MUTABLE_NOTNULLABLE_OBJECT_REF_METHODS \
+ TVM_FFI_DEFINE_MUTABLE_NOTNULLABLE_OBJECT_REF_METHODS
#define TVM_STR_CONCAT_(__x, __y) __x##__y
#define TVM_STR_CONCAT(__x, __y) TVM_STR_CONCAT_(__x, __y)
diff --git a/include/tvm/runtime/packed_func.h b/include/tvm/runtime/packed_func.h
index 912a15e976..d2141103f1 100644
--- a/include/tvm/runtime/packed_func.h
+++ b/include/tvm/runtime/packed_func.h
@@ -641,14 +641,16 @@ class TVMPODValue_ {
return Module(ObjectPtr<Object>(nullptr));
}
TVM_CHECK_TYPE_CODE(type_code_, kTVMModuleHandle);
- return Module(ffi::details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(static_cast<Object*>(value_.v_handle)));
+ return Module(ffi::details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(
+ static_cast<Object*>(value_.v_handle)));
}
operator PackedFunc() const {
if (type_code_ == kTVMNullptr) {
return PackedFunc(ObjectPtr<Object>(nullptr));
}
TVM_CHECK_TYPE_CODE(type_code_, kTVMPackedFuncHandle);
- return PackedFunc(ffi::details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(static_cast<Object*>(value_.v_handle)));
+ return PackedFunc(ffi::details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(
+ static_cast<Object*>(value_.v_handle)));
}
operator Device() const {
TVM_CHECK_TYPE_CODE(type_code_, kDLDevice);
diff --git a/src/runtime/debug_compile.cc b/src/runtime/debug_compile.cc
index 17f86d5f7f..96d220307a 100644
--- a/src/runtime/debug_compile.cc
+++ b/src/runtime/debug_compile.cc
@@ -21,16 +21,16 @@
* \file src/runtime/debug_compile.cc
* \brief File used for debug migration
*/
-#include <tvm/runtime/container/string.h>
-#include <tvm/runtime/container/optional.h>
+#include <tvm/ir/expr.h>
#include <tvm/runtime/container/array.h>
#include <tvm/runtime/container/map.h>
+#include <tvm/runtime/container/optional.h>
+#include <tvm/runtime/container/string.h>
#include <tvm/runtime/container/variant.h>
+#include <tvm/runtime/disco/disco_worker.h>
#include <tvm/runtime/ndarray.h>
#include <tvm/runtime/packed_func.h>
#include <tvm/runtime/registry.h>
-#include <tvm/runtime/disco/disco_worker.h>
-#include <tvm/ir/expr.h>
#include <tvm/tir/expr.h>
namespace tvm {
@@ -41,5 +41,5 @@ String Test() {
return value;
}
-}
-}
\ No newline at end of file
+} // namespace debug
+} // namespace tvm
\ No newline at end of file
diff --git a/src/runtime/ndarray.cc b/src/runtime/ndarray.cc
index 63087fda6f..67b30bb0d1 100644
--- a/src/runtime/ndarray.cc
+++ b/src/runtime/ndarray.cc
@@ -105,8 +105,7 @@ struct NDArray::Internal {
auto* ptr = static_cast<NDArray::Container*>(ptr_obj);
if (ptr->manager_ctx != nullptr) {
ffi::details::ObjectUnsafe::DecRefObjectHandle(
- static_cast<NDArray::Container*>(ptr->manager_ctx)
- );
+ static_cast<NDArray::Container*>(ptr->manager_ctx));
} else if (ptr->dl_tensor.data != nullptr) {
tvm::runtime::DeviceAPI::Get(ptr->dl_tensor.device)
->FreeDataSpace(ptr->dl_tensor.device, ptr->dl_tensor.data);
@@ -179,7 +178,8 @@ struct NDArray::Internal {
}
// Delete dlpack object.
static void NDArrayDLPackDeleter(DLManagedTensor* tensor) {
- ffi::details::ObjectUnsafe::DecRefObjectHandle(static_cast<NDArray::Container*>(tensor->manager_ctx));
+ ffi::details::ObjectUnsafe::DecRefObjectHandle(
+ static_cast<NDArray::Container*>(tensor->manager_ctx));
delete tensor;
}
};
@@ -368,9 +368,8 @@ void TVMNDArrayDLPackDeleter(DLManagedTensor* tensor) {
int TVMArrayGetTypeIndex(TVMArrayHandle handle, unsigned* out_tindex) {
API_BEGIN();
- *out_tindex = tvm::ffi::details::ObjectUnsafe::GetHeader(
- TVMArrayHandleToObjectHandle(handle)
- )->type_index;
+ *out_tindex =
+ tvm::ffi::details::ObjectUnsafe::GetHeader(TVMArrayHandleToObjectHandle(handle))->type_index;
API_END();
}
diff --git a/src/runtime/object.cc b/src/runtime/object.cc
index 9c4356ef4c..095eee5f5e 100644
--- a/src/runtime/object.cc
+++ b/src/runtime/object.cc
@@ -71,7 +71,8 @@ int TVMObjectDerivedFrom(uint32_t child_type_index, uint32_t parent_type_index,
const TVMFFITypeInfo* child_type_info = TVMFFIGetTypeInfo(child_type_index);
const TVMFFITypeInfo* parent_type_info = TVMFFIGetTypeInfo(parent_type_index);
return (child_type_info->type_depth > parent_type_info->type_depth &&
- child_type_info->type_acenstors[parent_type_info->type_depth] == static_cast<int32_t>(parent_type_index));
+ child_type_info->type_acenstors[parent_type_info->type_depth] ==
+ static_cast<int32_t>(parent_type_index));
}();
API_END();
}
diff --git a/src/runtime/relax_vm/rnn_state.cc b/src/runtime/relax_vm/rnn_state.cc
index c823cfbb98..0b26e93de4 100644
--- a/src/runtime/relax_vm/rnn_state.cc
+++ b/src/runtime/relax_vm/rnn_state.cc
@@ -454,8 +454,7 @@ class RNNStateImpObj : public RNNStateObj {
}
public:
-
- static constexpr const char* _type_key = "relax.vm.RNNStateImp";
+ static constexpr const char* _type_key = "relax.vm.RNNStateImp";
TVM_DECLARE_FINAL_OBJECT_INFO(RNNStateImpObj, RNNStateObj);
};
(tvm) 21/38: Fix ffi
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 238342a2009c7dc1afcc63b76cff74475244cbc9
Author: tqchen <ti...@gmail.com>
AuthorDate: Mon Mar 10 10:06:36 2025 -0400
Fix ffi
---
ffi/include/tvm/ffi/object.h | 6 ++----
ffi/include/tvm/ffi/reflection.h | 5 +++--
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 8173a362e2..a7afb6c12f 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -521,10 +521,8 @@ class ObjectUnsafe {
template <typename Class>
static TVM_FFI_INLINE int64_t GetObjectOffsetToSubclass() {
- return (
- reinterpret_cast<int64_t>(&(static_cast<Class*>(nullptr)->header_)) -
- reinterpret_cast<int64_t>(&(static_cast<Object*>(nullptr)->header_))
- );
+ return (reinterpret_cast<int64_t>(&(static_cast<Class*>(nullptr)->header_)) -
+ reinterpret_cast<int64_t>(&(static_cast<Object*>(nullptr)->header_)));
}
template <typename T>
diff --git a/ffi/include/tvm/ffi/reflection.h b/ffi/include/tvm/ffi/reflection.h
index 6da880ab1f..f3cc580853 100644
--- a/ffi/include/tvm/ffi/reflection.h
+++ b/ffi/include/tvm/ffi/reflection.h
@@ -52,8 +52,9 @@ struct Type2FieldStaticTypeIndex<T, std::enable_if_t<TypeTraits<T>::enabled>> {
* \returns The byteoffset
*/
template <typename Class, typename T>
-inline int64_t GetFieldByteOffsetToObject(T Class::* field_ptr) {
- int64_t field_offset_to_class = reinterpret_cast<int64_t>(&(static_cast<Class*>(nullptr)->*field_ptr));
+inline int64_t GetFieldByteOffsetToObject(T Class::*field_ptr) {
+ int64_t field_offset_to_class =
+ reinterpret_cast<int64_t>(&(static_cast<Class*>(nullptr)->*field_ptr));
return field_offset_to_class - details::ObjectUnsafe::GetObjectOffsetToSubclass<Class>();
}
(tvm) 31/38: pass object and runtime compile
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit fb62f9034d4b15a31c328f66070525d4d590f761
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 14:42:32 2025 -0400
pass object and runtime compile
---
ffi/include/tvm/ffi/function.h | 2 +-
src/runtime/object.cc | 211 ++---------------------------------------
src/runtime/object_internal.h | 10 +-
3 files changed, 15 insertions(+), 208 deletions(-)
diff --git a/ffi/include/tvm/ffi/function.h b/ffi/include/tvm/ffi/function.h
index 680758159a..7ba12c3056 100644
--- a/ffi/include/tvm/ffi/function.h
+++ b/ffi/include/tvm/ffi/function.h
@@ -74,7 +74,7 @@ namespace ffi {
if (ret_code == -2) { \
throw ::tvm::ffi::EnvErrorAlreadySet(); \
} \
- Any error_any; \
+ ::tvm::ffi::Any error_any; \
TVMFFIMoveFromLastError(reinterpret_cast<TVMFFIAny*>(&error_any)); \
if (std::optional<tvm::ffi::Error> error = error_any.TryAs<tvm::ffi::Error>()) { \
throw std::move(*error); \
diff --git a/src/runtime/object.cc b/src/runtime/object.cc
index 85ec4f0360..9c4356ef4c 100644
--- a/src/runtime/object.cc
+++ b/src/runtime/object.cc
@@ -37,211 +37,10 @@
namespace tvm {
namespace runtime {
-/*! \brief Type information */
-struct TypeInfo {
- /*! \brief The current index. */
- uint32_t index{0};
- /*! \brief Index of the parent in the type hierarchy */
- uint32_t parent_index{0};
- // NOTE: the indices in [index, index + num_reserved_slots) are
- // reserved for the child-class of this type.
- /*! \brief Total number of slots reserved for the type and its children. */
- uint32_t num_slots{0};
- /*! \brief number of allocated child slots. */
- uint32_t allocated_slots{0};
- /*! \brief Whether child can overflow. */
- bool child_slots_can_overflow{true};
- /*! \brief name of the type. */
- std::string name;
- /*! \brief hash of the name */
- size_t name_hash{0};
-};
-
-/*!
- * \brief Type context that manages the type hierarchy information.
- */
-class TypeContext {
- public:
- // NOTE: this is a relatively slow path for child checking
- // Most types are already checked by the fast-path via reserved slot checking.
- bool DerivedFrom(uint32_t child_tindex, uint32_t parent_tindex) {
- // invariance: child's type index is always bigger than its parent.
- if (child_tindex < parent_tindex) return false;
- if (child_tindex == parent_tindex) return true;
- {
- std::lock_guard<std::mutex> lock(mutex_);
- ICHECK_LT(child_tindex, type_table_.size());
- while (child_tindex > parent_tindex) {
- child_tindex = type_table_[child_tindex].parent_index;
- }
- }
- return child_tindex == parent_tindex;
- }
-
- uint32_t GetOrAllocRuntimeTypeIndex(const std::string& skey, uint32_t static_tindex,
- uint32_t parent_tindex, uint32_t num_child_slots,
- bool child_slots_can_overflow) {
- std::lock_guard<std::mutex> lock(mutex_);
- auto it = type_key2index_.find(skey);
- if (it != type_key2index_.end()) {
- return it->second;
- }
- // try to allocate from parent's type table.
- ICHECK_LT(parent_tindex, type_table_.size())
- << " skey=" << skey << ", static_index=" << static_tindex;
- TypeInfo& pinfo = type_table_[parent_tindex];
- ICHECK_EQ(pinfo.index, parent_tindex);
-
- // if parent cannot overflow, then this class cannot.
- if (!pinfo.child_slots_can_overflow) {
- child_slots_can_overflow = false;
- }
-
- // total number of slots include the type itself.
- uint32_t num_slots = num_child_slots + 1;
- uint32_t allocated_tindex;
-
- if (static_tindex != TypeIndex::kDynamic) {
- // statically assigned type
- VLOG(3) << "TypeIndex[" << static_tindex << "]: static: " << skey << ", parent "
- << type_table_[parent_tindex].name;
- allocated_tindex = static_tindex;
- ICHECK_LT(static_tindex, type_table_.size());
- ICHECK_EQ(type_table_[allocated_tindex].allocated_slots, 0U)
- << "Conflicting static index " << static_tindex << " between "
- << type_table_[allocated_tindex].name << " and " << skey;
- } else if (pinfo.allocated_slots + num_slots <= pinfo.num_slots) {
- // allocate the slot from parent's reserved pool
- allocated_tindex = parent_tindex + pinfo.allocated_slots;
- VLOG(3) << "TypeIndex[" << allocated_tindex << "]: dynamic: " << skey << ", parent "
- << type_table_[parent_tindex].name;
- // update parent's state
- pinfo.allocated_slots += num_slots;
- } else {
- VLOG(3) << "TypeIndex[" << type_counter_ << "]: dynamic (overflow): " << skey << ", parent "
- << type_table_[parent_tindex].name;
- ICHECK(pinfo.child_slots_can_overflow)
- << "Reach maximum number of sub-classes for " << pinfo.name;
- // allocate new entries.
- allocated_tindex = type_counter_;
- type_counter_ += num_slots;
- ICHECK_LE(type_table_.size(), type_counter_);
- type_table_.resize(type_counter_, TypeInfo());
- }
- ICHECK_GT(allocated_tindex, parent_tindex);
- // initialize the slot.
- type_table_[allocated_tindex].index = allocated_tindex;
- type_table_[allocated_tindex].parent_index = parent_tindex;
- type_table_[allocated_tindex].num_slots = num_slots;
- type_table_[allocated_tindex].allocated_slots = 1;
- type_table_[allocated_tindex].child_slots_can_overflow = child_slots_can_overflow;
- type_table_[allocated_tindex].name = skey;
- type_table_[allocated_tindex].name_hash = std::hash<std::string>()(skey);
- // update the key2index mapping.
- type_key2index_[skey] = allocated_tindex;
- return allocated_tindex;
- }
-
- std::string TypeIndex2Key(uint32_t tindex) {
- std::lock_guard<std::mutex> lock(mutex_);
- if (tindex != 0) {
- // always return the right type key for root
- // for non-root type nodes, allocated slots should not equal 0
- ICHECK(tindex < type_table_.size() && type_table_[tindex].allocated_slots != 0)
- << "Unknown type index " << tindex;
- }
- return type_table_[tindex].name;
- }
-
- size_t TypeIndex2KeyHash(uint32_t tindex) {
- std::lock_guard<std::mutex> lock(mutex_);
- ICHECK(tindex < type_table_.size() && type_table_[tindex].allocated_slots != 0)
- << "Unknown type index " << tindex;
- return type_table_[tindex].name_hash;
- }
-
- uint32_t TypeKey2Index(const std::string& skey) {
- auto it = type_key2index_.find(skey);
- ICHECK(it != type_key2index_.end())
- << "Cannot find type " << skey
- << ". Did you forget to register the node by TVM_REGISTER_NODE_TYPE ?";
- return it->second;
- }
-
- void Dump(int min_children_count) {
- std::vector<int> num_children(type_table_.size(), 0);
- // expected child slots compute the expected slots
- // based on the current child slot setting
- std::vector<int> expected_child_slots(type_table_.size(), 0);
- // reverse accumulation so we can get total counts in a bottom-up manner.
- for (auto it = type_table_.rbegin(); it != type_table_.rend(); ++it) {
- if (it->index != 0) {
- num_children[it->parent_index] += num_children[it->index] + 1;
- if (static_cast<uint32_t>(expected_child_slots[it->index] + 1) < it->num_slots) {
- expected_child_slots[it->index] = it->num_slots - 1;
- }
- expected_child_slots[it->parent_index] += expected_child_slots[it->index] + 1;
- }
- }
-
- for (const auto& info : type_table_) {
- if (info.index != 0 && num_children[info.index] >= min_children_count) {
- std::cerr << '[' << info.index << "] " << info.name
- << "\tparent=" << type_table_[info.parent_index].name
- << "\tnum_child_slots=" << info.num_slots - 1
- << "\tnum_children=" << num_children[info.index]
- << "\texpected_child_slots=" << expected_child_slots[info.index] << std::endl;
- }
- }
- }
-
- static TypeContext* Global() {
- static TypeContext inst;
- return &inst;
- }
-
- private:
- TypeContext() {
- type_table_.resize(TypeIndex::kStaticIndexEnd, TypeInfo());
- type_table_[0].name = "runtime.Object";
- }
- // mutex to avoid registration from multiple threads.
- std::mutex mutex_;
- std::atomic<uint32_t> type_counter_{TypeIndex::kStaticIndexEnd};
- std::vector<TypeInfo> type_table_;
- std::unordered_map<std::string, uint32_t> type_key2index_;
-};
-
-uint32_t Object::GetOrAllocRuntimeTypeIndex(const std::string& key, uint32_t static_tindex,
- uint32_t parent_tindex, uint32_t num_child_slots,
- bool child_slots_can_overflow) {
- return TypeContext::Global()->GetOrAllocRuntimeTypeIndex(
- key, static_tindex, parent_tindex, num_child_slots, child_slots_can_overflow);
-}
-
-bool Object::DerivedFrom(uint32_t parent_tindex) const {
- return TypeContext::Global()->DerivedFrom(this->type_index_, parent_tindex);
-}
-
-std::string Object::TypeIndex2Key(uint32_t tindex) {
- return TypeContext::Global()->TypeIndex2Key(tindex);
-}
-
-size_t Object::TypeIndex2KeyHash(uint32_t tindex) {
- return TypeContext::Global()->TypeIndex2KeyHash(tindex);
-}
-
-uint32_t Object::TypeKey2Index(const std::string& key) {
- return TypeContext::Global()->TypeKey2Index(key);
-}
-
TVM_REGISTER_GLOBAL("runtime.ObjectPtrHash").set_body_typed([](ObjectRef obj) {
return static_cast<int64_t>(ObjectPtrHash()(obj));
});
-TVM_REGISTER_GLOBAL("runtime.DumpTypeTable").set_body_typed([](int min_child_count) {
- TypeContext::Global()->Dump(min_child_count);
-});
} // namespace runtime
} // namespace tvm
@@ -266,8 +65,14 @@ int TVMObjectFree(TVMObjectHandle obj) {
int TVMObjectDerivedFrom(uint32_t child_type_index, uint32_t parent_type_index, int* is_derived) {
API_BEGIN();
- *is_derived =
- tvm::runtime::TypeContext::Global()->DerivedFrom(child_type_index, parent_type_index);
+ *is_derived = [&]() {
+ if (child_type_index == parent_type_index) return true;
+ if (child_type_index < parent_type_index) return false;
+ const TVMFFITypeInfo* child_type_info = TVMFFIGetTypeInfo(child_type_index);
+ const TVMFFITypeInfo* parent_type_info = TVMFFIGetTypeInfo(parent_type_index);
+ return (child_type_info->type_depth > parent_type_info->type_depth &&
+ child_type_info->type_acenstors[parent_type_info->type_depth] == static_cast<int32_t>(parent_type_index));
+ }();
API_END();
}
diff --git a/src/runtime/object_internal.h b/src/runtime/object_internal.h
index 3cde330f59..a369895434 100644
--- a/src/runtime/object_internal.h
+++ b/src/runtime/object_internal.h
@@ -64,16 +64,18 @@ class ObjectInternal {
* \param type_index The type index of interest.
* \return The derivation checking result.
*/
- static bool DerivedFrom(const Object* obj, uint32_t type_index) {
- return obj->DerivedFrom(type_index);
- }
+ // static bool DerivedFrom(const Object* obj, uint32_t type_index) {
+ // return obj->DerivedFrom(type_index);
+ // }
/*!
* \brief Expose TypeKey2Index
* \param type_key The original type key.
* \return the corresponding index.
*/
static uint32_t ObjectTypeKey2Index(const std::string& type_key) {
- return Object::TypeKey2Index(type_key);
+ int32_t type_index;
+ TVM_FFI_CHECK_SAFE_CALL(TVMFFITypeKey2Index(type_key.c_str(), &type_index));
+ return static_cast<uint32_t>(type_index);
}
/*!
* \brief Convert ModuleHandle to module node pointer.
(tvm) 29/38: pass basic disco session compile
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit d263641f104b346282edef5675c9482d0ff252e6
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 12:51:09 2025 -0400
pass basic disco session compile
---
ffi/include/tvm/ffi/object.h | 49 +++++++++++++++++++++++++++++
include/tvm/runtime/container/string.h | 1 +
include/tvm/runtime/disco/session.h | 3 +-
include/tvm/runtime/memory/memory_manager.h | 1 -
include/tvm/runtime/object.h | 6 ++++
include/tvm/runtime/packed_func.h | 5 +--
src/runtime/debug_compile.cc | 1 +
src/runtime/disco/protocol.h | 4 +--
src/runtime/minrpc/rpc_reference.h | 2 +-
9 files changed, 65 insertions(+), 7 deletions(-)
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 8fbd6705fd..01dca88878 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -114,6 +114,9 @@ class Object {
return details::IsObjectInstance<TargetType>(header_.type_index);
}
+ /*! \return The internal runtime type index of the object. */
+ int32_t type_index() const { return header_.type_index; }
+
/*!
* \return the type key of the object.
* \note this operation is expensive, can be used for error reporting.
@@ -124,6 +127,16 @@ class Object {
return type_info->type_key;
}
+ /*!
+ * \brief Get the type key of the corresponding index from runtime.
+ * \param tindex The type index.
+ * \return the result.
+ */
+ static std::string TypeIndex2Key(int32_t tindex) {
+ const TypeInfo* type_info = TVMFFIGetTypeInfo(tindex);
+ return type_info->type_key;
+ }
+
// Information about the object
static constexpr const char* _type_key = "object.Object";
@@ -464,6 +477,7 @@ struct ObjectPtrEqual {
static int32_t RuntimeTypeIndex() { return TypeName::_type_index; } \
TVM_FFI_OBJECT_STATIC_DEFS(TypeName, ParentType)
+
/*
* \brief Define object reference methods.
* \param TypeName The object type name
@@ -493,6 +507,37 @@ struct ObjectPtrEqual {
static constexpr bool _type_is_nullable = false; \
using ContainerType = ObjectName
+/*
+ * \brief Define object reference methods of whose content is mutable.
+ * \param TypeName The object type name
+ * \param ParentType The parent type of the objectref
+ * \param ObjectName The type name of the object.
+ * \note We recommend making objects immutable when possible.
+ * This macro is only reserved for objects that stores runtime states.
+ */
+#define TVM_DEFINE_MUTABLE_NULLABLE_OBJECT_REF_METHODS(TypeName, ParentType, ObjectName) \
+ TypeName() = default; \
+ TVM_FFI_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName); \
+ explicit TypeName(::tvm::runtime::ObjectPtr<::tvm::runtime::Object> n) : ParentType(n) {} \
+ ObjectName* operator->() const { return static_cast<ObjectName*>(data_.get()); } \
+ using ContainerType = ObjectName;
+
+/*
+ * \brief Define object reference methods that is both not nullable and mutable.
+ *
+ * \param TypeName The object type name
+ * \param ParentType The parent type of the objectref
+ * \param ObjectName The type name of the object.
+ */
+#define TVM_FFI_DEFINE_MUTABLE_NOTNULLABLE_OBJECT_REF_METHODS(TypeName, ParentType, ObjectName) \
+ explicit TypeName(::tvm::ffi::ObjectPtr<::tvm::ffi::Object> n) : ParentType(n) {} \
+ TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName); \
+ ObjectName* operator->() const { return static_cast<ObjectName*>(data_.get()); } \
+ ObjectName* get() const { return operator->(); } \
+ static constexpr bool _type_is_nullable = false; \
+ using ContainerType = ObjectName;
+
+
namespace details {
template <typename TargetType>
@@ -587,6 +632,10 @@ class ObjectUnsafe {
reinterpret_cast<Object*>(src->v_obj)->IncRef();
}
+ static TVM_FFI_INLINE Object* GetRawObjectPtrFromObjectRef(const ObjectRef& src) {
+ return src.data_.data_;
+ }
+
static TVM_FFI_INLINE TVMFFIObject* GetTVMFFIObjectPtrFromObjectRef(const ObjectRef& src) {
return GetHeader(src.data_.data_);
}
diff --git a/include/tvm/runtime/container/string.h b/include/tvm/runtime/container/string.h
index 661d95bba2..4ee48d9c95 100644
--- a/include/tvm/runtime/container/string.h
+++ b/include/tvm/runtime/container/string.h
@@ -59,6 +59,7 @@ class StringObj : public Object {
static constexpr const uint32_t _type_index = TypeIndex::kRuntimeString;
static constexpr const char* _type_key = "runtime.String";
+ static const constexpr bool _type_final = true;
TVM_FFI_DECLARE_STATIC_OBJECT_INFO(StringObj, Object);
private:
diff --git a/include/tvm/runtime/disco/session.h b/include/tvm/runtime/disco/session.h
index 9c34f8a2af..1c3af22d83 100644
--- a/include/tvm/runtime/disco/session.h
+++ b/include/tvm/runtime/disco/session.h
@@ -148,7 +148,8 @@ class DRefObj : public Object {
static constexpr const char* _type_key = "runtime.disco.DRef";
static constexpr const uint32_t _type_index = TypeIndex::kRuntimeDiscoDRef;
- TVM_DECLARE_FINAL_OBJECT_INFO(DRefObj, Object);
+ static const constexpr bool _type_final = true;
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(DRefObj, Object);
/*! \brief The id of the register */
int64_t reg_id;
diff --git a/include/tvm/runtime/memory/memory_manager.h b/include/tvm/runtime/memory/memory_manager.h
index ab1e6b5c9f..754d7b4fe3 100644
--- a/include/tvm/runtime/memory/memory_manager.h
+++ b/include/tvm/runtime/memory/memory_manager.h
@@ -182,7 +182,6 @@ class StorageObj : public Object {
}
}
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const char* _type_key = "vm.Storage";
TVM_DECLARE_FINAL_OBJECT_INFO(StorageObj, Object);
};
diff --git a/include/tvm/runtime/object.h b/include/tvm/runtime/object.h
index 046f0d3948..2f89d11965 100644
--- a/include/tvm/runtime/object.h
+++ b/include/tvm/runtime/object.h
@@ -143,14 +143,20 @@ class ObjectRef : public tvm::ffi::ObjectRef {
TypeName& operator=(const TypeName& other) = default; \
TypeName& operator=(TypeName&& other) = default;
+
+#define TVM_DECLARE_BASE_OBJECT_INFO TVM_FFI_DECLARE_BASE_OBJECT_INFO
#define TVM_DECLARE_FINAL_OBJECT_INFO TVM_FFI_DECLARE_FINAL_OBJECT_INFO
#define TVM_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS TVM_FFI_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS
+#define TVM_DEFINE_MUTABLE_OBJECT_REF_METHODS TVM_DEFINE_MUTABLE_NULLABLE_OBJECT_REF_METHODS
#define TVM_DEFINE_OBJECT_REF_METHODS TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS
+#define TVM_DEFINE_MUTABLE_NOTNULLABLE_OBJECT_REF_METHODS TVM_FFI_DEFINE_MUTABLE_NOTNULLABLE_OBJECT_REF_METHODS
#define TVM_STR_CONCAT_(__x, __y) __x##__y
#define TVM_STR_CONCAT(__x, __y) TVM_STR_CONCAT_(__x, __y)
+// Object register type is now a nop
+#define TVM_REGISTER_OBJECT_TYPE(x)
} // namespace runtime
} // namespace tvm
diff --git a/include/tvm/runtime/packed_func.h b/include/tvm/runtime/packed_func.h
index 804fd3b98e..0a2461515e 100644
--- a/include/tvm/runtime/packed_func.h
+++ b/include/tvm/runtime/packed_func.h
@@ -2096,7 +2096,7 @@ inline void TVMArgsSetter::SetObject(size_t i, T&& value) const {
return;
}
- Object* ptr = value.data_.data_;
+ Object* ptr = details::ObjectUnsafe::GetRawObjectPtrFromObjectRef(value);
if constexpr (std::is_base_of_v<NDArray::ContainerType, ContainerType> ||
std::is_base_of_v<ContainerType, NDArray::ContainerType>) {
if (std::is_base_of_v<NDArray::ContainerType, ContainerType> ||
@@ -2186,7 +2186,8 @@ inline void TVMArgsSetter::SetObject(size_t i, T&& value) const {
values_[i].v_handle = const_cast<Object**>(&(value.data_.data_));
type_codes_[i] = kTVMObjectRValueRefArg;
} else {
- values_[i].v_handle = value.data_.data_;
+ // value.data_.data_;
+ values_[i].v_handle = details::ObjectUnsafe::GetTVMFFIObjectPtrFromObjectRef(value);
type_codes_[i] = kTVMObjectHandle;
}
}
diff --git a/src/runtime/debug_compile.cc b/src/runtime/debug_compile.cc
index 81f1edffc7..4233035abd 100644
--- a/src/runtime/debug_compile.cc
+++ b/src/runtime/debug_compile.cc
@@ -29,6 +29,7 @@
#include <tvm/runtime/ndarray.h>
#include <tvm/runtime/packed_func.h>
#include <tvm/runtime/registry.h>
+#include <tvm/runtime/disco/disco_worker.h>
namespace tvm {
namespace debug {
diff --git a/src/runtime/disco/protocol.h b/src/runtime/disco/protocol.h
index 50a6b091af..cc741fdbb1 100644
--- a/src/runtime/disco/protocol.h
+++ b/src/runtime/disco/protocol.h
@@ -155,7 +155,7 @@ inline void DiscoProtocol<SubClassType>::WriteObject(Object* obj) {
self->template Write<uint64_t>(shape->size);
self->template WriteArray<ShapeTupleObj::index_type>(shape->data, shape->size);
} else if (obj->IsInstance<DiscoDebugObject>()) {
- self->template Write<uint32_t>(TypeIndex::kRoot);
+ self->template Write<uint32_t>(0);
std::string str = static_cast<DiscoDebugObject*>(obj)->SaveToStr();
self->template Write<uint64_t>(str.size());
self->template WriteArray<char>(str.data(), str.size());
@@ -188,7 +188,7 @@ inline void DiscoProtocol<SubClassType>::ReadObject(int* tcode, TVMValue* value)
std::vector<ShapeTupleObj::index_type> data(ndim);
self->template ReadArray<ShapeTupleObj::index_type>(data.data(), ndim);
result = ShapeTuple(std::move(data));
- } else if (type_index == TypeIndex::kRoot) {
+ } else if (type_index == 0) {
uint64_t size = 0;
self->template Read<uint64_t>(&size);
std::string data(size, '\0');
diff --git a/src/runtime/minrpc/rpc_reference.h b/src/runtime/minrpc/rpc_reference.h
index 13c1fa4b38..bb9c5d3c86 100644
--- a/src/runtime/minrpc/rpc_reference.h
+++ b/src/runtime/minrpc/rpc_reference.h
@@ -28,7 +28,7 @@ namespace tvm {
namespace runtime {
// Forward declare TVM Object to use `Object*` in RPC protocol.
-class Object;
+// class Object;
/*! \brief The current RPC procotol version. */
constexpr const char* kRPCProtocolVer = "0.8.0";
(tvm) 15/38: [FFI] Formalize SafeCall handling
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 36f8f8f2b0bd1a05c83aae6b2a09e5eade75797c
Author: tqchen <ti...@gmail.com>
AuthorDate: Sat Sep 14 17:28:11 2024 -0400
[FFI] Formalize SafeCall handling
Move all dll calls into c api.
Leverage the TLS for safecall exception propagation
so it works better for codegen.
---
ffi/CMakeLists.txt | 12 +-
ffi/include/tvm/ffi/base_details.h | 18 +
ffi/include/tvm/ffi/c_api.h | 111 ++-
ffi/include/tvm/ffi/container/optional.h | 2 +
ffi/include/tvm/ffi/error.h | 38 +-
ffi/include/tvm/ffi/function.h | 78 ++-
ffi/include/tvm/ffi/object.h | 57 +-
ffi/include/tvm/ffi/string.h | 2 +-
ffi/include/tvm/ffi/type_traits.h | 6 +-
ffi/scripts/run_tests.sh | 5 +-
ffi/src/ffi/function.cc | 63 ++
ffi/src/ffi/object.cc | 25 +-
ffi/src/ffi/traceback.cc | 20 +-
ffi/tests/{example => backup_cpp}/CMakeLists.txt | 5 +-
ffi/tests/{cpp => backup_cpp}/test_any.cc | 157 ++---
ffi/tests/{cpp => backup_cpp}/test_any_view.cc | 163 ++---
ffi/tests/{cpp => backup_cpp}/test_dict.cc | 21 +-
ffi/tests/{cpp => backup_cpp}/test_func.cc | 58 +-
ffi/tests/{cpp => backup_cpp}/test_list.cc | 69 +-
ffi/tests/{cpp => backup_cpp}/test_ref.cc | 63 +-
ffi/tests/{cpp => backup_cpp}/test_str.cc | 12 +-
ffi/tests/{cpp => backup_cpp}/test_type_dyn.cc | 15 +-
ffi/tests/{cpp => backup_cpp}/test_type_static.cc | 17 +-
ffi/tests/cpp/CMakeLists.txt | 4 +-
ffi/tests/cpp/test_any.cc | 794 +++++-----------------
ffi/tests/{example => cpp}/test_array.cc | 0
ffi/tests/{example => cpp}/test_c_ffi_abi.cc | 0
ffi/tests/{example => cpp}/test_error.cc | 0
ffi/tests/{example => cpp}/test_function.cc | 0
ffi/tests/{example => cpp}/test_map.cc | 0
ffi/tests/{example => cpp}/test_object.cc | 2 +-
ffi/tests/{example => cpp}/test_optional.cc | 0
ffi/tests/{example => cpp}/test_string.cc | 0
ffi/tests/{example => cpp}/testing_object.h | 0
ffi/tests/example/test_any.cc | 166 -----
35 files changed, 739 insertions(+), 1244 deletions(-)
diff --git a/ffi/CMakeLists.txt b/ffi/CMakeLists.txt
index 89429d253b..b327c6667a 100644
--- a/ffi/CMakeLists.txt
+++ b/ffi/CMakeLists.txt
@@ -20,7 +20,6 @@ option(TVM_FFI_BUILD_REGISTRY
)
option(TVM_FFI_USE_LIBBRACKTRACE "Enable libbacktrace" ON)
option(TVM_FFI_BACKTRACE_ON_SEGFAULT "Set signal handler to print traceback on segfault" ON)
-option(TVM_FFI_ALLOW_DYN_TYPE "Wehthert to allow dynamic features" ON)
include(cmake/Utils/CxxWarning.cmake)
include(cmake/Utils/Sanitizer.cmake)
@@ -39,20 +38,13 @@ target_link_libraries(tvm_ffi INTERFACE dlpack_header)
target_compile_features(tvm_ffi INTERFACE cxx_std_17)
target_include_directories(tvm_ffi INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
-if (TVM_FFI_ALLOW_DYN_TYPE)
- message(STATUS "Setting C++ macro TVM_FFI_ALLOW_DYN_TYPE - 1")
- target_compile_definitions(tvm_ffi INTERFACE TVM_FFI_ALLOW_DYN_TYPE=1)
-else()
- message(STATUS "Setting C++ macro TVM_FFI_ALLOW_DYN_TYPES - 0")
- target_compile_definitions(tvm_ffi INTERFACE TVM_FFI_ALLOW_DYN_TYPE=0)
-endif()
-
########## Target: `tvm_ffi_registry` ##########
if (TVM_FFI_BUILD_REGISTRY)
add_library(tvm_ffi_registry_objs OBJECT
"${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/traceback.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/object.cc"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/function.cc"
)
set_target_properties(
tvm_ffi_registry_objs PROPERTIES
@@ -101,6 +93,6 @@ if (${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME})
enable_testing()
message(STATUS "Enable Testing")
include(cmake/Utils/AddGoogleTest.cmake)
- add_subdirectory(tests/example/)
+ add_subdirectory(tests/cpp/)
endif()
endif ()
diff --git a/ffi/include/tvm/ffi/base_details.h b/ffi/include/tvm/ffi/base_details.h
index cf825fdea7..a779eb89ea 100644
--- a/ffi/include/tvm/ffi/base_details.h
+++ b/ffi/include/tvm/ffi/base_details.h
@@ -82,6 +82,23 @@
TypeName& operator=(const TypeName& other) = default; \
TypeName& operator=(TypeName&& other) = default;
+/**
+ * \brief marks the begining of a C call that logs exception
+ */
+#define TVM_FFI_LOG_EXCEPTION_CALL_BEGIN() \
+ try { \
+ (void)0
+
+/*!
+ * \brief Marks the end of a C call that logs exception
+ */
+#define TVM_FFI_LOG_EXCEPTION_CALL_END(Name) \
+ } \
+ catch (const std::exception& err) { \
+ std::cout << "Exception caught during " << #Name << ":\n" << err.what() << std::endl; \
+ throw err; \
+ }
+
namespace tvm {
namespace ffi {
namespace details {
@@ -221,6 +238,7 @@ TVM_FFI_INLINE uint64_t StableHashBytes(const char* data, size_t size) {
}
return result;
}
+
} // namespace details
} // namespace ffi
} // namespace tvm
diff --git a/ffi/include/tvm/ffi/c_api.h b/ffi/include/tvm/ffi/c_api.h
index 20579bbe06..e6d3449ac3 100644
--- a/ffi/include/tvm/ffi/c_api.h
+++ b/ffi/include/tvm/ffi/c_api.h
@@ -20,8 +20,6 @@
/*
* \file tvm/ffi/c_api.h
* \brief This file defines the C convention of the FFI convention
- *
- * Only use the APIs when TVM_FFI_ALLOW_DYN_TYPE is set to true
*/
#ifndef TVM_FFI_C_API_H_
#define TVM_FFI_C_API_H_
@@ -29,14 +27,6 @@
#include <dlpack/dlpack.h>
#include <stdint.h>
-/*!
- * \brief Macro defines whether we enable dynamic runtime features.
- * \note Turning this one would mean that we need to link tvm_ffi_registry_shared
- */
-#ifndef TVM_FFI_ALLOW_DYN_TYPE
-#define TVM_FFI_ALLOW_DYN_TYPE 1
-#endif
-
#if !defined(TVM_FFI_DLL) && defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
#define TVM_FFI_DLL EMSCRIPTEN_KEEPALIVE
@@ -92,9 +82,12 @@ typedef enum {
} TVMFFITypeIndex;
#endif
+/*! \brief Handle to Object from C API's pov */
+typedef void* TVMFFIObjectHandle;
+
/*!
* \brief C-based type of all FFI object types that allocates on heap.
- * \note TVMFFIObject and TVMFFIAny share the common type_index_ header
+ * \note TVMFFIObject and TVMFFIAny share the common type_index header
*/
typedef struct TVMFFIObject {
/*!
@@ -164,6 +157,9 @@ typedef struct {
const int32_t* type_acenstors;
} TVMFFITypeInfo;
+//------------------------------------------------------------
+// Section: User APIs to interact with the FFI
+//------------------------------------------------------------
/*!
* \brief Type that defines C-style safe call convention
*
@@ -176,11 +172,102 @@ typedef struct {
*
* \return The call return 0 if call is successful.
* It returns non-zero value if there is an error.
- * When error happens, the exception object will be stored in result.
+ *
+ * Possible return error of the API functions:
+ * * 0: success
+ * * -1: error happens, can be retrieved by TVMFFIGetLastError
+ * * -2: a frontend error occurred and recorded in the frontend.
+ *
+ * \note We decided to leverage TVMFFIGetLastError and TVMFFISetLastError
+ * for C function error propagation. This design choice, while
+ * introducing a dependency for TLS runtime, simplifies error
+ * propgation in chains of calls in compiler codegen.
+ * As we do not need to propagate error through argument but simply
+ * set them in the runtime environment.
*/
typedef int (*TVMFFISafeCallType)(void* func, int32_t num_args, const TVMFFIAny* args,
TVMFFIAny* result);
+/*!
+ * \brief Free an object handle by decreasing reference
+ * \param obj The object handle.
+ * \note Internally we decrease the reference counter of the object.
+ * The object will be freed when every reference to the object are removed.
+ * \return 0 when success, nonzero when failure happens
+ */
+TVM_FFI_DLL int TVMFFIObjectFree(TVMFFIObjectHandle obj);
+
+/*!
+ * \brief Move the last error from the environment to result.
+ *
+ * \param result The result error.
+ *
+ * \note This function clears the error stored in the TLS.
+ */
+TVM_FFI_DLL void TVMFFIMoveFromLastError(TVMFFIAny* result);
+
+/*!
+ * \brief Set the last error in TLS, which can be fetched by TVMFFIGetLastError.
+ *
+ * \param error_view The error in format of any view.
+ * It can be an object, or simply a raw c_str.
+ * \note
+ */
+TVM_FFI_DLL void TVMFFISetLastError(const TVMFFIAny* error_view);
+
+//------------------------------------------------------------
+// Section: Backend noexcept functions for internal use
+//
+// These functions are used internally and do not throw error
+// instead the error will be logged and abort the process
+// These are function are being called in startup or exit time
+// so exception handling do not apply
+//------------------------------------------------------------
+/*!
+ * \brief Get stack traceback in a string.
+ * \param filaname The current file name.
+ * \param func The current function
+ * \param lineno The current line number
+ * \return The traceback string
+ *
+ * \note filename func and lino are only used as a backup info, most cases they are not needed.
+ * The return value is set to const char* to be more compatible across dll boundaries.
+ */
+TVM_FFI_DLL const char* TVMFFITraceback(const char* filename, const char* func, int lineno);
+
+/*!
+ * \brief Initialize the type info during runtime.
+ *
+ * When the function is first time called for a type,
+ * it will register the type to the type table in the runtime.
+ *
+ * If the static_tindex is non-negative, the function will
+ * allocate a runtime type index.
+ * Otherwise, we will populate the type table and return the static index.
+ *
+ * \param type_key The type key.
+ * \param static_type_index Static type index if any, can be -1, which means this is a dynamic index
+ * \param num_child_slots Number of slots reserved for its children.
+ * \param child_slots_can_overflow Whether to allow child to overflow the slots.
+ * \param parent_type_index Parent type index, pass in -1 if it is root.
+ * \param result The output type index
+ *
+ * \return 0 if success, -1 if error occured
+ */
+TVM_FFI_DLL int32_t TVMFFIGetOrAllocTypeIndex(const char* type_key, int32_t static_type_index,
+ int32_t type_depth, int32_t num_child_slots,
+ int32_t child_slots_can_overflow,
+ int32_t parent_type_index);
+/*!
+ * \brief Get dynamic type info by type index.
+ *
+ * \param type_index The type index
+ * \param result The output type information
+ *
+ * \return 0 when success, nonzero when failure happens
+ */
+TVM_FFI_DLL const TVMFFITypeInfo* TVMFFIGetTypeInfo(int32_t type_index);
+
#ifdef __cplusplus
} // TVM_FFI_EXTERN_C
#endif
diff --git a/ffi/include/tvm/ffi/container/optional.h b/ffi/include/tvm/ffi/container/optional.h
index 5f08db0a5b..a35d4f8ae2 100644
--- a/ffi/include/tvm/ffi/container/optional.h
+++ b/ffi/include/tvm/ffi/container/optional.h
@@ -28,6 +28,8 @@
#include <tvm/ffi/object.h>
#include <optional>
+#include <string>
+#include <utility>
namespace tvm {
namespace ffi {
diff --git a/ffi/include/tvm/ffi/error.h b/ffi/include/tvm/ffi/error.h
index 1f703f1fac..aeed40f243 100644
--- a/ffi/include/tvm/ffi/error.h
+++ b/ffi/include/tvm/ffi/error.h
@@ -53,6 +53,17 @@
namespace tvm {
namespace ffi {
+/*!
+ * \brief Error already set in frontend env.
+ *
+ * This error can be thrown by EnvCheckSignals to indicate
+ * that there is an error set in the frontend environment(e.g.
+ * python interpreter). The TVM FFI should catch this error
+ * and return a proper code tell the frontend caller about
+ * this fact.
+ */
+struct EnvErrorAlreadySet : public std::exception {};
+
/*!
* \brief Error object class.
*/
@@ -128,33 +139,8 @@ class ErrorBuilder {
bool log_before_throw_;
};
-// Code section that depends on dynamic components that requires linking
-#if TVM_FFI_ALLOW_DYN_TYPE
-/*!
- * \brief Get stack traceback in a string.
- * \param filaname The current file name.
- * \param func The current function
- * \param lineno The current line number
- * \return The traceback string
- *
- * \note filename func and lino are only used as a backup info, most cases they are not needed.
- * The return value is set to const char* to be more compatible across dll boundaries.
- */
-TVM_FFI_DLL const char* Traceback(const char* filename, const char* func, int lineno);
// define traceback here as call into traceback function
-#define TVM_FFI_TRACEBACK_HERE ::tvm::ffi::details::Traceback(__FILE__, TVM_FFI_FUNC_SIG, __LINE__)
-
-#else
-// simple traceback when allow dyn type is set to false
-inline std::string SimpleTraceback(const char* filename, const char* func, int lineno) {
- std::ostringstream traceback;
- // python style backtrace
- traceback << " " << filename << ", line " << lineno << ", in " << func << '\n';
- return traceback.str();
-}
-#define TVM_FFI_TRACEBACK_HERE \
- ::tvm::ffi::details::SimpleTraceback(__FILE__, TVM_FFI_FUNC_SIG, __LINE__)
-#endif // TVM_FFI_ALLOW_DYN_TYPE
+#define TVM_FFI_TRACEBACK_HERE TVMFFITraceback(__FILE__, TVM_FFI_FUNC_SIG, __LINE__)
} // namespace details
/*!
diff --git a/ffi/include/tvm/ffi/function.h b/ffi/include/tvm/ffi/function.h
index e2adee03c0..cb4cb2b937 100644
--- a/ffi/include/tvm/ffi/function.h
+++ b/ffi/include/tvm/ffi/function.h
@@ -29,12 +29,61 @@
#include <tvm/ffi/error.h>
#include <tvm/ffi/function_details.h>
+#include <functional>
#include <string>
#include <utility>
namespace tvm {
namespace ffi {
+/**
+ * Helper macro to construct a safe call
+ *
+ * \brief Marks the begining of the safe call that catches exception explicitly
+ *
+ */
+#define TVM_FFI_SAFE_CALL_BEGIN() \
+ try { \
+ (void)0
+
+/*!
+ * \brief Marks the end of safe call.
+ */
+#define TVM_FFI_SAFE_CALL_END() \
+ return 0; \
+ } \
+ catch (const ::tvm::ffi::Error& err) { \
+ ::tvm::ffi::AnyView error_as_any(err); \
+ TVMFFISetLastError(reinterpret_cast<TVMFFIAny*>(&error_as_any)); \
+ return -1; \
+ } \
+ catch (const ::tvm::ffi::EnvErrorAlreadySet&) { \
+ return -2; \
+ } \
+ catch (const std::exception& err) { \
+ ::tvm::ffi::Any error_as_any(tvm::ffi::Error("InternalError", err.what(), "")); \
+ TVMFFISetLastError(reinterpret_cast<TVMFFIAny*>(&error_as_any)); \
+ return -1; \
+ } \
+ TVM_FFI_UNREACHABLE()
+
+#define TVM_FFI_CHECK_SAFE_CALL(func) \
+ { \
+ int ret_code = (func); \
+ if (ret_code != 0) { \
+ if (ret_code == -2) { \
+ throw ::tvm::ffi::EnvErrorAlreadySet(); \
+ } \
+ Any error_any; \
+ TVMFFIMoveFromLastError(reinterpret_cast<TVMFFIAny*>(&error_any)); \
+ if (std::optional<tvm::ffi::Error> error = error_any.TryAs<tvm::ffi::Error>()) { \
+ throw std::move(*error); \
+ } else { \
+ TVM_FFI_THROW(RuntimeError) << "Error encountered"; \
+ } \
+ } \
+ }
+
/*!
* \brief Object container class that backs ffi::Function
* \note Do not use this function directly, use ffi::Function
@@ -61,20 +110,12 @@ class FunctionObj : public Object {
FunctionObj() {}
// Implementing safe call style
- static int32_t SafeCall(void* func, int32_t num_args, const TVMFFIAny* args, TVMFFIAny* result) {
+ static int SafeCall(void* func, int32_t num_args, const TVMFFIAny* args, TVMFFIAny* result) {
+ TVM_FFI_SAFE_CALL_BEGIN();
FunctionObj* self = static_cast<FunctionObj*>(func);
- try {
- self->call(self, num_args, reinterpret_cast<const AnyView*>(args),
- reinterpret_cast<Any*>(result));
- return 0;
- } catch (const tvm::ffi::Error& err) {
- Any(std::move(err)).MoveToTVMFFIAny(result);
- return 1;
- } catch (const std::runtime_error& err) {
- Any(tvm::ffi::Error("RuntimeError", err.what(), "")).MoveToTVMFFIAny(result);
- return 1;
- }
- TVM_FFI_UNREACHABLE();
+ self->call(self, num_args, reinterpret_cast<const AnyView*>(args),
+ reinterpret_cast<Any*>(result));
+ TVM_FFI_SAFE_CALL_END();
}
friend class Function;
@@ -119,15 +160,8 @@ template <typename Derived>
struct RedirectCallToSafeCall {
static void Call(const FunctionObj* func, int32_t num_args, const AnyView* args, Any* rv) {
Derived* self = static_cast<Derived*>(const_cast<FunctionObj*>(func));
- int ret_code = self->RedirectSafeCall(num_args, reinterpret_cast<const TVMFFIAny*>(args),
- reinterpret_cast<TVMFFIAny*>(rv));
- if (ret_code != 0) {
- if (std::optional<tvm::ffi::Error> err = rv->TryAs<tvm::ffi::Error>()) {
- throw std::move(*err);
- } else {
- TVM_FFI_THROW(RuntimeError) << "Error encountered when calling a tvm::ffi::Function";
- }
- }
+ TVM_FFI_CHECK_SAFE_CALL(self->RedirectSafeCall(
+ num_args, reinterpret_cast<const TVMFFIAny*>(args), reinterpret_cast<TVMFFIAny*>(rv)));
}
static int32_t SafeCall(void* func, int32_t num_args, const TVMFFIAny* args, TVMFFIAny* rv) {
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 07170dce8f..dbb940ab45 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -41,39 +41,6 @@ namespace details {
// unsafe operations related to object
struct ObjectUnsafe;
-// Code section that depends on dynamic components
-#if TVM_FFI_ALLOW_DYN_TYPE
-/*!
- * \brief Initialize the type info during runtime.
- *
- * When the function is first time called for a type,
- * it will register the type to the type table in the runtime.
- *
- * If the static_tindex is non-negative, the function will
- * allocate a runtime type index.
- * Otherwise, we will populate the type table and return the static index.
- *
- * \param type_key The type key.
- * \param static_type_index Static type index if any, can be -1, which means this is a dynamic index
- * \param num_child_slots Number of slots reserved for its children.
- * \param child_slots_can_overflow Whether to allow child to overflow the slots.
- * \param parent_type_index Parent type index, pass in -1 if it is root.
- *
- * \return The allocated type index
- */
-TVM_FFI_DLL int32_t ObjectGetOrAllocTypeIndex(const char* type_key, int32_t static_type_index,
- int32_t type_depth, int32_t num_child_slots,
- bool child_slots_can_overflow,
- int32_t parent_type_index);
-
-/*!
- * \brief Get Type information from type index.
- * \param type_index The type index
- * \return The type information
- */
-TVM_FFI_DLL const TypeInfo* ObjectGetTypeInfo(int32_t type_index);
-#endif // TVM_FFI_ALLOW_DYN_TYPE
-
/*!
* Check if the type_index is an instance of TargetObjectType.
*
@@ -153,13 +120,9 @@ class Object {
* \note this operation is expensive, can be used for error reporting.
*/
std::string GetTypeKey() const {
-#if TVM_FFI_ALLOW_DYN_TYPE
// the function checks that the info exists
- const TypeInfo* type_info = details::ObjectGetTypeInfo(header_.type_index);
+ const TypeInfo* type_info = TVMFFIGetTypeInfo(header_.type_index);
return type_info->type_key;
-#else
- return "<unknown>";
-#endif
}
// Information about the object
@@ -462,19 +425,15 @@ inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
"Need to set _type_child_slots when parent specifies it.")
// If dynamic type is enabled, we still need to register the runtime type of parent
-#if TVM_FFI_ALLOW_DYN_TYPE
#define TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType) \
static int32_t _GetOrAllocRuntimeTypeIndex() { \
- static int32_t tindex = ::tvm::ffi::details::ObjectGetOrAllocTypeIndex( \
+ static int32_t tindex = TVMFFIGetOrAllocTypeIndex( \
TypeName::_type_key, TypeName::_type_index, TypeName::_type_depth, \
TypeName::_type_child_slots, TypeName::_type_child_slots_can_overflow, \
ParentType::_GetOrAllocRuntimeTypeIndex()); \
return tindex; \
} \
static inline int32_t _register_type_index = _GetOrAllocRuntimeTypeIndex()
-#else
-#define TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType)
-#endif
/*!
* \brief Helper macro to declare a object that comes with static type index.
@@ -492,11 +451,9 @@ inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
* \param ParentType The name of the ParentType
*/
#define TVM_FFI_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType) \
- static_assert(TVM_FFI_ALLOW_DYN_TYPE, \
- "Dynamic object depend on TVM_FFI_ALLOW_DYN_TYPE cd set to 1"); \
TVM_FFI_OBJECT_STATIC_DEFS(TypeName, ParentType); \
static int32_t _GetOrAllocRuntimeTypeIndex() { \
- static int32_t tindex = ::tvm::ffi::details::ObjectGetOrAllocTypeIndex( \
+ static int32_t tindex = TVMFFIGetOrAllocTypeIndex( \
TypeName::_type_key, -1, TypeName::_type_depth, TypeName::_type_child_slots, \
TypeName::_type_child_slots_can_overflow, ParentType::_GetOrAllocRuntimeTypeIndex()); \
return tindex; \
@@ -571,15 +528,11 @@ TVM_FFI_INLINE bool IsObjectInstance(int32_t object_type_index) {
if (!TargetType::_type_child_slots_can_overflow) return false;
// Invariance: parent index is always smaller than the child.
if (object_type_index < target_type_index) return false;
- // Do a runtime lookup of type information
-#if TVM_FFI_ALLOW_DYN_TYPE
+ // Do a runtime lookup of type information
// the function checks that the info exists
- const TypeInfo* type_info = details::ObjectGetTypeInfo(object_type_index);
+ const TypeInfo* type_info = TVMFFIGetTypeInfo(object_type_index);
return (type_info->type_depth > TargetType::_type_depth &&
type_info->type_acenstors[TargetType::_type_depth] == target_type_index);
-#else
- return false;
-#endif
}
/*!
* \brief Namespace to internally manipulate object class.
diff --git a/ffi/include/tvm/ffi/string.h b/ffi/include/tvm/ffi/string.h
index 6b43829ff8..7cbed2f7e8 100644
--- a/ffi/include/tvm/ffi/string.h
+++ b/ffi/include/tvm/ffi/string.h
@@ -165,7 +165,7 @@ class String : public ObjectRef {
template <typename T>
String& operator=(T&& other) {
// copy-and-swap idiom
- String(std::forward<T>(other)).swap(*this);
+ String(std::forward<T>(other)).swap(*this); // NOLINT(*)
return *this;
}
diff --git a/ffi/include/tvm/ffi/type_traits.h b/ffi/include/tvm/ffi/type_traits.h
index 68c83bc0df..a46d04ad83 100644
--- a/ffi/include/tvm/ffi/type_traits.h
+++ b/ffi/include/tvm/ffi/type_traits.h
@@ -59,12 +59,8 @@ inline std::string TypeIndex2TypeKey(int32_t type_index) {
default: {
TVM_FFI_ICHECK_GE(type_index, TypeIndex::kTVMFFIStaticObjectBegin)
<< "Uknown type_index=" << type_index;
-#if TVM_FFI_ALLOW_DYN_TYPE
- const TypeInfo* type_info = details::ObjectGetTypeInfo(type_index);
+ const TypeInfo* type_info = TVMFFIGetTypeInfo(type_index);
return type_info->type_key;
-#else
- return "object.Object";
-#endif
}
}
}
diff --git a/ffi/scripts/run_tests.sh b/ffi/scripts/run_tests.sh
index 8219afc90d..dc7e6cdc71 100755
--- a/ffi/scripts/run_tests.sh
+++ b/ffi/scripts/run_tests.sh
@@ -1,11 +1,10 @@
#!/bin/bash
set -euxo pipefail
-TVM_FFI_ALLOW_DYN_TYPE=ON
-BUILD_TYPE=Release
+BUILD_TYPE=RelWithDebugInfo
rm -rf build/CMakeFiles build/CMakeCache.txt
-cmake -G Ninja -S . -B build -DTVM_FFI_ALLOW_DYN_TYPE=${TVM_FFI_ALLOW_DYN_TYPE} -DTVM_FFI_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
+cmake -G Ninja -S . -B build -DTVM_FFI_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
-DTVM_FFI_BUILD_REGISTRY=ON \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
cmake --build build --parallel 16 --clean-first --config ${BUILD_TYPE} --target tvm_ffi_tests
diff --git a/ffi/src/ffi/function.cc b/ffi/src/ffi/function.cc
new file mode 100644
index 0000000000..5da4e0178b
--- /dev/null
+++ b/ffi/src/ffi/function.cc
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+/*
+ * \file src/ffi/function.cc
+ * \brief Function call registry and safecall context
+ */
+#include <tvm/ffi/any.h>
+#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/error.h>
+#include <tvm/ffi/string.h>
+
+namespace tvm {
+namespace ffi {
+
+class SafeCallContext {
+ public:
+ void SetLastError(const TVMFFIAny* error_view) {
+ last_error_ = Any(AnyView::CopyFromTVMFFIAny(error_view[0]));
+ // turn string into formal error.
+ if (std::optional<String> opt_str = last_error_.TryAs<String>()) {
+ last_error_ = ::tvm::ffi::Error("RuntimeError", *opt_str, "");
+ }
+ }
+
+ void MoveFromLastError(TVMFFIAny* result) { last_error_.MoveToTVMFFIAny(result); }
+
+ static SafeCallContext* ThreadLocal() {
+ static thread_local SafeCallContext ctx;
+ return &ctx;
+ }
+
+ private:
+ Any last_error_;
+};
+
+} // namespace ffi
+} // namespace tvm
+
+extern "C" {
+void TVMFFISetLastError(const TVMFFIAny* error_view) {
+ tvm::ffi::SafeCallContext::ThreadLocal()->SetLastError(error_view);
+}
+
+void TVMFFIMoveFromLastError(TVMFFIAny* result) {
+ tvm::ffi::SafeCallContext::ThreadLocal()->MoveFromLastError(result);
+}
+}
diff --git a/ffi/src/ffi/object.cc b/ffi/src/ffi/object.cc
index f4e2ff3e30..d0782d1b50 100644
--- a/ffi/src/ffi/object.cc
+++ b/ffi/src/ffi/object.cc
@@ -22,6 +22,7 @@
*/
#include <tvm/ffi/c_api.h>
#include <tvm/ffi/error.h>
+#include <tvm/ffi/function.h>
#include <memory>
#include <string>
@@ -219,20 +220,24 @@ class TypeTable {
std::vector<std::unique_ptr<Entry>> type_table_;
std::unordered_map<std::string, int32_t> type_key2index_;
};
+} // namespace ffi
+} // namespace tvm
-namespace details {
+extern "C" {
-int32_t ObjectGetOrAllocTypeIndex(const char* type_key, int32_t static_type_index,
+int32_t TVMFFIGetOrAllocTypeIndex(const char* type_key, int32_t static_type_index,
int32_t type_depth, int32_t num_child_slots,
- bool child_slots_can_overflow, int32_t parent_index) {
- return tvm::ffi::TypeTable::Global()->GetOrAllocTypeIndex(type_key, static_type_index, type_depth,
- num_child_slots,
- child_slots_can_overflow, parent_index);
+ int32_t child_slots_can_overflow, int32_t parent_type_index) {
+ TVM_FFI_LOG_EXCEPTION_CALL_BEGIN();
+ return tvm::ffi::TypeTable::Global()->GetOrAllocTypeIndex(
+ type_key, static_type_index, type_depth, num_child_slots, child_slots_can_overflow,
+ parent_type_index);
+ TVM_FFI_LOG_EXCEPTION_CALL_END(TVMFFIGetOrAllocTypeIndex);
}
-const TypeInfo* ObjectGetTypeInfo(int32_t type_index) {
+const TVMFFITypeInfo* TVMFFIGetTypeInfo(int32_t type_index) {
+ TVM_FFI_LOG_EXCEPTION_CALL_BEGIN();
return tvm::ffi::TypeTable::Global()->GetTypeInfo(type_index);
+ TVM_FFI_LOG_EXCEPTION_CALL_END(TVMFFIGetTypeInfo);
}
-} // namespace details
-} // namespace ffi
-} // namespace tvm
+} // extern "C"
diff --git a/ffi/src/ffi/traceback.cc b/ffi/src/ffi/traceback.cc
index f2826957a2..70de055719 100644
--- a/ffi/src/ffi/traceback.cc
+++ b/ffi/src/ffi/traceback.cc
@@ -146,22 +146,20 @@ __attribute__((constructor)) void install_signal_handler(void) {
}
#endif // TVM_FFI_BACKTRACE_ON_SEGFAULT
} // namespace
+} // namespace ffi
+} // namespace tvm
-namespace details {
-const char* Traceback(const char*, const char*, int) {
+extern "C" {
+const char* TVMFFITraceback(const char*, const char*, int) {
static thread_local std::string traceback_str;
traceback_str = ::tvm::ffi::Traceback();
return traceback_str.c_str();
}
-} // namespace details
-} // namespace ffi
-} // namespace tvm
+} // extern "C"
#else
-namespace tvm {
-namespace ffi {
-namespace details {
+extern "C" {
// fallback implementation simply print out the last trace
-const char* Traceback(const char* filename, const char* func, int lineno) {
+const char* TVMFFITraceback(const char* filename, const char* func, int lineno) {
static thread_local std::string traceback_str;
std::ostringstream traceback_stream;
// python style backtrace
@@ -169,8 +167,6 @@ const char* Traceback(const char* filename, const char* func, int lineno) {
traceback_str = traceback_stream.str();
return traceback_str.c_str();
}
-} // namespace details
-} // namespace ffi
-} // namespace tvm
+} // extern "C"
#endif // TVM_FFI_USE_LIBBACKTRACE
#endif // _MSC_VER
diff --git a/ffi/tests/example/CMakeLists.txt b/ffi/tests/backup_cpp/CMakeLists.txt
similarity index 84%
rename from ffi/tests/example/CMakeLists.txt
rename to ffi/tests/backup_cpp/CMakeLists.txt
index 62543756b9..cb17040090 100644
--- a/ffi/tests/example/CMakeLists.txt
+++ b/ffi/tests/backup_cpp/CMakeLists.txt
@@ -17,9 +17,8 @@ set_target_properties(
add_cxx_warning(tvm_ffi_tests)
add_sanitizer_address(tvm_ffi_tests)
target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi)
+add_sanitizer_address(tvm_ffi_registry_shared)
+target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi_registry_shared)
-if (TVM_FFI_BUILD_REGISTRY)
- target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi_registry_shared)
-endif()
add_googletest(tvm_ffi_tests)
diff --git a/ffi/tests/cpp/test_any.cc b/ffi/tests/backup_cpp/test_any.cc
similarity index 84%
copy from ffi/tests/cpp/test_any.cc
copy to ffi/tests/backup_cpp/test_any.cc
index a57829bd0a..d3c1e17002 100644
--- a/ffi/tests/cpp/test_any.cc
+++ b/ffi/tests/backup_cpp/test_any.cc
@@ -1,11 +1,12 @@
#include <gtest/gtest.h>
+
#include <tvm/ffi/ffi.hpp>
namespace {
using namespace tvm::ffi;
template <typename SrcType, typename Checker>
-void TestAnyConstructor(Checker check, TVMFFITypeIndex expected_type_index, const SrcType &source) {
+void TestAnyConstructor(Checker check, TVMFFITypeIndex expected_type_index, const SrcType& source) {
Any v(source);
EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
EXPECT_EQ(v.ref_cnt, 0);
@@ -19,7 +20,7 @@ std::vector<Any> AnyArrayFactory() {
Any(nullptr),
Any(1),
Any(2.5),
- Any(reinterpret_cast<void *>(FuncCall)),
+ Any(reinterpret_cast<void*>(FuncCall)),
Any(DLDevice{kDLCPU, 0}),
Any(DLDataType{kDLInt, 32, 1}),
Any("Hello (raw str)"),
@@ -31,22 +32,22 @@ std::vector<Any> AnyArrayFactory() {
}
template <typename SrcType>
-void TestAnyStringify(const SrcType &source, TVMFFITypeIndex expected_type_index,
- const std::string &expected) {
+void TestAnyStringify(const SrcType& source, TVMFFITypeIndex expected_type_index,
+ const std::string& expected) {
Any v(source);
EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
EXPECT_EQ(v.str()->c_str(), expected);
}
template <typename SrcType, typename Checker>
-void TestAnyStringifyChecker(const SrcType &source, TVMFFITypeIndex expected_type_index,
+void TestAnyStringifyChecker(const SrcType& source, TVMFFITypeIndex expected_type_index,
Checker check) {
Any v(source);
EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
check(v);
}
-void CheckAnyRefCnt(const TVMFFIAny *v) {
+void CheckAnyRefCnt(const TVMFFIAny* v) {
if (v->type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
EXPECT_EQ(v->v_obj->ref_cnt, 1);
}
@@ -98,17 +99,17 @@ TEST(Any_Constructor_2_AnyView, Move) {
TEST(Any_Constructor_3_Ref, Copy) {
Ref<Object> obj = Ref<Object>::New();
Any v(obj);
- const TVMFFIAny *v_obj = v.v_obj;
+ const TVMFFIAny* v_obj = v.v_obj;
EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
EXPECT_EQ(v.ref_cnt, 0);
- EXPECT_EQ(v_obj, static_cast<const void *>(obj.get()));
+ EXPECT_EQ(v_obj, static_cast<const void*>(obj.get()));
EXPECT_EQ(v_obj->ref_cnt, 2);
}
TEST(Any_Constructor_3_Ref, Move) {
Ref<Object> obj = Ref<Object>::New();
Any v(std::move(obj));
- const TVMFFIAny *v_obj = v.v_obj;
+ const TVMFFIAny* v_obj = v.v_obj;
EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
EXPECT_EQ(v.ref_cnt, 0);
EXPECT_EQ(v_obj->ref_cnt, 1);
@@ -116,7 +117,7 @@ TEST(Any_Constructor_3_Ref, Move) {
}
TEST(Any_Constructor_4_TypeTraits, Integer) {
- auto check = [](TVMFFIAny *v, int64_t source) -> void { EXPECT_EQ(v->v_int64, source); };
+ auto check = [](TVMFFIAny* v, int64_t source) -> void { EXPECT_EQ(v->v_int64, source); };
TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int8_t>(1));
TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int16_t>(2));
TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int32_t>(3));
@@ -128,20 +129,20 @@ TEST(Any_Constructor_4_TypeTraits, Integer) {
}
TEST(Any_Constructor_4_TypeTraits, Float) {
- auto check = [](TVMFFIAny *v, double source) -> void { EXPECT_EQ(v->v_float64, source); };
+ auto check = [](TVMFFIAny* v, double source) -> void { EXPECT_EQ(v->v_float64, source); };
TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(3));
TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(4));
}
TEST(Any_Constructor_4_TypeTraits, Ptr) {
int p = 4;
- auto check = [](TVMFFIAny *v, void *source) -> void { EXPECT_EQ(v->v_ptr, source); };
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFINone, static_cast<void *>(nullptr));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIPtr, static_cast<void *>(&p));
+ auto check = [](TVMFFIAny* v, void* source) -> void { EXPECT_EQ(v->v_ptr, source); };
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFINone, static_cast<void*>(nullptr));
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIPtr, static_cast<void*>(&p));
}
TEST(Any_Constructor_4_TypeTraits, Device) {
- auto check = [](TVMFFIAny *v, const DLDevice &source) -> void {
+ auto check = [](TVMFFIAny* v, const DLDevice& source) -> void {
EXPECT_EQ(v->v_device.device_type, source.device_type);
EXPECT_EQ(v->v_device.device_id, source.device_id);
};
@@ -150,7 +151,7 @@ TEST(Any_Constructor_4_TypeTraits, Device) {
}
TEST(Any_Constructor_4_TypeTraits, DataType) {
- auto check = [](TVMFFIAny *v, const DLDataType &source) -> void {
+ auto check = [](TVMFFIAny* v, const DLDataType& source) -> void {
EXPECT_EQ(v->v_dtype.code, source.code);
EXPECT_EQ(v->v_dtype.bits, source.bits);
EXPECT_EQ(v->v_dtype.lanes, source.lanes);
@@ -160,19 +161,19 @@ TEST(Any_Constructor_4_TypeTraits, DataType) {
}
TEST(Any_Constructor_4_TypeTraits, RawStr) {
- auto check = [](TVMFFIAny *v, const char *source) -> void {
- Str *str = static_cast<Str *>(v->v_ptr);
+ auto check = [](TVMFFIAny* v, const char* source) -> void {
+ Str* str = static_cast<Str*>(v->v_ptr);
EXPECT_STREQ(str->c_str(), source);
};
- const char *empty = "";
- const char *hello = "hello";
+ const char* empty = "";
+ const char* hello = "hello";
TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, empty);
TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, hello);
}
TEST(Any_Constructor_4_TypeTraits, CharArray) {
- auto check = [](TVMFFIAny *v, const char *source) -> void {
- Str *str = static_cast<Str *>(v->v_ptr);
+ auto check = [](TVMFFIAny* v, const char* source) -> void {
+ Str* str = static_cast<Str*>(v->v_ptr);
EXPECT_STREQ(str->c_str(), source);
};
const char empty[] = "";
@@ -182,8 +183,8 @@ TEST(Any_Constructor_4_TypeTraits, CharArray) {
}
TEST(Any_Constructor_4_TypeTraits, StdString) {
- auto check = [](TVMFFIAny *v, const std::string &source) -> void {
- Str *str = static_cast<Str *>(v->v_ptr);
+ auto check = [](TVMFFIAny* v, const std::string& source) -> void {
+ Str* str = static_cast<Str*>(v->v_ptr);
EXPECT_EQ(str->c_str(), source);
};
std::string empty = "";
@@ -194,7 +195,7 @@ TEST(Any_Constructor_4_TypeTraits, StdString) {
TEST(Any_Constructor_5_Object_Ptr, Object) {
Ref<Object> obj = Ref<Object>::New();
- TVMFFIAny *ptr = reinterpret_cast<TVMFFIAny *>(obj.get());
+ TVMFFIAny* ptr = reinterpret_cast<TVMFFIAny*>(obj.get());
{
Any v(obj.get());
EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
@@ -206,7 +207,7 @@ TEST(Any_Constructor_5_Object_Ptr, Object) {
TEST(Any_Constructor_5_Object_Ptr, Func) {
Ref<Func> func = Ref<Func>::New(FuncCall);
- TVMFFIAny *ptr = reinterpret_cast<TVMFFIAny *>(func.get());
+ TVMFFIAny* ptr = reinterpret_cast<TVMFFIAny*>(func.get());
{
Any v(func.get());
EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
@@ -218,7 +219,7 @@ TEST(Any_Constructor_5_Object_Ptr, Func) {
TEST(Any_Constructor_5_Object_Ptr, Str) {
Ref<Str> str = Ref<Str>::New("hello");
- TVMFFIAny *ptr = reinterpret_cast<TVMFFIAny *>(str.get());
+ TVMFFIAny* ptr = reinterpret_cast<TVMFFIAny*>(str.get());
{
Any v(str.get());
EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
@@ -230,7 +231,7 @@ TEST(Any_Constructor_5_Object_Ptr, Str) {
TEST(Any_Converter_0_TypeTraits, Integer) {
std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
+ for (const Any& v : vs) {
auto convert = [&]() -> int64_t { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
EXPECT_EQ(convert(), 1);
@@ -238,7 +239,7 @@ TEST(Any_Converter_0_TypeTraits, Integer) {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `int`";
EXPECT_EQ(ex.what(), os.str());
@@ -250,7 +251,7 @@ TEST(Any_Converter_0_TypeTraits, Integer) {
TEST(Any_Converter_0_TypeTraits, Float) {
std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
+ for (const Any& v : vs) {
auto convert = [&]() -> double { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
EXPECT_EQ(convert(), 1.0);
@@ -260,7 +261,7 @@ TEST(Any_Converter_0_TypeTraits, Float) {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `float`";
EXPECT_EQ(ex.what(), os.str());
@@ -272,19 +273,19 @@ TEST(Any_Converter_0_TypeTraits, Float) {
TEST(Any_Converter_0_TypeTraits, Ptr) {
std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
- auto convert = [&]() -> void * { return v; };
+ for (const Any& v : vs) {
+ auto convert = [&]() -> void* { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
EXPECT_EQ(convert(), nullptr);
} else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIPtr)) {
- EXPECT_EQ(convert(), reinterpret_cast<void *>(&FuncCall));
+ EXPECT_EQ(convert(), reinterpret_cast<void*>(&FuncCall));
} else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
EXPECT_EQ(convert(), v.v_str);
} else {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Ptr`";
EXPECT_EQ(ex.what(), os.str());
@@ -296,7 +297,7 @@ TEST(Any_Converter_0_TypeTraits, Ptr) {
TEST(Any_Converter_0_TypeTraits, Device) {
std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
+ for (const Any& v : vs) {
auto convert = [&]() -> DLDevice { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDevice)) {
EXPECT_EQ(convert().device_type, kDLCPU);
@@ -305,7 +306,7 @@ TEST(Any_Converter_0_TypeTraits, Device) {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Device`";
EXPECT_EQ(ex.what(), os.str());
@@ -317,7 +318,7 @@ TEST(Any_Converter_0_TypeTraits, Device) {
TEST(Any_Converter_0_TypeTraits, DataType) {
std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
+ for (const Any& v : vs) {
auto convert = [&]() -> DLDataType { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDataType)) {
EXPECT_EQ(convert().code, kDLInt);
@@ -327,7 +328,7 @@ TEST(Any_Converter_0_TypeTraits, DataType) {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `dtype`";
EXPECT_EQ(ex.what(), os.str());
@@ -340,8 +341,8 @@ TEST(Any_Converter_0_TypeTraits, DataType) {
TEST(Any_Converter_0_TypeTraits, RawStr) {
std::vector<Any> vs = AnyArrayFactory();
int counter = 0;
- for (const Any &v : vs) {
- auto convert = [&]() -> const char * { return v; };
+ for (const Any& v : vs) {
+ auto convert = [&]() -> const char* { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
++counter;
EXPECT_LE(counter, 3);
@@ -356,7 +357,7 @@ TEST(Any_Converter_0_TypeTraits, RawStr) {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `const char *`";
@@ -369,7 +370,7 @@ TEST(Any_Converter_0_TypeTraits, RawStr) {
TEST(Any_Converter_1_AnyView, Any) {
std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
+ for (const Any& v : vs) {
auto convert = [&]() -> AnyView { return v; };
{
AnyView ret = convert();
@@ -384,20 +385,20 @@ TEST(Any_Converter_1_AnyView, Any) {
TEST(Any_Converter_2_Ref, Object) {
std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
+ for (const Any& v : vs) {
auto convert = [&]() -> Ref<Object> { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
Ref<Object> ret = convert();
EXPECT_EQ(ret.get(), nullptr);
} else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
Ref<Object> ret = convert();
- EXPECT_EQ(ret.get(), static_cast<void *>(v.v_obj));
+ EXPECT_EQ(ret.get(), static_cast<void*>(v.v_obj));
EXPECT_EQ(v.v_obj->ref_cnt, 2);
} else {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `object.Object`";
@@ -410,19 +411,19 @@ TEST(Any_Converter_2_Ref, Object) {
TEST(Any_Converter_2_Ref, Func) {
std::vector<Any> views = AnyArrayFactory();
- for (const Any &v : views) {
+ for (const Any& v : views) {
auto convert = [&]() -> Ref<Func> { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
Ref<Func> ret = convert();
EXPECT_EQ(ret.get(), nullptr);
} else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
Ref<Func> ret = convert();
- EXPECT_EQ(ret.get(), static_cast<void *>(v.v_obj));
+ EXPECT_EQ(ret.get(), static_cast<void*>(v.v_obj));
} else {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `object.Func`";
@@ -436,7 +437,7 @@ TEST(Any_Converter_2_Ref, Func) {
TEST(Any_Converter_2_Ref, Str) {
int counter = 0;
std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
+ for (const Any& v : vs) {
auto convert = [&]() -> Ref<Str> { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
Ref<Str> ret = convert();
@@ -456,7 +457,7 @@ TEST(Any_Converter_2_Ref, Str) {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `object.Str`";
@@ -469,20 +470,20 @@ TEST(Any_Converter_2_Ref, Str) {
TEST(Any_Converter_3_Object_Ptr, Object) {
std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
- auto convert = [&]() -> Object * { return v; };
+ for (const Any& v : vs) {
+ auto convert = [&]() -> Object* { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Object *ret = convert();
+ Object* ret = convert();
EXPECT_EQ(ret, nullptr);
} else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
- Object *ret = convert();
- EXPECT_EQ(ret, static_cast<void *>(v.v_obj));
+ Object* ret = convert();
+ EXPECT_EQ(ret, static_cast<void*>(v.v_obj));
EXPECT_EQ(v.v_obj->ref_cnt, 1);
} else {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `object.Object *`";
@@ -495,19 +496,19 @@ TEST(Any_Converter_3_Object_Ptr, Object) {
TEST(Any_Converter_3_Object_Ptr, Func) {
std::vector<Any> views = AnyArrayFactory();
- for (const Any &v : views) {
- auto convert = [&]() -> Func * { return v; };
+ for (const Any& v : views) {
+ auto convert = [&]() -> Func* { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Func *ret = convert();
+ Func* ret = convert();
EXPECT_EQ(ret, nullptr);
} else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
- Func *ret = convert();
- EXPECT_EQ(ret, reinterpret_cast<Func *>(v.v_ptr));
+ Func* ret = convert();
+ EXPECT_EQ(ret, reinterpret_cast<Func*>(v.v_ptr));
} else {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `object.Func *`";
@@ -521,13 +522,13 @@ TEST(Any_Converter_3_Object_Ptr, Func) {
TEST(Any_Converter_3_Object_Ptr, Str) {
std::vector<Any> vs = AnyArrayFactory();
int counter = 0;
- for (const Any &v : vs) {
- auto convert = [&]() -> Str * { return v; };
+ for (const Any& v : vs) {
+ auto convert = [&]() -> Str* { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Str *ret = convert();
+ Str* ret = convert();
EXPECT_EQ(ret, nullptr);
} else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- Str *ret = convert();
+ Str* ret = convert();
++counter;
EXPECT_LE(counter, 3);
if (counter == 1) {
@@ -541,7 +542,7 @@ TEST(Any_Converter_3_Object_Ptr, Str) {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `object.Str *`";
@@ -560,7 +561,7 @@ TEST(Any_Stringify, Integer) {
}
TEST(Any_Stringify, Float) {
- auto check = [](const Any &v) -> void {
+ auto check = [](const Any& v) -> void {
std::string str = v.str()->c_str();
double f_str = std::stod(str);
double f_src = v.v_float64;
@@ -571,13 +572,13 @@ TEST(Any_Stringify, Float) {
}
TEST(Any_Stringify, Ptr) {
- auto check = [](const Any &v) -> void {
+ auto check = [](const Any& v) -> void {
std::string str = v.str()->c_str();
EXPECT_GT(str.size(), 2);
};
- TestAnyStringify<void *>(nullptr, TVMFFITypeIndex::kTVMFFINone, "None");
- TestAnyStringifyChecker<void *>(reinterpret_cast<void *>(FuncCall), TVMFFITypeIndex::kTVMFFIPtr,
- check);
+ TestAnyStringify<void*>(nullptr, TVMFFITypeIndex::kTVMFFINone, "None");
+ TestAnyStringifyChecker<void*>(reinterpret_cast<void*>(FuncCall), TVMFFITypeIndex::kTVMFFIPtr,
+ check);
}
TEST(Any_Stringify, Device) {
@@ -596,13 +597,13 @@ TEST(Any_Stringify, DataType) {
}
TEST(Any_Stringify, RawStr) {
- TestAnyStringify<const char *>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
+ TestAnyStringify<const char*>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
TestAnyStringify<char[6]>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
TestAnyStringify<const char[6]>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
}
TEST(Any_Stringify, Object) {
- auto check = [](const Any &v) -> void {
+ auto check = [](const Any& v) -> void {
std::string expected_prefix = "object.Object@0";
int n = static_cast<int>(expected_prefix.size());
std::string str = v.str()->c_str();
@@ -613,7 +614,7 @@ TEST(Any_Stringify, Object) {
}
TEST(Any_Stringify, Func) {
- auto check = [](const Any &v) -> void {
+ auto check = [](const Any& v) -> void {
std::string expected_prefix = "object.Func@0";
int n = static_cast<int>(expected_prefix.size());
std::string str = v.str()->c_str();
@@ -624,7 +625,7 @@ TEST(Any_Stringify, Func) {
}
TEST(Any_Stringify, Str) {
- auto check = [](const Any &v) -> void {
+ auto check = [](const Any& v) -> void {
std::string str = v.str()->c_str();
EXPECT_EQ(str, "\"Hello World\"");
};
@@ -632,4 +633,4 @@ TEST(Any_Stringify, Str) {
check);
}
-} // namespace
+} // namespace
diff --git a/ffi/tests/cpp/test_any_view.cc b/ffi/tests/backup_cpp/test_any_view.cc
similarity index 84%
rename from ffi/tests/cpp/test_any_view.cc
rename to ffi/tests/backup_cpp/test_any_view.cc
index 67aaa9de3a..122c9ec368 100644
--- a/ffi/tests/cpp/test_any_view.cc
+++ b/ffi/tests/backup_cpp/test_any_view.cc
@@ -1,4 +1,5 @@
#include <gtest/gtest.h>
+
#include <tvm/ffi/ffi.hpp>
namespace {
@@ -6,7 +7,7 @@ using namespace tvm::ffi;
template <typename SrcType, typename Checker>
void TestAnyViewConstructor(Checker check, TVMFFITypeIndex expected_type_index,
- const SrcType &source) {
+ const SrcType& source) {
AnyView v(source);
EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
EXPECT_EQ(v.ref_cnt, 0);
@@ -16,7 +17,7 @@ void TestAnyViewConstructor(Checker check, TVMFFITypeIndex expected_type_index,
int64_t FuncCall(int64_t x) { return x + 1; }
std::vector<AnyView> AnyViewArrayFactory() {
- static const char *raw_str = "Hello (raw str)";
+ static const char* raw_str = "Hello (raw str)";
static std::string std_str = "World (std::string)";
static std::string ref_str = "Hello World (Ref<Str>)";
static Ref<Object> obj = Ref<Object>::New();
@@ -26,34 +27,34 @@ std::vector<AnyView> AnyViewArrayFactory() {
AnyView(nullptr),
AnyView(1),
AnyView(2.5),
- AnyView(reinterpret_cast<void *>(FuncCall)),
+ AnyView(reinterpret_cast<void*>(FuncCall)),
AnyView(DLDevice{kDLCPU, 0}),
AnyView(DLDataType{kDLInt, 32, 1}),
AnyView(raw_str),
AnyView(obj),
AnyView(func),
- AnyView(std_str), // TODO: disable AnyView(std::string&&)
+ AnyView(std_str), // TODO: disable AnyView(std::string&&)
AnyView(str),
};
}
template <typename SrcType>
-void TestAnyViewStringify(const SrcType &source, TVMFFITypeIndex expected_type_index,
- const std::string &expected) {
+void TestAnyViewStringify(const SrcType& source, TVMFFITypeIndex expected_type_index,
+ const std::string& expected) {
AnyView v(source);
EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
EXPECT_EQ(v.str()->c_str(), expected);
}
template <typename SrcType, typename Checker>
-void TestAnyViewStringifyChecker(const SrcType &source, TVMFFITypeIndex expected_type_index,
+void TestAnyViewStringifyChecker(const SrcType& source, TVMFFITypeIndex expected_type_index,
Checker check) {
AnyView v(source);
EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
check(v);
}
-void CheckAnyViewRefCnt(const TVMFFIAny *v) {
+void CheckAnyViewRefCnt(const TVMFFIAny* v) {
if (v->type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
EXPECT_EQ(v->v_obj->ref_cnt, 1);
}
@@ -102,10 +103,10 @@ TEST(AnyView_Constructor_2_Any, Move) {
TEST(AnyView_Constructor_3_Ref, Copy) {
Ref<Object> obj = Ref<Object>::New();
AnyView v(obj);
- const TVMFFIAny *v_obj = v.v_obj;
+ const TVMFFIAny* v_obj = v.v_obj;
EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
EXPECT_EQ(v.ref_cnt, 0);
- EXPECT_EQ(v_obj, static_cast<const void *>(obj.get()));
+ EXPECT_EQ(v_obj, static_cast<const void*>(obj.get()));
EXPECT_EQ(v_obj->ref_cnt, 1);
}
@@ -116,7 +117,7 @@ TEST(AnyView_Constructor_3_Ref, Move) {
}
TEST(AnyView_Constructor_4_TypeTraits, Integer) {
- auto check = [](TVMFFIAny *v, int64_t source) -> void { EXPECT_EQ(v->v_int64, source); };
+ auto check = [](TVMFFIAny* v, int64_t source) -> void { EXPECT_EQ(v->v_int64, source); };
TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int8_t>(1));
TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int16_t>(2));
TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int32_t>(3));
@@ -128,20 +129,20 @@ TEST(AnyView_Constructor_4_TypeTraits, Integer) {
}
TEST(AnyView_Constructor_4_TypeTraits, Float) {
- auto check = [](TVMFFIAny *v, double source) -> void { EXPECT_EQ(v->v_float64, source); };
+ auto check = [](TVMFFIAny* v, double source) -> void { EXPECT_EQ(v->v_float64, source); };
TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(3));
TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(4));
}
TEST(AnyView_Constructor_4_TypeTraits, Ptr) {
int p = 4;
- auto check = [](TVMFFIAny *v, void *source) -> void { EXPECT_EQ(v->v_ptr, source); };
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFINone, static_cast<void *>(nullptr));
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIPtr, static_cast<void *>(&p));
+ auto check = [](TVMFFIAny* v, void* source) -> void { EXPECT_EQ(v->v_ptr, source); };
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFINone, static_cast<void*>(nullptr));
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIPtr, static_cast<void*>(&p));
}
TEST(AnyView_Constructor_4_TypeTraits, Device) {
- auto check = [](TVMFFIAny *v, const DLDevice &source) -> void {
+ auto check = [](TVMFFIAny* v, const DLDevice& source) -> void {
EXPECT_EQ(v->v_device.device_type, source.device_type);
EXPECT_EQ(v->v_device.device_id, source.device_id);
};
@@ -150,7 +151,7 @@ TEST(AnyView_Constructor_4_TypeTraits, Device) {
}
TEST(AnyView_Constructor_4_TypeTraits, DataType) {
- auto check = [](TVMFFIAny *v, const DLDataType &source) -> void {
+ auto check = [](TVMFFIAny* v, const DLDataType& source) -> void {
EXPECT_EQ(v->v_dtype.code, source.code);
EXPECT_EQ(v->v_dtype.bits, source.bits);
EXPECT_EQ(v->v_dtype.lanes, source.lanes);
@@ -160,15 +161,15 @@ TEST(AnyView_Constructor_4_TypeTraits, DataType) {
}
TEST(AnyView_Constructor_4_TypeTraits, RawStr) {
- auto check = [](TVMFFIAny *v, const char *source) -> void { EXPECT_EQ(v->v_str, source); };
- const char *empty = "";
- const char *hello = "hello";
+ auto check = [](TVMFFIAny* v, const char* source) -> void { EXPECT_EQ(v->v_str, source); };
+ const char* empty = "";
+ const char* hello = "hello";
TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, empty);
TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, hello);
}
TEST(AnyView_Constructor_4_TypeTraits, CharArray) {
- auto check = [](TVMFFIAny *v, const char *source) -> void { EXPECT_EQ(v->v_str, source); };
+ auto check = [](TVMFFIAny* v, const char* source) -> void { EXPECT_EQ(v->v_str, source); };
const char empty[] = "";
const char hello[] = "hello";
TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, empty);
@@ -176,7 +177,7 @@ TEST(AnyView_Constructor_4_TypeTraits, CharArray) {
}
TEST(AnyView_Constructor_4_TypeTraits, StdString) {
- auto check = [](TVMFFIAny *v, const std::string &source) -> void {
+ auto check = [](TVMFFIAny* v, const std::string& source) -> void {
EXPECT_EQ(v->v_str, source.data());
};
std::string empty = "";
@@ -190,7 +191,7 @@ TEST(AnyView_Constructor_5_Object_Ptr, Object) {
AnyView v(obj.get());
EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
EXPECT_EQ(v.v_obj->ref_cnt, 1);
- EXPECT_EQ(v.v_obj, static_cast<void *>(obj.get()));
+ EXPECT_EQ(v.v_obj, static_cast<void*>(obj.get()));
}
TEST(AnyView_Constructor_5_Object_Ptr, Func) {
@@ -198,7 +199,7 @@ TEST(AnyView_Constructor_5_Object_Ptr, Func) {
AnyView v(func.get());
EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
EXPECT_EQ(v.v_obj->ref_cnt, 1);
- EXPECT_EQ(v.v_ptr, static_cast<void *>(func.get()));
+ EXPECT_EQ(v.v_ptr, static_cast<void*>(func.get()));
}
TEST(AnyView_Constructor_5_Object_Ptr, Str) {
@@ -206,12 +207,12 @@ TEST(AnyView_Constructor_5_Object_Ptr, Str) {
AnyView v(str.get());
EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
EXPECT_EQ(v.v_obj->ref_cnt, 1);
- EXPECT_EQ(v.v_obj, static_cast<void *>(str.get()));
+ EXPECT_EQ(v.v_obj, static_cast<void*>(str.get()));
}
TEST(AnyView_Converter_0_TypeTraits, Integer) {
std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView &v : views) {
+ for (const AnyView& v : views) {
auto convert = [&]() -> int64_t { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
EXPECT_EQ(convert(), 1);
@@ -219,7 +220,7 @@ TEST(AnyView_Converter_0_TypeTraits, Integer) {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `int`";
EXPECT_EQ(ex.what(), os.str());
@@ -231,7 +232,7 @@ TEST(AnyView_Converter_0_TypeTraits, Integer) {
TEST(AnyView_Converter_0_TypeTraits, Float) {
std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView &v : views) {
+ for (const AnyView& v : views) {
auto convert = [&]() -> double { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
EXPECT_EQ(convert(), 1.0);
@@ -241,7 +242,7 @@ TEST(AnyView_Converter_0_TypeTraits, Float) {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `float`";
EXPECT_EQ(ex.what(), os.str());
@@ -253,19 +254,19 @@ TEST(AnyView_Converter_0_TypeTraits, Float) {
TEST(AnyView_Converter_0_TypeTraits, Ptr) {
std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView &v : views) {
- auto convert = [&]() -> void * { return v; };
+ for (const AnyView& v : views) {
+ auto convert = [&]() -> void* { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
EXPECT_EQ(convert(), nullptr);
} else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIPtr)) {
- EXPECT_EQ(convert(), reinterpret_cast<void *>(&FuncCall));
+ EXPECT_EQ(convert(), reinterpret_cast<void*>(&FuncCall));
} else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
EXPECT_EQ(convert(), v.v_str);
} else {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Ptr`";
EXPECT_EQ(ex.what(), os.str());
@@ -277,7 +278,7 @@ TEST(AnyView_Converter_0_TypeTraits, Ptr) {
TEST(AnyView_Converter_0_TypeTraits, Device) {
std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView &v : views) {
+ for (const AnyView& v : views) {
auto convert = [&]() -> DLDevice { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDevice)) {
EXPECT_EQ(convert().device_type, kDLCPU);
@@ -286,7 +287,7 @@ TEST(AnyView_Converter_0_TypeTraits, Device) {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Device`";
EXPECT_EQ(ex.what(), os.str());
@@ -298,7 +299,7 @@ TEST(AnyView_Converter_0_TypeTraits, Device) {
TEST(AnyView_Converter_0_TypeTraits, DataType) {
std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView &v : views) {
+ for (const AnyView& v : views) {
auto convert = [&]() -> DLDataType { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDataType)) {
EXPECT_EQ(convert().code, kDLInt);
@@ -308,7 +309,7 @@ TEST(AnyView_Converter_0_TypeTraits, DataType) {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `dtype`";
EXPECT_EQ(ex.what(), os.str());
@@ -321,8 +322,8 @@ TEST(AnyView_Converter_0_TypeTraits, DataType) {
TEST(AnyView_Converter_0_TypeTraits, RawStr) {
std::vector<AnyView> views = AnyViewArrayFactory();
int counter = 0;
- for (const AnyView &v : views) {
- auto convert = [&]() -> const char * { return v; };
+ for (const AnyView& v : views) {
+ auto convert = [&]() -> const char* { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
counter += 1;
EXPECT_LT(counter, 3);
@@ -337,7 +338,7 @@ TEST(AnyView_Converter_0_TypeTraits, RawStr) {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `const char *`";
@@ -351,10 +352,10 @@ TEST(AnyView_Converter_0_TypeTraits, RawStr) {
TEST(AnyView_Converter_0_TypeTraits, RawStrToStrStar_Fail) {
AnyView v = "Hello";
try {
- Str *v_str = v;
+ Str* v_str = v;
(void)v_str;
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
EXPECT_STREQ(ex.what(), "Cannot convert from type `const char *` to `object.Str *`");
}
}
@@ -362,19 +363,19 @@ TEST(AnyView_Converter_0_TypeTraits, RawStrToStrStar_Fail) {
TEST(AnyView_Converter_0_TypeTraits, RawStrToStrStar_WrithStorage) {
Any storage;
AnyView v = "Hello";
- Str *v_str = v.CastWithStorage<Str *>(&storage);
+ Str* v_str = v.CastWithStorage<Str*>(&storage);
EXPECT_STREQ(v_str->c_str(), "Hello");
}
TEST(AnyView_Converter_1_Any, Any) {
std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView &view : views) {
+ for (const AnyView& view : views) {
auto convert = [&]() -> Any { return view; };
{
Any ret = convert();
EXPECT_EQ(view.ref_cnt, 0);
if (view.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
- Str *str = ret;
+ Str* str = ret;
EXPECT_EQ(ret.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
EXPECT_STREQ(str->c_str(), view.v_str);
EXPECT_EQ(ret.ref_cnt, 0);
@@ -390,20 +391,20 @@ TEST(AnyView_Converter_1_Any, Any) {
TEST(AnyView_Converter_2_Ref, Object) {
std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView &v : views) {
+ for (const AnyView& v : views) {
auto convert = [&]() -> Ref<Object> { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
Ref<Object> ret = convert();
EXPECT_EQ(ret.get(), nullptr);
} else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
Ref<Object> ret = convert();
- EXPECT_EQ(ret.get(), static_cast<void *>(v.v_obj));
+ EXPECT_EQ(ret.get(), static_cast<void*>(v.v_obj));
EXPECT_EQ(v.v_obj->ref_cnt, 2);
} else {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `object.Object`";
@@ -416,19 +417,19 @@ TEST(AnyView_Converter_2_Ref, Object) {
TEST(AnyView_Converter_2_Ref, Func) {
std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView &v : views) {
+ for (const AnyView& v : views) {
auto convert = [&]() -> Ref<Func> { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
Ref<Func> ret = convert();
EXPECT_EQ(ret.get(), nullptr);
} else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
Ref<Func> ret = convert();
- EXPECT_EQ(ret.get(), static_cast<void *>(v.v_obj));
+ EXPECT_EQ(ret.get(), static_cast<void*>(v.v_obj));
} else {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `object.Func`";
@@ -441,7 +442,7 @@ TEST(AnyView_Converter_2_Ref, Func) {
TEST(AnyView_Converter_2_Ref, Str) {
std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView &v : views) {
+ for (const AnyView& v : views) {
auto convert = [&]() -> Ref<Str> { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
Ref<Str> ret = convert();
@@ -451,13 +452,13 @@ TEST(AnyView_Converter_2_Ref, Str) {
EXPECT_STREQ(ret->c_str(), "Hello World (Ref<Str>)");
} else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
Ref<Str> ret = convert();
- EXPECT_EQ(reinterpret_cast<TVMFFIStr *>(ret.get())->ref_cnt, 1);
+ EXPECT_EQ(reinterpret_cast<TVMFFIStr*>(ret.get())->ref_cnt, 1);
EXPECT_STREQ(ret->c_str(), v.v_str);
} else {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `object.Str`";
@@ -470,20 +471,20 @@ TEST(AnyView_Converter_2_Ref, Str) {
TEST(AnyView_Converter_3_Object_Ptr, Object) {
std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView &v : views) {
- auto convert = [&]() -> Object * { return v; };
+ for (const AnyView& v : views) {
+ auto convert = [&]() -> Object* { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Object *ret = convert();
+ Object* ret = convert();
EXPECT_EQ(ret, nullptr);
} else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
- Object *ret = convert();
- EXPECT_EQ(ret, static_cast<void *>(v.v_obj));
+ Object* ret = convert();
+ EXPECT_EQ(ret, static_cast<void*>(v.v_obj));
EXPECT_EQ(v.v_obj->ref_cnt, 1);
} else {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `object.Object *`";
@@ -496,19 +497,19 @@ TEST(AnyView_Converter_3_Object_Ptr, Object) {
TEST(AnyView_Converter_3_Object_Ptr, Func) {
std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView &v : views) {
- auto convert = [&]() -> Func * { return v; };
+ for (const AnyView& v : views) {
+ auto convert = [&]() -> Func* { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Func *ret = convert();
+ Func* ret = convert();
EXPECT_EQ(ret, nullptr);
} else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
- Func *ret = convert();
- EXPECT_EQ(ret, reinterpret_cast<Func *>(v.v_ptr));
+ Func* ret = convert();
+ EXPECT_EQ(ret, reinterpret_cast<Func*>(v.v_ptr));
} else {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `object.Func *`";
@@ -521,19 +522,19 @@ TEST(AnyView_Converter_3_Object_Ptr, Func) {
TEST(AnyView_Converter_3_Object_Ptr, Str) {
std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView &v : views) {
- auto convert = [&]() -> Str * { return v; };
+ for (const AnyView& v : views) {
+ auto convert = [&]() -> Str* { return v; };
if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Str *ret = convert();
+ Str* ret = convert();
EXPECT_EQ(ret, nullptr);
} else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- Str *ret = convert();
+ Str* ret = convert();
EXPECT_STREQ(ret->c_str(), "Hello World (Ref<Str>)");
} else {
try {
convert();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
std::ostringstream os;
os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
<< "` to `object.Str *`";
@@ -552,7 +553,7 @@ TEST(AnyView_Stringify, Integer) {
}
TEST(AnyView_Stringify, Float) {
- auto check = [](const AnyView &v) -> void {
+ auto check = [](const AnyView& v) -> void {
std::string str = v.str()->c_str();
double f_str = std::stod(str);
double f_src = v.v_float64;
@@ -563,13 +564,13 @@ TEST(AnyView_Stringify, Float) {
}
TEST(AnyView_Stringify, Ptr) {
- auto check = [](const AnyView &v) -> void {
+ auto check = [](const AnyView& v) -> void {
std::string str = v.str()->c_str();
EXPECT_GT(str.size(), 2);
};
- TestAnyViewStringify<void *>(nullptr, TVMFFITypeIndex::kTVMFFINone, "None");
- TestAnyViewStringifyChecker<void *>(reinterpret_cast<void *>(FuncCall),
- TVMFFITypeIndex::kTVMFFIPtr, check);
+ TestAnyViewStringify<void*>(nullptr, TVMFFITypeIndex::kTVMFFINone, "None");
+ TestAnyViewStringifyChecker<void*>(reinterpret_cast<void*>(FuncCall), TVMFFITypeIndex::kTVMFFIPtr,
+ check);
}
TEST(AnyView_Stringify, Device) {
@@ -589,13 +590,13 @@ TEST(AnyView_Stringify, DataType) {
}
TEST(AnyView_Stringify, RawStr) {
- TestAnyViewStringify<const char *>("Hello", TVMFFITypeIndex::kTVMFFIRawStr, "\"Hello\"");
+ TestAnyViewStringify<const char*>("Hello", TVMFFITypeIndex::kTVMFFIRawStr, "\"Hello\"");
TestAnyViewStringify<char[6]>("Hello", TVMFFITypeIndex::kTVMFFIRawStr, "\"Hello\"");
TestAnyViewStringify<const char[6]>("Hello", TVMFFITypeIndex::kTVMFFIRawStr, "\"Hello\"");
}
TEST(AnyView_Stringify, Object) {
- auto check = [](const AnyView &v) -> void {
+ auto check = [](const AnyView& v) -> void {
std::string expected_prefix = "object.Object@0";
int n = static_cast<int>(expected_prefix.size());
std::string str = v.str()->c_str();
@@ -607,7 +608,7 @@ TEST(AnyView_Stringify, Object) {
}
TEST(AnyView_Stringify, Func) {
- auto check = [](const AnyView &v) -> void {
+ auto check = [](const AnyView& v) -> void {
std::string expected_prefix = "object.Func@0";
int n = static_cast<int>(expected_prefix.size());
std::string str = v.str()->c_str();
@@ -619,7 +620,7 @@ TEST(AnyView_Stringify, Func) {
}
TEST(AnyView_Stringify, Str) {
- auto check = [](const AnyView &v) -> void {
+ auto check = [](const AnyView& v) -> void {
std::string str = v.str()->c_str();
EXPECT_EQ(str, "\"Hello World\"");
};
@@ -627,4 +628,4 @@ TEST(AnyView_Stringify, Str) {
check);
}
-} // namespace
+} // namespace
diff --git a/ffi/tests/cpp/test_dict.cc b/ffi/tests/backup_cpp/test_dict.cc
similarity index 92%
rename from ffi/tests/cpp/test_dict.cc
rename to ffi/tests/backup_cpp/test_dict.cc
index 04d5a2854f..53c4452401 100644
--- a/ffi/tests/cpp/test_dict.cc
+++ b/ffi/tests/backup_cpp/test_dict.cc
@@ -1,4 +1,5 @@
#include <gtest/gtest.h>
+
#include <tvm/ffi/ffi.hpp>
#include <unordered_set>
@@ -16,7 +17,7 @@ bool DTypeEqual(DLDataType a, DLDataType b) {
TEST(Dict_Construtor, Default) {
Ref<Dict> dict;
ASSERT_EQ(dict.size(), 0);
- TVMFFIDict *dict_ptr = reinterpret_cast<TVMFFIDict *>(dict.get());
+ TVMFFIDict* dict_ptr = reinterpret_cast<TVMFFIDict*>(dict.get());
EXPECT_EQ(dict_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDict));
EXPECT_EQ(dict_ptr->ref_cnt, 1);
EXPECT_NE(dict_ptr->deleter, nullptr);
@@ -32,7 +33,7 @@ TEST(Dict_Construtor, InitializerList) {
EXPECT_EQ(int(dict[3]), 4);
bool found[3] = {false, false, false};
- for (const auto &kv : dict) {
+ for (const auto& kv : dict) {
if (AnyEqual()(kv.first, Any("key1"))) {
found[0] = true;
EXPECT_EQ(int(kv.second), 1);
@@ -65,7 +66,7 @@ TEST(Dict_Insert, New) {
EXPECT_DOUBLE_EQ(double(dict[integer]), fp);
EXPECT_PRED2(DTypeEqual, DLDataType(dict[str]), dtype);
EXPECT_EQ(int(dict[null_obj]), 0);
- EXPECT_EQ((Object *)(dict[device]), nullptr);
+ EXPECT_EQ((Object*)(dict[device]), nullptr);
}
TEST(Dict_Insert, Override) {
@@ -98,7 +99,7 @@ TEST(Dict_At, NotFound) {
try {
dict.at("key3");
FAIL() << "Expected TVMError";
- } catch (const TVMError &e) {
+ } catch (const TVMError& e) {
}
}
@@ -109,7 +110,7 @@ TEST(Dict_ReHash, POD) {
}
EXPECT_EQ(dict.size(), 1000);
std::unordered_set<int64_t> keys;
- for (auto &kv : dict) {
+ for (auto& kv : dict) {
int64_t key = kv.first;
int64_t value = kv.second;
EXPECT_EQ(key, value);
@@ -122,7 +123,7 @@ TEST(Dict_ReHash, POD) {
TEST(Dict_ReHash, Object) {
std::vector<Ref<Object>> objs;
- std::unordered_map<Object *, int64_t> obj_map;
+ std::unordered_map<Object*, int64_t> obj_map;
for (int j = 0; j < 1000; ++j) {
objs.push_back(Ref<Object>::New());
obj_map[objs[j].get()] = j;
@@ -132,8 +133,8 @@ TEST(Dict_ReHash, Object) {
dict[objs[j]] = j;
}
EXPECT_EQ(dict.size(), 1000);
- std::unordered_set<Object *> keys;
- for (auto &kv : dict) {
+ std::unordered_set<Object*> keys;
+ for (auto& kv : dict) {
Ref<Object> key = kv.first;
int64_t value = kv.second;
keys.insert(key.get());
@@ -160,7 +161,7 @@ TEST(Dict_Erase, POD) {
TEST(Dict_Erase, Object) {
std::vector<Ref<Object>> objs;
- std::unordered_map<Object *, int64_t> obj_map;
+ std::unordered_map<Object*, int64_t> obj_map;
for (int j = 0; j < 1000; ++j) {
objs.push_back(Ref<Object>::New());
obj_map[objs[j].get()] = j;
@@ -180,4 +181,4 @@ TEST(Dict_Erase, Object) {
}
}
-} // namespace
+} // namespace
diff --git a/ffi/tests/cpp/test_func.cc b/ffi/tests/backup_cpp/test_func.cc
similarity index 77%
rename from ffi/tests/cpp/test_func.cc
rename to ffi/tests/backup_cpp/test_func.cc
index 32512d3dcf..0790692bb9 100644
--- a/ffi/tests/cpp/test_func.cc
+++ b/ffi/tests/backup_cpp/test_func.cc
@@ -1,12 +1,13 @@
#include <gtest/gtest.h>
+
#include <tvm/ffi/ffi.hpp>
namespace {
using namespace tvm::ffi;
-const char *c_str_raw = "Hello";
+const char* c_str_raw = "Hello";
-double func_unpacked_0(int64_t a, double b, const char *c, const double &d) {
+double func_unpacked_0(int64_t a, double b, const char* c, const double& d) {
EXPECT_STREQ(c, c_str_raw);
return a + b + d;
}
@@ -17,7 +18,7 @@ void func_unpacked_1(DLDataType dtype, DLDevice device, std::string str) {
(void)str;
}
-void func_packed_0(int num_args, const AnyView *, Any *ret) { *ret = num_args; }
+void func_packed_0(int num_args, const AnyView*, Any* ret) { *ret = num_args; }
template <enum TVMFFITypeIndex type_index>
void func_unpacked_anyview_arg(AnyView a) {
@@ -30,8 +31,8 @@ void func_unpacked_any_arg(Any a) {
AnyView func_unpacked_anyview_ret() { return AnyView(1); }
Any func_unpacked_any_ret() { return Any(1); }
-std::string func_unpacked_str_obj(Str *str, const char *str_2) {
- EXPECT_EQ(reinterpret_cast<TVMFFIStr *>(str)->ref_cnt, 1);
+std::string func_unpacked_str_obj(Str* str, const char* str_2) {
+ EXPECT_EQ(reinterpret_cast<TVMFFIStr*>(str)->ref_cnt, 1);
EXPECT_STREQ(str->c_str(), str_2);
return str->c_str();
}
@@ -47,26 +48,23 @@ TEST(Func_Signature, 1) {
}
TEST(Func_Signature, AnyView_Arg) {
- EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_anyview_arg<
- TVMFFITypeIndex::kTVMFFIInt>)>::Sig(),
- "(0: AnyView) -> void");
+ EXPECT_EQ(
+ details::FuncFunctor<decltype(func_unpacked_anyview_arg<TVMFFITypeIndex::kTVMFFIInt>)>::Sig(),
+ "(0: AnyView) -> void");
}
TEST(Func_Signature, AnyView_Ret) {
- EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_anyview_ret)>::Sig(),
- "() -> AnyView");
+ EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_anyview_ret)>::Sig(), "() -> AnyView");
}
TEST(Func_Signature, Any_Arg) {
EXPECT_EQ(
- details::FuncFunctor<
- decltype(func_unpacked_any_arg<TVMFFITypeIndex::kTVMFFIInt>)>::Sig(),
+ details::FuncFunctor<decltype(func_unpacked_any_arg<TVMFFITypeIndex::kTVMFFIInt>)>::Sig(),
"(0: Any) -> void");
}
TEST(Func_Signature, Any_Ret) {
- EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_any_ret)>::Sig(),
- "() -> Any");
+ EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_any_ret)>::Sig(), "() -> Any");
}
TEST(Func_Unpacked_Invoke, Func0_RawStr) {
@@ -98,8 +96,7 @@ TEST(Func_Unpacked_Invoke, Func1) {
}
TEST(Func_Unpacked_Invoke, AnyView_Arg) {
- Ref<Func> func =
- Ref<Func>::New(func_unpacked_anyview_arg<TVMFFITypeIndex::kTVMFFIInt>);
+ Ref<Func> func = Ref<Func>::New(func_unpacked_anyview_arg<TVMFFITypeIndex::kTVMFFIInt>);
func(1);
}
@@ -110,8 +107,7 @@ TEST(Func_Unpacked_Invoke, AnyView_Ret) {
}
TEST(Func_Unpacked_Invoke, Any_Arg) {
- Ref<Func> func =
- Ref<Func>::New(func_unpacked_any_arg<TVMFFITypeIndex::kTVMFFIInt>);
+ Ref<Func> func = Ref<Func>::New(func_unpacked_any_arg<TVMFFITypeIndex::kTVMFFIInt>);
func(1);
}
@@ -156,7 +152,7 @@ TEST(Func_Unpacked_Invoke_TypeError, TypeMismatch_0) {
try {
func(1.0, 2, c_str_raw, 4);
FAIL() << "No execption thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
EXPECT_STREQ(ex.what(),
"Mismatched type on argument #0 when calling: "
"`(0: int, 1: float, 2: const char *, 3: float) -> float`. "
@@ -169,10 +165,11 @@ TEST(Func_Unpacked_Invoke_TypeError, TypeMismatch_1) {
try {
func(DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, 1);
FAIL() << "No execption thrown";
- } catch (TVMError &ex) {
- EXPECT_STREQ(ex.what(), "Mismatched type on argument #2 when calling: "
- "`(0: dtype, 1: Device, 2: str) -> void`. "
- "Expected `str` but got `int`");
+ } catch (TVMError& ex) {
+ EXPECT_STREQ(ex.what(),
+ "Mismatched type on argument #2 when calling: "
+ "`(0: dtype, 1: Device, 2: str) -> void`. "
+ "Expected `str` but got `int`");
}
}
@@ -181,7 +178,7 @@ TEST(Func_Unpacked_Invoke_TypeError, ArgCountMismatch_0) {
try {
func(1, 2, c_str_raw);
FAIL() << "No execption thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
EXPECT_STREQ(ex.what(),
"Mismatched number of arguments when calling: "
"`(0: int, 1: float, 2: const char *, 3: float) -> float`. "
@@ -194,10 +191,11 @@ TEST(Func_Unpacked_Invoke_TypeError, ArgCountMismatch_1) {
try {
func(DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0});
FAIL() << "No execption thrown";
- } catch (TVMError &ex) {
- EXPECT_STREQ(ex.what(), "Mismatched number of arguments when calling: "
- "`(0: dtype, 1: Device, 2: str) -> void`. "
- "Expected 3 but got 2 arguments");
+ } catch (TVMError& ex) {
+ EXPECT_STREQ(ex.what(),
+ "Mismatched number of arguments when calling: "
+ "`(0: dtype, 1: Device, 2: str) -> void`. "
+ "Expected 3 but got 2 arguments");
}
}
@@ -207,9 +205,9 @@ TEST(Func_Unpacked_Invoke_TypeError, ReturnTypeMismatch_0) {
int ret = func(DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, "Hello");
(void)ret;
FAIL() << "No execption thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
EXPECT_STREQ(ex.what(), "Cannot convert from type `None` to `int`");
}
}
-} // namespace
+} // namespace
diff --git a/ffi/tests/cpp/test_list.cc b/ffi/tests/backup_cpp/test_list.cc
similarity index 88%
rename from ffi/tests/cpp/test_list.cc
rename to ffi/tests/backup_cpp/test_list.cc
index 53fd727af6..06b9d6a483 100644
--- a/ffi/tests/cpp/test_list.cc
+++ b/ffi/tests/backup_cpp/test_list.cc
@@ -1,4 +1,5 @@
#include <gtest/gtest.h>
+
#include <tvm/ffi/ffi.hpp>
namespace {
@@ -12,7 +13,7 @@ bool DeviceEqual(DLDevice a, DLDevice b) {
return a.device_type == b.device_type && a.device_id == b.device_id;
}
-void TestSizeCapacityClear(Ref<List> *list, int64_t size, int64_t capacity) {
+void TestSizeCapacityClear(Ref<List>* list, int64_t size, int64_t capacity) {
EXPECT_EQ(list->size(), size);
EXPECT_EQ(list->capacity(), capacity);
EXPECT_EQ(list->empty(), size == 0);
@@ -24,7 +25,7 @@ void TestSizeCapacityClear(Ref<List> *list, int64_t size, int64_t capacity) {
TEST(List_Constructor, Default) {
Ref<List> list = Ref<List>::New();
- TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
ASSERT_NE(list_ptr, nullptr);
EXPECT_EQ(list_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIList));
EXPECT_EQ(list_ptr->ref_cnt, 1);
@@ -44,8 +45,8 @@ TEST(List_Constructor, InitializerList) {
100, 1.0f, "Hi", DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, Ref<Object>::New(),
Ref<Object>()};
- auto test = [](Ref<List> *src) {
- auto *list_ptr = reinterpret_cast<const TVMFFIList *>(src->get());
+ auto test = [](Ref<List>* src) {
+ auto* list_ptr = reinterpret_cast<const TVMFFIList*>(src->get());
ASSERT_NE(list_ptr, nullptr);
EXPECT_EQ(list_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIList));
EXPECT_EQ(list_ptr->ref_cnt, 1);
@@ -53,7 +54,7 @@ TEST(List_Constructor, InitializerList) {
EXPECT_EQ(list_ptr->list_capacity, 7);
EXPECT_EQ(list_ptr->list_length, 7);
EXPECT_EQ(list_ptr->pool_capacity, 7);
- EXPECT_EQ(list_ptr->pool_length, 4); // string is not in the POD pool
+ EXPECT_EQ(list_ptr->pool_length, 4); // string is not in the POD pool
EXPECT_EQ(src->size(), 7);
EXPECT_EQ(src->capacity(), 7);
EXPECT_EQ(src->empty(), false);
@@ -68,7 +69,7 @@ TEST(List_PushBack, POD) {
ASSERT_NE(list.get(), nullptr);
list.push_back(100);
list.push_back(1.0f);
- TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
ASSERT_NE(list_ptr, nullptr);
EXPECT_EQ(list_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIList));
EXPECT_EQ(list_ptr->ref_cnt, 1);
@@ -88,7 +89,7 @@ TEST(List_PushBack, Obj) {
Ref<Object> obj2 = Ref<Object>::New();
list.push_back(obj1);
list.push_back(obj2);
- TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
ASSERT_NE(list_ptr, nullptr);
EXPECT_EQ(list_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIList));
EXPECT_EQ(list_ptr->ref_cnt, 1);
@@ -97,8 +98,8 @@ TEST(List_PushBack, Obj) {
EXPECT_EQ(list_ptr->list_length, 2);
EXPECT_EQ(list_ptr->pool_capacity, 0);
EXPECT_EQ(list_ptr->pool_length, 0);
- EXPECT_EQ((Object *)(list[0]), obj1.get());
- EXPECT_EQ((Object *)(list[1]), obj2.get());
+ EXPECT_EQ((Object*)(list[0]), obj1.get());
+ EXPECT_EQ((Object*)(list[1]), obj2.get());
TestSizeCapacityClear(&list, 2, List::kMinCapacity);
}
@@ -138,9 +139,9 @@ TEST(List_PushBack, Heterogeneous) {
std::string i_2 = list[i * k + 2];
DLDataType i_3 = list[i * k + 3];
DLDevice i_4 = list[i * k + 4];
- Object *i_5 = list[i * k + 5];
- Object *i_6 = list[i * k + 6];
- const char *i_7 = list[i * k + 7];
+ Object* i_5 = list[i * k + 5];
+ Object* i_6 = list[i * k + 6];
+ const char* i_7 = list[i * k + 7];
EXPECT_EQ(i_0, integer);
EXPECT_DOUBLE_EQ(i_1, fp);
EXPECT_EQ(i_2, str);
@@ -150,7 +151,7 @@ TEST(List_PushBack, Heterogeneous) {
EXPECT_EQ(i_6, nullptr);
EXPECT_STREQ(i_7, long_str.c_str());
}
- auto *list_ptr = reinterpret_cast<const TVMFFIList *>(list.get());
+ auto* list_ptr = reinterpret_cast<const TVMFFIList*>(list.get());
EXPECT_EQ(list_ptr->list_capacity, expected_capacity);
EXPECT_EQ(list_ptr->list_length, expected_size);
EXPECT_EQ(list_ptr->pool_capacity, expected_pool_capacity);
@@ -160,7 +161,7 @@ TEST(List_PushBack, Heterogeneous) {
TEST(List_Insert, Once) {
Ref<List> values = {100,
1.0,
- "Hi", //
+ "Hi", //
DLDataType{kDLInt, 32, 1},
DLDevice{kDLCPU, 0},
Ref<Object>::New(),
@@ -192,7 +193,7 @@ TEST(List_Insert, Error_0) {
try {
list.insert(-1, 1.0);
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
EXPECT_STREQ(ex.what(), "Indexing `-1` of a list of size 3");
}
}
@@ -202,7 +203,7 @@ TEST(List_Insert, Error_1) {
try {
list.insert(4, 1.0);
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
EXPECT_STREQ(ex.what(), "Indexing `4` of a list of size 3");
}
}
@@ -224,7 +225,7 @@ TEST(List_Resize, Expand) {
EXPECT_EQ(int32_t(list[0]), 100);
EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
EXPECT_STREQ(list[2], "Hi");
- EXPECT_EQ(list[3].operator void *(), nullptr);
+ EXPECT_EQ(list[3].operator void*(), nullptr);
}
TEST(List_Reserve, Shrink) {
@@ -259,7 +260,7 @@ TEST(List_SetItem, PodToPod) {
}
EXPECT_EQ(list.size(), 3);
EXPECT_EQ(list.capacity(), 3);
- TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
EXPECT_EQ(list_ptr->list_capacity, 3);
EXPECT_EQ(list_ptr->list_length, 3);
EXPECT_EQ(list_ptr->pool_capacity, 24);
@@ -267,7 +268,7 @@ TEST(List_SetItem, PodToPod) {
}
TEST(List_SetItem, ObjToPod) {
- Ref<List> list = {100, 1.0, "Hi"}; //
+ Ref<List> list = {100, 1.0, "Hi"}; //
for (int i = 0; i < 16; ++i) {
list[2] = i;
EXPECT_EQ(list.size(), 3);
@@ -278,7 +279,7 @@ TEST(List_SetItem, ObjToPod) {
}
EXPECT_EQ(list.size(), 3);
EXPECT_EQ(list.capacity(), 3);
- TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
EXPECT_EQ(list_ptr->list_capacity, 3);
EXPECT_EQ(list_ptr->list_length, 3);
EXPECT_EQ(list_ptr->pool_capacity, 24);
@@ -292,13 +293,13 @@ TEST(List_SetItem, PodToObj) {
list[0] = obj;
EXPECT_EQ(list.size(), 3);
EXPECT_EQ(list.capacity(), 3);
- EXPECT_EQ((Object *)(list[0]), obj.get());
+ EXPECT_EQ((Object*)(list[0]), obj.get());
EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
EXPECT_STREQ(list[2], "Hi");
}
EXPECT_EQ(list.size(), 3);
EXPECT_EQ(list.capacity(), 3);
- TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
EXPECT_EQ(list_ptr->list_capacity, 3);
EXPECT_EQ(list_ptr->list_length, 3);
EXPECT_EQ(list_ptr->pool_capacity, 3);
@@ -314,11 +315,11 @@ TEST(List_SetItem, ObjToObj) {
EXPECT_EQ(list.capacity(), 3);
EXPECT_EQ(int32_t(list[0]), 100);
EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
- EXPECT_EQ((Object *)(list[2]), obj.get());
+ EXPECT_EQ((Object*)(list[2]), obj.get());
}
EXPECT_EQ(list.size(), 3);
EXPECT_EQ(list.capacity(), 3);
- TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
EXPECT_EQ(list_ptr->list_capacity, 3);
EXPECT_EQ(list_ptr->list_length, 3);
EXPECT_EQ(list_ptr->pool_capacity, 3);
@@ -356,10 +357,10 @@ TEST(List_PopBack, Heterogeneous) {
EXPECT_PRED2(DeviceEqual, DLDevice(list[4]), device);
}
if (m > 5) {
- EXPECT_EQ((Object *)(list[5]), obj.get());
+ EXPECT_EQ((Object*)(list[5]), obj.get());
}
if (m > 6) {
- EXPECT_EQ((Object *)(list[6]), nullptr);
+ EXPECT_EQ((Object*)(list[6]), nullptr);
}
}
EXPECT_EQ(list.size(), 0);
@@ -369,7 +370,7 @@ TEST(List_PopBack, Heterogeneous) {
try {
list.pop_back();
FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
+ } catch (TVMError& ex) {
EXPECT_STREQ(ex.what(), "Indexing `-1` of a list of size 0");
}
}
@@ -390,8 +391,8 @@ TEST(List_Erase, Front) {
EXPECT_STREQ(list[1], "Hi");
EXPECT_PRED2(DTypeEqual, DLDataType(list[2]), dtype);
EXPECT_PRED2(DeviceEqual, DLDevice(list[3]), device);
- EXPECT_EQ((Object *)(list[4]), obj.get());
- EXPECT_EQ((Object *)(list[5]), nullptr);
+ EXPECT_EQ((Object*)(list[4]), obj.get());
+ EXPECT_EQ((Object*)(list[5]), nullptr);
}
TEST(List_Erase, Back) {
@@ -410,8 +411,8 @@ TEST(List_Erase, Back) {
EXPECT_STREQ(list[1], "Hi");
EXPECT_PRED2(DTypeEqual, DLDataType(list[2]), dtype);
EXPECT_PRED2(DeviceEqual, DLDevice(list[3]), device);
- EXPECT_EQ((Object *)(list[4]), obj.get());
- EXPECT_EQ((Object *)(list[5]), nullptr);
+ EXPECT_EQ((Object*)(list[4]), obj.get());
+ EXPECT_EQ((Object*)(list[5]), nullptr);
}
TEST(List_Erase, Mid) {
@@ -430,8 +431,8 @@ TEST(List_Erase, Mid) {
EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
EXPECT_STREQ(list[2], "Hi");
EXPECT_PRED2(DeviceEqual, DLDevice(list[3]), device);
- EXPECT_EQ((Object *)(list[4]), obj.get());
- EXPECT_EQ((Object *)(list[5]), nullptr);
+ EXPECT_EQ((Object*)(list[4]), obj.get());
+ EXPECT_EQ((Object*)(list[5]), nullptr);
}
TEST(List_Iter, Test) {
@@ -458,4 +459,4 @@ TEST(List_RevIter, Test) {
}
}
-} // namespace
+} // namespace
diff --git a/ffi/tests/cpp/test_ref.cc b/ffi/tests/backup_cpp/test_ref.cc
similarity index 80%
rename from ffi/tests/cpp/test_ref.cc
rename to ffi/tests/backup_cpp/test_ref.cc
index 3c9dc0561d..197f2def88 100644
--- a/ffi/tests/cpp/test_ref.cc
+++ b/ffi/tests/backup_cpp/test_ref.cc
@@ -1,4 +1,5 @@
#include <gtest/gtest.h>
+
#include <tvm/ffi/ffi.hpp>
#include <unordered_map>
@@ -7,30 +8,30 @@ using namespace tvm::ffi;
using tvm::ffi::details::StrPad;
using tvm::ffi::details::StrStd;
-using ObjDeleter = void (*)(void *);
+using ObjDeleter = void (*)(void*);
struct AllocRecorder {
- std::unordered_map<void *, ObjDeleter> deleters;
+ std::unordered_map<void*, ObjDeleter> deleters;
- void Alloc(void *ptr) {
- deleters[ptr] = reinterpret_cast<TVMFFIAny *>(ptr)->deleter;
- reinterpret_cast<TVMFFIAny *>(ptr)->deleter = AllocRecorder::Deleter;
+ void Alloc(void* ptr) {
+ deleters[ptr] = reinterpret_cast<TVMFFIAny*>(ptr)->deleter;
+ reinterpret_cast<TVMFFIAny*>(ptr)->deleter = AllocRecorder::Deleter;
}
- void Delete(void *ptr) {
+ void Delete(void* ptr) {
ASSERT_EQ(deleters.count(ptr), 1);
ObjDeleter d = this->deleters[ptr];
d(ptr);
deleters.erase(ptr);
}
- bool IsDeletedImpl(void *ptr) { return deleters.count(ptr) == 0; }
+ bool IsDeletedImpl(void* ptr) { return deleters.count(ptr) == 0; }
- static void Deleter(void *ptr) { AllocRecorder::Global()->Delete(ptr); }
+ static void Deleter(void* ptr) { AllocRecorder::Global()->Delete(ptr); }
- static bool IsDeleted(void *ptr) { return AllocRecorder::Global()->IsDeletedImpl(ptr); }
+ static bool IsDeleted(void* ptr) { return AllocRecorder::Global()->IsDeletedImpl(ptr); }
- static AllocRecorder *Global() {
+ static AllocRecorder* Global() {
static AllocRecorder inst;
return &inst;
}
@@ -41,15 +42,15 @@ struct TestAllocator {
using Allocator = typename ::tvm::ffi::GetAllocator<ObjectType>::Type;
template <typename... Args>
- TVM_FFI_INLINE static ObjectType *New(Args &&...args) {
- ObjectType *ret = Allocator::New(std::forward<Args>(args)...);
+ TVM_FFI_INLINE static ObjectType* New(Args&&... args) {
+ ObjectType* ret = Allocator::New(std::forward<Args>(args)...);
AllocRecorder::Global()->Alloc(ret);
return ret;
}
template <typename PadType, typename... Args>
- TVM_FFI_INLINE static ObjectType *NewWithPad(size_t pad_size, Args &&...args) {
- ObjectType *ret =
+ TVM_FFI_INLINE static ObjectType* NewWithPad(size_t pad_size, Args&&... args) {
+ ObjectType* ret =
Allocator::template NewWithPad<PadType>(pad_size, std::forward<Args>(args)...);
AllocRecorder::Global()->Alloc(ret);
return ret;
@@ -58,11 +59,11 @@ struct TestAllocator {
int64_t FuncCall(int64_t x) { return x + 1; }
-int32_t GetRefCount(void *obj) { return reinterpret_cast<TVMFFIAny *>(obj)->ref_cnt; }
+int32_t GetRefCount(void* obj) { return reinterpret_cast<TVMFFIAny*>(obj)->ref_cnt; }
-int32_t GetTypeIndex(void *obj) { return reinterpret_cast<TVMFFIAny *>(obj)->type_index; }
+int32_t GetTypeIndex(void* obj) { return reinterpret_cast<TVMFFIAny*>(obj)->type_index; }
-ObjDeleter GetDeleter(void *obj) { return reinterpret_cast<TVMFFIAny *>(obj)->deleter; }
+ObjDeleter GetDeleter(void* obj) { return reinterpret_cast<TVMFFIAny*>(obj)->deleter; }
TEST(Ref_Constructor_0_Default, Default) {
Ref<Object> ref;
@@ -70,7 +71,7 @@ TEST(Ref_Constructor_0_Default, Default) {
}
TEST(Ref_Constructor_1_Ptr, SameType) {
- Object *obj = TestAllocator<Object>::New();
+ Object* obj = TestAllocator<Object>::New();
{
Ref<Object> ref(obj);
EXPECT_EQ(ref.get(), obj);
@@ -80,17 +81,17 @@ TEST(Ref_Constructor_1_Ptr, SameType) {
}
TEST(Ref_Constructor_1_Ptr, SubType) {
- Str *obj = TestAllocator<Str>::New("Hello world");
+ Str* obj = TestAllocator<Str>::New("Hello world");
{
Ref<Object> ref(obj);
- EXPECT_EQ(ref.get(), static_cast<void *>(obj));
+ EXPECT_EQ(ref.get(), static_cast<void*>(obj));
EXPECT_EQ(GetRefCount(obj), 1);
}
EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
}
TEST(Ref_Constructor_2_Ref, SameType_Copy) {
- Object *obj = TestAllocator<Object>::New();
+ Object* obj = TestAllocator<Object>::New();
{
Ref<Object> ref1(obj);
EXPECT_EQ(GetRefCount(obj), 1);
@@ -104,7 +105,7 @@ TEST(Ref_Constructor_2_Ref, SameType_Copy) {
}
TEST(Ref_Constructor_2_Ref, SameType_Move) {
- Object *obj = TestAllocator<Object>::New();
+ Object* obj = TestAllocator<Object>::New();
{
Ref<Object> ref1(obj);
EXPECT_EQ(GetRefCount(obj), 1);
@@ -118,7 +119,7 @@ TEST(Ref_Constructor_2_Ref, SameType_Move) {
}
TEST(Ref_Constructor_2_Ref, SubType_Copy) {
- Str *obj = TestAllocator<Str>::New("Hello world");
+ Str* obj = TestAllocator<Str>::New("Hello world");
{
Ref<Str> ref1(obj);
EXPECT_EQ(GetRefCount(obj), 1);
@@ -132,7 +133,7 @@ TEST(Ref_Constructor_2_Ref, SubType_Copy) {
}
TEST(Ref_Constructor_2_Ref, SubType_Move) {
- Str *obj = TestAllocator<Str>::New("Hello world");
+ Str* obj = TestAllocator<Str>::New("Hello world");
{
Ref<Str> ref1(obj);
EXPECT_EQ(GetRefCount(obj), 1);
@@ -146,7 +147,7 @@ TEST(Ref_Constructor_2_Ref, SubType_Move) {
}
TEST(Ref_Constructor_3_AnyView, Copy) {
- Object *obj = TestAllocator<Object>::New();
+ Object* obj = TestAllocator<Object>::New();
{
Ref<Object> ref1(obj);
EXPECT_EQ(GetRefCount(obj), 1);
@@ -161,7 +162,7 @@ TEST(Ref_Constructor_3_AnyView, Copy) {
}
TEST(Ref_Constructor_3_AnyView, Move) {
- Object *obj = TestAllocator<Object>::New();
+ Object* obj = TestAllocator<Object>::New();
{
Ref<Object> ref1(obj);
EXPECT_EQ(GetRefCount(obj), 1);
@@ -182,7 +183,7 @@ TEST(Ref_Constructor_3_AnyView, Move) {
}
TEST(Ref_Constructor_4_Any, Copy) {
- Object *obj = TestAllocator<Object>::New();
+ Object* obj = TestAllocator<Object>::New();
{
Ref<Object> ref1(obj);
EXPECT_EQ(GetRefCount(obj), 1);
@@ -198,7 +199,7 @@ TEST(Ref_Constructor_4_Any, Copy) {
}
TEST(Ref_Constructor_4_Any, Move) {
- Object *obj = TestAllocator<Object>::New();
+ Object* obj = TestAllocator<Object>::New();
{
Ref<Object> ref1(obj);
EXPECT_EQ(GetRefCount(obj), 1);
@@ -231,7 +232,7 @@ TEST(Ref_New, Func) {
}
TEST(Ref_New, RawStr) {
- const char *str = "Hello world";
+ const char* str = "Hello world";
Ref<Str> ref = Ref<Str>::New(str);
EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
EXPECT_EQ(GetRefCount(ref.get()), 1);
@@ -283,11 +284,11 @@ TEST(Ref_Stringify, Str) {
}
TEST(Ref_Misc, MoveToRaw) {
- TVMFFIAny *str = reinterpret_cast<TVMFFIAny *>(Ref<Str>::New("Hello world").MoveToRawObjPtr());
+ TVMFFIAny* str = reinterpret_cast<TVMFFIAny*>(Ref<Str>::New("Hello world").MoveToRawObjPtr());
EXPECT_EQ(GetTypeIndex(str), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
EXPECT_EQ(GetRefCount(str), 1);
EXPECT_EQ(GetDeleter(str), DefaultObjectAllocator<StrPad>::Deleter);
str->deleter(str);
}
-} // namespace
+} // namespace
diff --git a/ffi/tests/cpp/test_str.cc b/ffi/tests/backup_cpp/test_str.cc
similarity index 81%
rename from ffi/tests/cpp/test_str.cc
rename to ffi/tests/backup_cpp/test_str.cc
index bd91f241f6..c0b38c2935 100644
--- a/ffi/tests/cpp/test_str.cc
+++ b/ffi/tests/backup_cpp/test_str.cc
@@ -1,11 +1,13 @@
#include <gtest/gtest.h>
+
#include <tvm/ffi/ffi.hpp>
namespace {
using namespace tvm::ffi;
-const char c_str_long[] = "Hello, World! This is an extremely long string to "
- "avoid any on-stack optimization.";
+const char c_str_long[] =
+ "Hello, World! This is an extremely long string to "
+ "avoid any on-stack optimization.";
TEST(Str, CopyFromStdString) {
std::string std_str = "Hello, World!";
@@ -17,9 +19,9 @@ TEST(Str, CopyFromStdString) {
TEST(Str, MoveFromStdString_0) {
std::string std_str = c_str_long;
- const void *data = std_str.data();
+ const void* data = std_str.data();
Ref<Str> str = Ref<Str>::New(std::move(std_str));
- EXPECT_EQ(static_cast<const void *>(str->data()), data);
+ EXPECT_EQ(static_cast<const void*>(str->data()), data);
EXPECT_STREQ(str->c_str(), c_str_long);
}
@@ -44,4 +46,4 @@ TEST(Str, CopyFromCharArray) {
EXPECT_EQ(str->size(), 17);
}
-} // namespace
+} // namespace
diff --git a/ffi/tests/cpp/test_type_dyn.cc b/ffi/tests/backup_cpp/test_type_dyn.cc
similarity index 82%
rename from ffi/tests/cpp/test_type_dyn.cc
rename to ffi/tests/backup_cpp/test_type_dyn.cc
index 45910528b3..b93595dc3b 100644
--- a/ffi/tests/cpp/test_type_dyn.cc
+++ b/ffi/tests/backup_cpp/test_type_dyn.cc
@@ -1,4 +1,5 @@
#include <gtest/gtest.h>
+
#include <tvm/ffi/ffi.hpp>
#if TVM_FFI_ALLOW_DYN_TYPE == 1
@@ -18,8 +19,7 @@ struct SubTestObj : public TestObj {
TVM_FFI_DEF_DYN_TYPE(SubTestObj, TestObj, "test.SubTestObj");
};
-void CheckAncestor(int32_t num, const int32_t *ancestors,
- std::vector<int32_t> expected) {
+void CheckAncestor(int32_t num, const int32_t* ancestors, std::vector<int32_t> expected) {
EXPECT_EQ(num, expected.size());
for (int i = 0; i < num; ++i) {
EXPECT_EQ(ancestors[i], expected[i]);
@@ -27,8 +27,7 @@ void CheckAncestor(int32_t num, const int32_t *ancestors,
}
TEST(DynTypeInfo, TestObj) {
- EXPECT_GE(TestObj::_type_index,
- static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDynObjectBegin));
+ EXPECT_GE(TestObj::_type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDynObjectBegin));
EXPECT_STRCASEEQ(TestObj::_type_key, "test.TestObj");
EXPECT_EQ(TestObj::_type_depth, 1);
CheckAncestor(TestObj::_type_depth, TestObj::_type_ancestors.data(),
@@ -36,14 +35,12 @@ TEST(DynTypeInfo, TestObj) {
}
TEST(DynTypeInfo, SubTestObj) {
- EXPECT_GE(SubTestObj::_type_index,
- static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDynObjectBegin));
+ EXPECT_GE(SubTestObj::_type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDynObjectBegin));
EXPECT_NE(SubTestObj::_type_index, TestObj::_type_index);
EXPECT_STRCASEEQ(SubTestObj::_type_key, "test.SubTestObj");
EXPECT_EQ(SubTestObj::_type_depth, 2);
CheckAncestor(SubTestObj::_type_depth, SubTestObj::_type_ancestors.data(),
- {static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject),
- TestObj::_type_index});
+ {static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject), TestObj::_type_index});
}
TEST(DynTypeInheritance, TestObj) {
@@ -66,6 +63,6 @@ TEST(DynTypeInheritance, SubTestObj) {
EXPECT_FALSE(obj->IsInstance<Str>());
}
-} // namespace
+} // namespace
#endif
diff --git a/ffi/tests/cpp/test_type_static.cc b/ffi/tests/backup_cpp/test_type_static.cc
similarity index 87%
rename from ffi/tests/cpp/test_type_static.cc
rename to ffi/tests/backup_cpp/test_type_static.cc
index aebeba7c3e..abb21af2f5 100644
--- a/ffi/tests/cpp/test_type_static.cc
+++ b/ffi/tests/backup_cpp/test_type_static.cc
@@ -1,4 +1,5 @@
#include <gtest/gtest.h>
+
#include <tvm/ffi/ffi.hpp>
namespace {
@@ -15,8 +16,7 @@ struct SubType : public Object {
int64_t FuncCall(int64_t x) { return x + 1; }
-void CheckAncestor(int32_t num, const int32_t *ancestors,
- std::vector<int32_t> expected) {
+void CheckAncestor(int32_t num, const int32_t* ancestors, std::vector<int32_t> expected) {
EXPECT_EQ(num, expected.size());
for (int i = 0; i < num; ++i) {
EXPECT_EQ(ancestors[i], expected[i]);
@@ -28,16 +28,14 @@ static_assert(IsObject<Func>, "IsObject<Func> == true");
static_assert(IsObject<Str>, "IsObject<Str> == true");
TEST(StaticTypeInfo, Object) {
- EXPECT_EQ(Object::_type_index,
- static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
+ EXPECT_EQ(Object::_type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
EXPECT_STRCASEEQ(Object::_type_key, "object.Object");
EXPECT_EQ(Object::_type_depth, 0);
CheckAncestor(Object::_type_depth, Object::_type_ancestors.data(), {});
}
TEST(StaticTypeInfo, Func) {
- EXPECT_EQ(Func::_type_index,
- static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
+ EXPECT_EQ(Func::_type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
EXPECT_STRCASEEQ(Func::_type_key, "object.Func");
EXPECT_EQ(Func::_type_depth, 1);
CheckAncestor(Func::_type_depth, Func::_type_ancestors.data(),
@@ -45,8 +43,7 @@ TEST(StaticTypeInfo, Func) {
}
TEST(StaticTypeInfo, Str) {
- EXPECT_EQ(Str::_type_index,
- static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
+ EXPECT_EQ(Str::_type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
EXPECT_STRCASEEQ(Str::_type_key, "object.Str");
EXPECT_EQ(Str::_type_depth, 1);
CheckAncestor(Str::_type_depth, Str::_type_ancestors.data(),
@@ -111,9 +108,9 @@ TEST(StaticTypeSubclass, Exception) {
try {
Ref<SubType>::New(1);
FAIL() << "No exception thrown";
- } catch (std::runtime_error &ex) {
+ } catch (std::runtime_error& ex) {
EXPECT_STREQ(ex.what(), "New Error");
}
}
-} // namespace
+} // namespace
diff --git a/ffi/tests/cpp/CMakeLists.txt b/ffi/tests/cpp/CMakeLists.txt
index 539115f24d..62543756b9 100644
--- a/ffi/tests/cpp/CMakeLists.txt
+++ b/ffi/tests/cpp/CMakeLists.txt
@@ -17,8 +17,8 @@ set_target_properties(
add_cxx_warning(tvm_ffi_tests)
add_sanitizer_address(tvm_ffi_tests)
target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi)
-if (TVM_FFI_ALLOW_DYN_TYPE)
- add_sanitizer_address(tvm_ffi_registry_shared)
+
+if (TVM_FFI_BUILD_REGISTRY)
target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi_registry_shared)
endif()
diff --git a/ffi/tests/cpp/test_any.cc b/ffi/tests/cpp/test_any.cc
index a57829bd0a..2cb3e8b95b 100644
--- a/ffi/tests/cpp/test_any.cc
+++ b/ffi/tests/cpp/test_any.cc
@@ -1,635 +1,167 @@
+/*
+ * 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.
+ */
#include <gtest/gtest.h>
-#include <tvm/ffi/ffi.hpp>
+#include <tvm/ffi/any.h>
+#include <tvm/ffi/memory.h>
-namespace {
-using namespace tvm::ffi;
-
-template <typename SrcType, typename Checker>
-void TestAnyConstructor(Checker check, TVMFFITypeIndex expected_type_index, const SrcType &source) {
- Any v(source);
- EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
- EXPECT_EQ(v.ref_cnt, 0);
- check(&v, source);
-};
-
-int64_t FuncCall(int64_t x) { return x + 1; }
-
-std::vector<Any> AnyArrayFactory() {
- return std::vector<Any>{
- Any(nullptr),
- Any(1),
- Any(2.5),
- Any(reinterpret_cast<void *>(FuncCall)),
- Any(DLDevice{kDLCPU, 0}),
- Any(DLDataType{kDLInt, 32, 1}),
- Any("Hello (raw str)"),
- Any(Ref<Object>::New()),
- Any(Ref<Func>::New(FuncCall)),
- Any(std::string("World (std::string)")),
- Any(Ref<Str>::New("Hello World (Ref<Str>)")),
- };
-}
-
-template <typename SrcType>
-void TestAnyStringify(const SrcType &source, TVMFFITypeIndex expected_type_index,
- const std::string &expected) {
- Any v(source);
- EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
- EXPECT_EQ(v.str()->c_str(), expected);
-}
-
-template <typename SrcType, typename Checker>
-void TestAnyStringifyChecker(const SrcType &source, TVMFFITypeIndex expected_type_index,
- Checker check) {
- Any v(source);
- EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
- check(v);
-}
-
-void CheckAnyRefCnt(const TVMFFIAny *v) {
- if (v->type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
- EXPECT_EQ(v->v_obj->ref_cnt, 1);
- }
-}
-
-TEST(Any_Constructor_0_Default, Default) {
- Any v;
- EXPECT_EQ(v.type_index, 0);
- EXPECT_EQ(v.ref_cnt, 0);
- EXPECT_EQ(v.v_int64, 0);
-}
-
-TEST(Any_Constructor_1_Any, Copy) {
- Any v1(1);
- Any v2(v1);
- EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v1.v_int64, 1);
- EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v2.v_int64, 1);
-}
-
-TEST(Any_Constructor_1_Any, Move) {
- Any v1(1);
- Any v2(std::move(v1));
- EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone));
- EXPECT_EQ(v1.v_int64, 0);
- EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v2.v_int64, 1);
-}
-
-TEST(Any_Constructor_2_AnyView, Copy) {
- AnyView v1(1);
- Any v2(v1);
- EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v1.v_int64, 1);
- EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v2.v_int64, 1);
-}
-
-TEST(Any_Constructor_2_AnyView, Move) {
- AnyView v1(1);
- Any v2(std::move(v1));
- EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone));
- EXPECT_EQ(v1.v_int64, 0);
- EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v2.v_int64, 1);
-}
-
-TEST(Any_Constructor_3_Ref, Copy) {
- Ref<Object> obj = Ref<Object>::New();
- Any v(obj);
- const TVMFFIAny *v_obj = v.v_obj;
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
- EXPECT_EQ(v.ref_cnt, 0);
- EXPECT_EQ(v_obj, static_cast<const void *>(obj.get()));
- EXPECT_EQ(v_obj->ref_cnt, 2);
-}
+#include "./testing_object.h"
-TEST(Any_Constructor_3_Ref, Move) {
- Ref<Object> obj = Ref<Object>::New();
- Any v(std::move(obj));
- const TVMFFIAny *v_obj = v.v_obj;
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
- EXPECT_EQ(v.ref_cnt, 0);
- EXPECT_EQ(v_obj->ref_cnt, 1);
- EXPECT_EQ(obj.get(), nullptr);
-}
-
-TEST(Any_Constructor_4_TypeTraits, Integer) {
- auto check = [](TVMFFIAny *v, int64_t source) -> void { EXPECT_EQ(v->v_int64, source); };
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int8_t>(1));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int16_t>(2));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int32_t>(3));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int64_t>(4));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint8_t>(1));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint16_t>(2));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint32_t>(3));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint64_t>(4));
-}
-
-TEST(Any_Constructor_4_TypeTraits, Float) {
- auto check = [](TVMFFIAny *v, double source) -> void { EXPECT_EQ(v->v_float64, source); };
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(3));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(4));
-}
-
-TEST(Any_Constructor_4_TypeTraits, Ptr) {
- int p = 4;
- auto check = [](TVMFFIAny *v, void *source) -> void { EXPECT_EQ(v->v_ptr, source); };
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFINone, static_cast<void *>(nullptr));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIPtr, static_cast<void *>(&p));
-}
-
-TEST(Any_Constructor_4_TypeTraits, Device) {
- auto check = [](TVMFFIAny *v, const DLDevice &source) -> void {
- EXPECT_EQ(v->v_device.device_type, source.device_type);
- EXPECT_EQ(v->v_device.device_id, source.device_id);
- };
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIDevice, DLDevice{kDLCPU, 0});
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIDevice, DLDevice{kDLCUDA, 1});
-}
-
-TEST(Any_Constructor_4_TypeTraits, DataType) {
- auto check = [](TVMFFIAny *v, const DLDataType &source) -> void {
- EXPECT_EQ(v->v_dtype.code, source.code);
- EXPECT_EQ(v->v_dtype.bits, source.bits);
- EXPECT_EQ(v->v_dtype.lanes, source.lanes);
- };
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIDataType, DLDataType{kDLInt, 32, 1});
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIDataType, DLDataType{kDLUInt, 0, 0});
-}
-
-TEST(Any_Constructor_4_TypeTraits, RawStr) {
- auto check = [](TVMFFIAny *v, const char *source) -> void {
- Str *str = static_cast<Str *>(v->v_ptr);
- EXPECT_STREQ(str->c_str(), source);
- };
- const char *empty = "";
- const char *hello = "hello";
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, empty);
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, hello);
-}
-
-TEST(Any_Constructor_4_TypeTraits, CharArray) {
- auto check = [](TVMFFIAny *v, const char *source) -> void {
- Str *str = static_cast<Str *>(v->v_ptr);
- EXPECT_STREQ(str->c_str(), source);
- };
- const char empty[] = "";
- const char hello[] = "hello";
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, empty);
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, hello);
-}
-
-TEST(Any_Constructor_4_TypeTraits, StdString) {
- auto check = [](TVMFFIAny *v, const std::string &source) -> void {
- Str *str = static_cast<Str *>(v->v_ptr);
- EXPECT_EQ(str->c_str(), source);
- };
- std::string empty = "";
- std::string hello = "hello";
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, hello);
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, empty);
-}
-
-TEST(Any_Constructor_5_Object_Ptr, Object) {
- Ref<Object> obj = Ref<Object>::New();
- TVMFFIAny *ptr = reinterpret_cast<TVMFFIAny *>(obj.get());
- {
- Any v(obj.get());
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
- EXPECT_EQ(v.v_obj->ref_cnt, 2);
- EXPECT_EQ(v.v_obj, ptr);
- }
- EXPECT_EQ(ptr->ref_cnt, 1);
-}
-
-TEST(Any_Constructor_5_Object_Ptr, Func) {
- Ref<Func> func = Ref<Func>::New(FuncCall);
- TVMFFIAny *ptr = reinterpret_cast<TVMFFIAny *>(func.get());
- {
- Any v(func.get());
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
- EXPECT_EQ(v.v_obj->ref_cnt, 2);
- EXPECT_EQ(v.v_obj, ptr);
- }
- EXPECT_EQ(ptr->ref_cnt, 1);
-}
-
-TEST(Any_Constructor_5_Object_Ptr, Str) {
- Ref<Str> str = Ref<Str>::New("hello");
- TVMFFIAny *ptr = reinterpret_cast<TVMFFIAny *>(str.get());
- {
- Any v(str.get());
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
- EXPECT_EQ(v.v_obj->ref_cnt, 2);
- EXPECT_EQ(v.v_obj, ptr);
- }
- EXPECT_EQ(ptr->ref_cnt, 1);
-}
-
-TEST(Any_Converter_0_TypeTraits, Integer) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
- auto convert = [&]() -> int64_t { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
- EXPECT_EQ(convert(), 1);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `int`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_0_TypeTraits, Float) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
- auto convert = [&]() -> double { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
- EXPECT_EQ(convert(), 1.0);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFloat)) {
- EXPECT_EQ(convert(), 2.5);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `float`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_0_TypeTraits, Ptr) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
- auto convert = [&]() -> void * { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- EXPECT_EQ(convert(), nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIPtr)) {
- EXPECT_EQ(convert(), reinterpret_cast<void *>(&FuncCall));
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
- EXPECT_EQ(convert(), v.v_str);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Ptr`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_0_TypeTraits, Device) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
- auto convert = [&]() -> DLDevice { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDevice)) {
- EXPECT_EQ(convert().device_type, kDLCPU);
- EXPECT_EQ(convert().device_id, 0);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Device`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_0_TypeTraits, DataType) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
- auto convert = [&]() -> DLDataType { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDataType)) {
- EXPECT_EQ(convert().code, kDLInt);
- EXPECT_EQ(convert().bits, 32);
- EXPECT_EQ(convert().lanes, 1);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `dtype`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_0_TypeTraits, RawStr) {
- std::vector<Any> vs = AnyArrayFactory();
- int counter = 0;
- for (const Any &v : vs) {
- auto convert = [&]() -> const char * { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- ++counter;
- EXPECT_LE(counter, 3);
- if (counter == 1) {
- EXPECT_STREQ(convert(), "Hello (raw str)");
- } else if (counter == 2) {
- EXPECT_STREQ(convert(), "World (std::string)");
- } else if (counter == 3) {
- EXPECT_STREQ(convert(), "Hello World (Ref<Str>)");
- }
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `const char *`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_1_AnyView, Any) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
- auto convert = [&]() -> AnyView { return v; };
- {
- AnyView ret = convert();
- EXPECT_EQ(ret.type_index, v.type_index);
- EXPECT_EQ(ret.ref_cnt, 0);
- EXPECT_EQ(v.ref_cnt, 0);
- EXPECT_EQ(ret.v_obj, v.v_obj);
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_2_Ref, Object) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
- auto convert = [&]() -> Ref<Object> { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Ref<Object> ret = convert();
- EXPECT_EQ(ret.get(), nullptr);
- } else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
- Ref<Object> ret = convert();
- EXPECT_EQ(ret.get(), static_cast<void *>(v.v_obj));
- EXPECT_EQ(v.v_obj->ref_cnt, 2);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Object`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_2_Ref, Func) {
- std::vector<Any> views = AnyArrayFactory();
- for (const Any &v : views) {
- auto convert = [&]() -> Ref<Func> { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Ref<Func> ret = convert();
- EXPECT_EQ(ret.get(), nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
- Ref<Func> ret = convert();
- EXPECT_EQ(ret.get(), static_cast<void *>(v.v_obj));
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Func`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_2_Ref, Str) {
- int counter = 0;
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
- auto convert = [&]() -> Ref<Str> { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Ref<Str> ret = convert();
- EXPECT_EQ(ret.get(), nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- Ref<Str> ret = convert();
- ++counter;
- EXPECT_LE(counter, 3);
- if (counter == 1) {
- EXPECT_STREQ(ret->c_str(), "Hello (raw str)");
- } else if (counter == 2) {
- EXPECT_STREQ(ret->c_str(), "World (std::string)");
- } else {
- EXPECT_STREQ(ret->c_str(), "Hello World (Ref<Str>)");
- }
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Str`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_3_Object_Ptr, Object) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any &v : vs) {
- auto convert = [&]() -> Object * { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Object *ret = convert();
- EXPECT_EQ(ret, nullptr);
- } else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
- Object *ret = convert();
- EXPECT_EQ(ret, static_cast<void *>(v.v_obj));
- EXPECT_EQ(v.v_obj->ref_cnt, 1);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Object *`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_3_Object_Ptr, Func) {
- std::vector<Any> views = AnyArrayFactory();
- for (const Any &v : views) {
- auto convert = [&]() -> Func * { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Func *ret = convert();
- EXPECT_EQ(ret, nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
- Func *ret = convert();
- EXPECT_EQ(ret, reinterpret_cast<Func *>(v.v_ptr));
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Func *`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_3_Object_Ptr, Str) {
- std::vector<Any> vs = AnyArrayFactory();
- int counter = 0;
- for (const Any &v : vs) {
- auto convert = [&]() -> Str * { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Str *ret = convert();
- EXPECT_EQ(ret, nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- Str *ret = convert();
- ++counter;
- EXPECT_LE(counter, 3);
- if (counter == 1) {
- EXPECT_STREQ(ret->c_str(), "Hello (raw str)");
- } else if (counter == 2) {
- EXPECT_STREQ(ret->c_str(), "World (std::string)");
- } else {
- EXPECT_STREQ(ret->c_str(), "Hello World (Ref<Str>)");
- }
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError &ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Str *`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Stringify, Integer) {
- TestAnyStringify<int8_t>(-13, TVMFFITypeIndex::kTVMFFIInt, "-13");
- TestAnyStringify<int16_t>(-5, TVMFFITypeIndex::kTVMFFIInt, "-5");
- TestAnyStringify<int32_t>(0, TVMFFITypeIndex::kTVMFFIInt, "0");
- TestAnyStringify<int64_t>(1, TVMFFITypeIndex::kTVMFFIInt, "1");
-}
-
-TEST(Any_Stringify, Float) {
- auto check = [](const Any &v) -> void {
- std::string str = v.str()->c_str();
- double f_str = std::stod(str);
- double f_src = v.v_float64;
- EXPECT_NEAR(f_src, f_str, 1e-5);
- };
- TestAnyStringifyChecker<float>(float(-3.14), TVMFFITypeIndex::kTVMFFIFloat, check);
- TestAnyStringifyChecker<double>(0.0, TVMFFITypeIndex::kTVMFFIFloat, check);
-}
-
-TEST(Any_Stringify, Ptr) {
- auto check = [](const Any &v) -> void {
- std::string str = v.str()->c_str();
- EXPECT_GT(str.size(), 2);
- };
- TestAnyStringify<void *>(nullptr, TVMFFITypeIndex::kTVMFFINone, "None");
- TestAnyStringifyChecker<void *>(reinterpret_cast<void *>(FuncCall), TVMFFITypeIndex::kTVMFFIPtr,
- check);
-}
-
-TEST(Any_Stringify, Device) {
- TestAnyStringify<DLDevice>(DLDevice{kDLCPU, 0}, TVMFFITypeIndex::kTVMFFIDevice, "cpu:0");
- TestAnyStringify<DLDevice>(DLDevice{kDLCUDA, 1}, TVMFFITypeIndex::kTVMFFIDevice, "cuda:1");
-}
-
-TEST(Any_Stringify, DataType) {
- TestAnyStringify<DLDataType>(DLDataType{kDLInt, 32, 1}, TVMFFITypeIndex::kTVMFFIDataType,
- "int32");
- TestAnyStringify<DLDataType>(DLDataType{kDLUInt, 1, 1}, TVMFFITypeIndex::kTVMFFIDataType, "bool");
- TestAnyStringify<DLDataType>(DLDataType{kDLOpaqueHandle, 0, 0}, TVMFFITypeIndex::kTVMFFIDataType,
- "void");
- TestAnyStringify<DLDataType>(DLDataType{kDLFloat, 8, 4}, TVMFFITypeIndex::kTVMFFIDataType,
- "float8x4");
-}
-
-TEST(Any_Stringify, RawStr) {
- TestAnyStringify<const char *>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
- TestAnyStringify<char[6]>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
- TestAnyStringify<const char[6]>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
-}
-
-TEST(Any_Stringify, Object) {
- auto check = [](const Any &v) -> void {
- std::string expected_prefix = "object.Object@0";
- int n = static_cast<int>(expected_prefix.size());
- std::string str = v.str()->c_str();
- EXPECT_GT(str.size(), n);
- EXPECT_EQ(str.substr(0, n), expected_prefix);
- };
- TestAnyStringifyChecker<Ref<Object>>(Ref<Object>::New(), TVMFFITypeIndex::kTVMFFIObject, check);
-}
-
-TEST(Any_Stringify, Func) {
- auto check = [](const Any &v) -> void {
- std::string expected_prefix = "object.Func@0";
- int n = static_cast<int>(expected_prefix.size());
- std::string str = v.str()->c_str();
- EXPECT_GT(str.size(), n);
- EXPECT_EQ(str.substr(0, n), expected_prefix);
- };
- TestAnyStringifyChecker<Ref<Func>>(Ref<Func>::New(FuncCall), TVMFFITypeIndex::kTVMFFIFunc, check);
-}
-
-TEST(Any_Stringify, Str) {
- auto check = [](const Any &v) -> void {
- std::string str = v.str()->c_str();
- EXPECT_EQ(str, "\"Hello World\"");
- };
- TestAnyStringifyChecker<Ref<Str>>(Ref<Str>::New("Hello World"), TVMFFITypeIndex::kTVMFFIStr,
- check);
-}
+namespace {
-} // namespace
+using namespace tvm::ffi;
+using namespace tvm::ffi::testing;
+
+TEST(Any, Int) {
+ AnyView view0;
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
+
+ std::optional<int64_t> opt_v0 = view0.TryAs<int64_t>();
+ EXPECT_TRUE(!opt_v0.has_value());
+
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] int64_t v0 = view0;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("Cannot convert from type `None` to `int`"), std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+
+ AnyView view1 = 1;
+ EXPECT_EQ(view1.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIInt);
+ EXPECT_EQ(view1.CopyToTVMFFIAny().v_int64, 1);
+
+ int32_t int_v1 = view1;
+ EXPECT_EQ(int_v1, 1);
+
+ int64_t v1 = 2;
+ view0 = v1;
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIInt);
+ EXPECT_EQ(view0.CopyToTVMFFIAny().v_int64, 2);
+}
+
+TEST(Any, Float) {
+ AnyView view0;
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
+
+ std::optional<double> opt_v0 = view0.TryAs<double>();
+ EXPECT_TRUE(!opt_v0.has_value());
+
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] double v0 = view0;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("Cannot convert from type `None` to `float`"), std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+
+ AnyView view1_int = 1;
+ float float_v1 = view1_int;
+ EXPECT_EQ(float_v1, 1);
+
+ AnyView view2 = 2.2;
+ EXPECT_EQ(view2.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIFloat);
+ EXPECT_EQ(view2.CopyToTVMFFIAny().v_float64, 2.2);
+
+ float v1 = 2;
+ view0 = v1;
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIFloat);
+ EXPECT_EQ(view0.CopyToTVMFFIAny().v_float64, 2);
+}
+
+TEST(Any, Object) {
+ AnyView view0;
+ EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
+
+ // int object is not nullable
+ std::optional<TInt> opt_v0 = view0.TryAs<TInt>();
+ EXPECT_TRUE(!opt_v0.has_value());
+
+ TInt v1(11);
+ EXPECT_EQ(v1.use_count(), 1);
+ // view won't increase refcount
+ AnyView view1 = v1;
+ EXPECT_EQ(v1.use_count(), 1);
+ // any will trigger ref count increase
+ Any any1 = v1;
+ EXPECT_EQ(v1.use_count(), 2);
+ // copy to another view
+ AnyView view2 = any1;
+ EXPECT_EQ(v1.use_count(), 2);
+
+ // convert to weak raw object ptr
+ const TIntObj* v1_ptr = view2;
+ EXPECT_EQ(v1.use_count(), 2);
+ EXPECT_EQ(v1_ptr->value, 11);
+ Any any2 = v1_ptr;
+ EXPECT_EQ(v1.use_count(), 3);
+ EXPECT_TRUE(any2.TryAs<TInt>().has_value());
+
+ // convert to raw opaque ptr
+ void* raw_v1_ptr = const_cast<TIntObj*>(v1_ptr);
+ any2 = raw_v1_ptr;
+ EXPECT_TRUE(any2.TryAs<void*>().value() == v1_ptr);
+
+ // convert to ObjectPtr
+ ObjectPtr<TNumberObj> v1_obj_ptr = view2;
+ EXPECT_EQ(v1.use_count(), 3);
+ any2 = v1_obj_ptr;
+ EXPECT_EQ(v1.use_count(), 4);
+ EXPECT_TRUE(any2.TryAs<TInt>().has_value());
+ any2.reset();
+ v1_obj_ptr.reset();
+
+ // convert that triggers error
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] TFloat v0 = view1;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ std::cout << what;
+ EXPECT_NE(what.find("Cannot convert from type `test.Int` to `test.Float`"),
+ std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+ // Try to convert to number
+ TNumber number0 = any1;
+ EXPECT_EQ(v1.use_count(), 3);
+ EXPECT_TRUE(number0.as<TIntObj>());
+ EXPECT_EQ(number0.as<TIntObj>()->value, 11);
+ EXPECT_TRUE(!any1.TryAs<int>().has_value());
+
+ TInt int1 = view2;
+ EXPECT_EQ(v1.use_count(), 4);
+ any1.reset();
+ EXPECT_EQ(v1.use_count(), 3);
+}
+
+} // namespace
diff --git a/ffi/tests/example/test_array.cc b/ffi/tests/cpp/test_array.cc
similarity index 100%
rename from ffi/tests/example/test_array.cc
rename to ffi/tests/cpp/test_array.cc
diff --git a/ffi/tests/example/test_c_ffi_abi.cc b/ffi/tests/cpp/test_c_ffi_abi.cc
similarity index 100%
rename from ffi/tests/example/test_c_ffi_abi.cc
rename to ffi/tests/cpp/test_c_ffi_abi.cc
diff --git a/ffi/tests/example/test_error.cc b/ffi/tests/cpp/test_error.cc
similarity index 100%
rename from ffi/tests/example/test_error.cc
rename to ffi/tests/cpp/test_error.cc
diff --git a/ffi/tests/example/test_function.cc b/ffi/tests/cpp/test_function.cc
similarity index 100%
rename from ffi/tests/example/test_function.cc
rename to ffi/tests/cpp/test_function.cc
diff --git a/ffi/tests/example/test_map.cc b/ffi/tests/cpp/test_map.cc
similarity index 100%
rename from ffi/tests/example/test_map.cc
rename to ffi/tests/cpp/test_map.cc
diff --git a/ffi/tests/example/test_object.cc b/ffi/tests/cpp/test_object.cc
similarity index 96%
rename from ffi/tests/example/test_object.cc
rename to ffi/tests/cpp/test_object.cc
index f931c99419..ed3361a0ba 100644
--- a/ffi/tests/example/test_object.cc
+++ b/ffi/tests/cpp/test_object.cc
@@ -47,7 +47,7 @@ TEST(Object, RefCounter) {
}
TEST(Object, TypeInfo) {
- const TypeInfo* info = tvm::ffi::details::ObjectGetTypeInfo(TIntObj::RuntimeTypeIndex());
+ const TypeInfo* info = TVMFFIGetTypeInfo(TIntObj::RuntimeTypeIndex());
EXPECT_TRUE(info != nullptr);
EXPECT_EQ(info->type_index, TIntObj::RuntimeTypeIndex());
EXPECT_EQ(info->type_depth, 2);
diff --git a/ffi/tests/example/test_optional.cc b/ffi/tests/cpp/test_optional.cc
similarity index 100%
rename from ffi/tests/example/test_optional.cc
rename to ffi/tests/cpp/test_optional.cc
diff --git a/ffi/tests/example/test_string.cc b/ffi/tests/cpp/test_string.cc
similarity index 100%
rename from ffi/tests/example/test_string.cc
rename to ffi/tests/cpp/test_string.cc
diff --git a/ffi/tests/example/testing_object.h b/ffi/tests/cpp/testing_object.h
similarity index 100%
rename from ffi/tests/example/testing_object.h
rename to ffi/tests/cpp/testing_object.h
diff --git a/ffi/tests/example/test_any.cc b/ffi/tests/example/test_any.cc
deleted file mode 100644
index 36d4783dc5..0000000000
--- a/ffi/tests/example/test_any.cc
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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.
- */
-#include <gtest/gtest.h>
-#include <tvm/ffi/any.h>
-#include <tvm/ffi/memory.h>
-
-#include "./testing_object.h"
-
-namespace {
-
-using namespace tvm::ffi;
-using namespace tvm::ffi::testing;
-
-TEST(Any, Int) {
- AnyView view0;
- EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
-
- std::optional<int64_t> opt_v0 = view0.TryAs<int64_t>();
- EXPECT_TRUE(!opt_v0.has_value());
-
- EXPECT_THROW(
- {
- try {
- [[maybe_unused]] int64_t v0 = view0;
- } catch (const Error& error) {
- EXPECT_EQ(error->kind, "TypeError");
- std::string what = error.what();
- EXPECT_NE(what.find("Cannot convert from type `None` to `int`"), std::string::npos);
- throw;
- }
- },
- ::tvm::ffi::Error);
-
- AnyView view1 = 1;
- EXPECT_EQ(view1.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIInt);
- EXPECT_EQ(view1.CopyToTVMFFIAny().v_int64, 1);
-
- int32_t int_v1 = view1;
- EXPECT_EQ(int_v1, 1);
-
- int64_t v1 = 2;
- view0 = v1;
- EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIInt);
- EXPECT_EQ(view0.CopyToTVMFFIAny().v_int64, 2);
-}
-
-TEST(Any, Float) {
- AnyView view0;
- EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
-
- std::optional<double> opt_v0 = view0.TryAs<double>();
- EXPECT_TRUE(!opt_v0.has_value());
-
- EXPECT_THROW(
- {
- try {
- [[maybe_unused]] double v0 = view0;
- } catch (const Error& error) {
- EXPECT_EQ(error->kind, "TypeError");
- std::string what = error.what();
- EXPECT_NE(what.find("Cannot convert from type `None` to `float`"), std::string::npos);
- throw;
- }
- },
- ::tvm::ffi::Error);
-
- AnyView view1_int = 1;
- float float_v1 = view1_int;
- EXPECT_EQ(float_v1, 1);
-
- AnyView view2 = 2.2;
- EXPECT_EQ(view2.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIFloat);
- EXPECT_EQ(view2.CopyToTVMFFIAny().v_float64, 2.2);
-
- float v1 = 2;
- view0 = v1;
- EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIFloat);
- EXPECT_EQ(view0.CopyToTVMFFIAny().v_float64, 2);
-}
-
-TEST(Any, Object) {
- AnyView view0;
- EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
-
- // int object is not nullable
- std::optional<TInt> opt_v0 = view0.TryAs<TInt>();
- EXPECT_TRUE(!opt_v0.has_value());
-
- TInt v1(11);
- EXPECT_EQ(v1.use_count(), 1);
- // view won't increase refcount
- AnyView view1 = v1;
- EXPECT_EQ(v1.use_count(), 1);
- // any will trigger ref count increase
- Any any1 = v1;
- EXPECT_EQ(v1.use_count(), 2);
- // copy to another view
- AnyView view2 = any1;
- EXPECT_EQ(v1.use_count(), 2);
-
- // convert to weak raw object ptr
- const TIntObj* v1_ptr = view2;
- EXPECT_EQ(v1.use_count(), 2);
- EXPECT_EQ(v1_ptr->value, 11);
- Any any2 = v1_ptr;
- EXPECT_EQ(v1.use_count(), 3);
- EXPECT_TRUE(any2.TryAs<TInt>().has_value());
-
- // convert to raw opaque ptr
- void* raw_v1_ptr = const_cast<TIntObj*>(v1_ptr);
- any2 = raw_v1_ptr;
- EXPECT_TRUE(any2.TryAs<void*>().value() == v1_ptr);
-
- // convert to ObjectPtr
- ObjectPtr<TNumberObj> v1_obj_ptr = view2;
- EXPECT_EQ(v1.use_count(), 3);
- any2 = v1_obj_ptr;
- EXPECT_EQ(v1.use_count(), 4);
- EXPECT_TRUE(any2.TryAs<TInt>().has_value());
- any2.reset();
- v1_obj_ptr.reset();
-
- // convert that triggers error
- EXPECT_THROW(
- {
- try {
- [[maybe_unused]] TFloat v0 = view1;
- } catch (const Error& error) {
- EXPECT_EQ(error->kind, "TypeError");
- std::string what = error.what();
- EXPECT_NE(what.find("Cannot convert from type `test.Int` to `test.Float`"),
- std::string::npos);
- throw;
- }
- },
- ::tvm::ffi::Error);
- // Try to convert to number
- TNumber number0 = any1;
- EXPECT_EQ(v1.use_count(), 3);
- EXPECT_TRUE(number0.as<TIntObj>());
- EXPECT_EQ(number0.as<TIntObj>()->value, 11);
- EXPECT_TRUE(!any1.TryAs<int>().has_value());
-
- TInt int1 = view2;
- EXPECT_EQ(v1.use_count(), 4);
- any1.reset();
- EXPECT_EQ(v1.use_count(), 3);
-}
-
-} // namespace
(tvm) 12/38: [FFI] Introduce String support
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 344ab1843782af992acb86ab08bafca07b445b52
Author: tqchen <ti...@gmail.com>
AuthorDate: Mon Sep 9 11:38:46 2024 -0400
[FFI] Introduce String support
---
ffi/include/tvm/ffi/any.h | 21 ++
ffi/include/tvm/ffi/base_details.h | 72 ++++++
ffi/include/tvm/ffi/endian.h | 89 ++++++++
ffi/include/tvm/ffi/object.h | 24 +-
ffi/include/tvm/ffi/string.h | 436 +++++++++++++++++++++++++++++++++++++
ffi/tests/example/test_string.cc | 265 ++++++++++++++++++++++
6 files changed, 904 insertions(+), 3 deletions(-)
diff --git a/ffi/include/tvm/ffi/any.h b/ffi/include/tvm/ffi/any.h
index 3fad36eacc..031038d4fc 100644
--- a/ffi/include/tvm/ffi/any.h
+++ b/ffi/include/tvm/ffi/any.h
@@ -24,6 +24,7 @@
#define TVM_FFI_ANY_H_
#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/string.h>
#include <tvm/ffi/type_traits.h>
#include <string>
@@ -271,6 +272,26 @@ struct AnyUnsafe : public ObjectUnsafe {
}
};
} // namespace details
+
+// Downcast an object
+// NOTE: the implementation is put in here to avoid cyclic dependency
+// with the
+template <typename SubRef, typename BaseRef,
+ typename = std::enable_if_t<std::is_base_of_v<ObjectRef, BaseRef>>>
+TVM_FFI_INLINE SubRef Downcast(BaseRef ref) {
+ if (ref.defined()) {
+ if (!ref->template IsInstance<typename SubRef::ContainerType>()) {
+ TVM_FFI_THROW(TypeError) << "Downcast from " << ref->GetTypeKey() << " to "
+ << SubRef::ContainerType::_type_key << " failed.";
+ }
+ } else {
+ if (!SubRef::_type_is_nullable) {
+ TVM_FFI_THROW(TypeError) << "Downcast from nullptr to not nullable reference of "
+ << SubRef::ContainerType::_type_key;
+ }
+ }
+ return details::ObjectUnsafe::DowncastRefNoCheck<SubRef>(std::move(ref));
+}
} // namespace ffi
} // namespace tvm
#endif // TVM_FFI_ANY_H_
diff --git a/ffi/include/tvm/ffi/base_details.h b/ffi/include/tvm/ffi/base_details.h
index c44ad0608a..c817a6d817 100644
--- a/ffi/include/tvm/ffi/base_details.h
+++ b/ffi/include/tvm/ffi/base_details.h
@@ -26,6 +26,7 @@
#define TVM_FFI_BASE_DETAILS_H_
#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/endian.h>
#include <cstddef>
#include <utility>
@@ -133,6 +134,77 @@ void for_each(const F& f, Args&&... args) { // NOLINT(*)
for_each_dispatcher<sizeof...(Args) == 0, 0, F>::run(f, std::forward<Args>(args)...);
}
+/*!
+ * \brief Hash the binary bytes
+ * \param data The data pointer
+ * \param size The size of the bytes.
+ * \return the hash value.
+ */
+TVM_FFI_INLINE uint64_t StableHashBytes(const char* data, size_t size) {
+ const constexpr uint64_t kMultiplier = 1099511628211ULL;
+ const constexpr uint64_t kMod = 2147483647ULL;
+ union Union {
+ uint8_t a[8];
+ uint64_t b;
+ } u;
+ static_assert(sizeof(Union) == sizeof(uint64_t), "sizeof(Union) != sizeof(uint64_t)");
+ const char* it = data;
+ const char* end = it + size;
+ uint64_t result = 0;
+ for (; it + 8 <= end; it += 8) {
+ if (TVM_FFI_IO_NO_ENDIAN_SWAP) {
+ u.a[0] = it[0];
+ u.a[1] = it[1];
+ u.a[2] = it[2];
+ u.a[3] = it[3];
+ u.a[4] = it[4];
+ u.a[5] = it[5];
+ u.a[6] = it[6];
+ u.a[7] = it[7];
+ } else {
+ u.a[0] = it[7];
+ u.a[1] = it[6];
+ u.a[2] = it[5];
+ u.a[3] = it[4];
+ u.a[4] = it[3];
+ u.a[5] = it[2];
+ u.a[6] = it[1];
+ u.a[7] = it[0];
+ }
+ result = (result * kMultiplier + u.b) % kMod;
+ }
+ if (it < end) {
+ u.b = 0;
+ uint8_t* a = u.a;
+ if (it + 4 <= end) {
+ a[0] = it[0];
+ a[1] = it[1];
+ a[2] = it[2];
+ a[3] = it[3];
+ it += 4;
+ a += 4;
+ }
+ if (it + 2 <= end) {
+ a[0] = it[0];
+ a[1] = it[1];
+ it += 2;
+ a += 2;
+ }
+ if (it + 1 <= end) {
+ a[0] = it[0];
+ it += 1;
+ a += 1;
+ }
+ if (!TVM_FFI_IO_NO_ENDIAN_SWAP) {
+ std::swap(u.a[0], u.a[7]);
+ std::swap(u.a[1], u.a[6]);
+ std::swap(u.a[2], u.a[5]);
+ std::swap(u.a[3], u.a[4]);
+ }
+ result = (result * kMultiplier + u.b) % kMod;
+ }
+ return result;
+}
} // namespace details
} // namespace ffi
} // namespace tvm
diff --git a/ffi/include/tvm/ffi/endian.h b/ffi/include/tvm/ffi/endian.h
new file mode 100644
index 0000000000..4a73b82e6c
--- /dev/null
+++ b/ffi/include/tvm/ffi/endian.h
@@ -0,0 +1,89 @@
+
+/*
+ * 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.
+ */
+
+/*
+ * \file tvm/ffi/endian.h
+ * \brief Endian detection and handling
+ */
+#ifndef TVM_FFI_ENDIAN_H_
+#define TVM_FFI_ENDIAN_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#ifndef TVM_FFI_IO_USE_LITTLE_ENDIAN
+#define TVM_FFI_IO_USE_LITTLE_ENDIAN 1
+#endif
+
+#ifdef TVM_FFI_CMAKE_LITTLE_ENDIAN
+// If compiled with CMake, use CMake's endian detection logic
+#define TVM_FFI_LITTLE_ENDIAN TVM_FFI_CMAKE_LITTLE_ENDIAN
+#else
+#if defined(__APPLE__) || defined(_WIN32)
+#define TVM_FFI_LITTLE_ENDIAN 1
+#elif defined(__GLIBC__) || defined(__GNU_LIBRARY__) || defined(__ANDROID__) || defined(__RISCV__)
+#include <endian.h>
+#define TVM_FFI_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN)
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <sys/endian.h>
+#define TVM_FFI_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN)
+#elif defined(__QNX__)
+#include <sys/param.h>
+#define TVM_FFI_LITTLE_ENDIAN (BYTE_ORDER == LITTLE_ENDIAN)
+#elif defined(__EMSCRIPTEN__) || defined(__hexagon__)
+#define TVM_FFI_LITTLE_ENDIAN 1
+#elif defined(__sun) || defined(sun)
+#include <sys/isa_defs.h>
+#if defined(_LITTLE_ENDIAN)
+#define TVM_FFI_LITTLE_ENDIAN 1
+#else
+#define TVM_FFI_LITTLE_ENDIAN 0
+#endif
+#else
+#error "Unable to determine endianness of your machine; use CMake to compile"
+#endif
+#endif
+
+/*! \brief whether serialize using little endian */
+#define TVM_FFI_IO_NO_ENDIAN_SWAP (TVM_FFI_LITTLE_ENDIAN == TVM_FFI_IO_USE_LITTLE_ENDIAN)
+
+namespace tvm {
+namespace ffi {
+/*!
+ * \brief A generic inplace byte swapping function.
+ * \param data The data pointer.
+ * \param elem_bytes The number of bytes of the data elements
+ * \param num_elems Number of elements in the data.
+ * \note Always try pass in constant elem_bytes to enable
+ * compiler optimization
+ */
+inline void ByteSwap(void* data, size_t elem_bytes, size_t num_elems) {
+ for (size_t i = 0; i < num_elems; ++i) {
+ uint8_t* bptr = reinterpret_cast<uint8_t*>(data) + elem_bytes * i;
+ for (size_t j = 0; j < elem_bytes / 2; ++j) {
+ uint8_t v = bptr[elem_bytes - 1 - j];
+ bptr[elem_bytes - 1 - j] = bptr[j];
+ bptr[j] = v;
+ }
+ }
+}
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_ENDIAN_H_
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 9e218899d7..120a735c82 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -26,6 +26,7 @@
#include <tvm/ffi/base_details.h>
#include <tvm/ffi/c_api.h>
+#include <string>
#include <type_traits>
#include <utility>
@@ -147,8 +148,22 @@ class Object {
return details::IsObjectInstance<TargetType>(header_.type_index);
}
+ /*!
+ * \return the type key of the object.
+ * \note this operation is expensive, can be used for error reporting.
+ */
+ std::string GetTypeKey() const {
+#if TVM_FFI_ALLOW_DYN_TYPE
+ // the function checks that the info exists
+ const TypeInfo* type_info = details::ObjectGetTypeInfo(header_.type_index);
+ return type_info->type_key;
+#else
+ return "<unknown>";
+#endif
+ }
+
// Information about the object
- static constexpr const char* _type_key = "runtime.Object";
+ static constexpr const char* _type_key = "object.Object";
// Default object type properties for sub-classes
static constexpr bool _type_final = false;
@@ -418,8 +433,6 @@ class ObjectRef {
// friend classes.
friend struct ObjectPtrHash;
friend class tvm::ffi::details::ObjectUnsafe;
- template <typename SubRef, typename BaseRef>
- friend SubRef Downcast(BaseRef ref);
};
/*!
@@ -621,6 +634,11 @@ struct ObjectUnsafe {
return GetHeader(obj_ptr);
}
+ template <typename SubRef, typename BaseRef>
+ static TVM_FFI_INLINE SubRef DowncastRefNoCheck(BaseRef&& base) {
+ return SubRef(std::move(base.data_));
+ }
+
// Create objectptr by moving from an existing address of object and setting its
// address to nullptr
template <typename T>
diff --git a/ffi/include/tvm/ffi/string.h b/ffi/include/tvm/ffi/string.h
new file mode 100644
index 0000000000..6b43829ff8
--- /dev/null
+++ b/ffi/include/tvm/ffi/string.h
@@ -0,0 +1,436 @@
+/*
+ * 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.
+ */
+
+/*!
+ * \file tvm/ffi/string.h
+ * \brief Runtime String type.
+ */
+#ifndef TVM_FFI_STRING_H_
+#define TVM_FFI_STRING_H_
+
+#include <tvm/ffi/base_details.h>
+#include <tvm/ffi/error.h>
+#include <tvm/ffi/memory.h>
+#include <tvm/ffi/object.h>
+
+#include <cstddef>
+#include <cstring>
+#include <initializer_list>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+
+// NOTE: We place string in tvm/ffi instead of tvm/ffi/container
+// because string itself needs special handling and is an inherent
+// core component for return string handling.
+// The following dependency relation holds
+// containers -> any -> string -> object
+
+namespace tvm {
+namespace ffi {
+
+/*! \brief An object representing string. It's POD type. */
+class StringObj : public Object {
+ public:
+ /*! \brief The pointer to string data. */
+ const char* data;
+
+ /*! \brief The length of the string object. */
+ uint64_t size;
+
+ static constexpr const uint32_t _type_index = TypeIndex::kTVMFFIStr;
+ static constexpr const char* _type_key = "object.String";
+ static const constexpr bool _type_final = true;
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(StringObj, Object);
+};
+
+namespace details {
+
+// String moved from std::string
+// without having to trigger a copy
+class StringObjStdImpl : public StringObj {
+ public:
+ explicit StringObjStdImpl(std::string other) : data_{other} {
+ this->data = data_.data();
+ this->size = data_.size();
+ }
+
+ private:
+ std::string data_;
+};
+
+// inplace string allocation
+TVM_FFI_INLINE ObjectPtr<StringObj> MakeInplaceString(const char* data, size_t length) {
+ ObjectPtr<StringObj> p = make_inplace_array_object<StringObj, char>(length + 1);
+ static_assert(alignof(StringObj) % alignof(char) == 0);
+ static_assert(sizeof(StringObj) % alignof(char) == 0);
+ char* dest_data = reinterpret_cast<char*>(p.get()) + sizeof(StringObj);
+ p->data = dest_data;
+ p->size = length;
+ std::memcpy(dest_data, data, length);
+ dest_data[length] = '\0';
+ return p;
+}
+} // namespace details
+
+/*!
+ * \brief Reference to string objects.
+ *
+ * \code
+ *
+ * // Example to create runtime String reference object from std::string
+ * std::string s = "hello world";
+ *
+ * // You can create the reference from existing std::string
+ * String ref{std::move(s)};
+ *
+ * // You can rebind the reference to another string.
+ * ref = std::string{"hello world2"};
+ *
+ * // You can use the reference as hash map key
+ * std::unordered_map<String, int32_t> m;
+ * m[ref] = 1;
+ *
+ * // You can compare the reference object with other string objects
+ * assert(ref == "hello world", true);
+ *
+ * // You can convert the reference to std::string again
+ * string s2 = (string)ref;
+ *
+ * \endcode
+ */
+class String : public ObjectRef {
+ public:
+ /*!
+ * \brief constructor from char [N]
+ *
+ * \param other a char array.
+ */
+ template <size_t N>
+ String(const char other[N]) // NOLINT(*)
+ : ObjectRef(details::MakeInplaceString(other, N)) {}
+
+ /*!
+ * \brief constructor
+ */
+ String() : String("") {}
+
+ /*!
+ * \brief constructor from raw string
+ *
+ * \param other a char array.
+ */
+ String(const char* other) // NOLINT(*)
+ : ObjectRef(details::MakeInplaceString(other, std::strlen(other))) {}
+
+ /*!
+ * \brief Construct a new string object
+ * \param other The std::string object to be copied
+ */
+ String(const std::string& other) // NOLINT(*)
+ : ObjectRef(details::MakeInplaceString(other.data(), other.size())) {}
+
+ /*!
+ * \brief Construct a new string object
+ * \param other The std::string object to be moved
+ */
+ String(std::string&& other) // NOLINT(*)
+ : ObjectRef(make_object<details::StringObjStdImpl>(std::move(other))) {}
+
+ /*!
+ * \brief Swap this String with another string
+ * \param other The other string
+ */
+ void swap(String& other) { // NOLINT(*)
+ std::swap(data_, other.data_);
+ }
+
+ template <typename T>
+ String& operator=(T&& other) {
+ // copy-and-swap idiom
+ String(std::forward<T>(other)).swap(*this);
+ return *this;
+ }
+
+ /*!
+ * \brief Compares this String object to other
+ *
+ * \param other The String to compare with.
+ *
+ * \return zero if both char sequences compare equal. negative if this appear
+ * before other, positive otherwise.
+ */
+ int compare(const String& other) const {
+ return memncmp(data(), other.data(), size(), other.size());
+ }
+
+ /*!
+ * \brief Compares this String object to other
+ *
+ * \param other The string to compare with.
+ *
+ * \return zero if both char sequences compare equal. negative if this appear
+ * before other, positive otherwise.
+ */
+ int compare(const std::string& other) const {
+ return memncmp(data(), other.data(), size(), other.size());
+ }
+
+ /*!
+ * \brief Compares this to other
+ *
+ * \param other The character array to compare with.
+ *
+ * \return zero if both char sequences compare equal. negative if this appear
+ * before other, positive otherwise.
+ */
+ int compare(const char* other) const {
+ return memncmp(data(), other, size(), std::strlen(other));
+ }
+
+ /*!
+ * \brief Returns a pointer to the char array in the string.
+ *
+ * \return const char*
+ */
+ const char* c_str() const { return get()->data; }
+
+ /*!
+ * \brief Return the length of the string
+ *
+ * \return size_t string length
+ */
+ size_t size() const {
+ const auto* ptr = get();
+ return ptr->size;
+ }
+
+ /*!
+ * \brief Return the length of the string
+ *
+ * \return size_t string length
+ */
+ size_t length() const { return size(); }
+
+ /*!
+ * \brief Retun if the string is empty
+ *
+ * \return true if empty, false otherwise.
+ */
+ bool empty() const { return size() == 0; }
+
+ /*!
+ * \brief Read an element.
+ * \param pos The position at which to read the character.
+ *
+ * \return The char at position
+ */
+ char at(size_t pos) const {
+ if (pos < size()) {
+ return data()[pos];
+ } else {
+ throw std::out_of_range("tvm::String index out of bounds");
+ }
+ }
+
+ /*!
+ * \brief Return the data pointer
+ *
+ * \return const char* data pointer
+ */
+ const char* data() const { return get()->data; }
+
+ /*!
+ * \brief Convert String to an std::string object
+ *
+ * \return std::string
+ */
+ operator std::string() const { return std::string{get()->data, size()}; }
+
+ TVM_FFI_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(String, ObjectRef, StringObj);
+
+ private:
+ /*!
+ * \brief Compare two char sequence
+ *
+ * \param lhs Pointers to the char array to compare
+ * \param rhs Pointers to the char array to compare
+ * \param lhs_count Length of the char array to compare
+ * \param rhs_count Length of the char array to compare
+ * \return int zero if both char sequences compare equal. negative if this
+ * appear before other, positive otherwise.
+ */
+ static int memncmp(const char* lhs, const char* rhs, size_t lhs_count, size_t rhs_count);
+
+ /*!
+ * \brief Concatenate two char sequences
+ *
+ * \param lhs Pointers to the lhs char array
+ * \param lhs_size The size of the lhs char array
+ * \param rhs Pointers to the rhs char array
+ * \param rhs_size The size of the rhs char array
+ *
+ * \return The concatenated char sequence
+ */
+ static String Concat(const char* lhs, size_t lhs_size, const char* rhs, size_t rhs_size) {
+ std::string ret(lhs, lhs_size);
+ ret.append(rhs, rhs_size);
+ return String(ret);
+ }
+
+ // Overload + operator
+ friend String operator+(const String& lhs, const String& rhs);
+ friend String operator+(const String& lhs, const std::string& rhs);
+ friend String operator+(const std::string& lhs, const String& rhs);
+ friend String operator+(const String& lhs, const char* rhs);
+ friend String operator+(const char* lhs, const String& rhs);
+
+ friend struct AnyEqual;
+};
+
+inline String operator+(const String& lhs, const String& rhs) {
+ size_t lhs_size = lhs.size();
+ size_t rhs_size = rhs.size();
+ return String::Concat(lhs.data(), lhs_size, rhs.data(), rhs_size);
+}
+
+inline String operator+(const String& lhs, const std::string& rhs) {
+ size_t lhs_size = lhs.size();
+ size_t rhs_size = rhs.size();
+ return String::Concat(lhs.data(), lhs_size, rhs.data(), rhs_size);
+}
+
+inline String operator+(const std::string& lhs, const String& rhs) {
+ size_t lhs_size = lhs.size();
+ size_t rhs_size = rhs.size();
+ return String::Concat(lhs.data(), lhs_size, rhs.data(), rhs_size);
+}
+
+inline String operator+(const char* lhs, const String& rhs) {
+ size_t lhs_size = std::strlen(lhs);
+ size_t rhs_size = rhs.size();
+ return String::Concat(lhs, lhs_size, rhs.data(), rhs_size);
+}
+
+inline String operator+(const String& lhs, const char* rhs) {
+ size_t lhs_size = lhs.size();
+ size_t rhs_size = std::strlen(rhs);
+ return String::Concat(lhs.data(), lhs_size, rhs, rhs_size);
+}
+
+// Overload < operator
+inline bool operator<(const String& lhs, const std::string& rhs) { return lhs.compare(rhs) < 0; }
+
+inline bool operator<(const std::string& lhs, const String& rhs) { return rhs.compare(lhs) > 0; }
+
+inline bool operator<(const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; }
+
+inline bool operator<(const String& lhs, const char* rhs) { return lhs.compare(rhs) < 0; }
+
+inline bool operator<(const char* lhs, const String& rhs) { return rhs.compare(lhs) > 0; }
+
+// Overload > operator
+inline bool operator>(const String& lhs, const std::string& rhs) { return lhs.compare(rhs) > 0; }
+
+inline bool operator>(const std::string& lhs, const String& rhs) { return rhs.compare(lhs) < 0; }
+
+inline bool operator>(const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; }
+
+inline bool operator>(const String& lhs, const char* rhs) { return lhs.compare(rhs) > 0; }
+
+inline bool operator>(const char* lhs, const String& rhs) { return rhs.compare(lhs) < 0; }
+
+// Overload <= operator
+inline bool operator<=(const String& lhs, const std::string& rhs) { return lhs.compare(rhs) <= 0; }
+
+inline bool operator<=(const std::string& lhs, const String& rhs) { return rhs.compare(lhs) >= 0; }
+
+inline bool operator<=(const String& lhs, const String& rhs) { return lhs.compare(rhs) <= 0; }
+
+inline bool operator<=(const String& lhs, const char* rhs) { return lhs.compare(rhs) <= 0; }
+
+inline bool operator<=(const char* lhs, const String& rhs) { return rhs.compare(lhs) >= 0; }
+
+// Overload >= operator
+inline bool operator>=(const String& lhs, const std::string& rhs) { return lhs.compare(rhs) >= 0; }
+
+inline bool operator>=(const std::string& lhs, const String& rhs) { return rhs.compare(lhs) <= 0; }
+
+inline bool operator>=(const String& lhs, const String& rhs) { return lhs.compare(rhs) >= 0; }
+
+inline bool operator>=(const String& lhs, const char* rhs) { return lhs.compare(rhs) >= 0; }
+
+inline bool operator>=(const char* lhs, const String& rhs) { return rhs.compare(lhs) <= 0; }
+
+// Overload == operator
+inline bool operator==(const String& lhs, const std::string& rhs) { return lhs.compare(rhs) == 0; }
+
+inline bool operator==(const std::string& lhs, const String& rhs) { return rhs.compare(lhs) == 0; }
+
+inline bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; }
+
+inline bool operator==(const String& lhs, const char* rhs) { return lhs.compare(rhs) == 0; }
+
+inline bool operator==(const char* lhs, const String& rhs) { return rhs.compare(lhs) == 0; }
+
+// Overload != operator
+inline bool operator!=(const String& lhs, const std::string& rhs) { return lhs.compare(rhs) != 0; }
+
+inline bool operator!=(const std::string& lhs, const String& rhs) { return rhs.compare(lhs) != 0; }
+
+inline bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; }
+
+inline bool operator!=(const String& lhs, const char* rhs) { return lhs.compare(rhs) != 0; }
+
+inline bool operator!=(const char* lhs, const String& rhs) { return rhs.compare(lhs) != 0; }
+
+inline std::ostream& operator<<(std::ostream& out, const String& input) {
+ out.write(input.data(), input.size());
+ return out;
+}
+
+inline int String::memncmp(const char* lhs, const char* rhs, size_t lhs_count, size_t rhs_count) {
+ if (lhs == rhs && lhs_count == rhs_count) return 0;
+
+ for (size_t i = 0; i < lhs_count && i < rhs_count; ++i) {
+ if (lhs[i] < rhs[i]) return -1;
+ if (lhs[i] > rhs[i]) return 1;
+ }
+ if (lhs_count < rhs_count) {
+ return -1;
+ } else if (lhs_count > rhs_count) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+} // namespace ffi
+} // namespace tvm
+
+namespace std {
+
+template <>
+struct hash<::tvm::ffi::String> {
+ std::size_t operator()(const ::tvm::ffi::String& str) const {
+ return ::tvm::ffi::details::StableHashBytes(str.data(), str.size());
+ }
+};
+} // namespace std
+#endif // TVM_FFI_STRING_H_
diff --git a/ffi/tests/example/test_string.cc b/ffi/tests/example/test_string.cc
new file mode 100644
index 0000000000..23310824a6
--- /dev/null
+++ b/ffi/tests/example/test_string.cc
@@ -0,0 +1,265 @@
+/*
+ * 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.
+ */
+#include <gtest/gtest.h>
+#include <tvm/ffi/any.h>
+#include <tvm/ffi/string.h>
+
+namespace {
+
+using namespace tvm::ffi;
+
+TEST(String, MoveFromStd) {
+ using namespace std;
+ string source = "this is a string";
+ string expect = source;
+ String s(std::move(source));
+ string copy = (string)s;
+ EXPECT_EQ(copy, expect);
+ EXPECT_EQ(source.size(), 0);
+}
+
+TEST(String, CopyFromStd) {
+ using namespace std;
+ string source = "this is a string";
+ string expect = source;
+ String s{source};
+ string copy = (string)s;
+ EXPECT_EQ(copy, expect);
+ EXPECT_EQ(source.size(), expect.size());
+}
+
+TEST(String, Assignment) {
+ using namespace std;
+ String s{string{"hello"}};
+ s = string{"world"};
+ EXPECT_EQ(s == "world", true);
+ string s2{"world2"};
+ s = std::move(s2);
+ EXPECT_EQ(s == "world2", true);
+}
+
+TEST(String, empty) {
+ using namespace std;
+ String s{"hello"};
+ EXPECT_EQ(s.empty(), false);
+ s = std::string("");
+ EXPECT_EQ(s.empty(), true);
+}
+
+TEST(String, Comparisons) {
+ using namespace std;
+ string source = "a string";
+ string mismatch = "a string but longer";
+ String s{"a string"};
+ String m{mismatch};
+
+ EXPECT_EQ("a str" >= s, false);
+ EXPECT_EQ(s == source, true);
+ EXPECT_EQ(s == mismatch, false);
+ EXPECT_EQ(s == source.data(), true);
+ EXPECT_EQ(s == mismatch.data(), false);
+
+ EXPECT_EQ(s < m, source < mismatch);
+ EXPECT_EQ(s > m, source > mismatch);
+ EXPECT_EQ(s <= m, source <= mismatch);
+ EXPECT_EQ(s >= m, source >= mismatch);
+ EXPECT_EQ(s == m, source == mismatch);
+ EXPECT_EQ(s != m, source != mismatch);
+
+ EXPECT_EQ(m < s, mismatch < source);
+ EXPECT_EQ(m > s, mismatch > source);
+ EXPECT_EQ(m <= s, mismatch <= source);
+ EXPECT_EQ(m >= s, mismatch >= source);
+ EXPECT_EQ(m == s, mismatch == source);
+ EXPECT_EQ(m != s, mismatch != source);
+}
+
+// Check '\0' handling
+TEST(String, null_byte_handling) {
+ using namespace std;
+ // Ensure string still compares equal if it contains '\0'.
+ string v1 = "hello world";
+ size_t v1_size = v1.size();
+ v1[5] = '\0';
+ EXPECT_EQ(v1[5], '\0');
+ EXPECT_EQ(v1.size(), v1_size);
+ String str_v1{v1};
+ EXPECT_EQ(str_v1.compare(v1), 0);
+ EXPECT_EQ(str_v1.size(), v1_size);
+
+ // Ensure bytes after '\0' are taken into account for mismatches.
+ string v2 = "aaa one";
+ string v3 = "aaa two";
+ v2[3] = '\0';
+ v3[3] = '\0';
+ String str_v2{v2};
+ String str_v3{v3};
+ EXPECT_EQ(str_v2.compare(str_v3), -1);
+ EXPECT_EQ(str_v2.size(), 7);
+ // strcmp won't be able to detect the mismatch
+ EXPECT_EQ(strcmp(v2.data(), v3.data()), 0);
+ // string::compare can handle \0 since it knows size
+ EXPECT_LT(v2.compare(v3), 0);
+
+ // If there is mismatch before '\0', should still handle it.
+ string v4 = "acc one";
+ string v5 = "abb two";
+ v4[3] = '\0';
+ v5[3] = '\0';
+ String str_v4{v4};
+ String str_v5{v5};
+ EXPECT_GT(str_v4.compare(str_v5), 0);
+ EXPECT_EQ(str_v4.size(), 7);
+ // strcmp is able to detect the mismatch
+ EXPECT_GT(strcmp(v4.data(), v5.data()), 0);
+ // string::compare can handle \0 since it knows size
+ EXPECT_GT(v4.compare(v5), 0);
+}
+
+TEST(String, compare_same_memory_region_different_size) {
+ using namespace std;
+ string source = "a string";
+ String str_source{source};
+ char* memory = const_cast<char*>(str_source.data());
+ EXPECT_EQ(str_source.compare(memory), 0);
+ // This changes the string size
+ memory[2] = '\0';
+ // memory is logically shorter now
+ EXPECT_GT(str_source.compare(memory), 0);
+}
+
+TEST(String, compare) {
+ using namespace std;
+ constexpr auto mismatch1_cstr = "a string but longer";
+ string source = "a string";
+ string mismatch1 = mismatch1_cstr;
+ string mismatch2 = "a strin";
+ string mismatch3 = "a b";
+ string mismatch4 = "a t";
+ String str_source{source};
+ String str_mismatch1{mismatch1_cstr};
+ String str_mismatch2{mismatch2};
+ String str_mismatch3{mismatch3};
+ String str_mismatch4{mismatch4};
+
+ // compare with string
+ EXPECT_EQ(str_source.compare(source), 0);
+ EXPECT_TRUE(str_source == source);
+ EXPECT_TRUE(source == str_source);
+ EXPECT_TRUE(str_source <= source);
+ EXPECT_TRUE(source <= str_source);
+ EXPECT_TRUE(str_source >= source);
+ EXPECT_TRUE(source >= str_source);
+ EXPECT_LT(str_source.compare(mismatch1), 0);
+ EXPECT_TRUE(str_source < mismatch1);
+ EXPECT_TRUE(mismatch1 != str_source);
+ EXPECT_GT(str_source.compare(mismatch2), 0);
+ EXPECT_TRUE(str_source > mismatch2);
+ EXPECT_TRUE(mismatch2 < str_source);
+ EXPECT_GT(str_source.compare(mismatch3), 0);
+ EXPECT_TRUE(str_source > mismatch3);
+ EXPECT_LT(str_source.compare(mismatch4), 0);
+ EXPECT_TRUE(str_source < mismatch4);
+ EXPECT_TRUE(mismatch4 > str_source);
+
+ // compare with char*
+ EXPECT_EQ(str_source.compare(source.data()), 0);
+ EXPECT_TRUE(str_source == source.data());
+ EXPECT_TRUE(source.data() == str_source);
+ EXPECT_TRUE(str_source <= source.data());
+ EXPECT_TRUE(source <= str_source.data());
+ EXPECT_TRUE(str_source >= source.data());
+ EXPECT_TRUE(source >= str_source.data());
+ EXPECT_LT(str_source.compare(mismatch1.data()), 0);
+ EXPECT_TRUE(str_source < mismatch1.data());
+ EXPECT_TRUE(str_source != mismatch1.data());
+ EXPECT_TRUE(mismatch1.data() != str_source);
+ EXPECT_GT(str_source.compare(mismatch2.data()), 0);
+ EXPECT_TRUE(str_source > mismatch2.data());
+ EXPECT_TRUE(mismatch2.data() < str_source);
+ EXPECT_GT(str_source.compare(mismatch3.data()), 0);
+ EXPECT_TRUE(str_source > mismatch3.data());
+ EXPECT_LT(str_source.compare(mismatch4.data()), 0);
+ EXPECT_TRUE(str_source < mismatch4.data());
+ EXPECT_TRUE(mismatch4.data() > str_source);
+
+ // compare with String
+ EXPECT_LT(str_source.compare(str_mismatch1), 0);
+ EXPECT_TRUE(str_source < str_mismatch1);
+ EXPECT_GT(str_source.compare(str_mismatch2), 0);
+ EXPECT_TRUE(str_source > str_mismatch2);
+ EXPECT_GT(str_source.compare(str_mismatch3), 0);
+ EXPECT_TRUE(str_source > str_mismatch3);
+ EXPECT_LT(str_source.compare(str_mismatch4), 0);
+ EXPECT_TRUE(str_source < str_mismatch4);
+}
+
+TEST(String, c_str) {
+ using namespace std;
+ string source = "this is a string";
+ string mismatch = "mismatch";
+ String s{source};
+
+ EXPECT_EQ(std::strcmp(s.c_str(), source.data()), 0);
+ EXPECT_NE(std::strcmp(s.c_str(), mismatch.data()), 0);
+}
+
+TEST(String, hash) {
+ using namespace std;
+ string source = "this is a string";
+ String s{source};
+ std::hash<String>()(s);
+
+ std::unordered_map<String, std::string> map;
+ String k1{string{"k1"}};
+ string v1{"v1"};
+ String k2{string{"k2"}};
+ string v2{"v2"};
+ map[k1] = v1;
+ map[k2] = v2;
+
+ EXPECT_EQ(map[k1], v1);
+ EXPECT_EQ(map[k2], v2);
+}
+
+TEST(String, Cast) {
+ using namespace std;
+ string source = "this is a string";
+ String s{source};
+ ObjectRef r = s;
+ String s2 = Downcast<String>(r);
+}
+
+TEST(String, Concat) {
+ String s1("hello");
+ String s2("world");
+ std::string s3("world");
+ String res1 = s1 + s2;
+ String res2 = s1 + s3;
+ String res3 = s3 + s1;
+ String res4 = s1 + "world";
+ String res5 = "world" + s1;
+
+ EXPECT_EQ(res1.compare("helloworld"), 0);
+ EXPECT_EQ(res2.compare("helloworld"), 0);
+ EXPECT_EQ(res3.compare("worldhello"), 0);
+ EXPECT_EQ(res4.compare("helloworld"), 0);
+ EXPECT_EQ(res5.compare("worldhello"), 0);
+}
+} // namespace
\ No newline at end of file
(tvm) 36/38: Fix deleter to match latest
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 69b4611a6479aac821363f5fe6776a50e524b571
Author: tqchen <ti...@gmail.com>
AuthorDate: Mon Mar 10 10:06:01 2025 -0400
Fix deleter to match latest
---
include/tvm/runtime/memory.h | 1 +
include/tvm/runtime/memory/memory_manager.h | 4 ++--
include/tvm/runtime/ndarray.h | 2 +-
include/tvm/runtime/object.h | 10 ++++++----
src/runtime/memory/memory_manager.cc | 12 ++++++------
src/runtime/ndarray.cc | 15 +++++++++------
src/runtime/rpc/rpc_module.cc | 4 ++--
7 files changed, 27 insertions(+), 21 deletions(-)
diff --git a/include/tvm/runtime/memory.h b/include/tvm/runtime/memory.h
index 04b4a1e7d4..2b317d05c5 100644
--- a/include/tvm/runtime/memory.h
+++ b/include/tvm/runtime/memory.h
@@ -30,6 +30,7 @@ namespace runtime {
using tvm::ffi::make_inplace_array_object;
using tvm::ffi::make_object;
+using tvm::ffi::FObjectDeleter;
} // namespace runtime
} // namespace tvm
diff --git a/include/tvm/runtime/memory/memory_manager.h b/include/tvm/runtime/memory/memory_manager.h
index a88d05603f..3def5224ed 100644
--- a/include/tvm/runtime/memory/memory_manager.h
+++ b/include/tvm/runtime/memory/memory_manager.h
@@ -171,10 +171,10 @@ class StorageObj : public Object {
String scope = "global");
/*! \brief The deleter for an NDArray when allocated from underlying storage. */
- static void ScopedDeleter(void* ptr);
+ static void ScopedDeleter(TVMFFIObject* ptr);
/*! \brief The deleter for an NDArray when allocated from underlying storage. */
- static void Deleter(void* ptr);
+ static void Deleter(TVMFFIObject* ptr);
~StorageObj() {
if (allocator) {
diff --git a/include/tvm/runtime/ndarray.h b/include/tvm/runtime/ndarray.h
index 4ed86fee82..3bfdc74e7c 100644
--- a/include/tvm/runtime/ndarray.h
+++ b/include/tvm/runtime/ndarray.h
@@ -332,7 +332,7 @@ class NDArray::Container : public Object, public NDArray::ContainerBase {
* \brief Set the deleter field.
* \param deleter The deleter.
*/
- void SetDeleter(void (*deleter)(void* self)) { header_.deleter = deleter; }
+ void SetDeleter(ffi::FObjectDeleter deleter) { header_.deleter = deleter; }
// Expose DecRef and IncRef as public function
// NOTE: they are only for developer purposes only.
diff --git a/include/tvm/runtime/object.h b/include/tvm/runtime/object.h
index 1edfae6abb..76ed5c0096 100644
--- a/include/tvm/runtime/object.h
+++ b/include/tvm/runtime/object.h
@@ -51,9 +51,7 @@ using tvm::ffi::GetRef;
enum TypeIndex : int32_t {
// Standard static index assignments,
// Frontends can take benefit of these constants.
- kRuntimeString = TVMFFITypeIndex::kTVMFFIStr,
- kRuntimeMap = TVMFFITypeIndex::kTVMFFIMap,
- kRuntimeArray = TVMFFITypeIndex::kTVMFFIArray,
+
/*! \brief runtime::Module. */
kRuntimeModule = TVMFFITypeIndex::kTVMFFIRuntimeModule,
/*! \brief runtime::NDArray. */
@@ -68,6 +66,10 @@ enum TypeIndex : int32_t {
kRuntimeDiscoDRef = kCustomStaticIndex + 2,
/*! \brief runtime::RPCObjectRef */
kRuntimeRPCObjectRef = kCustomStaticIndex + 3,
+ // custom builtin
+ kRuntimeString,
+ kRuntimeMap,
+ kRuntimeArray,
// static assignments that may subject to change.
kStaticIndexEnd,
};
@@ -194,7 +196,7 @@ class ObjectRef : public tvm::ffi::ObjectRef {
*/
#define TVM_DEFINE_OBJECT_REF_METHODS_WITHOUT_DEFAULT_CONSTRUCTOR(TypeName, ParentType, \
ObjectName) \
- explicit TypeName(::tvm::runtime::ObjectPtr<::tvm::runtime::Object> n) : ParentType(n) {} \
+ explicit TypeName(::tvm::ffi::ObjectPtr<::tvm::ffi::Object> n) : ParentType(n) {} \
TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName); \
const ObjectName* operator->() const { return static_cast<const ObjectName*>(data_.get()); } \
const ObjectName* get() const { return operator->(); } \
diff --git a/src/runtime/memory/memory_manager.cc b/src/runtime/memory/memory_manager.cc
index fb10b7724b..97fcf0534f 100644
--- a/src/runtime/memory/memory_manager.cc
+++ b/src/runtime/memory/memory_manager.cc
@@ -34,8 +34,8 @@ namespace tvm {
namespace runtime {
namespace memory {
-static void BufferDeleter(void* obj) {
- auto* ptr = static_cast<NDArray::Container*>(obj);
+static void BufferDeleter(TVMFFIObject* ptr_obj) {
+ auto* ptr = ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
ICHECK(ptr->manager_ctx != nullptr);
Buffer* buffer = reinterpret_cast<Buffer*>(ptr->manager_ctx);
MemoryManager::GetAllocator(buffer->device, buffer->alloc_type)->Free(*(buffer));
@@ -50,8 +50,8 @@ Storage::Storage(Buffer buffer, Allocator* allocator) {
data_ = std::move(n);
}
-void StorageObj::Deleter(void* obj) {
- auto* ptr = static_cast<NDArray::Container*>(obj);
+void StorageObj::Deleter(TVMFFIObject* ptr_obj) {
+ auto* ptr = ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
// When invoking AllocNDArray we don't own the underlying allocation
// and should not delete the buffer, but instead let it be reclaimed
// by the storage object's destructor.
@@ -85,8 +85,8 @@ inline size_t GetDataAlignment(const DLTensor& arr) {
return align;
}
-void StorageObj::ScopedDeleter(void* obj) {
- auto* ptr = static_cast<NDArray::Container*>(obj);
+void StorageObj::ScopedDeleter(TVMFFIObject* ptr_obj) {
+ auto* ptr = ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
StorageObj* storage = reinterpret_cast<StorageObj*>(ptr->manager_ctx);
// Let the device handle proper cleanup of view
diff --git a/src/runtime/ndarray.cc b/src/runtime/ndarray.cc
index 67b30bb0d1..9ad2b19ef9 100644
--- a/src/runtime/ndarray.cc
+++ b/src/runtime/ndarray.cc
@@ -101,8 +101,9 @@ void ArrayCopyToBytes(const DLTensor* handle, void* data, size_t nbytes) {
struct NDArray::Internal {
// Default deleter for the container
- static void DefaultDeleter(void* ptr_obj) {
- auto* ptr = static_cast<NDArray::Container*>(ptr_obj);
+ static void DefaultDeleter(TVMFFIObject* ptr_obj) {
+ auto* ptr =
+ ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
if (ptr->manager_ctx != nullptr) {
ffi::details::ObjectUnsafe::DecRefObjectHandle(
static_cast<NDArray::Container*>(ptr->manager_ctx));
@@ -117,8 +118,9 @@ struct NDArray::Internal {
// that are not allocated inside of TVM.
// This enables us to create NDArray from memory allocated by other
// frameworks that are DLPack compatible
- static void DLPackDeleter(void* ptr_obj) {
- auto* ptr = static_cast<NDArray::Container*>(ptr_obj);
+ static void DLPackDeleter(TVMFFIObject* ptr_obj) {
+ auto* ptr =
+ ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
DLManagedTensor* tensor = static_cast<DLManagedTensor*>(ptr->manager_ctx);
if (tensor->deleter != nullptr) {
(*tensor->deleter)(tensor);
@@ -128,8 +130,9 @@ struct NDArray::Internal {
// Deleter for NDArray based on external DLTensor
// The memory is allocated from outside and it is assumed that
// responsibility for its freeing is also outside
- static void SelfDeleter(void* ptr_obj) {
- auto* ptr = static_cast<NDArray::Container*>(ptr_obj);
+ static void SelfDeleter(TVMFFIObject* ptr_obj) {
+ NDArray::Container* ptr =
+ ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
delete ptr;
}
// Local create function which allocates tensor metadata
diff --git a/src/runtime/rpc/rpc_module.cc b/src/runtime/rpc/rpc_module.cc
index 57f2771501..aef4ed4a99 100644
--- a/src/runtime/rpc/rpc_module.cc
+++ b/src/runtime/rpc/rpc_module.cc
@@ -41,8 +41,8 @@ namespace tvm {
namespace runtime {
// deleter of RPC remote array
-static void RemoteNDArrayDeleter(void* obj) {
- auto* ptr = static_cast<NDArray::Container*>(obj);
+static void RemoteNDArrayDeleter(TVMFFIObject* ptr_obj) {
+ auto* ptr = ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
RemoteSpace* space = static_cast<RemoteSpace*>(ptr->dl_tensor.data);
if (ptr->manager_ctx != nullptr) {
space->sess->FreeHandle(ptr->manager_ctx, kTVMNDArrayHandle);
(tvm) 37/38: lint
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit ffc1ec0ebde1ed97fde9eebaddb295978a9b6d61
Author: tqchen <ti...@gmail.com>
AuthorDate: Mon Mar 10 10:06:45 2025 -0400
lint
---
include/tvm/ir/attrs.h | 8 ++++----
include/tvm/runtime/memory.h | 2 +-
src/runtime/ndarray.cc | 8 +++-----
3 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/include/tvm/ir/attrs.h b/include/tvm/ir/attrs.h
index 479cc476bf..6fcf07bfaa 100644
--- a/include/tvm/ir/attrs.h
+++ b/include/tvm/ir/attrs.h
@@ -63,10 +63,10 @@ namespace tvm {
* \param ClassName The name of the class.
* \param TypeKey The type key to be used by the TVM node system.
*/
-#define TVM_DECLARE_ATTRS(ClassName, TypeKey) \
- static constexpr const char* _type_key = TypeKey; \
- TVM_DECLARE_FINAL_OBJECT_INFO(ClassName, ::tvm::BaseAttrsNode);\
- template <typename FVisit> \
+#define TVM_DECLARE_ATTRS(ClassName, TypeKey) \
+ static constexpr const char* _type_key = TypeKey; \
+ TVM_DECLARE_FINAL_OBJECT_INFO(ClassName, ::tvm::BaseAttrsNode); \
+ template <typename FVisit> \
void _tvm_VisitAttrs(FVisit& _tvm_fvisit) // NOLINT(*)
/*!
diff --git a/include/tvm/runtime/memory.h b/include/tvm/runtime/memory.h
index 2b317d05c5..96ca0c8d69 100644
--- a/include/tvm/runtime/memory.h
+++ b/include/tvm/runtime/memory.h
@@ -28,9 +28,9 @@
namespace tvm {
namespace runtime {
+using tvm::ffi::FObjectDeleter;
using tvm::ffi::make_inplace_array_object;
using tvm::ffi::make_object;
-using tvm::ffi::FObjectDeleter;
} // namespace runtime
} // namespace tvm
diff --git a/src/runtime/ndarray.cc b/src/runtime/ndarray.cc
index 9ad2b19ef9..37f470b07e 100644
--- a/src/runtime/ndarray.cc
+++ b/src/runtime/ndarray.cc
@@ -102,8 +102,7 @@ void ArrayCopyToBytes(const DLTensor* handle, void* data, size_t nbytes) {
struct NDArray::Internal {
// Default deleter for the container
static void DefaultDeleter(TVMFFIObject* ptr_obj) {
- auto* ptr =
- ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
+ auto* ptr = ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
if (ptr->manager_ctx != nullptr) {
ffi::details::ObjectUnsafe::DecRefObjectHandle(
static_cast<NDArray::Container*>(ptr->manager_ctx));
@@ -119,8 +118,7 @@ struct NDArray::Internal {
// This enables us to create NDArray from memory allocated by other
// frameworks that are DLPack compatible
static void DLPackDeleter(TVMFFIObject* ptr_obj) {
- auto* ptr =
- ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
+ auto* ptr = ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
DLManagedTensor* tensor = static_cast<DLManagedTensor*>(ptr->manager_ctx);
if (tensor->deleter != nullptr) {
(*tensor->deleter)(tensor);
@@ -132,7 +130,7 @@ struct NDArray::Internal {
// responsibility for its freeing is also outside
static void SelfDeleter(TVMFFIObject* ptr_obj) {
NDArray::Container* ptr =
- ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
+ ffi::details::ObjectUnsafe::RawObjectPtrFromUnowned<NDArray::Container>(ptr_obj);
delete ptr;
}
// Local create function which allocates tensor metadata
(tvm) 10/38: [FFI] Introduce array support
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 40b5ad587eb76ae669acb7fbbf37b55ab92931ad
Author: tqchen <ti...@gmail.com>
AuthorDate: Sat Sep 7 19:05:46 2024 -0400
[FFI] Introduce array support
---
ffi/include/tvm/ffi/any.h | 46 +-
ffi/include/tvm/ffi/c_api.h | 2 +-
ffi/include/tvm/ffi/container/array.h | 1010 ++++++++++++++++++++++++++++++
ffi/include/tvm/ffi/container/base.h | 268 ++++++++
ffi/include/tvm/ffi/container/optional.h | 0
ffi/include/tvm/ffi/memory.h | 9 +-
ffi/include/tvm/ffi/object.h | 2 +-
ffi/include/tvm/ffi/type_traits.h | 230 ++++---
ffi/tests/example/test_array.cc | 231 +++++++
9 files changed, 1703 insertions(+), 95 deletions(-)
diff --git a/ffi/include/tvm/ffi/any.h b/ffi/include/tvm/ffi/any.h
index 2a2d223c57..9912c45f12 100644
--- a/ffi/include/tvm/ffi/any.h
+++ b/ffi/include/tvm/ffi/any.h
@@ -31,6 +31,12 @@ namespace ffi {
class Any;
+namespace details {
+// Helper to perform
+// unsafe operations related to object
+struct AnyUnsafe;
+} // namespace details
+
/*!
* \brief AnyView allows us to take un-managed reference view of any value.
*/
@@ -94,8 +100,9 @@ class AnyView {
if (opt.has_value()) {
return std::move(*opt);
}
- TVM_FFI_THROW(TypeError) << "Cannot convert from type `" << TypeIndex2TypeKey(data_.type_index)
- << "` to `" << TypeTraits<T>::TypeStr() << "`";
+ TVM_FFI_THROW(TypeError) << "Cannot convert from type `"
+ << TypeTraits<T>::GetMismatchTypeInfo(&data_) << "` to `"
+ << TypeTraits<T>::TypeStr() << "`";
TVM_FFI_UNREACHABLE();
}
// The following functions are only used for testing purposes
@@ -212,8 +219,9 @@ class Any {
if (opt.has_value()) {
return std::move(*opt);
}
- TVM_FFI_THROW(TypeError) << "Cannot convert from type `" << TypeIndex2TypeKey(data_.type_index)
- << "` to `" << TypeTraits<T>::TypeStr() << "`";
+ TVM_FFI_THROW(TypeError) << "Cannot convert from type `"
+ << TypeTraits<T>::GetMismatchTypeInfo(&data_) << "` to `"
+ << TypeTraits<T>::TypeStr() << "`";
TVM_FFI_UNREACHABLE();
}
@@ -226,12 +234,42 @@ class Any {
*result = data_;
data_.type_index = TypeIndex::kTVMFFINone;
}
+
+ friend class details::AnyUnsafe;
};
// layout assert to ensure we can freely cast between the two types
static_assert(sizeof(AnyView) == sizeof(TVMFFIAny));
static_assert(sizeof(Any) == sizeof(TVMFFIAny));
+namespace details {
+// Extra unsafe method to help any manipulation
+struct AnyUnsafe : public ObjectUnsafe {
+ /*!
+ * \brief Internal helper function downcast a any that already passes check.
+ * \note Only used for internal dev purposes.
+ * \tparam T The target reference type.
+ * \return The casted result.
+ */
+ template <typename T>
+ static TVM_FFI_INLINE T ConvertAfterCheck(const Any& ref) {
+ if constexpr (!std::is_same_v<T, Any>) {
+ return TypeTraits<T>::ConvertFromAnyViewAfterCheck(&(ref.data_));
+ } else {
+ return ref;
+ }
+ }
+ template <typename T>
+ static TVM_FFI_INLINE bool CheckAny(const Any& ref) {
+ return TypeTraits<T>::CheckAnyView(&(ref.data_));
+ }
+
+ template <typename T>
+ static TVM_FFI_INLINE std::string GetMismatchTypeInfo(const Any& ref) {
+ return TypeTraits<T>::GetMismatchTypeInfo(&(ref.data_));
+ }
+};
+} // namespace details
} // namespace ffi
} // namespace tvm
#endif // TVM_FFI_ANY_H_
diff --git a/ffi/include/tvm/ffi/c_api.h b/ffi/include/tvm/ffi/c_api.h
index 65341feb83..659ee6cd83 100644
--- a/ffi/include/tvm/ffi/c_api.h
+++ b/ffi/include/tvm/ffi/c_api.h
@@ -77,7 +77,7 @@ typedef enum {
// [Section] Static Boxed: [kTVMFFIStaticObjectBegin, kTVMFFIDynObjectBegin)
kTVMFFIStaticObjectBegin = 64,
kTVMFFIObject = 64,
- kTVMFFIList = 65,
+ kTVMFFIArray = 65,
kTVMFFIDict = 66,
kTVMFFIError = 67,
kTVMFFIFunc = 68,
diff --git a/ffi/include/tvm/ffi/container/array.h b/ffi/include/tvm/ffi/container/array.h
new file mode 100644
index 0000000000..2ce946a120
--- /dev/null
+++ b/ffi/include/tvm/ffi/container/array.h
@@ -0,0 +1,1010 @@
+/*
+ * 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.
+ */
+
+/*!
+ * \file tvm/ffi/container/array.h
+ * \brief Array type.
+ *
+ * tvm::ffi::Array<Any> is an erased type that contains list of content
+ */
+#ifndef TVM_FFI_CONTAINER_ARRAY_H_
+#define TVM_FFI_CONTAINER_ARRAY_H_
+
+#include <tvm/ffi/any.h>
+#include <tvm/ffi/container/base.h>
+#include <tvm/ffi/memory.h>
+#include <tvm/ffi/object.h>
+
+#include <algorithm>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+namespace tvm {
+namespace ffi {
+
+/*! \brief array node content in array */
+class ArrayNode : public Object, public InplaceArrayBase<ArrayNode, Any> {
+ public:
+ /*! \return The size of the array */
+ size_t size() const { return this->size_; }
+
+ /*!
+ * \brief Read i-th element from array.
+ * \param i The index
+ * \return the i-th element.
+ */
+ const Any at(int64_t i) const { return this->operator[](i); }
+
+ /*! \return begin constant iterator */
+ const Any* begin() const { return static_cast<Any*>(InplaceArrayBase::AddressOf(0)); }
+
+ /*! \return end constant iterator */
+ const Any* end() const { return begin() + size_; }
+
+ /*! \brief Release reference to all the elements */
+ void clear() { ShrinkBy(size_); }
+
+ /*!
+ * \brief Set i-th element of the array in-place
+ * \param i The index
+ * \param item The value to be set
+ */
+ void SetItem(int64_t i, Any item) { this->operator[](i) = std::move(item); }
+
+ /*!
+ * \brief Constructs a container and copy from another
+ * \param cap The capacity of the container
+ * \param from Source of the copy
+ * \return Ref-counted ArrayNode requested
+ */
+ static ObjectPtr<ArrayNode> CopyFrom(int64_t cap, ArrayNode* from) {
+ int64_t size = from->size_;
+ if (size > cap) {
+ TVM_FFI_THROW(ValueError) << "not enough capacity";
+ }
+ ObjectPtr<ArrayNode> p = ArrayNode::Empty(cap);
+ Any* write = p->MutableBegin();
+ Any* read = from->MutableBegin();
+ // To ensure exception safety, size is only incremented after the initialization succeeds
+ for (int64_t& i = p->size_ = 0; i < size; ++i) {
+ new (write++) Any(*read++);
+ }
+ return p;
+ }
+
+ /*!
+ * \brief Constructs a container and move from another
+ * \param cap The capacity of the container
+ * \param from Source of the move
+ * \return Ref-counted ArrayNode requested
+ */
+ static ObjectPtr<ArrayNode> MoveFrom(int64_t cap, ArrayNode* from) {
+ int64_t size = from->size_;
+ if (size > cap) {
+ TVM_FFI_THROW(RuntimeError) << "not enough capacity";
+ }
+ ObjectPtr<ArrayNode> p = ArrayNode::Empty(cap);
+ Any* write = p->MutableBegin();
+ Any* read = from->MutableBegin();
+ // To ensure exception safety, size is only incremented after the initialization succeeds
+ for (int64_t& i = p->size_ = 0; i < size; ++i) {
+ new (write++) Any(std::move(*read++));
+ }
+ from->size_ = 0;
+ return p;
+ }
+
+ /*!
+ * \brief Constructs a container with n elements. Each element is a copy of val
+ * \param n The size of the container
+ * \param val The init value
+ * \return Ref-counted ArrayNode requested
+ */
+ static ObjectPtr<ArrayNode> CreateRepeated(int64_t n, const Any& val) {
+ ObjectPtr<ArrayNode> p = ArrayNode::Empty(n);
+ Any* itr = p->MutableBegin();
+ for (int64_t& i = p->size_ = 0; i < n; ++i) {
+ new (itr++) Any(val);
+ }
+ return p;
+ }
+
+ static constexpr const int32_t _type_index = TypeIndex::kTVMFFIArray;
+ static constexpr const char* _type_key = "object.Array";
+ static const constexpr bool _type_final = true;
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(ArrayNode, Object);
+
+ private:
+ /*! \return Size of initialized memory, used by InplaceArrayBase. */
+ size_t GetSize() const { return this->size_; }
+
+ /*! \return begin mutable iterator */
+ Any* MutableBegin() const { return static_cast<Any*>(InplaceArrayBase::AddressOf(0)); }
+
+ /*! \return end mutable iterator */
+ Any* MutableEnd() const { return MutableBegin() + size_; }
+
+ /*!
+ * \brief Create an ArrayNode with the given capacity.
+ * \param n Required capacity
+ * \return Ref-counted ArrayNode requested
+ */
+ static ObjectPtr<ArrayNode> Empty(int64_t n = kInitSize) {
+ TVM_FFI_ICHECK_GE(n, 0);
+ ObjectPtr<ArrayNode> p = make_inplace_array_object<ArrayNode, Any>(n);
+ p->capacity_ = n;
+ p->size_ = 0;
+ return p;
+ }
+
+ /*!
+ * \brief Inplace-initialize the elements starting idx from [first, last)
+ * \param idx The starting point
+ * \param first Begin of iterator
+ * \param last End of iterator
+ * \tparam IterType The type of iterator
+ * \return Self
+ */
+ template <typename IterType>
+ ArrayNode* InitRange(int64_t idx, IterType first, IterType last) {
+ Any* itr = MutableBegin() + idx;
+ for (; first != last; ++first) {
+ Any ref = *first;
+ new (itr++) Any(std::move(ref));
+ }
+ return this;
+ }
+
+ /*!
+ * \brief Move elements from right to left, requires src_begin > dst
+ * \param dst Destination
+ * \param src_begin The start point of copy (inclusive)
+ * \param src_end The end point of copy (exclusive)
+ * \return Self
+ */
+ ArrayNode* MoveElementsLeft(int64_t dst, int64_t src_begin, int64_t src_end) {
+ Any* from = MutableBegin() + src_begin;
+ Any* to = MutableBegin() + dst;
+ while (src_begin++ != src_end) {
+ *to++ = std::move(*from++);
+ }
+ return this;
+ }
+
+ /*!
+ * \brief Move elements from left to right, requires src_begin < dst
+ * \param dst Destination
+ * \param src_begin The start point of move (inclusive)
+ * \param src_end The end point of move (exclusive)
+ * \return Self
+ */
+ ArrayNode* MoveElementsRight(int64_t dst, int64_t src_begin, int64_t src_end) {
+ Any* from = MutableBegin() + src_end;
+ Any* to = MutableBegin() + (src_end - src_begin + dst);
+ while (src_begin++ != src_end) {
+ *--to = std::move(*--from);
+ }
+ return this;
+ }
+
+ /*!
+ * \brief Enlarges the size of the array
+ * \param delta Size enlarged, should be positive
+ * \param val Default value
+ * \return Self
+ */
+ ArrayNode* EnlargeBy(int64_t delta, const Any& val = Any()) {
+ Any* itr = MutableEnd();
+ while (delta-- > 0) {
+ new (itr++) Any(val);
+ ++size_;
+ }
+ return this;
+ }
+
+ /*!
+ * \brief Shrinks the size of the array
+ * \param delta Size shrinked, should be positive
+ * \return Self
+ */
+ ArrayNode* ShrinkBy(int64_t delta) {
+ Any* itr = MutableEnd();
+ while (delta-- > 0) {
+ (--itr)->Any::~Any();
+ --size_;
+ }
+ return this;
+ }
+
+ /*! \brief Number of elements used */
+ int64_t size_;
+
+ /*! \brief Number of elements allocated */
+ int64_t capacity_;
+
+ /*! \brief Initial size of ArrayNode */
+ static constexpr int64_t kInitSize = 4;
+
+ /*! \brief Expansion factor of the Array */
+ static constexpr int64_t kIncFactor = 2;
+
+ // CRTP parent class
+ friend InplaceArrayBase<ArrayNode, Any>;
+
+ // Reference class
+ template <typename, typename>
+ friend class Array;
+
+ // To specialize make_object<ArrayNode>
+ friend ObjectPtr<ArrayNode> make_object<>();
+};
+
+/*! \brief Helper struct for type-checking
+ *
+ * is_valid_iterator<T,IterType>::value will be true if IterType can
+ * be dereferenced into a type that can be stored in an Array<T>, and
+ * false otherwise.
+ */
+template <typename T, typename IterType>
+struct is_valid_iterator
+ : std::bool_constant<
+ std::is_same_v<
+ T, std::remove_cv_t<std::remove_reference_t<decltype(*std::declval<IterType>())>>> ||
+ std::is_base_of_v<
+ T, std::remove_cv_t<std::remove_reference_t<decltype(*std::declval<IterType>())>>>> {
+};
+
+template <typename T, typename IterType>
+struct is_valid_iterator<Optional<T>, IterType> : is_valid_iterator<T, IterType> {};
+
+template <typename T, typename IterType>
+inline constexpr bool is_valid_iterator_v = is_valid_iterator<T, IterType>::value;
+
+/*!
+ * \brief Array, container representing a contiguous sequence of ObjectRefs.
+ *
+ * Array implements in-place copy-on-write semantics.
+ *
+ * As in typical copy-on-write, a method which would typically mutate the array
+ * instead opaquely copies the underlying container, and then acts on its copy.
+ *
+ * If the array has reference count equal to one, we directly update the
+ * container in place without copying. This is optimization is sound because
+ * when the reference count is equal to one this reference is guranteed to be
+ * the sole pointer to the container.
+ *
+ *
+ * operator[] only provides const access, use Set to mutate the content.
+ * \tparam T The content Value type, must be compatible with tvm::ffi::Any
+ */
+template <typename T,
+ typename = typename std::enable_if_t<std::is_same_v<T, Any> || TypeTraits<T>::enabled>>
+class Array : public ObjectRef {
+ public:
+ using value_type = T;
+ // constructors
+ /*!
+ * \brief default constructor
+ */
+ Array() { data_ = ArrayNode::Empty(); }
+
+ /*!
+ * \brief move constructor
+ * \param other source
+ */
+ Array(Array<T>&& other) : ObjectRef() { // NOLINT(*)
+ data_ = std::move(other.data_);
+ }
+
+ /*!
+ * \brief copy constructor
+ * \param other source
+ */
+ Array(const Array<T>& other) : ObjectRef() { // NOLINT(*)
+ data_ = other.data_;
+ }
+
+ /*!
+ * \brief constructor from pointer
+ * \param n the container pointer
+ */
+ explicit Array(ObjectPtr<Object> n) : ObjectRef(n) {}
+
+ /*!
+ * \brief Constructor from iterator
+ * \param first begin of iterator
+ * \param last end of iterator
+ * \tparam IterType The type of iterator
+ */
+ template <typename IterType>
+ Array(IterType first, IterType last) {
+ static_assert(is_valid_iterator_v<T, IterType>,
+ "IterType cannot be inserted into a tvm::Array<T>");
+ Assign(first, last);
+ }
+
+ /*!
+ * \brief constructor from initializer list
+ * \param init The initializer list
+ */
+ Array(std::initializer_list<T> init) { // NOLINT(*)
+ Assign(init.begin(), init.end());
+ }
+
+ /*!
+ * \brief constructor from vector
+ * \param init The vector
+ */
+ Array(const std::vector<T>& init) { // NOLINT(*)
+ Assign(init.begin(), init.end());
+ }
+
+ /*!
+ * \brief Constructs a container with n elements. Each element is a copy of val
+ * \param n The size of the container
+ * \param val The init value
+ */
+ explicit Array(const size_t n, const T& val) { data_ = ArrayNode::CreateRepeated(n, val); }
+
+ /*!
+ * \brief move assign operator
+ * \param other The source of assignment
+ * \return reference to self.
+ */
+ Array<T>& operator=(Array<T>&& other) {
+ data_ = std::move(other.data_);
+ return *this;
+ }
+
+ /*!
+ * \brief copy assign operator
+ * \param other The source of assignment
+ * \return reference to self.
+ */
+ Array<T>& operator=(const Array<T>& other) {
+ data_ = other.data_;
+ return *this;
+ }
+
+ public:
+ // iterators
+ struct ValueConverter {
+ using ResultType = T;
+ static T convert(const Any& n) { return details::AnyUnsafe::ConvertAfterCheck<T>(n); }
+ };
+
+ using iterator = IterAdapter<ValueConverter, const Any*>;
+ using reverse_iterator = ReverseIterAdapter<ValueConverter, const Any*>;
+
+ /*! \return begin iterator */
+ iterator begin() const { return iterator(GetArrayNode()->begin()); }
+
+ /*! \return end iterator */
+ iterator end() const { return iterator(GetArrayNode()->end()); }
+
+ /*! \return rbegin iterator */
+ reverse_iterator rbegin() const {
+ // ArrayNode::end() is never nullptr
+ return reverse_iterator(GetArrayNode()->end() - 1);
+ }
+
+ /*! \return rend iterator */
+ reverse_iterator rend() const {
+ // ArrayNode::begin() is never nullptr
+ return reverse_iterator(GetArrayNode()->begin() - 1);
+ }
+
+ public:
+ // const methods in std::vector
+ /*!
+ * \brief Immutably read i-th element from array.
+ * \param i The index
+ * \return the i-th element.
+ */
+ const T operator[](int64_t i) const {
+ ArrayNode* p = GetArrayNode();
+ if (p == nullptr) {
+ TVM_FFI_THROW(IndexError) << "cannot index a null array";
+ }
+ if (i < 0 || i >= p->size_) {
+ TVM_FFI_THROW(IndexError) << "indexing " << i << " on an array of size " << p->size_;
+ }
+ return details::AnyUnsafe::ConvertAfterCheck<T>(*(p->begin() + i));
+ }
+
+ /*! \return The size of the array */
+ size_t size() const {
+ ArrayNode* p = GetArrayNode();
+ return p == nullptr ? 0 : GetArrayNode()->size_;
+ }
+
+ /*! \return The capacity of the array */
+ size_t capacity() const {
+ ArrayNode* p = GetArrayNode();
+ return p == nullptr ? 0 : GetArrayNode()->capacity_;
+ }
+
+ /*! \return Whether array is empty */
+ bool empty() const { return size() == 0; }
+
+ /*! \return The first element of the array */
+ const T front() const {
+ ArrayNode* p = GetArrayNode();
+ if (p == nullptr || p->size_ == 0) {
+ TVM_FFI_THROW(IndexError) << "cannot index a empty array";
+ }
+ return details::AnyUnsafe::ConvertAfterCheck<T>(*(p->begin()));
+ }
+
+ /*! \return The last element of the array */
+ const T back() const {
+ ArrayNode* p = GetArrayNode();
+ if (p == nullptr || p->size_ == 0) {
+ TVM_FFI_THROW(IndexError) << "cannot index a empty array";
+ }
+ return details::AnyUnsafe::ConvertAfterCheck<T>(*(p->end() - 1));
+ }
+
+ public:
+ // mutation in std::vector, implements copy-on-write
+ /*!
+ * \brief push a new item to the back of the list
+ * \param item The item to be pushed.
+ */
+ void push_back(const T& item) {
+ ArrayNode* p = CopyOnWrite(1);
+ p->EmplaceInit(p->size_++, item);
+ }
+
+ /*!
+ * \brief Insert an element into the given position
+ * \param position An iterator pointing to the insertion point
+ * \param val The element to insert
+ */
+ void insert(iterator position, const T& val) {
+ if (data_ == nullptr) {
+ TVM_FFI_THROW(RuntimeError) << "cannot insert a null array";
+ }
+ int64_t idx = std::distance(begin(), position);
+ int64_t size = GetArrayNode()->size_;
+ auto addr = CopyOnWrite(1) //
+ ->EnlargeBy(1) //
+ ->MoveElementsRight(idx + 1, idx, size) //
+ ->MutableBegin();
+ new (addr + idx) Any(val);
+ }
+
+ /*!
+ * \brief Insert a range of elements into the given position
+ * \param position An iterator pointing to the insertion point
+ * \param first The begin iterator of the range
+ * \param last The end iterator of the range
+ */
+ template <typename IterType>
+ void insert(iterator position, IterType first, IterType last) {
+ static_assert(is_valid_iterator_v<T, IterType>,
+ "IterType cannot be inserted into a tvm::Array<T>");
+
+ if (first == last) {
+ return;
+ }
+ if (data_ == nullptr) {
+ TVM_FFI_THROW(RuntimeError) << "cannot insert a null array";
+ }
+ int64_t idx = std::distance(begin(), position);
+ int64_t size = GetArrayNode()->size_;
+ int64_t numel = std::distance(first, last);
+ CopyOnWrite(numel)
+ ->EnlargeBy(numel)
+ ->MoveElementsRight(idx + numel, idx, size)
+ ->InitRange(idx, first, last);
+ }
+
+ /*! \brief Remove the last item of the list */
+ void pop_back() {
+ if (data_ == nullptr) {
+ TVM_FFI_THROW(RuntimeError) << "cannot pop_back a null array";
+ }
+ int64_t size = GetArrayNode()->size_;
+ if (size == 0) {
+ TVM_FFI_THROW(RuntimeError) << "cannot pop_back an empty array";
+ }
+ CopyOnWrite()->ShrinkBy(1);
+ }
+
+ /*!
+ * \brief Erase an element on the given position
+ * \param position An iterator pointing to the element to be erased
+ */
+ void erase(iterator position) {
+ if (data_ == nullptr) {
+ TVM_FFI_THROW(RuntimeError) << "cannot erase a null array";
+ }
+ int64_t st = std::distance(begin(), position);
+ int64_t size = GetArrayNode()->size_;
+ if (st < 0 || st >= size) {
+ TVM_FFI_THROW(RuntimeError) << "cannot erase at index " << st << ", because Array size is "
+ << size;
+ }
+ CopyOnWrite() //
+ ->MoveElementsLeft(st, st + 1, size) //
+ ->ShrinkBy(1);
+ }
+
+ /*!
+ * \brief Erase a given range of elements
+ * \param first The begin iterator of the range
+ * \param last The end iterator of the range
+ */
+ void erase(iterator first, iterator last) {
+ if (first == last) {
+ return;
+ }
+ if (data_ == nullptr) {
+ TVM_FFI_THROW(RuntimeError) << "cannot erase a null array";
+ }
+ int64_t size = GetArrayNode()->size_;
+ int64_t st = std::distance(begin(), first);
+ int64_t ed = std::distance(begin(), last);
+ if (st >= ed) {
+ TVM_FFI_THROW(IndexError) << "cannot erase array in range [" << st << ", " << ed << ")";
+ }
+ if (st < 0 || st > size || ed < 0 || ed > size) {
+ TVM_FFI_THROW(IndexError) << "cannot erase array in range [" << st << ", " << ed << ")"
+ << ", because array size is " << size;
+ }
+ CopyOnWrite() //
+ ->MoveElementsLeft(st, ed, size) //
+ ->ShrinkBy(ed - st);
+ }
+
+ /*!
+ * \brief Resize the array.
+ * \param n The new size.
+ */
+ void resize(int64_t n) {
+ if (n < 0) {
+ TVM_FFI_THROW(ValueError) << "cannot resize an Array to negative size";
+ }
+ if (data_ == nullptr) {
+ SwitchContainer(n);
+ return;
+ }
+ int64_t size = GetArrayNode()->size_;
+ if (size < n) {
+ CopyOnWrite(n - size)->EnlargeBy(n - size);
+ } else if (size > n) {
+ CopyOnWrite()->ShrinkBy(size - n);
+ }
+ }
+
+ /*!
+ * \brief Make sure the list has the capacity of at least n
+ * \param n lower bound of the capacity
+ */
+ void reserve(int64_t n) {
+ if (data_ == nullptr || n > GetArrayNode()->capacity_) {
+ SwitchContainer(n);
+ }
+ }
+
+ /*! \brief Release reference to all the elements */
+ void clear() {
+ if (data_ != nullptr) {
+ ArrayNode* p = CopyOnWrite();
+ p->clear();
+ }
+ }
+
+ template <typename... Args>
+ static size_t CalcCapacityImpl() {
+ return 0;
+ }
+
+ template <typename... Args>
+ static size_t CalcCapacityImpl(Array<T> value, Args... args) {
+ return value.size() + CalcCapacityImpl(args...);
+ }
+
+ template <typename... Args>
+ static size_t CalcCapacityImpl(T value, Args... args) {
+ return 1 + CalcCapacityImpl(args...);
+ }
+
+ template <typename... Args>
+ static void AgregateImpl(Array<T>& dest) {} // NOLINT(*)
+
+ template <typename... Args>
+ static void AgregateImpl(Array<T>& dest, Array<T> value, Args... args) { // NOLINT(*)
+ dest.insert(dest.end(), value.begin(), value.end());
+ AgregateImpl(dest, args...);
+ }
+
+ template <typename... Args>
+ static void AgregateImpl(Array<T>& dest, T value, Args... args) { // NOLINT(*)
+ dest.push_back(value);
+ AgregateImpl(dest, args...);
+ }
+
+ public:
+ // Array's own methods
+
+ /*!
+ * \brief set i-th element of the array.
+ * \param i The index
+ * \param value The value to be setted.
+ */
+ void Set(int64_t i, T value) {
+ ArrayNode* p = this->CopyOnWrite();
+ if (i < 0 || i >= p->size_) {
+ TVM_FFI_THROW(IndexError) << "indexing " << i << " on an array of size " << p->size_;
+ }
+ *(p->MutableBegin() + i) = std::move(value);
+ }
+
+ /*! \return The underlying ArrayNode */
+ ArrayNode* GetArrayNode() const { return static_cast<ArrayNode*>(data_.get()); }
+
+ /*!
+ * \brief Helper function to apply a map function onto the array.
+ *
+ * \param fmap The transformation function T -> U.
+ *
+ * \tparam F The type of the mutation function.
+ *
+ * \tparam U The type of the returned array, inferred from the
+ * return type of F. If overridden by the user, must be something
+ * that is convertible from the return type of F.
+ *
+ * \note This function performs copy on write optimization. If
+ * `fmap` returns an object of type `T`, and all elements of the
+ * array are mapped to themselves, then the returned array will be
+ * the same as the original, and reference counts of the elements in
+ * the array will not be incremented.
+ *
+ * \return The transformed array.
+ */
+ template <typename F, typename U = std::invoke_result_t<F, T>>
+ Array<U> Map(F fmap) const {
+ return Array<U>(MapHelper(data_, fmap));
+ }
+
+ /*!
+ * \brief Helper function to apply fmutate to mutate an array.
+ * \param fmutate The transformation function T -> T.
+ * \tparam F the type of the mutation function.
+ * \note This function performs copy on write optimization.
+ */
+ template <typename F, typename = std::enable_if_t<std::is_same_v<T, std::invoke_result_t<F, T>>>>
+ void MutateByApply(F fmutate) {
+ data_ = MapHelper(std::move(data_), fmutate);
+ }
+
+ /*!
+ * \brief reset the array to content from iterator.
+ * \param first begin of iterator
+ * \param last end of iterator
+ * \tparam IterType The type of iterator
+ */
+ template <typename IterType>
+ void Assign(IterType first, IterType last) {
+ int64_t cap = std::distance(first, last);
+ if (cap < 0) {
+ TVM_FFI_THROW(ValueError) << "cannot construct an Array of negative size";
+ }
+ ArrayNode* p = GetArrayNode();
+ if (p != nullptr && data_.unique() && p->capacity_ >= cap) {
+ // do not have to make new space
+ p->clear();
+ } else {
+ // create new space
+ data_ = ArrayNode::Empty(cap);
+ p = GetArrayNode();
+ }
+ // To ensure exception safety, size is only incremented after the initialization succeeds
+ Any* itr = p->MutableBegin();
+ for (int64_t& i = p->size_ = 0; i < cap; ++i, ++first, ++itr) {
+ new (itr) Any(*first);
+ }
+ }
+
+ /*!
+ * \brief Copy on write semantics
+ * Do nothing if current handle is the unique copy of the array.
+ * Otherwise make a new copy of the array to ensure the current handle
+ * hold a unique copy.
+ *
+ * \return Handle to the internal node container(which ganrantees to be unique)
+ */
+ ArrayNode* CopyOnWrite() {
+ if (data_ == nullptr) {
+ return SwitchContainer(ArrayNode::kInitSize);
+ }
+ if (!data_.unique()) {
+ return SwitchContainer(capacity());
+ }
+ return static_cast<ArrayNode*>(data_.get());
+ }
+
+ /*! \brief specify container node */
+ using ContainerType = ArrayNode;
+
+ /*!
+ * \brief Agregate arguments into a single Array<T>
+ * \param args sequence of T or Array<T> elements
+ * \return Agregated Array<T>
+ */
+ template <typename... Args>
+ static Array<T> Agregate(Args... args) {
+ Array<T> result;
+ result.reserve(CalcCapacityImpl(args...));
+ AgregateImpl(result, args...);
+ return result;
+ }
+
+ private:
+ /*!
+ * \brief Implement copy-on-write semantics, and ensures capacity is enough for extra elements.
+ * \param reserve_extra Number of extra slots needed
+ * \return ArrayNode pointer to the unique copy
+ */
+ ArrayNode* CopyOnWrite(int64_t reserve_extra) {
+ ArrayNode* p = GetArrayNode();
+ if (p == nullptr) {
+ // necessary to get around the constexpr address issue before c++17
+ const int64_t kInitSize = ArrayNode::kInitSize;
+ return SwitchContainer(std::max(kInitSize, reserve_extra));
+ }
+ if (p->capacity_ >= p->size_ + reserve_extra) {
+ return CopyOnWrite();
+ }
+ int64_t cap = p->capacity_ * ArrayNode::kIncFactor;
+ cap = std::max(cap, p->size_ + reserve_extra);
+ return SwitchContainer(cap);
+ }
+
+ /*!
+ * \brief Move or copy the ArrayNode to new address with the given capacity
+ * \param capacity The capacity requirement of the new address
+ */
+ ArrayNode* SwitchContainer(int64_t capacity) {
+ if (data_ == nullptr) {
+ data_ = ArrayNode::Empty(capacity);
+ } else if (data_.unique()) {
+ data_ = ArrayNode::MoveFrom(capacity, GetArrayNode());
+ } else {
+ data_ = ArrayNode::CopyFrom(capacity, GetArrayNode());
+ }
+ return static_cast<ArrayNode*>(data_.get());
+ }
+
+ /*! \brief Helper method for mutate/map
+ *
+ * A helper function used internally by both `Array::Map` and
+ * `Array::MutateInPlace`. Given an array of data, apply the
+ * mapping function to each element, returning the collected array.
+ * Applies both mutate-in-place and copy-on-write optimizations, if
+ * possible.
+ *
+ * \param data A pointer to the ArrayNode containing input data.
+ * Passed by value to allow for mutate-in-place optimizations.
+ *
+ * \param fmap The mapping function
+ *
+ * \tparam F The type of the mutation function.
+ *
+ * \tparam U The output type of the mutation function. Inferred
+ * from the callable type given. Must inherit from ObjectRef.
+ *
+ * \return The mapped array. Depending on whether mutate-in-place
+ * or copy-on-write optimizations were applicable, may be the same
+ * underlying array as the `data` parameter.
+ */
+ template <typename F, typename U = std::invoke_result_t<F, T>>
+ static ObjectPtr<Object> MapHelper(ObjectPtr<Object> data, F fmap) {
+ if (data == nullptr) {
+ return nullptr;
+ }
+
+ TVM_FFI_ICHECK(data->IsInstance<ArrayNode>());
+
+ constexpr bool is_same_output_type = std::is_same_v<T, U>;
+
+ if constexpr (is_same_output_type) {
+ if (data.unique()) {
+ // Mutate-in-place path. Only allowed if the output type U is
+ // the same as type T, we have a mutable this*, and there are
+ // no other shared copies of the array.
+ auto arr = static_cast<ArrayNode*>(data.get());
+ for (auto it = arr->MutableBegin(); it != arr->MutableEnd(); it++) {
+ T mapped = fmap(details::AnyUnsafe::ConvertAfterCheck<T>(std::move(*it)));
+ *it = std::move(mapped);
+ }
+ return data;
+ }
+ }
+
+ constexpr bool compatible_types = is_valid_iterator_v<T, U*> || is_valid_iterator_v<U, T*>;
+
+ ObjectPtr<ArrayNode> output = nullptr;
+ auto arr = static_cast<ArrayNode*>(data.get());
+
+ auto it = arr->begin();
+ if constexpr (compatible_types) {
+ // Copy-on-write path, if the output Array<U> might be
+ // represented by the same underlying array as the existing
+ // Array<T>. Typically, this is for functions that map `T` to
+ // `T`, but can also apply to functions that map `T` to
+ // `Optional<T>`, or that map `T` to a subclass or superclass of
+ // `T`.
+ bool all_identical = true;
+ for (; it != arr->end(); it++) {
+ U mapped = fmap(details::AnyUnsafe::ConvertAfterCheck<T>(*it));
+ if (!mapped.same_as(*it)) {
+ // At least one mapped element is different than the
+ // original. Therefore, prepare the output array,
+ // consisting of any previous elements that had mapped to
+ // themselves (if any), and the element that didn't map to
+ // itself.
+ //
+ // We cannot use `U()` as the default object, as `U` may be
+ // a non-nullable type. Since the default `Any()`
+ // will be overwritten before returning, all objects will be
+ // of type `U` for the calling scope.
+ all_identical = false;
+ output = ArrayNode::CreateRepeated(arr->size(), Any());
+ output->InitRange(0, arr->begin(), it);
+ output->SetItem(it - arr->begin(), std::move(mapped));
+ it++;
+ break;
+ }
+ }
+ if (all_identical) {
+ return data;
+ }
+ } else {
+ // Path for incompatible types. The constexpr check for
+ // compatible types isn't strictly necessary, as the first
+ // mapped.same_as(*it) would return false, but we might as well
+ // avoid it altogether.
+ //
+ // We cannot use `U()` as the default object, as `U` may be a
+ // non-nullable type. Since the default `Any()` will be
+ // overwritten before returning, all objects will be of type `U`
+ // for the calling scope.
+ output = ArrayNode::CreateRepeated(arr->size(), Any());
+ }
+
+ // Normal path for incompatible types, or post-copy path for
+ // copy-on-write instances.
+ //
+ // If the types are incompatible, then at this point `output` is
+ // empty, and `it` points to the first element of the input.
+ //
+ // If the types were compatible, then at this point `output`
+ // contains zero or more elements that mapped to themselves
+ // followed by the first element that does not map to itself, and
+ // `it` points to the element just after the first element that
+ // does not map to itself. Because at least one element has been
+ // changed, we no longer have the opportunity to avoid a copy, so
+ // we don't need to check the result.
+ //
+ // In both cases, `it` points to the next element to be processed,
+ // so we can either start or resume the iteration from that point,
+ // with no further checks on the result.
+ for (; it != arr->end(); it++) {
+ U mapped = fmap(details::AnyUnsafe::ConvertAfterCheck<T>(*it));
+ output->SetItem(it - arr->begin(), std::move(mapped));
+ }
+
+ return output;
+ }
+};
+
+/*!
+ * \brief Concat two Arrays.
+ * \param lhs first Array to be concatenated.
+ * \param rhs second Array to be concatenated.
+ * \return The concatenated Array. Original Arrays are kept unchanged.
+ */
+template <typename T,
+ typename = typename std::enable_if_t<std::is_same_v<T, Any> || TypeTraits<T>::enabled>>
+inline Array<T> Concat(Array<T> lhs, const Array<T>& rhs) {
+ for (const auto& x : rhs) {
+ lhs.push_back(x);
+ }
+ return std::move(lhs);
+}
+
+// Specialize make_object<ArrayNode> to make sure it is correct.
+template <>
+inline ObjectPtr<ArrayNode> make_object() {
+ return ArrayNode::Empty();
+}
+
+// Traits for Array
+template <typename T>
+inline constexpr bool use_default_type_traits_v<Array<T>> = false;
+
+template <typename T>
+struct TypeTraits<Array<T>> : public TypeTraitsBase {
+ static TVM_FFI_INLINE void ConvertToAnyView(const Array<T>& src, TVMFFIAny* result) {
+ TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetTVMFFIObjectPtrFromObjectRef(src);
+ result->type_index = obj_ptr->type_index;
+ result->v_obj = obj_ptr;
+ }
+
+ static TVM_FFI_INLINE void MoveToAny(Array<T> src, TVMFFIAny* result) {
+ TVMFFIObject* obj_ptr = details::ObjectUnsafe::MoveTVMFFIObjectPtrFromObjectRef(&src);
+ result->type_index = obj_ptr->type_index;
+ result->v_obj = obj_ptr;
+ }
+
+ static TVM_FFI_INLINE std::string GetMismatchTypeInfo(const TVMFFIAny* src) {
+ if (src->type_index != TypeIndex::kTVMFFIArray) {
+ return TypeTraitsBase::GetMismatchTypeInfo(src);
+ }
+ if constexpr (!std::is_same_v<T, Any>) {
+ const ArrayNode* n = reinterpret_cast<const ArrayNode*>(src->v_obj);
+ for (size_t i = 0; i < n->size(); i++) {
+ const Any& p = (*n)[i];
+ if (!details::AnyUnsafe::CheckAny<T>(p)) {
+ return "Array[index " + std::to_string(i) + ": " +
+ details::AnyUnsafe::GetMismatchTypeInfo<T>(p) + "]";
+ }
+ }
+ }
+ TVM_FFI_THROW(InternalError) << "Cannot reach here";
+ TVM_FFI_UNREACHABLE();
+ }
+
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ // for now allow null array, TODO: revisit this
+ if (src->type_index == TypeIndex::kTVMFFINone) return true;
+ if (src->type_index != TypeIndex::kTVMFFIArray) return false;
+ if constexpr (std::is_same_v<T, Any>) {
+ return true;
+ } else {
+ const ArrayNode* n = reinterpret_cast<const ArrayNode*>(src->v_obj);
+ for (size_t i = 0; i < n->size(); i++) {
+ const Any& p = (*n)[i];
+ if (!details::AnyUnsafe::CheckAny<T>(p)) return false;
+ }
+ return true;
+ }
+ }
+
+ static TVM_FFI_INLINE Array<T> ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFINone) return Array<T>(nullptr);
+ return Array<T>(details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(src->v_obj));
+ }
+
+ static TVM_FFI_INLINE std::optional<Array<T>> TryConvertFromAnyView(const TVMFFIAny* src) {
+ if (CheckAnyView(src)) return ConvertFromAnyViewAfterCheck(src);
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return "Array<" + TypeTraits<T>::TypeStr() + ">"; }
+};
+
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_CONTAINER_ARRAY_H_
diff --git a/ffi/include/tvm/ffi/container/base.h b/ffi/include/tvm/ffi/container/base.h
new file mode 100644
index 0000000000..bfeeacd59b
--- /dev/null
+++ b/ffi/include/tvm/ffi/container/base.h
@@ -0,0 +1,268 @@
+/*
+ * 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.
+ */
+
+/*!
+ * \file tvm/ffi/container/base.h
+ * \brief Base utilities for common POD(plain old data) container types.
+ */
+#ifndef TVM_FFI_CONTAINER_BASE_H_
+#define TVM_FFI_CONTAINER_BASE_H_
+
+#include <tvm/ffi/memory.h>
+#include <tvm/ffi/object.h>
+
+#include <algorithm>
+#include <initializer_list>
+#include <utility>
+
+namespace tvm {
+namespace ffi {
+/*!
+ * \brief Base template for classes with array like memory layout.
+ *
+ * It provides general methods to access the memory. The memory
+ * layout is ArrayType + [ElemType]. The alignment of ArrayType
+ * and ElemType is handled by the memory allocator.
+ *
+ * \tparam ArrayType The array header type, contains object specific metadata.
+ * \tparam ElemType The type of objects stored in the array right after
+ * ArrayType.
+ *
+ * \code
+ * // Example usage of the template to define a simple array wrapper
+ * class ArrayNode : public InplaceArrayBase<ArrayNode, Elem> {
+ * public:
+ * // Wrap EmplaceInit to initialize the elements
+ * template <typename Iterator>
+ * void Init(Iterator begin, Iterator end) {
+ * size_t num_elems = std::distance(begin, end);
+ * auto it = begin;
+ * this->size = 0;
+ * for (size_t i = 0; i < num_elems; ++i) {
+ * InplaceArrayBase::EmplaceInit(i, *it++);
+ * this->size++;
+ * }
+ * }
+ * }
+ *
+ * void test_function() {
+ * vector<Elem> fields;
+ * auto ptr = make_inplace_array_object<ArrayObj, Elem>(fields.size());
+ * ptr->Init(fields.begin(), fields.end());
+ *
+ * // Access the 0th element in the array.
+ * assert(ptr->operator[](0) == fields[0]);
+ * }
+ *
+ * \endcode
+ */
+template <typename ArrayType, typename ElemType>
+class InplaceArrayBase {
+ public:
+ /*!
+ * \brief Access element at index
+ * \param idx The index of the element.
+ * \return Const reference to ElemType at the index.
+ */
+ const ElemType& operator[](size_t idx) const {
+ size_t size = Self()->GetSize();
+ if (idx > size) {
+ TVM_FFI_THROW(IndexError) << "Index " << idx << " out of bounds " << size;
+ }
+ return *(reinterpret_cast<ElemType*>(AddressOf(idx)));
+ }
+
+ /*!
+ * \brief Access element at index
+ * \param idx The index of the element.
+ * \return Reference to ElemType at the index.
+ */
+ ElemType& operator[](size_t idx) {
+ size_t size = Self()->GetSize();
+ if (idx > size) {
+ TVM_FFI_THROW(IndexError) << "Index " << idx << " out of bounds " << size;
+ }
+ return *(reinterpret_cast<ElemType*>(AddressOf(idx)));
+ }
+
+ /*!
+ * \brief Destroy the Inplace Array Base object
+ */
+ ~InplaceArrayBase() {
+ if (!(std::is_standard_layout<ElemType>::value && std::is_trivial<ElemType>::value)) {
+ size_t size = Self()->GetSize();
+ for (size_t i = 0; i < size; ++i) {
+ ElemType* fp = reinterpret_cast<ElemType*>(AddressOf(i));
+ fp->ElemType::~ElemType();
+ }
+ }
+ }
+
+ protected:
+ /*!
+ * \brief Construct a value in place with the arguments.
+ *
+ * \tparam Args Type parameters of the arguments.
+ * \param idx Index of the element.
+ * \param args Arguments to construct the new value.
+ *
+ * \note Please make sure ArrayType::GetSize returns 0 before first call of
+ * EmplaceInit, and increment GetSize by 1 each time EmplaceInit succeeds.
+ */
+ template <typename... Args>
+ void EmplaceInit(size_t idx, Args&&... args) {
+ void* field_ptr = AddressOf(idx);
+ new (field_ptr) ElemType(std::forward<Args>(args)...);
+ }
+
+ /*!
+ * \brief Return the self object for the array.
+ *
+ * \return Pointer to ArrayType.
+ */
+ inline ArrayType* Self() const {
+ return static_cast<ArrayType*>(const_cast<InplaceArrayBase*>(this));
+ }
+
+ /*!
+ * \brief Return the raw pointer to the element at idx.
+ *
+ * \param idx The index of the element.
+ * \return Raw pointer to the element.
+ */
+ void* AddressOf(size_t idx) const {
+ static_assert(
+ alignof(ArrayType) % alignof(ElemType) == 0 && sizeof(ArrayType) % alignof(ElemType) == 0,
+ "The size and alignment of ArrayType should respect "
+ "ElemType's alignment.");
+
+ size_t kDataStart = sizeof(ArrayType);
+ ArrayType* self = Self();
+ char* data_start = reinterpret_cast<char*>(self) + kDataStart;
+ return data_start + idx * sizeof(ElemType);
+ }
+};
+
+/*!
+ * \brief iterator adapter that adapts TIter to return another type.
+ * \tparam Converter a struct that contains converting function
+ * \tparam TIter the content iterator type.
+ */
+template <typename Converter, typename TIter>
+class IterAdapter {
+ public:
+ using difference_type = typename std::iterator_traits<TIter>::difference_type;
+ using value_type = typename Converter::ResultType;
+ using pointer = typename Converter::ResultType*;
+ using reference = typename Converter::ResultType&;
+ using iterator_category = typename std::iterator_traits<TIter>::iterator_category;
+
+ explicit IterAdapter(TIter iter) : iter_(iter) {}
+ IterAdapter& operator++() {
+ ++iter_;
+ return *this;
+ }
+ IterAdapter& operator--() {
+ --iter_;
+ return *this;
+ }
+ IterAdapter operator++(int) {
+ IterAdapter copy = *this;
+ ++iter_;
+ return copy;
+ }
+ IterAdapter operator--(int) {
+ IterAdapter copy = *this;
+ --iter_;
+ return copy;
+ }
+
+ IterAdapter operator+(difference_type offset) const { return IterAdapter(iter_ + offset); }
+
+ IterAdapter operator-(difference_type offset) const { return IterAdapter(iter_ - offset); }
+
+ template <typename T = IterAdapter>
+ typename std::enable_if<std::is_same<iterator_category, std::random_access_iterator_tag>::value,
+ typename T::difference_type>::type inline
+ operator-(const IterAdapter& rhs) const {
+ return iter_ - rhs.iter_;
+ }
+
+ bool operator==(IterAdapter other) const { return iter_ == other.iter_; }
+ bool operator!=(IterAdapter other) const { return !(*this == other); }
+ const value_type operator*() const { return Converter::convert(*iter_); }
+
+ private:
+ TIter iter_;
+};
+
+/*!
+ * \brief iterator adapter that adapts TIter to return another type.
+ * \tparam Converter a struct that contains converting function
+ * \tparam TIter the content iterator type.
+ */
+template <typename Converter, typename TIter>
+class ReverseIterAdapter {
+ public:
+ using difference_type = typename std::iterator_traits<TIter>::difference_type;
+ using value_type = typename Converter::ResultType;
+ using pointer = typename Converter::ResultType*;
+ using reference = typename Converter::ResultType&; // NOLINT(*)
+ using iterator_category = typename std::iterator_traits<TIter>::iterator_category;
+
+ explicit ReverseIterAdapter(TIter iter) : iter_(iter) {}
+ ReverseIterAdapter& operator++() {
+ --iter_;
+ return *this;
+ }
+ ReverseIterAdapter& operator--() {
+ ++iter_;
+ return *this;
+ }
+ ReverseIterAdapter operator++(int) {
+ ReverseIterAdapter copy = *this;
+ --iter_;
+ return copy;
+ }
+ ReverseIterAdapter operator--(int) {
+ ReverseIterAdapter copy = *this;
+ ++iter_;
+ return copy;
+ }
+ ReverseIterAdapter operator+(difference_type offset) const {
+ return ReverseIterAdapter(iter_ - offset);
+ }
+
+ template <typename T = ReverseIterAdapter>
+ typename std::enable_if<std::is_same<iterator_category, std::random_access_iterator_tag>::value,
+ typename T::difference_type>::type inline
+ operator-(const ReverseIterAdapter& rhs) const {
+ return rhs.iter_ - iter_;
+ }
+
+ bool operator==(ReverseIterAdapter other) const { return iter_ == other.iter_; }
+ bool operator!=(ReverseIterAdapter other) const { return !(*this == other); }
+ const value_type operator*() const { return Converter::convert(*iter_); }
+
+ private:
+ TIter iter_;
+};
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_CONTAINER_BASE_H_
diff --git a/ffi/include/tvm/ffi/container/optional.h b/ffi/include/tvm/ffi/container/optional.h
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/ffi/include/tvm/ffi/memory.h b/ffi/include/tvm/ffi/memory.h
index 03cd542d91..d0e5995fa5 100644
--- a/ffi/include/tvm/ffi/memory.h
+++ b/ffi/include/tvm/ffi/memory.h
@@ -95,9 +95,10 @@ class ObjAllocatorBase {
"make_inplace_array can only be used to create Object");
ArrayType* ptr =
Handler::New(static_cast<Derived*>(this), num_elems, std::forward<Args>(args)...);
- ptr->type_index_ = ArrayType::RuntimeTypeIndex();
- ptr->deleter_ = Handler::Deleter();
- return ObjectPtr<ArrayType>(ptr);
+ TVMFFIObject* ffi_ptr = details::ObjectUnsafe::GetHeader(ptr);
+ ffi_ptr->type_index = ArrayType::RuntimeTypeIndex();
+ ffi_ptr->deleter = Handler::Deleter();
+ return details::ObjectUnsafe::ObjectPtrFromUnowned<ArrayType>(ptr);
}
};
@@ -181,7 +182,7 @@ class SimpleObjAllocator : public ObjAllocatorBase<SimpleObjAllocator> {
static FObjectDeleter Deleter() { return Deleter_; }
private:
- static void Deleter_(Object* objptr) {
+ static void Deleter_(void* objptr) {
// NOTE: this is important to cast back to ArrayType*
// because objptr and tptr may not be the same
// depending on how sub-class allocates the space.
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index b7a0d1a7c5..13de74fa07 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -319,7 +319,7 @@ class ObjectPtr {
* \param data The data pointer
*/
explicit ObjectPtr(Object* data) : data_(data) {
- if (data != nullptr) {
+ if (data_ != nullptr) {
data_->IncRef();
}
}
diff --git a/ffi/include/tvm/ffi/type_traits.h b/ffi/include/tvm/ffi/type_traits.h
index e1d453fdba..be46639642 100644
--- a/ffi/include/tvm/ffi/type_traits.h
+++ b/ffi/include/tvm/ffi/type_traits.h
@@ -34,6 +34,39 @@
namespace tvm {
namespace ffi {
+/*!
+ * \brief Get type key from type index
+ * \param type_index The input type index
+ * \return the type key
+ */
+inline std::string TypeIndex2TypeKey(int32_t type_index) {
+ switch (type_index) {
+ case TypeIndex::kTVMFFINone:
+ return "None";
+ case TypeIndex::kTVMFFIInt:
+ return "int";
+ case TypeIndex::kTVMFFIFloat:
+ return "float";
+ case TypeIndex::kTVMFFIOpaquePtr:
+ return "void*";
+ case TypeIndex::kTVMFFIDataType:
+ return "DataType";
+ case TypeIndex::kTVMFFIDevice:
+ return "Device";
+ case TypeIndex::kTVMFFIRawStr:
+ return "const char*";
+ default: {
+ TVM_FFI_ICHECK_GE(type_index, TypeIndex::kTVMFFIStaticObjectBegin)
+ << "Uknown type_index=" << type_index;
+#if TVM_FFI_ALLOW_DYN_TYPE
+ const TypeInfo* type_info = details::ObjectGetTypeInfo(type_index);
+ return type_info->type_key;
+#else
+ return "object.Object";
+#endif
+ }
+ }
+}
/*!
* \brief TypeTraits that specifies the conversion behavior from/to FFI Any.
*
@@ -59,17 +92,36 @@ struct TypeTraits {
template <typename T>
using TypeTraitsNoCR = TypeTraits<std::remove_const_t<std::remove_reference_t<T>>>;
-// None
-template <>
-struct TypeTraits<std::nullptr_t> {
+template <typename T>
+inline constexpr bool use_default_type_traits_v = true;
+
+struct TypeTraitsBase {
static constexpr bool enabled = true;
+ // get mismatched type when result mismatches the trait.
+ // this function is called after TryConvertFromAnyView fails
+ // to get more detailed type information in runtime
+ // especially when the error involves nested container type
+ static TVM_FFI_INLINE std::string GetMismatchTypeInfo(const TVMFFIAny* source) {
+ return TypeIndex2TypeKey(source->type_index);
+ }
+};
+
+// None
+template <>
+struct TypeTraits<std::nullptr_t> : public TypeTraitsBase {
static TVM_FFI_INLINE void ConvertToAnyView(const std::nullptr_t&, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFINone;
+ // invariant: the pointer field also equals nullptr
+ // this will simplify the recovery of nullable object from the any
+ result->v_int64 = 0;
}
static TVM_FFI_INLINE void MoveToAny(std::nullptr_t, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFINone;
+ // invariant: the pointer field also equals nullptr
+ // this will simplify the recovery of nullable object from the any
+ result->v_int64 = 0;
}
static TVM_FFI_INLINE std::optional<std::nullptr_t> TryConvertFromAnyView(const TVMFFIAny* src) {
@@ -79,14 +131,20 @@ struct TypeTraits<std::nullptr_t> {
return std::nullopt;
}
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ return src->type_index == TypeIndex::kTVMFFINone;
+ }
+
+ static TVM_FFI_INLINE std::nullptr_t ConvertFromAnyViewAfterCheck(const TVMFFIAny*) {
+ return nullptr;
+ }
+
static TVM_FFI_INLINE std::string TypeStr() { return "None"; }
};
// Integer POD values
template <typename Int>
-struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> {
- static constexpr bool enabled = true;
-
+struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> : public TypeTraitsBase {
static TVM_FFI_INLINE void ConvertToAnyView(const Int& src, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFIInt;
result->v_int64 = static_cast<int64_t>(src);
@@ -103,14 +161,21 @@ struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> {
return std::nullopt;
}
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ return src->type_index == TypeIndex::kTVMFFIInt;
+ }
+
+ static TVM_FFI_INLINE int ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ return static_cast<Int>(src->v_int64);
+ }
+
static TVM_FFI_INLINE std::string TypeStr() { return "int"; }
};
// Float POD values
template <typename Float>
-struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>> {
- static constexpr bool enabled = true;
-
+struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>>
+ : public TypeTraitsBase {
static TVM_FFI_INLINE void ConvertToAnyView(const Float& src, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFIFloat;
result->v_float64 = static_cast<double>(src);
@@ -129,14 +194,24 @@ struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>> {
return std::nullopt;
}
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ return src->type_index == TypeIndex::kTVMFFIFloat || src->type_index == TypeIndex::kTVMFFIInt;
+ }
+
+ static TVM_FFI_INLINE Float ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFIFloat) {
+ return static_cast<Float>(src->v_float64);
+ } else {
+ return static_cast<Float>(src->v_int64);
+ }
+ }
+
static TVM_FFI_INLINE std::string TypeStr() { return "float"; }
};
// void*
template <>
-struct TypeTraits<void*> {
- static constexpr bool enabled = true;
-
+struct TypeTraits<void*> : public TypeTraitsBase {
static TVM_FFI_INLINE void ConvertToAnyView(void* src, TVMFFIAny* result) {
result->type_index = TypeIndex::kTVMFFIOpaquePtr;
result->v_ptr = src;
@@ -150,19 +225,31 @@ struct TypeTraits<void*> {
if (src->type_index == TypeIndex::kTVMFFIOpaquePtr) {
return std::make_optional<void*>(src->v_ptr);
}
+ if (src->type_index == TypeIndex::kTVMFFINone) {
+ return std::make_optional<void*>(nullptr);
+ }
return std::nullopt;
}
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ return src->type_index == TypeIndex::kTVMFFIOpaquePtr ||
+ src->type_index == TypeIndex::kTVMFFINone;
+ }
+
+ static TVM_FFI_INLINE void* ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ return src->v_ptr;
+ }
+
static TVM_FFI_INLINE std::string TypeStr() { return "void*"; }
};
-// Traits for object
+// Traits for ObjectRef
template <typename TObjRef>
-struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef>>> {
+struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef> &&
+ use_default_type_traits_v<TObjRef>>>
+ : public TypeTraitsBase {
using ContainerType = typename TObjRef::ContainerType;
- static constexpr bool enabled = true;
-
static TVM_FFI_INLINE void ConvertToAnyView(const TObjRef& src, TVMFFIAny* result) {
TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetTVMFFIObjectPtrFromObjectRef(src);
result->type_index = obj_ptr->type_index;
@@ -175,19 +262,27 @@ struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef
result->v_obj = obj_ptr;
}
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ return (src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin &&
+ details::IsObjectInstance<ContainerType>(src->type_index)) ||
+ (src->type_index == kTVMFFINone && TObjRef::_type_is_nullable);
+ }
+
+ static TVM_FFI_INLINE TObjRef ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ if constexpr (TObjRef::_type_is_nullable) {
+ if (src->type_index == kTVMFFINone) return TObjRef(nullptr);
+ }
+ return TObjRef(details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(src->v_obj));
+ }
+
static TVM_FFI_INLINE std::optional<TObjRef> TryConvertFromAnyView(const TVMFFIAny* src) {
if (src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
-#if TVM_FFI_ALLOW_DYN_TYPE
if (details::IsObjectInstance<ContainerType>(src->type_index)) {
return TObjRef(details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(src->v_obj));
}
-#else
- TVM_FFI_THROW(RuntimeError)
- << "Converting to object requires `TVM_FFI_ALLOW_DYN_TYPE` to be on".
-#endif
- } else if (src->type_index == kTVMFFINone) {
- if (!TObjRef::_type_is_nullable) return std::nullopt;
- return TObjRef(ObjectPtr<Object>());
+ }
+ if constexpr (TObjRef::_type_is_nullable) {
+ if (src->type_index == kTVMFFINone) return TObjRef(nullptr);
}
return std::nullopt;
}
@@ -195,11 +290,9 @@ struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef
static TVM_FFI_INLINE std::string TypeStr() { return ContainerType::_type_key; }
};
-// Traits for object
+// Traits for ObjectPtr
template <typename T>
-struct TypeTraits<ObjectPtr<T>> {
- static constexpr bool enabled = true;
-
+struct TypeTraits<ObjectPtr<T>> : public TypeTraitsBase {
static TVM_FFI_INLINE void ConvertToAnyView(const ObjectPtr<T>& src, TVMFFIAny* result) {
TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetTVMFFIObjectPtrFromObjectPtr(src);
result->type_index = obj_ptr->type_index;
@@ -212,28 +305,27 @@ struct TypeTraits<ObjectPtr<T>> {
result->v_obj = obj_ptr;
}
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ return src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin &&
+ details::IsObjectInstance<T>(src->type_index);
+ }
+
+ static TVM_FFI_INLINE ObjectPtr<T> ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ return details::ObjectUnsafe::ObjectPtrFromUnowned<T>(src->v_obj);
+ }
+
static TVM_FFI_INLINE std::optional<ObjectPtr<T>> TryConvertFromAnyView(const TVMFFIAny* src) {
- if (src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
-#if TVM_FFI_ALLOW_DYN_TYPE
- if (details::IsObjectInstance<T>(src->type_index)) {
- return details::ObjectUnsafe::ObjectPtrFromUnowned<T>(src->v_obj);
- }
-#else
- TVM_FFI_THROW(RuntimeError)
- << "Converting to object requires `TVM_FFI_ALLOW_DYN_TYPE` to be on".
-#endif
- }
+ if (CheckAnyView(src)) return ConvertFromAnyViewAfterCheck(src);
return std::nullopt;
}
static TVM_FFI_INLINE std::string TypeStr() { return T::_type_key; }
};
-// Traits for object
+// Traits for weak pointer of object
template <typename TObject>
-struct TypeTraits<const TObject*, std::enable_if_t<std::is_base_of_v<Object, TObject>>> {
- static constexpr bool enabled = true;
-
+struct TypeTraits<const TObject*, std::enable_if_t<std::is_base_of_v<Object, TObject>>>
+ : public TypeTraitsBase {
static TVM_FFI_INLINE void ConvertToAnyView(const TObject* src, TVMFFIAny* result) {
TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetHeader(src);
result->type_index = obj_ptr->type_index;
@@ -244,59 +336,27 @@ struct TypeTraits<const TObject*, std::enable_if_t<std::is_base_of_v<Object, TOb
TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetHeader(src);
result->type_index = obj_ptr->type_index;
result->v_obj = obj_ptr;
+ // needs to increase ref because original weak ptr do not own the code
details::ObjectUnsafe::IncRefObjectInAny(result);
}
+ static TVM_FFI_INLINE bool CheckAnyView(const TVMFFIAny* src) {
+ return src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin &&
+ details::IsObjectInstance<TObject>(src->type_index);
+ }
+
+ static TVM_FFI_INLINE const TObject* ConvertFromAnyViewAfterCheck(const TVMFFIAny* src) {
+ return reinterpret_cast<const TObject*>(src->v_obj);
+ }
+
static TVM_FFI_INLINE std::optional<const TObject*> TryConvertFromAnyView(const TVMFFIAny* src) {
- if (src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
-#if TVM_FFI_ALLOW_DYN_TYPE
- if (details::IsObjectInstance<TObject>(src->type_index)) {
- return reinterpret_cast<const TObject*>(src->v_obj);
- }
-#else
- TVM_FFI_THROW(RuntimeError)
- << "Converting to object requires `TVM_FFI_ALLOW_DYN_TYPE` to be on".
-#endif
- }
+ if (CheckAnyView(src)) return ConvertFromAnyViewAfterCheck(src);
return std::nullopt;
}
static TVM_FFI_INLINE std::string TypeStr() { return TObject::_type_key; }
};
-/*!
- * \brief Get type key from type index
- * \param type_index The input type index
- * \return the type key
- */
-inline std::string TypeIndex2TypeKey(int32_t type_index) {
- switch (type_index) {
- case TypeIndex::kTVMFFINone:
- return "None";
- case TypeIndex::kTVMFFIInt:
- return "int";
- case TypeIndex::kTVMFFIFloat:
- return "float";
- case TypeIndex::kTVMFFIOpaquePtr:
- return "void*";
- case TypeIndex::kTVMFFIDataType:
- return "DataType";
- case TypeIndex::kTVMFFIDevice:
- return "Device";
- case TypeIndex::kTVMFFIRawStr:
- return "const char*";
- default: {
- TVM_FFI_ICHECK_GE(type_index, TypeIndex::kTVMFFIStaticObjectBegin)
- << "Uknown type_index=" << type_index;
-#if TVM_FFI_ALLOW_DYN_TYPE
- const TypeInfo* type_info = details::ObjectGetTypeInfo(type_index);
- return type_info->type_key;
-#else
- return "object.Object";
-#endif
- }
- }
-}
} // namespace ffi
} // namespace tvm
#endif // TVM_FFI_TYPE_TRAITS_H_
diff --git a/ffi/tests/example/test_array.cc b/ffi/tests/example/test_array.cc
new file mode 100644
index 0000000000..c5ba557425
--- /dev/null
+++ b/ffi/tests/example/test_array.cc
@@ -0,0 +1,231 @@
+/*
+ * 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.
+ */
+#include <gtest/gtest.h>
+#include <tvm/ffi/container/array.h>
+
+#include "./testing_object.h"
+
+namespace {
+
+using namespace tvm::ffi;
+using namespace tvm::ffi::testing;
+
+TEST(Array, Basic) {
+ Array<TInt> arr = {TInt(11), TInt(12)};
+ TInt v1 = arr[0];
+ EXPECT_EQ(v1->value, 11);
+ EXPECT_EQ(v1.use_count(), 2);
+ EXPECT_EQ(arr[1]->value, 12);
+}
+
+TEST(Array, COWSet) {
+ Array<TInt> arr = {TInt(11), TInt(12)};
+ Array<TInt> arr2 = arr;
+ EXPECT_EQ(arr.use_count(), 2);
+ arr.Set(1, TInt(13));
+ EXPECT_EQ(arr.use_count(), 1);
+ EXPECT_EQ(arr[1]->value, 13);
+ EXPECT_EQ(arr2[1]->value, 12);
+}
+
+TEST(Array, AnyConvertCheck) {
+ Array<Any> arr = {11.1, 1};
+ EXPECT_EQ(arr[1].operator int(), 1);
+
+ AnyView view0 = arr;
+ Array<double> arr1 = view0;
+ EXPECT_EQ(arr1[0], 11.1);
+ EXPECT_EQ(arr1[1], 1.0);
+
+ Any any1 = arr;
+
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] Array<int> arr2 = any1;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("Cannot convert from type `Array[index 0: float]` to `Array<int>`"),
+ std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+
+ Array<Array<TNumber>> arr_nested = {{}, {TInt(1), TFloat(2)}};
+ any1 = arr_nested;
+ Array<Array<TNumber>> arr1_nested = any1;
+ EXPECT_EQ(arr1_nested.use_count(), 3);
+
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] Array<Array<int>> arr2 = any1;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("`Array[index 1: Array[index 0: test.Int]]` to `Array<Array<int>>`"),
+ std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+}
+
+TEST(Array, MutateInPlaceForUniqueReference) {
+ TInt x(1);
+ Array<TInt> arr{x, x};
+ EXPECT_TRUE(arr.unique());
+ auto* before = arr.get();
+
+ arr.MutateByApply([](TInt) { return TInt(2); });
+ auto* after = arr.get();
+ EXPECT_EQ(before, after);
+}
+
+TEST(Array, CopyWhenMutatingNonUniqueReference) {
+ TInt x(1);
+ Array<TInt> arr{x, x};
+ Array<TInt> arr2 = arr;
+
+ EXPECT_TRUE(!arr.unique());
+ auto* before = arr.get();
+
+ arr.MutateByApply([](TInt) { return TInt(2); });
+ auto* after = arr.get();
+ EXPECT_NE(before, after);
+}
+
+TEST(Array, Map) {
+ // Basic functionality
+ TInt x(1), y(1);
+ Array<TInt> var_arr{x, y};
+ Array<TNumber> expr_arr = var_arr.Map([](TInt var) -> TNumber { return TFloat(var->value + 1); });
+
+ EXPECT_NE(var_arr.get(), expr_arr.get());
+ EXPECT_TRUE(expr_arr[0]->IsInstance<TFloatObj>());
+ EXPECT_TRUE(expr_arr[1]->IsInstance<TFloatObj>());
+}
+
+TEST(Array, Iterator) {
+ Array<int> array{1, 2, 3};
+ std::vector<int> vector(array.begin(), array.end());
+ EXPECT_EQ(vector[1], 2);
+}
+
+TEST(Array, PushPop) {
+ Array<int> a;
+ std::vector<int> b;
+ for (int i = 0; i < 10; ++i) {
+ a.push_back(i);
+ b.push_back(i);
+ ASSERT_EQ(a.front(), b.front());
+ ASSERT_EQ(a.back(), b.back());
+ ASSERT_EQ(a.size(), b.size());
+ int n = a.size();
+ for (int j = 0; j < n; ++j) {
+ ASSERT_EQ(a[j], b[j]);
+ }
+ }
+ for (int i = 9; i >= 0; --i) {
+ ASSERT_EQ(a.front(), b.front());
+ ASSERT_EQ(a.back(), b.back());
+ ASSERT_EQ(a.size(), b.size());
+ a.pop_back();
+ b.pop_back();
+ int n = a.size();
+ for (int j = 0; j < n; ++j) {
+ ASSERT_EQ(a[j], b[j]);
+ }
+ }
+ ASSERT_EQ(a.empty(), true);
+}
+
+TEST(Array, ResizeReserveClear) {
+ for (size_t n = 0; n < 10; ++n) {
+ Array<int> a;
+ Array<int> b;
+ a.resize(n);
+ b.reserve(n);
+ ASSERT_EQ(a.size(), n);
+ ASSERT_GE(a.capacity(), n);
+ a.clear();
+ b.clear();
+ ASSERT_EQ(a.size(), 0);
+ ASSERT_EQ(b.size(), 0);
+ }
+}
+
+TEST(Array, InsertErase) {
+ Array<int> a;
+ std::vector<int> b;
+ for (int n = 1; n <= 10; ++n) {
+ a.insert(a.end(), n);
+ b.insert(b.end(), n);
+ for (int pos = 0; pos <= n; ++pos) {
+ a.insert(a.begin() + pos, pos);
+ b.insert(b.begin() + pos, pos);
+ ASSERT_EQ(a.front(), b.front());
+ ASSERT_EQ(a.back(), b.back());
+ ASSERT_EQ(a.size(), n + 1);
+ ASSERT_EQ(b.size(), n + 1);
+ for (int k = 0; k <= n; ++k) {
+ ASSERT_EQ(a[k], b[k]);
+ }
+ a.erase(a.begin() + pos);
+ b.erase(b.begin() + pos);
+ }
+ ASSERT_EQ(a.front(), b.front());
+ ASSERT_EQ(a.back(), b.back());
+ ASSERT_EQ(a.size(), n);
+ }
+}
+
+TEST(Array, InsertEraseRange) {
+ Array<int> range_a{-1, -2, -3, -4};
+ std::vector<int> range_b{-1, -2, -3, -4};
+ Array<int> a;
+ std::vector<int> b;
+
+ static_assert(std::is_same_v<decltype(*range_a.begin()), int>);
+ for (size_t n = 1; n <= 10; ++n) {
+ a.insert(a.end(), n);
+ b.insert(b.end(), n);
+ for (size_t pos = 0; pos <= n; ++pos) {
+ a.insert(a.begin() + pos, range_a.begin(), range_a.end());
+ b.insert(b.begin() + pos, range_b.begin(), range_b.end());
+ ASSERT_EQ(a.front(), b.front());
+ ASSERT_EQ(a.back(), b.back());
+ ASSERT_EQ(a.size(), n + range_a.size());
+ ASSERT_EQ(b.size(), n + range_b.size());
+ size_t m = n + range_a.size();
+ for (size_t k = 0; k < m; ++k) {
+ ASSERT_EQ(a[k], b[k]);
+ }
+ a.erase(a.begin() + pos, a.begin() + pos + range_a.size());
+ b.erase(b.begin() + pos, b.begin() + pos + range_b.size());
+ }
+ ASSERT_EQ(a.front(), b.front());
+ ASSERT_EQ(a.back(), b.back());
+ ASSERT_EQ(a.size(), n);
+ }
+}
+
+} // namespace
(tvm) 07/38: [FFI] Any support
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 4d03e51f29d115903440c6bc74932aa47e59865e
Author: tqchen <ti...@gmail.com>
AuthorDate: Sat Aug 17 15:58:44 2024 -0400
[FFI] Any support
Co-authored-by: Junru Shao <ju...@apache.org>
---
ffi/include/tvm/ffi/any.h | 217 ++++++++++++++++++++++++++++++++++++
ffi/include/tvm/ffi/object.h | 119 ++++++++++++--------
ffi/include/tvm/ffi/type_traits.h | 179 +++++++++++++++++++++++++++++
ffi/tests/example/test_any.cc | 145 ++++++++++++++++++++++++
ffi/tests/example/test_c_ffi_abi.cc | 18 +++
ffi/tests/example/test_error.cc | 18 +++
ffi/tests/example/test_object.cc | 126 ++++++++-------------
7 files changed, 698 insertions(+), 124 deletions(-)
diff --git a/ffi/include/tvm/ffi/any.h b/ffi/include/tvm/ffi/any.h
new file mode 100644
index 0000000000..1568fe98b2
--- /dev/null
+++ b/ffi/include/tvm/ffi/any.h
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+/*!
+ * \file tvm/ffi/any.h
+ * \brief Any value support.
+ */
+#ifndef TVM_FFI_ANY_H_
+#define TVM_FFI_ANY_H_
+
+#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/type_traits.h>
+
+namespace tvm {
+namespace ffi {
+
+class Any;
+
+/*!
+ * \brief AnyView allows us to take un-managed reference view of any value.
+ */
+class AnyView {
+ protected:
+ /*! \brief The underlying backing data of the any object */
+ TVMFFIAny data_;
+ // Any can see AnyView
+ friend class Any;
+
+ public:
+ // NOTE: the following two functions uses styl style
+ // since they are common functions appearing in FFI.
+ /*!
+ * \brief Reset any view to None
+ */
+ void reset() { data_.type_index = TypeIndex::kTVMFFINone; }
+ /*!
+ * \brief Swap this array with another Object
+ * \param other The other Object
+ */
+ void swap(AnyView& other) { // NOLINT(*)
+ std::swap(data_, other.data_);
+ }
+ // default constructors
+ AnyView() { data_.type_index = TypeIndex::kTVMFFINone; }
+ ~AnyView() = default;
+ // constructors from any view
+ AnyView(const AnyView&) = default;
+ AnyView& operator=(const AnyView&) = default;
+ AnyView(AnyView&& other) : data_(other.data_) { other.data_.type_index = TypeIndex::kTVMFFINone; }
+ AnyView& operator=(AnyView&& other) {
+ // copy-and-swap idiom
+ AnyView(std::move(other)).swap(*this); // NOLINT(*)
+ return *this;
+ }
+ // constructor from general types
+ template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
+ AnyView(const T& other) { // NOLINT(*)
+ TypeTraits<T>::ConvertToAnyView(other, &data_);
+ }
+ template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
+ AnyView& operator=(const T& other) { // NOLINT(*)
+ // copy-and-swap idiom
+ AnyView(other).swap(*this); // NOLINT(*)
+ return *this;
+ }
+
+ template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
+ std::optional<T> TryAs() const {
+ return TypeTraits<T>::TryConvertFromAnyView(&data_);
+ }
+
+ template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
+ operator T() const {
+ std::optional<T> opt = TypeTraits<T>::TryConvertFromAnyView(&data_);
+ if (opt.has_value()) {
+ return std::move(opt.value());
+ }
+ TVM_FFI_THROW(TypeError) << "Cannot convert from type `" << TypeIndex2TypeKey(data_.type_index)
+ << "` to `" << TypeTraits<T>::TypeStr() << "`";
+ }
+ // The following functions are only used for testing purposes
+ /*!
+ * \return The underlying supporting data of any view
+ * \note This function is used only for testing purposes.
+ */
+ TVMFFIAny AsTVMFFIAny() const { return data_; }
+ /*!
+ * \return Create an AnyView from TVMFFIAny
+ * \param data the underlying ffi data.
+ */
+ static AnyView FromTVMFFIAny(TVMFFIAny data) {
+ AnyView view;
+ view.data_ = data;
+ return view;
+ }
+};
+
+// layout assert to ensure we can freely cast between the two types
+static_assert(sizeof(AnyView) == sizeof(TVMFFIAny));
+
+namespace details {
+/*!
+ * \brief Helper function to inplace convert any view to any.
+ * \param data The pointer that represents the format as any view.
+ * \param extra_any_bytes Indicate that the data may contain extra bytes following
+ * the TVMFFIAny data structure. This is reserved for future possible optimizations
+ * of small-string and extended any object.
+ */
+TVM_FFI_INLINE void InplaceConvertAnyViewToAny(TVMFFIAny* data,
+ [[maybe_unused]] size_t extra_any_bytes = 0) {
+ // TODO: string conversion.
+ if (data->type_index >= TVMFFITypeIndex::kTVMFFIStaticObjectBegin) {
+ details::ObjectInternal::IncRefObjectInAny(data);
+ }
+}
+} // namespace details
+
+/*!
+ * \brief
+ */
+class Any {
+ protected:
+ /*! \brief The underlying backing data of the any object */
+ TVMFFIAny data_;
+
+ public:
+ /*!
+ * \brief Reset any to None
+ */
+ void reset() {
+ if (data_.type_index >= TVMFFITypeIndex::kTVMFFIStaticObjectBegin) {
+ details::ObjectInternal::DecRefObjectInAny(&data_);
+ }
+ data_.type_index = TVMFFITypeIndex::kTVMFFINone;
+ }
+ /*!
+ * \brief Swap this array with another Object
+ * \param other The other Object
+ */
+ void swap(Any& other) { // NOLINT(*)
+ std::swap(data_, other.data_);
+ }
+
+ // default constructors
+ Any() { data_.type_index = TypeIndex::kTVMFFINone; }
+ ~Any() { this->reset(); }
+ // constructors from Any
+ Any(const Any& other) : data_(other.data_) {
+ if (data_.type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
+ details::ObjectInternal::IncRefObjectInAny(&data_);
+ }
+ }
+ Any(Any&& other) : data_(other.data_) { other.data_.type_index = TypeIndex::kTVMFFINone; }
+ Any& operator=(const Any& other) {
+ // copy-and-swap idiom
+ Any(other).swap(*this); // NOLINT(*)
+ return *this;
+ }
+ Any& operator=(Any&& other) {
+ // copy-and-swap idiom
+ Any(std::move(other)).swap(*this); // NOLINT(*)
+ return *this;
+ }
+ // convert from/to AnyView
+ Any(const AnyView& other) : data_(other.data_) { details::InplaceConvertAnyViewToAny(&data_); }
+ Any& operator=(const AnyView& other) {
+ // copy-and-swap idiom
+ Any(other).swap(*this); // NOLINT(*)
+ return *this;
+ }
+ /*! \brief Any can be converted to AnyView in zero cost. */
+ operator AnyView() { return AnyView::FromTVMFFIAny(data_); }
+ // constructor from general types
+ template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
+ Any(T other) { // NOLINT(*)
+ TypeTraits<T>::MoveToManagedAny(std::move(other), &data_);
+ }
+ template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
+ Any& operator=(T other) { // NOLINT(*)
+ // copy-and-swap idiom
+ Any(std::move(other)).swap(*this); // NOLINT(*)
+ return *this;
+ }
+ template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
+ std::optional<T> TryAs() const {
+ return TypeTraits<T>::TryConvertFromAnyView(&data_);
+ }
+
+ template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
+ operator T() const {
+ std::optional<T> opt = TypeTraits<T>::TryConvertFromAnyView(&data_);
+ if (opt.has_value()) {
+ return std::move(opt.value());
+ }
+ TVM_FFI_THROW(TypeError) << "Cannot convert from type `" << TypeIndex2TypeKey(data_.type_index)
+ << "` to `" << TypeTraits<T>::TypeStr() << "`";
+ }
+};
+
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_ANY_H_
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 5a6b552e1c..a1547b1900 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -71,6 +71,19 @@ TVM_FFI_DLL int32_t ObjectGetOrAllocTypeIndex(const char* type_key, int32_t stat
*/
TVM_FFI_DLL const TypeInfo* ObjectGetTypeInfo(int32_t type_index);
#endif // TVM_FFI_ALLOW_DYN_TYPE
+
+/*!
+ * Check if the type_index is an instance of TargetObjectType.
+ *
+ * \tparam TargetType The target object type to be checked.
+ *
+ * \param object_type_index The type index to be checked, caller
+ * ensures that the index is already within the object index range.
+ *
+ * \return Whether the target type is true.
+ */
+template <typename TargetType>
+TVM_FFI_INLINE bool IsObjectInstance(int32_t object_type_index);
} // namespace details
/*!
@@ -130,38 +143,7 @@ class Object {
*/
template <typename TargetType>
bool IsInstance() const {
- // Everything is a subclass of object.
- if constexpr (std::is_same<TargetType, Object>::value) return true;
-
- if constexpr (TargetType::_type_final) {
- // if the target type is a final type
- // then we only need to check the equivalence.
- return header_.type_index == TargetType::RuntimeTypeIndex();
- }
-
- // if target type is a non-leaf type
- // Check if type index falls into the range of reserved slots.
- int32_t target_type_index = TargetType::RuntimeTypeIndex();
- int32_t begin = target_type_index;
- // The condition will be optimized by constant-folding.
- if constexpr (TargetType::_type_child_slots != 0) {
- int32_t end = begin + TargetType::_type_child_slots;
- if (header_.type_index >= begin && header_.type_index < end) return true;
- } else {
- if (header_.type_index == begin) return true;
- }
- if (!TargetType::_type_child_slots_can_overflow) return false;
- // Invariance: parent index is always smaller than the child.
- if (header_.type_index < target_type_index) return false;
- // Do a runtime lookup of type information
-#if TVM_FFI_ALLOW_DYN_TYPE
- // the function checks that the info exists
- const TypeInfo* type_info = details::ObjectGetTypeInfo(header_.type_index);
- return (type_info->type_depth > TargetType::_type_depth &&
- type_info->type_acenstors[TargetType::_type_depth] == target_type_index);
-#else
- return false;
-#endif
+ return details::IsObjectInstance<TargetType>(header_.type_index);
}
// Information about the object
@@ -551,21 +533,42 @@ inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
namespace details {
-// auxiliary class to enable static type info table at depth
-template <int depth>
-struct TypeInfoAtDepth : public TypeInfo {
- /*! \brief extra type acenstors fields */
- int32_t _type_acenstors[depth];
-
- TypeInfoAtDepth(const char* type_key, int32_t static_type_index) {
- this->type_key = type_key;
- this->type_key_hash = 0;
- this->type_index = static_type_index;
- this->type_depth = depth;
- this->type_acenstors = _type_acenstors;
+template <typename TargetType>
+TVM_FFI_INLINE bool IsObjectInstance(int32_t object_type_index) {
+ static_assert(std::is_base_of_v<Object, TargetType>);
+ // Everything is a subclass of object.
+ if constexpr (std::is_same<TargetType, Object>::value) return true;
+
+ if constexpr (TargetType::_type_final) {
+ // if the target type is a final type
+ // then we only need to check the equivalence.
+ return object_type_index == TargetType::RuntimeTypeIndex();
}
-};
+ // if target type is a non-leaf type
+ // Check if type index falls into the range of reserved slots.
+ int32_t target_type_index = TargetType::RuntimeTypeIndex();
+ int32_t begin = target_type_index;
+ // The condition will be optimized by constant-folding.
+ if constexpr (TargetType::_type_child_slots != 0) {
+ int32_t end = begin + TargetType::_type_child_slots;
+ if (object_type_index >= begin && object_type_index < end) return true;
+ } else {
+ if (object_type_index == begin) return true;
+ }
+ if (!TargetType::_type_child_slots_can_overflow) return false;
+ // Invariance: parent index is always smaller than the child.
+ if (object_type_index < target_type_index) return false;
+ // Do a runtime lookup of type information
+#if TVM_FFI_ALLOW_DYN_TYPE
+ // the function checks that the info exists
+ const TypeInfo* type_info = details::ObjectGetTypeInfo(object_type_index);
+ return (type_info->type_depth > TargetType::_type_depth &&
+ type_info->type_acenstors[TargetType::_type_depth] == target_type_index);
+#else
+ return false;
+#endif
+}
/*!
* \brief Namespace to internally manipulate object class.
* \note These functions are only supposed to be used by internal
@@ -582,6 +585,31 @@ struct ObjectInternal {
static TVM_FFI_INLINE ObjectPtr<T> ObjectPtrFromUnowned(Object* raw_ptr) {
return tvm::ffi::ObjectPtr<T>(raw_ptr);
}
+
+ template <typename T>
+ static TVM_FFI_INLINE ObjectPtr<T> ObjectPtrFromUnowned(TVMFFIObject* obj_ptr) {
+ return tvm::ffi::ObjectPtr<T>(reinterpret_cast<Object*>(obj_ptr));
+ }
+
+ // Interactions with Any system
+ static TVM_FFI_INLINE void DecRefObjectInAny(TVMFFIAny* src) {
+ reinterpret_cast<Object*>(src->v_obj)->DecRef();
+ }
+
+ static TVM_FFI_INLINE void IncRefObjectInAny(TVMFFIAny* src) {
+ reinterpret_cast<Object*>(src->v_obj)->IncRef();
+ }
+
+ static TVM_FFI_INLINE TVMFFIObject* GetTVMFFIObjectPtrFromObjectRef(const ObjectRef& src) {
+ return GetHeader(src.data_.data_);
+ }
+
+ static TVM_FFI_INLINE TVMFFIObject* MoveTVMFFIObjectPtrFromObjectRef(ObjectRef* src) {
+ Object* obj_ptr = src->data_.data_;
+ src->data_.data_ = nullptr;
+ return GetHeader(obj_ptr);
+ }
+
// Create objectptr by moving from an existing address of object and setting its
// address to nullptr
template <typename T>
@@ -592,7 +620,6 @@ struct ObjectInternal {
return ptr;
}
};
-
} // namespace details
} // namespace ffi
} // namespace tvm
diff --git a/ffi/include/tvm/ffi/type_traits.h b/ffi/include/tvm/ffi/type_traits.h
new file mode 100644
index 0000000000..0a1d7d9941
--- /dev/null
+++ b/ffi/include/tvm/ffi/type_traits.h
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+/*!
+ * \file tvm/ffi/object.h
+ * \brief A managed object in the TVM FFI.
+ */
+#ifndef TVM_FFI_TYPE_TRAITS_H_
+#define TVM_FFI_TYPE_TRAITS_H_
+
+#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/error.h>
+#include <tvm/ffi/internal_utils.h>
+#include <tvm/ffi/object.h>
+
+#include <optional>
+#include <type_traits>
+
+namespace tvm {
+namespace ffi {
+
+/*!
+ * \brief TypeTraits that specifies the conversion behavior from/to FFI Any.
+ *
+ * We need to implement the following conversion functions
+ *
+ * - void ConvertToAnyView(const T& src, TVMFFIAny* result);
+ *
+ * Convert a value to AnyView
+ *
+ * - std::optional<T> TryConvertFromAnyView(const TVMFFIAny* src);
+ *
+ * Try convert AnyView to a value type.
+ */
+template <typename, typename = void>
+struct TypeTraits {
+ static constexpr bool enabled = false;
+};
+
+// Integer POD values
+template <typename Int>
+struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> {
+ static constexpr bool enabled = true;
+
+ static TVM_FFI_INLINE void ConvertToAnyView(const Int& src, TVMFFIAny* result) {
+ result->type_index = TypeIndex::kTVMFFIInt;
+ result->v_int64 = static_cast<int64_t>(src);
+ }
+
+ static TVM_FFI_INLINE void MoveToManagedAny(Int src, TVMFFIAny* result) {
+ ConvertToAnyView(src, result);
+ }
+
+ static TVM_FFI_INLINE std::optional<Int> TryConvertFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFIInt) {
+ return std::make_optional<Int>(src->v_int64);
+ }
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return "int"; }
+};
+
+// Float POD values
+template <typename Float>
+struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>> {
+ static constexpr bool enabled = true;
+
+ static TVM_FFI_INLINE void ConvertToAnyView(const Float& src, TVMFFIAny* result) {
+ result->type_index = TypeIndex::kTVMFFIFloat;
+ result->v_float64 = static_cast<double>(src);
+ }
+
+ static TVM_FFI_INLINE void MoveToManagedAny(Float src, TVMFFIAny* result) {
+ ConvertToAnyView(src, result);
+ }
+
+ static TVM_FFI_INLINE std::optional<Float> TryConvertFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFIFloat) {
+ return std::make_optional<Float>(src->v_float64);
+ } else if (src->type_index == TypeIndex::kTVMFFIInt) {
+ return std::make_optional<Float>(src->v_int64);
+ }
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return "float"; }
+};
+
+// Traits for object
+template <typename TObjRef>
+struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef>>> {
+ using ContainerType = typename TObjRef::ContainerType;
+
+ static constexpr bool enabled = true;
+
+ static TVM_FFI_INLINE void ConvertToAnyView(const TObjRef& src, TVMFFIAny* result) {
+ TVMFFIObject* obj_ptr = details::ObjectInternal::GetTVMFFIObjectPtrFromObjectRef(src);
+ result->type_index = obj_ptr->type_index;
+ result->v_obj = obj_ptr;
+ }
+
+ static TVM_FFI_INLINE void MoveToManagedAny(TObjRef src, TVMFFIAny* result) {
+ TVMFFIObject* obj_ptr = details::ObjectInternal::MoveTVMFFIObjectPtrFromObjectRef(&src);
+ result->type_index = obj_ptr->type_index;
+ result->v_obj = obj_ptr;
+ }
+
+ static TVM_FFI_INLINE std::optional<TObjRef> TryConvertFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
+#if TVM_FFI_ALLOW_DYN_TYPE
+ if (details::IsObjectInstance<ContainerType>(src->type_index)) {
+ return TObjRef(details::ObjectInternal::ObjectPtrFromUnowned<Object>(src->v_obj));
+ }
+#else
+ TVM_FFI_THROW(RuntimeError)
+ << "Converting to object requires `TVM_FFI_ALLOW_DYN_TYPE` to be on".
+#endif
+ } else if (src->type_index == kTVMFFINone) {
+ if (!TObjRef::_type_is_nullable) return std::nullopt;
+ return TObjRef(ObjectPtr<Object>());
+ }
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return ContainerType::_type_key; }
+};
+
+/*!
+ * \brief Get type key from type index
+ * \param type_index The input type index
+ * \return the type key
+ */
+inline std::string TypeIndex2TypeKey(int32_t type_index) {
+ switch (type_index) {
+ case TypeIndex::kTVMFFINone:
+ return "None";
+ case TypeIndex::kTVMFFIInt:
+ return "int";
+ case TypeIndex::kTVMFFIFloat:
+ return "double";
+ case TypeIndex::kTVMFFIOpaquePtr:
+ return "void*";
+ case TypeIndex::kTVMFFIDataType:
+ return "DataType";
+ case TypeIndex::kTVMFFIDevice:
+ return "Device";
+ case TypeIndex::kTVMFFIRawStr:
+ return "const char*";
+ default: {
+ TVM_FFI_ICHECK_GE(type_index, TypeIndex::kTVMFFIStaticObjectBegin)
+ << "Uknown type_index=" << type_index;
+#if TVM_FFI_ALLOW_DYN_TYPE
+ const TypeInfo* type_info = details::ObjectGetTypeInfo(type_index);
+ return type_info->type_key;
+#else
+ return "object.Object";
+#endif
+ }
+ }
+}
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_TYPE_TRAITS_H_
diff --git a/ffi/tests/example/test_any.cc b/ffi/tests/example/test_any.cc
new file mode 100644
index 0000000000..55ef21405e
--- /dev/null
+++ b/ffi/tests/example/test_any.cc
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+#include <gtest/gtest.h>
+#include <tvm/ffi/any.h>
+#include <tvm/ffi/memory.h>
+
+#include "./testing_object.h"
+
+namespace {
+
+using namespace tvm::ffi;
+using namespace tvm::ffi::testing;
+
+TEST(Any, Int) {
+ AnyView view0;
+ EXPECT_EQ(view0.AsTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
+
+ std::optional<int64_t> opt_v0 = view0.TryAs<int64_t>();
+ EXPECT_TRUE(!opt_v0.has_value());
+
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] int64_t v0 = view0;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("Cannot convert from type `None` to `int`"), std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+
+ AnyView view1 = 1;
+ EXPECT_EQ(view1.AsTVMFFIAny().type_index, TypeIndex::kTVMFFIInt);
+ EXPECT_EQ(view1.AsTVMFFIAny().v_int64, 1);
+
+ int32_t int_v1 = view1;
+ EXPECT_EQ(int_v1, 1);
+
+ int64_t v1 = 2;
+ view0 = v1;
+ EXPECT_EQ(view0.AsTVMFFIAny().type_index, TypeIndex::kTVMFFIInt);
+ EXPECT_EQ(view0.AsTVMFFIAny().v_int64, 2);
+}
+
+TEST(Any, Float) {
+ AnyView view0;
+ EXPECT_EQ(view0.AsTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
+
+ std::optional<double> opt_v0 = view0.TryAs<double>();
+ EXPECT_TRUE(!opt_v0.has_value());
+
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] double v0 = view0;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("Cannot convert from type `None` to `float`"), std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+
+ AnyView view1_int = 1;
+ float float_v1 = view1_int;
+ EXPECT_EQ(float_v1, 1);
+
+ AnyView view2 = 2.2;
+ EXPECT_EQ(view2.AsTVMFFIAny().type_index, TypeIndex::kTVMFFIFloat);
+ EXPECT_EQ(view2.AsTVMFFIAny().v_float64, 2.2);
+
+ float v1 = 2;
+ view0 = v1;
+ EXPECT_EQ(view0.AsTVMFFIAny().type_index, TypeIndex::kTVMFFIFloat);
+ EXPECT_EQ(view0.AsTVMFFIAny().v_float64, 2);
+}
+
+TEST(Any, Object) {
+ AnyView view0;
+ EXPECT_EQ(view0.AsTVMFFIAny().type_index, TypeIndex::kTVMFFINone);
+
+ // int object is not nullable
+ std::optional<TInt> opt_v0 = view0.TryAs<TInt>();
+ EXPECT_TRUE(!opt_v0.has_value());
+
+ TInt v1(11);
+ EXPECT_EQ(v1.use_count(), 1);
+ // view won't increase refcount
+ AnyView view1 = v1;
+ EXPECT_EQ(v1.use_count(), 1);
+ // any will trigger ref count increase
+ Any any1 = v1;
+ EXPECT_EQ(v1.use_count(), 2);
+ // copy to another view
+ AnyView view2 = any1;
+ EXPECT_EQ(v1.use_count(), 2);
+
+ // convert that triggers error
+ EXPECT_THROW(
+ {
+ try {
+ [[maybe_unused]] TFloat v0 = view1;
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "TypeError");
+ std::string what = error.what();
+ std::cout << what;
+ EXPECT_NE(what.find("Cannot convert from type `test.Int` to `test.Float`"),
+ std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+ // Try to convert to number
+ TNumber number0 = any1;
+ EXPECT_EQ(v1.use_count(), 3);
+ EXPECT_TRUE(number0.as<TIntObj>());
+ EXPECT_EQ(number0.as<TIntObj>()->value, 11);
+ EXPECT_TRUE(!any1.TryAs<int>().has_value());
+
+ TInt int1 = view2;
+ EXPECT_EQ(v1.use_count(), 4);
+ any1.reset();
+ EXPECT_EQ(v1.use_count(), 3);
+}
+
+} // namespace
diff --git a/ffi/tests/example/test_c_ffi_abi.cc b/ffi/tests/example/test_c_ffi_abi.cc
index d936247653..9cbc7e67e1 100644
--- a/ffi/tests/example/test_c_ffi_abi.cc
+++ b/ffi/tests/example/test_c_ffi_abi.cc
@@ -1,3 +1,21 @@
+/*
+ * 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.
+ */
#include <gtest/gtest.h>
#include <tvm/ffi/c_api.h>
diff --git a/ffi/tests/example/test_error.cc b/ffi/tests/example/test_error.cc
index 015551c953..bf43f97ad5 100644
--- a/ffi/tests/example/test_error.cc
+++ b/ffi/tests/example/test_error.cc
@@ -1,3 +1,21 @@
+/*
+ * 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.
+ */
#include <gtest/gtest.h>
#include <tvm/ffi/error.h>
diff --git a/ffi/tests/example/test_object.cc b/ffi/tests/example/test_object.cc
index 4395fccf52..f931c99419 100644
--- a/ffi/tests/example/test_object.cc
+++ b/ffi/tests/example/test_object.cc
@@ -1,65 +1,35 @@
+/*
+ * 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.
+ */
#include <gtest/gtest.h>
#include <tvm/ffi/memory.h>
#include <tvm/ffi/object.h>
+#include "./testing_object.h"
+
namespace {
using namespace tvm::ffi;
-
-class NumberObj : public Object {
- public:
- // declare as one slot, with float as overflow
- static constexpr uint32_t _type_child_slots = 1;
- static constexpr const char* _type_key = "test.Number";
- TVM_FFI_DECLARE_BASE_OBJECT_INFO(NumberObj, Object);
-};
-
-class Number : public ObjectRef {
- public:
- TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS(Number, ObjectRef, NumberObj);
-};
-
-class IntObj : public NumberObj {
- public:
- int64_t value;
-
- IntObj(int64_t value) : value(value) {}
-
- static constexpr const char* _type_key = "test.Int";
- TVM_FFI_DECLARE_FINAL_OBJECT_INFO(IntObj, NumberObj);
-};
-
-class Int : public Number {
- public:
- explicit Int(int64_t value) {
- data_ = make_object<IntObj>(value);
- }
-
- TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS(Int, Number, IntObj);
-};
-
-class FloatObj : public NumberObj {
- public:
- double value;
-
- FloatObj(double value) : value(value) {}
-
- static constexpr const char* _type_key = "test.Float";
- TVM_FFI_DECLARE_FINAL_OBJECT_INFO(FloatObj, NumberObj);
-};
-
-class Float : public Number {
- public:
- explicit Float(double value) {
- data_ = make_object<FloatObj>(value);
- }
-
- TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS(Float, Number, FloatObj);
-};
+using namespace tvm::ffi::testing;
TEST(Object, RefCounter) {
- ObjectPtr<IntObj> a = make_object<IntObj>(11);
- ObjectPtr<IntObj> b = a;
+ ObjectPtr<TIntObj> a = make_object<TIntObj>(11);
+ ObjectPtr<TIntObj> b = a;
EXPECT_EQ(a->value, 11);
@@ -69,7 +39,7 @@ TEST(Object, RefCounter) {
EXPECT_TRUE(b == nullptr);
EXPECT_EQ(b.use_count(), 0);
- ObjectPtr<IntObj> c = std::move(a);
+ ObjectPtr<TIntObj> c = std::move(a);
EXPECT_EQ(c.use_count(), 1);
EXPECT_TRUE(a == nullptr);
@@ -77,50 +47,50 @@ TEST(Object, RefCounter) {
}
TEST(Object, TypeInfo) {
- const TypeInfo* info = tvm::ffi::details::ObjectGetTypeInfo(IntObj::RuntimeTypeIndex());
+ const TypeInfo* info = tvm::ffi::details::ObjectGetTypeInfo(TIntObj::RuntimeTypeIndex());
EXPECT_TRUE(info != nullptr);
- EXPECT_EQ(info->type_index, IntObj::RuntimeTypeIndex());
+ EXPECT_EQ(info->type_index, TIntObj::RuntimeTypeIndex());
EXPECT_EQ(info->type_depth, 2);
EXPECT_EQ(info->type_acenstors[0], Object::_type_index);
- EXPECT_EQ(info->type_acenstors[1], NumberObj::_type_index);
+ EXPECT_EQ(info->type_acenstors[1], TNumberObj::_type_index);
EXPECT_GE(info->type_index, TypeIndex::kTVMFFIDynObjectBegin);
}
TEST(Object, InstanceCheck) {
- ObjectPtr<Object> a = make_object<IntObj>(11);
- ObjectPtr<Object> b = make_object<FloatObj>(11);
+ ObjectPtr<Object> a = make_object<TIntObj>(11);
+ ObjectPtr<Object> b = make_object<TFloatObj>(11);
EXPECT_TRUE(a->IsInstance<Object>());
- EXPECT_TRUE(a->IsInstance<NumberObj>());
- EXPECT_TRUE(a->IsInstance<IntObj>());
- EXPECT_TRUE(!a->IsInstance<FloatObj>());
+ EXPECT_TRUE(a->IsInstance<TNumberObj>());
+ EXPECT_TRUE(a->IsInstance<TIntObj>());
+ EXPECT_TRUE(!a->IsInstance<TFloatObj>());
EXPECT_TRUE(a->IsInstance<Object>());
- EXPECT_TRUE(b->IsInstance<NumberObj>());
- EXPECT_TRUE(!b->IsInstance<IntObj>());
- EXPECT_TRUE(b->IsInstance<FloatObj>());
+ EXPECT_TRUE(b->IsInstance<TNumberObj>());
+ EXPECT_TRUE(!b->IsInstance<TIntObj>());
+ EXPECT_TRUE(b->IsInstance<TFloatObj>());
}
TEST(ObjectRef, as) {
- ObjectRef a = Int(10);
- ObjectRef b = Float(20);
+ ObjectRef a = TInt(10);
+ ObjectRef b = TFloat(20);
// nullable object
ObjectRef c(nullptr);
- EXPECT_TRUE(a.as<IntObj>() != nullptr);
- EXPECT_TRUE(a.as<FloatObj>() == nullptr);
- EXPECT_TRUE(a.as<NumberObj>() != nullptr);
+ EXPECT_TRUE(a.as<TIntObj>() != nullptr);
+ EXPECT_TRUE(a.as<TFloatObj>() == nullptr);
+ EXPECT_TRUE(a.as<TNumberObj>() != nullptr);
- EXPECT_TRUE(b.as<IntObj>() == nullptr);
- EXPECT_TRUE(b.as<FloatObj>() != nullptr);
- EXPECT_TRUE(b.as<NumberObj>() != nullptr);
+ EXPECT_TRUE(b.as<TIntObj>() == nullptr);
+ EXPECT_TRUE(b.as<TFloatObj>() != nullptr);
+ EXPECT_TRUE(b.as<TNumberObj>() != nullptr);
- EXPECT_TRUE(c.as<IntObj>() == nullptr);
- EXPECT_TRUE(c.as<FloatObj>() == nullptr);
- EXPECT_TRUE(c.as<NumberObj>() == nullptr);
+ EXPECT_TRUE(c.as<TIntObj>() == nullptr);
+ EXPECT_TRUE(c.as<TFloatObj>() == nullptr);
+ EXPECT_TRUE(c.as<TNumberObj>() == nullptr);
- EXPECT_EQ(a.as<IntObj>()->value, 10);
- EXPECT_EQ(b.as<FloatObj>()->value, 20);
+ EXPECT_EQ(a.as<TIntObj>()->value, 10);
+ EXPECT_EQ(b.as<TFloatObj>()->value, 20);
}
} // namespace
(tvm) 03/38: [FFI] Enable ObjectPtr and tests
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 1ab674c646df35d1a8aca46ec390bf7ef4a96c74
Author: tqchen <ti...@gmail.com>
AuthorDate: Tue Aug 6 16:06:58 2024 -0400
[FFI] Enable ObjectPtr and tests
This commit enables object ptr and tests.
Move most of the friend class to internal.
---
ffi/include/tvm/ffi/c_ffi_abi.h | 7 +-
ffi/include/tvm/ffi/memory.h | 213 +++++++++++++++++++++++++++++++++++++++
ffi/include/tvm/ffi/object.h | 98 ++++++++++++++++--
ffi/tests/example/test_object.cc | 27 ++++-
4 files changed, 335 insertions(+), 10 deletions(-)
diff --git a/ffi/include/tvm/ffi/c_ffi_abi.h b/ffi/include/tvm/ffi/c_ffi_abi.h
index ccba3f58df..f1a1ae78da 100644
--- a/ffi/include/tvm/ffi/c_ffi_abi.h
+++ b/ffi/include/tvm/ffi/c_ffi_abi.h
@@ -51,7 +51,7 @@ extern "C" {
#endif
#ifdef __cplusplus
-enum class TVMFFITypeIndex : int32_t {
+enum TVMFFITypeIndex : int32_t {
#else
typedef enum {
#endif
@@ -77,7 +77,10 @@ typedef enum {
kTVMFFIFunc = 68,
kTVMFFIStr = 69,
// [Section] Dynamic Boxed: [kTVMFFIDynObjectBegin, +oo)
- kTVMFFIDynObjectBegin = 128,
+ // kTVMFFIDynObject is used to indicate that the type index
+ // is dynamic and needs to be looked up at runtime
+ kTVMFFIDynObject = 128,
+ kTVMFFIDynObjectBegin = 129
#ifdef __cplusplus
};
#else
diff --git a/ffi/include/tvm/ffi/memory.h b/ffi/include/tvm/ffi/memory.h
index e69de29bb2..cb7067502e 100644
--- a/ffi/include/tvm/ffi/memory.h
+++ b/ffi/include/tvm/ffi/memory.h
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+/*!
+ * \file tvm/ffi/memory.h
+ * \brief Runtime memory management to allocate on heap object.
+ */
+#ifndef TVM_FFI_MEMORY_H_
+#define TVM_FFI_MEMORY_H_
+
+#include <tvm/ffi/object.h>
+
+#include <cstdlib>
+#include <type_traits>
+#include <utility>
+
+namespace tvm {
+namespace ffi {
+
+/*! \brief Deleter function for obeject */
+typedef void (*FObjectDeleter)(TVMFFIObject* obj);
+
+/*!
+ * \brief Allocate an object using default allocator.
+ * \param args arguments to the constructor.
+ * \tparam T the node type.
+ * \return The ObjectPtr to the allocated object.
+ */
+template <typename T, typename... Args>
+inline ObjectPtr<T> make_object(Args&&... args);
+
+// Detail implementations after this
+//
+// The current design allows swapping the
+// allocator pattern when necessary.
+//
+// Possible future allocator optimizations:
+// - Arena allocator that gives ownership of memory to arena (deleter = nullptr)
+// - Thread-local object pools: one pool per size and alignment requirement.
+// - Can specialize by type of object to give the specific allocator to each object.
+
+/*!
+ * \brief Base class of object allocators that implements make.
+ * Use curiously recurring template pattern.
+ *
+ * \tparam Derived The derived class.
+ */
+template <typename Derived>
+class ObjAllocatorBase {
+ public:
+ /*!
+ * \brief Make a new object using the allocator.
+ * \tparam T The type to be allocated.
+ * \tparam Args The constructor signature.
+ * \param args The arguments.
+ */
+ template <typename T, typename... Args>
+ inline ObjectPtr<T> make_object(Args&&... args) {
+ using Handler = typename Derived::template Handler<T>;
+ static_assert(std::is_base_of<Object, T>::value, "make can only be used to create Object");
+ T* ptr = Handler::New(static_cast<Derived*>(this), std::forward<Args>(args)...);
+ TVMFFIObject* ffi_ptr = details::ObjectInternal::StaticCast<TVMFFIObject*>(ptr);
+ // NOTE: ref_counter is initialized in object constructor
+ ffi_ptr->type_index = T::RuntimeTypeIndex();
+ ffi_ptr->deleter = Handler::Deleter();
+ return details::ObjectInternal::ObjectPtr<T>(ptr);
+ }
+
+ /*!
+ * \tparam ArrayType The type to be allocated.
+ * \tparam ElemType The type of array element.
+ * \tparam Args The constructor signature.
+ * \param num_elems The number of array elements.
+ * \param args The arguments.
+ */
+ template <typename ArrayType, typename ElemType, typename... Args>
+ inline ObjectPtr<ArrayType> make_inplace_array(size_t num_elems, Args&&... args) {
+ using Handler = typename Derived::template ArrayHandler<ArrayType, ElemType>;
+ static_assert(std::is_base_of<Object, ArrayType>::value,
+ "make_inplace_array can only be used to create Object");
+ ArrayType* ptr =
+ Handler::New(static_cast<Derived*>(this), num_elems, std::forward<Args>(args)...);
+ ptr->type_index_ = ArrayType::RuntimeTypeIndex();
+ ptr->deleter_ = Handler::Deleter();
+ return ObjectPtr<ArrayType>(ptr);
+ }
+};
+
+// Simple allocator that uses new/delete.
+class SimpleObjAllocator : public ObjAllocatorBase<SimpleObjAllocator> {
+ public:
+ template <typename T>
+ class Handler {
+ public:
+ using StorageType = typename std::aligned_storage<sizeof(T), alignof(T)>::type;
+
+ template <typename... Args>
+ static T* New(SimpleObjAllocator*, Args&&... args) {
+ // NOTE: the first argument is not needed for SimpleObjAllocator
+ // It is reserved for special allocators that needs to recycle
+ // the object to itself (e.g. in the case of object pool).
+ //
+ // In the case of an object pool, an allocator needs to create
+ // a special chunk memory that hides reference to the allocator
+ // and call allocator's release function in the deleter.
+
+ // NOTE2: Use inplace new to allocate
+ // This is used to get rid of warning when deleting a virtual
+ // class with non-virtual destructor.
+ // We are fine here as we captured the right deleter during construction.
+ // This is also the right way to get storage type for an object pool.
+ StorageType* data = new StorageType();
+ new (data) T(std::forward<Args>(args)...);
+ return reinterpret_cast<T*>(data);
+ }
+
+ static FObjectDeleter Deleter() { return Deleter_; }
+
+ private:
+ static void Deleter_(TVMFFIObject* objptr) {
+ // NOTE: this is important to cast back to T*
+ // because objptr and tptr may not be the same
+ // depending on how sub-class allocates the space.
+ T* tptr = details::ObjectInternal::StaticCast<T*>(objptr);
+ // It is important to do tptr->T::~T(),
+ // so that we explicitly call the specific destructor
+ // instead of tptr->~T(), which could mean the intention
+ // call a virtual destructor(which may not be available and is not required).
+ tptr->T::~T();
+ delete reinterpret_cast<StorageType*>(tptr);
+ }
+ };
+
+ // Array handler that uses new/delete.
+ template <typename ArrayType, typename ElemType>
+ class ArrayHandler {
+ public:
+ using StorageType = typename std::aligned_storage<sizeof(ArrayType), alignof(ArrayType)>::type;
+ // for now only support elements that aligns with array header.
+ static_assert(alignof(ArrayType) % alignof(ElemType) == 0 &&
+ sizeof(ArrayType) % alignof(ElemType) == 0,
+ "element alignment constraint");
+
+ template <typename... Args>
+ static ArrayType* New(SimpleObjAllocator*, size_t num_elems, Args&&... args) {
+ // NOTE: the first argument is not needed for ArrayObjAllocator
+ // It is reserved for special allocators that needs to recycle
+ // the object to itself (e.g. in the case of object pool).
+ //
+ // In the case of an object pool, an allocator needs to create
+ // a special chunk memory that hides reference to the allocator
+ // and call allocator's release function in the deleter.
+ // NOTE2: Use inplace new to allocate
+ // This is used to get rid of warning when deleting a virtual
+ // class with non-virtual destructor.
+ // We are fine here as we captured the right deleter during construction.
+ // This is also the right way to get storage type for an object pool.
+ size_t unit = sizeof(StorageType);
+ size_t requested_size = num_elems * sizeof(ElemType) + sizeof(ArrayType);
+ size_t num_storage_slots = (requested_size + unit - 1) / unit;
+ StorageType* data = new StorageType[num_storage_slots];
+ new (data) ArrayType(std::forward<Args>(args)...);
+ return reinterpret_cast<ArrayType*>(data);
+ }
+
+ static FObjectDeleter Deleter() { return Deleter_; }
+
+ private:
+ static void Deleter_(Object* objptr) {
+ // NOTE: this is important to cast back to ArrayType*
+ // because objptr and tptr may not be the same
+ // depending on how sub-class allocates the space.
+ ArrayType* tptr = static_cast<ArrayType*>(objptr);
+ // It is important to do tptr->ArrayType::~ArrayType(),
+ // so that we explicitly call the specific destructor
+ // instead of tptr->~ArrayType(), which could mean the intention
+ // call a virtual destructor(which may not be available and is not required).
+ tptr->ArrayType::~ArrayType();
+ StorageType* p = reinterpret_cast<StorageType*>(tptr);
+ delete[] p;
+ }
+ };
+};
+
+template <typename T, typename... Args>
+inline ObjectPtr<T> make_object(Args&&... args) {
+ return SimpleObjAllocator().make_object<T>(std::forward<Args>(args)...);
+}
+
+template <typename ArrayType, typename ElemType, typename... Args>
+inline ObjectPtr<ArrayType> make_inplace_array_object(size_t num_elems, Args&&... args) {
+ return SimpleObjAllocator().make_inplace_array<ArrayType, ElemType>(num_elems,
+ std::forward<Args>(args)...);
+}
+
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_MEMORY_H_
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 90e78defae..6b973f943c 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -29,22 +29,89 @@
namespace tvm {
namespace ffi {
+using TypeIndex = TVMFFITypeIndex;
+
namespace details {
// forward declare object internal
struct ObjectInternal;
} // namespace details
-class Object : protected TVMFFIObject {
+/*!
+ * \brief base class of all object containers.
+ *
+ * Sub-class of objects should declare the following static constexpr fields:
+ *
+ * - _type_index:
+ * Static type index of the object, if assigned to TypeIndex::kTVMFFIDynObject
+ * the type index will be assigned during runtime.
+ * Runtime type index can be accessed by ObjectType::TypeIndex();
+ * - _type_key:
+ * The unique string identifier of the type.
+ * - _type_final:
+ * Whether the type is terminal type(there is no subclass of the type in the object system).
+ * This field is automatically set by macro TVM_DECLARE_FINAL_OBJECT_INFO
+ * It is still OK to sub-class a terminal object type T and construct it using make_object.
+ * But IsInstance check will only show that the object type is T(instead of the sub-class).
+ *
+ * The following two fields are necessary for base classes that can be sub-classed.
+ *
+ * - _type_child_slots:
+ * Number of reserved type index slots for child classes.
+ * Used for runtime optimization for type checking in IsInstance.
+ * If an object's type_index is within range of [type_index, type_index + _type_child_slots]
+ * Then the object can be quickly decided as sub-class of the current object class.
+ * If not, a fallback mechanism is used to check the global type table.
+ * Recommendation: set to estimate number of children needed.
+ *
+ * - _type_child_slots_can_overflow:
+ * Whether we can add additional child classes even if the number of child classes
+ * exceeds the _type_child_slots. A fallback mechanism to check global type table will be
+ * used. Recommendation: set to false for optimal runtime speed if we know exact number of children.
+ *
+ * Two macros are used to declare helper functions in the object:
+ * - Use TVM_FFI_DECLARE_BASE_OBJECT_INFO for object classes that can be sub-classed.
+ * - Use TVM_FFI_DECLARE_FINAL_OBJECT_INFO for object classes that cannot be sub-classed.
+ *
+ * New objects can be created using make_object function.
+ * Which will automatically populate the type_index and deleter of the object.
+ */
+class Object : private TVMFFIObject {
public:
Object() {
TVMFFIObject::ref_counter = 0;
TVMFFIObject::deleter = nullptr;
}
+ // Information about the object
+ static constexpr const char* _type_key = "runtime.Object";
+
+ // Default object type properties for sub-classes
+ static constexpr bool _type_final = false;
+ static constexpr uint32_t _type_child_slots = 0;
+ static constexpr bool _type_child_slots_can_overflow = true;
+ // NOTE: the following field is not type index of Object
+ // but was intended to be used by sub-classes as default value.
+ // The type index of Object is TypeIndex::kRoot
+ static constexpr int32_t _type_index = TypeIndex::kTVMFFIDynObject;
+
+ // The following functions are provided by macro
+ // TVM_FFI_DECLARE_BASE_OBJECT_INFO and TVM_DECLARE_FINAL_OBJECT_INFO
+ /*!
+ * \brief Get the runtime allocated type index of the type
+ * \note Getting this information may need dynamic calls into a global table.
+ */
+ static int32_t RuntimeTypeIndex() { return TypeIndex::kTVMFFIObject; }
+ /*!
+ * \brief Internal function to get or allocate a runtime index.
+ * \note
+ */
+ static int32_t _GetOrAllocRuntimeTypeIndex() { return TypeIndex::kTVMFFIObject; }
+
private:
- /*! \brief decreas*/
+ /*! \brief increase reference count */
void IncRef() { details::AtomicIncrementRelaxed(&(this->ref_counter)); }
+ /*! \brief decrease reference count and delete the object */
void DecRef() {
if (details::AtomicDecrementRelAcq(&(this->ref_counter)) == 1) {
if (this->deleter != nullptr) {
@@ -177,9 +244,9 @@ class ObjectPtr {
/*! \return Whether two ObjectPtr equals each other */
bool operator!=(const ObjectPtr<T>& other) const { return data_ != other.data_; }
/*! \return Whether the pointer is nullptr */
- bool operator==(std::nullptr_t null) const { return data_ == nullptr; }
+ bool operator==(std::nullptr_t) const { return data_ == nullptr; }
/*! \return Whether the pointer is not nullptr */
- bool operator!=(std::nullptr_t null) const { return data_ != nullptr; }
+ bool operator!=(std::nullptr_t) const { return data_ != nullptr; }
private:
/*! \brief internal pointer field */
@@ -207,6 +274,9 @@ class ObjectPtr {
// friend classes
friend class Object;
friend class ObjectRef;
+ friend struct ObjectPtrHash;
+ template <typename>
+ friend class ObjectPtr;
friend class tvm::ffi::details::ObjectInternal;
template <typename RelayRefType, typename ObjType>
friend RelayRefType GetRef(const ObjType* ptr);
@@ -215,8 +285,24 @@ class ObjectPtr {
};
namespace details {
-/*! \brief Namespace to internally manipulate object class. */
-struct ObjectInternal {};
+/*!
+ * \brief Namespace to internally manipulate object class.
+ * \note These functions are only supposed to be used by internal
+ * implementations and not external users of the tvm::ffi
+ */
+struct ObjectInternal {
+ // NOTE: these helper to perform static cast
+ // that also allows conversion from/to FFI values
+ template <typename T, typename U>
+ static TVM_FFI_INLINE T StaticCast(U src) {
+ return static_cast<T>(src);
+ }
+
+ template <typename T>
+ static TVM_FFI_INLINE ObjectPtr<T> ObjectPtr(Object* raw_ptr) {
+ return tvm::ffi::ObjectPtr<T>(raw_ptr);
+ }
+};
} // namespace details
} // namespace ffi
} // namespace tvm
diff --git a/ffi/tests/example/test_object.cc b/ffi/tests/example/test_object.cc
index 95b7d0717f..959246fda0 100644
--- a/ffi/tests/example/test_object.cc
+++ b/ffi/tests/example/test_object.cc
@@ -1,12 +1,35 @@
#include <gtest/gtest.h>
+#include <tvm/ffi/memory.h>
#include <tvm/ffi/object.h>
namespace {
using namespace tvm::ffi;
-TEST(Object, Default) {
- // Object* x = new Object();
+class IntObj : public Object {
+ public:
+ int64_t value;
+
+ IntObj(int64_t value) : value(value) {}
+};
+
+TEST(Object, RefCounter) {
+ ObjectPtr<IntObj> a = make_object<IntObj>(11);
+ ObjectPtr<IntObj> b = a;
+
+ EXPECT_EQ(a->value, 11);
+
+ EXPECT_EQ(a.use_count(), 2);
+ b.reset();
+ EXPECT_EQ(a.use_count(), 1);
+ EXPECT_TRUE(b == nullptr);
+ EXPECT_EQ(b.use_count(), 0);
+
+ ObjectPtr<IntObj> c = std::move(a);
+ EXPECT_EQ(c.use_count(), 1);
+ EXPECT_TRUE(a == nullptr);
+
+ EXPECT_EQ(c->value, 11);
}
} // namespace
(tvm) 35/38: Pass compile
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit a16c253f0e4e21bdd7500c1c909a6d39faf77b5a
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 19:39:58 2025 -0400
Pass compile
---
ffi/include/tvm/ffi/object.h | 20 ++++++++++++++++++--
ffi/include/tvm/ffi/reflection.h | 9 +++++++++
ffi/src/ffi/object.cc | 1 +
include/tvm/ir/attrs.h | 2 +-
include/tvm/ir/expr.h | 1 +
include/tvm/ir/op.h | 4 ++--
include/tvm/relax/block_builder.h | 1 -
include/tvm/relax/exec_builder.h | 1 -
include/tvm/relax/tir_pattern.h | 2 +-
include/tvm/target/target_kind.h | 2 +-
include/tvm/tir/function.h | 2 +-
python/setup.py | 3 ++-
src/target/llvm/llvm_module.cc | 2 +-
src/tir/ir/stmt_functor.cc | 2 +-
tests/cpp/container_test.cc | 1 -
tests/cpp/object_protocol_test.cc | 4 ----
16 files changed, 39 insertions(+), 18 deletions(-)
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index b9e5df51d1..d4f4544cd6 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -127,6 +127,15 @@ class Object {
return type_info->type_key;
}
+ /*!
+ * \return A hash value of the return of GetTypeKey.
+ */
+ uint64_t GetTypeKeyHash() const {
+ // the function checks that the info exists
+ const TypeInfo* type_info = TVMFFIGetTypeInfo(header_.type_index);
+ return type_info->type_key_hash;
+ }
+
/*!
* \brief Get the type key of the corresponding index from runtime.
* \param tindex The type index.
@@ -137,6 +146,8 @@ class Object {
return type_info->type_key;
}
+ bool unique() const { return use_count() == 1; }
+
// Information about the object
static constexpr const char* _type_key = "object.Object";
@@ -148,6 +159,11 @@ class Object {
static constexpr int32_t _type_index = TypeIndex::kTVMFFIObject;
// the static type depth of the class
static constexpr int32_t _type_depth = 0;
+ // extra fields used by plug-ins for attribute visiting
+ // and structural information
+ static constexpr const bool _type_has_method_visit_attrs = true;
+ static constexpr const bool _type_has_method_sequal_reduce = false;
+ static constexpr const bool _type_has_method_shash_reduce = false;
// The following functions are provided by macro
// TVM_FFI_DECLARE_BASE_OBJECT_INFO and TVM_DECLARE_FINAL_OBJECT_INFO
/*!
@@ -449,7 +465,7 @@ struct ObjectPtrEqual {
*/
#define TVM_FFI_DECLARE_STATIC_OBJECT_INFO(TypeName, ParentType) \
static int32_t RuntimeTypeIndex() { return TypeName::_type_index; } \
- TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType);
+ TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType)
/*
* \brief Define object reference methods.
@@ -649,7 +665,7 @@ class ObjectUnsafe {
return ptr;
}
- static TVM_FFI_INLINE Object** GetObjectRValueRefValue(ObjectRef* ref) {
+ static TVM_FFI_INLINE Object** GetObjectRValueRefValue(const ObjectRef* ref) {
return const_cast<Object**>(&(ref->data_.data_));
}
diff --git a/ffi/include/tvm/ffi/reflection.h b/ffi/include/tvm/ffi/reflection.h
index b29b3d919f..64a0e21d0e 100644
--- a/ffi/include/tvm/ffi/reflection.h
+++ b/ffi/include/tvm/ffi/reflection.h
@@ -148,6 +148,15 @@ class ReflectionFieldGetter {
} // namespace details
+/*!
+ * \brief helper function to get type index from key
+ */
+inline int32_t TypeKey2Index(const char* type_key) {
+ int32_t type_index;
+ TVM_FFI_CHECK_SAFE_CALL(TVMFFITypeKey2Index(type_key, &type_index));
+ return type_index;
+}
+
/*!
* \brief helper macro to declare a base object type that can be inherited.
* \param TypeName The name of the current type.
diff --git a/ffi/src/ffi/object.cc b/ffi/src/ffi/object.cc
index 1cf37ac448..fbbc1993fd 100644
--- a/ffi/src/ffi/object.cc
+++ b/ffi/src/ffi/object.cc
@@ -259,6 +259,7 @@ int TVMFFITypeKey2Index(const char* type_key, int32_t* out_tindex) {
TVM_FFI_SAFE_CALL_BEGIN();
out_tindex[0] = tvm::ffi::TypeTable::Global()->TypeKey2Index(type_key);
TVM_FFI_SAFE_CALL_END();
+
}
int TVMFFIRegisterTypeField(int32_t type_index, const TVMFFIFieldInfo* info) {
diff --git a/include/tvm/ir/attrs.h b/include/tvm/ir/attrs.h
index d038d5f59a..479cc476bf 100644
--- a/include/tvm/ir/attrs.h
+++ b/include/tvm/ir/attrs.h
@@ -65,7 +65,7 @@ namespace tvm {
*/
#define TVM_DECLARE_ATTRS(ClassName, TypeKey) \
static constexpr const char* _type_key = TypeKey; \
- TVM_DECLARE_FINAL_OBJECT_INFO(ClassName, ::tvm::BaseAttrsNode) \
+ TVM_DECLARE_FINAL_OBJECT_INFO(ClassName, ::tvm::BaseAttrsNode);\
template <typename FVisit> \
void _tvm_VisitAttrs(FVisit& _tvm_fvisit) // NOLINT(*)
diff --git a/include/tvm/ir/expr.h b/include/tvm/ir/expr.h
index 53af269756..3f9d321bdb 100644
--- a/include/tvm/ir/expr.h
+++ b/include/tvm/ir/expr.h
@@ -56,6 +56,7 @@ class BaseExprNode : public Object {
mutable Span span;
static constexpr const char* _type_key = "BaseExpr";
+ static constexpr const bool _type_has_method_visit_attrs = true;
static constexpr const bool _type_has_method_sequal_reduce = true;
static constexpr const bool _type_has_method_shash_reduce = true;
static constexpr const uint32_t _type_child_slots = 64;
diff --git a/include/tvm/ir/op.h b/include/tvm/ir/op.h
index 7fbd1cbb84..b8dd3495cd 100644
--- a/include/tvm/ir/op.h
+++ b/include/tvm/ir/op.h
@@ -160,7 +160,7 @@ class Op : public RelaxExpr {
*/
TVM_DLL static const Op& Get(const String& op_name);
- TVM_DEFINE_OBJECT_REF_METHODS(Op, RelaxExpr, OpNode)
+ TVM_DEFINE_OBJECT_REF_METHODS(Op, RelaxExpr, OpNode);
private:
/*!
@@ -359,7 +359,7 @@ inline OpRegEntry& OpRegEntry::set_attrs_type() { // NOLINT(*)
inline OpRegEntry& OpRegEntry::set_attrs_type_key(const String& key) { // NOLINT(*)
get()->attrs_type_key = key;
- get()->attrs_type_index = Object::TypeKey2Index(key);
+ get()->attrs_type_index = tvm::ffi::TypeKey2Index(key.c_str());
return *this;
}
diff --git a/include/tvm/relax/block_builder.h b/include/tvm/relax/block_builder.h
index 070aef2fcb..5efe91a5e4 100644
--- a/include/tvm/relax/block_builder.h
+++ b/include/tvm/relax/block_builder.h
@@ -257,7 +257,6 @@ class BlockBuilderNode : public Object {
*/
virtual arith::Analyzer* GetAnalyzer() = 0;
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const char* _type_key = "relax.BlockBuilder";
TVM_DECLARE_BASE_OBJECT_INFO(BlockBuilderNode, Object);
};
diff --git a/include/tvm/relax/exec_builder.h b/include/tvm/relax/exec_builder.h
index 8940408f80..c5103719d0 100644
--- a/include/tvm/relax/exec_builder.h
+++ b/include/tvm/relax/exec_builder.h
@@ -139,7 +139,6 @@ class ExecBuilderNode : public Object {
void VisitAttrs(AttrVisitor* v) {}
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const char* _type_key = "relax.ExecBuilder";
TVM_DECLARE_FINAL_OBJECT_INFO(ExecBuilderNode, Object);
diff --git a/include/tvm/relax/tir_pattern.h b/include/tvm/relax/tir_pattern.h
index 02634dcbbf..1b29cb0358 100644
--- a/include/tvm/relax/tir_pattern.h
+++ b/include/tvm/relax/tir_pattern.h
@@ -66,7 +66,7 @@ class MatchResult : public ObjectRef {
TVM_DLL explicit MatchResult(TIRPattern pattern, Array<PrimExpr> symbol_values,
Array<tir::Buffer> matched_buffers);
- TVM_DEFINE_OBJECT_REF_METHODS(MatchResult, ObjectRef, MatchResultNode)
+ TVM_DEFINE_OBJECT_REF_METHODS(MatchResult, ObjectRef, MatchResultNode);
};
using FCodegen = runtime::TypedPackedFunc<Array<ObjectRef>(Array<MatchResult> match_results)>;
diff --git a/include/tvm/target/target_kind.h b/include/tvm/target/target_kind.h
index f398736c73..7cb88251f3 100644
--- a/include/tvm/target/target_kind.h
+++ b/include/tvm/target/target_kind.h
@@ -92,7 +92,7 @@ class TargetKindNode : public Object {
/*! \brief Stores the required type_key and type_index of a specific attr of a target */
struct ValueTypeInfo {
String type_key;
- uint32_t type_index;
+ int32_t type_index;
std::unique_ptr<ValueTypeInfo> key;
std::unique_ptr<ValueTypeInfo> val;
};
diff --git a/include/tvm/tir/function.h b/include/tvm/tir/function.h
index e90c49fc4f..fbd61e7a38 100644
--- a/include/tvm/tir/function.h
+++ b/include/tvm/tir/function.h
@@ -222,7 +222,7 @@ class TensorIntrin : public ObjectRef {
*/
TVM_DLL static Optional<TensorIntrin> Get(String name, bool allow_missing = false);
- TVM_DEFINE_OBJECT_REF_METHODS(TensorIntrin, ObjectRef, TensorIntrinNode)
+ TVM_DEFINE_OBJECT_REF_METHODS(TensorIntrin, ObjectRef, TensorIntrinNode);
};
/*!
diff --git a/python/setup.py b/python/setup.py
index ad0ef6ffde..26e209de61 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -168,7 +168,8 @@ def config_cython():
include_dirs=[
"../include/",
"../3rdparty/dmlc-core/include",
- "../3rdparty/dlpack/include",
+ "../ffi/include/",
+ "../ffi/3rdparty/dlpack/include",
],
extra_compile_args=extra_compile_args,
library_dirs=library_dirs,
diff --git a/src/target/llvm/llvm_module.cc b/src/target/llvm/llvm_module.cc
index d3a1eb1391..4db37149e3 100644
--- a/src/target/llvm/llvm_module.cc
+++ b/src/target/llvm/llvm_module.cc
@@ -196,7 +196,7 @@ PackedFunc LLVMModuleNode::GetFunction(const String& name, const ObjectPtr<Objec
faddr = reinterpret_cast<TVMBackendPackedCFunc>(GetFunctionAddr(name, *llvm_target));
}
if (faddr == nullptr) return PackedFunc();
- return WrapPackedFunc(faddr, sptr_to_self);
+ return tvm::runtime::WrapPackedFunc(faddr, sptr_to_self);
}
namespace {
diff --git a/src/tir/ir/stmt_functor.cc b/src/tir/ir/stmt_functor.cc
index 1c15f95826..9bdcb60407 100644
--- a/src/tir/ir/stmt_functor.cc
+++ b/src/tir/ir/stmt_functor.cc
@@ -638,7 +638,7 @@ Stmt IRTransform(Stmt ir_node, const runtime::PackedFunc& f_preorder,
std::unordered_set<uint32_t> only_type_index;
if (only_enable.defined()) {
for (auto s : only_enable.value()) {
- only_type_index.insert(Object::TypeKey2Index(s.c_str()));
+ only_type_index.insert(ffi::TypeKey2Index(s.c_str()));
}
}
IRTransformer transform(f_preorder, f_postorder, only_type_index);
diff --git a/tests/cpp/container_test.cc b/tests/cpp/container_test.cc
index 6f0c47c0ea..51391c86a0 100644
--- a/tests/cpp/container_test.cc
+++ b/tests/cpp/container_test.cc
@@ -60,7 +60,6 @@ class TestErrorSwitch {
class TestArrayObj : public Object, public InplaceArrayBase<TestArrayObj, TestErrorSwitch> {
public:
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const char* _type_key = "test.TestArrayObj";
TVM_DECLARE_FINAL_OBJECT_INFO(TestArrayObj, Object);
uint32_t size;
diff --git a/tests/cpp/object_protocol_test.cc b/tests/cpp/object_protocol_test.cc
index c4c83dcd95..3f76b1d2f1 100644
--- a/tests/cpp/object_protocol_test.cc
+++ b/tests/cpp/object_protocol_test.cc
@@ -30,7 +30,6 @@ using namespace tvm::runtime;
class ObjBase : public Object {
public:
// dynamically allocate slow
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const uint32_t _type_child_slots = 1;
static constexpr const char* _type_key = "test.ObjBase";
TVM_DECLARE_BASE_OBJECT_INFO(ObjBase, Object);
@@ -38,7 +37,6 @@ class ObjBase : public Object {
class ObjA : public ObjBase {
public:
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const uint32_t _type_child_slots = 0;
static constexpr const char* _type_key = "test.ObjA";
TVM_DECLARE_BASE_OBJECT_INFO(ObjA, ObjBase);
@@ -46,7 +44,6 @@ class ObjA : public ObjBase {
class ObjB : public ObjBase {
public:
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const uint32_t _type_child_slots = 0;
static constexpr const char* _type_key = "test.ObjB";
TVM_DECLARE_BASE_OBJECT_INFO(ObjB, ObjBase);
@@ -54,7 +51,6 @@ class ObjB : public ObjBase {
class ObjAA : public ObjA {
public:
- static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
static constexpr const char* _type_key = "test.ObjAA";
TVM_DECLARE_FINAL_OBJECT_INFO(ObjAA, ObjA);
};
(tvm) 33/38: pass expr first stab
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 958938ee34cb8909c69de2b973bd4369bc8b5599
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 18:14:26 2025 -0400
pass expr first stab
---
ffi/include/tvm/ffi/object.h | 28 +++++++++-------------
ffi/include/tvm/ffi/reflection.h | 8 +++++--
include/tvm/runtime/object.h | 51 +++++++++++++++++++++++++++++++++++++++-
src/node/reflection.cc | 5 ++--
src/runtime/debug_compile.cc | 2 ++
5 files changed, 72 insertions(+), 22 deletions(-)
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index d63b4efbd7..b2189fb7f5 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -426,24 +426,15 @@ struct ObjectPtrEqual {
}
};
-/*!
- * \brief Helper macro to declare list of static checks about object meta-data.
- * \param TypeName The name of the current type.
- * \param ParentType The name of the ParentType
- */
-#define TVM_FFI_OBJECT_STATIC_DEFS(TypeName, ParentType) \
- static constexpr int32_t _type_depth = ParentType::_type_depth + 1; \
- static_assert(!ParentType::_type_final, "ParentType marked as final"); \
- static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
- TypeName::_type_child_slots < ParentType::_type_child_slots, \
- "Need to set _type_child_slots when parent specifies it."); \
- static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
- TypeName::_type_child_slots < ParentType::_type_child_slots, \
- "Need to set _type_child_slots when parent specifies it.")
// If dynamic type is enabled, we still need to register the runtime type of parent
#define TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType) \
+ static constexpr int32_t _type_depth = ParentType::_type_depth + 1; \
static int32_t _GetOrAllocRuntimeTypeIndex() { \
+ static_assert(!ParentType::_type_final, "ParentType marked as final"); \
+ static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
+ TypeName::_type_child_slots < ParentType::_type_child_slots, \
+ "Need to set _type_child_slots when parent specifies it."); \
static int32_t tindex = TVMFFIGetOrAllocTypeIndex( \
TypeName::_type_key, TypeName::_type_index, TypeName::_type_depth, \
TypeName::_type_child_slots, TypeName::_type_child_slots_can_overflow, \
@@ -458,9 +449,8 @@ struct ObjectPtrEqual {
* \param ParentType The name of the ParentType
*/
#define TVM_FFI_DECLARE_STATIC_OBJECT_INFO(TypeName, ParentType) \
- TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType); \
static int32_t RuntimeTypeIndex() { return TypeName::_type_index; } \
- TVM_FFI_OBJECT_STATIC_DEFS(TypeName, ParentType)
+ TVM_FFI_REGISTER_STATIC_TYPE_INFO(TypeName, ParentType);
/*
@@ -524,7 +514,6 @@ struct ObjectPtrEqual {
namespace details {
-
template <typename TargetType>
TVM_FFI_INLINE bool IsObjectInstance(int32_t object_type_index) {
static_assert(std::is_base_of_v<Object, TargetType>);
@@ -575,6 +564,11 @@ class ObjectUnsafe {
reinterpret_cast<int64_t>(&(static_cast<Object*>(nullptr)->header_)));
}
+ template <typename T>
+ static TVM_FFI_INLINE ObjectPtr<T> ObjectPtrFromObjectRef(const ObjectRef& ref) {
+ return tvm::ffi::ObjectPtr<T>(ref.data_.data_);
+ }
+
template <typename T>
static TVM_FFI_INLINE ObjectPtr<T> ObjectPtrFromOwned(Object* raw_ptr) {
tvm::ffi::ObjectPtr<T> ptr;
diff --git a/ffi/include/tvm/ffi/reflection.h b/ffi/include/tvm/ffi/reflection.h
index f3cc580853..d154522b9f 100644
--- a/ffi/include/tvm/ffi/reflection.h
+++ b/ffi/include/tvm/ffi/reflection.h
@@ -154,8 +154,12 @@ class ReflectionFieldGetter {
* \param ParentType The name of the ParentType
*/
#define TVM_FFI_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType) \
- TVM_FFI_OBJECT_STATIC_DEFS(TypeName, ParentType); \
- static int32_t _GetOrAllocRuntimeTypeIndex() { \
+ static constexpr int32_t _type_depth = ParentType::_type_depth + 1; \
+ static int32_t _GetOrAllocRuntimeTypeIndex() { \
+ static_assert(!ParentType::_type_final, "ParentType marked as final"); \
+ static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
+ TypeName::_type_child_slots < ParentType::_type_child_slots, \
+ "Need to set _type_child_slots when parent specifies it."); \
static int32_t tindex = TVMFFIGetOrAllocTypeIndex( \
TypeName::_type_key, -1, TypeName::_type_depth, TypeName::_type_child_slots, \
TypeName::_type_child_slots_can_overflow, ParentType::_GetOrAllocRuntimeTypeIndex()); \
diff --git a/include/tvm/runtime/object.h b/include/tvm/runtime/object.h
index ec39461b0c..d956eebf1f 100644
--- a/include/tvm/runtime/object.h
+++ b/include/tvm/runtime/object.h
@@ -128,11 +128,13 @@ class ObjectRef : public tvm::ffi::ObjectRef {
* \brief Internal helper function get data_ as ObjectPtr of ObjectType.
* \note only used for internal dev purpose.
* \tparam ObjectType The corresponding object type.
+ * \param ref The object reference
* \return the corresponding type.
*/
template <typename ObjectType>
static ObjectPtr<ObjectType> GetDataPtr(const ObjectRef& ref) {
- return ObjectPtr<ObjectType>(ref.data_.data_);
+ // return ObjectPtr<ObjectType>(ref.data_.data_);
+ return ffi::details::ObjectUnsafe::ObjectPtrFromObjectRef<ObjectType>(ref);
}
// friend classes.
@@ -152,6 +154,53 @@ class ObjectRef : public tvm::ffi::ObjectRef {
TypeName& operator=(const TypeName& other) = default; \
TypeName& operator=(TypeName&& other) = default;
+/*!
+ * \brief Define CopyOnWrite function in an ObjectRef.
+ * \param ObjectName The Type of the Node.
+ *
+ * CopyOnWrite will generate a unique copy of the internal node.
+ * The node will be copied if it is referenced by multiple places.
+ * The function returns the raw pointer to the node to allow modification
+ * of the content.
+ *
+ * \code
+ *
+ * MyCOWObjectRef ref, ref2;
+ * ref2 = ref;
+ * ref.CopyOnWrite()->value = new_value;
+ * assert(ref2->value == old_value);
+ * assert(ref->value == new_value);
+ *
+ * \endcode
+ */
+#define TVM_DEFINE_OBJECT_REF_COW_METHOD(ObjectName) \
+static_assert(ObjectName::_type_final, \
+ "TVM's CopyOnWrite may only be used for " \
+ "Object types that are declared as final, " \
+ "using the TVM_DECLARE_FINAL_OBJECT_INFO macro."); \
+ObjectName* CopyOnWrite() { \
+ ICHECK(data_ != nullptr); \
+ if (!data_.unique()) { \
+ auto n = make_object<ObjectName>(*(operator->())); \
+ ObjectPtr<Object>(std::move(n)).swap(data_); \
+ } \
+ return static_cast<ObjectName*>(data_.get()); \
+}
+
+/*
+ * \brief Define object reference methods.
+ * \param TypeName The object type name
+ * \param ParentType The parent type of the objectref
+ * \param ObjectName The type name of the object.
+ */
+#define TVM_DEFINE_OBJECT_REF_METHODS_WITHOUT_DEFAULT_CONSTRUCTOR(TypeName, ParentType, \
+ ObjectName) \
+explicit TypeName(::tvm::runtime::ObjectPtr<::tvm::runtime::Object> n) : ParentType(n) {} \
+TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName); \
+const ObjectName* operator->() const { return static_cast<const ObjectName*>(data_.get()); } \
+const ObjectName* get() const { return operator->(); } \
+using ContainerType = ObjectName;
+
#define TVM_DECLARE_BASE_OBJECT_INFO TVM_FFI_DECLARE_BASE_OBJECT_INFO
#define TVM_DECLARE_FINAL_OBJECT_INFO TVM_FFI_DECLARE_FINAL_OBJECT_INFO
diff --git a/src/node/reflection.cc b/src/node/reflection.cc
index aa572e9965..02e7b2423d 100644
--- a/src/node/reflection.cc
+++ b/src/node/reflection.cc
@@ -153,8 +153,9 @@ ReflectionVTable* ReflectionVTable::Global() {
ObjectPtr<Object> ReflectionVTable::CreateInitObject(const std::string& type_key,
const std::string& repr_bytes) const {
- uint32_t tindex = Object::TypeKey2Index(type_key);
- if (tindex >= fcreate_.size() || fcreate_[tindex] == nullptr) {
+ int32_t tindex;
+ TVM_FFI_CHECK_SAFE_CALL(TVMFFITypeKey2Index(type_key.c_str(), &tindex));
+ if (static_cast<size_t>(tindex) >= fcreate_.size() || fcreate_[tindex] == nullptr) {
LOG(FATAL) << "TypeError: " << type_key << " is not registered via TVM_REGISTER_NODE_TYPE";
}
return fcreate_[tindex](repr_bytes);
diff --git a/src/runtime/debug_compile.cc b/src/runtime/debug_compile.cc
index 4233035abd..17f86d5f7f 100644
--- a/src/runtime/debug_compile.cc
+++ b/src/runtime/debug_compile.cc
@@ -30,6 +30,8 @@
#include <tvm/runtime/packed_func.h>
#include <tvm/runtime/registry.h>
#include <tvm/runtime/disco/disco_worker.h>
+#include <tvm/ir/expr.h>
+#include <tvm/tir/expr.h>
namespace tvm {
namespace debug {
(tvm) 09/38: [FFI] Improve Any coverage to include ObjectPtr
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 9a5cca4645338f663df2024b759b942e9d50ad80
Author: tqchen <ti...@gmail.com>
AuthorDate: Fri Sep 6 18:57:38 2024 -0400
[FFI] Improve Any coverage to include ObjectPtr
---
ffi/include/tvm/ffi/any.h | 10 +--
ffi/include/tvm/ffi/memory.h | 4 +-
ffi/include/tvm/ffi/object.h | 29 ++++++---
ffi/include/tvm/ffi/type_traits.h | 128 ++++++++++++++++++++++++++++++++++++--
ffi/tests/example/test_any.cc | 22 +++++++
5 files changed, 173 insertions(+), 20 deletions(-)
diff --git a/ffi/include/tvm/ffi/any.h b/ffi/include/tvm/ffi/any.h
index 6f2935f8a6..2a2d223c57 100644
--- a/ffi/include/tvm/ffi/any.h
+++ b/ffi/include/tvm/ffi/any.h
@@ -96,6 +96,7 @@ class AnyView {
}
TVM_FFI_THROW(TypeError) << "Cannot convert from type `" << TypeIndex2TypeKey(data_.type_index)
<< "` to `" << TypeTraits<T>::TypeStr() << "`";
+ TVM_FFI_UNREACHABLE();
}
// The following functions are only used for testing purposes
/*!
@@ -126,7 +127,7 @@ TVM_FFI_INLINE void InplaceConvertAnyViewToAny(TVMFFIAny* data,
[[maybe_unused]] size_t extra_any_bytes = 0) {
// TODO: string conversion.
if (data->type_index >= TVMFFITypeIndex::kTVMFFIStaticObjectBegin) {
- details::ObjectInternal::IncRefObjectInAny(data);
+ details::ObjectUnsafe::IncRefObjectInAny(data);
}
}
} // namespace details
@@ -145,7 +146,7 @@ class Any {
*/
void reset() {
if (data_.type_index >= TVMFFITypeIndex::kTVMFFIStaticObjectBegin) {
- details::ObjectInternal::DecRefObjectInAny(&data_);
+ details::ObjectUnsafe::DecRefObjectInAny(&data_);
}
data_.type_index = TVMFFITypeIndex::kTVMFFINone;
}
@@ -166,7 +167,7 @@ class Any {
// constructors from Any
Any(const Any& other) : data_(other.data_) {
if (data_.type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
- details::ObjectInternal::IncRefObjectInAny(&data_);
+ details::ObjectUnsafe::IncRefObjectInAny(&data_);
}
}
Any(Any&& other) : data_(other.data_) { other.data_.type_index = TypeIndex::kTVMFFINone; }
@@ -192,7 +193,7 @@ class Any {
// constructor from general types
template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
Any(T other) { // NOLINT(*)
- TypeTraits<T>::MoveToManagedAny(std::move(other), &data_);
+ TypeTraits<T>::MoveToAny(std::move(other), &data_);
}
template <typename T, typename = std::enable_if_t<TypeTraits<T>::enabled>>
Any& operator=(T other) { // NOLINT(*)
@@ -213,6 +214,7 @@ class Any {
}
TVM_FFI_THROW(TypeError) << "Cannot convert from type `" << TypeIndex2TypeKey(data_.type_index)
<< "` to `" << TypeTraits<T>::TypeStr() << "`";
+ TVM_FFI_UNREACHABLE();
}
// FFI related operations
diff --git a/ffi/include/tvm/ffi/memory.h b/ffi/include/tvm/ffi/memory.h
index ca3021ff78..03cd542d91 100644
--- a/ffi/include/tvm/ffi/memory.h
+++ b/ffi/include/tvm/ffi/memory.h
@@ -74,11 +74,11 @@ class ObjAllocatorBase {
using Handler = typename Derived::template Handler<T>;
static_assert(std::is_base_of<Object, T>::value, "make can only be used to create Object");
T* ptr = Handler::New(static_cast<Derived*>(this), std::forward<Args>(args)...);
- TVMFFIObject* ffi_ptr = details::ObjectInternal::GetHeader(ptr);
+ TVMFFIObject* ffi_ptr = details::ObjectUnsafe::GetHeader(ptr);
// NOTE: ref_counter is initialized in object constructor
ffi_ptr->type_index = T::RuntimeTypeIndex();
ffi_ptr->deleter = Handler::Deleter();
- return details::ObjectInternal::ObjectPtrFromUnowned<T>(ptr);
+ return details::ObjectUnsafe::ObjectPtrFromUnowned<T>(ptr);
}
/*!
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index a1547b1900..b7a0d1a7c5 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -36,8 +36,9 @@ using TypeIndex = TVMFFITypeIndex;
using TypeInfo = TVMFFITypeInfo;
namespace details {
-// forward declare object internal
-struct ObjectInternal;
+// Helper to perform
+// unsafe operations related to object
+struct ObjectUnsafe;
// Code section that depends on dynamic components
#if TVM_FFI_ALLOW_DYN_TYPE
@@ -191,7 +192,7 @@ class Object {
// friend classes
template <typename>
friend class ObjectPtr;
- friend class tvm::ffi::details::ObjectInternal;
+ friend class tvm::ffi::details::ObjectUnsafe;
};
/*!
@@ -328,7 +329,7 @@ class ObjectPtr {
friend struct ObjectPtrHash;
template <typename>
friend class ObjectPtr;
- friend class tvm::ffi::details::ObjectInternal;
+ friend class tvm::ffi::details::ObjectUnsafe;
template <typename RelayRefType, typename ObjType>
friend RelayRefType GetRef(const ObjType* ptr);
template <typename BaseType, typename ObjType>
@@ -417,7 +418,7 @@ class ObjectRef {
Object* get_mutable() const { return data_.get(); }
// friend classes.
friend struct ObjectPtrHash;
- friend class tvm::ffi::details::ObjectInternal;
+ friend class tvm::ffi::details::ObjectUnsafe;
template <typename SubRef, typename BaseRef>
friend SubRef Downcast(BaseRef ref);
};
@@ -574,10 +575,10 @@ TVM_FFI_INLINE bool IsObjectInstance(int32_t object_type_index) {
* \note These functions are only supposed to be used by internal
* implementations and not external users of the tvm::ffi
*/
-struct ObjectInternal {
+struct ObjectUnsafe {
// NOTE: get ffi header from an object
- static TVM_FFI_INLINE TVMFFIObject* GetHeader(Object* src) {
- return &(src->header_);
+ static TVM_FFI_INLINE TVMFFIObject* GetHeader(const Object* src) {
+ return const_cast<TVMFFIObject*>(&(src->header_));
}
// Create ObjectPtr from unknowned ptr
@@ -610,6 +611,18 @@ struct ObjectInternal {
return GetHeader(obj_ptr);
}
+ template <typename T>
+ static TVM_FFI_INLINE TVMFFIObject* GetTVMFFIObjectPtrFromObjectPtr(const ObjectPtr<T>& src) {
+ return GetHeader(src.data_);
+ }
+
+ template <typename T>
+ static TVM_FFI_INLINE TVMFFIObject* MoveTVMFFIObjectPtrFromObjectPtr(ObjectPtr<T>* src) {
+ Object* obj_ptr = src->data_;
+ src->data_ = nullptr;
+ return GetHeader(obj_ptr);
+ }
+
// Create objectptr by moving from an existing address of object and setting its
// address to nullptr
template <typename T>
diff --git a/ffi/include/tvm/ffi/type_traits.h b/ffi/include/tvm/ffi/type_traits.h
index 18c7ab2a35..e1d453fdba 100644
--- a/ffi/include/tvm/ffi/type_traits.h
+++ b/ffi/include/tvm/ffi/type_traits.h
@@ -59,6 +59,29 @@ struct TypeTraits {
template <typename T>
using TypeTraitsNoCR = TypeTraits<std::remove_const_t<std::remove_reference_t<T>>>;
+// None
+template <>
+struct TypeTraits<std::nullptr_t> {
+ static constexpr bool enabled = true;
+
+ static TVM_FFI_INLINE void ConvertToAnyView(const std::nullptr_t&, TVMFFIAny* result) {
+ result->type_index = TypeIndex::kTVMFFINone;
+ }
+
+ static TVM_FFI_INLINE void MoveToAny(std::nullptr_t, TVMFFIAny* result) {
+ result->type_index = TypeIndex::kTVMFFINone;
+ }
+
+ static TVM_FFI_INLINE std::optional<std::nullptr_t> TryConvertFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFINone) {
+ return nullptr;
+ }
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return "None"; }
+};
+
// Integer POD values
template <typename Int>
struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> {
@@ -69,7 +92,7 @@ struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> {
result->v_int64 = static_cast<int64_t>(src);
}
- static TVM_FFI_INLINE void MoveToManagedAny(Int src, TVMFFIAny* result) {
+ static TVM_FFI_INLINE void MoveToAny(Int src, TVMFFIAny* result) {
ConvertToAnyView(src, result);
}
@@ -93,7 +116,7 @@ struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>> {
result->v_float64 = static_cast<double>(src);
}
- static TVM_FFI_INLINE void MoveToManagedAny(Float src, TVMFFIAny* result) {
+ static TVM_FFI_INLINE void MoveToAny(Float src, TVMFFIAny* result) {
ConvertToAnyView(src, result);
}
@@ -109,6 +132,30 @@ struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>> {
static TVM_FFI_INLINE std::string TypeStr() { return "float"; }
};
+// void*
+template <>
+struct TypeTraits<void*> {
+ static constexpr bool enabled = true;
+
+ static TVM_FFI_INLINE void ConvertToAnyView(void* src, TVMFFIAny* result) {
+ result->type_index = TypeIndex::kTVMFFIOpaquePtr;
+ result->v_ptr = src;
+ }
+
+ static TVM_FFI_INLINE void MoveToAny(void* src, TVMFFIAny* result) {
+ ConvertToAnyView(src, result);
+ }
+
+ static TVM_FFI_INLINE std::optional<void*> TryConvertFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index == TypeIndex::kTVMFFIOpaquePtr) {
+ return std::make_optional<void*>(src->v_ptr);
+ }
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return "void*"; }
+};
+
// Traits for object
template <typename TObjRef>
struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef>>> {
@@ -117,13 +164,13 @@ struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef
static constexpr bool enabled = true;
static TVM_FFI_INLINE void ConvertToAnyView(const TObjRef& src, TVMFFIAny* result) {
- TVMFFIObject* obj_ptr = details::ObjectInternal::GetTVMFFIObjectPtrFromObjectRef(src);
+ TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetTVMFFIObjectPtrFromObjectRef(src);
result->type_index = obj_ptr->type_index;
result->v_obj = obj_ptr;
}
- static TVM_FFI_INLINE void MoveToManagedAny(TObjRef src, TVMFFIAny* result) {
- TVMFFIObject* obj_ptr = details::ObjectInternal::MoveTVMFFIObjectPtrFromObjectRef(&src);
+ static TVM_FFI_INLINE void MoveToAny(TObjRef src, TVMFFIAny* result) {
+ TVMFFIObject* obj_ptr = details::ObjectUnsafe::MoveTVMFFIObjectPtrFromObjectRef(&src);
result->type_index = obj_ptr->type_index;
result->v_obj = obj_ptr;
}
@@ -132,7 +179,7 @@ struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef
if (src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
#if TVM_FFI_ALLOW_DYN_TYPE
if (details::IsObjectInstance<ContainerType>(src->type_index)) {
- return TObjRef(details::ObjectInternal::ObjectPtrFromUnowned<Object>(src->v_obj));
+ return TObjRef(details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(src->v_obj));
}
#else
TVM_FFI_THROW(RuntimeError)
@@ -148,6 +195,75 @@ struct TypeTraits<TObjRef, std::enable_if_t<std::is_base_of_v<ObjectRef, TObjRef
static TVM_FFI_INLINE std::string TypeStr() { return ContainerType::_type_key; }
};
+// Traits for object
+template <typename T>
+struct TypeTraits<ObjectPtr<T>> {
+ static constexpr bool enabled = true;
+
+ static TVM_FFI_INLINE void ConvertToAnyView(const ObjectPtr<T>& src, TVMFFIAny* result) {
+ TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetTVMFFIObjectPtrFromObjectPtr(src);
+ result->type_index = obj_ptr->type_index;
+ result->v_obj = obj_ptr;
+ }
+
+ static TVM_FFI_INLINE void MoveToAny(ObjectPtr<T> src, TVMFFIAny* result) {
+ TVMFFIObject* obj_ptr = details::ObjectUnsafe::MoveTVMFFIObjectPtrFromObjectPtr(&src);
+ result->type_index = obj_ptr->type_index;
+ result->v_obj = obj_ptr;
+ }
+
+ static TVM_FFI_INLINE std::optional<ObjectPtr<T>> TryConvertFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
+#if TVM_FFI_ALLOW_DYN_TYPE
+ if (details::IsObjectInstance<T>(src->type_index)) {
+ return details::ObjectUnsafe::ObjectPtrFromUnowned<T>(src->v_obj);
+ }
+#else
+ TVM_FFI_THROW(RuntimeError)
+ << "Converting to object requires `TVM_FFI_ALLOW_DYN_TYPE` to be on".
+#endif
+ }
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return T::_type_key; }
+};
+
+// Traits for object
+template <typename TObject>
+struct TypeTraits<const TObject*, std::enable_if_t<std::is_base_of_v<Object, TObject>>> {
+ static constexpr bool enabled = true;
+
+ static TVM_FFI_INLINE void ConvertToAnyView(const TObject* src, TVMFFIAny* result) {
+ TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetHeader(src);
+ result->type_index = obj_ptr->type_index;
+ result->v_obj = obj_ptr;
+ }
+
+ static TVM_FFI_INLINE void MoveToAny(const TObject* src, TVMFFIAny* result) {
+ TVMFFIObject* obj_ptr = details::ObjectUnsafe::GetHeader(src);
+ result->type_index = obj_ptr->type_index;
+ result->v_obj = obj_ptr;
+ details::ObjectUnsafe::IncRefObjectInAny(result);
+ }
+
+ static TVM_FFI_INLINE std::optional<const TObject*> TryConvertFromAnyView(const TVMFFIAny* src) {
+ if (src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
+#if TVM_FFI_ALLOW_DYN_TYPE
+ if (details::IsObjectInstance<TObject>(src->type_index)) {
+ return reinterpret_cast<const TObject*>(src->v_obj);
+ }
+#else
+ TVM_FFI_THROW(RuntimeError)
+ << "Converting to object requires `TVM_FFI_ALLOW_DYN_TYPE` to be on".
+#endif
+ }
+ return std::nullopt;
+ }
+
+ static TVM_FFI_INLINE std::string TypeStr() { return TObject::_type_key; }
+};
+
/*!
* \brief Get type key from type index
* \param type_index The input type index
diff --git a/ffi/tests/example/test_any.cc b/ffi/tests/example/test_any.cc
index 791ecfe68c..02d0ad6a23 100644
--- a/ffi/tests/example/test_any.cc
+++ b/ffi/tests/example/test_any.cc
@@ -114,6 +114,28 @@ TEST(Any, Object) {
AnyView view2 = any1;
EXPECT_EQ(v1.use_count(), 2);
+ // convert to weak raw object ptr
+ const TIntObj* v1_ptr = view2;
+ EXPECT_EQ(v1.use_count(), 2);
+ EXPECT_EQ(v1_ptr->value, 11);
+ Any any2 = v1_ptr;
+ EXPECT_EQ(v1.use_count(), 3);
+ EXPECT_TRUE(any2.TryAs<TInt>().has_value());
+
+ // convert to raw opaque ptr
+ void* raw_v1_ptr = const_cast<TIntObj*>(v1_ptr);
+ any2 = raw_v1_ptr;
+ EXPECT_TRUE(any2.TryAs<void*>().value() == v1_ptr);
+
+ // convert to ObjectPtr
+ ObjectPtr<TNumberObj> v1_obj_ptr = view2;
+ EXPECT_EQ(v1.use_count(), 3);
+ any2 = v1_obj_ptr;
+ EXPECT_EQ(v1.use_count(), 4);
+ EXPECT_TRUE(any2.TryAs<TInt>().has_value());
+ any2.reset();
+ v1_obj_ptr.reset();
+
// convert that triggers error
EXPECT_THROW(
{
(tvm) 24/38: First step, pass string compile
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 84d7aaa81bfd37e74ea5de5e990d987595fd6473
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 10:59:25 2025 -0400
First step, pass string compile
---
ffi/include/tvm/ffi/c_api.h | 1 +
ffi/include/tvm/ffi/object.h | 28 +-
include/tvm/runtime/container/map.h | 4 +-
include/tvm/runtime/container/string.h | 2 +-
include/tvm/runtime/memory.h | 180 +----
include/tvm/runtime/{memory.h => memory_backup.h} | 0
include/tvm/runtime/object.h | 887 +---------------------
include/tvm/runtime/{object.h => object_backup.h} | 12 +-
src/runtime/debug_compile.cc | 35 +
9 files changed, 99 insertions(+), 1050 deletions(-)
diff --git a/ffi/include/tvm/ffi/c_api.h b/ffi/include/tvm/ffi/c_api.h
index a1cf753448..44456aacd6 100644
--- a/ffi/include/tvm/ffi/c_api.h
+++ b/ffi/include/tvm/ffi/c_api.h
@@ -85,6 +85,7 @@ typedef enum {
kTVMFFIShapeTuple = 70,
kTVMFFINDArray = 71,
kTVMFFIRuntimeModule = 72,
+ kTVMFFIStaticObjectEnd,
// [Section] Dynamic Boxed: [kTVMFFIDynObjectBegin, +oo)
// kTVMFFIDynObject is used to indicate that the type index
// is dynamic and needs to be looked up at runtime
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index a7afb6c12f..4cb26f1c2b 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -147,7 +147,7 @@ class Object {
*/
static int32_t _GetOrAllocRuntimeTypeIndex() { return TypeIndex::kTVMFFIObject; }
- private:
+ private:
/*! \brief increase reference count */
void IncRef() { details::AtomicIncrementRelaxed(&(header_.ref_counter)); }
@@ -408,6 +408,26 @@ class ObjectRef {
template <typename BaseType, typename ObjectType>
inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
+/*! \brief ObjectRef hash functor */
+struct ObjectPtrHash {
+ size_t operator()(const ObjectRef& a) const { return operator()(a.data_); }
+
+ template <typename T>
+ size_t operator()(const ObjectPtr<T>& a) const {
+ return std::hash<Object*>()(a.get());
+ }
+};
+
+/*! \brief ObjectRef equal functor */
+struct ObjectPtrEqual {
+ bool operator()(const ObjectRef& a, const ObjectRef& b) const { return a.same_as(b); }
+
+ template <typename T>
+ size_t operator()(const ObjectPtr<T>& a, const ObjectPtr<T>& b) const {
+ return a == b;
+ }
+};
+
/*!
* \brief Helper macro to declare list of static checks about object meta-data.
* \param TypeName The name of the current type.
@@ -599,7 +619,13 @@ class ObjectUnsafe {
*ref = nullptr;
return ptr;
}
+
+ // legacy APIs to support migration and can be moved later
+ static TVM_FFI_INLINE void LegacyClearObjectPtrAfterMove(ObjectRef* src) {
+ src->data_.data_ = nullptr;
+ }
};
+
} // namespace details
} // namespace ffi
} // namespace tvm
diff --git a/include/tvm/runtime/container/map.h b/include/tvm/runtime/container/map.h
index eb86ddb7b8..1e4015aa23 100644
--- a/include/tvm/runtime/container/map.h
+++ b/include/tvm/runtime/container/map.h
@@ -1251,8 +1251,8 @@ inline void MapNode::InsertMaybeReHash(const KVType& kv, ObjectPtr<Object>* map)
}
}
-template <>
-inline ObjectPtr<MapNode> make_object<>() = delete;
+//template <>
+//inline ObjectPtr<MapNode> make_object<>() = delete;
#endif
diff --git a/include/tvm/runtime/container/string.h b/include/tvm/runtime/container/string.h
index a7be84de23..661d95bba2 100644
--- a/include/tvm/runtime/container/string.h
+++ b/include/tvm/runtime/container/string.h
@@ -59,7 +59,7 @@ class StringObj : public Object {
static constexpr const uint32_t _type_index = TypeIndex::kRuntimeString;
static constexpr const char* _type_key = "runtime.String";
- TVM_DECLARE_FINAL_OBJECT_INFO(StringObj, Object);
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(StringObj, Object);
private:
/*! \brief String object which is moved from std::string container. */
diff --git a/include/tvm/runtime/memory.h b/include/tvm/runtime/memory.h
index 1199c420f2..adf7fe9a3a 100644
--- a/include/tvm/runtime/memory.h
+++ b/include/tvm/runtime/memory.h
@@ -17,190 +17,18 @@
* under the License.
*/
/*!
- * \file tvm/runtime/memory.h
- * \brief Runtime memory management.
+ * \file tvm/runtime/object.h
+ * \brief A managed object in the TVM runtime.
*/
#ifndef TVM_RUNTIME_MEMORY_H_
#define TVM_RUNTIME_MEMORY_H_
-#include <tvm/runtime/object.h>
-
-#include <cstdlib>
-#include <type_traits>
-#include <utility>
+#include <tvm/ffi/memory.h>
namespace tvm {
namespace runtime {
-/*!
- * \brief Allocate an object using default allocator.
- * \param args arguments to the constructor.
- * \tparam T the node type.
- * \return The ObjectPtr to the allocated object.
- */
-template <typename T, typename... Args>
-inline ObjectPtr<T> make_object(Args&&... args);
-
-// Detail implementations after this
-//
-// The current design allows swapping the
-// allocator pattern when necessary.
-//
-// Possible future allocator optimizations:
-// - Arena allocator that gives ownership of memory to arena (deleter_= nullptr)
-// - Thread-local object pools: one pool per size and alignment requirement.
-// - Can specialize by type of object to give the specific allocator to each object.
-
-/*!
- * \brief Base class of object allocators that implements make.
- * Use curiously recurring template pattern.
- *
- * \tparam Derived The derived class.
- */
-template <typename Derived>
-class ObjAllocatorBase {
- public:
- /*!
- * \brief Make a new object using the allocator.
- * \tparam T The type to be allocated.
- * \tparam Args The constructor signature.
- * \param args The arguments.
- */
- template <typename T, typename... Args>
- inline ObjectPtr<T> make_object(Args&&... args) {
- using Handler = typename Derived::template Handler<T>;
- static_assert(std::is_base_of<Object, T>::value, "make can only be used to create Object");
- T* ptr = Handler::New(static_cast<Derived*>(this), std::forward<Args>(args)...);
- ptr->type_index_ = T::RuntimeTypeIndex();
- ptr->deleter_ = Handler::Deleter();
- return ObjectPtr<T>(ptr);
- }
-
- /*!
- * \tparam ArrayType The type to be allocated.
- * \tparam ElemType The type of array element.
- * \tparam Args The constructor signature.
- * \param num_elems The number of array elements.
- * \param args The arguments.
- */
- template <typename ArrayType, typename ElemType, typename... Args>
- inline ObjectPtr<ArrayType> make_inplace_array(size_t num_elems, Args&&... args) {
- using Handler = typename Derived::template ArrayHandler<ArrayType, ElemType>;
- static_assert(std::is_base_of<Object, ArrayType>::value,
- "make_inplace_array can only be used to create Object");
- ArrayType* ptr =
- Handler::New(static_cast<Derived*>(this), num_elems, std::forward<Args>(args)...);
- ptr->type_index_ = ArrayType::RuntimeTypeIndex();
- ptr->deleter_ = Handler::Deleter();
- return ObjectPtr<ArrayType>(ptr);
- }
-};
-
-// Simple allocator that uses new/delete.
-class SimpleObjAllocator : public ObjAllocatorBase<SimpleObjAllocator> {
- public:
- template <typename T>
- class Handler {
- public:
- using StorageType = typename std::aligned_storage<sizeof(T), alignof(T)>::type;
-
- template <typename... Args>
- static T* New(SimpleObjAllocator*, Args&&... args) {
- // NOTE: the first argument is not needed for SimpleObjAllocator
- // It is reserved for special allocators that needs to recycle
- // the object to itself (e.g. in the case of object pool).
- //
- // In the case of an object pool, an allocator needs to create
- // a special chunk memory that hides reference to the allocator
- // and call allocator's release function in the deleter.
-
- // NOTE2: Use inplace new to allocate
- // This is used to get rid of warning when deleting a virtual
- // class with non-virtual destructor.
- // We are fine here as we captured the right deleter during construction.
- // This is also the right way to get storage type for an object pool.
- StorageType* data = new StorageType();
- new (data) T(std::forward<Args>(args)...);
- return reinterpret_cast<T*>(data);
- }
-
- static Object::FDeleter Deleter() { return Deleter_; }
-
- private:
- static void Deleter_(Object* objptr) {
- // NOTE: this is important to cast back to T*
- // because objptr and tptr may not be the same
- // depending on how sub-class allocates the space.
- T* tptr = static_cast<T*>(objptr);
- // It is important to do tptr->T::~T(),
- // so that we explicitly call the specific destructor
- // instead of tptr->~T(), which could mean the intention
- // call a virtual destructor(which may not be available and is not required).
- tptr->T::~T();
- delete reinterpret_cast<StorageType*>(tptr);
- }
- };
-
- // Array handler that uses new/delete.
- template <typename ArrayType, typename ElemType>
- class ArrayHandler {
- public:
- using StorageType = typename std::aligned_storage<sizeof(ArrayType), alignof(ArrayType)>::type;
- // for now only support elements that aligns with array header.
- static_assert(alignof(ArrayType) % alignof(ElemType) == 0 &&
- sizeof(ArrayType) % alignof(ElemType) == 0,
- "element alignment constraint");
-
- template <typename... Args>
- static ArrayType* New(SimpleObjAllocator*, size_t num_elems, Args&&... args) {
- // NOTE: the first argument is not needed for ArrayObjAllocator
- // It is reserved for special allocators that needs to recycle
- // the object to itself (e.g. in the case of object pool).
- //
- // In the case of an object pool, an allocator needs to create
- // a special chunk memory that hides reference to the allocator
- // and call allocator's release function in the deleter.
- // NOTE2: Use inplace new to allocate
- // This is used to get rid of warning when deleting a virtual
- // class with non-virtual destructor.
- // We are fine here as we captured the right deleter during construction.
- // This is also the right way to get storage type for an object pool.
- size_t unit = sizeof(StorageType);
- size_t requested_size = num_elems * sizeof(ElemType) + sizeof(ArrayType);
- size_t num_storage_slots = (requested_size + unit - 1) / unit;
- StorageType* data = new StorageType[num_storage_slots];
- new (data) ArrayType(std::forward<Args>(args)...);
- return reinterpret_cast<ArrayType*>(data);
- }
-
- static Object::FDeleter Deleter() { return Deleter_; }
-
- private:
- static void Deleter_(Object* objptr) {
- // NOTE: this is important to cast back to ArrayType*
- // because objptr and tptr may not be the same
- // depending on how sub-class allocates the space.
- ArrayType* tptr = static_cast<ArrayType*>(objptr);
- // It is important to do tptr->ArrayType::~ArrayType(),
- // so that we explicitly call the specific destructor
- // instead of tptr->~ArrayType(), which could mean the intention
- // call a virtual destructor(which may not be available and is not required).
- tptr->ArrayType::~ArrayType();
- StorageType* p = reinterpret_cast<StorageType*>(tptr);
- delete[] p;
- }
- };
-};
-
-template <typename T, typename... Args>
-inline ObjectPtr<T> make_object(Args&&... args) {
- return SimpleObjAllocator().make_object<T>(std::forward<Args>(args)...);
-}
-template <typename ArrayType, typename ElemType, typename... Args>
-inline ObjectPtr<ArrayType> make_inplace_array_object(size_t num_elems, Args&&... args) {
- return SimpleObjAllocator().make_inplace_array<ArrayType, ElemType>(num_elems,
- std::forward<Args>(args)...);
-}
+using namespace tvm::ffi;
} // namespace runtime
} // namespace tvm
diff --git a/include/tvm/runtime/memory.h b/include/tvm/runtime/memory_backup.h
similarity index 100%
copy from include/tvm/runtime/memory.h
copy to include/tvm/runtime/memory_backup.h
diff --git a/include/tvm/runtime/object.h b/include/tvm/runtime/object.h
index 527d663c02..c2b3681bc4 100644
--- a/include/tvm/runtime/object.h
+++ b/include/tvm/runtime/object.h
@@ -23,588 +23,52 @@
#ifndef TVM_RUNTIME_OBJECT_H_
#define TVM_RUNTIME_OBJECT_H_
-#include <tvm/runtime/c_runtime_api.h>
-#include <tvm/runtime/logging.h>
#include <tvm/ffi/object.h>
-
-#include <string>
-#include <type_traits>
-#include <utility>
-
-/*!
- * \brief Whether or not use atomic reference counter.
- * If the reference counter is not atomic,
- * an object cannot be owned by multiple threads.
- * We can, however, move an object across threads
- */
-#ifndef TVM_OBJECT_ATOMIC_REF_COUNTER
-#define TVM_OBJECT_ATOMIC_REF_COUNTER 1
-#endif
-
-#if TVM_OBJECT_ATOMIC_REF_COUNTER
-#include <atomic>
-#endif // TVM_OBJECT_ATOMIC_REF_COUNTER
+#include <tvm/ffi/cast.h>
+#include <tvm/ffi/reflection.h>
namespace tvm {
namespace runtime {
+using namespace tvm::ffi;
+
/*!
* \brief Namespace for the list of type index.
* \note Use struct so that we have to use TypeIndex::ENumName to refer to
* the constant, but still able to use enum.
*/
-struct TypeIndex {
- enum {
- /*! \brief Root object type. */
- kRoot = 0,
+enum TypeIndex : int32_t {
// Standard static index assignments,
// Frontends can take benefit of these constants.
+ kRuntimeString = TVMFFITypeIndex::kTVMFFIStr,
+ kRuntimeMap = TVMFFITypeIndex::kTVMFFIMap,
+ kRuntimeArray = TVMFFITypeIndex::kTVMFFIArray,
/*! \brief runtime::Module. */
- kRuntimeModule = 1,
+ kRuntimeModule = TVMFFITypeIndex::kTVMFFIRuntimeModule,
/*! \brief runtime::NDArray. */
- kRuntimeNDArray = 2,
- /*! \brief runtime::String. */
- kRuntimeString = 3,
- /*! \brief runtime::Array. */
- kRuntimeArray = 4,
- /*! \brief runtime::Map. */
- kRuntimeMap = 5,
+ kRuntimeNDArray = TVMFFITypeIndex::kTVMFFINDArray,
/*! \brief runtime::ShapeTuple. */
- kRuntimeShapeTuple = 6,
+ kRuntimeShapeTuple = TVMFFITypeIndex::kTVMFFIShapeTuple,
+ // Extra builtin static index here
+ kCustomStaticIndex = TVMFFITypeIndex::kTVMFFIStaticObjectEnd,
/*! \brief runtime::PackedFunc. */
- kRuntimePackedFunc = 7,
+ kRuntimePackedFunc = kCustomStaticIndex + 1,
/*! \brief runtime::DRef for disco distributed runtime */
- kRuntimeDiscoDRef = 8,
+ kRuntimeDiscoDRef = kCustomStaticIndex + 2,
/*! \brief runtime::RPCObjectRef */
- kRuntimeRPCObjectRef = 9,
+ kRuntimeRPCObjectRef = kCustomStaticIndex + 3,
// static assignments that may subject to change.
- kStaticIndexEnd = 10,
- /*!
- * \brief Type index is allocated during runtime, keeping it as
- * constant for now to ensure compatibility across versions
- */
- kDynamic = 12
- };
-}; // namespace TypeIndex
-
-/*!
- * \brief base class of all object containers.
- *
- * Sub-class of objects should declare the following static constexpr fields:
- *
- * - _type_index:
- * Static type index of the object, if assigned to TypeIndex::kDynamic
- * the type index will be assigned during runtime.
- * Runtime type index can be accessed by ObjectType::TypeIndex();
- * - _type_key:
- * The unique string identifier of the type.
- * - _type_final:
- * Whether the type is terminal type(there is no subclass of the type in the object system).
- * This field is automatically set by macro TVM_DECLARE_FINAL_OBJECT_INFO
- * It is still OK to sub-class a terminal object type T and construct it using make_object.
- * But IsInstance check will only show that the object type is T(instead of the sub-class).
- *
- * The following two fields are necessary for base classes that can be sub-classed.
- *
- * - _type_child_slots:
- * Number of reserved type index slots for child classes.
- * Used for runtime optimization for type checking in IsInstance.
- * If an object's type_index is within range of [type_index, type_index + _type_child_slots]
- * Then the object can be quickly decided as sub-class of the current object class.
- * If not, a fallback mechanism is used to check the global type table.
- * Recommendation: set to estimate number of children needed.
- * - _type_child_slots_can_overflow:
- * Whether we can add additional child classes even if the number of child classes
- * exceeds the _type_child_slots. A fallback mechanism to check global type table will be
- * used. Recommendation: set to false for optimal runtime speed if we know exact number of children.
- *
- * Two macros are used to declare helper functions in the object:
- * - Use TVM_DECLARE_BASE_OBJECT_INFO for object classes that can be sub-classed.
- * - Use TVM_DECLARE_FINAL_OBJECT_INFO for object classes that cannot be sub-classed.
- *
- * New objects can be created using make_object function.
- * Which will automatically populate the type_index and deleter of the object.
- *
- * \sa make_object
- * \sa ObjectPtr
- * \sa ObjectRef
- *
- * \code
- *
- * // Create a base object
- * class BaseObj : public Object {
- * public:
- * // object fields
- * int field0;
- *
- * // object properties
- * static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
- * static constexpr const char* _type_key = "test.BaseObj";
- * TVM_DECLARE_BASE_OBJECT_INFO(BaseObj, Object);
- * };
- *
- * class LeafObj : public BaseObj {
- * public:
- * // fields
- * int child_field0;
- * // object properties
- * static constexpr const uint32_t _type_index = TypeIndex::kDynamic;
- * static constexpr const char* _type_key = "test.LeafObj";
- * TVM_DECLARE_BASE_OBJECT_INFO(LeafObj, Object);
- * };
- *
- * // The following code should be put into a cc file.
- * TVM_REGISTER_OBJECT_TYPE(BaseObj);
- * TVM_REGISTER_OBJECT_TYPE(LeafObj);
- *
- * // Usage example.
- * void TestObjects() {
- * // create an object
- * ObjectRef leaf_ref(make_object<LeafObj>());
- * // cast to a specific instance
- * const LeafObj* leaf_ptr = leaf_ref.as<LeafObj>();
- * ICHECK(leaf_ptr != nullptr);
- * // can also cast to the base class.
- * ICHECK(leaf_ref.as<BaseObj>() != nullptr);
- * }
- *
- * \endcode
- */
-class TVM_DLL Object {
- public:
- /*!
- * \brief Object deleter
- * \param self pointer to the Object.
- */
- typedef void (*FDeleter)(Object* self);
- /*! \return The internal runtime type index of the object. */
- uint32_t type_index() const { return type_index_; }
- /*!
- * \return the type key of the object.
- * \note this operation is expensive, can be used for error reporting.
- */
- std::string GetTypeKey() const { return TypeIndex2Key(type_index_); }
- /*!
- * \return A hash value of the return of GetTypeKey.
- */
- size_t GetTypeKeyHash() const { return TypeIndex2KeyHash(type_index_); }
- /*!
- * Check if the object is an instance of TargetType.
- * \tparam TargetType The target type to be checked.
- * \return Whether the target type is true.
- */
- template <typename TargetType>
- inline bool IsInstance() const;
- /*!
- * \return Whether the cell has only one reference
- * \note We use stl style naming to be consistent with known API in shared_ptr.
- */
- inline bool unique() const;
- /*!
- * \brief Get the type key of the corresponding index from runtime.
- * \param tindex The type index.
- * \return the result.
- */
- static std::string TypeIndex2Key(uint32_t tindex);
- /*!
- * \brief Get the type key hash of the corresponding index from runtime.
- * \param tindex The type index.
- * \return the related key-hash.
- */
- static size_t TypeIndex2KeyHash(uint32_t tindex);
- /*!
- * \brief Get the type index of the corresponding key from runtime.
- * \param key The type key.
- * \return the result.
- */
- static uint32_t TypeKey2Index(const std::string& key);
-
-#if TVM_OBJECT_ATOMIC_REF_COUNTER
- using RefCounterType = std::atomic<int32_t>;
-#else
- using RefCounterType = int32_t;
-#endif
-
- static constexpr const char* _type_key = "runtime.Object";
-
- static uint32_t _GetOrAllocRuntimeTypeIndex() { return TypeIndex::kRoot; }
- static uint32_t RuntimeTypeIndex() { return TypeIndex::kRoot; }
-
- // Default object type properties for sub-classes
- static constexpr bool _type_final = false;
- static constexpr uint32_t _type_child_slots = 0;
- static constexpr bool _type_child_slots_can_overflow = true;
- // member information
- static constexpr bool _type_has_method_visit_attrs = true;
- static constexpr bool _type_has_method_sequal_reduce = false;
- static constexpr bool _type_has_method_shash_reduce = false;
- // NOTE: the following field is not type index of Object
- // but was intended to be used by sub-classes as default value.
- // The type index of Object is TypeIndex::kRoot
- static constexpr uint32_t _type_index = TypeIndex::kDynamic;
-
- // Default constructor and copy constructor
- Object() {}
- // Override the copy and assign constructors to do nothing.
- // This is to make sure only contents, but not deleter and ref_counter
- // are copied when a child class copies itself.
- // This will enable us to use make_object<ObjectClass>(*obj_ptr)
- // to copy an existing object.
- Object(const Object& other) { // NOLINT(*)
- }
- Object(Object&& other) { // NOLINT(*)
- }
- Object& operator=(const Object& other) { // NOLINT(*)
- return *this;
- }
- Object& operator=(Object&& other) { // NOLINT(*)
- return *this;
- }
-
- protected:
- // The fields of the base object cell.
- /*! \brief Type index(tag) that indicates the type of the object. */
- uint32_t type_index_{0};
- /*! \brief The internal reference counter */
- RefCounterType ref_counter_{0};
- /*!
- * \brief deleter of this object to enable customized allocation.
- * If the deleter is nullptr, no deletion will be performed.
- * The creator of the object must always set the deleter field properly.
- */
- FDeleter deleter_ = nullptr;
- // Invariant checks.
- static_assert(sizeof(int32_t) == sizeof(RefCounterType) &&
- alignof(int32_t) == sizeof(RefCounterType),
- "RefCounter ABI check.");
-
- /*!
- * \brief Get the type index using type key.
- *
- * When the function is first time called for a type,
- * it will register the type to the type table in the runtime.
- * If the static_tindex is TypeIndex::kDynamic, the function will
- * allocate a runtime type index.
- * Otherwise, we will populate the type table and return the static index.
- *
- * \param key the type key.
- * \param static_tindex The current _type_index field.
- * can be TypeIndex::kDynamic.
- * \param parent_tindex The index of the parent.
- * \param type_child_slots Number of slots reserved for its children.
- * \param type_child_slots_can_overflow Whether to allow child to overflow the slots.
- * \return The allocated type index.
- */
- static uint32_t GetOrAllocRuntimeTypeIndex(const std::string& key, uint32_t static_tindex,
- uint32_t parent_tindex, uint32_t type_child_slots,
- bool type_child_slots_can_overflow);
-
- // reference counter related operations
- /*! \brief developer function, increases reference counter. */
- inline void IncRef();
- /*!
- * \brief developer function, decrease reference counter.
- * \note The deleter will be called when ref_counter_ becomes zero.
- */
- inline void DecRef();
-
- private:
- /*!
- * \return The usage count of the cell.
- * \note We use stl style naming to be consistent with known API in shared_ptr.
- */
- inline int use_count() const;
- /*!
- * \brief Check of this object is derived from the parent.
- * \param parent_tindex The parent type index.
- * \return The derivation results.
- */
- bool DerivedFrom(uint32_t parent_tindex) const;
- // friend classes
- template <typename>
- friend class ObjAllocatorBase;
- template <typename>
- friend class ObjectPtr;
- friend class TVMRetValue;
- friend class ObjectInternal;
-};
-
-/*!
- * \brief Get a reference type from a raw object ptr type
- *
- * It is always important to get a reference type
- * if we want to return a value as reference or keep
- * the object alive beyond the scope of the function.
- *
- * \param ptr The object pointer
- * \tparam RefType The reference type
- * \tparam ObjectType The object type
- * \return The corresponding RefType
- */
-template <typename ObjectRefType, typename ObjectType>
-inline ObjectRefType GetRef(const ObjectType* ptr);
-
-/*!
- * \brief Downcast a base reference type to a more specific type.
- *
- * \param ref The input reference
- * \return The corresponding SubRef.
- * \tparam SubRef The target specific reference type.
- * \tparam BaseRef the current reference type.
- */
-template <typename SubRef, typename BaseRef>
-inline SubRef Downcast(BaseRef ref);
-
-/*!
- * \brief A custom smart pointer for Object.
- * \tparam T the content data type.
- * \sa make_object
- */
-template <typename T>
-class ObjectPtr {
- public:
- /*! \brief default constructor */
- ObjectPtr() {}
- /*! \brief default constructor */
- ObjectPtr(std::nullptr_t) {} // NOLINT(*)
- /*!
- * \brief copy constructor
- * \param other The value to be moved
- */
- ObjectPtr(const ObjectPtr<T>& other) // NOLINT(*)
- : ObjectPtr(other.data_) {}
- /*!
- * \brief copy constructor
- * \param other The value to be moved
- */
- template <typename U>
- ObjectPtr(const ObjectPtr<U>& other) // NOLINT(*)
- : ObjectPtr(other.data_) {
- static_assert(std::is_base_of<T, U>::value,
- "can only assign of child class ObjectPtr to parent");
- }
- /*!
- * \brief move constructor
- * \param other The value to be moved
- */
- ObjectPtr(ObjectPtr<T>&& other) // NOLINT(*)
- : data_(other.data_) {
- other.data_ = nullptr;
- }
- /*!
- * \brief move constructor
- * \param other The value to be moved
- */
- template <typename Y>
- ObjectPtr(ObjectPtr<Y>&& other) // NOLINT(*)
- : data_(other.data_) {
- static_assert(std::is_base_of<T, Y>::value,
- "can only assign of child class ObjectPtr to parent");
- other.data_ = nullptr;
- }
- /*! \brief destructor */
- ~ObjectPtr() { this->reset(); }
- /*!
- * \brief Swap this array with another Object
- * \param other The other Object
- */
- void swap(ObjectPtr<T>& other) { // NOLINT(*)
- std::swap(data_, other.data_);
- }
- /*!
- * \return Get the content of the pointer
- */
- T* get() const { return static_cast<T*>(data_); }
- /*!
- * \return The pointer
- */
- T* operator->() const { return get(); }
- /*!
- * \return The reference
- */
- T& operator*() const { // NOLINT(*)
- return *get();
- }
- /*!
- * \brief copy assignment
- * \param other The value to be assigned.
- * \return reference to self.
- */
- ObjectPtr<T>& operator=(const ObjectPtr<T>& other) { // NOLINT(*)
- // takes in plane operator to enable copy elison.
- // copy-and-swap idiom
- ObjectPtr(other).swap(*this); // NOLINT(*)
- return *this;
- }
- /*!
- * \brief move assignment
- * \param other The value to be assigned.
- * \return reference to self.
- */
- ObjectPtr<T>& operator=(ObjectPtr<T>&& other) { // NOLINT(*)
- // copy-and-swap idiom
- ObjectPtr(std::move(other)).swap(*this); // NOLINT(*)
- return *this;
- }
- /*!
- * \brief nullptr check
- * \return result of comparison of internal pointer with nullptr.
- */
- explicit operator bool() const { return get() != nullptr; }
- /*! \brief reset the content of ptr to be nullptr */
- void reset() {
- if (data_ != nullptr) {
- data_->DecRef();
- data_ = nullptr;
- }
- }
- /*! \return The use count of the ptr, for debug purposes */
- int use_count() const { return data_ != nullptr ? data_->use_count() : 0; }
- /*! \return whether the reference is unique */
- bool unique() const { return data_ != nullptr && data_->use_count() == 1; }
- /*! \return Whether two ObjectPtr do not equal each other */
- bool operator==(const ObjectPtr<T>& other) const { return data_ == other.data_; }
- /*! \return Whether two ObjectPtr equals each other */
- bool operator!=(const ObjectPtr<T>& other) const { return data_ != other.data_; }
- /*! \return Whether the pointer is nullptr */
- bool operator==(std::nullptr_t null) const { return data_ == nullptr; }
- /*! \return Whether the pointer is not nullptr */
- bool operator!=(std::nullptr_t null) const { return data_ != nullptr; }
-
- private:
- /*! \brief internal pointer field */
- Object* data_{nullptr};
- /*!
- * \brief constructor from Object
- * \param data The data pointer
- */
- explicit ObjectPtr(Object* data) : data_(data) {
- if (data != nullptr) {
- data_->IncRef();
- }
- }
- /*!
- * \brief Move an ObjectPtr from an RValueRef argument.
- * \param ref The rvalue reference.
- * \return the moved result.
- */
- static ObjectPtr<T> MoveFromRValueRefArg(Object** ref) {
- ObjectPtr<T> ptr;
- ptr.data_ = *ref;
- *ref = nullptr;
- return ptr;
- }
- // friend classes
- friend class Object;
- friend class ObjectRef;
- friend struct ObjectPtrHash;
- template <typename>
- friend class ObjectPtr;
- template <typename>
- friend class ObjAllocatorBase;
- friend class TVMPODValue_;
- friend class TVMArgsSetter;
- friend class TVMRetValue;
- friend class TVMArgValue;
- friend class TVMMovableArgValue_;
- template <typename ObjectRefType, typename ObjType>
- friend ObjectRefType GetRef(const ObjType* ptr);
- template <typename BaseType, typename ObjType>
- friend ObjectPtr<BaseType> GetObjectPtr(ObjType* ptr);
+ kStaticIndexEnd
};
-// Forward declaration, to prevent circular includes.
-template <typename T>
-class Optional;
-
-/*! \brief Base class of all object reference */
-class ObjectRef {
+class ObjectRef : public tvm::ffi::ObjectRef {
public:
/*! \brief default constructor */
ObjectRef() = default;
/*! \brief Constructor from existing object ptr */
- explicit ObjectRef(ObjectPtr<Object> data) : data_(data) {}
- /*!
- * \brief Comparator
- * \param other Another object ref.
- * \return the compare result.
- */
- bool same_as(const ObjectRef& other) const { return data_ == other.data_; }
- /*!
- * \brief Comparator
- * \param other Another object ref.
- * \return the compare result.
- */
- bool operator==(const ObjectRef& other) const { return data_ == other.data_; }
- /*!
- * \brief Comparator
- * \param other Another object ref.
- * \return the compare result.
- */
- bool operator!=(const ObjectRef& other) const { return data_ != other.data_; }
- /*!
- * \brief Comparator
- * \param other Another object ref by address.
- * \return the compare result.
- */
- bool operator<(const ObjectRef& other) const { return data_.get() < other.data_.get(); }
- /*!
- * \return whether the object is defined(not null).
- */
- bool defined() const { return data_ != nullptr; }
- /*! \return the internal object pointer */
- const Object* get() const { return data_.get(); }
- /*! \return the internal object pointer */
- const Object* operator->() const { return get(); }
- /*! \return whether the reference is unique */
- bool unique() const { return data_.unique(); }
- /*! \return The use count of the ptr, for debug purposes */
- int use_count() const { return data_.use_count(); }
-
- /*!
- * \brief Try to downcast the internal Object to a
- * raw pointer of a corresponding type.
- *
- * The function will return a nullptr if the cast failed.
- *
- * if (const AddNode *ptr = node_ref.as<AddNode>()) {
- * // This is an add node
- * }
- *
- * \tparam ObjectType the target type, must be a subtype of Object
- */
- template <typename ObjectType, typename = std::enable_if_t<std::is_base_of_v<Object, ObjectType>>>
- inline const ObjectType* as() const;
-
- /*!
- * \brief Try to downcast the ObjectRef to a
- * Optional<T> of the requested type.
- *
- * The function will return a NullOpt if the cast failed.
- *
- * if (Optional<Add> opt = node_ref.as<Add>()) {
- * // This is an add node
- * }
- *
- * \note While this method is declared in <tvm/runtime/object.h>,
- * the implementation is in <tvm/runtime/container/optional.h> to
- * prevent circular includes. This additional include file is only
- * required in compilation units that uses this method.
- *
- * \tparam ObjectRefType the target type, must be a subtype of ObjectRef
- */
- template <typename ObjectRefType,
- typename = std::enable_if_t<std::is_base_of_v<ObjectRef, ObjectRefType>>>
- inline Optional<ObjectRefType> as() const;
-
- /*! \brief type indicate the container type. */
- using ContainerType = Object;
- // Default type properties for the reference class.
- static constexpr bool _type_is_nullable = true;
+ explicit ObjectRef(ObjectPtr<Object> data) : tvm::ffi::ObjectRef(data) {}
protected:
- /*! \brief Internal pointer that backs the reference. */
- ObjectPtr<Object> data_;
/*! \return return a mutable internal ptr, can be used by sub-classes. */
Object* get_mutable() const { return data_.get(); }
/*!
@@ -622,7 +86,9 @@ class ObjectRef {
* after we successfully moved the field.
* \param ref The reference data.
*/
- static void FFIClearAfterMove(ObjectRef* ref) { ref->data_.data_ = nullptr; }
+ static void FFIClearAfterMove(ObjectRef* ref) {
+ details::ObjectUnsafe::LegacyClearObjectPtrAfterMove(ref);
+ }
/*!
* \brief Internal helper function get data_ as ObjectPtr of ObjectType.
* \note only used for internal dev purpose.
@@ -638,316 +104,11 @@ class ObjectRef {
friend class TVMRetValue;
friend class TVMArgsSetter;
friend class ObjectInternal;
- template <typename SubRef, typename BaseRef>
- friend SubRef Downcast(BaseRef ref);
-};
-
-/*!
- * \brief Get an object ptr type from a raw object ptr.
- *
- * \param ptr The object pointer
- * \tparam BaseType The reference type
- * \tparam ObjectType The object type
- * \return The corresponding RefType
- */
-template <typename BaseType, typename ObjectType>
-inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
-
-/*! \brief ObjectRef hash functor */
-struct ObjectPtrHash {
- size_t operator()(const ObjectRef& a) const { return operator()(a.data_); }
-
- template <typename T>
- size_t operator()(const ObjectPtr<T>& a) const {
- return std::hash<Object*>()(a.get());
- }
};
-/*! \brief ObjectRef equal functor */
-struct ObjectPtrEqual {
- bool operator()(const ObjectRef& a, const ObjectRef& b) const { return a.same_as(b); }
-
- template <typename T>
- size_t operator()(const ObjectPtr<T>& a, const ObjectPtr<T>& b) const {
- return a == b;
- }
-};
-
-/*!
- * \brief helper macro to declare a base object type that can be inherited.
- * \param TypeName The name of the current type.
- * \param ParentType The name of the ParentType
- */
-#define TVM_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType) \
- static_assert(!ParentType::_type_final, "ParentObj marked as final"); \
- static uint32_t RuntimeTypeIndex() { \
- static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
- TypeName::_type_child_slots < ParentType::_type_child_slots, \
- "Need to set _type_child_slots when parent specifies it."); \
- if (TypeName::_type_index != ::tvm::runtime::TypeIndex::kDynamic) { \
- return TypeName::_type_index; \
- } \
- return _GetOrAllocRuntimeTypeIndex(); \
- } \
- static uint32_t _GetOrAllocRuntimeTypeIndex() { \
- static uint32_t tindex = Object::GetOrAllocRuntimeTypeIndex( \
- TypeName::_type_key, TypeName::_type_index, ParentType::_GetOrAllocRuntimeTypeIndex(), \
- TypeName::_type_child_slots, TypeName::_type_child_slots_can_overflow); \
- return tindex; \
- }
-
-/*!
- * \brief helper macro to declare type information in a final class.
- * \param TypeName The name of the current type.
- * \param ParentType The name of the ParentType
- */
-#define TVM_DECLARE_FINAL_OBJECT_INFO(TypeName, ParentType) \
- static const constexpr bool _type_final = true; \
- static const constexpr int _type_child_slots = 0; \
- TVM_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType)
-
-/*! \brief helper macro to suppress unused warning */
-#if defined(__GNUC__)
-#define TVM_ATTRIBUTE_UNUSED __attribute__((unused))
-#else
-#define TVM_ATTRIBUTE_UNUSED
-#endif
-
-#define TVM_STR_CONCAT_(__x, __y) __x##__y
-#define TVM_STR_CONCAT(__x, __y) TVM_STR_CONCAT_(__x, __y)
-
-#define TVM_OBJECT_REG_VAR_DEF static TVM_ATTRIBUTE_UNUSED uint32_t __make_Object_tid
-
-/*!
- * \brief Helper macro to register the object type to runtime.
- * Makes sure that the runtime type table is correctly populated.
- *
- * Use this macro in the cc file for each terminal class.
- */
-#define TVM_REGISTER_OBJECT_TYPE(TypeName) \
- TVM_STR_CONCAT(TVM_OBJECT_REG_VAR_DEF, __COUNTER__) = TypeName::_GetOrAllocRuntimeTypeIndex()
-
-/*
- * \brief Define the default copy/move constructor and assign operator
- * \param TypeName The class typename.
- */
-#define TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName) \
- TypeName(const TypeName& other) = default; \
- TypeName(TypeName&& other) = default; \
- TypeName& operator=(const TypeName& other) = default; \
- TypeName& operator=(TypeName&& other) = default;
-
-/*
- * \brief Define object reference methods.
- * \param TypeName The object type name
- * \param ParentType The parent type of the objectref
- * \param ObjectName The type name of the object.
- */
-#define TVM_DEFINE_OBJECT_REF_METHODS_WITHOUT_DEFAULT_CONSTRUCTOR(TypeName, ParentType, \
- ObjectName) \
- explicit TypeName(::tvm::runtime::ObjectPtr<::tvm::runtime::Object> n) : ParentType(n) {} \
- TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName); \
- const ObjectName* operator->() const { return static_cast<const ObjectName*>(data_.get()); } \
- const ObjectName* get() const { return operator->(); } \
- using ContainerType = ObjectName;
-
-/*
- * \brief Define object reference methods.
- * \param TypeName The object type name
- * \param ParentType The parent type of the objectref
- * \param ObjectName The type name of the object.
- */
-#define TVM_DEFINE_OBJECT_REF_METHODS(TypeName, ParentType, ObjectName) \
- TypeName() = default; \
- TVM_DEFINE_OBJECT_REF_METHODS_WITHOUT_DEFAULT_CONSTRUCTOR(TypeName, ParentType, ObjectName)
-
-/*
- * \brief Define object reference methods that is not nullable.
- *
- * \param TypeName The object type name
- * \param ParentType The parent type of the objectref
- * \param ObjectName The type name of the object.
- */
-#define TVM_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(TypeName, ParentType, ObjectName) \
- explicit TypeName(::tvm::runtime::ObjectPtr<::tvm::runtime::Object> n) : ParentType(n) {} \
- TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName); \
- const ObjectName* operator->() const { return static_cast<const ObjectName*>(data_.get()); } \
- const ObjectName* get() const { return operator->(); } \
- static constexpr bool _type_is_nullable = false; \
- using ContainerType = ObjectName;
-
-/*
- * \brief Define object reference methods of whose content is mutable.
- * \param TypeName The object type name
- * \param ParentType The parent type of the objectref
- * \param ObjectName The type name of the object.
- * \note We recommend making objects immutable when possible.
- * This macro is only reserved for objects that stores runtime states.
- */
-#define TVM_DEFINE_MUTABLE_OBJECT_REF_METHODS(TypeName, ParentType, ObjectName) \
- TypeName() = default; \
- TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName); \
- explicit TypeName(::tvm::runtime::ObjectPtr<::tvm::runtime::Object> n) : ParentType(n) {} \
- ObjectName* operator->() const { return static_cast<ObjectName*>(data_.get()); } \
- using ContainerType = ObjectName;
-
-/*
- * \brief Define object reference methods that is both not nullable and mutable.
- *
- * \param TypeName The object type name
- * \param ParentType The parent type of the objectref
- * \param ObjectName The type name of the object.
- */
-#define TVM_DEFINE_MUTABLE_NOTNULLABLE_OBJECT_REF_METHODS(TypeName, ParentType, ObjectName) \
- explicit TypeName(::tvm::runtime::ObjectPtr<::tvm::runtime::Object> n) : ParentType(n) {} \
- TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName); \
- ObjectName* operator->() const { return static_cast<ObjectName*>(data_.get()); } \
- ObjectName* get() const { return operator->(); } \
- static constexpr bool _type_is_nullable = false; \
- using ContainerType = ObjectName;
-
-/*!
- * \brief Define CopyOnWrite function in an ObjectRef.
- * \param ObjectName The Type of the Node.
- *
- * CopyOnWrite will generate a unique copy of the internal node.
- * The node will be copied if it is referenced by multiple places.
- * The function returns the raw pointer to the node to allow modification
- * of the content.
- *
- * \code
- *
- * MyCOWObjectRef ref, ref2;
- * ref2 = ref;
- * ref.CopyOnWrite()->value = new_value;
- * assert(ref2->value == old_value);
- * assert(ref->value == new_value);
- *
- * \endcode
- */
-#define TVM_DEFINE_OBJECT_REF_COW_METHOD(ObjectName) \
- static_assert(ObjectName::_type_final, \
- "TVM's CopyOnWrite may only be used for " \
- "Object types that are declared as final, " \
- "using the TVM_DECLARE_FINAL_OBJECT_INFO macro."); \
- ObjectName* CopyOnWrite() { \
- ICHECK(data_ != nullptr); \
- if (!data_.unique()) { \
- auto n = make_object<ObjectName>(*(operator->())); \
- ObjectPtr<Object>(std::move(n)).swap(data_); \
- } \
- return static_cast<ObjectName*>(data_.get()); \
- }
-
-// Implementations details below
-// Object reference counting.
-#if TVM_OBJECT_ATOMIC_REF_COUNTER
-
-inline void Object::IncRef() { ref_counter_.fetch_add(1, std::memory_order_relaxed); }
-
-inline void Object::DecRef() {
- if (ref_counter_.fetch_sub(1, std::memory_order_release) == 1) {
- std::atomic_thread_fence(std::memory_order_acquire);
- if (this->deleter_ != nullptr) {
- (*this->deleter_)(this);
- }
- }
-}
-
-inline int Object::use_count() const { return ref_counter_.load(std::memory_order_relaxed); }
-
-#else
-
-inline void Object::IncRef() { ++ref_counter_; }
-
-inline void Object::DecRef() {
- if (--ref_counter_ == 0) {
- if (this->deleter_ != nullptr) {
- (*this->deleter_)(this);
- }
- }
-}
-
-inline int Object::use_count() const { return ref_counter_; }
-
-#endif // TVM_OBJECT_ATOMIC_REF_COUNTER
-
-template <typename TargetType>
-inline bool Object::IsInstance() const {
- const Object* self = this;
- // NOTE: the following code can be optimized by
- // compiler dead-code elimination for already known constants.
- if (self != nullptr) {
- // Everything is a subclass of object.
- if (std::is_same<TargetType, Object>::value) return true;
- if (TargetType::_type_final) {
- // if the target type is a final type
- // then we only need to check the equivalence.
- return self->type_index_ == TargetType::RuntimeTypeIndex();
- } else {
- // if target type is a non-leaf type
- // Check if type index falls into the range of reserved slots.
- uint32_t begin = TargetType::RuntimeTypeIndex();
- // The condition will be optimized by constant-folding.
- if (TargetType::_type_child_slots != 0) {
- uint32_t end = begin + TargetType::_type_child_slots;
- if (self->type_index_ >= begin && self->type_index_ < end) return true;
- } else {
- if (self->type_index_ == begin) return true;
- }
- if (!TargetType::_type_child_slots_can_overflow) return false;
- // Invariance: parent index is always smaller than the child.
- if (self->type_index_ < TargetType::RuntimeTypeIndex()) return false;
- // The rare slower-path, check type hierarchy.
- return self->DerivedFrom(TargetType::RuntimeTypeIndex());
- }
- } else {
- return false;
- }
-}
-
-inline bool Object::unique() const { return use_count() == 1; }
-
-template <typename ObjectType, typename>
-inline const ObjectType* ObjectRef::as() const {
- if (data_ != nullptr && data_->IsInstance<ObjectType>()) {
- return static_cast<ObjectType*>(data_.get());
- } else {
- return nullptr;
- }
-}
-
-template <typename RefType, typename ObjType>
-inline RefType GetRef(const ObjType* ptr) {
- static_assert(std::is_base_of<typename RefType::ContainerType, ObjType>::value,
- "Can only cast to the ref of same container type");
- if (!RefType::_type_is_nullable) {
- ICHECK(ptr != nullptr);
- }
- return RefType(ObjectPtr<Object>(const_cast<Object*>(static_cast<const Object*>(ptr))));
-}
-
-template <typename BaseType, typename ObjType>
-inline ObjectPtr<BaseType> GetObjectPtr(ObjType* ptr) {
- static_assert(std::is_base_of<BaseType, ObjType>::value,
- "Can only cast to the ref of same container type");
- return ObjectPtr<BaseType>(static_cast<Object*>(ptr));
-}
-
-template <typename SubRef, typename BaseRef>
-inline SubRef Downcast(BaseRef ref) {
- if (ref.defined()) {
- ICHECK(ref->template IsInstance<typename SubRef::ContainerType>())
- << "Downcast from " << ref->GetTypeKey() << " to " << SubRef::ContainerType::_type_key
- << " failed.";
- } else {
- ICHECK(SubRef::_type_is_nullable) << "Downcast from nullptr to not nullable reference of "
- << SubRef::ContainerType::_type_key;
- }
- return SubRef(std::move(ref.data_));
-}
+#define TVM_DECLARE_FINAL_OBJECT_INFO TVM_FFI_DECLARE_FINAL_OBJECT_INFO
+#define TVM_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS TVM_FFI_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS
} // namespace runtime
} // namespace tvm
-
#endif // TVM_RUNTIME_OBJECT_H_
diff --git a/include/tvm/runtime/object.h b/include/tvm/runtime/object_backup.h
similarity index 99%
copy from include/tvm/runtime/object.h
copy to include/tvm/runtime/object_backup.h
index 527d663c02..ea31fcac48 100644
--- a/include/tvm/runtime/object.h
+++ b/include/tvm/runtime/object_backup.h
@@ -25,7 +25,6 @@
#include <tvm/runtime/c_runtime_api.h>
#include <tvm/runtime/logging.h>
-#include <tvm/ffi/object.h>
#include <string>
#include <type_traits>
@@ -78,12 +77,11 @@ struct TypeIndex {
/*! \brief runtime::RPCObjectRef */
kRuntimeRPCObjectRef = 9,
// static assignments that may subject to change.
- kStaticIndexEnd = 10,
- /*!
- * \brief Type index is allocated during runtime, keeping it as
- * constant for now to ensure compatibility across versions
- */
- kDynamic = 12
+ kRuntimeClosure,
+ kRuntimeADT,
+ kStaticIndexEnd,
+ /*! \brief Type index is allocated during runtime. */
+ kDynamic = kStaticIndexEnd
};
}; // namespace TypeIndex
diff --git a/src/runtime/debug_compile.cc b/src/runtime/debug_compile.cc
new file mode 100644
index 0000000000..5f9f6b10d1
--- /dev/null
+++ b/src/runtime/debug_compile.cc
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+/*!
+ * \file src/runtime/debug_compile.cc
+ * \brief File used for debug migration
+ */
+#include <tvm/runtime/container/string.h>
+
+namespace tvm {
+namespace debug {
+
+String Test() {
+ String value = "xyz";
+ return value;
+}
+
+}
+}
\ No newline at end of file
(tvm) 05/38: [FFI] Add libbacktrace support
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit d3280ddb40bd0bfb6f3e6a8e5fb3d309d16a8863
Author: tqchen <ti...@gmail.com>
AuthorDate: Tue Aug 13 08:35:18 2024 -0400
[FFI] Add libbacktrace support
Co-authored-by: Junru Shao <ju...@apache.org>
---
ffi/CMakeLists.txt | 102 ++++++++-----
ffi/cmake/Utils/AddLibbacktrace.cmake | 52 +++++++
ffi/cmake/Utils/Library.cmake | 28 ++++
ffi/include/tvm/ffi/{c_ffi_abi.h => c_api.h} | 39 ++---
ffi/include/tvm/ffi/c_ffi_api.h | 60 --------
ffi/include/tvm/ffi/error.h | 162 ++++++++++++++++++--
ffi/include/tvm/ffi/internal_utils.h | 15 +-
ffi/include/tvm/ffi/object.h | 138 ++++++++++--------
ffi/scripts/run_tests.sh | 4 +-
ffi/src/ffi/object.cc | 211 +++++++++++++++++++++++++++
ffi/src/ffi/registry.cc | 0
ffi/src/ffi/traceback.cc | 176 ++++++++++++++++++++++
ffi/src/ffi/traceback.h | 127 ++++++++++++++++
ffi/tests/example/CMakeLists.txt | 4 +
ffi/tests/example/test_c_ffi_abi.cc | 2 +-
ffi/tests/example/test_error.cc | 19 ++-
16 files changed, 945 insertions(+), 194 deletions(-)
diff --git a/ffi/CMakeLists.txt b/ffi/CMakeLists.txt
index 532922bf14..7c830f8a0f 100644
--- a/ffi/CMakeLists.txt
+++ b/ffi/CMakeLists.txt
@@ -4,53 +4,34 @@ project(
tvm_ffi
VERSION 1.0
DESCRIPTION "TVM's FFI system"
- LANGUAGES CXX
+ LANGUAGES CXX C
)
-option(TVM_FFI_ALLOW_DYN_TYPE "Support for objects with non-static type indices. When turned on, targets linked against `tvm_ffi` will allow objects that comes with non-pre-defined type indices, so that the object hierarchy could expand without limitation. This will require the downstream targets to link against target `tvm_ffi_registry` to be effective." OFF)
option(TVM_FFI_BUILD_TESTS "Adding test targets." OFF)
+########## NOTE: all options below are related to dynamic registry #####
+option(TVM_FFI_BUILD_REGISTRY
+ "Support for objects with non-static type indices. When turned on, \
+ targets linked against `tvm_ffi` will allow objects that comes with non-pre-defined type indices, \
+ as well as getting full stacktrace during debugging. \
+ so that the object hierarchy could expand without limitation. \
+ This will require the downstream targets to link against target `tvm_ffi_registry` to be effective."
+ OFF
+)
+option(TVM_FFI_USE_LIBBRACKTRACE "Enable libbacktrace" ON)
+option(TVM_FFI_BACKTRACE_ON_SEGFAULT "Set signal handler to print traceback on segfault" ON)
+option(TVM_FFI_ALLOW_DYN_TYPE "Wehthert to allow dynamic features" ON)
+
include(cmake/Utils/CxxWarning.cmake)
include(cmake/Utils/Sanitizer.cmake)
+include(cmake/Utils/Library.cmake)
+include(cmake/Utils/AddLibbacktrace.cmake)
########## Target: `dlpack_header` ##########
add_library(dlpack_header INTERFACE)
target_include_directories(dlpack_header INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/dlpack/include")
-########## Target: `tvm_ffi_registry_{objs|static|shared}` ##########
-
-add_library(
- tvm_ffi_registry_objs
- EXCLUDE_FROM_ALL
- OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/registry.cc"
-)
-set_target_properties(
- tvm_ffi_registry_objs PROPERTIES
- POSITION_INDEPENDENT_CODE ON
- CXX_STANDARD 17
- CXX_EXTENSIONS OFF
- CXX_VISIBILITY_PRESET hidden
- VISIBILITY_INLINES_HIDDEN ON
-)
-add_cxx_warning(tvm_ffi_registry_objs)
-target_link_libraries(tvm_ffi_registry_objs PRIVATE dlpack_header)
-target_include_directories(tvm_ffi_registry_objs PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
-target_compile_definitions(tvm_ffi_registry_objs PRIVATE TVM_FFI_ALLOW_DYN_TYPE=1)
-if (MSVC)
- target_compile_definitions(tvm_ffi_registry_objs PRIVATE TVM_FFI_EXPORTS)
-endif()
-
-add_library(tvm_ffi_registry_static EXCLUDE_FROM_ALL STATIC $<TARGET_OBJECTS:tvm_ffi_registry_objs>)
-add_library(tvm_ffi_registry_shared EXCLUDE_FROM_ALL SHARED $<TARGET_OBJECTS:tvm_ffi_registry_objs>)
-set_target_properties(tvm_ffi_registry_shared tvm_ffi_registry_static tvm_ffi_registry_objs
- PROPERTIES
- MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
- ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
- LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
- RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
-)
-
########## Target: `tvm_ffi` ##########
add_library(tvm_ffi INTERFACE)
@@ -58,14 +39,61 @@ target_link_libraries(tvm_ffi INTERFACE dlpack_header)
target_compile_features(tvm_ffi INTERFACE cxx_std_17)
target_include_directories(tvm_ffi INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
-if (TVM_FFI_ALLOW_DYN_TYPE)
+if (TVM_FFI_USE_LIBBRACKTRACE)
message(STATUS "Setting C++ macro TVM_FFI_ALLOW_DYN_TYPE - 1")
target_compile_definitions(tvm_ffi INTERFACE TVM_FFI_ALLOW_DYN_TYPE=1)
else()
- message(STATUS "Setting C++ macro TVM_FFI_ALLOW_DYN_TYPE - 0")
+ message(STATUS "Setting C++ macro TVM_FFI_ALLOW_DYN_TYPES - 0")
target_compile_definitions(tvm_ffi INTERFACE TVM_FFI_ALLOW_DYN_TYPE=0)
endif()
+########## Target: `tvm_ffi_registry` ##########
+
+if (TVM_FFI_BUILD_REGISTRY)
+ add_library(tvm_ffi_registry_objs OBJECT
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/traceback.cc"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/object.cc"
+ )
+ set_target_properties(
+ tvm_ffi_registry_objs PROPERTIES
+ POSITION_INDEPENDENT_CODE ON
+ CXX_STANDARD 17
+ CXX_EXTENSIONS OFF
+ CXX_STANDARD_REQUIRED ON
+ CXX_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ PREFIX "lib"
+ )
+ add_cxx_warning(tvm_ffi_registry_objs)
+ target_link_libraries(tvm_ffi_registry_objs PRIVATE dlpack_header)
+ target_include_directories(tvm_ffi_registry_objs PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
+
+ if (TVM_FFI_USE_LIBBRACKTRACE)
+ message(STATUS "Setting C++ macro TVM_FFI_USE_LIBBRACKTRACE - 1")
+ target_compile_definitions(tvm_ffi_registry_objs PRIVATE TVM_FFI_USE_LIBBRACKTRACE=1)
+ else()
+ message(STATUS "Setting C++ macro TVM_FFI_USE_LIBBRACKTRACE - 0")
+ target_compile_definitions(tvm_ffi_registry_objs PRIVATE TVM_FFI_USE_LIBBRACKTRACE=0)
+ endif()
+
+ if (TVM_FFI_BACKTRACE_ON_SEGFAULT)
+ message(STATUS "Setting C++ macro TVM_FFI_BACKTRACE_ON_SEGFAULT - 1")
+ target_compile_definitions(tvm_ffi_registry_objs PRIVATE TVM_FFI_BACKTRACE_ON_SEGFAULT=1)
+ else()
+ message(STATUS "Setting C++ macro TVM_FFI_BACKTRACE_ON_SEGFAULT - 0")
+ target_compile_definitions(tvm_ffi_registry_objs PRIVATE TVM_FFI_BACKTRACE_ON_SEGFAULT=0)
+ endif()
+
+ add_target_from_obj(tvm_ffi_registry tvm_ffi_registry_objs)
+ if (TARGET libbacktrace)
+ target_link_libraries(tvm_ffi_registry_objs PRIVATE libbacktrace)
+ target_link_libraries(tvm_ffi_registry_shared PRIVATE libbacktrace)
+ target_link_libraries(tvm_ffi_registry_static PRIVATE libbacktrace)
+ endif ()
+ install(TARGETS tvm_ffi_registry_static DESTINATION "lib/")
+ install(TARGETS tvm_ffi_registry_shared DESTINATION "lib/")
+endif (TVM_FFI_BUILD_REGISTRY)
+
########## Adding tests ##########
if (${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME})
diff --git a/ffi/cmake/Utils/AddLibbacktrace.cmake b/ffi/cmake/Utils/AddLibbacktrace.cmake
new file mode 100644
index 0000000000..a837ca7b3f
--- /dev/null
+++ b/ffi/cmake/Utils/AddLibbacktrace.cmake
@@ -0,0 +1,52 @@
+include(ExternalProject)
+
+function(_libbacktrace_compile)
+ set(_libbacktrace_source ${CMAKE_CURRENT_LIST_DIR}/../../../3rdparty/libbacktrace)
+ set(_libbacktrace_prefix ${CMAKE_CURRENT_BINARY_DIR}/libbacktrace)
+ if(CMAKE_SYSTEM_NAME MATCHES "Darwin" AND (CMAKE_C_COMPILER MATCHES "^/Library" OR CMAKE_C_COMPILER MATCHES "^/Applications"))
+ set(_cmake_c_compiler "/usr/bin/cc")
+ else()
+ set(_cmake_c_compiler "${CMAKE_C_COMPILER}")
+ endif()
+
+ message(STATUS CMAKC_C_COMPILER="${CMAKE_C_COMPILER}")
+
+ file(MAKE_DIRECTORY ${_libbacktrace_prefix}/include)
+ file(MAKE_DIRECTORY ${_libbacktrace_prefix}/lib)
+ ExternalProject_Add(project_libbacktrace
+ PREFIX libbacktrace
+ SOURCE_DIR ${_libbacktrace_source}
+ BINARY_DIR ${_libbacktrace_prefix}
+ CONFIGURE_COMMAND
+ ${_libbacktrace_source}/configure
+ "--prefix=${_libbacktrace_prefix}"
+ "--with-pic"
+ "CC=${_cmake_c_compiler}"
+ "CPP=${_cmake_c_compiler} -E"
+ "CFLAGS=${CMAKE_C_FLAGS}"
+ "LDFLAGS=${CMAKE_EXE_LINKER_FLAGS}"
+ "NM=${CMAKE_NM}"
+ "STRIP=${CMAKE_STRIP}"
+ "--host=${MACHINE_NAME}"
+ BUILD_COMMAND make -j
+ BUILD_BYPRODUCTS ${_libbacktrace_prefix}/lib/libbacktrace.a ${_libbacktrace_prefix}/include/backtrace.h
+ INSTALL_DIR ${_libbacktrace_prefix}
+ INSTALL_COMMAND make install
+ LOG_CONFIGURE ON
+ LOG_INSTALL ON
+ LOG_BUILD ON
+ LOG_OUTPUT_ON_FAILURE ON
+ )
+ ExternalProject_Add_Step(project_libbacktrace checkout DEPENDERS configure DEPENDEES download)
+ set_target_properties(project_libbacktrace PROPERTIES EXCLUDE_FROM_ALL TRUE)
+ add_library(libbacktrace STATIC IMPORTED)
+ add_dependencies(libbacktrace project_libbacktrace)
+ set_target_properties(libbacktrace PROPERTIES
+ IMPORTED_LOCATION ${_libbacktrace_prefix}/lib/libbacktrace.a
+ INTERFACE_INCLUDE_DIRECTORIES ${_libbacktrace_prefix}/include
+ )
+endfunction()
+
+if(NOT MSVC)
+ _libbacktrace_compile()
+endif()
diff --git a/ffi/cmake/Utils/Library.cmake b/ffi/cmake/Utils/Library.cmake
new file mode 100644
index 0000000000..fe8c48a52c
--- /dev/null
+++ b/ffi/cmake/Utils/Library.cmake
@@ -0,0 +1,28 @@
+function(add_target_from_obj target_name obj_target_name)
+ add_library(${target_name}_static STATIC $<TARGET_OBJECTS:${obj_target_name}>)
+ set_target_properties(
+ ${target_name}_static PROPERTIES
+ OUTPUT_NAME "${target_name}_static"
+ PREFIX "lib"
+ ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ )
+ add_library(${target_name}_shared SHARED $<TARGET_OBJECTS:${obj_target_name}>)
+ set_target_properties(
+ ${target_name}_shared PROPERTIES
+ OUTPUT_NAME "${target_name}"
+ PREFIX "lib"
+ ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ )
+ add_custom_target(${target_name})
+ add_dependencies(${target_name} ${target_name}_static ${target_name}_shared)
+ if (MSVC)
+ target_compile_definitions(${obj_target_name} PRIVATE TVM_FFI_EXPORTS)
+ set_target_properties(
+ ${obj_target_name} ${target_name}_shared ${target_name}_static
+ PROPERTIES
+ MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
+ )
+ endif()
+endfunction()
diff --git a/ffi/include/tvm/ffi/c_ffi_abi.h b/ffi/include/tvm/ffi/c_api.h
similarity index 85%
rename from ffi/include/tvm/ffi/c_ffi_abi.h
rename to ffi/include/tvm/ffi/c_api.h
index 34d25bf17b..2616ae1648 100644
--- a/ffi/include/tvm/ffi/c_ffi_abi.h
+++ b/ffi/include/tvm/ffi/c_api.h
@@ -18,28 +18,40 @@
*/
/*
- * \file tvm/ffi/c_ffi_abi.h
- * \brief This file defines the ABI convention of the FFI convention
- *
- * \note This file only include data structures that can be used in
- * a header only way. The APIs are defined in c_ffi_api.h
- * and requires linking to tvm_ffi library.
+ * \file tvm/ffi/c_api.h
+ * \brief This file defines the C convention of the FFI convention
*
* Only use the APIs when TVM_FFI_ALLOW_DYN_TYPE is set to true
*/
-#ifndef TVM_FFI_C_FFI_ABI_H_
-#define TVM_FFI_C_FFI_ABI_H_
+#ifndef TVM_FFI_C_API_H_
+#define TVM_FFI_C_API_H_
#include <dlpack/dlpack.h>
#include <stdint.h>
/*!
* \brief Macro defines whether we enable dynamic runtime features.
+ * \note Turning this one would mean that we need to link tvm_ffi_registry_shared
*/
#ifndef TVM_FFI_ALLOW_DYN_TYPE
#define TVM_FFI_ALLOW_DYN_TYPE 1
#endif
+#if !defined(TVM_FFI_DLL) && defined(__EMSCRIPTEN__)
+#include <emscripten/emscripten.h>
+#define TVM_FFI_DLL EMSCRIPTEN_KEEPALIVE
+#endif
+#if !defined(TVM_FFI_DLL) && defined(_MSC_VER)
+#ifdef TVM_FFI_EXPORTS
+#define TVM_FFI_DLL __declspec(dllexport)
+#else
+#define TVM_FFI_DLL __declspec(dllimport)
+#endif
+#endif
+#ifndef TVM_FFI_DLL
+#define TVM_FFI_DLL __attribute__((visibility("default")))
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -130,16 +142,7 @@ typedef struct {
const char* bytes;
} TVMFFIByteArray;
-/*! \brief The error type. */
-typedef struct {
- /*! \brief header */
- TVMFFIObject header_;
-
-
-} TVMFFIError;
-
-
#ifdef __cplusplus
} // TVM_FFI_EXTERN_C
#endif
-#endif // TVM_FFI_C_FFI_ABI_H_
+#endif // TVM_FFI_C_API_H_
diff --git a/ffi/include/tvm/ffi/c_ffi_api.h b/ffi/include/tvm/ffi/c_ffi_api.h
deleted file mode 100644
index dda54f0032..0000000000
--- a/ffi/include/tvm/ffi/c_ffi_api.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.
- */
-
-/*
- * \file tvm/ffi/c_ffi_abi.h
- * \brief This file defines the ABI convention of the FFI convention
- *
- * Including global calling conventions
- */
-#ifndef TVM_FFI_C_FFI_ABI_H_
-#define TVM_FFI_C_FFI_ABI_H_
-
-#include <tvm/ffi/c_ffi_abi.h>
-
-#if !defined(TVM_FFI_DLL) && defined(__EMSCRIPTEN__)
-#include <emscripten/emscripten.h>
-#define TVM_FFI_API EMSCRIPTEN_KEEPALIVE
-#endif
-#if !defined(TVM_FFI_DLL) && defined(_MSC_VER)
-#ifdef TVM_FFI_EXPORTS
-#define TVM_FFI_DLL __declspec(dllexport)
-#else
-#define TVM_FFI_DLL __declspec(dllimport)
-#endif
-#endif
-#ifndef TVM_FFI_DLL
-#define TVM_FFI_DLL __attribute__((visibility("default")))
-#endif
-
-#ifdef __cplusplus
-static_assert(
- TVM_FFI_ALLOW_DYN_TYPE,
- "Only include c_ffi_abi when TVM_FFI_ALLOW_DYN_TYPE is set to true"
-);
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef __cplusplus
-} // TVM_FFI_EXTERN_C
-#endif
-#endif // TVM_FFI_C_FFI_ABI_H_
diff --git a/ffi/include/tvm/ffi/error.h b/ffi/include/tvm/ffi/error.h
index 7b40f00bf9..250206ac8b 100644
--- a/ffi/include/tvm/ffi/error.h
+++ b/ffi/include/tvm/ffi/error.h
@@ -24,11 +24,30 @@
#ifndef TVM_FFI_ERROR_H_
#define TVM_FFI_ERROR_H_
-#include <tvm/ffi/object.h>
+#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/internal_utils.h>
#include <tvm/ffi/memory.h>
+#include <tvm/ffi/object.h>
-#include <string>
+#include <iostream>
+#include <memory>
#include <sstream>
+#include <string>
+
+/*!
+ * \brief Macro defines whether we enable libbacktrace
+ */
+#ifndef TVM_FFI_USE_LIBBACKTRACE
+#define TVM_FFI_USE_LIBBACKTRACE 1
+#endif
+
+/*!
+ * \brief Macro defines whether to install signal handler
+ * and print backtrace during segfault
+ */
+#ifndef TVM_FFI_BACKTRACE_ON_SEGFAULT
+#define TVM_FFI_BACKTRACE_ON_SEGFAULT 1
+#endif
namespace tvm {
namespace ffi {
@@ -83,41 +102,152 @@ namespace details {
class ErrorBuilder {
public:
- explicit ErrorBuilder(const char* kind, const char* filename, const char* func, int32_t lineno)
- : kind_(kind) {
- std::ostringstream backtrace;
- // python style backtrace
- backtrace << " " << filename << ", line " << lineno << ", in " << func << '\n';
- backtrace_ = backtrace.str();
- }
+ explicit ErrorBuilder(std::string kind, std::string traceback, bool log_before_throw)
+ : kind_(kind), traceback_(traceback), log_before_throw_(log_before_throw) {}
// MSVC disable warning in error builder as it is exepected
#ifdef _MSC_VER
#pragma disagnostic push
#pragma warning(disable : 4722)
#endif
+ // avoid inline to reduce binary size, error throw path do not need to be fast
[[noreturn]] ~ErrorBuilder() noexcept(false) {
- throw ::tvm::ffi::Error(std::move(kind_), message_.str(), std::move(backtrace_));
+ ::tvm::ffi::Error error(std::move(kind_), stream_.str(), std::move(traceback_));
+ if (log_before_throw_) {
+ std::cerr << error.what();
+ }
+ throw error;
}
#ifdef _MSC_VER
#pragma disagnostic pop
#endif
- std::ostringstream &Get() { return message_; }
+ std::ostringstream& stream() { return stream_; }
-protected:
+ protected:
std::string kind_;
- std::ostringstream message_;
- std::string backtrace_;
+ std::ostringstream stream_;
+ std::string traceback_;
+ bool log_before_throw_;
};
+
+// Code section that depends on dynamic components that requires linking
+#if TVM_FFI_ALLOW_DYN_TYPE
+/*!
+ * \brief Get stack traceback in a string.
+ * \param filaname The current file name.
+ * \param func The current function
+ * \param lineno The current line number
+ * \return The traceback string
+ *
+ * \note filename func and lino are only used as a backup info, most cases they are not needed.
+ * The return value is set to const char* to be more compatible across dll boundaries.
+ */
+TVM_FFI_DLL const char* Traceback(const char* filename, const char* func, int lineno);
+// define traceback here as call into traceback function
+#define TVM_FFI_TRACEBACK_HERE ::tvm::ffi::details::Traceback(__FILE__, TVM_FFI_FUNC_SIG, __LINE__)
+
+#else
+// simple traceback when allow dyn type is set to false
+inline std::string SimpleTraceback(const char* filename, const char* func, int lineno) {
+ std::ostringstream traceback;
+ // python style backtrace
+ traceback << " " << filename << ", line " << lineno << ", in " << func << '\n';
+ return traceback.str();
+}
+#define TVM_FFI_TRACEBACK_HERE \
+ ::tvm::ffi::details::SimpleTraceback(__FILE__, TVM_FFI_FUNC_SIG, __LINE__)
+#endif // TVM_FFI_ALLOW_DYN_TYPE
} // namespace details
/*!
- * \brief Helper macro to throw an error with backtrace and message
+ * \brief Helper macro to throw an error with traceback and message
+ *
+ * \code
+ *
+ * void ThrowError() {
+ * TVM_FFI_THROW(RuntimeError) << "error message";
+ * }
+ *
+ * \endcode
*/
#define TVM_FFI_THROW(ErrorKind) \
- ::tvm::ffi::details::ErrorBuilder(#ErrorKind, __FILE__, TVM_FFI_FUNC_SIG, __LINE__).Get()
+ ::tvm::ffi::details::ErrorBuilder(#ErrorKind, TVM_FFI_TRACEBACK_HERE, false).stream()
+
+/*!
+ * \brief Explicitly log error in stderr and then throw the error.
+ *
+ * \note This is only necessary on startup functions where we know error
+ * cannot be caught, and it is better to have a clear log message.
+ * In most cases, we should use use TVM_FFI_THROW.
+ */
+#define TVM_FFI_LOG_AND_THROW(ErrorKind) \
+ ::tvm::ffi::details::ErrorBuilder(#ErrorKind, TVM_FFI_TRACEBACK_HERE, true).stream()
+
+// Glog style checks with TVM_FFI prefix
+// NOTE: we explicitly avoid glog style generic macros (LOG/CHECK) in tvm ffi
+// to avoid potential conflict of downstream users who might have their own GLOG style macros
+namespace details {
+
+template <typename X, typename Y>
+TVM_FFI_INLINE std::unique_ptr<std::string> LogCheckFormat(const X& x, const Y& y) {
+ std::ostringstream os;
+ os << " (" << x << " vs. " << y << ") "; // CHECK_XX(x, y) requires x and y can be serialized to
+ // string. Use CHECK(x OP y) otherwise.
+ return std::make_unique<std::string>(os.str());
+}
+
+#define TVM_FFI_CHECK_FUNC(name, op) \
+ template <typename X, typename Y> \
+ TVM_FFI_INLINE std::unique_ptr<std::string> LogCheck##name(const X& x, const Y& y) { \
+ if (x op y) return nullptr; \
+ return LogCheckFormat(x, y); \
+ } \
+ TVM_FFI_INLINE std::unique_ptr<std::string> LogCheck##name(int x, int y) { \
+ return LogCheck##name<int, int>(x, y); \
+ }
+
+// Inline _Pragma in macros does not work reliably on old version of MSVC and
+// GCC. We wrap all comparisons in a function so that we can use #pragma to
+// silence bad comparison warnings.
+#if defined(__GNUC__) || defined(__clang__) // GCC and Clang
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-compare"
+#elif defined(_MSC_VER) // MSVC
+#pragma warning(push)
+#pragma warning(disable : 4389) // '==' : signed/unsigned mismatch
+#endif
+
+TVM_FFI_CHECK_FUNC(_LT, <)
+TVM_FFI_CHECK_FUNC(_GT, >)
+TVM_FFI_CHECK_FUNC(_LE, <=)
+TVM_FFI_CHECK_FUNC(_GE, >=)
+TVM_FFI_CHECK_FUNC(_EQ, ==)
+TVM_FFI_CHECK_FUNC(_NE, !=)
+
+#if defined(__GNUC__) || defined(__clang__) // GCC and Clang
+#pragma GCC diagnostic pop
+#elif defined(_MSC_VER) // MSVC
+#pragma warning(pop)
+#endif
+} // namespace details
+#define TVM_FFI_ICHECK_BINARY_OP(name, op, x, y) \
+ if (auto __tvm__log__err = ::tvm::ffi::details::LogCheck##name(x, y)) \
+ TVM_FFI_THROW(InternalError) << "Check failed: " << #x " " #op " " #y << *__tvm__log__err << ": "
+
+#define TVM_FFI_ICHECK(x) \
+ if (!(x)) TVM_FFI_THROW(InternalError) << "Check failed: (" #x << ") is false: "
+
+#define TVM_FFI_ICHECK_LT(x, y) TVM_FFI_ICHECK_BINARY_OP(_LT, <, x, y)
+#define TVM_FFI_ICHECK_GT(x, y) TVM_FFI_ICHECK_BINARY_OP(_GT, >, x, y)
+#define TVM_FFI_ICHECK_LE(x, y) TVM_FFI_ICHECK_BINARY_OP(_LE, <=, x, y)
+#define TVM_FFI_ICHECK_GE(x, y) TVM_FFI_ICHECK_BINARY_OP(_GE, >=, x, y)
+#define TVM_FFI_ICHECK_EQ(x, y) TVM_FFI_ICHECK_BINARY_OP(_EQ, ==, x, y)
+#define TVM_FFI_ICHECK_NE(x, y) TVM_FFI_ICHECK_BINARY_OP(_NE, !=, x, y)
+#define TVM_FFI_ICHECK_NOTNULL(x) \
+ ((x) == nullptr ? TVM_FFI_THROW(InternalError) << "Check not null: " #x << ' ', \
+ (x) : (x)) // NOLINT(*)
} // namespace ffi
} // namespace tvm
#endif // TVM_FFI_ERROR_H_
diff --git a/ffi/include/tvm/ffi/internal_utils.h b/ffi/include/tvm/ffi/internal_utils.h
index 5eca030028..900b7fd2e2 100644
--- a/ffi/include/tvm/ffi/internal_utils.h
+++ b/ffi/include/tvm/ffi/internal_utils.h
@@ -18,12 +18,12 @@
*/
/*!
* \file tvm/ffi/internal_utils.h
- * \brief Utility functions and macros for internal use, not meant for
+ * \brief Utility functions and macros for internal use
*/
#ifndef TVM_FFI_INTERNAL_UTILS_H_
#define TVM_FFI_INTERNAL_UTILS_H_
-#include <tvm/ffi/c_ffi_abi.h>
+#include <tvm/ffi/c_api.h>
#include <cstddef>
@@ -33,6 +33,17 @@
#define TVM_FFI_INLINE inline __attribute__((always_inline))
#endif
+/*!
+ * \brief Macro helper to force a function not to be inlined.
+ * It is only used in places that we know not inlining is good,
+ * e.g. some logging functions.
+ */
+#if defined(_MSC_VER)
+#define TVM_FFI_NO_INLINE __declspec(noinline)
+#else
+#define TVM_FFI_NO_INLINE __attribute__((noinline))
+#endif
+
#if defined(_MSC_VER)
#define TVM_FFI_UNREACHABLE() __assume(false)
#else
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 037b08bbca..03ba7d7dbf 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -23,9 +23,12 @@
#ifndef TVM_FFI_OBJECT_H_
#define TVM_FFI_OBJECT_H_
-#include <tvm/ffi/c_ffi_abi.h>
+#include <tvm/ffi/c_api.h>
#include <tvm/ffi/internal_utils.h>
+#include <type_traits>
+#include <utility>
+
namespace tvm {
namespace ffi {
@@ -65,8 +68,8 @@ struct ObjectInternal;
*
* - _type_child_slots_can_overflow:
* Whether we can add additional child classes even if the number of child classes
- * exceeds the _type_child_slots. A fallback mechanism to check global type table will be
- * used. Recommendation: set to false for optimal runtime speed if we know exact number of children.
+ * exceeds the _type_child_slots. A fallback mechanism to check type table will be used.
+ * Recommendation: set to false for optimal runtime speed if we know exact number of children.
*
* Two macros are used to declare helper functions in the object:
* - Use TVM_FFI_DECLARE_BASE_OBJECT_INFO for object classes that can be sub-classed.
@@ -96,7 +99,7 @@ class Object {
// NOTE: the following field is not type index of Object
// but was intended to be used by sub-classes as default value.
// The type index of Object is TypeIndex::kRoot
- static constexpr int32_t _type_index = TypeIndex::kTVMFFIDynObject;
+ static constexpr int32_t _type_index = TypeIndex::kTVMFFIObject;
// The following functions are provided by macro
// TVM_FFI_DECLARE_BASE_OBJECT_INFO and TVM_DECLARE_FINAL_OBJECT_INFO
@@ -264,17 +267,6 @@ class ObjectPtr {
data_->IncRef();
}
}
- /*!
- * \brief Move an ObjectPtr from an RValueRef argument.
- * \param ref The rvalue reference.
- * \return the moved result.
- */
- static ObjectPtr<T> MoveFromRValueRefArg(Object** ref) {
- ObjectPtr<T> ptr;
- ptr.data_ = *ref;
- *ref = nullptr;
- return ptr;
- }
// friend classes
friend class Object;
friend class ObjectRef;
@@ -380,56 +372,45 @@ class ObjectRef {
template <typename BaseType, typename ObjectType>
inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
-/*! \brief ObjectRef hash functor */
-struct ObjectPtrHash {
- size_t operator()(const ObjectRef& a) const { return operator()(a.data_); }
-
- template <typename T>
- size_t operator()(const ObjectPtr<T>& a) const {
- return std::hash<Object*>()(a.get());
- }
-};
-
-/*! \brief ObjectRef equal functor */
-struct ObjectPtrEqual {
- bool operator()(const ObjectRef& a, const ObjectRef& b) const { return a.same_as(b); }
-
- template <typename T>
- size_t operator()(const ObjectPtr<T>& a, const ObjectPtr<T>& b) const {
- return a == b;
- }
-};
-
/*!
- * \brief helper macro to declare a base object type that can be inherited.
+ * \brief Helper macro to declare list of static checks about object meta-data.
* \param TypeName The name of the current type.
* \param ParentType The name of the ParentType
*/
-#define TVM_FFI_DECLARE_STATIC_OBJECT_INFO(TypeName, ParentType) \
- static_assert(!ParentType::_type_final, "ParentObj marked as final"); \
- static int32_t RuntimeTypeIndex() { \
- static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
- TypeName::_type_child_slots < ParentType::_type_child_slots, \
- "Need to set _type_child_slots when parent specifies it."); \
- static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
- TypeName::_type_child_slots < ParentType::_type_child_slots, \
- "Need to set _type_child_slots when parent specifies it."); \
- static_assert(TypeName::_type_index != TypeIndex::kTVMFFIDynObject, \
- "Static object cannot have dynamic type index."); \
- return TypeName::_type_index; \
- }
+#define TVM_FFI_OBJECT_STATIC_CHECKS(TypeName, ParentType) \
+ static_assert(!ParentType::_type_final, "ParentType marked as final"); \
+ static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
+ TypeName::_type_child_slots < ParentType::_type_child_slots, \
+ "Need to set _type_child_slots when parent specifies it."); \
+ static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
+ TypeName::_type_child_slots < ParentType::_type_child_slots, \
+ "Need to set _type_child_slots when parent specifies it.");
-#define TVM_FFI_OBJECT_REG_VAR_DEF static TVM_ATTRIBUTE_UNUSED uint32_t __make_Object_tid
+/*!
+ * \brief Helper macro to declare a object that comes with static type index.
+ * \param TypeName The name of the current type.
+ * \param ParentType The name of the ParentType
+ */
+#define TVM_FFI_DECLARE_STATIC_OBJECT_INFO(TypeName, ParentType) \
+ TVM_FFI_OBJECT_STATIC_CHECKS(TypeName, ParentType) \
+ static int32_t RuntimeTypeIndex() { return TypeName::_type_index; }
/*!
- * \brief Helper macro to register the object type to runtime.
- * Makes sure that the runtime type table is correctly populated.
- *
- * Use this macro in the cc file for each terminal class.
+ * \brief helper macro to declare a base object type that can be inherited.
+ * \param TypeName The name of the current type.
+ * \param ParentType The name of the ParentType
*/
-#define TVM_FFI_REGISTER_OBJECT_TYPE(TypeName) \
- TVM_FFI_STR_CONCAT(TVM_FFI_OBJECT_REG_VAR_DEF, __COUNTER__) = \
- TypeName::_GetOrAllocRuntimeTypeIndex()
+#define TVM_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType) \
+ static_assert(TVM_FFI_ALLOW_DYN_TYPE, \
+ "Dynamic object depend on TVM_FFI_ALLOW_DYN_TYPE cd set to 1"); \
+ TVM_FFI_OBJECT_STATIC_CHECKS(TypaName, ParentType) \
+ static inline int32_t _type_index = _GetOrAllocRuntimeTypeIndex(); \
+ static int32_t RuntimeTypeIndex() { return TypeName::_type_index; } \
+ static int32_t _GetOrAllocRuntimeTypeIndex() { \
+ return ::tvm::ffi::details::ObjectGetOrAllocTypeIndex( \
+ TypeName::_type_key, -1, ParentType::_GetOrAllocRuntimeTypeIndex(), \
+ TypeName::_type_child_slots, TypeName::_type_child_slots_can_overflow); \
+ }
/*
* \brief Define object reference methods.
@@ -472,12 +453,53 @@ struct ObjectInternal {
return &(src->header_);
}
- // create ObjectPtr from unknowned ptr
+ // Create ObjectPtr from unknowned ptr
template <typename T>
static TVM_FFI_INLINE ObjectPtr<T> ObjectPtrFromUnowned(Object* raw_ptr) {
return tvm::ffi::ObjectPtr<T>(raw_ptr);
}
+ // Create objectptr by moving from an existing address of object and setting its
+ // address to nullptr
+ template <typename T>
+ static TVM_FFI_INLINE ObjectPtr<T> MoveObjectPtrFromRValueRef(Object** ref) {
+ ObjectPtr<T> ptr;
+ ptr.data_ = *ref;
+ *ref = nullptr;
+ return ptr;
+ }
};
+
+// Code section that depends on dynamic components
+#if TVM_FFI_ALLOW_DYN_TYPE
+/*!
+ * \brief Get the type index using type key.
+ *
+ * When the function is first time called for a type,
+ * it will register the type to the type table in the runtime.
+ * If the static_tindex is TypeIndex::kDynamic, the function will
+ * allocate a runtime type index.
+ * Otherwise, we will populate the type table and return the static index.
+ *
+ * \param type_key the type key.
+ * \param static_tindex Static type index if any, can be -1, which means this is a dynamic index
+ * \param parent_tindex The index of the parent.
+ * \param type_child_slots Number of slots reserved for its children.
+ * \param type_child_slots_can_overflow Whether to allow child to overflow the slots.
+ *
+ * \return The allocated type index
+ */
+TVM_FFI_DLL int ObjectGetOrAllocTypeIndex(const char* type_key, int32_t static_tindex,
+ int32_t parent_tindex, int32_t type_child_slots,
+ bool type_child_slots_can_overflow);
+
+/*!
+ * \brief Check whether child type is derived from parent type.
+ * \param child_type_index The candidate child type index.
+ * \param parent_type_index The candidate parent type index.
+ * \return the Check result.
+ */
+TVM_FFI_DLL bool ObjectDerivedFrom(int32_t child_type_index, int32_t parent_type_index);
+#endif // TVM_FFI_ALLOW_DYN_TYPE
} // namespace details
} // namespace ffi
} // namespace tvm
diff --git a/ffi/scripts/run_tests.sh b/ffi/scripts/run_tests.sh
index 704abaeab9..0d4efe6bf2 100755
--- a/ffi/scripts/run_tests.sh
+++ b/ffi/scripts/run_tests.sh
@@ -5,6 +5,8 @@ HEADER_ONLY=OFF
BUILD_TYPE=RelWithDebInfo
rm -rf build/CMakeFiles build/CMakeCache.txt
-cmake -G Ninja -S . -B build -DTVM_FFI_ALLOW_DYN_TYPE=${HEADER_ONLY} -DTVM_FFI_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
+cmake -G Ninja -S . -B build -DTVM_FFI_ALLOW_DYN_TYPE=${HEADER_ONLY} -DTVM_FFI_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
+ -DTVM_FFI_BUILD_REGISTRY=ON \
+ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
cmake --build build --parallel 16 --clean-first --config ${BUILD_TYPE} --target tvm_ffi_tests
GTEST_COLOR=1 ctest -V -C ${BUILD_TYPE} --test-dir build --output-on-failure
diff --git a/ffi/src/ffi/object.cc b/ffi/src/ffi/object.cc
new file mode 100644
index 0000000000..ac8e04ebb9
--- /dev/null
+++ b/ffi/src/ffi/object.cc
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+/*
+ * \file src/ffi/object.cc
+ * \brief Registry to record dynamic types
+ */
+#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/error.h>
+
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+namespace tvm {
+namespace ffi {
+
+/*! \brief Type information */
+struct TypeInfo {
+ /*! \brief The current index. */
+ int32_t index{0};
+ /*! \brief Index of the parent in the type hierarchy */
+ int32_t parent_index{0};
+ // NOTE: the indices in [index, index + num_reserved_slots) are
+ // reserved for the child-class of this type.
+ /*! \brief Total number of slots reserved for the type and its children. */
+ int32_t num_slots{0};
+ /*! \brief number of allocated child slots. */
+ int32_t allocated_slots{0};
+ /*! \brief Whether child can overflow. */
+ bool child_slots_can_overflow{true};
+ /*! \brief name of the type. */
+ std::string name;
+ /*! \brief hash of the name */
+ size_t name_hash{0};
+};
+
+/*!
+ * \brief Type context that manages the type hierarchy information.
+ *
+ * \note We do not use mutex to guard updating of TypeContext
+ *
+ * The assumption is that updating of TypeContext will be done
+ * in the main thread during initialization or loading.
+ *
+ * Then the followup code will leverage the information
+ */
+class TypeContext {
+ public:
+ // NOTE: this is a relatively slow path for child checking
+ // Most types are already checked by the fast-path via reserved slot checking.
+ bool DerivedFrom(int32_t child_tindex, int32_t parent_tindex) {
+ // invariance: child's type index is always bigger than its parent.
+ if (child_tindex < parent_tindex) return false;
+ if (child_tindex == parent_tindex) return true;
+ TVM_FFI_ICHECK_LT(child_tindex, type_table_.size());
+ while (child_tindex > parent_tindex) {
+ child_tindex = type_table_[child_tindex].parent_index;
+ }
+ return child_tindex == parent_tindex;
+ }
+
+ int32_t GetOrAllocRuntimeTypeIndex(const std::string& skey, int32_t static_tindex,
+ int32_t parent_tindex, int32_t num_child_slots,
+ bool child_slots_can_overflow) {
+ auto it = type_key2index_.find(skey);
+ if (it != type_key2index_.end()) {
+ return it->second;
+ }
+ // try to allocate from parent's type table.
+ TVM_FFI_ICHECK_LT(parent_tindex, type_table_.size())
+ << " skey=" << skey << ", static_index=" << static_tindex;
+
+ TypeInfo& pinfo = type_table_[parent_tindex];
+ TVM_FFI_ICHECK_EQ(pinfo.index, parent_tindex);
+
+ // if parent cannot overflow, then this class cannot.
+ if (!pinfo.child_slots_can_overflow) {
+ child_slots_can_overflow = false;
+ }
+
+ // total number of slots include the type itself.
+ int32_t num_slots = num_child_slots + 1;
+ int32_t allocated_tindex;
+
+ if (static_tindex > 0) {
+ // statically assigned type
+ allocated_tindex = static_tindex;
+ TVM_FFI_ICHECK_LT(static_tindex, type_table_.size());
+ TVM_FFI_ICHECK_EQ(type_table_[allocated_tindex].allocated_slots, 0U)
+ << "Conflicting static index " << static_tindex << " between "
+ << type_table_[allocated_tindex].name << " and " << skey;
+ } else if (pinfo.allocated_slots + num_slots <= pinfo.num_slots) {
+ // allocate the slot from parent's reserved pool
+ allocated_tindex = parent_tindex + pinfo.allocated_slots;
+ // update parent's state
+ pinfo.allocated_slots += num_slots;
+ } else {
+ TVM_FFI_ICHECK(pinfo.child_slots_can_overflow)
+ << "Reach maximum number of sub-classes for " << pinfo.name;
+ // allocate new entries.
+ allocated_tindex = type_counter_;
+ type_counter_ += num_slots;
+ TVM_FFI_ICHECK_LE(type_table_.size(), type_counter_);
+ type_table_.resize(type_counter_, TypeInfo());
+ }
+ TVM_FFI_ICHECK_GT(allocated_tindex, parent_tindex);
+ // initialize the slot.
+ type_table_[allocated_tindex].index = allocated_tindex;
+ type_table_[allocated_tindex].parent_index = parent_tindex;
+ type_table_[allocated_tindex].num_slots = num_slots;
+ type_table_[allocated_tindex].allocated_slots = 1;
+ type_table_[allocated_tindex].child_slots_can_overflow = child_slots_can_overflow;
+ type_table_[allocated_tindex].name = skey;
+ type_table_[allocated_tindex].name_hash = std::hash<std::string>()(skey);
+ // update the key2index mapping.
+ type_key2index_[skey] = allocated_tindex;
+ return allocated_tindex;
+ }
+
+ const std::string& TypeIndex2Key(int32_t tindex) {
+ if (tindex != 0) {
+ // always return the right type key for root
+ // for non-root type nodes, allocated slots should not equal 0
+ TVM_FFI_ICHECK(tindex < static_cast<int32_t>(type_table_.size()) &&
+ type_table_[tindex].allocated_slots != 0)
+ << "Unknown type index " << tindex;
+ }
+ return type_table_[tindex].name;
+ }
+
+ size_t TypeIndex2KeyHash(int32_t tindex) {
+ TVM_FFI_ICHECK(tindex < static_cast<int32_t>(type_table_.size()) &&
+ type_table_[tindex].allocated_slots != 0)
+ << "Unknown type index " << tindex;
+ return type_table_[tindex].name_hash;
+ }
+
+ int32_t TypeKey2Index(const std::string& skey) {
+ auto it = type_key2index_.find(skey);
+ TVM_FFI_ICHECK(it != type_key2index_.end()) << "Cannot find type " << skey;
+ return it->second;
+ }
+
+ void Dump(int min_children_count) {
+ std::vector<int> num_children(type_table_.size(), 0);
+ // reverse accumulation so we can get total counts in a bottom-up manner.
+ for (auto it = type_table_.rbegin(); it != type_table_.rend(); ++it) {
+ if (it->index != 0) {
+ num_children[it->parent_index] += num_children[it->index] + 1;
+ }
+ }
+
+ for (const auto& info : type_table_) {
+ if (info.index != 0 && num_children[info.index] >= min_children_count) {
+ std::cerr << '[' << info.index << "] " << info.name
+ << "\tparent=" << type_table_[info.parent_index].name
+ << "\tnum_child_slots=" << info.num_slots - 1
+ << "\tnum_children=" << num_children[info.index] << std::endl;
+ }
+ }
+ }
+
+ static TypeContext* Global() {
+ static TypeContext inst;
+ return &inst;
+ }
+
+ private:
+ TypeContext() {
+ type_table_.resize(TypeIndex::kTVMFFIDynObjectBegin, TypeInfo());
+ type_table_[0].name = "runtime.Object";
+ }
+
+ int32_t type_counter_{TypeIndex::kTVMFFIDynObjectBegin};
+ std::vector<TypeInfo> type_table_;
+ std::unordered_map<std::string, int32_t> type_key2index_;
+};
+
+namespace details {
+
+int32_t ObjectGetOrAllocTypeIndex(const char* type_key, int32_t static_tindex,
+ int32_t parent_tindex, int32_t type_child_slots,
+ bool type_child_slots_can_overflow) {
+ return tvm::ffi::TypeContext::Global()->GetOrAllocRuntimeTypeIndex(
+ type_key, static_tindex, parent_tindex, type_child_slots, type_child_slots_can_overflow != 0);
+}
+
+bool ObjectDerivedFrom(int32_t child_type_index, int32_t parent_type_index) {
+ return static_cast<int>(
+ tvm::ffi::TypeContext::Global()->DerivedFrom(child_type_index, parent_type_index));
+}
+} // namespace details
+} // namespace ffi
+} // namespace tvm
diff --git a/ffi/src/ffi/registry.cc b/ffi/src/ffi/registry.cc
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/ffi/src/ffi/traceback.cc b/ffi/src/ffi/traceback.cc
new file mode 100644
index 0000000000..f2826957a2
--- /dev/null
+++ b/ffi/src/ffi/traceback.cc
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+/*!
+ * \file traceback.cc
+ * \brief Traceback implementation on non-windows platforms
+ * \note We use the term "traceback" to be consistent with python naming convention.
+ */
+#ifndef _MSC_VER
+
+#include "./traceback.h"
+
+#include <tvm/ffi/c_api.h>
+#include <tvm/ffi/error.h>
+
+#if TVM_FFI_USE_LIBBACKTRACE
+
+#include <backtrace.h>
+#include <cxxabi.h>
+
+#include <cstring>
+#include <iomanip>
+#include <iostream>
+#include <mutex>
+
+#if TVM_FFI_BACKTRACE_ON_SEGFAULT
+#include <csignal>
+#endif
+
+namespace tvm {
+namespace ffi {
+namespace {
+
+void BacktraceCreateErrorCallback(void*, const char* msg, int) {
+ std::cerr << "Could not initialize backtrace state: " << msg << std::endl;
+}
+
+backtrace_state* BacktraceCreate() {
+ return backtrace_create_state(nullptr, 1, BacktraceCreateErrorCallback, nullptr);
+}
+
+static backtrace_state* _bt_state = BacktraceCreate();
+
+std::string DemangleName(std::string name) {
+ int status = 0;
+ size_t length = name.size();
+ std::unique_ptr<char, void (*)(void* __ptr)> demangled_name = {
+ abi::__cxa_demangle(name.c_str(), nullptr, &length, &status), &std::free};
+ if (demangled_name && status == 0 && length > 0) {
+ return demangled_name.get();
+ } else {
+ return name;
+ }
+}
+
+void BacktraceErrorCallback(void*, const char*, int) {
+ // do nothing
+}
+
+void BacktraceSyminfoCallback(void* data, uintptr_t pc, const char* symname, uintptr_t,
+ uintptr_t symsize) {
+ auto str = reinterpret_cast<std::string*>(data);
+
+ if (symname != nullptr) {
+ std::string tmp(symname, symsize);
+ *str = DemangleName(tmp.c_str());
+ } else {
+ std::ostringstream s;
+ s << "0x" << std::setfill('0') << std::setw(sizeof(uintptr_t) * 2) << std::hex << pc;
+ *str = s.str();
+ }
+}
+
+int BacktraceFullCallback(void* data, uintptr_t pc, const char* filename, int lineno,
+ const char* symbol) {
+ auto stack_trace = reinterpret_cast<TracebackStorage*>(data);
+ std::string symbol_str = "<unknown>";
+ if (symbol) {
+ symbol_str = DemangleName(symbol);
+ } else {
+ // see if syminfo gives anything
+ backtrace_syminfo(_bt_state, pc, BacktraceSyminfoCallback, BacktraceErrorCallback, &symbol_str);
+ }
+ symbol = symbol_str.data();
+
+ if (stack_trace->ExceedTracebackLimit()) {
+ return 1;
+ }
+ if (ShouldExcludeFrame(filename, symbol)) {
+ return 0;
+ }
+
+ stack_trace->Append(filename, symbol, lineno);
+ return 0;
+}
+
+std::string Traceback() {
+ TracebackStorage traceback;
+
+ if (_bt_state == nullptr) {
+ return "";
+ }
+ // libbacktrace eats memory if run on multiple threads at the same time, so we guard against it
+ {
+ static std::mutex m;
+ std::lock_guard<std::mutex> lock(m);
+ backtrace_full(_bt_state, 0, BacktraceFullCallback, BacktraceErrorCallback, &traceback);
+ }
+ return traceback.GetTraceback();
+}
+
+#if TVM_FFI_BACKTRACE_ON_SEGFAULT
+void backtrace_handler(int sig) {
+ // Technically we shouldn't do any allocation in a signal handler, but
+ // Backtrace may allocate. What's the worst it could do? We're already
+ // crashing.
+ std::cerr << "!!!!!!! TVM FFI encountered a Segfault !!!!!!!\n" << Traceback() << std::endl;
+
+ // Re-raise signal with default handler
+ struct sigaction act;
+ std::memset(&act, 0, sizeof(struct sigaction));
+ act.sa_flags = SA_RESETHAND;
+ act.sa_handler = SIG_DFL;
+ sigaction(sig, &act, nullptr);
+ raise(sig);
+}
+
+__attribute__((constructor)) void install_signal_handler(void) {
+ // this may override already installed signal handlers
+ std::signal(SIGSEGV, backtrace_handler);
+}
+#endif // TVM_FFI_BACKTRACE_ON_SEGFAULT
+} // namespace
+
+namespace details {
+const char* Traceback(const char*, const char*, int) {
+ static thread_local std::string traceback_str;
+ traceback_str = ::tvm::ffi::Traceback();
+ return traceback_str.c_str();
+}
+} // namespace details
+} // namespace ffi
+} // namespace tvm
+#else
+namespace tvm {
+namespace ffi {
+namespace details {
+// fallback implementation simply print out the last trace
+const char* Traceback(const char* filename, const char* func, int lineno) {
+ static thread_local std::string traceback_str;
+ std::ostringstream traceback_stream;
+ // python style backtrace
+ traceback_stream << " " << filename << ", line " << lineno << ", in " << func << '\n';
+ traceback_str = traceback_stream.str();
+ return traceback_str.c_str();
+}
+} // namespace details
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_USE_LIBBACKTRACE
+#endif // _MSC_VER
diff --git a/ffi/src/ffi/traceback.h b/ffi/src/ffi/traceback.h
new file mode 100644
index 0000000000..2bc9e523a7
--- /dev/null
+++ b/ffi/src/ffi/traceback.h
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+/*!
+ * \file traceback.h
+ * \brief Common headers for traceback.
+ * \note We use the term "traceback" to be consistent with python naming convention.
+ */
+#ifndef TVM_FFI_TRACEBACK_H_
+#define TVM_FFI_TRACEBACK_H_
+
+#include <cstring>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace tvm {
+namespace ffi {
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4996) // std::getenv is unsafe
+#endif
+
+inline int32_t GetTracebackLimit() {
+ if (const char* env = std::getenv("TVM_TRACEBACK_LIMIT")) {
+ return std::stoi(env);
+ }
+ return 512;
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+/*!
+ * \brief List frame patterns that should be excluded as they contain less information
+ */
+inline bool ShouldExcludeFrame(const char* filename, const char* symbol) {
+ if (filename) {
+ // Stack frames for TVM FFI
+ if (strstr(filename, "include/tvm/ffi/error.h")) {
+ return true;
+ }
+ if (strstr(filename, "src/ffi/traceback.cc")) {
+ return true;
+ }
+ // Python interpreter stack frames
+ if (strstr(filename, "/python-") || strstr(filename, "/Python/ceval.c") ||
+ strstr(filename, "/Modules/_ctypes")) {
+ return true;
+ }
+ // C++ stdlib frames
+ if (strstr(filename, "include/c++/")) {
+ return true;
+ }
+ }
+
+ if (symbol) {
+ // C++ stdlib frames
+ if (strstr(symbol, "__libc_")) {
+ return true;
+ }
+ // Python interpreter stack frames
+ if (strstr(symbol, "_Py") == symbol || strstr(symbol, "PyObject")) {
+ return true;
+ }
+ }
+ // libffi.so stack frames. These may also show up as numeric
+ // addresses with no symbol name. This could be improved in the
+ // future by using dladdr() to check whether an address is contained
+ // in libffi.so
+ if (filename == nullptr && strstr(symbol, "ffi_call_")) {
+ return true;
+ }
+ return false;
+}
+
+/*!
+ * \brief storage to store traceback
+ */
+struct TracebackStorage {
+ std::vector<std::string> lines;
+ /*! \brief Maximum size of the traceback. */
+ size_t max_frame_size = GetTracebackLimit();
+
+ void Append(const char* filename, const char* func, int lineno) {
+ std::ostringstream trackeback_stream;
+ trackeback_stream << " " << filename;
+ if (lineno != 0) {
+ trackeback_stream << ", line " << lineno;
+ }
+ trackeback_stream << ", in " << func << '\n';
+ lines.push_back(trackeback_stream.str());
+ }
+
+ bool ExceedTracebackLimit() const { return lines.size() >= max_frame_size; }
+
+ // get traceback in the order of most recent call last
+ std::string GetTraceback() const {
+ std::string traceback;
+ for (auto it = lines.rbegin(); it != lines.rend(); ++it) {
+ traceback.insert(traceback.end(), it->begin(), it->end());
+ }
+ return traceback;
+ }
+};
+
+} // namespace ffi
+} // namespace tvm
+
+#endif
diff --git a/ffi/tests/example/CMakeLists.txt b/ffi/tests/example/CMakeLists.txt
index a78e3ee01a..62543756b9 100644
--- a/ffi/tests/example/CMakeLists.txt
+++ b/ffi/tests/example/CMakeLists.txt
@@ -18,4 +18,8 @@ add_cxx_warning(tvm_ffi_tests)
add_sanitizer_address(tvm_ffi_tests)
target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi)
+if (TVM_FFI_BUILD_REGISTRY)
+ target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi_registry_shared)
+endif()
+
add_googletest(tvm_ffi_tests)
diff --git a/ffi/tests/example/test_c_ffi_abi.cc b/ffi/tests/example/test_c_ffi_abi.cc
index 66f2dcf739..d936247653 100644
--- a/ffi/tests/example/test_c_ffi_abi.cc
+++ b/ffi/tests/example/test_c_ffi_abi.cc
@@ -1,5 +1,5 @@
#include <gtest/gtest.h>
-#include <tvm/ffi/c_ffi_abi.h>
+#include <tvm/ffi/c_api.h>
namespace {
diff --git a/ffi/tests/example/test_error.cc b/ffi/tests/example/test_error.cc
index a2c073aa3f..4f208b6999 100644
--- a/ffi/tests/example/test_error.cc
+++ b/ffi/tests/example/test_error.cc
@@ -19,10 +19,27 @@ TEST(Error, Traceback) {
EXPECT_EQ(error->kind, "RuntimeError");
std::string what = error.what();
EXPECT_NE(what.find("line"), std::string::npos);
- EXPECT_NE(what.find("ThrowRuntimeError()"), std::string::npos);
+ EXPECT_NE(what.find("ThrowRuntimeError"), std::string::npos);
EXPECT_NE(what.find("RuntimeError: test0"), std::string::npos);
throw;
}
}, ::tvm::ffi::Error);
}
+
+TEST(CheckError, Traceback) {
+ EXPECT_THROW(
+ {
+ try {
+ TVM_FFI_ICHECK_GT(2, 3);
+ } catch (const Error& error) {
+ EXPECT_EQ(error->kind, "InternalError");
+ std::cout << error.what();
+ std::string what = error.what();
+ EXPECT_NE(what.find("line"), std::string::npos);
+ EXPECT_NE(what.find("2 > 3"), std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
+}
} // namespace
(tvm) 04/38: [FFI] ObjectRef based Error
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 8c2a94802dbb2002f0407203cc168c562a1b3995
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Aug 11 10:38:15 2024 -0400
[FFI] ObjectRef based Error
---
ffi/include/tvm/ffi/c_ffi_abi.h | 46 ++++----
ffi/include/tvm/ffi/c_ffi_api.h | 60 +++++++++++
ffi/include/tvm/ffi/error.h | 123 +++++++++++++++++++++
ffi/include/tvm/ffi/internal_utils.h | 28 +++++
ffi/include/tvm/ffi/memory.h | 10 +-
ffi/include/tvm/ffi/object.h | 203 ++++++++++++++++++++++++++++++++---
ffi/src/ffi/registry.cc | 1 -
ffi/tests/example/test_error.cc | 28 +++++
8 files changed, 460 insertions(+), 39 deletions(-)
diff --git a/ffi/include/tvm/ffi/c_ffi_abi.h b/ffi/include/tvm/ffi/c_ffi_abi.h
index f1a1ae78da..34d25bf17b 100644
--- a/ffi/include/tvm/ffi/c_ffi_abi.h
+++ b/ffi/include/tvm/ffi/c_ffi_abi.h
@@ -19,7 +19,13 @@
/*
* \file tvm/ffi/c_ffi_abi.h
- * \brief This file defines
+ * \brief This file defines the ABI convention of the FFI convention
+ *
+ * \note This file only include data structures that can be used in
+ * a header only way. The APIs are defined in c_ffi_api.h
+ * and requires linking to tvm_ffi library.
+ *
+ * Only use the APIs when TVM_FFI_ALLOW_DYN_TYPE is set to true
*/
#ifndef TVM_FFI_C_FFI_ABI_H_
#define TVM_FFI_C_FFI_ABI_H_
@@ -27,23 +33,11 @@
#include <dlpack/dlpack.h>
#include <stdint.h>
-#if !defined(TVM_FFI_DLL) && defined(__EMSCRIPTEN__)
-#include <emscripten/emscripten.h>
-#define TVM_FFI_API EMSCRIPTEN_KEEPALIVE
-#endif
-#if !defined(TVM_FFI_DLL) && defined(_MSC_VER)
-#ifdef TVM_FFI_EXPORTS
-#define TVM_FFI_DLL __declspec(dllexport)
-#else
-#define TVM_FFI_DLL __declspec(dllimport)
-#endif
-#endif
-#ifndef TVM_FFI_DLL
-#define TVM_FFI_DLL __attribute__((visibility("default")))
-#endif
-
+/*!
+ * \brief Macro defines whether we enable dynamic runtime features.
+ */
#ifndef TVM_FFI_ALLOW_DYN_TYPE
-#define TVM_FFI_ALLOW_DYN_TYPE 0
+#define TVM_FFI_ALLOW_DYN_TYPE 1
#endif
#ifdef __cplusplus
@@ -100,7 +94,7 @@ typedef struct TVMFFIObject {
/*! \brief Reference counter of the object. */
int32_t ref_counter;
/*! \brief Deleter to be invoked when reference counter goes to zero. */
- void (*deleter)(struct TVMFFIObject* self);
+ void (*deleter)(void* self);
} TVMFFIObject;
/*!
@@ -130,8 +124,22 @@ typedef struct TVMFFIAny {
};
} TVMFFIAny;
+/*! \brief Safe byte array */
+typedef struct {
+ int64_t num_bytes;
+ const char* bytes;
+} TVMFFIByteArray;
+
+/*! \brief The error type. */
+typedef struct {
+ /*! \brief header */
+ TVMFFIObject header_;
+
+
+} TVMFFIError;
+
+
#ifdef __cplusplus
} // TVM_FFI_EXTERN_C
#endif
-
#endif // TVM_FFI_C_FFI_ABI_H_
diff --git a/ffi/include/tvm/ffi/c_ffi_api.h b/ffi/include/tvm/ffi/c_ffi_api.h
new file mode 100644
index 0000000000..dda54f0032
--- /dev/null
+++ b/ffi/include/tvm/ffi/c_ffi_api.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+/*
+ * \file tvm/ffi/c_ffi_abi.h
+ * \brief This file defines the ABI convention of the FFI convention
+ *
+ * Including global calling conventions
+ */
+#ifndef TVM_FFI_C_FFI_ABI_H_
+#define TVM_FFI_C_FFI_ABI_H_
+
+#include <tvm/ffi/c_ffi_abi.h>
+
+#if !defined(TVM_FFI_DLL) && defined(__EMSCRIPTEN__)
+#include <emscripten/emscripten.h>
+#define TVM_FFI_API EMSCRIPTEN_KEEPALIVE
+#endif
+#if !defined(TVM_FFI_DLL) && defined(_MSC_VER)
+#ifdef TVM_FFI_EXPORTS
+#define TVM_FFI_DLL __declspec(dllexport)
+#else
+#define TVM_FFI_DLL __declspec(dllimport)
+#endif
+#endif
+#ifndef TVM_FFI_DLL
+#define TVM_FFI_DLL __attribute__((visibility("default")))
+#endif
+
+#ifdef __cplusplus
+static_assert(
+ TVM_FFI_ALLOW_DYN_TYPE,
+ "Only include c_ffi_abi when TVM_FFI_ALLOW_DYN_TYPE is set to true"
+);
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+} // TVM_FFI_EXTERN_C
+#endif
+#endif // TVM_FFI_C_FFI_ABI_H_
diff --git a/ffi/include/tvm/ffi/error.h b/ffi/include/tvm/ffi/error.h
new file mode 100644
index 0000000000..7b40f00bf9
--- /dev/null
+++ b/ffi/include/tvm/ffi/error.h
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+/*
+ * \file tvm/ffi/error.h
+ * \brief Error handling component.
+ */
+#ifndef TVM_FFI_ERROR_H_
+#define TVM_FFI_ERROR_H_
+
+#include <tvm/ffi/object.h>
+#include <tvm/ffi/memory.h>
+
+#include <string>
+#include <sstream>
+
+namespace tvm {
+namespace ffi {
+
+/*!
+ * \brief Error object class.
+ */
+class ErrorObj: public Object {
+ public:
+ /*! \brief The error kind */
+ std::string kind;
+ /*! \brief Message the error message. */
+ std::string message;
+ /*! \brief Backtrace, follows python convention(most recent last). */
+ std::string backtrace;
+ /*! \brief Full message in what_str */
+ std::string what_str;
+
+ static constexpr const int32_t _type_index = TypeIndex::kTVMFFIError;
+ static constexpr const char* _type_key = "object.Error";
+
+ TVM_FFI_DECLARE_STATIC_OBJECT_INFO(ErrorObj, Object);
+};
+
+/*!
+ * \brief Managed reference to ErrorObj
+ * \sa Error Object
+ */
+class Error :
+ public ObjectRef,
+ public std::exception {
+ public:
+ Error(std::string kind, std::string message, std::string backtrace) {
+ std::ostringstream what;
+ what << "Traceback (most recent call last):\n" << backtrace << kind << ": " << message << '\n';
+ ObjectPtr<ErrorObj> n = make_object<ErrorObj>();
+ n->kind = std::move(kind);
+ n->message = std::move(message);
+ n->backtrace = std::move(backtrace);
+ n->what_str = what.str();
+ data_ = std::move(n);
+ }
+
+ const char* what() const noexcept(true) override {
+ return get()->what_str.c_str();
+ }
+
+ TVM_FFI_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(Error, ObjectRef, ErrorObj)
+};
+
+namespace details {
+
+class ErrorBuilder {
+ public:
+ explicit ErrorBuilder(const char* kind, const char* filename, const char* func, int32_t lineno)
+ : kind_(kind) {
+ std::ostringstream backtrace;
+ // python style backtrace
+ backtrace << " " << filename << ", line " << lineno << ", in " << func << '\n';
+ backtrace_ = backtrace.str();
+ }
+
+// MSVC disable warning in error builder as it is exepected
+#ifdef _MSC_VER
+#pragma disagnostic push
+#pragma warning(disable : 4722)
+#endif
+ [[noreturn]] ~ErrorBuilder() noexcept(false) {
+ throw ::tvm::ffi::Error(std::move(kind_), message_.str(), std::move(backtrace_));
+ }
+#ifdef _MSC_VER
+#pragma disagnostic pop
+#endif
+
+ std::ostringstream &Get() { return message_; }
+
+protected:
+ std::string kind_;
+ std::ostringstream message_;
+ std::string backtrace_;
+};
+} // namespace details
+
+/*!
+ * \brief Helper macro to throw an error with backtrace and message
+ */
+#define TVM_FFI_THROW(ErrorKind) \
+ ::tvm::ffi::details::ErrorBuilder(#ErrorKind, __FILE__, TVM_FFI_FUNC_SIG, __LINE__).Get()
+
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_ERROR_H_
diff --git a/ffi/include/tvm/ffi/internal_utils.h b/ffi/include/tvm/ffi/internal_utils.h
index 53d08c364c..5eca030028 100644
--- a/ffi/include/tvm/ffi/internal_utils.h
+++ b/ffi/include/tvm/ffi/internal_utils.h
@@ -39,6 +39,34 @@
#define TVM_FFI_UNREACHABLE() __builtin_unreachable()
#endif
+/*! \brief helper macro to suppress unused warning */
+#if defined(__GNUC__)
+#define TVM_FFI_ATTRIBUTE_UNUSED __attribute__((unused))
+#else
+#define TVM_FFI_ATTRIBUTE_UNUSED
+#endif
+
+#define TVM_FFI_STR_CONCAT_(__x, __y) __x##__y
+#define TVM_FFI_STR_CONCAT(__x, __y) TVM_FFI_STR_CONCAT_(__x, __y)
+
+#if defined(__GNUC__) || defined(__clang__)
+#define TVM_FFI_FUNC_SIG __PRETTY_FUNCTION__
+#elif defined(_MSC_VER)
+#define TVM_FFI_FUNC_SIG __FUNCSIG__
+#else
+#define TVM_FFI_FUNC_SIG __func__
+#endif
+
+/*
+ * \brief Define the default copy/move constructor and assign operator
+ * \param TypeName The class typename.
+ */
+#define TVM_FFI_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName& other) = default; \
+ TypeName(TypeName&& other) = default; \
+ TypeName& operator=(const TypeName& other) = default; \
+ TypeName& operator=(TypeName&& other) = default;
+
namespace tvm {
namespace ffi {
diff --git a/ffi/include/tvm/ffi/memory.h b/ffi/include/tvm/ffi/memory.h
index cb7067502e..ca3021ff78 100644
--- a/ffi/include/tvm/ffi/memory.h
+++ b/ffi/include/tvm/ffi/memory.h
@@ -33,7 +33,7 @@ namespace tvm {
namespace ffi {
/*! \brief Deleter function for obeject */
-typedef void (*FObjectDeleter)(TVMFFIObject* obj);
+typedef void (*FObjectDeleter)(void* obj);
/*!
* \brief Allocate an object using default allocator.
@@ -74,11 +74,11 @@ class ObjAllocatorBase {
using Handler = typename Derived::template Handler<T>;
static_assert(std::is_base_of<Object, T>::value, "make can only be used to create Object");
T* ptr = Handler::New(static_cast<Derived*>(this), std::forward<Args>(args)...);
- TVMFFIObject* ffi_ptr = details::ObjectInternal::StaticCast<TVMFFIObject*>(ptr);
+ TVMFFIObject* ffi_ptr = details::ObjectInternal::GetHeader(ptr);
// NOTE: ref_counter is initialized in object constructor
ffi_ptr->type_index = T::RuntimeTypeIndex();
ffi_ptr->deleter = Handler::Deleter();
- return details::ObjectInternal::ObjectPtr<T>(ptr);
+ return details::ObjectInternal::ObjectPtrFromUnowned<T>(ptr);
}
/*!
@@ -132,11 +132,11 @@ class SimpleObjAllocator : public ObjAllocatorBase<SimpleObjAllocator> {
static FObjectDeleter Deleter() { return Deleter_; }
private:
- static void Deleter_(TVMFFIObject* objptr) {
+ static void Deleter_(void* objptr) {
// NOTE: this is important to cast back to T*
// because objptr and tptr may not be the same
// depending on how sub-class allocates the space.
- T* tptr = details::ObjectInternal::StaticCast<T*>(objptr);
+ T* tptr = static_cast<T*>(objptr);
// It is important to do tptr->T::~T(),
// so that we explicitly call the specific destructor
// instead of tptr->~T(), which could mean the intention
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 6b973f943c..037b08bbca 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -75,11 +75,15 @@ struct ObjectInternal;
* New objects can be created using make_object function.
* Which will automatically populate the type_index and deleter of the object.
*/
-class Object : private TVMFFIObject {
+class Object {
+ private:
+ /*! \brief header field that is the common prefix of all objects */
+ TVMFFIObject header_;
+
public:
Object() {
- TVMFFIObject::ref_counter = 0;
- TVMFFIObject::deleter = nullptr;
+ header_.ref_counter = 0;
+ header_.deleter = nullptr;
}
// Information about the object
@@ -109,13 +113,13 @@ class Object : private TVMFFIObject {
private:
/*! \brief increase reference count */
- void IncRef() { details::AtomicIncrementRelaxed(&(this->ref_counter)); }
+ void IncRef() { details::AtomicIncrementRelaxed(&(header_.ref_counter)); }
/*! \brief decrease reference count and delete the object */
void DecRef() {
- if (details::AtomicDecrementRelAcq(&(this->ref_counter)) == 1) {
- if (this->deleter != nullptr) {
- this->deleter(this);
+ if (details::AtomicDecrementRelAcq(&(header_.ref_counter)) == 1) {
+ if (header_.deleter != nullptr) {
+ header_.deleter(this);
}
}
}
@@ -124,7 +128,7 @@ class Object : private TVMFFIObject {
* \return The usage count of the cell.
* \note We use stl style naming to be consistent with known API in shared_ptr.
*/
- int32_t use_count() const { return details::AtomicLoadRelaxed(&(this->ref_counter)); }
+ int32_t use_count() const { return details::AtomicLoadRelaxed(&(header_.ref_counter)); }
// friend classes
template <typename>
@@ -284,6 +288,178 @@ class ObjectPtr {
friend ObjectPtr<BaseType> GetObjectPtr(ObjType* ptr);
};
+
+// Forward declaration, to prevent circular includes.
+template <typename T>
+class Optional;
+
+/*! \brief Base class of all object reference */
+class ObjectRef {
+ public:
+ /*! \brief default constructor */
+ ObjectRef() = default;
+ /*! \brief Constructor from existing object ptr */
+ explicit ObjectRef(ObjectPtr<Object> data) : data_(data) {}
+ /*!
+ * \brief Comparator
+ * \param other Another object ref.
+ * \return the compare result.
+ */
+ bool same_as(const ObjectRef& other) const { return data_ == other.data_; }
+ /*!
+ * \brief Comparator
+ * \param other Another object ref.
+ * \return the compare result.
+ */
+ bool operator==(const ObjectRef& other) const { return data_ == other.data_; }
+ /*!
+ * \brief Comparator
+ * \param other Another object ref.
+ * \return the compare result.
+ */
+ bool operator!=(const ObjectRef& other) const { return data_ != other.data_; }
+ /*!
+ * \brief Comparator
+ * \param other Another object ref by address.
+ * \return the compare result.
+ */
+ bool operator<(const ObjectRef& other) const { return data_.get() < other.data_.get(); }
+ /*!
+ * \return whether the object is defined(not null).
+ */
+ bool defined() const { return data_ != nullptr; }
+ /*! \return the internal object pointer */
+ const Object* get() const { return data_.get(); }
+ /*! \return the internal object pointer */
+ const Object* operator->() const { return get(); }
+ /*! \return whether the reference is unique */
+ bool unique() const { return data_.unique(); }
+ /*! \return The use count of the ptr, for debug purposes */
+ int use_count() const { return data_.use_count(); }
+
+ /*!
+ * \brief Try to downcast the internal Object to a
+ * raw pointer of a corresponding type.
+ *
+ * The function will return a nullptr if the cast failed.
+ *
+ * if (const AddNode *ptr = node_ref.as<AddNode>()) {
+ * // This is an add node
+ * }
+ *
+ * \tparam ObjectType the target type, must be a subtype of Object
+ */
+ template <typename ObjectType, typename = std::enable_if_t<std::is_base_of_v<Object, ObjectType>>>
+ inline const ObjectType* as() const;
+
+ /*! \brief type indicate the container type. */
+ using ContainerType = Object;
+ // Default type properties for the reference class.
+ static constexpr bool _type_is_nullable = true;
+
+ protected:
+ /*! \brief Internal pointer that backs the reference. */
+ ObjectPtr<Object> data_;
+ /*! \return return a mutable internal ptr, can be used by sub-classes. */
+ Object* get_mutable() const { return data_.get(); }
+ // friend classes.
+ friend struct ObjectPtrHash;
+ friend class tvm::ffi::details::ObjectInternal;
+ template <typename SubRef, typename BaseRef>
+ friend SubRef Downcast(BaseRef ref);
+};
+
+/*!
+ * \brief Get an object ptr type from a raw object ptr.
+ *
+ * \param ptr The object pointer
+ * \tparam BaseType The reference type
+ * \tparam ObjectType The object type
+ * \return The corresponding RefType
+ */
+template <typename BaseType, typename ObjectType>
+inline ObjectPtr<BaseType> GetObjectPtr(ObjectType* ptr);
+
+/*! \brief ObjectRef hash functor */
+struct ObjectPtrHash {
+ size_t operator()(const ObjectRef& a) const { return operator()(a.data_); }
+
+ template <typename T>
+ size_t operator()(const ObjectPtr<T>& a) const {
+ return std::hash<Object*>()(a.get());
+ }
+};
+
+/*! \brief ObjectRef equal functor */
+struct ObjectPtrEqual {
+ bool operator()(const ObjectRef& a, const ObjectRef& b) const { return a.same_as(b); }
+
+ template <typename T>
+ size_t operator()(const ObjectPtr<T>& a, const ObjectPtr<T>& b) const {
+ return a == b;
+ }
+};
+
+/*!
+ * \brief helper macro to declare a base object type that can be inherited.
+ * \param TypeName The name of the current type.
+ * \param ParentType The name of the ParentType
+ */
+#define TVM_FFI_DECLARE_STATIC_OBJECT_INFO(TypeName, ParentType) \
+ static_assert(!ParentType::_type_final, "ParentObj marked as final"); \
+ static int32_t RuntimeTypeIndex() { \
+ static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
+ TypeName::_type_child_slots < ParentType::_type_child_slots, \
+ "Need to set _type_child_slots when parent specifies it."); \
+ static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 || \
+ TypeName::_type_child_slots < ParentType::_type_child_slots, \
+ "Need to set _type_child_slots when parent specifies it."); \
+ static_assert(TypeName::_type_index != TypeIndex::kTVMFFIDynObject, \
+ "Static object cannot have dynamic type index."); \
+ return TypeName::_type_index; \
+ }
+
+#define TVM_FFI_OBJECT_REG_VAR_DEF static TVM_ATTRIBUTE_UNUSED uint32_t __make_Object_tid
+
+/*!
+ * \brief Helper macro to register the object type to runtime.
+ * Makes sure that the runtime type table is correctly populated.
+ *
+ * Use this macro in the cc file for each terminal class.
+ */
+#define TVM_FFI_REGISTER_OBJECT_TYPE(TypeName) \
+ TVM_FFI_STR_CONCAT(TVM_FFI_OBJECT_REG_VAR_DEF, __COUNTER__) = \
+ TypeName::_GetOrAllocRuntimeTypeIndex()
+
+/*
+ * \brief Define object reference methods.
+ * \param TypeName The object type name
+ * \param ParentType The parent type of the objectref
+ * \param ObjectName The type name of the object.
+ */
+#define TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS(TypeName, ParentType, ObjectName) \
+ TypeName() = default; \
+ explicit TypeName(::tvm::ffi::ObjectPtr<::tvm::ffi::Object> n) : ParentType(n) {} \
+ TVM_FFI_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName) \
+ const ObjectName* operator->() const { return static_cast<const ObjectName*>(data_.get()); } \
+ const ObjectName* get() const { return operator->(); } \
+ using ContainerType = ObjectName;
+
+/*
+ * \brief Define object reference methods that is not nullable.
+ *
+ * \param TypeName The object type name
+ * \param ParentType The parent type of the objectref
+ * \param ObjectName The type name of the object.
+ */
+#define TVM_FFI_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(TypeName, ParentType, ObjectName) \
+ explicit TypeName(::tvm::ffi::ObjectPtr<::tvm::ffi::Object> n) : ParentType(n) {} \
+ TVM_FFI_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName) \
+ const ObjectName* operator->() const { return static_cast<const ObjectName*>(data_.get()); } \
+ const ObjectName* get() const { return operator->(); } \
+ static constexpr bool _type_is_nullable = false; \
+ using ContainerType = ObjectName;
+
namespace details {
/*!
* \brief Namespace to internally manipulate object class.
@@ -291,15 +467,14 @@ namespace details {
* implementations and not external users of the tvm::ffi
*/
struct ObjectInternal {
- // NOTE: these helper to perform static cast
- // that also allows conversion from/to FFI values
- template <typename T, typename U>
- static TVM_FFI_INLINE T StaticCast(U src) {
- return static_cast<T>(src);
+ // NOTE: get ffi header from an object
+ static TVM_FFI_INLINE TVMFFIObject* GetHeader(Object* src) {
+ return &(src->header_);
}
+ // create ObjectPtr from unknowned ptr
template <typename T>
- static TVM_FFI_INLINE ObjectPtr<T> ObjectPtr(Object* raw_ptr) {
+ static TVM_FFI_INLINE ObjectPtr<T> ObjectPtrFromUnowned(Object* raw_ptr) {
return tvm::ffi::ObjectPtr<T>(raw_ptr);
}
};
diff --git a/ffi/src/ffi/registry.cc b/ffi/src/ffi/registry.cc
index 698f81c2b6..e69de29bb2 100644
--- a/ffi/src/ffi/registry.cc
+++ b/ffi/src/ffi/registry.cc
@@ -1 +0,0 @@
-namespace tvm {}
\ No newline at end of file
diff --git a/ffi/tests/example/test_error.cc b/ffi/tests/example/test_error.cc
new file mode 100644
index 0000000000..a2c073aa3f
--- /dev/null
+++ b/ffi/tests/example/test_error.cc
@@ -0,0 +1,28 @@
+#include <gtest/gtest.h>
+#include <tvm/ffi/error.h>
+
+namespace {
+
+using namespace tvm::ffi;
+
+void ThrowRuntimeError() {
+ TVM_FFI_THROW(RuntimeError)
+ << "test0";
+}
+
+TEST(Error, Traceback) {
+ EXPECT_THROW({
+ try {
+ ThrowRuntimeError();
+ } catch (const Error& error) {
+ EXPECT_EQ(error->message, "test0");
+ EXPECT_EQ(error->kind, "RuntimeError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("line"), std::string::npos);
+ EXPECT_NE(what.find("ThrowRuntimeError()"), std::string::npos);
+ EXPECT_NE(what.find("RuntimeError: test0"), std::string::npos);
+ throw;
+ }
+ }, ::tvm::ffi::Error);
+}
+} // namespace
(tvm) 28/38: pass ndarray compile
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 125da2e5736d942227911f7d71dd33e3448dd1d5
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 12:22:06 2025 -0400
pass ndarray compile
---
ffi/include/tvm/ffi/object.h | 4 ++++
include/tvm/runtime/object.h | 5 +++++
src/runtime/debug_compile.cc | 1 +
src/runtime/ndarray.cc | 23 ++++++++++++++---------
4 files changed, 24 insertions(+), 9 deletions(-)
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index a6f85be999..8fbd6705fd 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -575,6 +575,10 @@ class ObjectUnsafe {
reinterpret_cast<Object*>(handle)->DecRef();
}
+ static TVM_FFI_INLINE void IncRefObjectHandle(TVMFFIObjectHandle handle) {
+ reinterpret_cast<Object*>(handle)->IncRef();
+ }
+
static TVM_FFI_INLINE void DecRefObjectInAny(TVMFFIAny* src) {
reinterpret_cast<Object*>(src->v_obj)->DecRef();
}
diff --git a/include/tvm/runtime/object.h b/include/tvm/runtime/object.h
index b84cf1914b..046f0d3948 100644
--- a/include/tvm/runtime/object.h
+++ b/include/tvm/runtime/object.h
@@ -26,6 +26,7 @@
#include <tvm/ffi/object.h>
#include <tvm/ffi/cast.h>
#include <tvm/ffi/reflection.h>
+#include <tvm/runtime/c_runtime_api.h>
namespace tvm {
namespace runtime {
@@ -147,6 +148,10 @@ class ObjectRef : public tvm::ffi::ObjectRef {
#define TVM_DEFINE_OBJECT_REF_METHODS TVM_FFI_DEFINE_NULLABLE_OBJECT_REF_METHODS
+#define TVM_STR_CONCAT_(__x, __y) __x##__y
+#define TVM_STR_CONCAT(__x, __y) TVM_STR_CONCAT_(__x, __y)
+
+
} // namespace runtime
} // namespace tvm
#endif // TVM_RUNTIME_OBJECT_H_
diff --git a/src/runtime/debug_compile.cc b/src/runtime/debug_compile.cc
index e04c0b0003..81f1edffc7 100644
--- a/src/runtime/debug_compile.cc
+++ b/src/runtime/debug_compile.cc
@@ -28,6 +28,7 @@
#include <tvm/runtime/container/variant.h>
#include <tvm/runtime/ndarray.h>
#include <tvm/runtime/packed_func.h>
+#include <tvm/runtime/registry.h>
namespace tvm {
namespace debug {
diff --git a/src/runtime/ndarray.cc b/src/runtime/ndarray.cc
index 5a328413a1..32f11a9e8a 100644
--- a/src/runtime/ndarray.cc
+++ b/src/runtime/ndarray.cc
@@ -101,10 +101,12 @@ void ArrayCopyToBytes(const DLTensor* handle, void* data, size_t nbytes) {
struct NDArray::Internal {
// Default deleter for the container
- static void DefaultDeleter(Object* ptr_obj) {
+ static void DefaultDeleter(void* ptr_obj) {
auto* ptr = static_cast<NDArray::Container*>(ptr_obj);
if (ptr->manager_ctx != nullptr) {
- static_cast<NDArray::Container*>(ptr->manager_ctx)->DecRef();
+ details::ObjectUnsafe::DecRefObjectHandle(
+ static_cast<NDArray::Container*>(ptr->manager_ctx)
+ );
} else if (ptr->dl_tensor.data != nullptr) {
tvm::runtime::DeviceAPI::Get(ptr->dl_tensor.device)
->FreeDataSpace(ptr->dl_tensor.device, ptr->dl_tensor.data);
@@ -116,7 +118,7 @@ struct NDArray::Internal {
// that are not allocated inside of TVM.
// This enables us to create NDArray from memory allocated by other
// frameworks that are DLPack compatible
- static void DLPackDeleter(Object* ptr_obj) {
+ static void DLPackDeleter(void* ptr_obj) {
auto* ptr = static_cast<NDArray::Container*>(ptr_obj);
DLManagedTensor* tensor = static_cast<DLManagedTensor*>(ptr->manager_ctx);
if (tensor->deleter != nullptr) {
@@ -127,7 +129,7 @@ struct NDArray::Internal {
// Deleter for NDArray based on external DLTensor
// The memory is allocated from outside and it is assumed that
// responsibility for its freeing is also outside
- static void SelfDeleter(Object* ptr_obj) {
+ static void SelfDeleter(void* ptr_obj) {
auto* ptr = static_cast<NDArray::Container*>(ptr_obj);
delete ptr;
}
@@ -171,13 +173,13 @@ struct NDArray::Internal {
DLManagedTensor* ret = new DLManagedTensor();
ret->dl_tensor = from->dl_tensor;
ret->manager_ctx = from;
- from->IncRef();
+ tvm::ffi::details::ObjectUnsafe::IncRefObjectHandle(from);
ret->deleter = TVMNDArrayDLPackDeleter;
return ret;
}
// Delete dlpack object.
static void NDArrayDLPackDeleter(DLManagedTensor* tensor) {
- static_cast<NDArray::Container*>(tensor->manager_ctx)->DecRef();
+ details::ObjectUnsafe::DecRefObjectHandle(static_cast<NDArray::Container*>(tensor->manager_ctx));
delete tensor;
}
};
@@ -224,7 +226,8 @@ NDArray NDArray::CreateView(ShapeTuple shape, DLDataType dtype, uint64_t relativ
<< ", dtype= " << curr_dl_tensor.dtype << ").";
// increase ref count
- get_mutable()->IncRef();
+ // get_mutable()->IncRef();
+ tvm::ffi::details::ObjectUnsafe::IncRefObjectHandle(get_mutable());
ret.get_mutable()->manager_ctx = get_mutable();
ret.get_mutable()->dl_tensor.data = get_mutable()->dl_tensor.data;
ret.get_mutable()->dl_tensor.byte_offset =
@@ -352,7 +355,7 @@ bool NDArray::IsAligned(const DLTensor& tensor) {
0);
}
-TVM_REGISTER_OBJECT_TYPE(NDArray::Container);
+// TVM_REGISTER_OBJECT_TYPE(NDArray::Container);
} // namespace runtime
} // namespace tvm
@@ -365,7 +368,9 @@ void TVMNDArrayDLPackDeleter(DLManagedTensor* tensor) {
int TVMArrayGetTypeIndex(TVMArrayHandle handle, unsigned* out_tindex) {
API_BEGIN();
- *out_tindex = TVMArrayHandleToObjectHandle(handle)->type_index();
+ *out_tindex = tvm::ffi::details::ObjectUnsafe::GetHeader(
+ TVMArrayHandleToObjectHandle(handle)
+ )->type_index;
API_END();
}
(tvm) 23/38: CMake links ok
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit dce37a83b363fbd6e9afdfa0a0f1b8a0857ceaa9
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 09:27:02 2025 -0400
CMake links ok
---
CMakeLists.txt | 16 ++--
cmake/modules/contrib/CUTLASS.cmake | 1 +
ffi/CMakeLists.txt | 97 ++++++++++++------------
ffi/scripts/run_tests.sh | 1 -
ffi/tests/cpp/CMakeLists.txt | 6 +-
include/tvm/runtime/object.h | 1 +
src/ffi/registry.cc | 144 ------------------------------------
7 files changed, 63 insertions(+), 203 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5509408d95..b630f12b06 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -507,6 +507,9 @@ list(REMOVE_ITEM COMPILER_SRCS ${LIBINFO_FILE})
add_library(tvm_objs OBJECT ${COMPILER_SRCS})
add_library(tvm_runtime_objs OBJECT ${RUNTIME_SRCS})
add_library(tvm_libinfo_objs OBJECT ${LIBINFO_FILE})
+target_link_libraries(tvm_objs PUBLIC tvm_ffi_header)
+target_link_libraries(tvm_runtime_objs PUBLIC tvm_ffi_header)
+target_link_libraries(tvm_libinfo_objs PUBLIC tvm_ffi_header)
include(GNUInstallDirs)
if(NOT BUILD_DUMMY_LIBTVM)
@@ -612,8 +615,10 @@ endif()
target_link_libraries(tvm PRIVATE ${TVM_RUNTIME_LINKER_LIBS})
target_link_libraries(tvm_runtime PRIVATE ${TVM_RUNTIME_LINKER_LIBS})
-target_link_directories(tvm PRIVATE tvm_ffi)
-target_link_directories(tvm_runtime PRIVATE tvm_ffi)
+
+target_link_libraries(tvm PUBLIC tvm_ffi_objs)
+target_link_libraries(tvm_runtime PUBLIC tvm_ffi_objs)
+
if(BUILD_FOR_HEXAGON AND DEFINED USE_HEXAGON_GTEST AND EXISTS ${USE_HEXAGON_GTEST})
include(FetchContent)
@@ -652,6 +657,7 @@ if (HIDE_PRIVATE_SYMBOLS AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
target_link_libraries(tvm_runtime PRIVATE ${HIDE_SYMBOLS_LINKER_FLAGS})
target_compile_definitions(tvm_allvisible PUBLIC $<TARGET_PROPERTY:tvm,INTERFACE_COMPILE_DEFINITONS>)
target_compile_definitions(tvm_allvisible PRIVATE $<TARGET_PROPERTY:tvm,COMPILE_DEFINITONS>)
+ target_link_libraries(tvm_allvisible PUBLIC tvm_ffi_objs)
endif()
# Create the `cpptest` target if we can find GTest. If not, we create dummy
@@ -709,10 +715,10 @@ if(NOT DEFINED ENV{CONDA_BUILD})
endif()
# Installation rules
-install(TARGETS tvm EXPORT ${PROJECT_NAME}Targets DESTINATION lib${LIB_SUFFIX})
-install(TARGETS tvm_runtime EXPORT ${PROJECT_NAME}Targets DESTINATION lib${LIB_SUFFIX})
+install(TARGETS tvm DESTINATION lib${LIB_SUFFIX})
+install(TARGETS tvm_runtime DESTINATION lib${LIB_SUFFIX})
if(BUILD_FOR_HEXAGON AND DEFINED USE_HEXAGON_GTEST AND EXISTS ${USE_HEXAGON_GTEST})
- install(TARGETS gtest EXPORT ${PROJECT_NAME}Targets DESTINATION lib${LIB_SUFFIX})
+ install(TARGETS gtest DESTINATION lib${LIB_SUFFIX})
endif()
if (INSTALL_DEV)
diff --git a/cmake/modules/contrib/CUTLASS.cmake b/cmake/modules/contrib/CUTLASS.cmake
index b302622cbc..75e7f68c88 100644
--- a/cmake/modules/contrib/CUTLASS.cmake
+++ b/cmake/modules/contrib/CUTLASS.cmake
@@ -36,6 +36,7 @@ if(USE_CUDA AND USE_CUTLASS)
set(CUTLASS_FPA_INTB_RUNTIME_SRCS "")
list(APPEND CUTLASS_FPA_INTB_RUNTIME_SRCS src/runtime/contrib/cutlass/weight_preprocess.cc)
add_library(fpA_intB_cutlass_objs OBJECT ${CUTLASS_FPA_INTB_RUNTIME_SRCS})
+ target_link_libraries(fpA_intB_cutlass_objs PRIVATE tvm_ffi_header)
target_compile_definitions(fpA_intB_cutlass_objs PRIVATE DMLC_USE_LOGGING_LIBRARY=<tvm/runtime/logging.h>)
target_include_directories(fpA_intB_cutlass_objs PRIVATE
${PROJECT_SOURCE_DIR}/3rdparty/cutlass_fpA_intB_gemm
diff --git a/ffi/CMakeLists.txt b/ffi/CMakeLists.txt
index 1aa05110a7..154b13ef05 100644
--- a/ffi/CMakeLists.txt
+++ b/ffi/CMakeLists.txt
@@ -31,60 +31,61 @@ include(cmake/Utils/AddLibbacktrace.cmake)
add_library(dlpack_header INTERFACE)
target_include_directories(dlpack_header INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/dlpack/include")
-########## Target: `tvm_ffi` ##########
+########## Target: `tvm_ffi_header` ##########
-add_library(tvm_ffi INTERFACE)
-target_link_libraries(tvm_ffi INTERFACE dlpack_header)
-target_compile_features(tvm_ffi INTERFACE cxx_std_17)
-target_include_directories(tvm_ffi INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
+add_library(tvm_ffi_header INTERFACE)
+target_include_directories(tvm_ffi_header INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
+target_link_libraries(tvm_ffi_header INTERFACE dlpack_header)
########## Target: `tvm_ffi` ##########
+add_library(tvm_ffi_objs OBJECT
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/traceback.cc"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/object.cc"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/function.cc"
+)
+set_target_properties(
+ tvm_ffi_objs PROPERTIES
+ POSITION_INDEPENDENT_CODE ON
+ CXX_STANDARD 17
+ CXX_EXTENSIONS OFF
+ CXX_STANDARD_REQUIRED ON
+ CXX_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ PREFIX "lib"
+)
+add_cxx_warning(tvm_ffi_objs)
+target_link_libraries(tvm_ffi_objs PRIVATE dlpack_header)
+target_include_directories(tvm_ffi_objs PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
-if (TVM_FFI_BUILD_REGISTRY)
- add_library(tvm_ffi_objs OBJECT
- "${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/traceback.cc"
- "${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/object.cc"
- "${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/function.cc"
- )
- set_target_properties(
- tvm_ffi_objs PROPERTIES
- POSITION_INDEPENDENT_CODE ON
- CXX_STANDARD 17
- CXX_EXTENSIONS OFF
- CXX_STANDARD_REQUIRED ON
- CXX_VISIBILITY_PRESET hidden
- VISIBILITY_INLINES_HIDDEN ON
- PREFIX "lib"
- )
- add_cxx_warning(tvm_ffi_objs)
- target_link_libraries(tvm_ffi_objs PRIVATE dlpack_header)
- target_include_directories(tvm_ffi_objs PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
-
- if (TVM_FFI_USE_LIBBRACKTRACE)
- message(STATUS "Setting C++ macro TVM_FFI_USE_LIBBRACKTRACE - 1")
- target_compile_definitions(tvm_ffi_objs PRIVATE TVM_FFI_USE_LIBBRACKTRACE=1)
- else()
- message(STATUS "Setting C++ macro TVM_FFI_USE_LIBBRACKTRACE - 0")
- target_compile_definitions(tvm_ffi_objs PRIVATE TVM_FFI_USE_LIBBRACKTRACE=0)
- endif()
+if (TVM_FFI_USE_LIBBRACKTRACE)
+ message(STATUS "Setting C++ macro TVM_FFI_USE_LIBBRACKTRACE - 1")
+ target_compile_definitions(tvm_ffi_objs PRIVATE TVM_FFI_USE_LIBBRACKTRACE=1)
+else()
+ message(STATUS "Setting C++ macro TVM_FFI_USE_LIBBRACKTRACE - 0")
+ target_compile_definitions(tvm_ffi_objs PRIVATE TVM_FFI_USE_LIBBRACKTRACE=0)
+endif()
- if (TVM_FFI_BACKTRACE_ON_SEGFAULT)
- message(STATUS "Setting C++ macro TVM_FFI_BACKTRACE_ON_SEGFAULT - 1")
- target_compile_definitions(tvm_ffi_objs PRIVATE TVM_FFI_BACKTRACE_ON_SEGFAULT=1)
- else()
- message(STATUS "Setting C++ macro TVM_FFI_BACKTRACE_ON_SEGFAULT - 0")
- target_compile_definitions(tvm_ffi_objs PRIVATE TVM_FFI_BACKTRACE_ON_SEGFAULT=0)
- endif()
+if (TVM_FFI_BACKTRACE_ON_SEGFAULT)
+ message(STATUS "Setting C++ macro TVM_FFI_BACKTRACE_ON_SEGFAULT - 1")
+ target_compile_definitions(tvm_ffi_objs PRIVATE TVM_FFI_BACKTRACE_ON_SEGFAULT=1)
+else()
+ message(STATUS "Setting C++ macro TVM_FFI_BACKTRACE_ON_SEGFAULT - 0")
+ target_compile_definitions(tvm_ffi_objs PRIVATE TVM_FFI_BACKTRACE_ON_SEGFAULT=0)
+endif()
+
+add_target_from_obj(tvm_ffi tvm_ffi_objs)
+
+if (TARGET libbacktrace)
+ target_link_libraries(tvm_ffi_objs PRIVATE libbacktrace)
+ target_link_libraries(tvm_ffi_shared PRIVATE libbacktrace)
+ target_link_libraries(tvm_ffi_static PRIVATE libbacktrace)
+endif ()
+target_link_libraries(tvm_ffi_objs PUBLIC tvm_ffi_header)
+target_link_libraries(tvm_ffi_shared PUBLIC tvm_ffi_header)
+target_link_libraries(tvm_ffi_static PUBLIC tvm_ffi_header)
- add_target_from_obj(tvm_ffi tvm_ffi_objs)
- if (TARGET libbacktrace)
- target_link_libraries(tvm_ffi_objs PRIVATE libbacktrace)
- target_link_libraries(tvm_ffi_shared PRIVATE libbacktrace)
- target_link_libraries(tvm_ffi_static PRIVATE libbacktrace)
- endif ()
- install(TARGETS tvm_ffi_static DESTINATION "lib/")
- install(TARGETS tvm_ffi_shared DESTINATION "lib/")
-endif (TVM_FFI_BUILD_REGISTRY)
+install(TARGETS tvm_ffi_static DESTINATION lib${LIB_SUFFIX})
+install(TARGETS tvm_ffi_shared DESTINATION lib${LIB_SUFFIX})
########## Adding tests ##########
diff --git a/ffi/scripts/run_tests.sh b/ffi/scripts/run_tests.sh
index dc7e6cdc71..bfe36745b6 100755
--- a/ffi/scripts/run_tests.sh
+++ b/ffi/scripts/run_tests.sh
@@ -5,7 +5,6 @@ BUILD_TYPE=RelWithDebugInfo
rm -rf build/CMakeFiles build/CMakeCache.txt
cmake -G Ninja -S . -B build -DTVM_FFI_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
- -DTVM_FFI_BUILD_REGISTRY=ON \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
cmake --build build --parallel 16 --clean-first --config ${BUILD_TYPE} --target tvm_ffi_tests
GTEST_COLOR=1 ctest -V -C ${BUILD_TYPE} --test-dir build --output-on-failure
diff --git a/ffi/tests/cpp/CMakeLists.txt b/ffi/tests/cpp/CMakeLists.txt
index 54dae7177d..542a60e658 100644
--- a/ffi/tests/cpp/CMakeLists.txt
+++ b/ffi/tests/cpp/CMakeLists.txt
@@ -16,10 +16,6 @@ set_target_properties(
)
add_cxx_warning(tvm_ffi_tests)
add_sanitizer_address(tvm_ffi_tests)
-target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi)
-
-if (TVM_FFI_BUILD_REGISTRY)
- target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi_shared)
-endif()
+target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi_shared)
add_googletest(tvm_ffi_tests)
diff --git a/include/tvm/runtime/object.h b/include/tvm/runtime/object.h
index 892294bd82..527d663c02 100644
--- a/include/tvm/runtime/object.h
+++ b/include/tvm/runtime/object.h
@@ -25,6 +25,7 @@
#include <tvm/runtime/c_runtime_api.h>
#include <tvm/runtime/logging.h>
+#include <tvm/ffi/object.h>
#include <string>
#include <type_traits>
diff --git a/src/ffi/registry.cc b/src/ffi/registry.cc
deleted file mode 100644
index 05c0280796..0000000000
--- a/src/ffi/registry.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-#include <memory>
-#include <string>
-#include <tvm/ffi/ffi.hpp>
-#include <unordered_map>
-#include <vector>
-
-namespace {
-
-using tvm::ffi::Any;
-using tvm::ffi::AnyView;
-using tvm::ffi::Func;
-using tvm::ffi::Ref;
-using tvm::ffi::TypeIndexTraits;
-using tvm::ffi::TypeTraits;
-using tvm::ffi::details::InitTypeTable;
-
-struct TypeInfoImpl : public TVMFFITypeInfo {
- std::string type_key_data;
- std::vector<int32_t> type_ancestors_data;
-};
-
-struct TypeTable {
- int32_t num_types;
- std::vector<std::shared_ptr<TypeInfoImpl>> type_table;
- std::unordered_map<std::string, std::shared_ptr<TypeInfoImpl>> type_key_to_info;
- std::unordered_map<std::string, std::vector<Any>> type_attrs;
-
- static TypeTable *New();
- static TypeTable *Global() {
- static std::unique_ptr<TypeTable> instance(TypeTable::New());
- return instance.get();
- }
-};
-
-int32_t TypeDef(TypeTable *self, int32_t type_index, const char *type_key, int32_t type_depth,
- const int32_t *type_ancestors) {
- if (self == nullptr) {
- TVM_FFI_THROW(ValueError) << "TypeTable is provided as nullptr.";
- }
- if (self->type_key_to_info.count(type_key) != 0) {
- TVM_FFI_THROW(ValueError) << "Type key is already registered.";
- }
- if (type_index == -1) {
- type_index = self->num_types++;
- }
- std::shared_ptr<TypeInfoImpl> info = std::make_shared<TypeInfoImpl>();
- {
- info->type_key_data = std::string(type_key);
- info->type_ancestors_data = std::vector<int32_t>(type_ancestors, type_ancestors + type_depth);
- info->type_index = type_index;
- info->type_key = info->type_key_data.c_str();
- info->type_depth = type_depth;
- info->type_ancestors = info->type_ancestors_data.data();
- }
- if (type_index >= static_cast<int32_t>(self->type_table.size())) {
- self->type_table.resize((type_index / 1024 + 1) * 1024);
- }
- self->type_table.at(type_index) = info;
- self->type_key_to_info[type_key] = info;
- return type_index;
-}
-
-struct InitFunctor {
- template <enum TVMFFITypeIndex type_index, typename ObjectType>
- void RegisterType(TypeTable *self) {
- TypeDef(self, static_cast<int32_t>(type_index), TypeIndexTraits<type_index>::type_key,
- std::max<int32_t>(ObjectType::_type_depth, 0), ObjectType::_type_ancestors.data());
- }
-
- template <enum TVMFFITypeIndex type_index, typename ObjectType>
- void RegisterStr(TypeTable *self) {
- Any value = Any(Ref<Func>::New(TypeTraits<ObjectType>::__str__));
- TVMFFIDynTypeSetAttr(self, static_cast<int32_t>(type_index), "__str__", &value);
- }
-};
-
-TypeTable *TypeTable::New() {
- TypeTable *self = new TypeTable();
- self->type_table.resize(1024);
- self->type_key_to_info.reserve(1024);
- self->num_types = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDynObjectBegin);
- InitTypeTable(InitFunctor(), self);
- return self;
-}
-
-} // namespace
-
-TVM_FFI_API void TVMFFIDynTypeDef(TVMFFITypeTableHandle _self, const char *type_key,
- int32_t type_depth, const int32_t *type_ancestors,
- int32_t *type_index) {
- TypeTable *self = static_cast<TypeTable *>(_self);
- if (self == nullptr) {
- self = TypeTable::Global();
- }
- *type_index = TypeDef(self, -1, type_key, type_depth, type_ancestors);
-}
-
-TVM_FFI_API void TVMFFIDynTypeIndex2Info(TVMFFITypeTableHandle _self, int32_t type_index,
- TVMFFITypeInfoHandle *ret) {
- TypeTable *self = static_cast<TypeTable *>(_self);
- if (self == nullptr) {
- self = TypeTable::Global();
- }
- if (type_index < 0 || type_index >= static_cast<int32_t>(self->type_table.size())) {
- *ret = nullptr;
- } else {
- *ret = self->type_table.at(type_index).get();
- }
-}
-
-TVM_FFI_API void TVMFFIDynTypeSetAttr(TVMFFITypeTableHandle _self, int32_t type_index,
- const char *key, TVMFFIAnyHandle value) {
- TypeTable *self = static_cast<TypeTable *>(_self);
- if (self == nullptr) {
- self = TypeTable::Global();
- }
- std::vector<Any> &attrs = self->type_attrs[key];
- if (type_index >= static_cast<int32_t>(attrs.size())) {
- attrs.resize((type_index / 1024 + 1) * 1024);
- }
- attrs.at(type_index) = Any(*static_cast<AnyView *>(value));
-}
-
-TVM_FFI_API void TVMFFIDynTypeGetAttr(TVMFFITypeTableHandle _self, int32_t type_index,
- const char *key, TVMFFIAnyHandle *ret) {
- TypeTable *self = static_cast<TypeTable *>(_self);
- if (self == nullptr) {
- self = TypeTable::Global();
- }
- std::vector<Any> &attrs = self->type_attrs[key];
- if (type_index >= static_cast<int32_t>(attrs.size())) {
- *ret = nullptr;
- } else {
- *ret = &attrs.at(type_index);
- }
-}
-
-TVM_FFI_API void TVMFFIDynTypeTypeTableCreate(TVMFFITypeTableHandle *ret) {
- *ret = TypeTable::New();
-}
-
-TVM_FFI_API void TVMFFIDynTypeTypeTableDestroy(TVMFFITypeTableHandle handle) {
- delete static_cast<TypeTable *>(handle);
-}
(tvm) 30/38: Use specific import via using and not do global namespace
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 8b0ac0deb6f78a44df02492d920855d7b77fa1f7
Author: tqchen <ti...@gmail.com>
AuthorDate: Sun Mar 9 14:24:14 2025 -0400
Use specific import via using and not do global namespace
---
include/tvm/runtime/memory.h | 3 ++-
include/tvm/runtime/ndarray.h | 2 +-
include/tvm/runtime/object.h | 13 +++++++++++--
include/tvm/runtime/packed_func.h | 18 +++++++++---------
src/runtime/object_internal.h | 6 ++++--
5 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/include/tvm/runtime/memory.h b/include/tvm/runtime/memory.h
index adf7fe9a3a..8429650421 100644
--- a/include/tvm/runtime/memory.h
+++ b/include/tvm/runtime/memory.h
@@ -28,7 +28,8 @@
namespace tvm {
namespace runtime {
-using namespace tvm::ffi;
+using tvm::ffi::make_object;
+using tvm::ffi::make_inplace_array_object;
} // namespace runtime
} // namespace tvm
diff --git a/include/tvm/runtime/ndarray.h b/include/tvm/runtime/ndarray.h
index bd209f2c4c..4ed86fee82 100644
--- a/include/tvm/runtime/ndarray.h
+++ b/include/tvm/runtime/ndarray.h
@@ -445,7 +445,7 @@ inline Object* TVMArrayHandleToObjectHandle(TVMArrayHandle handle) {
}
inline void NDArray::FFIDecRef(TVMArrayHandle handle) {
- details::ObjectUnsafe::DecRefObjectHandle(TVMArrayHandleToObjectHandle(handle));
+ ffi::details::ObjectUnsafe::DecRefObjectHandle(TVMArrayHandleToObjectHandle(handle));
}
/*! \brief Magic number for NDArray file */
diff --git a/include/tvm/runtime/object.h b/include/tvm/runtime/object.h
index 2f89d11965..ec39461b0c 100644
--- a/include/tvm/runtime/object.h
+++ b/include/tvm/runtime/object.h
@@ -33,7 +33,16 @@ namespace runtime {
template<typename T>
class Optional;
-using namespace tvm::ffi;
+
+using tvm::ffi::Object;
+using tvm::ffi::ObjectPtr;
+using tvm::ffi::ObjectPtrEqual;
+using tvm::ffi::ObjectPtrHash;
+
+using tvm::ffi::Downcast;
+using tvm::ffi::GetRef;
+using tvm::ffi::GetObjectPtr;
+
/*!
* \brief Namespace for the list of type index.
@@ -113,7 +122,7 @@ class ObjectRef : public tvm::ffi::ObjectRef {
* \param ref The reference data.
*/
static void FFIClearAfterMove(ObjectRef* ref) {
- details::ObjectUnsafe::LegacyClearObjectPtrAfterMove(ref);
+ ffi::details::ObjectUnsafe::LegacyClearObjectPtrAfterMove(ref);
}
/*!
* \brief Internal helper function get data_ as ObjectPtr of ObjectType.
diff --git a/include/tvm/runtime/packed_func.h b/include/tvm/runtime/packed_func.h
index 0a2461515e..168798149a 100644
--- a/include/tvm/runtime/packed_func.h
+++ b/include/tvm/runtime/packed_func.h
@@ -641,14 +641,14 @@ class TVMPODValue_ {
return Module(ObjectPtr<Object>(nullptr));
}
TVM_CHECK_TYPE_CODE(type_code_, kTVMModuleHandle);
- return Module(details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(static_cast<Object*>(value_.v_handle)));
+ return Module(ffi::details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(static_cast<Object*>(value_.v_handle)));
}
operator PackedFunc() const {
if (type_code_ == kTVMNullptr) {
return PackedFunc(ObjectPtr<Object>(nullptr));
}
TVM_CHECK_TYPE_CODE(type_code_, kTVMPackedFuncHandle);
- return PackedFunc(details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(static_cast<Object*>(value_.v_handle)));
+ return PackedFunc(ffi::details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(static_cast<Object*>(value_.v_handle)));
}
operator Device() const {
TVM_CHECK_TYPE_CODE(type_code_, kDLDevice);
@@ -1199,7 +1199,7 @@ class TVMRetValue : public TVMPODValue_CRTP_<TVMRetValue> {
if (other != nullptr) {
this->Clear();
type_code_ = type_code;
- value_.v_handle = details::ObjectUnsafe::MoveTVMFFIObjectPtrFromObjectPtr(&other);
+ value_.v_handle = ffi::details::ObjectUnsafe::MoveTVMFFIObjectPtrFromObjectPtr(&other);
} else {
SwitchToPOD(kTVMNullptr);
value_.v_handle = nullptr;
@@ -1213,18 +1213,18 @@ class TVMRetValue : public TVMPODValue_CRTP_<TVMRetValue> {
delete ptr<std::string>();
break;
case kTVMPackedFuncHandle:
- details::ObjectUnsafe::DecRefObjectHandle(static_cast<Object*>(value_.v_handle));
+ ffi::details::ObjectUnsafe::DecRefObjectHandle(static_cast<Object*>(value_.v_handle));
break;
case kTVMNDArrayHandle: {
NDArray::FFIDecRef(static_cast<TVMArrayHandle>(value_.v_handle));
break;
}
case kTVMModuleHandle: {
- details::ObjectUnsafe::DecRefObjectHandle(static_cast<Object*>(value_.v_handle));
+ ffi::details::ObjectUnsafe::DecRefObjectHandle(static_cast<Object*>(value_.v_handle));
break;
}
case kTVMObjectHandle: {
- details::ObjectUnsafe::DecRefObjectHandle(static_cast<Object*>(value_.v_handle));
+ ffi::details::ObjectUnsafe::DecRefObjectHandle(static_cast<Object*>(value_.v_handle));
break;
}
}
@@ -2096,7 +2096,7 @@ inline void TVMArgsSetter::SetObject(size_t i, T&& value) const {
return;
}
- Object* ptr = details::ObjectUnsafe::GetRawObjectPtrFromObjectRef(value);
+ Object* ptr = ffi::details::ObjectUnsafe::GetRawObjectPtrFromObjectRef(value);
if constexpr (std::is_base_of_v<NDArray::ContainerType, ContainerType> ||
std::is_base_of_v<ContainerType, NDArray::ContainerType>) {
if (std::is_base_of_v<NDArray::ContainerType, ContainerType> ||
@@ -2187,7 +2187,7 @@ inline void TVMArgsSetter::SetObject(size_t i, T&& value) const {
type_codes_[i] = kTVMObjectRValueRefArg;
} else {
// value.data_.data_;
- values_[i].v_handle = details::ObjectUnsafe::GetTVMFFIObjectPtrFromObjectRef(value);
+ values_[i].v_handle = ffi::details::ObjectUnsafe::GetTVMFFIObjectPtrFromObjectRef(value);
type_codes_[i] = kTVMObjectHandle;
}
}
@@ -2439,7 +2439,7 @@ inline TVMMovableArgValue_::operator T() const {
if (type_code_ == kTVMObjectRValueRefArg) {
auto** ref = static_cast<Object**>(value_.v_handle);
if (ObjectTypeChecker<T>::Check(*ref)) {
- return T(details::ObjectUnsafe::MoveObjectPtrFromRValueRef<Object>(ref));
+ return T(ffi::details::ObjectUnsafe::MoveObjectPtrFromRValueRef<Object>(ref));
}
}
// fallback
diff --git a/src/runtime/object_internal.h b/src/runtime/object_internal.h
index beae137e26..3cde330f59 100644
--- a/src/runtime/object_internal.h
+++ b/src/runtime/object_internal.h
@@ -44,7 +44,8 @@ class ObjectInternal {
*/
static void ObjectRetain(TVMObjectHandle obj) {
if (obj != nullptr) {
- static_cast<Object*>(obj)->IncRef();
+ // static_cast<Object*>(obj)->IncRef();
+ tvm::ffi::details::ObjectUnsafe::IncRefObjectHandle(obj);
}
}
@@ -53,7 +54,8 @@ class ObjectInternal {
*/
static void ObjectFree(TVMObjectHandle obj) {
if (obj != nullptr) {
- static_cast<Object*>(obj)->DecRef();
+ // static_cast<Object*>(obj)->DecRef();
+ tvm::ffi::details::ObjectUnsafe::DecRefObjectHandle(obj);
}
}
/*!
(tvm) 01/38: [FFI] Initial Code Commit
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 73ea198a2ae749560b9a887eb7e4e55b2457f57b
Author: Junru Shao <ju...@apache.org>
AuthorDate: Wed Apr 17 09:05:27 2024 +0800
[FFI] Initial Code Commit
---
.gitmodules | 3 +
ffi/3rdparty/dlpack | 1 +
ffi/CMakeLists.txt | 78 +++++
ffi/cmake/Utils/AddGoogleTest.cmake | 42 +++
ffi/cmake/Utils/CxxWarning.cmake | 13 +
ffi/cmake/Utils/Sanitizer.cmake | 18 +
ffi/include | 1 +
ffi/scripts/run_tests.sh | 10 +
ffi/src | 1 +
ffi/tests/cpp/CMakeLists.txt | 25 ++
ffi/tests/cpp/test_any.cc | 635 ++++++++++++++++++++++++++++++++++++
ffi/tests/cpp/test_any_view.cc | 630 +++++++++++++++++++++++++++++++++++
ffi/tests/cpp/test_dict.cc | 183 +++++++++++
ffi/tests/cpp/test_func.cc | 215 ++++++++++++
ffi/tests/cpp/test_list.cc | 461 ++++++++++++++++++++++++++
ffi/tests/cpp/test_ref.cc | 293 +++++++++++++++++
ffi/tests/cpp/test_str.cc | 47 +++
ffi/tests/cpp/test_type_dyn.cc | 71 ++++
ffi/tests/cpp/test_type_static.cc | 119 +++++++
include/tvm/ffi/core/c_ffi_abi.h | 166 ++++++++++
include/tvm/ffi/core/core.h | 421 ++++++++++++++++++++++++
include/tvm/ffi/core/traits.h | 613 ++++++++++++++++++++++++++++++++++
include/tvm/ffi/core/utils.h | 281 ++++++++++++++++
include/tvm/ffi/ext/dict.h | 515 +++++++++++++++++++++++++++++
include/tvm/ffi/ext/error.h | 89 +++++
include/tvm/ffi/ext/func.h | 390 ++++++++++++++++++++++
include/tvm/ffi/ext/list.h | 457 ++++++++++++++++++++++++++
include/tvm/ffi/ext/str.h | 245 ++++++++++++++
include/tvm/ffi/ffi.hpp | 9 +
src/ffi/registry.cc | 144 ++++++++
30 files changed, 6176 insertions(+)
diff --git a/.gitmodules b/.gitmodules
index a1187967f7..e8a48d99c2 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -31,3 +31,6 @@
[submodule "3rdparty/zlib"]
path = 3rdparty/zlib
url = https://github.com/madler/zlib.git
+[submodule "ffi/3rdparty/dlpack"]
+ path = ffi/3rdparty/dlpack
+ url = https://github.com/dmlc/dlpack.git
diff --git a/ffi/3rdparty/dlpack b/ffi/3rdparty/dlpack
new file mode 160000
index 0000000000..bbd2f4d324
--- /dev/null
+++ b/ffi/3rdparty/dlpack
@@ -0,0 +1 @@
+Subproject commit bbd2f4d32427e548797929af08cfe2a9cbb3cf12
diff --git a/ffi/CMakeLists.txt b/ffi/CMakeLists.txt
new file mode 100644
index 0000000000..8710a0c377
--- /dev/null
+++ b/ffi/CMakeLists.txt
@@ -0,0 +1,78 @@
+cmake_minimum_required(VERSION 3.14)
+
+project(
+ tvm_ffi
+ VERSION 1.0
+ DESCRIPTION "TVM's FFI system"
+ LANGUAGES CXX
+)
+
+option(TVM_FFI_ALLOW_DYN_TYPE "Support for objects with non-static type indices. When turned on, targets linked against `tvm_ffi` will allow objects that comes with non-pre-defined type indices, so that the object hierarchy could expand without limitation. This will require the downstream targets to link against target `tvm_ffi_registry` to be effective." OFF)
+option(TVM_FFI_BUILD_TESTS "Adding test targets." OFF)
+
+include(cmake/Utils/CxxWarning.cmake)
+include(cmake/Utils/Sanitizer.cmake)
+
+########## Target: `dlpack_header` ##########
+
+add_library(dlpack_header INTERFACE)
+target_include_directories(dlpack_header INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/dlpack/include")
+
+########## Target: `tvm_ffi_registry_{objs|static|shared}` ##########
+
+add_library(
+ tvm_ffi_registry_objs
+ EXCLUDE_FROM_ALL
+ OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/src/ffi/registry.cc"
+)
+set_target_properties(
+ tvm_ffi_registry_objs PROPERTIES
+ POSITION_INDEPENDENT_CODE ON
+ CXX_STANDARD 17
+ CXX_EXTENSIONS OFF
+ CXX_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+)
+add_cxx_warning(tvm_ffi_registry_objs)
+target_link_libraries(tvm_ffi_registry_objs PRIVATE dlpack_header)
+target_include_directories(tvm_ffi_registry_objs PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
+target_compile_definitions(tvm_ffi_registry_objs PRIVATE TVM_FFI_ALLOW_DYN_TYPE=1)
+if (MSVC)
+ target_compile_definitions(tvm_ffi_registry_objs PRIVATE TVM_FFI_EXPORTS)
+endif()
+
+add_library(tvm_ffi_registry_static EXCLUDE_FROM_ALL STATIC $<TARGET_OBJECTS:tvm_ffi_registry_objs>)
+add_library(tvm_ffi_registry_shared EXCLUDE_FROM_ALL SHARED $<TARGET_OBJECTS:tvm_ffi_registry_objs>)
+set_target_properties(tvm_ffi_registry_shared tvm_ffi_registry_static tvm_ffi_registry_objs
+ PROPERTIES
+ MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
+ ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
+)
+
+########## Target: `tvm_ffi` ##########
+
+add_library(tvm_ffi INTERFACE)
+target_link_libraries(tvm_ffi INTERFACE dlpack_header)
+target_compile_features(tvm_ffi INTERFACE cxx_std_17)
+target_include_directories(tvm_ffi INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
+
+if (TVM_FFI_ALLOW_DYN_TYPE)
+ message(STATUS "Setting C++ macro TVM_FFI_ALLOW_DYN_TYPE - 1")
+ target_compile_definitions(tvm_ffi INTERFACE TVM_FFI_ALLOW_DYN_TYPE=1)
+else()
+ message(STATUS "Setting C++ macro TVM_FFI_ALLOW_DYN_TYPE - 0")
+ target_compile_definitions(tvm_ffi INTERFACE TVM_FFI_ALLOW_DYN_TYPE=0)
+endif()
+
+########## Adding tests ##########
+
+if (${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME})
+ if (TVM_FFI_BUILD_TESTS)
+ enable_testing()
+ message(STATUS "Enable Testing")
+ include(cmake/Utils/AddGoogleTest.cmake)
+ add_subdirectory(tests/cpp/)
+ endif()
+endif ()
diff --git a/ffi/cmake/Utils/AddGoogleTest.cmake b/ffi/cmake/Utils/AddGoogleTest.cmake
new file mode 100644
index 0000000000..aec5c82b03
--- /dev/null
+++ b/ffi/cmake/Utils/AddGoogleTest.cmake
@@ -0,0 +1,42 @@
+include(FetchContent)
+set(gtest_force_shared_crt ON CACHE BOOL "Always use msvcrt.dll" FORCE)
+set(BUILD_GMOCK ON CACHE BOOL "" FORCE)
+set(BUILD_GTEST ON CACHE BOOL "" FORCE)
+FetchContent_Declare(
+ googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG v1.14.0
+)
+FetchContent_GetProperties(googletest)
+if (NOT googletest_POPULATED)
+ FetchContent_Populate(googletest)
+ message(STATUS "Found googletest_SOURCE_DIR - ${googletest_SOURCE_DIR}")
+ message(STATUS "Found googletest_BINARY_DIR - ${googletest_BINARY_DIR}")
+ add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
+ include(GoogleTest)
+ set_target_properties(gtest PROPERTIES EXPORT_COMPILE_COMMANDS OFF EXCLUDE_FROM_ALL ON FOLDER 3rdparty)
+ set_target_properties(gtest_main PROPERTIES EXPORT_COMPILE_COMMANDS OFF EXCLUDE_FROM_ALL ON FOLDER 3rdparty)
+ set_target_properties(gmock PROPERTIES EXPORT_COMPILE_COMMANDS OFF EXCLUDE_FROM_ALL ON FOLDER 3rdparty)
+ set_target_properties(gmock_main PROPERTIES EXPORT_COMPILE_COMMANDS OFF EXCLUDE_FROM_ALL ON FOLDER 3rdparty)
+ mark_as_advanced(
+ BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS
+ gmock_build_tests gtest_build_samples gtest_build_tests
+ gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols
+ )
+endif()
+
+macro(add_googletest target_name)
+ add_test(
+ NAME ${target_name}
+ COMMAND ${target_name}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+ target_link_libraries(${target_name} PRIVATE gtest_main)
+ gtest_discover_tests(${target_name}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ DISCOVERY_MODE PRE_TEST
+ PROPERTIES
+ VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
+ )
+ set_target_properties(${target_name} PROPERTIES FOLDER tests)
+endmacro()
diff --git a/ffi/cmake/Utils/CxxWarning.cmake b/ffi/cmake/Utils/CxxWarning.cmake
new file mode 100644
index 0000000000..50ee5b616d
--- /dev/null
+++ b/ffi/cmake/Utils/CxxWarning.cmake
@@ -0,0 +1,13 @@
+function(add_cxx_warning target_name)
+ # GNU, Clang, or AppleClang
+ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
+ target_compile_options(${target_name} PRIVATE "-Werror" "-Wall" "-Wextra" "-Wpedantic")
+ return()
+ endif()
+ # MSVC
+ if(MSVC)
+ target_compile_options(${target_name} PRIVATE "/W4" "/WX")
+ return()
+ endif()
+ message(FATAL_ERROR "Unsupported compiler: ${CMAKE_CXX_COMPILER_ID}")
+endfunction()
diff --git a/ffi/cmake/Utils/Sanitizer.cmake b/ffi/cmake/Utils/Sanitizer.cmake
new file mode 100644
index 0000000000..c1facc1999
--- /dev/null
+++ b/ffi/cmake/Utils/Sanitizer.cmake
@@ -0,0 +1,18 @@
+function(add_sanitizer_address target_name)
+ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
+ include(CheckCXXCompilerFlag)
+ set (_saved_CRF ${CMAKE_REQUIRED_FLAGS})
+ set(CMAKE_REQUIRED_FLAGS "-fsanitize=address")
+ check_cxx_source_compiles("int main() { return 0; }" COMPILER_SUPPORTS_ASAN)
+ set (CMAKE_REQUIRED_FLAGS ${_saved_CRF})
+ get_target_property(_saved_type ${target_name} TYPE)
+ if (${_saved_type} STREQUAL "INTERFACE_LIBRARY")
+ set(_saved_type INTERFACE)
+ else()
+ set(_saved_type PRIVATE)
+ endif()
+ target_link_options(${target_name} ${_saved_type} "-fsanitize=address")
+ target_compile_options(${target_name} ${_saved_type} "-fsanitize=address" "-fno-omit-frame-pointer" "-g")
+ return()
+ endif()
+endfunction()
diff --git a/ffi/include b/ffi/include
new file mode 120000
index 0000000000..f5030fe889
--- /dev/null
+++ b/ffi/include
@@ -0,0 +1 @@
+../include
\ No newline at end of file
diff --git a/ffi/scripts/run_tests.sh b/ffi/scripts/run_tests.sh
new file mode 100755
index 0000000000..704abaeab9
--- /dev/null
+++ b/ffi/scripts/run_tests.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+set -euxo pipefail
+
+HEADER_ONLY=OFF
+BUILD_TYPE=RelWithDebInfo
+
+rm -rf build/CMakeFiles build/CMakeCache.txt
+cmake -G Ninja -S . -B build -DTVM_FFI_ALLOW_DYN_TYPE=${HEADER_ONLY} -DTVM_FFI_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
+cmake --build build --parallel 16 --clean-first --config ${BUILD_TYPE} --target tvm_ffi_tests
+GTEST_COLOR=1 ctest -V -C ${BUILD_TYPE} --test-dir build --output-on-failure
diff --git a/ffi/src b/ffi/src
new file mode 120000
index 0000000000..5cd551cf26
--- /dev/null
+++ b/ffi/src
@@ -0,0 +1 @@
+../src
\ No newline at end of file
diff --git a/ffi/tests/cpp/CMakeLists.txt b/ffi/tests/cpp/CMakeLists.txt
new file mode 100644
index 0000000000..539115f24d
--- /dev/null
+++ b/ffi/tests/cpp/CMakeLists.txt
@@ -0,0 +1,25 @@
+file(GLOB _test_sources "${CMAKE_CURRENT_SOURCE_DIR}/test*.cc")
+add_executable(
+ tvm_ffi_tests
+ EXCLUDE_FROM_ALL
+ ${_test_sources}
+)
+set_target_properties(
+ tvm_ffi_tests PROPERTIES
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+ CXX_EXTENSIONS OFF
+ MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
+ ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
+)
+add_cxx_warning(tvm_ffi_tests)
+add_sanitizer_address(tvm_ffi_tests)
+target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi)
+if (TVM_FFI_ALLOW_DYN_TYPE)
+ add_sanitizer_address(tvm_ffi_registry_shared)
+ target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi_registry_shared)
+endif()
+
+add_googletest(tvm_ffi_tests)
diff --git a/ffi/tests/cpp/test_any.cc b/ffi/tests/cpp/test_any.cc
new file mode 100644
index 0000000000..a57829bd0a
--- /dev/null
+++ b/ffi/tests/cpp/test_any.cc
@@ -0,0 +1,635 @@
+#include <gtest/gtest.h>
+#include <tvm/ffi/ffi.hpp>
+
+namespace {
+using namespace tvm::ffi;
+
+template <typename SrcType, typename Checker>
+void TestAnyConstructor(Checker check, TVMFFITypeIndex expected_type_index, const SrcType &source) {
+ Any v(source);
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
+ EXPECT_EQ(v.ref_cnt, 0);
+ check(&v, source);
+};
+
+int64_t FuncCall(int64_t x) { return x + 1; }
+
+std::vector<Any> AnyArrayFactory() {
+ return std::vector<Any>{
+ Any(nullptr),
+ Any(1),
+ Any(2.5),
+ Any(reinterpret_cast<void *>(FuncCall)),
+ Any(DLDevice{kDLCPU, 0}),
+ Any(DLDataType{kDLInt, 32, 1}),
+ Any("Hello (raw str)"),
+ Any(Ref<Object>::New()),
+ Any(Ref<Func>::New(FuncCall)),
+ Any(std::string("World (std::string)")),
+ Any(Ref<Str>::New("Hello World (Ref<Str>)")),
+ };
+}
+
+template <typename SrcType>
+void TestAnyStringify(const SrcType &source, TVMFFITypeIndex expected_type_index,
+ const std::string &expected) {
+ Any v(source);
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
+ EXPECT_EQ(v.str()->c_str(), expected);
+}
+
+template <typename SrcType, typename Checker>
+void TestAnyStringifyChecker(const SrcType &source, TVMFFITypeIndex expected_type_index,
+ Checker check) {
+ Any v(source);
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
+ check(v);
+}
+
+void CheckAnyRefCnt(const TVMFFIAny *v) {
+ if (v->type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
+ EXPECT_EQ(v->v_obj->ref_cnt, 1);
+ }
+}
+
+TEST(Any_Constructor_0_Default, Default) {
+ Any v;
+ EXPECT_EQ(v.type_index, 0);
+ EXPECT_EQ(v.ref_cnt, 0);
+ EXPECT_EQ(v.v_int64, 0);
+}
+
+TEST(Any_Constructor_1_Any, Copy) {
+ Any v1(1);
+ Any v2(v1);
+ EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
+ EXPECT_EQ(v1.v_int64, 1);
+ EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
+ EXPECT_EQ(v2.v_int64, 1);
+}
+
+TEST(Any_Constructor_1_Any, Move) {
+ Any v1(1);
+ Any v2(std::move(v1));
+ EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone));
+ EXPECT_EQ(v1.v_int64, 0);
+ EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
+ EXPECT_EQ(v2.v_int64, 1);
+}
+
+TEST(Any_Constructor_2_AnyView, Copy) {
+ AnyView v1(1);
+ Any v2(v1);
+ EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
+ EXPECT_EQ(v1.v_int64, 1);
+ EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
+ EXPECT_EQ(v2.v_int64, 1);
+}
+
+TEST(Any_Constructor_2_AnyView, Move) {
+ AnyView v1(1);
+ Any v2(std::move(v1));
+ EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone));
+ EXPECT_EQ(v1.v_int64, 0);
+ EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
+ EXPECT_EQ(v2.v_int64, 1);
+}
+
+TEST(Any_Constructor_3_Ref, Copy) {
+ Ref<Object> obj = Ref<Object>::New();
+ Any v(obj);
+ const TVMFFIAny *v_obj = v.v_obj;
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
+ EXPECT_EQ(v.ref_cnt, 0);
+ EXPECT_EQ(v_obj, static_cast<const void *>(obj.get()));
+ EXPECT_EQ(v_obj->ref_cnt, 2);
+}
+
+TEST(Any_Constructor_3_Ref, Move) {
+ Ref<Object> obj = Ref<Object>::New();
+ Any v(std::move(obj));
+ const TVMFFIAny *v_obj = v.v_obj;
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
+ EXPECT_EQ(v.ref_cnt, 0);
+ EXPECT_EQ(v_obj->ref_cnt, 1);
+ EXPECT_EQ(obj.get(), nullptr);
+}
+
+TEST(Any_Constructor_4_TypeTraits, Integer) {
+ auto check = [](TVMFFIAny *v, int64_t source) -> void { EXPECT_EQ(v->v_int64, source); };
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int8_t>(1));
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int16_t>(2));
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int32_t>(3));
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int64_t>(4));
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint8_t>(1));
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint16_t>(2));
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint32_t>(3));
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint64_t>(4));
+}
+
+TEST(Any_Constructor_4_TypeTraits, Float) {
+ auto check = [](TVMFFIAny *v, double source) -> void { EXPECT_EQ(v->v_float64, source); };
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(3));
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(4));
+}
+
+TEST(Any_Constructor_4_TypeTraits, Ptr) {
+ int p = 4;
+ auto check = [](TVMFFIAny *v, void *source) -> void { EXPECT_EQ(v->v_ptr, source); };
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFINone, static_cast<void *>(nullptr));
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIPtr, static_cast<void *>(&p));
+}
+
+TEST(Any_Constructor_4_TypeTraits, Device) {
+ auto check = [](TVMFFIAny *v, const DLDevice &source) -> void {
+ EXPECT_EQ(v->v_device.device_type, source.device_type);
+ EXPECT_EQ(v->v_device.device_id, source.device_id);
+ };
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIDevice, DLDevice{kDLCPU, 0});
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIDevice, DLDevice{kDLCUDA, 1});
+}
+
+TEST(Any_Constructor_4_TypeTraits, DataType) {
+ auto check = [](TVMFFIAny *v, const DLDataType &source) -> void {
+ EXPECT_EQ(v->v_dtype.code, source.code);
+ EXPECT_EQ(v->v_dtype.bits, source.bits);
+ EXPECT_EQ(v->v_dtype.lanes, source.lanes);
+ };
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIDataType, DLDataType{kDLInt, 32, 1});
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIDataType, DLDataType{kDLUInt, 0, 0});
+}
+
+TEST(Any_Constructor_4_TypeTraits, RawStr) {
+ auto check = [](TVMFFIAny *v, const char *source) -> void {
+ Str *str = static_cast<Str *>(v->v_ptr);
+ EXPECT_STREQ(str->c_str(), source);
+ };
+ const char *empty = "";
+ const char *hello = "hello";
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, empty);
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, hello);
+}
+
+TEST(Any_Constructor_4_TypeTraits, CharArray) {
+ auto check = [](TVMFFIAny *v, const char *source) -> void {
+ Str *str = static_cast<Str *>(v->v_ptr);
+ EXPECT_STREQ(str->c_str(), source);
+ };
+ const char empty[] = "";
+ const char hello[] = "hello";
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, empty);
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, hello);
+}
+
+TEST(Any_Constructor_4_TypeTraits, StdString) {
+ auto check = [](TVMFFIAny *v, const std::string &source) -> void {
+ Str *str = static_cast<Str *>(v->v_ptr);
+ EXPECT_EQ(str->c_str(), source);
+ };
+ std::string empty = "";
+ std::string hello = "hello";
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, hello);
+ TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, empty);
+}
+
+TEST(Any_Constructor_5_Object_Ptr, Object) {
+ Ref<Object> obj = Ref<Object>::New();
+ TVMFFIAny *ptr = reinterpret_cast<TVMFFIAny *>(obj.get());
+ {
+ Any v(obj.get());
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
+ EXPECT_EQ(v.v_obj->ref_cnt, 2);
+ EXPECT_EQ(v.v_obj, ptr);
+ }
+ EXPECT_EQ(ptr->ref_cnt, 1);
+}
+
+TEST(Any_Constructor_5_Object_Ptr, Func) {
+ Ref<Func> func = Ref<Func>::New(FuncCall);
+ TVMFFIAny *ptr = reinterpret_cast<TVMFFIAny *>(func.get());
+ {
+ Any v(func.get());
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
+ EXPECT_EQ(v.v_obj->ref_cnt, 2);
+ EXPECT_EQ(v.v_obj, ptr);
+ }
+ EXPECT_EQ(ptr->ref_cnt, 1);
+}
+
+TEST(Any_Constructor_5_Object_Ptr, Str) {
+ Ref<Str> str = Ref<Str>::New("hello");
+ TVMFFIAny *ptr = reinterpret_cast<TVMFFIAny *>(str.get());
+ {
+ Any v(str.get());
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
+ EXPECT_EQ(v.v_obj->ref_cnt, 2);
+ EXPECT_EQ(v.v_obj, ptr);
+ }
+ EXPECT_EQ(ptr->ref_cnt, 1);
+}
+
+TEST(Any_Converter_0_TypeTraits, Integer) {
+ std::vector<Any> vs = AnyArrayFactory();
+ for (const Any &v : vs) {
+ auto convert = [&]() -> int64_t { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
+ EXPECT_EQ(convert(), 1);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `int`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Converter_0_TypeTraits, Float) {
+ std::vector<Any> vs = AnyArrayFactory();
+ for (const Any &v : vs) {
+ auto convert = [&]() -> double { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
+ EXPECT_EQ(convert(), 1.0);
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFloat)) {
+ EXPECT_EQ(convert(), 2.5);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `float`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Converter_0_TypeTraits, Ptr) {
+ std::vector<Any> vs = AnyArrayFactory();
+ for (const Any &v : vs) {
+ auto convert = [&]() -> void * { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ EXPECT_EQ(convert(), nullptr);
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIPtr)) {
+ EXPECT_EQ(convert(), reinterpret_cast<void *>(&FuncCall));
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
+ EXPECT_EQ(convert(), v.v_str);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Ptr`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Converter_0_TypeTraits, Device) {
+ std::vector<Any> vs = AnyArrayFactory();
+ for (const Any &v : vs) {
+ auto convert = [&]() -> DLDevice { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDevice)) {
+ EXPECT_EQ(convert().device_type, kDLCPU);
+ EXPECT_EQ(convert().device_id, 0);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Device`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Converter_0_TypeTraits, DataType) {
+ std::vector<Any> vs = AnyArrayFactory();
+ for (const Any &v : vs) {
+ auto convert = [&]() -> DLDataType { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDataType)) {
+ EXPECT_EQ(convert().code, kDLInt);
+ EXPECT_EQ(convert().bits, 32);
+ EXPECT_EQ(convert().lanes, 1);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `dtype`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Converter_0_TypeTraits, RawStr) {
+ std::vector<Any> vs = AnyArrayFactory();
+ int counter = 0;
+ for (const Any &v : vs) {
+ auto convert = [&]() -> const char * { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
+ ++counter;
+ EXPECT_LE(counter, 3);
+ if (counter == 1) {
+ EXPECT_STREQ(convert(), "Hello (raw str)");
+ } else if (counter == 2) {
+ EXPECT_STREQ(convert(), "World (std::string)");
+ } else if (counter == 3) {
+ EXPECT_STREQ(convert(), "Hello World (Ref<Str>)");
+ }
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `const char *`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Converter_1_AnyView, Any) {
+ std::vector<Any> vs = AnyArrayFactory();
+ for (const Any &v : vs) {
+ auto convert = [&]() -> AnyView { return v; };
+ {
+ AnyView ret = convert();
+ EXPECT_EQ(ret.type_index, v.type_index);
+ EXPECT_EQ(ret.ref_cnt, 0);
+ EXPECT_EQ(v.ref_cnt, 0);
+ EXPECT_EQ(ret.v_obj, v.v_obj);
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Converter_2_Ref, Object) {
+ std::vector<Any> vs = AnyArrayFactory();
+ for (const Any &v : vs) {
+ auto convert = [&]() -> Ref<Object> { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ Ref<Object> ret = convert();
+ EXPECT_EQ(ret.get(), nullptr);
+ } else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
+ Ref<Object> ret = convert();
+ EXPECT_EQ(ret.get(), static_cast<void *>(v.v_obj));
+ EXPECT_EQ(v.v_obj->ref_cnt, 2);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `object.Object`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Converter_2_Ref, Func) {
+ std::vector<Any> views = AnyArrayFactory();
+ for (const Any &v : views) {
+ auto convert = [&]() -> Ref<Func> { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ Ref<Func> ret = convert();
+ EXPECT_EQ(ret.get(), nullptr);
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
+ Ref<Func> ret = convert();
+ EXPECT_EQ(ret.get(), static_cast<void *>(v.v_obj));
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `object.Func`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Converter_2_Ref, Str) {
+ int counter = 0;
+ std::vector<Any> vs = AnyArrayFactory();
+ for (const Any &v : vs) {
+ auto convert = [&]() -> Ref<Str> { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ Ref<Str> ret = convert();
+ EXPECT_EQ(ret.get(), nullptr);
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
+ Ref<Str> ret = convert();
+ ++counter;
+ EXPECT_LE(counter, 3);
+ if (counter == 1) {
+ EXPECT_STREQ(ret->c_str(), "Hello (raw str)");
+ } else if (counter == 2) {
+ EXPECT_STREQ(ret->c_str(), "World (std::string)");
+ } else {
+ EXPECT_STREQ(ret->c_str(), "Hello World (Ref<Str>)");
+ }
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `object.Str`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Converter_3_Object_Ptr, Object) {
+ std::vector<Any> vs = AnyArrayFactory();
+ for (const Any &v : vs) {
+ auto convert = [&]() -> Object * { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ Object *ret = convert();
+ EXPECT_EQ(ret, nullptr);
+ } else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
+ Object *ret = convert();
+ EXPECT_EQ(ret, static_cast<void *>(v.v_obj));
+ EXPECT_EQ(v.v_obj->ref_cnt, 1);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `object.Object *`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Converter_3_Object_Ptr, Func) {
+ std::vector<Any> views = AnyArrayFactory();
+ for (const Any &v : views) {
+ auto convert = [&]() -> Func * { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ Func *ret = convert();
+ EXPECT_EQ(ret, nullptr);
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
+ Func *ret = convert();
+ EXPECT_EQ(ret, reinterpret_cast<Func *>(v.v_ptr));
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `object.Func *`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Converter_3_Object_Ptr, Str) {
+ std::vector<Any> vs = AnyArrayFactory();
+ int counter = 0;
+ for (const Any &v : vs) {
+ auto convert = [&]() -> Str * { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ Str *ret = convert();
+ EXPECT_EQ(ret, nullptr);
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
+ Str *ret = convert();
+ ++counter;
+ EXPECT_LE(counter, 3);
+ if (counter == 1) {
+ EXPECT_STREQ(ret->c_str(), "Hello (raw str)");
+ } else if (counter == 2) {
+ EXPECT_STREQ(ret->c_str(), "World (std::string)");
+ } else {
+ EXPECT_STREQ(ret->c_str(), "Hello World (Ref<Str>)");
+ }
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `object.Str *`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyRefCnt(&v);
+ }
+}
+
+TEST(Any_Stringify, Integer) {
+ TestAnyStringify<int8_t>(-13, TVMFFITypeIndex::kTVMFFIInt, "-13");
+ TestAnyStringify<int16_t>(-5, TVMFFITypeIndex::kTVMFFIInt, "-5");
+ TestAnyStringify<int32_t>(0, TVMFFITypeIndex::kTVMFFIInt, "0");
+ TestAnyStringify<int64_t>(1, TVMFFITypeIndex::kTVMFFIInt, "1");
+}
+
+TEST(Any_Stringify, Float) {
+ auto check = [](const Any &v) -> void {
+ std::string str = v.str()->c_str();
+ double f_str = std::stod(str);
+ double f_src = v.v_float64;
+ EXPECT_NEAR(f_src, f_str, 1e-5);
+ };
+ TestAnyStringifyChecker<float>(float(-3.14), TVMFFITypeIndex::kTVMFFIFloat, check);
+ TestAnyStringifyChecker<double>(0.0, TVMFFITypeIndex::kTVMFFIFloat, check);
+}
+
+TEST(Any_Stringify, Ptr) {
+ auto check = [](const Any &v) -> void {
+ std::string str = v.str()->c_str();
+ EXPECT_GT(str.size(), 2);
+ };
+ TestAnyStringify<void *>(nullptr, TVMFFITypeIndex::kTVMFFINone, "None");
+ TestAnyStringifyChecker<void *>(reinterpret_cast<void *>(FuncCall), TVMFFITypeIndex::kTVMFFIPtr,
+ check);
+}
+
+TEST(Any_Stringify, Device) {
+ TestAnyStringify<DLDevice>(DLDevice{kDLCPU, 0}, TVMFFITypeIndex::kTVMFFIDevice, "cpu:0");
+ TestAnyStringify<DLDevice>(DLDevice{kDLCUDA, 1}, TVMFFITypeIndex::kTVMFFIDevice, "cuda:1");
+}
+
+TEST(Any_Stringify, DataType) {
+ TestAnyStringify<DLDataType>(DLDataType{kDLInt, 32, 1}, TVMFFITypeIndex::kTVMFFIDataType,
+ "int32");
+ TestAnyStringify<DLDataType>(DLDataType{kDLUInt, 1, 1}, TVMFFITypeIndex::kTVMFFIDataType, "bool");
+ TestAnyStringify<DLDataType>(DLDataType{kDLOpaqueHandle, 0, 0}, TVMFFITypeIndex::kTVMFFIDataType,
+ "void");
+ TestAnyStringify<DLDataType>(DLDataType{kDLFloat, 8, 4}, TVMFFITypeIndex::kTVMFFIDataType,
+ "float8x4");
+}
+
+TEST(Any_Stringify, RawStr) {
+ TestAnyStringify<const char *>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
+ TestAnyStringify<char[6]>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
+ TestAnyStringify<const char[6]>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
+}
+
+TEST(Any_Stringify, Object) {
+ auto check = [](const Any &v) -> void {
+ std::string expected_prefix = "object.Object@0";
+ int n = static_cast<int>(expected_prefix.size());
+ std::string str = v.str()->c_str();
+ EXPECT_GT(str.size(), n);
+ EXPECT_EQ(str.substr(0, n), expected_prefix);
+ };
+ TestAnyStringifyChecker<Ref<Object>>(Ref<Object>::New(), TVMFFITypeIndex::kTVMFFIObject, check);
+}
+
+TEST(Any_Stringify, Func) {
+ auto check = [](const Any &v) -> void {
+ std::string expected_prefix = "object.Func@0";
+ int n = static_cast<int>(expected_prefix.size());
+ std::string str = v.str()->c_str();
+ EXPECT_GT(str.size(), n);
+ EXPECT_EQ(str.substr(0, n), expected_prefix);
+ };
+ TestAnyStringifyChecker<Ref<Func>>(Ref<Func>::New(FuncCall), TVMFFITypeIndex::kTVMFFIFunc, check);
+}
+
+TEST(Any_Stringify, Str) {
+ auto check = [](const Any &v) -> void {
+ std::string str = v.str()->c_str();
+ EXPECT_EQ(str, "\"Hello World\"");
+ };
+ TestAnyStringifyChecker<Ref<Str>>(Ref<Str>::New("Hello World"), TVMFFITypeIndex::kTVMFFIStr,
+ check);
+}
+
+} // namespace
diff --git a/ffi/tests/cpp/test_any_view.cc b/ffi/tests/cpp/test_any_view.cc
new file mode 100644
index 0000000000..67aaa9de3a
--- /dev/null
+++ b/ffi/tests/cpp/test_any_view.cc
@@ -0,0 +1,630 @@
+#include <gtest/gtest.h>
+#include <tvm/ffi/ffi.hpp>
+
+namespace {
+using namespace tvm::ffi;
+
+template <typename SrcType, typename Checker>
+void TestAnyViewConstructor(Checker check, TVMFFITypeIndex expected_type_index,
+ const SrcType &source) {
+ AnyView v(source);
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
+ EXPECT_EQ(v.ref_cnt, 0);
+ check(&v, source);
+};
+
+int64_t FuncCall(int64_t x) { return x + 1; }
+
+std::vector<AnyView> AnyViewArrayFactory() {
+ static const char *raw_str = "Hello (raw str)";
+ static std::string std_str = "World (std::string)";
+ static std::string ref_str = "Hello World (Ref<Str>)";
+ static Ref<Object> obj = Ref<Object>::New();
+ static Ref<Func> func = Ref<Func>::New(FuncCall);
+ static Ref<Str> str = Ref<Str>::New(ref_str);
+ return std::vector<AnyView>{
+ AnyView(nullptr),
+ AnyView(1),
+ AnyView(2.5),
+ AnyView(reinterpret_cast<void *>(FuncCall)),
+ AnyView(DLDevice{kDLCPU, 0}),
+ AnyView(DLDataType{kDLInt, 32, 1}),
+ AnyView(raw_str),
+ AnyView(obj),
+ AnyView(func),
+ AnyView(std_str), // TODO: disable AnyView(std::string&&)
+ AnyView(str),
+ };
+}
+
+template <typename SrcType>
+void TestAnyViewStringify(const SrcType &source, TVMFFITypeIndex expected_type_index,
+ const std::string &expected) {
+ AnyView v(source);
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
+ EXPECT_EQ(v.str()->c_str(), expected);
+}
+
+template <typename SrcType, typename Checker>
+void TestAnyViewStringifyChecker(const SrcType &source, TVMFFITypeIndex expected_type_index,
+ Checker check) {
+ AnyView v(source);
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
+ check(v);
+}
+
+void CheckAnyViewRefCnt(const TVMFFIAny *v) {
+ if (v->type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
+ EXPECT_EQ(v->v_obj->ref_cnt, 1);
+ }
+}
+
+TEST(AnyView_Constructor_0_Default, Default) {
+ AnyView v;
+ EXPECT_EQ(v.type_index, 0);
+ EXPECT_EQ(v.ref_cnt, 0);
+ EXPECT_EQ(v.v_int64, 0);
+}
+
+TEST(AnyView_Constructor_1_AnyView, Copy) {
+ AnyView v1(1);
+ AnyView v2(v1);
+ EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
+ EXPECT_EQ(v1.v_int64, 1);
+ EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
+ EXPECT_EQ(v2.v_int64, 1);
+}
+
+TEST(AnyView_Constructor_1_AnyView, Move) {
+ AnyView v1(1);
+ AnyView v2(std::move(v1));
+ EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone));
+ EXPECT_EQ(v1.v_int64, 0);
+ EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
+ EXPECT_EQ(v2.v_int64, 1);
+}
+
+TEST(AnyView_Constructor_2_Any, Copy) {
+ Any v1(1);
+ AnyView v2(v1);
+ EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
+ EXPECT_EQ(v1.v_int64, 1);
+ EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
+ EXPECT_EQ(v2.v_int64, 1);
+}
+
+TEST(AnyView_Constructor_2_Any, Move) {
+ // ---- The following behavior is disallowed ----
+ // Any v1(1);
+ // AnyView v2(std::move(v1));
+}
+
+TEST(AnyView_Constructor_3_Ref, Copy) {
+ Ref<Object> obj = Ref<Object>::New();
+ AnyView v(obj);
+ const TVMFFIAny *v_obj = v.v_obj;
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
+ EXPECT_EQ(v.ref_cnt, 0);
+ EXPECT_EQ(v_obj, static_cast<const void *>(obj.get()));
+ EXPECT_EQ(v_obj->ref_cnt, 1);
+}
+
+TEST(AnyView_Constructor_3_Ref, Move) {
+ // The following behavior is disallowed
+ // Ref<Object> obj = Ref<Object>::New();
+ // AnyView v(std::move(obj));
+}
+
+TEST(AnyView_Constructor_4_TypeTraits, Integer) {
+ auto check = [](TVMFFIAny *v, int64_t source) -> void { EXPECT_EQ(v->v_int64, source); };
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int8_t>(1));
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int16_t>(2));
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int32_t>(3));
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int64_t>(4));
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint8_t>(1));
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint16_t>(2));
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint32_t>(3));
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint64_t>(4));
+}
+
+TEST(AnyView_Constructor_4_TypeTraits, Float) {
+ auto check = [](TVMFFIAny *v, double source) -> void { EXPECT_EQ(v->v_float64, source); };
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(3));
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(4));
+}
+
+TEST(AnyView_Constructor_4_TypeTraits, Ptr) {
+ int p = 4;
+ auto check = [](TVMFFIAny *v, void *source) -> void { EXPECT_EQ(v->v_ptr, source); };
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFINone, static_cast<void *>(nullptr));
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIPtr, static_cast<void *>(&p));
+}
+
+TEST(AnyView_Constructor_4_TypeTraits, Device) {
+ auto check = [](TVMFFIAny *v, const DLDevice &source) -> void {
+ EXPECT_EQ(v->v_device.device_type, source.device_type);
+ EXPECT_EQ(v->v_device.device_id, source.device_id);
+ };
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIDevice, DLDevice{kDLCPU, 0});
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIDevice, DLDevice{kDLCUDA, 1});
+}
+
+TEST(AnyView_Constructor_4_TypeTraits, DataType) {
+ auto check = [](TVMFFIAny *v, const DLDataType &source) -> void {
+ EXPECT_EQ(v->v_dtype.code, source.code);
+ EXPECT_EQ(v->v_dtype.bits, source.bits);
+ EXPECT_EQ(v->v_dtype.lanes, source.lanes);
+ };
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIDataType, DLDataType{kDLInt, 32, 1});
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIDataType, DLDataType{kDLUInt, 0, 0});
+}
+
+TEST(AnyView_Constructor_4_TypeTraits, RawStr) {
+ auto check = [](TVMFFIAny *v, const char *source) -> void { EXPECT_EQ(v->v_str, source); };
+ const char *empty = "";
+ const char *hello = "hello";
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, empty);
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, hello);
+}
+
+TEST(AnyView_Constructor_4_TypeTraits, CharArray) {
+ auto check = [](TVMFFIAny *v, const char *source) -> void { EXPECT_EQ(v->v_str, source); };
+ const char empty[] = "";
+ const char hello[] = "hello";
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, empty);
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, hello);
+}
+
+TEST(AnyView_Constructor_4_TypeTraits, StdString) {
+ auto check = [](TVMFFIAny *v, const std::string &source) -> void {
+ EXPECT_EQ(v->v_str, source.data());
+ };
+ std::string empty = "";
+ std::string hello = "hello";
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, hello);
+ TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, empty);
+}
+
+TEST(AnyView_Constructor_5_Object_Ptr, Object) {
+ Ref<Object> obj = Ref<Object>::New();
+ AnyView v(obj.get());
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
+ EXPECT_EQ(v.v_obj->ref_cnt, 1);
+ EXPECT_EQ(v.v_obj, static_cast<void *>(obj.get()));
+}
+
+TEST(AnyView_Constructor_5_Object_Ptr, Func) {
+ Ref<Func> func = Ref<Func>::New(FuncCall);
+ AnyView v(func.get());
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
+ EXPECT_EQ(v.v_obj->ref_cnt, 1);
+ EXPECT_EQ(v.v_ptr, static_cast<void *>(func.get()));
+}
+
+TEST(AnyView_Constructor_5_Object_Ptr, Str) {
+ Ref<Str> str = Ref<Str>::New("hello");
+ AnyView v(str.get());
+ EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
+ EXPECT_EQ(v.v_obj->ref_cnt, 1);
+ EXPECT_EQ(v.v_obj, static_cast<void *>(str.get()));
+}
+
+TEST(AnyView_Converter_0_TypeTraits, Integer) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ for (const AnyView &v : views) {
+ auto convert = [&]() -> int64_t { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
+ EXPECT_EQ(convert(), 1);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `int`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyViewRefCnt(&v);
+ }
+}
+
+TEST(AnyView_Converter_0_TypeTraits, Float) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ for (const AnyView &v : views) {
+ auto convert = [&]() -> double { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
+ EXPECT_EQ(convert(), 1.0);
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFloat)) {
+ EXPECT_EQ(convert(), 2.5);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `float`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyViewRefCnt(&v);
+ }
+}
+
+TEST(AnyView_Converter_0_TypeTraits, Ptr) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ for (const AnyView &v : views) {
+ auto convert = [&]() -> void * { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ EXPECT_EQ(convert(), nullptr);
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIPtr)) {
+ EXPECT_EQ(convert(), reinterpret_cast<void *>(&FuncCall));
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
+ EXPECT_EQ(convert(), v.v_str);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Ptr`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyViewRefCnt(&v);
+ }
+}
+
+TEST(AnyView_Converter_0_TypeTraits, Device) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ for (const AnyView &v : views) {
+ auto convert = [&]() -> DLDevice { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDevice)) {
+ EXPECT_EQ(convert().device_type, kDLCPU);
+ EXPECT_EQ(convert().device_id, 0);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Device`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyViewRefCnt(&v);
+ }
+}
+
+TEST(AnyView_Converter_0_TypeTraits, DataType) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ for (const AnyView &v : views) {
+ auto convert = [&]() -> DLDataType { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDataType)) {
+ EXPECT_EQ(convert().code, kDLInt);
+ EXPECT_EQ(convert().bits, 32);
+ EXPECT_EQ(convert().lanes, 1);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `dtype`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyViewRefCnt(&v);
+ }
+}
+
+TEST(AnyView_Converter_0_TypeTraits, RawStr) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ int counter = 0;
+ for (const AnyView &v : views) {
+ auto convert = [&]() -> const char * { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
+ counter += 1;
+ EXPECT_LT(counter, 3);
+ if (counter == 1) {
+ EXPECT_STREQ(convert(), "Hello (raw str)");
+ } else if (counter == 2) {
+ EXPECT_STREQ(convert(), "World (std::string)");
+ }
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
+ EXPECT_STREQ(convert(), "Hello World (Ref<Str>)");
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `const char *`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyViewRefCnt(&v);
+ }
+}
+
+TEST(AnyView_Converter_0_TypeTraits, RawStrToStrStar_Fail) {
+ AnyView v = "Hello";
+ try {
+ Str *v_str = v;
+ (void)v_str;
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ EXPECT_STREQ(ex.what(), "Cannot convert from type `const char *` to `object.Str *`");
+ }
+}
+
+TEST(AnyView_Converter_0_TypeTraits, RawStrToStrStar_WrithStorage) {
+ Any storage;
+ AnyView v = "Hello";
+ Str *v_str = v.CastWithStorage<Str *>(&storage);
+ EXPECT_STREQ(v_str->c_str(), "Hello");
+}
+
+TEST(AnyView_Converter_1_Any, Any) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ for (const AnyView &view : views) {
+ auto convert = [&]() -> Any { return view; };
+ {
+ Any ret = convert();
+ EXPECT_EQ(view.ref_cnt, 0);
+ if (view.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
+ Str *str = ret;
+ EXPECT_EQ(ret.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
+ EXPECT_STREQ(str->c_str(), view.v_str);
+ EXPECT_EQ(ret.ref_cnt, 0);
+ } else {
+ EXPECT_EQ(ret.type_index, view.type_index);
+ EXPECT_EQ(ret.ref_cnt, 0);
+ EXPECT_EQ(ret.v_obj, view.v_obj);
+ }
+ }
+ CheckAnyViewRefCnt(&view);
+ }
+}
+
+TEST(AnyView_Converter_2_Ref, Object) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ for (const AnyView &v : views) {
+ auto convert = [&]() -> Ref<Object> { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ Ref<Object> ret = convert();
+ EXPECT_EQ(ret.get(), nullptr);
+ } else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
+ Ref<Object> ret = convert();
+ EXPECT_EQ(ret.get(), static_cast<void *>(v.v_obj));
+ EXPECT_EQ(v.v_obj->ref_cnt, 2);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `object.Object`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyViewRefCnt(&v);
+ }
+}
+
+TEST(AnyView_Converter_2_Ref, Func) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ for (const AnyView &v : views) {
+ auto convert = [&]() -> Ref<Func> { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ Ref<Func> ret = convert();
+ EXPECT_EQ(ret.get(), nullptr);
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
+ Ref<Func> ret = convert();
+ EXPECT_EQ(ret.get(), static_cast<void *>(v.v_obj));
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `object.Func`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyViewRefCnt(&v);
+ }
+}
+
+TEST(AnyView_Converter_2_Ref, Str) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ for (const AnyView &v : views) {
+ auto convert = [&]() -> Ref<Str> { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ Ref<Str> ret = convert();
+ EXPECT_EQ(ret.get(), nullptr);
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
+ Ref<Str> ret = convert();
+ EXPECT_STREQ(ret->c_str(), "Hello World (Ref<Str>)");
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
+ Ref<Str> ret = convert();
+ EXPECT_EQ(reinterpret_cast<TVMFFIStr *>(ret.get())->ref_cnt, 1);
+ EXPECT_STREQ(ret->c_str(), v.v_str);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `object.Str`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyViewRefCnt(&v);
+ }
+}
+
+TEST(AnyView_Converter_3_Object_Ptr, Object) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ for (const AnyView &v : views) {
+ auto convert = [&]() -> Object * { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ Object *ret = convert();
+ EXPECT_EQ(ret, nullptr);
+ } else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
+ Object *ret = convert();
+ EXPECT_EQ(ret, static_cast<void *>(v.v_obj));
+ EXPECT_EQ(v.v_obj->ref_cnt, 1);
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `object.Object *`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyViewRefCnt(&v);
+ }
+}
+
+TEST(AnyView_Converter_3_Object_Ptr, Func) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ for (const AnyView &v : views) {
+ auto convert = [&]() -> Func * { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ Func *ret = convert();
+ EXPECT_EQ(ret, nullptr);
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
+ Func *ret = convert();
+ EXPECT_EQ(ret, reinterpret_cast<Func *>(v.v_ptr));
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `object.Func *`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyViewRefCnt(&v);
+ }
+}
+
+TEST(AnyView_Converter_3_Object_Ptr, Str) {
+ std::vector<AnyView> views = AnyViewArrayFactory();
+ for (const AnyView &v : views) {
+ auto convert = [&]() -> Str * { return v; };
+ if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
+ Str *ret = convert();
+ EXPECT_EQ(ret, nullptr);
+ } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
+ Str *ret = convert();
+ EXPECT_STREQ(ret->c_str(), "Hello World (Ref<Str>)");
+ } else {
+ try {
+ convert();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ std::ostringstream os;
+ os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
+ << "` to `object.Str *`";
+ EXPECT_EQ(ex.what(), os.str());
+ }
+ }
+ CheckAnyViewRefCnt(&v);
+ }
+}
+
+TEST(AnyView_Stringify, Integer) {
+ TestAnyViewStringify<int8_t>(-13, TVMFFITypeIndex::kTVMFFIInt, "-13");
+ TestAnyViewStringify<int16_t>(-5, TVMFFITypeIndex::kTVMFFIInt, "-5");
+ TestAnyViewStringify<int32_t>(0, TVMFFITypeIndex::kTVMFFIInt, "0");
+ TestAnyViewStringify<int64_t>(1, TVMFFITypeIndex::kTVMFFIInt, "1");
+}
+
+TEST(AnyView_Stringify, Float) {
+ auto check = [](const AnyView &v) -> void {
+ std::string str = v.str()->c_str();
+ double f_str = std::stod(str);
+ double f_src = v.v_float64;
+ EXPECT_NEAR(f_src, f_str, 1e-5);
+ };
+ TestAnyViewStringifyChecker<float>(float(-3.14), TVMFFITypeIndex::kTVMFFIFloat, check);
+ TestAnyViewStringifyChecker<double>(0.0, TVMFFITypeIndex::kTVMFFIFloat, check);
+}
+
+TEST(AnyView_Stringify, Ptr) {
+ auto check = [](const AnyView &v) -> void {
+ std::string str = v.str()->c_str();
+ EXPECT_GT(str.size(), 2);
+ };
+ TestAnyViewStringify<void *>(nullptr, TVMFFITypeIndex::kTVMFFINone, "None");
+ TestAnyViewStringifyChecker<void *>(reinterpret_cast<void *>(FuncCall),
+ TVMFFITypeIndex::kTVMFFIPtr, check);
+}
+
+TEST(AnyView_Stringify, Device) {
+ TestAnyViewStringify<DLDevice>(DLDevice{kDLCPU, 0}, TVMFFITypeIndex::kTVMFFIDevice, "cpu:0");
+ TestAnyViewStringify<DLDevice>(DLDevice{kDLCUDA, 1}, TVMFFITypeIndex::kTVMFFIDevice, "cuda:1");
+}
+
+TEST(AnyView_Stringify, DataType) {
+ TestAnyViewStringify<DLDataType>(DLDataType{kDLInt, 32, 1}, TVMFFITypeIndex::kTVMFFIDataType,
+ "int32");
+ TestAnyViewStringify<DLDataType>(DLDataType{kDLUInt, 1, 1}, TVMFFITypeIndex::kTVMFFIDataType,
+ "bool");
+ TestAnyViewStringify<DLDataType>(DLDataType{kDLOpaqueHandle, 0, 0},
+ TVMFFITypeIndex::kTVMFFIDataType, "void");
+ TestAnyViewStringify<DLDataType>(DLDataType{kDLFloat, 8, 4}, TVMFFITypeIndex::kTVMFFIDataType,
+ "float8x4");
+}
+
+TEST(AnyView_Stringify, RawStr) {
+ TestAnyViewStringify<const char *>("Hello", TVMFFITypeIndex::kTVMFFIRawStr, "\"Hello\"");
+ TestAnyViewStringify<char[6]>("Hello", TVMFFITypeIndex::kTVMFFIRawStr, "\"Hello\"");
+ TestAnyViewStringify<const char[6]>("Hello", TVMFFITypeIndex::kTVMFFIRawStr, "\"Hello\"");
+}
+
+TEST(AnyView_Stringify, Object) {
+ auto check = [](const AnyView &v) -> void {
+ std::string expected_prefix = "object.Object@0";
+ int n = static_cast<int>(expected_prefix.size());
+ std::string str = v.str()->c_str();
+ EXPECT_GT(str.size(), n);
+ EXPECT_EQ(str.substr(0, n), expected_prefix);
+ };
+ TestAnyViewStringifyChecker<Ref<Object>>(Ref<Object>::New(), TVMFFITypeIndex::kTVMFFIObject,
+ check);
+}
+
+TEST(AnyView_Stringify, Func) {
+ auto check = [](const AnyView &v) -> void {
+ std::string expected_prefix = "object.Func@0";
+ int n = static_cast<int>(expected_prefix.size());
+ std::string str = v.str()->c_str();
+ EXPECT_GT(str.size(), n);
+ EXPECT_EQ(str.substr(0, n), expected_prefix);
+ };
+ TestAnyViewStringifyChecker<Ref<Func>>(Ref<Func>::New(FuncCall), TVMFFITypeIndex::kTVMFFIFunc,
+ check);
+}
+
+TEST(AnyView_Stringify, Str) {
+ auto check = [](const AnyView &v) -> void {
+ std::string str = v.str()->c_str();
+ EXPECT_EQ(str, "\"Hello World\"");
+ };
+ TestAnyViewStringifyChecker<Ref<Str>>(Ref<Str>::New("Hello World"), TVMFFITypeIndex::kTVMFFIStr,
+ check);
+}
+
+} // namespace
diff --git a/ffi/tests/cpp/test_dict.cc b/ffi/tests/cpp/test_dict.cc
new file mode 100644
index 0000000000..04d5a2854f
--- /dev/null
+++ b/ffi/tests/cpp/test_dict.cc
@@ -0,0 +1,183 @@
+#include <gtest/gtest.h>
+#include <tvm/ffi/ffi.hpp>
+#include <unordered_set>
+
+namespace {
+
+using namespace tvm::ffi;
+
+bool DTypeEqual(DLDataType a, DLDataType b) {
+ return a.code == b.code && a.bits == b.bits && a.lanes == b.lanes;
+}
+// bool DeviceEqual(DLDevice a, DLDevice b) {
+// return a.device_type == b.device_type && a.device_id == b.device_id;
+// }
+
+TEST(Dict_Construtor, Default) {
+ Ref<Dict> dict;
+ ASSERT_EQ(dict.size(), 0);
+ TVMFFIDict *dict_ptr = reinterpret_cast<TVMFFIDict *>(dict.get());
+ EXPECT_EQ(dict_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDict));
+ EXPECT_EQ(dict_ptr->ref_cnt, 1);
+ EXPECT_NE(dict_ptr->deleter, nullptr);
+ EXPECT_EQ(dict_ptr->size, 0);
+ EXPECT_EQ(dict_ptr->capacity, 0);
+}
+
+TEST(Dict_Construtor, InitializerList) {
+ Ref<Dict> dict{{"key1", 1}, {"key2", "value2"}, {3, 4}};
+ EXPECT_EQ(dict.size(), 3);
+ EXPECT_EQ(int(dict["key1"]), 1);
+ EXPECT_EQ(std::string(dict["key2"]), "value2");
+ EXPECT_EQ(int(dict[3]), 4);
+
+ bool found[3] = {false, false, false};
+ for (const auto &kv : dict) {
+ if (AnyEqual()(kv.first, Any("key1"))) {
+ found[0] = true;
+ EXPECT_EQ(int(kv.second), 1);
+ } else if (AnyEqual()(kv.first, Any("key2"))) {
+ found[1] = true;
+ EXPECT_EQ(std::string(kv.second), "value2");
+ } else if (AnyEqual()(kv.first, Any(3))) {
+ found[2] = true;
+ EXPECT_EQ(int(kv.second), 4);
+ } else {
+ FAIL() << "Unexpected key: " << kv.first;
+ }
+ }
+ EXPECT_TRUE(found[0]);
+ EXPECT_TRUE(found[1]);
+ EXPECT_TRUE(found[2]);
+}
+
+TEST(Dict_Insert, New) {
+ int64_t integer = 100;
+ double fp = 1.0;
+ std::string str = "Hi";
+ DLDataType dtype{kDLInt, 32, 1};
+ DLDevice device{kDLCPU, 0};
+ Ref<Object> obj = Ref<Object>::New();
+ Ref<Object> null_obj{nullptr};
+ Ref<Dict> dict{{integer, fp}, {str, dtype}, {null_obj, 0}};
+ dict[device] = null_obj;
+ EXPECT_EQ(dict.size(), 4);
+ EXPECT_DOUBLE_EQ(double(dict[integer]), fp);
+ EXPECT_PRED2(DTypeEqual, DLDataType(dict[str]), dtype);
+ EXPECT_EQ(int(dict[null_obj]), 0);
+ EXPECT_EQ((Object *)(dict[device]), nullptr);
+}
+
+TEST(Dict_Insert, Override) {
+ Ref<Dict> dict{{"key1", 1}, {"key2", "value2"}, {3, 4}};
+ EXPECT_EQ(dict.size(), 3);
+ dict["key1"] = 2;
+ dict["key2"] = "new_value";
+ dict[3] = 5;
+ EXPECT_EQ(dict.size(), 3);
+ EXPECT_EQ(int(dict["key1"]), 2);
+ EXPECT_EQ(std::string(dict["key2"]), "new_value");
+ EXPECT_EQ(int(dict[3]), 5);
+}
+
+TEST(Dict_At, Found) {
+ int64_t integer = 100;
+ double fp = 1.0;
+ std::string str = "Hi";
+ DLDataType dtype{kDLInt, 32, 1};
+ Ref<Object> obj = Ref<Object>::New();
+ Ref<Object> null_obj{nullptr};
+ Ref<Dict> dict{{integer, fp}, {str, dtype}, {null_obj, 0}};
+ EXPECT_DOUBLE_EQ(double(dict.at(integer)), fp);
+ EXPECT_PRED2(DTypeEqual, DLDataType(dict.at(str)), dtype);
+ EXPECT_EQ(int(dict.at(null_obj)), 0);
+}
+
+TEST(Dict_At, NotFound) {
+ Ref<Dict> dict{{"key1", 1}, {"key2", "value2"}, {3, 4}};
+ try {
+ dict.at("key3");
+ FAIL() << "Expected TVMError";
+ } catch (const TVMError &e) {
+ }
+}
+
+TEST(Dict_ReHash, POD) {
+ Ref<Dict> dict;
+ for (int j = 0; j < 1000; ++j) {
+ dict[j] = j;
+ }
+ EXPECT_EQ(dict.size(), 1000);
+ std::unordered_set<int64_t> keys;
+ for (auto &kv : dict) {
+ int64_t key = kv.first;
+ int64_t value = kv.second;
+ EXPECT_EQ(key, value);
+ EXPECT_FALSE(keys.count(key));
+ EXPECT_EQ(key, value);
+ EXPECT_TRUE(0 <= key && key < 1000);
+ }
+ EXPECT_EQ(dict.size(), 1000);
+}
+
+TEST(Dict_ReHash, Object) {
+ std::vector<Ref<Object>> objs;
+ std::unordered_map<Object *, int64_t> obj_map;
+ for (int j = 0; j < 1000; ++j) {
+ objs.push_back(Ref<Object>::New());
+ obj_map[objs[j].get()] = j;
+ }
+ Ref<Dict> dict;
+ for (int j = 0; j < 1000; ++j) {
+ dict[objs[j]] = j;
+ }
+ EXPECT_EQ(dict.size(), 1000);
+ std::unordered_set<Object *> keys;
+ for (auto &kv : dict) {
+ Ref<Object> key = kv.first;
+ int64_t value = kv.second;
+ keys.insert(key.get());
+ EXPECT_EQ(value, obj_map[key.get()]);
+ }
+ EXPECT_EQ(dict.size(), 1000);
+}
+
+TEST(Dict_Erase, POD) {
+ Ref<Dict> dict;
+ for (int j = 0; j < 1000; ++j) {
+ dict[j] = j;
+ }
+ EXPECT_EQ(dict.size(), 1000);
+ for (int j = 0; j < 1000; ++j) {
+ dict.erase(j);
+ EXPECT_EQ(dict.size(), 1000 - j - 1);
+ }
+ for (int j = 0; j < 1000; ++j) {
+ dict[j] = j;
+ EXPECT_EQ(dict.size(), j + 1);
+ }
+}
+
+TEST(Dict_Erase, Object) {
+ std::vector<Ref<Object>> objs;
+ std::unordered_map<Object *, int64_t> obj_map;
+ for (int j = 0; j < 1000; ++j) {
+ objs.push_back(Ref<Object>::New());
+ obj_map[objs[j].get()] = j;
+ }
+ Ref<Dict> dict;
+ for (int j = 0; j < 1000; ++j) {
+ dict[objs[j]] = j;
+ }
+ EXPECT_EQ(dict.size(), 1000);
+ for (int j = 0; j < 1000; ++j) {
+ dict.erase(objs[j]);
+ EXPECT_EQ(dict.size(), 1000 - j - 1);
+ }
+ for (int j = 0; j < 1000; ++j) {
+ dict[objs[j]] = j;
+ EXPECT_EQ(dict.size(), j + 1);
+ }
+}
+
+} // namespace
diff --git a/ffi/tests/cpp/test_func.cc b/ffi/tests/cpp/test_func.cc
new file mode 100644
index 0000000000..32512d3dcf
--- /dev/null
+++ b/ffi/tests/cpp/test_func.cc
@@ -0,0 +1,215 @@
+#include <gtest/gtest.h>
+#include <tvm/ffi/ffi.hpp>
+
+namespace {
+using namespace tvm::ffi;
+
+const char *c_str_raw = "Hello";
+
+double func_unpacked_0(int64_t a, double b, const char *c, const double &d) {
+ EXPECT_STREQ(c, c_str_raw);
+ return a + b + d;
+}
+
+void func_unpacked_1(DLDataType dtype, DLDevice device, std::string str) {
+ (void)dtype;
+ (void)device;
+ (void)str;
+}
+
+void func_packed_0(int num_args, const AnyView *, Any *ret) { *ret = num_args; }
+
+template <enum TVMFFITypeIndex type_index>
+void func_unpacked_anyview_arg(AnyView a) {
+ EXPECT_EQ(a.type_index, static_cast<int32_t>(type_index));
+}
+template <enum TVMFFITypeIndex type_index>
+void func_unpacked_any_arg(Any a) {
+ EXPECT_EQ(a.type_index, static_cast<int32_t>(type_index));
+}
+AnyView func_unpacked_anyview_ret() { return AnyView(1); }
+Any func_unpacked_any_ret() { return Any(1); }
+
+std::string func_unpacked_str_obj(Str *str, const char *str_2) {
+ EXPECT_EQ(reinterpret_cast<TVMFFIStr *>(str)->ref_cnt, 1);
+ EXPECT_STREQ(str->c_str(), str_2);
+ return str->c_str();
+}
+
+TEST(Func_Signature, 0) {
+ EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_0)>::Sig(),
+ "(0: int, 1: float, 2: const char *, 3: float) -> float");
+}
+
+TEST(Func_Signature, 1) {
+ EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_1)>::Sig(),
+ "(0: dtype, 1: Device, 2: str) -> void");
+}
+
+TEST(Func_Signature, AnyView_Arg) {
+ EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_anyview_arg<
+ TVMFFITypeIndex::kTVMFFIInt>)>::Sig(),
+ "(0: AnyView) -> void");
+}
+
+TEST(Func_Signature, AnyView_Ret) {
+ EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_anyview_ret)>::Sig(),
+ "() -> AnyView");
+}
+
+TEST(Func_Signature, Any_Arg) {
+ EXPECT_EQ(
+ details::FuncFunctor<
+ decltype(func_unpacked_any_arg<TVMFFITypeIndex::kTVMFFIInt>)>::Sig(),
+ "(0: Any) -> void");
+}
+
+TEST(Func_Signature, Any_Ret) {
+ EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_any_ret)>::Sig(),
+ "() -> Any");
+}
+
+TEST(Func_Unpacked_Invoke, Func0_RawStr) {
+ Ref<Func> func = Ref<Func>::New(func_unpacked_0);
+ double ret = func(1, 2, c_str_raw, 4);
+ EXPECT_DOUBLE_EQ(ret, 7);
+ double ret2 = func(1, 2, std::string("Hello"), 4);
+ EXPECT_DOUBLE_EQ(ret2, 7);
+}
+
+TEST(Func_Unpacked_Invoke, Func0_StdString_Move) {
+ std::string str = c_str_raw;
+ Ref<Func> func = Ref<Func>::New(func_unpacked_0);
+ double ret = func(1, 2, std::move(str), 4);
+ EXPECT_DOUBLE_EQ(ret, 7);
+}
+
+TEST(Func_Unpacked_Invoke, Func0_StdString_Copy) {
+ std::string str = c_str_raw;
+ Ref<Func> func = Ref<Func>::New(func_unpacked_0);
+ double ret = func(1, 2, str, 4);
+ EXPECT_DOUBLE_EQ(ret, 7);
+ EXPECT_EQ(str, c_str_raw);
+}
+
+TEST(Func_Unpacked_Invoke, Func1) {
+ Ref<Func> func = Ref<Func>::New(func_unpacked_1);
+ func(DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, "Hello");
+}
+
+TEST(Func_Unpacked_Invoke, AnyView_Arg) {
+ Ref<Func> func =
+ Ref<Func>::New(func_unpacked_anyview_arg<TVMFFITypeIndex::kTVMFFIInt>);
+ func(1);
+}
+
+TEST(Func_Unpacked_Invoke, AnyView_Ret) {
+ Ref<Func> func = Ref<Func>::New(func_unpacked_anyview_ret);
+ int ret = func();
+ EXPECT_EQ(ret, 1);
+}
+
+TEST(Func_Unpacked_Invoke, Any_Arg) {
+ Ref<Func> func =
+ Ref<Func>::New(func_unpacked_any_arg<TVMFFITypeIndex::kTVMFFIInt>);
+ func(1);
+}
+
+TEST(Func_Unpacked_Invoke, Any_Ret) {
+ Ref<Func> func = Ref<Func>::New(func_unpacked_any_ret);
+ int ret = func();
+ EXPECT_EQ(ret, 1);
+}
+
+TEST(Func_Unpacked_Invoke, StrObj) {
+ Ref<Func> func = Ref<Func>::New(func_unpacked_str_obj);
+ std::string ret = func("Hello", "Hello");
+ EXPECT_EQ(ret, "Hello");
+}
+
+TEST(Func_Packed_Invoke, 0) {
+ Ref<Func> func = Ref<Func>::New(func_packed_0);
+ int ret = func();
+ EXPECT_EQ(ret, 0);
+}
+
+TEST(Func_Packed_Invoke, 1) {
+ Ref<Func> func = Ref<Func>::New(func_packed_0);
+ int ret = func(1.0);
+ EXPECT_EQ(ret, 1);
+}
+
+TEST(Func_Packed_Invoke, 2) {
+ Ref<Func> func = Ref<Func>::New(func_packed_0);
+ int ret = func(1.0, "test");
+ EXPECT_EQ(ret, 2);
+}
+
+TEST(Func_Packed_Invoke, 4) {
+ Ref<Func> func = Ref<Func>::New(func_packed_0);
+ int ret = func(1.0, "test", DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0});
+ EXPECT_EQ(ret, 4);
+}
+
+TEST(Func_Unpacked_Invoke_TypeError, TypeMismatch_0) {
+ Ref<Func> func = Ref<Func>::New(func_unpacked_0);
+ try {
+ func(1.0, 2, c_str_raw, 4);
+ FAIL() << "No execption thrown";
+ } catch (TVMError &ex) {
+ EXPECT_STREQ(ex.what(),
+ "Mismatched type on argument #0 when calling: "
+ "`(0: int, 1: float, 2: const char *, 3: float) -> float`. "
+ "Expected `int` but got `float`");
+ }
+}
+
+TEST(Func_Unpacked_Invoke_TypeError, TypeMismatch_1) {
+ Ref<Func> func = Ref<Func>::New(func_unpacked_1);
+ try {
+ func(DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, 1);
+ FAIL() << "No execption thrown";
+ } catch (TVMError &ex) {
+ EXPECT_STREQ(ex.what(), "Mismatched type on argument #2 when calling: "
+ "`(0: dtype, 1: Device, 2: str) -> void`. "
+ "Expected `str` but got `int`");
+ }
+}
+
+TEST(Func_Unpacked_Invoke_TypeError, ArgCountMismatch_0) {
+ Ref<Func> func = Ref<Func>::New(func_unpacked_0);
+ try {
+ func(1, 2, c_str_raw);
+ FAIL() << "No execption thrown";
+ } catch (TVMError &ex) {
+ EXPECT_STREQ(ex.what(),
+ "Mismatched number of arguments when calling: "
+ "`(0: int, 1: float, 2: const char *, 3: float) -> float`. "
+ "Expected 4 but got 3 arguments");
+ }
+}
+
+TEST(Func_Unpacked_Invoke_TypeError, ArgCountMismatch_1) {
+ Ref<Func> func = Ref<Func>::New(func_unpacked_1);
+ try {
+ func(DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0});
+ FAIL() << "No execption thrown";
+ } catch (TVMError &ex) {
+ EXPECT_STREQ(ex.what(), "Mismatched number of arguments when calling: "
+ "`(0: dtype, 1: Device, 2: str) -> void`. "
+ "Expected 3 but got 2 arguments");
+ }
+}
+
+TEST(Func_Unpacked_Invoke_TypeError, ReturnTypeMismatch_0) {
+ Ref<Func> func = Ref<Func>::New(func_unpacked_1);
+ try {
+ int ret = func(DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, "Hello");
+ (void)ret;
+ FAIL() << "No execption thrown";
+ } catch (TVMError &ex) {
+ EXPECT_STREQ(ex.what(), "Cannot convert from type `None` to `int`");
+ }
+}
+
+} // namespace
diff --git a/ffi/tests/cpp/test_list.cc b/ffi/tests/cpp/test_list.cc
new file mode 100644
index 0000000000..53fd727af6
--- /dev/null
+++ b/ffi/tests/cpp/test_list.cc
@@ -0,0 +1,461 @@
+#include <gtest/gtest.h>
+#include <tvm/ffi/ffi.hpp>
+
+namespace {
+
+using namespace tvm::ffi;
+
+bool DTypeEqual(DLDataType a, DLDataType b) {
+ return a.code == b.code && a.bits == b.bits && a.lanes == b.lanes;
+}
+bool DeviceEqual(DLDevice a, DLDevice b) {
+ return a.device_type == b.device_type && a.device_id == b.device_id;
+}
+
+void TestSizeCapacityClear(Ref<List> *list, int64_t size, int64_t capacity) {
+ EXPECT_EQ(list->size(), size);
+ EXPECT_EQ(list->capacity(), capacity);
+ EXPECT_EQ(list->empty(), size == 0);
+ list->clear();
+ EXPECT_EQ(list->size(), 0);
+ EXPECT_EQ(list->capacity(), capacity);
+ EXPECT_EQ(list->empty(), true);
+}
+
+TEST(List_Constructor, Default) {
+ Ref<List> list = Ref<List>::New();
+ TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ ASSERT_NE(list_ptr, nullptr);
+ EXPECT_EQ(list_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIList));
+ EXPECT_EQ(list_ptr->ref_cnt, 1);
+ EXPECT_NE(list_ptr->deleter, nullptr);
+ EXPECT_EQ(list_ptr->list_capacity, 0);
+ EXPECT_EQ(list_ptr->list_length, 0);
+ EXPECT_EQ(list_ptr->pool_capacity, 0);
+ EXPECT_EQ(list_ptr->pool_length, 0);
+ TestSizeCapacityClear(&list, 0, 0);
+}
+
+TEST(List_Constructor, InitializerList) {
+ Ref<List> list1{
+ 100, 1.0f, "Hi", DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, Ref<Object>::New(),
+ Ref<Object>()};
+ Ref<List> list2 = {
+ 100, 1.0f, "Hi", DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, Ref<Object>::New(),
+ Ref<Object>()};
+
+ auto test = [](Ref<List> *src) {
+ auto *list_ptr = reinterpret_cast<const TVMFFIList *>(src->get());
+ ASSERT_NE(list_ptr, nullptr);
+ EXPECT_EQ(list_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIList));
+ EXPECT_EQ(list_ptr->ref_cnt, 1);
+ EXPECT_NE(list_ptr->deleter, nullptr);
+ EXPECT_EQ(list_ptr->list_capacity, 7);
+ EXPECT_EQ(list_ptr->list_length, 7);
+ EXPECT_EQ(list_ptr->pool_capacity, 7);
+ EXPECT_EQ(list_ptr->pool_length, 4); // string is not in the POD pool
+ EXPECT_EQ(src->size(), 7);
+ EXPECT_EQ(src->capacity(), 7);
+ EXPECT_EQ(src->empty(), false);
+ TestSizeCapacityClear(src, 7, 7);
+ };
+ test(&list1);
+ test(&list2);
+}
+
+TEST(List_PushBack, POD) {
+ Ref<List> list;
+ ASSERT_NE(list.get(), nullptr);
+ list.push_back(100);
+ list.push_back(1.0f);
+ TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ ASSERT_NE(list_ptr, nullptr);
+ EXPECT_EQ(list_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIList));
+ EXPECT_EQ(list_ptr->ref_cnt, 1);
+ EXPECT_NE(list_ptr->deleter, nullptr);
+ EXPECT_EQ(list_ptr->list_capacity, List::kMinCapacity);
+ EXPECT_EQ(list_ptr->list_length, 2);
+ EXPECT_EQ(list_ptr->pool_capacity, List::kMinCapacity);
+ EXPECT_EQ(list_ptr->pool_length, 2);
+ EXPECT_EQ(int32_t(list[0]), 100);
+ EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
+ TestSizeCapacityClear(&list, 2, List::kMinCapacity);
+}
+
+TEST(List_PushBack, Obj) {
+ Ref<List> list;
+ Ref<Object> obj1 = Ref<Object>::New();
+ Ref<Object> obj2 = Ref<Object>::New();
+ list.push_back(obj1);
+ list.push_back(obj2);
+ TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ ASSERT_NE(list_ptr, nullptr);
+ EXPECT_EQ(list_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIList));
+ EXPECT_EQ(list_ptr->ref_cnt, 1);
+ EXPECT_NE(list_ptr->deleter, nullptr);
+ EXPECT_EQ(list_ptr->list_capacity, List::kMinCapacity);
+ EXPECT_EQ(list_ptr->list_length, 2);
+ EXPECT_EQ(list_ptr->pool_capacity, 0);
+ EXPECT_EQ(list_ptr->pool_length, 0);
+ EXPECT_EQ((Object *)(list[0]), obj1.get());
+ EXPECT_EQ((Object *)(list[1]), obj2.get());
+ TestSizeCapacityClear(&list, 2, List::kMinCapacity);
+}
+
+TEST(List_PushBack, Heterogeneous) {
+ constexpr int n = 128;
+ constexpr int k = 8;
+ constexpr int expected_size = n * k;
+ constexpr int expected_capacity = 1024;
+ constexpr int expected_pool_capacity = 1024;
+ constexpr int expected_pool_length = 512;
+ int64_t integer = 100;
+ double fp = 1.0;
+ std::string str = "Hi";
+ DLDataType dtype{kDLInt, 32, 1};
+ DLDevice device{kDLCPU, 0};
+ Ref<Object> obj = Ref<Object>::New();
+ Ref<Object> null_obj{nullptr};
+ std::string long_str(1024, 'a');
+
+ Ref<List> list = Ref<List>::New();
+ {
+ std::string long_str_copy(1024, 'a');
+ for (int i = 0; i < n; ++i) {
+ list.push_back(integer);
+ list.push_back(fp);
+ list.push_back(str);
+ list.push_back(dtype);
+ list.push_back(device);
+ list.push_back(obj);
+ list.push_back(null_obj);
+ list.push_back(long_str_copy);
+ }
+ }
+ for (int i = 0; i < n; ++i) {
+ int64_t i_0 = list[i * k];
+ double i_1 = list[i * k + 1];
+ std::string i_2 = list[i * k + 2];
+ DLDataType i_3 = list[i * k + 3];
+ DLDevice i_4 = list[i * k + 4];
+ Object *i_5 = list[i * k + 5];
+ Object *i_6 = list[i * k + 6];
+ const char *i_7 = list[i * k + 7];
+ EXPECT_EQ(i_0, integer);
+ EXPECT_DOUBLE_EQ(i_1, fp);
+ EXPECT_EQ(i_2, str);
+ EXPECT_PRED2(DTypeEqual, i_3, dtype);
+ EXPECT_PRED2(DeviceEqual, i_4, device);
+ EXPECT_EQ(i_5, obj.get());
+ EXPECT_EQ(i_6, nullptr);
+ EXPECT_STREQ(i_7, long_str.c_str());
+ }
+ auto *list_ptr = reinterpret_cast<const TVMFFIList *>(list.get());
+ EXPECT_EQ(list_ptr->list_capacity, expected_capacity);
+ EXPECT_EQ(list_ptr->list_length, expected_size);
+ EXPECT_EQ(list_ptr->pool_capacity, expected_pool_capacity);
+ EXPECT_EQ(list_ptr->pool_length, expected_pool_length);
+}
+
+TEST(List_Insert, Once) {
+ Ref<List> values = {100,
+ 1.0,
+ "Hi", //
+ DLDataType{kDLInt, 32, 1},
+ DLDevice{kDLCPU, 0},
+ Ref<Object>::New(),
+ Ref<Object>(),
+ std::string(1024, 'a')};
+ int n = values.size();
+ for (int pos = 0; pos <= n; ++pos) {
+ for (AnyView data : values) {
+ // Test: insert at `pos` with value `data`
+ Ref<List> list(values.begin(), values.end());
+ list.insert(pos, data);
+ auto test = [](AnyView expected, AnyView actual) {
+ EXPECT_EQ(expected.type_index, actual.type_index);
+ EXPECT_EQ(expected.v_int64, actual.v_int64);
+ };
+ for (int i = 0; i < pos; ++i) {
+ test(values[i], list[i]);
+ }
+ for (int i = pos; i < n; ++i) {
+ test(values[i], list[i + 1]);
+ }
+ test(data, list[pos]);
+ }
+ }
+}
+
+TEST(List_Insert, Error_0) {
+ Ref<List> list = {100, 1.0, "Hi"};
+ try {
+ list.insert(-1, 1.0);
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ EXPECT_STREQ(ex.what(), "Indexing `-1` of a list of size 3");
+ }
+}
+
+TEST(List_Insert, Error_1) {
+ Ref<List> list = {100, 1.0, "Hi"};
+ try {
+ list.insert(4, 1.0);
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ EXPECT_STREQ(ex.what(), "Indexing `4` of a list of size 3");
+ }
+}
+
+TEST(List_Resize, Shrink) {
+ Ref<List> list = {100, 1.0, "Hi"};
+ list.resize(2);
+ EXPECT_EQ(list.size(), 2);
+ EXPECT_EQ(list.capacity(), 3);
+ EXPECT_EQ(int32_t(list[0]), 100);
+ EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
+}
+
+TEST(List_Resize, Expand) {
+ Ref<List> list = {100, 1.0, "Hi"};
+ list.resize(4);
+ EXPECT_EQ(list.size(), 4);
+ EXPECT_EQ(list.capacity(), 6);
+ EXPECT_EQ(int32_t(list[0]), 100);
+ EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
+ EXPECT_STREQ(list[2], "Hi");
+ EXPECT_EQ(list[3].operator void *(), nullptr);
+}
+
+TEST(List_Reserve, Shrink) {
+ Ref<List> list = {100, 1.0, "Hi"};
+ list.reserve(2);
+ EXPECT_EQ(list.size(), 3);
+ EXPECT_EQ(list.capacity(), 3);
+ EXPECT_EQ(int32_t(list[0]), 100);
+ EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
+ EXPECT_STREQ(list[2], "Hi");
+}
+
+TEST(List_Reserve, Expand) {
+ Ref<List> list = {100, 1.0, "Hi"};
+ list.reserve(4);
+ EXPECT_EQ(list.size(), 3);
+ EXPECT_EQ(list.capacity(), 6);
+ EXPECT_EQ(int32_t(list[0]), 100);
+ EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
+ EXPECT_STREQ(list[2], "Hi");
+}
+
+TEST(List_SetItem, PodToPod) {
+ Ref<List> list = {100, 1.0, "Hi"};
+ for (int i = 0; i < 16; ++i) {
+ list[1] = i;
+ EXPECT_EQ(list.size(), 3);
+ EXPECT_EQ(list.capacity(), 3);
+ EXPECT_EQ(int32_t(list[0]), 100);
+ EXPECT_EQ(int32_t(list[1]), i);
+ EXPECT_STREQ(list[2], "Hi");
+ }
+ EXPECT_EQ(list.size(), 3);
+ EXPECT_EQ(list.capacity(), 3);
+ TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ EXPECT_EQ(list_ptr->list_capacity, 3);
+ EXPECT_EQ(list_ptr->list_length, 3);
+ EXPECT_EQ(list_ptr->pool_capacity, 24);
+ EXPECT_EQ(list_ptr->pool_length, 3);
+}
+
+TEST(List_SetItem, ObjToPod) {
+ Ref<List> list = {100, 1.0, "Hi"}; //
+ for (int i = 0; i < 16; ++i) {
+ list[2] = i;
+ EXPECT_EQ(list.size(), 3);
+ EXPECT_EQ(list.capacity(), 3);
+ EXPECT_EQ(int32_t(list[0]), 100);
+ EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
+ EXPECT_EQ(int32_t(list[2]), i);
+ }
+ EXPECT_EQ(list.size(), 3);
+ EXPECT_EQ(list.capacity(), 3);
+ TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ EXPECT_EQ(list_ptr->list_capacity, 3);
+ EXPECT_EQ(list_ptr->list_length, 3);
+ EXPECT_EQ(list_ptr->pool_capacity, 24);
+ EXPECT_EQ(list_ptr->pool_length, 6);
+}
+
+TEST(List_SetItem, PodToObj) {
+ Ref<List> list = {100, 1.0, "Hi"};
+ for (int i = 0; i < 1; ++i) {
+ Ref<Object> obj = Ref<Object>::New();
+ list[0] = obj;
+ EXPECT_EQ(list.size(), 3);
+ EXPECT_EQ(list.capacity(), 3);
+ EXPECT_EQ((Object *)(list[0]), obj.get());
+ EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
+ EXPECT_STREQ(list[2], "Hi");
+ }
+ EXPECT_EQ(list.size(), 3);
+ EXPECT_EQ(list.capacity(), 3);
+ TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ EXPECT_EQ(list_ptr->list_capacity, 3);
+ EXPECT_EQ(list_ptr->list_length, 3);
+ EXPECT_EQ(list_ptr->pool_capacity, 3);
+ EXPECT_EQ(list_ptr->pool_length, 2);
+}
+
+TEST(List_SetItem, ObjToObj) {
+ Ref<List> list = {100, 1.0, "Hi"};
+ for (int i = 0; i < 1; ++i) {
+ Ref<Object> obj = Ref<Object>::New();
+ list[2] = obj;
+ EXPECT_EQ(list.size(), 3);
+ EXPECT_EQ(list.capacity(), 3);
+ EXPECT_EQ(int32_t(list[0]), 100);
+ EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
+ EXPECT_EQ((Object *)(list[2]), obj.get());
+ }
+ EXPECT_EQ(list.size(), 3);
+ EXPECT_EQ(list.capacity(), 3);
+ TVMFFIList *list_ptr = reinterpret_cast<TVMFFIList *>(list.get());
+ EXPECT_EQ(list_ptr->list_capacity, 3);
+ EXPECT_EQ(list_ptr->list_length, 3);
+ EXPECT_EQ(list_ptr->pool_capacity, 3);
+ EXPECT_EQ(list_ptr->pool_length, 2);
+}
+
+TEST(List_PopBack, Heterogeneous) {
+ int64_t integer = 100;
+ double fp = 1.0;
+ std::string str = "Hi";
+ DLDataType dtype{kDLInt, 32, 1};
+ DLDevice device{kDLCPU, 0};
+ Ref<Object> obj = Ref<Object>::New();
+ Ref<Object> null_obj{nullptr};
+ Ref<List> list{integer, fp, str, dtype, device, obj, null_obj};
+ int n = static_cast<int32_t>(list.size());
+ for (int i = 0; i < n; ++i) {
+ list.pop_back();
+ EXPECT_EQ(list.size(), n - 1 - i);
+ EXPECT_EQ(list.capacity(), n);
+ int m = static_cast<int32_t>(list.size());
+ if (m > 0) {
+ EXPECT_EQ(int32_t(list[0]), integer);
+ }
+ if (m > 1) {
+ EXPECT_DOUBLE_EQ(double(list[1]), fp);
+ }
+ if (m > 2) {
+ EXPECT_STREQ(list[2], str.c_str());
+ }
+ if (m > 3) {
+ EXPECT_PRED2(DTypeEqual, DLDataType(list[3]), dtype);
+ }
+ if (m > 4) {
+ EXPECT_PRED2(DeviceEqual, DLDevice(list[4]), device);
+ }
+ if (m > 5) {
+ EXPECT_EQ((Object *)(list[5]), obj.get());
+ }
+ if (m > 6) {
+ EXPECT_EQ((Object *)(list[6]), nullptr);
+ }
+ }
+ EXPECT_EQ(list.size(), 0);
+ EXPECT_EQ(list.capacity(), n);
+ EXPECT_EQ(list.empty(), true);
+ EXPECT_EQ(list.begin(), list.end());
+ try {
+ list.pop_back();
+ FAIL() << "No exception thrown";
+ } catch (TVMError &ex) {
+ EXPECT_STREQ(ex.what(), "Indexing `-1` of a list of size 0");
+ }
+}
+
+TEST(List_Erase, Front) {
+ int64_t integer = 100;
+ double fp = 1.0;
+ std::string str = "Hi";
+ DLDataType dtype{kDLInt, 32, 1};
+ DLDevice device{kDLCPU, 0};
+ Ref<Object> obj = Ref<Object>::New();
+ Ref<Object> null_obj{nullptr};
+ Ref<List> list{integer, fp, str, dtype, device, obj, null_obj};
+ list.erase(0);
+ EXPECT_EQ(list.size(), 6);
+ EXPECT_EQ(list.capacity(), 7);
+ EXPECT_EQ(double(list[0]), 1.0);
+ EXPECT_STREQ(list[1], "Hi");
+ EXPECT_PRED2(DTypeEqual, DLDataType(list[2]), dtype);
+ EXPECT_PRED2(DeviceEqual, DLDevice(list[3]), device);
+ EXPECT_EQ((Object *)(list[4]), obj.get());
+ EXPECT_EQ((Object *)(list[5]), nullptr);
+}
+
+TEST(List_Erase, Back) {
+ int64_t integer = 100;
+ double fp = 1.0;
+ std::string str = "Hi";
+ DLDataType dtype{kDLInt, 32, 1};
+ DLDevice device{kDLCPU, 0};
+ Ref<Object> obj = Ref<Object>::New();
+ Ref<Object> null_obj{nullptr};
+ Ref<List> list{integer, fp, str, dtype, device, obj, null_obj};
+ list.erase(0);
+ EXPECT_EQ(list.size(), 6);
+ EXPECT_EQ(list.capacity(), 7);
+ EXPECT_EQ(double(list[0]), 1.0);
+ EXPECT_STREQ(list[1], "Hi");
+ EXPECT_PRED2(DTypeEqual, DLDataType(list[2]), dtype);
+ EXPECT_PRED2(DeviceEqual, DLDevice(list[3]), device);
+ EXPECT_EQ((Object *)(list[4]), obj.get());
+ EXPECT_EQ((Object *)(list[5]), nullptr);
+}
+
+TEST(List_Erase, Mid) {
+ int64_t integer = 100;
+ double fp = 1.0;
+ std::string str = "Hi";
+ DLDataType dtype{kDLInt, 32, 1};
+ DLDevice device{kDLCPU, 0};
+ Ref<Object> obj = Ref<Object>::New();
+ Ref<Object> null_obj{nullptr};
+ Ref<List> list{integer, fp, str, dtype, device, obj, null_obj};
+ list.erase(3);
+ EXPECT_EQ(list.size(), 6);
+ EXPECT_EQ(list.capacity(), 7);
+ EXPECT_EQ(int32_t(list[0]), 100);
+ EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
+ EXPECT_STREQ(list[2], "Hi");
+ EXPECT_PRED2(DeviceEqual, DLDevice(list[3]), device);
+ EXPECT_EQ((Object *)(list[4]), obj.get());
+ EXPECT_EQ((Object *)(list[5]), nullptr);
+}
+
+TEST(List_Iter, Test) {
+ Ref<List> list;
+ for (int i = 0; i < 16; ++i) {
+ list.push_back(i * i);
+ }
+ int i = 0;
+ for (int item : list) {
+ EXPECT_EQ(i * i, item);
+ ++i;
+ }
+}
+
+TEST(List_RevIter, Test) {
+ Ref<List> list;
+ for (int i = 0; i < 16; ++i) {
+ list.push_back(i * i);
+ }
+ int i = list.size() - 1;
+ for (auto it = list.rbegin(); it != list.rend(); ++it) {
+ EXPECT_EQ(i * i, int32_t(*it));
+ --i;
+ }
+}
+
+} // namespace
diff --git a/ffi/tests/cpp/test_ref.cc b/ffi/tests/cpp/test_ref.cc
new file mode 100644
index 0000000000..3c9dc0561d
--- /dev/null
+++ b/ffi/tests/cpp/test_ref.cc
@@ -0,0 +1,293 @@
+#include <gtest/gtest.h>
+#include <tvm/ffi/ffi.hpp>
+#include <unordered_map>
+
+namespace {
+using namespace tvm::ffi;
+using tvm::ffi::details::StrPad;
+using tvm::ffi::details::StrStd;
+
+using ObjDeleter = void (*)(void *);
+
+struct AllocRecorder {
+ std::unordered_map<void *, ObjDeleter> deleters;
+
+ void Alloc(void *ptr) {
+ deleters[ptr] = reinterpret_cast<TVMFFIAny *>(ptr)->deleter;
+ reinterpret_cast<TVMFFIAny *>(ptr)->deleter = AllocRecorder::Deleter;
+ }
+
+ void Delete(void *ptr) {
+ ASSERT_EQ(deleters.count(ptr), 1);
+ ObjDeleter d = this->deleters[ptr];
+ d(ptr);
+ deleters.erase(ptr);
+ }
+
+ bool IsDeletedImpl(void *ptr) { return deleters.count(ptr) == 0; }
+
+ static void Deleter(void *ptr) { AllocRecorder::Global()->Delete(ptr); }
+
+ static bool IsDeleted(void *ptr) { return AllocRecorder::Global()->IsDeletedImpl(ptr); }
+
+ static AllocRecorder *Global() {
+ static AllocRecorder inst;
+ return &inst;
+ }
+};
+
+template <typename ObjectType>
+struct TestAllocator {
+ using Allocator = typename ::tvm::ffi::GetAllocator<ObjectType>::Type;
+
+ template <typename... Args>
+ TVM_FFI_INLINE static ObjectType *New(Args &&...args) {
+ ObjectType *ret = Allocator::New(std::forward<Args>(args)...);
+ AllocRecorder::Global()->Alloc(ret);
+ return ret;
+ }
+
+ template <typename PadType, typename... Args>
+ TVM_FFI_INLINE static ObjectType *NewWithPad(size_t pad_size, Args &&...args) {
+ ObjectType *ret =
+ Allocator::template NewWithPad<PadType>(pad_size, std::forward<Args>(args)...);
+ AllocRecorder::Global()->Alloc(ret);
+ return ret;
+ }
+};
+
+int64_t FuncCall(int64_t x) { return x + 1; }
+
+int32_t GetRefCount(void *obj) { return reinterpret_cast<TVMFFIAny *>(obj)->ref_cnt; }
+
+int32_t GetTypeIndex(void *obj) { return reinterpret_cast<TVMFFIAny *>(obj)->type_index; }
+
+ObjDeleter GetDeleter(void *obj) { return reinterpret_cast<TVMFFIAny *>(obj)->deleter; }
+
+TEST(Ref_Constructor_0_Default, Default) {
+ Ref<Object> ref;
+ EXPECT_EQ(ref.get(), nullptr);
+}
+
+TEST(Ref_Constructor_1_Ptr, SameType) {
+ Object *obj = TestAllocator<Object>::New();
+ {
+ Ref<Object> ref(obj);
+ EXPECT_EQ(ref.get(), obj);
+ EXPECT_EQ(GetRefCount(obj), 1);
+ }
+ EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
+}
+
+TEST(Ref_Constructor_1_Ptr, SubType) {
+ Str *obj = TestAllocator<Str>::New("Hello world");
+ {
+ Ref<Object> ref(obj);
+ EXPECT_EQ(ref.get(), static_cast<void *>(obj));
+ EXPECT_EQ(GetRefCount(obj), 1);
+ }
+ EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
+}
+
+TEST(Ref_Constructor_2_Ref, SameType_Copy) {
+ Object *obj = TestAllocator<Object>::New();
+ {
+ Ref<Object> ref1(obj);
+ EXPECT_EQ(GetRefCount(obj), 1);
+ {
+ Ref<Object> ref2(ref1);
+ EXPECT_EQ(GetRefCount(obj), 2);
+ }
+ EXPECT_EQ(GetRefCount(ref1.get()), 1);
+ }
+ EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
+}
+
+TEST(Ref_Constructor_2_Ref, SameType_Move) {
+ Object *obj = TestAllocator<Object>::New();
+ {
+ Ref<Object> ref1(obj);
+ EXPECT_EQ(GetRefCount(obj), 1);
+ {
+ Ref<Object> ref2(std::move(ref1));
+ EXPECT_EQ(GetRefCount(obj), 1);
+ EXPECT_EQ(ref1.get(), nullptr);
+ }
+ }
+ EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
+}
+
+TEST(Ref_Constructor_2_Ref, SubType_Copy) {
+ Str *obj = TestAllocator<Str>::New("Hello world");
+ {
+ Ref<Str> ref1(obj);
+ EXPECT_EQ(GetRefCount(obj), 1);
+ {
+ Ref<Object> ref2(ref1);
+ EXPECT_EQ(GetRefCount(obj), 2);
+ }
+ EXPECT_EQ(GetRefCount(obj), 1);
+ }
+ EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
+}
+
+TEST(Ref_Constructor_2_Ref, SubType_Move) {
+ Str *obj = TestAllocator<Str>::New("Hello world");
+ {
+ Ref<Str> ref1(obj);
+ EXPECT_EQ(GetRefCount(obj), 1);
+ {
+ Ref<Object> ref2(std::move(ref1));
+ EXPECT_EQ(GetRefCount(obj), 1);
+ EXPECT_EQ(ref1.get(), nullptr);
+ }
+ }
+ EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
+}
+
+TEST(Ref_Constructor_3_AnyView, Copy) {
+ Object *obj = TestAllocator<Object>::New();
+ {
+ Ref<Object> ref1(obj);
+ EXPECT_EQ(GetRefCount(obj), 1);
+ {
+ AnyView view(ref1);
+ Ref<Object> ref2(view);
+ EXPECT_EQ(GetRefCount(obj), 2);
+ }
+ EXPECT_EQ(GetRefCount(obj), 1);
+ }
+ EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
+}
+
+TEST(Ref_Constructor_3_AnyView, Move) {
+ Object *obj = TestAllocator<Object>::New();
+ {
+ Ref<Object> ref1(obj);
+ EXPECT_EQ(GetRefCount(obj), 1);
+ {
+ AnyView view(ref1);
+ EXPECT_EQ(GetRefCount(obj), 1);
+ {
+ Ref<Object> ref2(std::move(view));
+ EXPECT_EQ(GetRefCount(obj), 2);
+ EXPECT_EQ(view.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone));
+ EXPECT_EQ(view.ref_cnt, 0);
+ EXPECT_EQ(view.v_int64, 0);
+ }
+ EXPECT_EQ(GetRefCount(obj), 1);
+ }
+ }
+ EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
+}
+
+TEST(Ref_Constructor_4_Any, Copy) {
+ Object *obj = TestAllocator<Object>::New();
+ {
+ Ref<Object> ref1(obj);
+ EXPECT_EQ(GetRefCount(obj), 1);
+ {
+ Any any(ref1);
+ EXPECT_EQ(GetRefCount(obj), 2);
+ Ref<Object> ref2(any);
+ EXPECT_EQ(GetRefCount(obj), 3);
+ }
+ EXPECT_EQ(GetRefCount(obj), 1);
+ }
+ EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
+}
+
+TEST(Ref_Constructor_4_Any, Move) {
+ Object *obj = TestAllocator<Object>::New();
+ {
+ Ref<Object> ref1(obj);
+ EXPECT_EQ(GetRefCount(obj), 1);
+ {
+ Any any(ref1);
+ EXPECT_EQ(GetRefCount(obj), 2);
+ {
+ Ref<Object> ref2(std::move(any));
+ EXPECT_EQ(GetRefCount(obj), 2);
+ EXPECT_EQ(any.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone));
+ EXPECT_EQ(any.ref_cnt, 0);
+ EXPECT_EQ(any.v_int64, 0);
+ }
+ EXPECT_EQ(GetRefCount(obj), 1);
+ }
+ }
+ EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
+}
+
+TEST(Ref_New, Object) {
+ Ref<Object> ref = Ref<Object>::New();
+ EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
+ EXPECT_EQ(GetRefCount(ref.get()), 1);
+}
+
+TEST(Ref_New, Func) {
+ Ref<Func> ref = Ref<Func>::New(FuncCall);
+ EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
+ EXPECT_EQ(GetRefCount(ref.get()), 1);
+}
+
+TEST(Ref_New, RawStr) {
+ const char *str = "Hello world";
+ Ref<Str> ref = Ref<Str>::New(str);
+ EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
+ EXPECT_EQ(GetRefCount(ref.get()), 1);
+ EXPECT_EQ(GetDeleter(ref.get()), DefaultObjectAllocator<StrPad>::Deleter);
+}
+
+TEST(Ref_New, CharArray) {
+ const char str[18] = "Hello world";
+ Ref<Str> ref = Ref<Str>::New(str);
+ EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
+ EXPECT_EQ(GetRefCount(ref.get()), 1);
+ EXPECT_EQ(GetDeleter(ref.get()), DefaultObjectAllocator<StrPad>::Deleter);
+ EXPECT_EQ(ref->size(), 17);
+}
+
+TEST(Ref_New, StdString_Copy) {
+ std::string str = "Hello world";
+ Ref<Str> ref = Ref<Str>::New(str);
+ EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
+ EXPECT_EQ(GetRefCount(ref.get()), 1);
+ EXPECT_EQ(GetDeleter(ref.get()), DefaultObjectAllocator<StrPad>::Deleter);
+}
+
+TEST(Ref_New, StdString_Move) {
+ std::string str = "Hello world";
+ Ref<Str> ref = Ref<Str>::New(std::move(str));
+ EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
+ EXPECT_EQ(GetRefCount(ref.get()), 1);
+ EXPECT_EQ(GetDeleter(ref.get()), DefaultObjectAllocator<StrStd>::Deleter);
+}
+
+TEST(Ref_Stringify, Object) {
+ std::string str = Ref<Object>::New().str()->c_str();
+ std::string expected_prefix = "object.Object@0";
+ EXPECT_GT(str.size(), expected_prefix.size());
+ EXPECT_EQ(str.substr(0, expected_prefix.size()), expected_prefix);
+}
+
+TEST(Ref_Stringify, Func) {
+ std::string str = Ref<Func>::New(FuncCall).str()->c_str();
+ std::string expected_prefix = "object.Func@0";
+ EXPECT_GT(str.size(), expected_prefix.size());
+ EXPECT_EQ(str.substr(0, expected_prefix.size()), expected_prefix);
+}
+
+TEST(Ref_Stringify, Str) {
+ std::string str = Ref<Str>::New("Hello world").str()->c_str();
+ EXPECT_EQ(str, "\"Hello world\"");
+}
+
+TEST(Ref_Misc, MoveToRaw) {
+ TVMFFIAny *str = reinterpret_cast<TVMFFIAny *>(Ref<Str>::New("Hello world").MoveToRawObjPtr());
+ EXPECT_EQ(GetTypeIndex(str), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
+ EXPECT_EQ(GetRefCount(str), 1);
+ EXPECT_EQ(GetDeleter(str), DefaultObjectAllocator<StrPad>::Deleter);
+ str->deleter(str);
+}
+
+} // namespace
diff --git a/ffi/tests/cpp/test_str.cc b/ffi/tests/cpp/test_str.cc
new file mode 100644
index 0000000000..bd91f241f6
--- /dev/null
+++ b/ffi/tests/cpp/test_str.cc
@@ -0,0 +1,47 @@
+#include <gtest/gtest.h>
+#include <tvm/ffi/ffi.hpp>
+
+namespace {
+using namespace tvm::ffi;
+
+const char c_str_long[] = "Hello, World! This is an extremely long string to "
+ "avoid any on-stack optimization.";
+
+TEST(Str, CopyFromStdString) {
+ std::string std_str = "Hello, World!";
+ Ref<Str> str = Ref<Str>::New(std_str);
+ EXPECT_EQ(str->size(), std_str.size());
+ EXPECT_STREQ(str->c_str(), std_str.c_str());
+ EXPECT_STREQ(str->data(), std_str.data());
+}
+
+TEST(Str, MoveFromStdString_0) {
+ std::string std_str = c_str_long;
+ const void *data = std_str.data();
+ Ref<Str> str = Ref<Str>::New(std::move(std_str));
+ EXPECT_EQ(static_cast<const void *>(str->data()), data);
+ EXPECT_STREQ(str->c_str(), c_str_long);
+}
+
+TEST(Str, MoveFromStdString_1) {
+ Ref<Str> str = Ref<Str>::New(std::string(c_str_long));
+ EXPECT_STREQ(str->c_str(), c_str_long);
+ EXPECT_EQ(str->size(), sizeof(c_str_long) - 1);
+}
+
+TEST(Str, CopyFromCStr) {
+ Ref<Str> str = Ref<Str>::New(c_str_long);
+ EXPECT_STREQ(str->c_str(), c_str_long);
+ EXPECT_EQ(str->size(), sizeof(c_str_long) - 1);
+}
+
+TEST(Str, CopyFromCharArray) {
+ const char c_str[18] = "Hello, World!";
+ Ref<Str> str = Ref<Str>::New(c_str);
+ EXPECT_EQ(sizeof(c_str), 18);
+ EXPECT_EQ(strlen(c_str), 13);
+ EXPECT_STREQ(str->c_str(), c_str);
+ EXPECT_EQ(str->size(), 17);
+}
+
+} // namespace
diff --git a/ffi/tests/cpp/test_type_dyn.cc b/ffi/tests/cpp/test_type_dyn.cc
new file mode 100644
index 0000000000..45910528b3
--- /dev/null
+++ b/ffi/tests/cpp/test_type_dyn.cc
@@ -0,0 +1,71 @@
+#include <gtest/gtest.h>
+#include <tvm/ffi/ffi.hpp>
+
+#if TVM_FFI_ALLOW_DYN_TYPE == 1
+
+namespace {
+using namespace tvm::ffi;
+
+struct TestObj : public Object {
+ int x;
+ explicit TestObj(int x) : x(x) {}
+ TVM_FFI_DEF_DYN_TYPE(TestObj, Object, "test.TestObj");
+};
+
+struct SubTestObj : public TestObj {
+ int y;
+ explicit SubTestObj(int x, int y) : TestObj(x), y(y) {}
+ TVM_FFI_DEF_DYN_TYPE(SubTestObj, TestObj, "test.SubTestObj");
+};
+
+void CheckAncestor(int32_t num, const int32_t *ancestors,
+ std::vector<int32_t> expected) {
+ EXPECT_EQ(num, expected.size());
+ for (int i = 0; i < num; ++i) {
+ EXPECT_EQ(ancestors[i], expected[i]);
+ }
+}
+
+TEST(DynTypeInfo, TestObj) {
+ EXPECT_GE(TestObj::_type_index,
+ static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDynObjectBegin));
+ EXPECT_STRCASEEQ(TestObj::_type_key, "test.TestObj");
+ EXPECT_EQ(TestObj::_type_depth, 1);
+ CheckAncestor(TestObj::_type_depth, TestObj::_type_ancestors.data(),
+ {static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject)});
+}
+
+TEST(DynTypeInfo, SubTestObj) {
+ EXPECT_GE(SubTestObj::_type_index,
+ static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDynObjectBegin));
+ EXPECT_NE(SubTestObj::_type_index, TestObj::_type_index);
+ EXPECT_STRCASEEQ(SubTestObj::_type_key, "test.SubTestObj");
+ EXPECT_EQ(SubTestObj::_type_depth, 2);
+ CheckAncestor(SubTestObj::_type_depth, SubTestObj::_type_ancestors.data(),
+ {static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject),
+ TestObj::_type_index});
+}
+
+TEST(DynTypeInheritance, TestObj) {
+ Ref<TestObj> obj = Ref<TestObj>::New(10);
+ EXPECT_EQ(obj->x, 10);
+ EXPECT_TRUE(obj->IsInstance<Object>());
+ EXPECT_TRUE(obj->IsInstance<TestObj>());
+ EXPECT_FALSE(obj->IsInstance<Func>());
+ EXPECT_FALSE(obj->IsInstance<Str>());
+}
+
+TEST(DynTypeInheritance, SubTestObj) {
+ Ref<SubTestObj> obj = Ref<SubTestObj>::New(10, 20);
+ EXPECT_EQ(obj->x, 10);
+ EXPECT_EQ(obj->y, 20);
+ EXPECT_TRUE(obj->IsInstance<Object>());
+ EXPECT_TRUE(obj->IsInstance<TestObj>());
+ EXPECT_TRUE(obj->IsInstance<SubTestObj>());
+ EXPECT_FALSE(obj->IsInstance<Func>());
+ EXPECT_FALSE(obj->IsInstance<Str>());
+}
+
+} // namespace
+
+#endif
diff --git a/ffi/tests/cpp/test_type_static.cc b/ffi/tests/cpp/test_type_static.cc
new file mode 100644
index 0000000000..aebeba7c3e
--- /dev/null
+++ b/ffi/tests/cpp/test_type_static.cc
@@ -0,0 +1,119 @@
+#include <gtest/gtest.h>
+#include <tvm/ffi/ffi.hpp>
+
+namespace {
+using namespace tvm::ffi;
+
+struct SubType : public Object {
+ int data;
+ explicit SubType(int data) : data(data) {
+ if (data == 1) {
+ throw std::runtime_error("New Error");
+ }
+ }
+};
+
+int64_t FuncCall(int64_t x) { return x + 1; }
+
+void CheckAncestor(int32_t num, const int32_t *ancestors,
+ std::vector<int32_t> expected) {
+ EXPECT_EQ(num, expected.size());
+ for (int i = 0; i < num; ++i) {
+ EXPECT_EQ(ancestors[i], expected[i]);
+ }
+}
+
+static_assert(IsObject<Object>, "IsObject<Object> == true");
+static_assert(IsObject<Func>, "IsObject<Func> == true");
+static_assert(IsObject<Str>, "IsObject<Str> == true");
+
+TEST(StaticTypeInfo, Object) {
+ EXPECT_EQ(Object::_type_index,
+ static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
+ EXPECT_STRCASEEQ(Object::_type_key, "object.Object");
+ EXPECT_EQ(Object::_type_depth, 0);
+ CheckAncestor(Object::_type_depth, Object::_type_ancestors.data(), {});
+}
+
+TEST(StaticTypeInfo, Func) {
+ EXPECT_EQ(Func::_type_index,
+ static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
+ EXPECT_STRCASEEQ(Func::_type_key, "object.Func");
+ EXPECT_EQ(Func::_type_depth, 1);
+ CheckAncestor(Func::_type_depth, Func::_type_ancestors.data(),
+ {static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject)});
+}
+
+TEST(StaticTypeInfo, Str) {
+ EXPECT_EQ(Str::_type_index,
+ static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
+ EXPECT_STRCASEEQ(Str::_type_key, "object.Str");
+ EXPECT_EQ(Str::_type_depth, 1);
+ CheckAncestor(Str::_type_depth, Str::_type_ancestors.data(),
+ {static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject)});
+}
+
+TEST(StaticTypeInheritance, None) {
+ Ref<Object> obj;
+ // FIXME: The lines below are going to segfault
+ // EXPECT_STREQ(obj->GetTypeKey(), "None");
+ // EXPECT_FALSE(obj->IsInstance<Object>());
+ // EXPECT_FALSE(obj->IsInstance<Func>());
+ // EXPECT_FALSE(obj->IsInstance<Str>());
+}
+
+TEST(StaticTypeInheritance, Object) {
+ Ref<Object> obj = Ref<Object>::New();
+ EXPECT_STREQ(obj->GetTypeKey(), "object.Object");
+ EXPECT_TRUE(obj->IsInstance<Object>());
+ EXPECT_FALSE(obj->IsInstance<Func>());
+ EXPECT_FALSE(obj->IsInstance<Str>());
+}
+
+TEST(StaticTypeInheritance, Func_0) {
+ Ref<Func> obj = Ref<Func>::New(FuncCall);
+ EXPECT_STREQ(obj->GetTypeKey(), "object.Func");
+ EXPECT_TRUE(obj->IsInstance<Object>());
+ EXPECT_TRUE(obj->IsInstance<Func>());
+ EXPECT_FALSE(obj->IsInstance<Str>());
+}
+
+TEST(StaticTypeInheritance, Func_1) {
+ Ref<Object> obj = Ref<Func>::New(FuncCall);
+ EXPECT_STREQ(obj->GetTypeKey(), "object.Func");
+ EXPECT_TRUE(obj->IsInstance<Object>());
+ EXPECT_TRUE(obj->IsInstance<Func>());
+ EXPECT_FALSE(obj->IsInstance<Str>());
+}
+
+TEST(StaticTypeInheritance, Str_0) {
+ Ref<Str> obj = Ref<Str>::New("Hello, World!");
+ EXPECT_STREQ(obj->GetTypeKey(), "object.Str");
+ EXPECT_TRUE(obj->IsInstance<Object>());
+ EXPECT_FALSE(obj->IsInstance<Func>());
+ EXPECT_TRUE(obj->IsInstance<Str>());
+}
+
+TEST(StaticTypeInheritance, Str_1) {
+ Ref<Object> obj = Ref<Str>::New("Hello, World!");
+ EXPECT_STREQ(obj->GetTypeKey(), "object.Str");
+ EXPECT_TRUE(obj->IsInstance<Object>());
+ EXPECT_FALSE(obj->IsInstance<Func>());
+ EXPECT_TRUE(obj->IsInstance<Str>());
+}
+
+TEST(StaticTypeSubclass, NoException) {
+ Ref<SubType> obj = Ref<SubType>::New(0);
+ EXPECT_EQ(obj->data, 0);
+}
+
+TEST(StaticTypeSubclass, Exception) {
+ try {
+ Ref<SubType>::New(1);
+ FAIL() << "No exception thrown";
+ } catch (std::runtime_error &ex) {
+ EXPECT_STREQ(ex.what(), "New Error");
+ }
+}
+
+} // namespace
diff --git a/include/tvm/ffi/core/c_ffi_abi.h b/include/tvm/ffi/core/c_ffi_abi.h
new file mode 100644
index 0000000000..fadbda220e
--- /dev/null
+++ b/include/tvm/ffi/core/c_ffi_abi.h
@@ -0,0 +1,166 @@
+#ifndef TVM_FFI_C_FFI_ABI_H_
+#define TVM_FFI_C_FFI_ABI_H_
+
+#include <dlpack/dlpack.h>
+#include <stdint.h>
+
+#if !defined(TVM_FFI_API) && defined(__EMSCRIPTEN__)
+#include <emscripten/emscripten.h>
+#define TVM_FFI_API EMSCRIPTEN_KEEPALIVE
+#endif
+#if !defined(TVM_FFI_API) && defined(_MSC_VER)
+#ifdef TVM_FFI_EXPORTS
+#define TVM_FFI_API __declspec(dllexport)
+#else
+#define TVM_FFI_API __declspec(dllimport)
+#endif
+#endif
+#ifndef TVM_FFI_API
+#define TVM_FFI_API __attribute__((visibility("default")))
+#endif
+
+#ifndef TVM_FFI_ALLOW_DYN_TYPE
+#define TVM_FFI_ALLOW_DYN_TYPE 0
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+enum class TVMFFITypeIndex : int32_t {
+#else
+typedef enum {
+#endif
+ // [Section] On-stack POD Types: [0, kTVMFFIStaticObjectBegin)
+ // N.B. `kTVMFFIRawStr` is a string backed by a `\0`-terminated char array,
+ // which is not owned by TVMFFIAny. It is required that the following
+ // invariant holds:
+ // - `Any::type_index` is never `kTVMFFIRawStr`
+ // - `AnyView::type_index` can be `kTVMFFIRawStr`
+ kTVMFFINone = 0,
+ kTVMFFIInt = 1,
+ kTVMFFIFloat = 2,
+ kTVMFFIPtr = 3,
+ kTVMFFIDataType = 4,
+ kTVMFFIDevice = 5,
+ kTVMFFIRawStr = 6,
+ // [Section] Static Boxed: [kTVMFFIStaticObjectBegin, kTVMFFIDynObjectBegin)
+ kTVMFFIStaticObjectBegin = 64,
+ kTVMFFIObject = 64,
+ kTVMFFIList = 65,
+ kTVMFFIDict = 66,
+ kTVMFFIError = 67,
+ kTVMFFIFunc = 68,
+ kTVMFFIStr = 69,
+ // [Section] Dynamic Boxed: [kTVMFFIDynObjectBegin, +oo)
+ kTVMFFIDynObjectBegin = 128,
+#ifdef __cplusplus
+};
+#else
+} TypeIndex;
+#endif
+
+struct TVMFFIAny;
+typedef TVMFFIAny TVMFFIObject;
+typedef TVMFFIObject *TVMFFIObjectHandle;
+typedef TVMFFIAny *TVMFFIAnyHandle;
+
+typedef struct TVMFFIAny {
+ int32_t type_index;
+ union { // 4 bytes
+ int32_t ref_cnt; // reference counter for heap object
+ int32_t small_len; // length for on-stack object
+ };
+ union { // 8 bytes
+ int64_t v_int64; // integers
+ double v_float64; // floating-point numbers
+ DLDataType v_dtype; // data type
+ DLDevice v_device; // device
+ void *v_ptr; // typeless pointers
+ const char *v_str; // raw string
+ TVMFFIObjectHandle v_obj; // ref counted objects
+ void (*deleter)(void *); // Deleter of the object
+ char v_bytes[8]; // small string
+ char32_t v_char32[2]; // UCS4 string and Unicode
+ };
+} TVMFFIAny;
+
+typedef struct {
+ int32_t type_index;
+ const char *type_key;
+ int32_t type_depth;
+ int32_t *type_ancestors;
+} TVMFFITypeInfo;
+
+typedef TVMFFITypeInfo *TVMFFITypeInfoHandle;
+
+typedef struct {
+ const char *filename;
+ const char *func;
+ int32_t lineno;
+ void (*deleter)(void *);
+} TVMFFIStackFrame;
+
+typedef struct {
+ int32_t type_index;
+ int32_t ref_cnt;
+ void (*deleter)(void *);
+ const char *kind;
+ int32_t num_frames;
+ const char **linenos;
+ const char *message;
+} TVMFFIError;
+
+typedef struct {
+ int32_t type_index;
+ int32_t ref_cnt;
+ void (*deleter)(void *);
+ int64_t length;
+ char *data;
+} TVMFFIStr;
+
+typedef struct {
+ int32_t type_index;
+ int32_t ref_cnt;
+ void (*deleter)(void *);
+ void (*call)(const void *self, int32_t num_args, const TVMFFIAny *args, TVMFFIAny *ret);
+ int32_t (*safe_call)(const void *self, int32_t num_args, const TVMFFIAny *args, TVMFFIAny *ret);
+} TVMFFIFunc;
+
+typedef struct {
+ int32_t type_index;
+ int32_t ref_cnt;
+ void (*deleter)(void *);
+ int64_t list_capacity;
+ int64_t list_length;
+ int64_t pool_capacity;
+ int64_t pool_length;
+} TVMFFIList;
+
+typedef struct {
+ int32_t type_index;
+ int32_t ref_cnt;
+ void (*deleter)(void *);
+ int64_t capacity;
+ int64_t size;
+} TVMFFIDict;
+
+#if TVM_FFI_ALLOW_DYN_TYPE
+typedef void *TVMFFITypeTableHandle;
+TVM_FFI_API void TVMFFIDynTypeIndex2Info(TVMFFITypeTableHandle self, int32_t type_index,
+ TVMFFITypeInfoHandle *out_type_info);
+TVM_FFI_API void TVMFFIDynTypeDef(TVMFFITypeTableHandle self, const char *type_key,
+ int32_t type_depth, const int32_t *type_ancestors,
+ int32_t *out_type_index);
+TVM_FFI_API void TVMFFIDynTypeSetAttr(TVMFFITypeTableHandle self, int32_t type_index,
+ const char *key, TVMFFIAnyHandle value);
+TVM_FFI_API void TVMFFIDynTypeGetAttr(TVMFFITypeTableHandle self, int32_t type_index,
+ const char *key, TVMFFIAnyHandle *out_type_attr);
+#endif // TVM_FFI_ALLOW_DYN_TYPE
+
+#ifdef __cplusplus
+} // TVM_FFI_EXTERN_C
+#endif
+
+#endif // TVM_FFI_C_FFI_ABI_H_
diff --git a/include/tvm/ffi/core/core.h b/include/tvm/ffi/core/core.h
new file mode 100644
index 0000000000..608e3e4c1b
--- /dev/null
+++ b/include/tvm/ffi/core/core.h
@@ -0,0 +1,421 @@
+#ifndef TVM_FFI_CORE_H_
+#define TVM_FFI_CORE_H_
+
+#include "./c_ffi_abi.h"
+#include "./traits.h"
+#include "./utils.h"
+#include <cstring>
+#include <type_traits>
+
+namespace tvm {
+namespace ffi {
+
+#define TVM_FFI_DEF_TYPE_FRIENDS() \
+ template <typename> \
+ friend struct ::tvm::ffi::IsAnyWithExtra; \
+ template <typename> \
+ friend struct ::tvm::ffi::Ref; \
+ template <typename> \
+ friend struct ::tvm::ffi::RefBase; \
+ template <typename, typename> \
+ friend struct ::tvm::ffi::TypeTraits; \
+ template <typename> \
+ friend struct ::tvm::ffi::TypeTraitsDefaultForObject; \
+ template <typename> \
+ friend struct ::tvm::ffi::DefaultObjectAllocator
+
+/********** Section 1. Object *********/
+
+#define TVM_FFI_DEF_STATIC_TYPE(SelfType, ParentType, TypeIndex) \
+public: \
+ TVM_FFI_DEF_TYPE_FRIENDS(); \
+ friend struct ::tvm::ffi::Any; \
+ friend struct ::tvm::ffi::AnyView; \
+ [[maybe_unused]] static constexpr int32_t _type_index = static_cast<int32_t>(TypeIndex); \
+ using _type_parent [[maybe_unused]] = ParentType; \
+ [[maybe_unused]] static constexpr int32_t _type_depth = ParentType::_type_depth + 1; \
+ [[maybe_unused]] static inline constexpr std::array<int32_t, _type_depth> _type_ancestors = \
+ ::tvm::ffi::details::ObjectAncestorsConstExpr<ParentType, _type_depth>(); \
+ template <typename DerivedType> \
+ TVM_FFI_INLINE bool IsInstance() const { \
+ return ::tvm::ffi::details::IsInstanceOf<DerivedType, SelfType>(this->type_index); \
+ } \
+ TVM_FFI_INLINE const char *GetTypeKey() const { return TypeIndex2TypeKey(this->type_index); } \
+ [[maybe_unused]] static constexpr const char *_type_key = TypeIndexTraits<TypeIndex>::type_key
+
+#define TVM_FFI_DEF_DYN_TYPE(SelfType, ParentType, TypeKey) \
+public: \
+ TVM_FFI_DEF_TYPE_FRIENDS(); \
+ friend struct ::tvm::ffi::Any; \
+ friend struct ::tvm::ffi::AnyView; \
+ using _type_parent [[maybe_unused]] = ParentType; \
+ [[maybe_unused]] static constexpr int32_t _type_depth = ParentType::_type_depth + 1; \
+ [[maybe_unused]] static inline const std::array<int32_t, _type_depth> _type_ancestors = \
+ ::tvm::ffi::details::ObjectAncestors<ParentType, _type_depth>(); \
+ [[maybe_unused]] static inline int32_t _type_index = []() -> int32_t { \
+ int32_t ret; \
+ TVMFFIDynTypeDef(nullptr, TypeKey, _type_depth, _type_ancestors.data(), &ret); \
+ return ret; \
+ }(); \
+ template <typename DerivedType> \
+ TVM_FFI_INLINE bool IsInstance() const { \
+ return ::tvm::ffi::details::IsInstanceOf<DerivedType, SelfType>(this->type_index); \
+ } \
+ TVM_FFI_INLINE const char *GetTypeKey() const { return TypeIndex2TypeKey(this->type_index); } \
+ [[maybe_unused]] static constexpr const char *_type_key = TypeKey
+
+struct Object : protected TVMFFIAny {
+ TVM_FFI_DEF_STATIC_TYPE(Object, details::DummyRoot, TVMFFITypeIndex::kTVMFFIObject);
+
+ TVM_FFI_INLINE Object() : TVMFFIAny() {}
+ TVM_FFI_INLINE Object(const Object &) : TVMFFIAny() {}
+ TVM_FFI_INLINE Object(Object &&) {}
+ TVM_FFI_INLINE Object &operator=(const Object &) { return *this; }
+ TVM_FFI_INLINE Object &operator=(Object &&) { return *this; }
+ Ref<Str> str() const;
+ TVM_FFI_INLINE friend std::ostream &operator<<(std::ostream &os, const Object &src) {
+ TVMFFIAny v{};
+ v.type_index = src.type_index;
+ v.v_obj = const_cast<Object *>(&src);
+ details::AnyView2Str(os, &v);
+ return os;
+ }
+};
+
+/********** Section 2. Ref<ObjectType> *********/
+
+#define TVM_FFI_REF_DEF_DELEGATE_CONSTRUCTORS(SelfType, BaseType) \
+ template <typename Other, typename = std::is_constructible<BaseType, Other &&>> \
+ TVM_FFI_INLINE Ref(Other &&src) : BaseType(std::forward<Other>(src)) {} \
+ template <typename Other, typename = std::is_constructible<BaseType, Other &&>> \
+ TVM_FFI_DEF_ASSIGN(SelfType, const Other &)
+
+template <typename Type>
+struct RefBase {
+private:
+ using TSelf = RefBase<Type>;
+ using TSub = Ref<Type>;
+ TVM_FFI_DEF_TYPE_FRIENDS();
+ friend struct Any;
+ friend struct AnyView;
+ template <typename T>
+ using EnableAnyOrViewOrRef = std::enable_if_t<details::IsAnyOrView<T> || details::IsRef<T>>;
+ template <typename Base, typename Derived>
+ using EnableDerivedObj =
+ typename std::enable_if_t<std::is_base_of_v<Base, Derived> ||
+ (std::is_same_v<Base, Object> && IsObject<Derived>)>;
+
+public:
+ /***** Factory: the `new` operator *****/
+ template <typename... Args>
+ TVM_FFI_INLINE static TSub New(Args &&...args) {
+ return TSub(Allocator::New(std::forward<Args>(args)...));
+ }
+ /***** Accessors *****/
+ TVM_FFI_INLINE const Type *get() const { return reinterpret_cast<const Type *>(this->data_); }
+ TVM_FFI_INLINE Type *get() { return reinterpret_cast<Type *>(data_); }
+ TVM_FFI_INLINE const Type *operator->() const { return get(); }
+ TVM_FFI_INLINE const Type &operator*() const { return *get(); }
+ TVM_FFI_INLINE Type *operator->() { return get(); }
+ TVM_FFI_INLINE Type &operator*() { return *get(); }
+ /***** Misc *****/
+ Ref<Str> str() const;
+ TVM_FFI_INLINE friend std::ostream &operator<<(std::ostream &os, const TSelf &src) {
+ TVMFFIAny v = src.AsTVMFFIAny();
+ details::AnyView2Str(os, &v);
+ return os;
+ }
+ template <typename BaseType = Type>
+ BaseType *GetRawObjPtr() const {
+ static_assert(std::is_same_v<BaseType, Object> || std::is_base_of_v<BaseType, Type>,
+ "Only downcasting is allowed");
+ return reinterpret_cast<BaseType *>(this->data_);
+ }
+ template <typename BaseType = Type>
+ BaseType *MoveToRawObjPtr() {
+ static_assert(std::is_same_v<BaseType, Object> || std::is_base_of_v<BaseType, Type>,
+ "Only downcasting is allowed");
+ BaseType *ret = reinterpret_cast<BaseType *>(this->data_);
+ this->data_ = nullptr;
+ return ret;
+ }
+
+protected:
+ using Allocator = typename GetAllocator<Type>::Type;
+ /***** Destructor *****/
+ TVM_FFI_INLINE ~RefBase() { this->DecRef(); }
+ /***** Constructor 0: default *****/
+ TVM_FFI_INLINE RefBase() : data_(nullptr) {}
+ /***** Constructor 1: From raw pointers *****/
+ TVM_FFI_INLINE RefBase(TVMFFIAny *data) : data_(data) { this->IncRef(); }
+ template <typename Derived, typename = EnableDerivedObj<Type, Derived>>
+ TVM_FFI_INLINE RefBase(Derived *data) : TSelf(reinterpret_cast<TVMFFIAny *>(data)) {}
+ TVM_FFI_DEF_ASSIGN(TSelf, TVMFFIAny *)
+ template <typename Derived, typename = EnableDerivedObj<Type, Derived>>
+ TVM_FFI_DEF_ASSIGN(TSelf, Derived *)
+ /***** Constructor 2: from RefBase<Type> *****/
+ TVM_FFI_INLINE RefBase(const TSelf &other) : data_(other.data_) {
+ this->IncRef();
+ }
+ TVM_FFI_INLINE RefBase(TSelf &&other) : data_(other.data_) { other.data_ = nullptr; }
+ TVM_FFI_DEF_ASSIGN(TSelf, const TSelf &)
+ TVM_FFI_DEF_ASSIGN(TSelf, TSelf &&)
+ /***** Constructor 3: from Ref<SubType>, Any and AnyView *****/
+ template <typename Other, typename = EnableAnyOrViewOrRef<Other>>
+ TVM_FFI_DEF_ASSIGN(TSub, Other &&)
+ template <typename Other, typename = EnableAnyOrViewOrRef<Other>>
+ TVM_FFI_DEF_ASSIGN(TSub, const Other &)
+ template <typename Other, typename = EnableAnyOrViewOrRef<Other>>
+ TVM_FFI_INLINE RefBase(const Other &src) : RefBase(src.template GetRawObjPtr<Type>()) {}
+ template <typename Other, typename = EnableAnyOrViewOrRef<Other>>
+ TVM_FFI_INLINE RefBase(Other &&src)
+ : data_(reinterpret_cast<TVMFFIAny *>(src.template MoveToRawObjPtr<Type>())) {
+ if constexpr (std::is_same_v<Other, AnyView>) {
+ this->IncRef();
+ }
+ }
+ TVM_FFI_INLINE void IncRef() { details::IncRef(this->data_); }
+ TVM_FFI_INLINE void DecRef() { details::DecRef(this->data_); }
+ TVM_FFI_INLINE void Swap(TSelf &other) { std::swap(this->data_, other.data_); }
+ TVM_FFI_INLINE TVMFFIAny AsTVMFFIAny() const {
+ if (data_ == nullptr) {
+ return TVMFFIAny();
+ }
+ TVMFFIAny ret{};
+ ret.type_index = data_->type_index;
+ ret.v_obj = data_;
+ return ret;
+ }
+
+ TVMFFIAny *data_;
+};
+
+template <typename Type>
+struct Ref : public RefBase<Type> {
+ TVM_FFI_DEF_TYPE_FRIENDS();
+ TVM_FFI_INLINE Ref() : RefBase<Type>() {}
+ TVM_FFI_REF_DEF_DELEGATE_CONSTRUCTORS(Ref<Type>, RefBase<Type>)
+};
+
+/********** Section 3. AnyView *********/
+
+struct AnyView : public TVMFFIAny {
+ TVM_FFI_DEF_TYPE_FRIENDS();
+ friend struct Any;
+ /***** Destructor *****/
+ TVM_FFI_INLINE ~AnyView() = default;
+ TVM_FFI_INLINE void Reset() { *(static_cast<TVMFFIAny *>(this)) = TVMFFIAny(); }
+ /***** Constructor 0: default *****/
+ TVM_FFI_INLINE AnyView() : TVMFFIAny() {}
+ /***** Constructor 1: from AnyView *****/
+ TVM_FFI_INLINE AnyView(const AnyView &src) = default;
+ TVM_FFI_INLINE AnyView &operator=(const AnyView &src) = default;
+ TVM_FFI_INLINE AnyView(AnyView &&src) : TVMFFIAny(*&src) { src.Reset(); }
+ TVM_FFI_DEF_ASSIGN(AnyView, AnyView &&)
+ /***** Constructor 2: from Any *****/
+ TVM_FFI_INLINE AnyView(const Any &src);
+ TVM_FFI_DEF_ASSIGN(AnyView, const Any &)
+ AnyView(Any &&src) = delete;
+ AnyView &operator=(Any &&src) = delete;
+ /***** Constructor 3: from Ref<T> *****/
+ template <typename T>
+ TVM_FFI_INLINE AnyView(const Ref<T> &src) : TVMFFIAny(src.AsTVMFFIAny()) {}
+ template <typename T>
+ TVM_FFI_DEF_ASSIGN(AnyView, const Ref<T> &)
+ template <typename T>
+ AnyView(Ref<T> &&src) = delete;
+ template <typename T>
+ AnyView &operator=(Ref<T> &&src) = delete;
+ /***** Constructors 4: use TypeTraits<T> *****/
+ template <typename Type, typename = HasTypeTraits<Type>>
+ TVM_FFI_INLINE AnyView(const Type &src) : TVMFFIAny() {
+ TypeTraitsNoCR<Type>::CopyFromTypeToTVMFFIAny(src, this);
+ }
+ template <typename Type, typename = HasTypeTraits<Type>>
+ TVM_FFI_DEF_ASSIGN(AnyView, const Type &)
+ /*** Converter 0: use TypeTraits<T> ***/
+ template <typename Type, typename = HasTypeTraits<Type>>
+ operator Type() const;
+ template <typename Type, typename = HasTypeTraits<Type>>
+ Type CastWithStorage(Any *storage) const;
+ /***** Misc *****/
+ Ref<Str> str() const;
+ TVM_FFI_INLINE friend std::ostream &operator<<(std::ostream &os, const AnyView &src) {
+ details::AnyView2Str(os, &src);
+ return os;
+ }
+ template <typename Type>
+ TVM_FFI_INLINE Type *GetRawObjPtr() const;
+ template <typename Type>
+ TVM_FFI_INLINE Type *MoveToRawObjPtr();
+
+protected:
+ TVM_FFI_INLINE void Swap(TVMFFIAny &src) {
+ TVMFFIAny tmp = *this;
+ *static_cast<TVMFFIAny *>(this) = src;
+ src = tmp;
+ }
+};
+
+/********** Section 4. Any *********/
+
+struct Any : public TVMFFIAny {
+ TVM_FFI_DEF_TYPE_FRIENDS();
+ friend struct AnyView;
+ /***** Destructor *****/
+ TVM_FFI_INLINE ~Any() { this->Reset(); }
+ TVM_FFI_INLINE void Reset() {
+ this->DecRef();
+ *(static_cast<TVMFFIAny *>(this)) = TVMFFIAny();
+ }
+ /***** Constructor 0: default *****/
+ TVM_FFI_INLINE Any() : TVMFFIAny() {}
+ /***** Constructor 1: from AnyView *****/
+ TVM_FFI_INLINE Any(const AnyView &src) : TVMFFIAny(*static_cast<const TVMFFIAny *>(&src)) {
+ if (this->type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
+ // Special case: handle the case where `Any` needs to own a raw string.
+ this->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr);
+ this->v_obj = details::StrCopyFromCharArray(this->v_str, std::strlen(this->v_str));
+ }
+ this->IncRef();
+ }
+ TVM_FFI_INLINE Any(AnyView &&src) : Any(static_cast<const AnyView &>(src)) { // TODO: add test
+ src.Reset();
+ }
+ TVM_FFI_DEF_ASSIGN(Any, const AnyView &)
+ TVM_FFI_DEF_ASSIGN(Any, AnyView &&)
+ /***** Constructor 2: from Any *****/
+ TVM_FFI_INLINE Any(const Any &src) : TVMFFIAny(*static_cast<const TVMFFIAny *>(&src)) {
+ this->IncRef();
+ }
+ TVM_FFI_INLINE Any(Any &&src) : TVMFFIAny(*static_cast<const TVMFFIAny *>(&src)) {
+ *static_cast<TVMFFIAny *>(&src) = TVMFFIAny();
+ }
+ TVM_FFI_DEF_ASSIGN(Any, const Any &)
+ TVM_FFI_DEF_ASSIGN(Any, Any &&)
+ /***** Constructor 3: from Ref<T> *****/
+ template <typename T>
+ TVM_FFI_INLINE Any(const Ref<T> &src) : TVMFFIAny(src.AsTVMFFIAny()) {
+ this->IncRef();
+ }
+ template <typename T>
+ TVM_FFI_INLINE Any(Ref<T> &&src) : TVMFFIAny(src.AsTVMFFIAny()) {
+ src.data_ = nullptr;
+ }
+ template <typename T>
+ TVM_FFI_DEF_ASSIGN(Any, const Ref<T> &)
+ template <typename T>
+ TVM_FFI_DEF_ASSIGN(Any, Ref<T> &&)
+ /***** Constructors 4: use TypeTraits<T> *****/
+ template <typename Type, typename = HasTypeTraits<Type>>
+ TVM_FFI_INLINE Any(const Type &src) : Any(AnyView(src)) {}
+ template <typename Type, typename = HasTypeTraits<Type>>
+ TVM_FFI_DEF_ASSIGN(Any, const Type &)
+ /***** Constructors 5: Special handling for strings *****/
+ TVM_FFI_INLINE Any(const std::string &s) : Any(AnyView(s)) {}
+ TVM_FFI_INLINE Any(const char *s) : Any(AnyView(s)) {}
+ TVM_FFI_INLINE Any(std::string &&s) : TVMFFIAny() {
+ this->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr);
+ this->v_obj = details::StrMoveFromStdString(std::move(s));
+ this->IncRef();
+ }
+ template <size_t N>
+ TVM_FFI_INLINE Any(const CharArray<N> &s) : TVMFFIAny() {
+ this->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr);
+ this->v_obj = details::StrCopyFromCharArray(s, N - 1);
+ this->IncRef();
+ }
+ TVM_FFI_DEF_ASSIGN(Any, const std::string &)
+ TVM_FFI_DEF_ASSIGN(Any, const char *&)
+ TVM_FFI_DEF_ASSIGN(Any, std::string &&)
+ template <size_t N>
+ TVM_FFI_DEF_ASSIGN(Any, const CharArray<N> &)
+ /*** Converter 0: use TypeTraits<T> ***/
+ template <typename Type, typename = HasTypeTraits<Type>>
+ operator Type() const;
+ /***** Misc *****/
+ Ref<Str> str() const;
+ TVM_FFI_INLINE friend std::ostream &operator<<(std::ostream &os, const Any &src) {
+ details::AnyView2Str(os, &src);
+ return os;
+ }
+ template <typename Type>
+ TVM_FFI_INLINE Type *GetRawObjPtr() const {
+ return reinterpret_cast<const AnyView *>(this)->GetRawObjPtr<Type>();
+ }
+ template <typename Type>
+ TVM_FFI_INLINE Type *MoveToRawObjPtr() {
+ return reinterpret_cast<AnyView *>(this)->MoveToRawObjPtr<Type>();
+ }
+
+protected:
+ TVM_FFI_INLINE void Swap(TVMFFIAny &src) {
+ TVMFFIAny tmp = *this;
+ *static_cast<TVMFFIAny *>(this) = src;
+ src = tmp;
+ }
+ TVM_FFI_INLINE void IncRef() {
+ if (!details::IsTypeIndexPOD(this->type_index)) {
+ details::IncRef(this->v_obj);
+ }
+ }
+ TVM_FFI_INLINE void DecRef() {
+ if (!details::IsTypeIndexPOD(this->type_index)) {
+ details::DecRef(this->v_obj);
+ }
+ }
+};
+
+/********** Section 5. Type Conversion and Type Table *********/
+
+TVM_FFI_INLINE AnyView::AnyView(const Any &src) : TVMFFIAny(*&src) {}
+
+#define TVM_FFI_TRY_CONVERT(Expr, TypeStr) \
+ try { \
+ return Expr; \
+ } catch (const TemporaryTypeError &) { \
+ TVM_FFI_THROW(TypeError) << "Cannot convert from type `" \
+ << TypeIndex2TypeKey(this->type_index) << "` to `" << TypeStr << "`"; \
+ } \
+ TVM_FFI_UNREACHABLE();
+template <typename Type, typename>
+inline AnyView::operator Type() const {
+ TVM_FFI_TRY_CONVERT(TypeTraitsNoCR<Type>::CopyFromTVMFFIAnyToType(this),
+ TypeTraitsNoCR<Type>::Type2Str());
+}
+template <typename Type, typename>
+inline Type AnyView::CastWithStorage(Any *storage) const {
+ TVM_FFI_TRY_CONVERT(TypeTraitsNoCR<Type>::CopyFromTVMFFIAnyToTypeWithStorage(this, storage),
+ TypeTraitsNoCR<Type>::Type2Str());
+}
+template <typename Type, typename>
+inline Any::operator Type() const {
+ TVM_FFI_TRY_CONVERT(TypeTraitsNoCR<Type>::CopyFromTVMFFIAnyToType(this),
+ TypeTraitsNoCR<Type>::Type2Str());
+}
+template <typename Type>
+inline Type *AnyView::GetRawObjPtr() const {
+ TVM_FFI_TRY_CONVERT(TypeTraitsNoCR<Type *>::CopyFromTVMFFIAnyToRef(this), Type::_type_key);
+}
+template <typename Type>
+inline Type *AnyView::MoveToRawObjPtr() {
+ TVM_FFI_TRY_CONVERT(TypeTraitsNoCR<Type *>::MoveFromTVMFFIAnyToRef(this), Type::_type_key);
+}
+#undef TVM_FFI_TRY_CONVERT
+
+#if TVM_FFI_ALLOW_DYN_TYPE
+TVM_FFI_INLINE void TypeSetAttr(int32_t type_index, const char *attr_key, AnyView attr_value) {
+ TVMFFIDynTypeSetAttr(nullptr, type_index, attr_key, &attr_value);
+}
+
+TVM_FFI_INLINE AnyView TypeGetAttr(int32_t type_index, const char *attr_key) {
+ TVMFFIAnyHandle attr;
+ TVMFFIDynTypeGetAttr(nullptr, type_index, attr_key, &attr);
+ return AnyView(*(static_cast<AnyView *>(attr)));
+}
+#endif
+
+} // namespace ffi
+} // namespace tvm
+
+#endif // TVM_FFI_CORE_H_
diff --git a/include/tvm/ffi/core/traits.h b/include/tvm/ffi/core/traits.h
new file mode 100644
index 0000000000..8f80b77f49
--- /dev/null
+++ b/include/tvm/ffi/core/traits.h
@@ -0,0 +1,613 @@
+#ifndef TVM_FFI_TRAITS_H_
+#define TVM_FFI_TRAITS_H_
+
+#include "./utils.h"
+#include <sstream>
+#include <string>
+
+namespace tvm {
+namespace ffi {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4702)
+#endif
+
+/*!
+ * \brief TypeTraits<T> is a template class that provides a set of static
+ * methods that are associated with a specific type `T` for compile-time
+ * dispatching.
+ *
+ * [Trait 0] Type2Str<T>: () -> std::string
+ * Returns the string representation of the type `T`
+ *
+ * [Trait 1] CopyFromTVMFFIAnyToType<T>: (const TVMFFIAny *) -> T
+ * Converts an `AnyView` or `Any` to `T`. It could incur copy when
+ * inevitable, for example, copy the content of a Str obj if it's converted
+ * to `std::string`.
+ *
+ * It is used in the following cases in the codebase:
+ * 1) `AnyView::operator Type()`
+ * 2) `Any::operator Type()`
+ *
+ * [Trait 2] CopyFromTypeToTVMFFIAny<T>: (T, TVMFFIAny *) -> void
+ * Converts a value of type T to `AnyView`.
+ *
+ * It is used in the following case in the codebase:
+ * 1) `AnyView::AnyView(const Type& src)`
+ *
+ * [Trait 3] CopyFromTVMFFIAnyToRef<T>: (const TVMFFIAny *) -> T*
+ * Converts an `AnyView` or `Any` to `TVMFFIAny*`, which is subsequently used
+ * to initialize `Ref<T>::data_`. Note that this method does not increment
+ * the reference counter, which is supposed to be later handled automatically by
+ * `Ref<T>`.
+ *
+ * It is used in the following cases in the codebase:
+ * 1) `Ref<Type>::Ref(const AnyView& src)`
+ * 2) `Ref<Type>::Ref(const Any& src)`
+ *
+ * [Trait 4] MoveFromTVMFFIAnyToRef<T>: (TVMFFIAny *) -> T*
+ * Moves an `AnyView` or `Any` to `TVMFFIAny*`, which is subsequently used
+ * to initialize `Ref<T>::data_`. Note that the reference counter will not
+ * increment throughout the process because it corresponds to move semantics.
+ *
+ * It is used in the following cases in the codebase:
+ * 1) `Ref<Type>::Ref(AnyView&& src)`
+ * 2) `Ref<Type>::Ref(Any&& src)`
+ *
+ * [Trait 5] (Optional) CopyFromTVMFFIAnyToTypeWithStorage<T>:
+ * (const TVMFFIAny *, Any *) -> T
+ * It does similar thing as [Trait 1] that converts an `AnyView` or `Any` to
+ * `T`, but is onyl used by TVM FFI's calling convention, where an additional
+ * storage `Any*` is provided to retain ownership when unpacking `AnyView[]`
+ * before calling into a C++ function.
+ *
+ * Example. When converting an `AnyView (kTVMFFIRawStr)` to `Str*`,
+ * which consists of two intermediate steps `AnyView (kTVMFFIRawStr)` to
+ * `Ref<Str>`, and `Ref<Str>` to `Str*`, this method will store the by-product
+ * `Ref<Str>` into the given storage to lifespan expiration.
+ *
+ * It is used in the following case(s) in the codebase:
+ * 1) `AnyView::CastWithStorage<Type>(Any *)`
+ */
+template <typename, typename = void>
+struct TypeTraits {
+ constexpr static bool enabled = false;
+};
+template <typename T>
+using TypeTraitsNoCR = TypeTraits<details::RemoveCR<T>>;
+template <typename T>
+using HasTypeTraits = std::enable_if_t<TypeTraitsNoCR<T>::enabled>;
+
+template <enum TVMFFITypeIndex type_index>
+struct TypeIndexTraits;
+
+#define TVM_FFI_DEF_TYPE_INDEX_TRAITS(TypeIndex_, TypeKey) \
+ template <> \
+ struct TypeIndexTraits<TypeIndex_> { \
+ static constexpr const char *type_key = TypeKey; \
+ }
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFINone, "None");
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIInt, "int");
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIFloat, "float");
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIPtr, "Ptr");
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIDataType, "dtype");
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIDevice, "Device");
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIRawStr, "const char *");
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIObject, "object.Object");
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIList, "object.List");
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIDict, "object.Dict");
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIError, "object.Error");
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIFunc, "object.Func");
+TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIStr, "object.Str");
+#undef TVM_FFI_DEF_TYPE_INDEX_TRAITS
+
+TVM_FFI_INLINE const char *TypeIndex2TypeKey(int32_t type_index) {
+#define TVM_FFI_TYPE_INDEX_SWITCH_CASE(TypeIndex_) \
+ case TypeIndex_: \
+ return TypeIndexTraits<TypeIndex_>::type_key;
+ switch (static_cast<TVMFFITypeIndex>(type_index)) {
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFINone);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIInt);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIFloat);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIPtr);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIDataType);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIDevice);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIRawStr);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIObject);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIList);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIDict);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIError);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIFunc);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIStr);
+ default:
+#if TVM_FFI_ALLOW_DYN_TYPE
+ {
+ TVMFFITypeInfoHandle type_info;
+ TVMFFIDynTypeIndex2Info(nullptr, type_index, &type_info);
+ return type_info ? type_info->type_key : "(undefined)";
+ }
+#else
+ return "Unknown";
+#endif
+ }
+#undef TVM_FFI_TYPE_INDEX_SWITCH_CASE
+ TVM_FFI_UNREACHABLE();
+}
+
+namespace details {
+struct DummyRoot {
+ static constexpr bool _type_is_static = true;
+ static constexpr int32_t _type_depth = -1;
+ static constexpr int32_t _type_index = -1;
+ static constexpr std::array<int32_t, 0> _type_ancestors = {};
+};
+template <typename F, typename TypeTableType>
+TVM_FFI_INLINE void InitTypeTable(F f, TypeTableType *self) {
+ {
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFINone, DummyRoot>(self);
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFIInt, DummyRoot>(self);
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFIFloat, DummyRoot>(self);
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFIPtr, DummyRoot>(self);
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFIDevice, DummyRoot>(self);
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFIDataType, DummyRoot>(self);
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFIRawStr, DummyRoot>(self);
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFIObject, Object>(self);
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFIList, Object>(self);
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFIDict, Object>(self);
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFIError, Error>(self);
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFIStr, Str>(self);
+ f.template RegisterType<TVMFFITypeIndex::kTVMFFIFunc, Func>(self);
+ }
+ {
+ f.template RegisterStr<TVMFFITypeIndex::kTVMFFINone, void *>(self);
+ f.template RegisterStr<TVMFFITypeIndex::kTVMFFIInt, int64_t>(self);
+ f.template RegisterStr<TVMFFITypeIndex::kTVMFFIFloat, double>(self);
+ f.template RegisterStr<TVMFFITypeIndex::kTVMFFIPtr, void *>(self);
+ f.template RegisterStr<TVMFFITypeIndex::kTVMFFIDevice, DLDevice>(self);
+ f.template RegisterStr<TVMFFITypeIndex::kTVMFFIDataType, DLDataType>(self);
+ f.template RegisterStr<TVMFFITypeIndex::kTVMFFIRawStr, const char *>(self);
+ f.template RegisterStr<TVMFFITypeIndex::kTVMFFIStr, const char *>(self);
+ }
+}
+
+template <typename T>
+using Identity = T;
+
+template <template <typename> typename F = Identity>
+TVM_FFI_INLINE void StaticAnyView2Str(std::ostream &os, const TVMFFIAny *v) {
+#define TVM_FFI_TYPE_INDEX_SWITCH_CASE(TypeIndex_, Type) \
+ case TypeIndex_: { \
+ using Traits = F<TypeTraits<Type>>; \
+ os << Traits::__str__(Traits::CopyFromTVMFFIAnyToType(v)); \
+ break; \
+ }
+ switch (static_cast<TVMFFITypeIndex>(v->type_index)) {
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFINone, void *);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIInt, int64_t);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIFloat, double);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIPtr, void *);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIDevice, DLDevice);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIDataType, DLDataType);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIRawStr, const char *);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIStr, const char *);
+ default: {
+ os << TypeIndex2TypeKey(v->type_index) << '@' << v->v_ptr;
+ break;
+ }
+ }
+#undef TVM_FFI_TYPE_INDEX_SWITCH_CASE
+}
+
+template <typename DerivedType>
+struct StaticIsInstanceOf {
+ static TVM_FFI_INLINE bool Check(int32_t type_index) {
+#define TVM_FFI_TYPE_INDEX_SWITCH_CASE(TypeIndex_, Type) \
+ case TypeIndex_: { \
+ return StaticIsInstanceOf<DerivedType>::CheckImpl<Type>(); \
+ }
+ switch (static_cast<TVMFFITypeIndex>(type_index)) {
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIObject, Object);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIList, List);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIDict, Dict);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIError, Error);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIFunc, Func);
+ TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIStr, Str);
+ default:
+ return false;
+ }
+#undef TVM_FFI_TYPE_INDEX_SWITCH_CASE
+ TVM_FFI_UNREACHABLE();
+ }
+
+private:
+ template <typename SelfType>
+ static constexpr bool CheckImpl() {
+ constexpr auto ancestors = SelfType::_type_ancestors;
+ constexpr int32_t ancestors_depth = static_cast<int32_t>(ancestors.size());
+ constexpr int32_t type_depth = DerivedType::_type_depth;
+ return ancestors_depth > type_depth && ancestors[type_depth] == DerivedType::_type_index;
+ }
+};
+
+template <typename DerivedType, typename SelfType = Object>
+TVM_FFI_INLINE bool IsInstanceOf(int32_t type_index) {
+ // TODO: Support non-Object types
+ if constexpr (std::is_same_v<DerivedType, Object> ||
+ std::is_base_of_v</*base=*/DerivedType, /*derived=*/SelfType>) {
+ return true;
+ }
+ // Special case: `DerivedType` is exactly the underlying type of `type_index`
+ if (type_index == DerivedType::_type_index) {
+ return true;
+ }
+ // Given an index `i = DerivedType::_type_index`,
+ // and the underlying type of `T = *this`, we wanted to check if
+ // `T::_type_ancestors[i] == DerivedType::_type_index`.
+ //
+ // There are 3 ways to reflect `T` out of `this->type_index`:
+ // (Case 1) Use `SelfType` as a surrogate type if `SelfType::_type_ancestors`
+ // is long enough, whose length is reflected by `SelfType::_type_depth`.
+ if constexpr (SelfType::_type_depth > DerivedType::_type_depth) {
+ return SelfType::_type_ancestors[DerivedType::_type_depth] == DerivedType::_type_index;
+ }
+ if constexpr (SelfType::_type_depth == DerivedType::_type_depth) {
+ return SelfType::_type_index == DerivedType::_type_index;
+ }
+ // (Case 2) If `type_index` falls in static object section, use switch case to
+ // enumerate all possibilities. It could be more efficient because checks can
+ // potentially be simplified by the compiler.
+ if (details::IsTypeIndexPOD(type_index)) {
+ return ::tvm::ffi::details::StaticIsInstanceOf<DerivedType>::Check(type_index);
+ }
+ // (Case 3) Look up the type table for type hierarchy via `type_index`.
+#if TVM_FFI_ALLOW_DYN_TYPE
+ TVMFFITypeInfoHandle info;
+ TVMFFIDynTypeIndex2Info(nullptr, type_index, &info);
+ if (info == nullptr) {
+ TVM_FFI_THROW(InternalError) << "Undefined type index: " << type_index;
+ }
+ return info->type_depth > DerivedType::_type_depth &&
+ info->type_ancestors[DerivedType::_type_depth] == DerivedType::_type_index;
+#else
+ if (type_index > static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDynObjectBegin)) {
+ TVM_FFI_THROW(BuildError) << "Dynamic type is not supported. Please set "
+ "`TVM_FFI_ALLOW_DYN_TYPE=ON` when building TVM FFI";
+ }
+ return false;
+#endif
+}
+
+} // namespace details
+
+TVM_FFI_INLINE const char *DLDeviceType2Str(DLDeviceType type) {
+ switch (type) {
+ case kDLCPU:
+ return "cpu";
+ case kDLCUDA:
+ return "cuda";
+ case kDLCUDAHost:
+ return "cuda_host";
+ case kDLOpenCL:
+ return "opencl";
+ case kDLVulkan:
+ return "vulkan";
+ case kDLMetal:
+ return "mps";
+ case kDLVPI:
+ return "vpi";
+ case kDLROCM:
+ return "rocm";
+ case kDLROCMHost:
+ return "rocm_host";
+ case kDLExtDev:
+ return "ext_dev";
+ case kDLCUDAManaged:
+ return "cuda_managed";
+ case kDLOneAPI:
+ return "oneapi";
+ case kDLWebGPU:
+ return "webgpu";
+ case kDLHexagon:
+ return "hexagon";
+ case kDLMAIA:
+ return "maia";
+ }
+ return "unknown";
+}
+
+TVM_FFI_INLINE const char *DLDataTypeCode2Str(DLDataTypeCode type_code) {
+ switch (type_code) {
+ case kDLInt:
+ return "int";
+ case kDLUInt:
+ return "uint";
+ case kDLFloat:
+ return "float";
+ case kDLOpaqueHandle:
+ return "ptr";
+ case kDLBfloat:
+ return "bfloat";
+ case kDLComplex:
+ return "complex";
+ case kDLBool:
+ return "bool";
+ }
+ return "unknown";
+}
+
+/********** TypeTraits: Object *********/
+
+template <typename ObjectType>
+struct TypeTraitsDefaultForObject {
+ static constexpr bool enabled = true;
+
+ TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(ObjectType *src, TVMFFIAny *ret) {
+ if (src == nullptr) {
+ ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone);
+ ret->v_obj = nullptr;
+ } else {
+ ret->type_index = src->type_index;
+ ret->v_obj = const_cast<TVMFFIAny *>(reinterpret_cast<const TVMFFIAny *>(src));
+ }
+ }
+
+ TVM_FFI_INLINE static ObjectType *CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
+ if (details::IsTypeIndexNone(v->type_index)) {
+ return nullptr;
+ }
+ if (!details::IsTypeIndexPOD(v->type_index) &&
+ details::IsInstanceOf<ObjectType>(v->type_index)) {
+ return reinterpret_cast<ObjectType *>(v->v_obj);
+ }
+ throw TemporaryTypeError();
+ }
+
+ TVM_FFI_INLINE static ObjectType *CopyFromTVMFFIAnyToRef(const TVMFFIAny *v) {
+ return CopyFromTVMFFIAnyToType(v);
+ }
+
+ TVM_FFI_INLINE static ObjectType *MoveFromTVMFFIAnyToRef(TVMFFIAny *v) {
+ if (details::IsTypeIndexNone(v->type_index)) {
+ return nullptr;
+ }
+ if (!details::IsTypeIndexPOD(v->type_index) &&
+ details::IsInstanceOf<ObjectType>(v->type_index)) {
+ ObjectType *ret = reinterpret_cast<ObjectType *>(v->v_obj);
+ v->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone);
+ v->v_obj = nullptr;
+ return ret;
+ }
+ throw TemporaryTypeError();
+ }
+
+ TVM_FFI_INLINE static std::string Type2Str() { return ObjectType::_type_key + std::string(" *"); }
+};
+
+template <typename ObjectType>
+struct TypeTraits<ObjectType *, std::enable_if_t<IsObject<ObjectType>>>
+ : public TypeTraitsDefaultForObject<ObjectType> {};
+
+/********** TypeTraits: Integer *********/
+
+template <typename Int>
+struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> {
+ static constexpr bool enabled = true;
+ TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(Int src, TVMFFIAny *ret) {
+ ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt);
+ ret->v_int64 = static_cast<int64_t>(src);
+ }
+
+ TVM_FFI_INLINE static Int CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
+ TVMFFITypeIndex type_index = static_cast<TVMFFITypeIndex>(v->type_index);
+ if (type_index == TVMFFITypeIndex::kTVMFFIInt) {
+ return static_cast<Int>(v->v_int64);
+ }
+ throw TemporaryTypeError();
+ }
+
+ TVM_FFI_INLINE static std::string Type2Str() {
+ return TypeIndexTraits<TVMFFITypeIndex::kTVMFFIInt>::type_key;
+ }
+
+ TVM_FFI_INLINE static std::string __str__(int64_t src) { return std::to_string(src); }
+};
+
+/********** TypeTraits: Float *********/
+
+template <typename Float>
+struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>> {
+ static constexpr bool enabled = true;
+ TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(Float src, TVMFFIAny *ret) {
+ ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFloat);
+ ret->v_float64 = src;
+ }
+
+ TVM_FFI_INLINE static Float CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
+ TVMFFITypeIndex type_index = static_cast<TVMFFITypeIndex>(v->type_index);
+ if (type_index == TVMFFITypeIndex::kTVMFFIFloat) {
+ return v->v_float64;
+ } else if (type_index == TVMFFITypeIndex::kTVMFFIInt) {
+ return static_cast<Float>(v->v_int64);
+ }
+ throw TemporaryTypeError();
+ }
+
+ TVM_FFI_INLINE static std::string Type2Str() {
+ return TypeIndexTraits<TVMFFITypeIndex::kTVMFFIFloat>::type_key;
+ }
+
+ TVM_FFI_INLINE static std::string __str__(double src) { return std::to_string(src); }
+};
+
+/********** TypeTraits: Opaque Pointer *********/
+
+template <>
+struct TypeTraits<void *> {
+ static constexpr bool enabled = true;
+ using Ptr = void *;
+
+ TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(Ptr src, TVMFFIAny *ret) {
+ ret->type_index = (src == nullptr) ? static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)
+ : static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIPtr);
+ ret->v_ptr = src;
+ }
+
+ TVM_FFI_INLINE static Ptr CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
+ TVMFFITypeIndex type_index = static_cast<TVMFFITypeIndex>(v->type_index);
+ if (type_index == TVMFFITypeIndex::kTVMFFIPtr || type_index == TVMFFITypeIndex::kTVMFFIRawStr ||
+ type_index == TVMFFITypeIndex::kTVMFFINone) {
+ return v->v_ptr;
+ }
+ throw TemporaryTypeError();
+ }
+
+ TVM_FFI_INLINE static std::string Type2Str() {
+ return TypeIndexTraits<TVMFFITypeIndex::kTVMFFIPtr>::type_key;
+ }
+
+ TVM_FFI_INLINE static std::string __str__(Ptr src) {
+ if (src == nullptr) {
+ return "None";
+ } else {
+ std::ostringstream oss;
+ oss << src;
+ return oss.str();
+ }
+ }
+};
+
+template <>
+struct TypeTraits<std::nullptr_t> : public TypeTraits<void *> {};
+
+/********** TypeTraits: DLDevice *********/
+
+template <>
+struct TypeTraits<DLDevice> {
+ static constexpr bool enabled = true;
+ TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(DLDevice src, TVMFFIAny *ret) {
+ ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDevice);
+ ret->v_device = src;
+ }
+
+ TVM_FFI_INLINE static DLDevice CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
+ TVMFFITypeIndex type_index = static_cast<TVMFFITypeIndex>(v->type_index);
+ if (type_index == TVMFFITypeIndex::kTVMFFIDevice) {
+ return v->v_device;
+ }
+ throw TemporaryTypeError();
+ }
+
+ TVM_FFI_INLINE static std::string Type2Str() {
+ return TypeIndexTraits<TVMFFITypeIndex::kTVMFFIDevice>::type_key;
+ }
+
+ TVM_FFI_INLINE static std::string __str__(DLDevice device) {
+ std::ostringstream os;
+ os << DLDeviceType2Str(static_cast<DLDeviceType>(device.device_type)) << ":"
+ << device.device_id;
+ return os.str();
+ }
+};
+
+/********** TypeTraits: DLDataType *********/
+
+template <>
+struct TypeTraits<DLDataType> {
+ static constexpr bool enabled = true;
+ TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(DLDataType src, TVMFFIAny *ret) {
+ ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDataType);
+ ret->v_dtype = src;
+ }
+
+ TVM_FFI_INLINE static DLDataType CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
+ TVMFFITypeIndex type_index = static_cast<TVMFFITypeIndex>(v->type_index);
+ if (type_index == TVMFFITypeIndex::kTVMFFIDataType) {
+ return v->v_dtype;
+ }
+ throw TemporaryTypeError();
+ }
+
+ TVM_FFI_INLINE static std::string Type2Str() {
+ return TypeIndexTraits<TVMFFITypeIndex::kTVMFFIDataType>::type_key;
+ }
+
+ TVM_FFI_INLINE static std::string __str__(DLDataType dtype) {
+ DLDataTypeCode code = static_cast<DLDataTypeCode>(dtype.code);
+ int32_t bits = dtype.bits;
+ int32_t lanes = dtype.lanes;
+ if (code == kDLUInt && bits == 1 && lanes == 1) {
+ return "bool";
+ }
+ if (code == kDLOpaqueHandle && bits == 0 && lanes == 0) {
+ return "void";
+ }
+ std::ostringstream os;
+ os << DLDataTypeCode2Str(code) << bits;
+ if (lanes != 1) {
+ os << "x" << lanes;
+ }
+ return os.str();
+ }
+};
+
+/********** TypeTraits: String *********/
+
+template <>
+struct TypeTraits<const char *> {
+ static constexpr bool enabled = true;
+
+ TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(const char *src, TVMFFIAny *ret) {
+ ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr);
+ ret->v_str = src;
+ }
+
+ TVM_FFI_INLINE static const char *CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
+ TVMFFITypeIndex type_index = static_cast<TVMFFITypeIndex>(v->type_index);
+ if (type_index == TVMFFITypeIndex::kTVMFFIRawStr) {
+ return v->v_str;
+ }
+ if (type_index == TVMFFITypeIndex::kTVMFFIStr) {
+ return reinterpret_cast<TVMFFIStr *>(v->v_obj)->data;
+ }
+ throw TemporaryTypeError();
+ }
+
+ TVM_FFI_INLINE static std::string Type2Str() {
+ return TypeIndexTraits<TVMFFITypeIndex::kTVMFFIRawStr>::type_key;
+ }
+
+ TVM_FFI_INLINE static std::string __str__(const char *src) {
+ return '"' + std::string(src) + '"';
+ }
+};
+
+template <size_t N>
+struct TypeTraits<char[N]> : public TypeTraits<const char *> {};
+
+template <>
+struct TypeTraits<std::string> {
+ static constexpr bool enabled = true;
+
+ TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(const std::string &src, TVMFFIAny *ret) {
+ ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr);
+ ret->v_str = src.data();
+ }
+
+ TVM_FFI_INLINE static std::string CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
+ return TypeTraits<const char *>::CopyFromTVMFFIAnyToType(v);
+ }
+
+ TVM_FFI_INLINE static std::string Type2Str() { return "str"; }
+
+ TVM_FFI_INLINE static std::string __str__(const char *src) {
+ return '"' + std::string(src) + '"';
+ }
+};
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+} // namespace ffi
+} // namespace tvm
+
+#endif // TVM_FFI_TRAITS_H_
diff --git a/include/tvm/ffi/core/utils.h b/include/tvm/ffi/core/utils.h
new file mode 100644
index 0000000000..93630058e6
--- /dev/null
+++ b/include/tvm/ffi/core/utils.h
@@ -0,0 +1,281 @@
+#ifndef TVM_FFI_UTILS_H_
+#define TVM_FFI_UTILS_H_
+#ifdef _MSC_VER
+#include <intrin.h>
+#pragma intrinsic(_BitScanReverse64)
+#pragma intrinsic(_InterlockedIncrement)
+#pragma intrinsic(_InterlockedDecrement)
+#endif
+#if __cplusplus >= 202002L
+#include <bit>
+#endif
+#include "./c_ffi_abi.h"
+#include <array>
+#include <cstddef>
+#include <sstream>
+#include <type_traits>
+
+namespace tvm {
+namespace ffi {
+
+/********** Section 1. Macros *********/
+
+#if defined(_MSC_VER)
+#define TVM_FFI_INLINE __forceinline
+#else
+#define TVM_FFI_INLINE inline __attribute__((always_inline))
+#endif
+
+#if defined(_MSC_VER)
+#define TVM_FFI_UNREACHABLE() __assume(false)
+#else
+#define TVM_FFI_UNREACHABLE() __builtin_unreachable()
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define TVM_FFI_SYMBOL_HIDE __attribute__((visibility("hidden")))
+#else
+#define TVM_FFI_SYMBOL_HIDE
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define TVM_FFI_FUNC_SIG __PRETTY_FUNCTION__
+#elif defined(_MSC_VER)
+#define TVM_FFI_FUNC_SIG __FUNCSIG__
+#else
+#define TVM_FFI_FUNC_SIG __func__
+#endif
+
+#define TVM_FFI_DEF_ASSIGN(SelfType, SourceType) \
+ TVM_FFI_INLINE SelfType &operator=(SourceType other) { \
+ SelfType(std::move(other)).Swap(*this); \
+ return *this; \
+ }
+
+#define TVM_FFI_THROW(ErrorKind) \
+ ::tvm::ffi::details::ErrorBuilder(#ErrorKind, __FILE__, TVM_FFI_FUNC_SIG, __LINE__).Get()
+
+/********** Section 2. Any/Object: Extensions and inheritance *********/
+
+template <typename TExtra>
+struct AnyWithExtra {
+ int32_t type_index;
+ int32_t ref_cnt;
+ void (*deleter)(void *);
+ TExtra _extra;
+ TVM_FFI_INLINE AnyWithExtra() = default;
+ // TODO: add tests for copy/move constructors
+ TVM_FFI_INLINE AnyWithExtra(const AnyWithExtra &other) : _extra(other._extra) {}
+ TVM_FFI_INLINE AnyWithExtra(AnyWithExtra &&other) : _extra(std::move(other._extra)) {}
+ TVM_FFI_INLINE AnyWithExtra &operator=(const AnyWithExtra &other) {
+ this->_extra = other._extra;
+ return *this;
+ }
+ TVM_FFI_INLINE AnyWithExtra &operator=(AnyWithExtra &&other) {
+ this->_extra = std::move(other._extra);
+ return *this;
+ }
+};
+
+struct AnyView;
+struct Any;
+struct Object;
+struct Dict;
+struct Error;
+struct Str;
+struct Func;
+template <typename>
+struct Ref;
+template <typename>
+struct RefBase;
+struct List;
+
+template <typename T>
+struct IsAnyWithExtra {
+ template <typename U>
+ static auto test(AnyWithExtra<U> *) -> std::true_type;
+ static auto test(...) -> std::false_type;
+ using type = decltype(test(std::declval<T *>()));
+};
+template <typename T>
+constexpr static bool IsObject = std::is_base_of_v<Object, T> || IsAnyWithExtra<T>::type::value;
+
+template <typename ObjectType>
+struct DefaultObjectAllocator {
+ using Storage = typename std::aligned_storage<sizeof(ObjectType), alignof(ObjectType)>::type;
+
+ template <typename... Args>
+ inline static ObjectType *NewImpl(size_t num_storages, Args &&...args) {
+ Storage *data = new Storage[num_storages];
+ try {
+ new (data) ObjectType(std::forward<Args>(args)...);
+ } catch (...) {
+ delete[] data;
+ throw;
+ }
+ ObjectType *ret = reinterpret_cast<ObjectType *>(data);
+ ret->type_index = ObjectType::_type_index;
+ ret->ref_cnt = 0;
+ ret->deleter = DefaultObjectAllocator::Deleter;
+ return ret;
+ }
+
+ template <typename... Args>
+ TVM_FFI_INLINE static ObjectType *New(Args &&...args) {
+ return NewImpl(1, std::forward<Args>(args)...);
+ }
+
+ template <typename PadType, typename... Args>
+ TVM_FFI_INLINE static ObjectType *NewWithPad(size_t pad_size, Args &&...args) {
+ return NewImpl((sizeof(ObjectType) + pad_size * sizeof(PadType) + sizeof(Storage) - 1) /
+ sizeof(Storage),
+ std::forward<Args>(args)...);
+ }
+
+ static void Deleter(void *objptr) {
+ ObjectType *tptr = static_cast<ObjectType *>(objptr);
+ tptr->ObjectType::~ObjectType();
+ delete[] reinterpret_cast<Storage *>(tptr);
+ }
+};
+
+template <typename T, typename = void>
+struct GetAllocator {
+ using Type = DefaultObjectAllocator<T>;
+};
+template <typename T>
+struct GetAllocator<T, std::void_t<typename T::Allocator>> {
+ using Type = typename T::Allocator;
+};
+
+template <size_t N>
+using CharArray = const char[N];
+
+/********** Section 3. Atomic Operations *********/
+
+namespace details {
+uint64_t TVMFFIStrHash(const TVMFFIStr *str);
+int32_t TVMFFIStrCmp(const TVMFFIStr *a, const TVMFFIStr *b);
+TVM_FFI_INLINE int32_t AtomicIncrementRelaxed(int32_t *ptr) {
+#ifdef _MSC_VER
+ return _InterlockedIncrement(reinterpret_cast<volatile long *>(ptr)) - 1;
+#else
+ return __atomic_fetch_add(ptr, 1, __ATOMIC_RELAXED);
+#endif
+}
+TVM_FFI_INLINE int32_t AtomicDecrementRelAcq(int32_t *ptr) {
+#ifdef _MSC_VER
+ return _InterlockedDecrement(reinterpret_cast<volatile long *>(ptr)) + 1;
+#else
+ return __atomic_fetch_sub(ptr, 1, __ATOMIC_ACQ_REL);
+#endif
+}
+
+TVM_FFI_INLINE void IncRef(TVMFFIObject *obj) {
+ if (obj != nullptr) {
+ AtomicIncrementRelaxed(&obj->ref_cnt);
+ }
+}
+
+TVM_FFI_INLINE void DecRef(TVMFFIObject *obj) {
+ if (obj != nullptr) {
+ if (AtomicDecrementRelAcq(&obj->ref_cnt) == 1) {
+ if (obj->deleter) {
+ obj->deleter(obj);
+ }
+ }
+ }
+}
+
+TVM_FFI_INLINE bool IsTypeIndexNone(int32_t type_index) {
+ return type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone);
+}
+
+TVM_FFI_INLINE bool IsTypeIndexPOD(int32_t type_index) {
+ return type_index < static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin);
+}
+
+} // namespace details
+
+/********** Section 4. Errors *********/
+
+struct TemporaryTypeError : public std::exception {};
+
+namespace details {
+
+template <typename T>
+using RemoveCR = std::remove_const_t<std::remove_reference_t<T>>;
+template <typename T>
+static constexpr bool IsAnyOrView = std::is_same_v<T, Any> || std::is_same_v<T, AnyView>;
+
+template <typename>
+struct IsRefImpl {
+ static constexpr bool value = false;
+};
+
+template <typename T>
+struct IsRefImpl<Ref<T>> {
+ static constexpr bool value = true;
+};
+template <typename T>
+constexpr bool IsRef = IsRefImpl<T>::value;
+
+void AnyView2Str(std::ostream &os, const TVMFFIAny *v);
+TVMFFIObject *StrMoveFromStdString(std::string &&source);
+TVMFFIObject *StrCopyFromCharArray(const char *source, size_t length);
+[[noreturn]] void TVMErrorFromBuilder(std::string &&kind, std::string &&lineno,
+ std::string &&message) noexcept(false);
+
+template <typename ParentType, size_t size>
+inline constexpr std::array<int32_t, size> ObjectAncestorsConstExpr() {
+ std::array<int32_t, size> ret{};
+ for (size_t i = 0; i < size; ++i) {
+ ret[i] = (i + 1 == size) ? (ParentType::_type_index) : (ParentType::_type_ancestors[i]);
+ }
+ return ret;
+}
+template <typename ParentType, size_t size>
+inline std::array<int32_t, size> ObjectAncestors() {
+ std::array<int32_t, size> ret{};
+ for (size_t i = 0; i < size; ++i) {
+ ret[i] = (i + 1 == size) ? (ParentType::_type_index) : (ParentType::_type_ancestors[i]);
+ }
+ return ret;
+}
+
+// Disable warning about throwing in the destructor
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4722) // throw in the destructor, which is expected behavior
+#endif
+
+struct ErrorBuilder {
+ const char *kind;
+ TVMFFIStackFrame frame;
+ std::ostringstream oss;
+ std::string line_info;
+
+ explicit ErrorBuilder(const char *kind, const char *filename, const char *func, int32_t lineno)
+ : kind(kind), frame{filename, func, lineno, ErrorBuilder::StackFrameDeleter} {
+ this->line_info = std::string(filename) + ":" + std::to_string(lineno);
+ }
+
+ [[noreturn]] ~ErrorBuilder() noexcept(false) {
+ details::TVMErrorFromBuilder(this->kind, std::move(this->line_info), oss.str());
+ throw;
+ }
+
+ std::ostringstream &Get() { return this->oss; }
+
+protected:
+ static void StackFrameDeleter(void *self) { delete static_cast<TVMFFIStackFrame *>(self); }
+};
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+} // namespace details
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_UTILS_H_
diff --git a/include/tvm/ffi/ext/dict.h b/include/tvm/ffi/ext/dict.h
new file mode 100644
index 0000000000..e88a29b5e6
--- /dev/null
+++ b/include/tvm/ffi/ext/dict.h
@@ -0,0 +1,515 @@
+#ifndef TVM_FFI_DICT_H_
+#define TVM_FFI_DICT_H_
+#include <iterator>
+#include <tvm/ffi/core/core.h>
+#include <type_traits>
+#include <unordered_map>
+
+namespace tvm {
+namespace ffi {
+namespace details {
+struct _DictHeader {
+ int64_t capacity;
+ int64_t size;
+};
+using DictHeader = AnyWithExtra<_DictHeader>;
+static_assert(sizeof(DictHeader) == sizeof(TVMFFIDict));
+static_assert(offsetof(DictHeader, _extra.capacity) == offsetof(TVMFFIDict, capacity));
+static_assert(offsetof(DictHeader, _extra.size) == offsetof(TVMFFIDict, size));
+int32_t CountLeadingZeros(uint64_t);
+uint64_t BitCeil(uint64_t);
+} // namespace details
+
+/*!
+ * Dict is a flat hash table that uses quadratic/triangluar probing [1] for collision resolution.
+ * Its underlying data structure is a consecutive array of `Block`s, where each `Block`
+ * contains `kCapacity = 16` key-value pairs and their metadata (see also `Block`).
+ * The capacity of a Dict is always a power of 2, and Fibonacci Hashing [2] is used to avoid integer
+ * modulo and help improve the distribution of the hash values.
+ *
+ * Probing is conducted only within a fixed set of 125 possible offsets (see also
+ * `kNextProbeLocation`), which effectively organizes the elements of collision chains in a linked
+ * list. With the help of metadata, we can easily iterate through the linked list and relocate
+ * elements whenever necessary.
+ *
+ * [1] Triangular numbers. https://fgiesen.wordpress.com/2015/02/22/triangular-numbers-mod-2n/
+ * [2] Fibonacci Hashing: https://programmingpraxis.com/2018/06/19/fibonacci-hash/
+ */
+template <typename Hash, typename Equal>
+struct DictBase : protected details::DictHeader {
+protected:
+ /* clang-format off */
+ inline static const constexpr uint64_t kNextProbeLocation[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ /* triangle numbers for quadratic probing */ 21, 28, 36, 45, 55, 66, 78, 91, 105, 120, 136, 153, 171, 190, 210, 231, 253, 276, 300, 325, 351, 378, 406, 435, 465, 496, 528, 561, 595, 630, 666, 703, 741, 780, 820, 861, 903, 946, 990, 1035, 1081, 1128, 1176, 1225, 1275, 1326, 1378, 1431, 1485, 1540, 1596, 1653, 1711, 1770, 1830, 1891, 1953, 2016, 2080, 2145, 2211, 2278, 2346, 2415, 2485, 2556, 2628,
+ /* larger triangle numbers */ 8515, 19110, 42778, 96141, 216153, 486591, 1092981, 2458653, 5532801, 12442566, 27993903, 62983476, 141717030, 318844378, 717352503, 1614057336, 3631522476, 8170957530, 18384510628, 41364789378, 93070452520, 209408356380, 471168559170, 1060128894105, 2385289465695, 5366898840628, 12075518705635, 27169915244790, 61132312065111, 137547689707000, 309482283181501, 696335127828753, 1566753995631385, 3525196511162271, 7931691992677701, 17846306936293605, 40154 [...]
+ };
+ /* clang-format on */
+ static constexpr int32_t kBlockCapacity = 16;
+ static constexpr uint8_t kNumProbe = std::size(kNextProbeLocation);
+ static constexpr uint8_t kEmptySlot = uint8_t(0b11111111);
+ static constexpr uint8_t kProtectedSlot = uint8_t(0b11111110);
+ static constexpr uint64_t kMinSize = 7;
+ static constexpr uint8_t kNewHead = 0b00000000;
+ static constexpr uint8_t kNewTail = 0b10000000;
+ using KVPair = std::pair<TVMFFIAny, TVMFFIAny>;
+
+ struct Block {
+ /**
+ * Each block has a 8-byte key (TVMFFIAny), 8-byte value (TVMFFIAny), and a 1-byte metadata.
+ *
+ * Metadata can be one of the following three cases:
+ * - 1) Empty: 0xFF (0b11111111)_2. The slot is available and can be written in.
+ * - 2) Protected: 0xFE (0b11111110)_2. The slot is empty but not allowed to be written. It is
+ * only used during insertion when relocating certain elements.
+ * - 3) Normal: (0bXYYYYYYY)_2. The highest bit `X` indicates if it is the head of a linked
+ * list, where `0` means head, and `1` means not head. The next 7 bits `YYYYYYY` are used as
+ * the "next pointer" (i.e. pointer to the next element), where `kNextProbeLocation[YYYYYYY]`
+ * is the offset to the next element. And if `YYYYYYY == 0`, it means the end of the linked
+ * list.
+ */
+ uint8_t meta[kBlockCapacity];
+ KVPair data[kBlockCapacity];
+ };
+ static_assert(sizeof(Block) == kBlockCapacity * (1 + sizeof(TVMFFIAny) * 2), "ABI check");
+ static_assert(std::is_aggregate_v<Block>, "ABI check");
+ static_assert(std::is_trivially_destructible_v<Block>, "ABI check");
+ static_assert(std::is_standard_layout_v<Block>, "ABI check");
+ static_assert(kNumProbe == 126, "Check assumption");
+
+ struct ListIter {
+ static ListIter None() { return ListIter(0, nullptr); }
+ static ListIter FromIndex(const DictBase *self, uint64_t i) {
+ if (i >= self->Cap()) // TODO: remove this check
+ TVM_FFI_THROW(InternalError) << "Indexing " << i << " of length " << self->Cap();
+ return ListIter(i, self->Blocks() + (i / kBlockCapacity));
+ }
+ static ListIter FromHash(const DictBase *self, uint64_t h) {
+ return ListIter::FromIndex(self, (11400714819323198485ull * h) >>
+ (details::CountLeadingZeros(self->Cap()) + 1));
+ }
+ auto &Data() const { return cur->data[i % kBlockCapacity]; }
+ uint8_t &Meta() const { return cur->meta[i % kBlockCapacity]; }
+ uint64_t Offset() const { return kNextProbeLocation[Meta() & 0b01111111]; }
+ bool IsHead() const { return (Meta() & 0b10000000) == 0b00000000; }
+ void SetNext(uint8_t jump) const { (Meta() &= 0b10000000) |= jump; }
+ void Advance(const DictBase *self) { *this = WithOffset(self, Offset()); }
+ bool IsNone() const { return cur == nullptr; }
+ ListIter WithOffset(const DictBase *self, uint64_t offset) const {
+ return offset == 0 ? ListIter::None()
+ : ListIter::FromIndex(self, (i + offset) & (self->Cap() - 1));
+ }
+
+ uint64_t i;
+ Block *cur;
+
+ protected:
+ ListIter() = default;
+ explicit ListIter(uint64_t i, Block *cur) : i(i), cur(cur) {}
+ };
+
+ ListIter Head(uint64_t hash) const {
+ ListIter iter = ListIter::FromHash(this, hash);
+ return iter.IsHead() ? iter : ListIter::None();
+ }
+
+ ListIter Prev(ListIter iter) const {
+ ListIter prev = Head(Hash()(iter.Data().first));
+ ListIter next = prev;
+ for (next.Advance(this); next.i != iter.i; prev = next, next.Advance(this)) {
+ }
+ return prev;
+ }
+
+ uint64_t Cap() const { return static_cast<uint64_t>(this->_extra.capacity); }
+ uint64_t Size() const { return this->_extra.size; }
+ Block *Blocks() { return reinterpret_cast<Block *>(static_cast<DictBase *>(this) + 1); }
+ Block *Blocks() const { return const_cast<DictBase *>(this)->Blocks(); }
+
+ ListIter Probe(ListIter cur, uint8_t *result) const {
+ for (uint8_t i = 1; i < kNumProbe && kNextProbeLocation[i] < this->Size(); ++i) {
+ ListIter next = cur.WithOffset(this, kNextProbeLocation[i]);
+ if (next.Meta() == kEmptySlot) {
+ *result = i;
+ return next;
+ }
+ }
+ *result = 0;
+ return ListIter::None();
+ }
+
+ ListIter Lookup(const TVMFFIAny &key) const {
+ uint64_t hash = Hash()(key);
+ for (ListIter iter = Head(hash); !iter.IsNone(); iter.Advance(this)) {
+ if (Equal()(key, iter.Data().first)) {
+ return iter;
+ }
+ }
+ return ListIter::None();
+ }
+
+ template <typename Pred>
+ void IterateAll(Pred pred) {
+ Block *blocks_ = this->Blocks();
+ int64_t num_blocks = this->_extra.capacity / kBlockCapacity;
+ for (int64_t i = 0; i < num_blocks; ++i) {
+ for (int j = 0; j < kBlockCapacity; ++j) {
+ uint8_t &meta = blocks_[i].meta[j];
+ auto &data = blocks_[i].data[j];
+ if (meta != kEmptySlot && meta != kProtectedSlot) {
+ pred(&meta, &data.first, &data.second);
+ }
+ }
+ }
+ }
+
+ void Clear() {
+ this->IterateAll([](uint8_t *meta, TVMFFIAny *key, TVMFFIAny *value) {
+ static_cast<Any *>(key)->Reset();
+ static_cast<Any *>(value)->Reset();
+ *meta = kEmptySlot;
+ });
+ this->_extra.size = 0;
+ }
+
+ void Erase(int64_t index) {
+ ListIter iter = ListIter::FromIndex(this, index);
+ if (uint64_t offset = iter.Offset(); offset != 0) {
+ ListIter prev = iter;
+ ListIter next = iter.WithOffset(this, offset);
+ while ((offset = next.Offset()) != 0) {
+ prev = next;
+ next = next.WithOffset(this, offset);
+ }
+ iter.Data() = next.Data();
+ next.Meta() = kEmptySlot;
+ prev.SetNext(0);
+ } else {
+ if (!iter.IsHead()) {
+ Prev(iter).SetNext(0);
+ }
+ iter.Meta() = kEmptySlot;
+ auto &data = iter.Data();
+ static_cast<Any &>(data.first).Reset();
+ static_cast<Any &>(data.second).Reset();
+ }
+ this->_extra.size -= 1;
+ }
+
+public:
+ struct Iterator;
+ friend struct Iterator;
+ struct Iterator {
+ using iterator_category = std::bidirectional_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = std::pair<const Any, Any>;
+ using pointer = value_type *;
+ using reference = value_type &;
+ static_assert(sizeof(value_type) == sizeof(KVPair), "ABI check");
+ static_assert(offsetof(value_type, first) == offsetof(KVPair, first), "ABI check");
+ static_assert(offsetof(value_type, second) == offsetof(KVPair, second), "ABI check");
+
+ Iterator() = default;
+ Iterator(const Iterator &) = default;
+ Iterator(Iterator &&) = default;
+ Iterator &operator=(const Iterator &) = default;
+ Iterator &operator=(Iterator &&) = default;
+
+ Iterator &operator++() {
+ int64_t cap = self->Cap();
+ while (++index < cap) {
+ if (ListIter::FromIndex(self, index).Meta() != DictBase::kEmptySlot) {
+ return *this;
+ }
+ }
+ return *this;
+ }
+ Iterator &operator--() {
+ while (--index >= 0) {
+ if (ListIter::FromIndex(self, index).Meta() != DictBase::kEmptySlot) {
+ return *this;
+ }
+ }
+ return *this;
+ }
+ Iterator operator++(int) {
+ Iterator copy(*this);
+ this->operator++();
+ return copy;
+ }
+ Iterator operator--(int) {
+ Iterator copy(*this);
+ this->operator--();
+ return copy;
+ }
+ bool operator==(const Iterator &other) const { return index == other.index; }
+ bool operator!=(const Iterator &other) const { return index != other.index; }
+ reference operator*() const { return *(this->operator->()); }
+ pointer operator->() const {
+ return reinterpret_cast<pointer>(&ListIter::FromIndex(self, this->index).Data());
+ }
+
+ protected:
+ friend struct DictBase;
+ Iterator(int64_t index, DictBase *self) : index(index), self(self) {}
+
+ int64_t index;
+ DictBase *self;
+ };
+
+ KVPair *TryInsertOrLookup(TVMFFIAny *key) {
+ if (Cap() == Size() || Size() + 1 > Cap() * 0.99) {
+ return nullptr;
+ }
+ // `iter` starts from the head of the linked list
+ ListIter iter = ListIter::FromHash(this, Hash()(*key));
+ uint8_t new_meta = kNewHead;
+ // There are three cases over all:
+ // 1) available - `iter` points to an empty slot that we could directly write in;
+ // 2) hit - `iter` points to the head of the linked list which we want to iterate through
+ // 3) relocate - `iter` points to the body of a different linked list, and in this case, we
+ // will have to relocate the elements to make space for the new element
+ if (iter.Meta() == kEmptySlot) { // (Case 1) available
+ // Do nothing
+ } else if (iter.IsHead()) { // (Case 2) hit
+ // Point `iter` to the last element of the linked list
+ for (ListIter prev = iter;; prev = iter) {
+ if (Equal()(*key, iter.Data().first)) {
+ return &iter.Data();
+ }
+ iter.Advance(this);
+ if (iter.IsNone()) {
+ iter = prev;
+ break;
+ }
+ }
+ // Prob at the end of the linked list for the next empty slot
+ ListIter prev = iter;
+ uint8_t jump;
+ iter = Probe(iter, &jump);
+ if (jump == 0) {
+ return nullptr;
+ }
+ prev.SetNext(jump);
+ new_meta = kNewTail;
+ } else {
+ // (Case 3) relocate: Chop the list starting from `iter`, and move it to other locations.
+ ListIter next = iter, prev = Prev(iter);
+ // Loop invariant:
+ // - `next` points to the first element of the list we are going to relocate.
+ // - `prev` points to the last element of the list that we are relocating to.
+ for (uint8_t next_meta = kProtectedSlot; !next.IsNone(); next_meta = kEmptySlot) {
+ // Step 1. Prob for the next empty slot `new_next` after `prev`
+ uint8_t jump;
+ ListIter new_next = Probe(prev, &jump);
+ if (jump == 0) {
+ return nullptr;
+ }
+ // Step 2. Relocate `next` to `new_next`
+ new_next.Meta() = kNewTail;
+ new_next.Data() = {next.Data().first, next.Data().second};
+ std::swap(next_meta, next.Meta());
+ prev.SetNext(jump);
+ // Step 3. Update `prev` -> `new_next`, `next` -> `Advance(next)`
+ prev = new_next;
+ next = next.WithOffset(this, kNextProbeLocation[next_meta & 0b01111111]);
+ }
+ }
+ this->_extra.size += 1;
+ iter.Meta() = new_meta;
+ auto &data = iter.Data() = {*key, TVMFFIAny()};
+ key->type_index = 0;
+ key->v_int64 = 0;
+ return &data;
+ }
+
+ Any &at(const Any &key) {
+ ListIter iter = Lookup(key);
+ if (iter.IsNone()) {
+ TVM_FFI_THROW(KeyError) << "Key not found";
+ }
+ return static_cast<Any &>(iter.Data().second);
+ }
+
+ const Any &at(const Any &key) const {
+ ListIter iter = Lookup(key);
+ if (iter.IsNone()) {
+ TVM_FFI_THROW(KeyError) << "Key not found";
+ }
+ return static_cast<const Any &>(iter.Data().second);
+ }
+ Any &operator[](const Any &key) = delete; // May need rehashing
+ const Any &operator[](const Any &key) const { return this->at(key); }
+ int64_t size() const { return this->_extra.size; }
+ int64_t count(const Any &key) const { return Lookup(key).IsNone() ? 0 : 1; }
+ void clear() { this->Clear(); }
+ Iterator begin() { return Iterator(-1, this).operator++(); }
+ Iterator end() { return Iterator(this->Cap(), this); }
+ Iterator find(const Any &key) {
+ ListIter iter = Lookup(key);
+ return iter.IsNone() ? end() : Iterator(iter.i, this);
+ }
+ void erase(const Any &key) {
+ ListIter iter = Lookup(key);
+ if (!iter.IsNone()) {
+ Erase(iter.i);
+ } else {
+ TVM_FFI_THROW(KeyError) << "Key not found";
+ }
+ }
+};
+
+struct AnyHash {
+ uint64_t operator()(const TVMFFIAny &a) const {
+ if (a.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
+ return details::TVMFFIStrHash(reinterpret_cast<TVMFFIStr *>(a.v_obj));
+ }
+ union {
+ int64_t i64;
+ uint64_t u64;
+ } cvt;
+ cvt.i64 = a.v_int64;
+ return cvt.u64;
+ }
+};
+
+struct AnyEqual {
+ bool operator()(const TVMFFIAny &a, const TVMFFIAny &b) const {
+ if (a.type_index != b.type_index) {
+ return false;
+ }
+ if (a.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
+ return details::TVMFFIStrCmp(reinterpret_cast<TVMFFIStr *>(a.v_obj),
+ reinterpret_cast<TVMFFIStr *>(b.v_obj)) == 0;
+ }
+ return a.v_int64 == b.v_int64;
+ }
+};
+
+struct Dict : protected DictBase<AnyHash, AnyEqual> {
+ TVM_FFI_DEF_STATIC_TYPE(Dict, Object, TVMFFITypeIndex::kTVMFFIDict);
+ TVM_FFI_DEF_TYPE_FRIENDS();
+ struct Allocator;
+ friend struct Allocator;
+
+protected:
+ explicit Dict(int64_t capacity) {
+ this->_extra.capacity = capacity;
+ this->_extra.size = 0;
+ for (int64_t i = 0; i < capacity / kBlockCapacity; ++i) {
+ Block &block = Blocks()[i];
+ std::memset(block.meta, kEmptySlot, sizeof(block.meta));
+ }
+ }
+ ~Dict() { this->Clear(); }
+};
+
+struct Dict::Allocator {
+ static TVM_FFI_INLINE Dict *New(int64_t capacity) {
+ if ((capacity & (capacity - 1)) != 0 || capacity % kBlockCapacity != 0) {
+ TVM_FFI_THROW(InternalError) << "capacity: " << capacity;
+ }
+ int64_t num_blocks = capacity / Dict::kBlockCapacity;
+ return DefaultObjectAllocator<Dict>::NewWithPad<Dict::Block>(num_blocks, capacity);
+ }
+ static TVM_FFI_INLINE Dict *New() { return New(0); }
+};
+
+template <>
+struct Ref<Dict> : public RefBase<Dict> {
+ using TSelf = Ref<Dict>;
+ using TBase = RefBase<Dict>;
+ using Iterator = Dict::Iterator;
+
+ TVM_FFI_REF_DEF_DELEGATE_CONSTRUCTORS(Ref<Dict>, RefBase<Dict>)
+ TVM_FFI_DEF_TYPE_FRIENDS();
+
+ Ref() : TBase(Dict::Allocator::New()) {}
+ template <typename Iter>
+ Ref(Iter begin, Iter end) : TBase(FromRange(begin, end)) {}
+ Ref(std::initializer_list<std::pair<Any, Any>> init) : TSelf(init.begin(), init.end()) {}
+ template <typename K, typename V, typename Hash, typename Equal>
+ Ref(const std::unordered_map<K, V, Hash, Equal> &source) : TSelf(source.begin(), source.end()) {}
+ const Any &at(const Any &key) const { return get()->at(key); }
+ Any &at(const Any &key) { return get()->at(key); }
+ const Any &operator[](const Any &key) const { return get()->at(key); }
+ Any &operator[](Any key) { return static_cast<Any &>(InsertOrLookup(std::move(key))->second); }
+ int64_t size() const { return get()->size(); }
+ int64_t count(const Any &key) const { return get()->count(key); }
+ bool empty() const { return size() == 0; }
+ void clear() { get()->clear(); }
+ Iterator begin() { return get()->begin(); }
+ Iterator end() { return get()->end(); }
+ Iterator find(const Any &key) { return get()->find(key); }
+ void erase(const Any &key) { get()->erase(key); }
+
+protected:
+ using TBase::operator*;
+ using TBase::operator->;
+
+ Dict::KVPair *InsertOrLookup(Any key) {
+ for (;;) {
+ if (Dict::KVPair *ret = get()->TryInsertOrLookup(&key)) {
+ return ret;
+ }
+ Ref<Dict>::FromRange(begin(), end(), get()->Cap() * 2).Swap(*this);
+ }
+ TVM_FFI_UNREACHABLE();
+ }
+
+ template <typename Iter>
+ static Ref<Dict> FromRange(Iter begin, Iter end) {
+ int64_t num = std::distance(begin, end);
+ int64_t capacity = details::BitCeil(num * 2);
+ capacity = std::max<int64_t>(
+ (capacity + Dict::kBlockCapacity - 1) & ~(Dict::kBlockCapacity - 1), Dict::kBlockCapacity);
+ Ref<Dict> dict(Dict::Allocator::New(capacity));
+ for (; begin != end; ++begin) {
+ Dict::KVPair *ret = dict.InsertOrLookup(Any(begin->first));
+ static_cast<Any &>(ret->second) = Any(begin->second);
+ }
+ return dict;
+ }
+
+ template <typename Iter>
+ static Ref<Dict> FromRange(Iter begin, Iter end, int64_t capacity) {
+ capacity = std::max<int64_t>(
+ (capacity + Dict::kBlockCapacity - 1) & ~(Dict::kBlockCapacity - 1), Dict::kBlockCapacity);
+ Ref<Dict> dict(Dict::Allocator::New(capacity));
+ for (; begin != end; ++begin) {
+ Dict::KVPair *ret = dict.InsertOrLookup(Any(begin->first));
+ static_cast<Any &>(ret->second) = Any(begin->second);
+ }
+ return dict;
+ }
+};
+
+namespace details {
+
+TVM_FFI_INLINE int32_t CountLeadingZeros(uint64_t x) {
+#if __cplusplus >= 202002L
+ return std::countl_zero(x);
+#elif defined(_MSC_VER)
+ DWORD leading_zero = 0;
+ if (_BitScanReverse64(&leading_zero, value)) {
+ return static_cast<int32_t>(63 - leading_zero);
+ } else {
+ return 64;
+ }
+#else
+ return x == 0 ? 64 : __builtin_clzll(x);
+#endif
+}
+
+TVM_FFI_INLINE uint64_t BitCeil(uint64_t x) {
+#if __cplusplus >= 202002L
+ return std::bit_ceil(x);
+#else
+ return x <= 1 ? 1 : static_cast<uint64_t>(1) << (64 - CountLeadingZeros(x - 1));
+#endif
+}
+
+} // namespace details
+} // namespace ffi
+} // namespace tvm
+#endif // TVM_FFI_DICT_H_
diff --git a/include/tvm/ffi/ext/error.h b/include/tvm/ffi/ext/error.h
new file mode 100644
index 0000000000..5d8ec47582
--- /dev/null
+++ b/include/tvm/ffi/ext/error.h
@@ -0,0 +1,89 @@
+#ifndef TVM_FFI_ERROR_H_
+#define TVM_FFI_ERROR_H_
+
+#include <string>
+#include <tvm/ffi/core/core.h>
+#include <vector>
+
+namespace tvm {
+namespace ffi {
+namespace details {
+struct _ErrorHeader {
+ const char *kind;
+ int32_t num_frames;
+ const char **linenos;
+ const char *message;
+};
+using ErrorHeader = AnyWithExtra<_ErrorHeader>;
+static_assert(sizeof(ErrorHeader) == sizeof(TVMFFIError));
+static_assert(offsetof(ErrorHeader, _extra.kind) == offsetof(TVMFFIError, kind));
+static_assert(offsetof(ErrorHeader, _extra.num_frames) == offsetof(TVMFFIError, num_frames));
+static_assert(offsetof(ErrorHeader, _extra.linenos) == offsetof(TVMFFIError, linenos));
+static_assert(offsetof(ErrorHeader, _extra.message) == offsetof(TVMFFIError, message));
+} // namespace details
+
+struct Error : private details::ErrorHeader {
+ TVM_FFI_DEF_STATIC_TYPE(Error, Object, TVMFFITypeIndex::kTVMFFIError);
+
+ std::string kind;
+ std::vector<std::string> linenos;
+ std::string message;
+ std::vector<const char *> lineno_holder;
+
+ Error(std::string kind, std::string lineno, std::string message)
+ : kind(kind), linenos({lineno}), message(message), lineno_holder() {
+ this->_extra.kind = this->kind.data();
+ this->_extra.num_frames = static_cast<int32_t>(this->linenos.size());
+ this->_extra.message = this->message.data();
+ this->RefreshLinenoHolder();
+ }
+
+ void Append(std::string lineno) {
+ linenos.push_back(lineno);
+ this->RefreshLinenoHolder();
+ }
+
+protected:
+ void RefreshLinenoHolder() {
+ this->lineno_holder.resize(linenos.size());
+ for (size_t i = 0; i < linenos.size(); ++i) {
+ this->lineno_holder[i] = linenos[i].data();
+ }
+ this->_extra.linenos = this->lineno_holder.data();
+ }
+};
+
+struct TVMError : public std::exception {
+ Ref<Error> data_;
+
+ TVMError(Ref<Error> data) : data_(data) {}
+ TVMError(const TVMError &other) : data_(other.data_) {}
+ TVMError(TVMError &&other) : data_(std::move(other.data_)) {}
+ TVMError &operator=(const TVMError &) = delete;
+ TVMError &operator=(TVMError &&) = delete;
+
+ const char *what() const noexcept(true) override {
+ if (data_.get() == nullptr) {
+ return "TVMError: Unspecified";
+ }
+ return data_->message.c_str();
+ }
+
+ void MoveToAny(Any *v) {
+ *v = data_;
+ data_ = Ref<Error>(nullptr);
+ }
+};
+
+namespace details {
+[[noreturn]] TVM_FFI_INLINE void TVMErrorFromBuilder(std::string &&kind, std::string &&lineno,
+ std::string &&message) noexcept(false) {
+ Ref<Error> ret = Ref<Error>::New(std::move(kind), std::move(lineno), std::move(message));
+ throw TVMError(ret);
+}
+} // namespace details
+
+} // namespace ffi
+} // namespace tvm
+
+#endif // TVM_FFI_ERROR_H_
diff --git a/include/tvm/ffi/ext/func.h b/include/tvm/ffi/ext/func.h
new file mode 100644
index 0000000000..96dea9a259
--- /dev/null
+++ b/include/tvm/ffi/ext/func.h
@@ -0,0 +1,390 @@
+#ifndef TVM_FFI_FUNC_H_
+#define TVM_FFI_FUNC_H_
+
+#include "./error.h"
+#include "./str.h"
+#include <functional>
+#include <sstream>
+#include <tvm/ffi/core/core.h>
+#include <type_traits>
+
+namespace tvm {
+namespace ffi {
+namespace details {
+struct FuncInfo {
+ using Call = void(const Func *, int32_t, const AnyView *, Any *);
+ using SafeCall = int32_t(const Func *, int32_t, const AnyView *, Any *);
+ Call *call;
+ SafeCall *safe_call;
+ static TVM_FFI_SYMBOL_HIDE int32_t SafeCallImpl(const Func *, int32_t, const AnyView *, Any *);
+};
+} // namespace details
+
+struct Func : private AnyWithExtra<details::FuncInfo> {
+ friend struct details::FuncInfo;
+ using Header = AnyWithExtra<details::FuncInfo>;
+ struct Allocator;
+ TVM_FFI_DEF_STATIC_TYPE(Func, Object, TVMFFITypeIndex::kTVMFFIFunc);
+
+ template <typename... Args>
+ Any operator()(Args &&...args) const;
+
+protected:
+ TVM_FFI_INLINE Func(details::FuncInfo::Call *f) : AnyWithExtra<details::FuncInfo>() {
+ this->_extra.call = f;
+ this->_extra.safe_call = details::FuncInfo::SafeCallImpl;
+ }
+};
+static_assert(sizeof(Func) == sizeof(TVMFFIFunc));
+static_assert(offsetof(Func::Header, _extra.call) == offsetof(TVMFFIFunc, call));
+static_assert(offsetof(Func::Header, _extra.safe_call) == offsetof(TVMFFIFunc, safe_call));
+
+template <>
+struct Ref<Func> : public RefBase<Func> {
+ TVM_FFI_DEF_TYPE_FRIENDS();
+ TVM_FFI_INLINE Ref() : RefBase<Func>() {}
+ TVM_FFI_REF_DEF_DELEGATE_CONSTRUCTORS(Ref<Func>, RefBase<Func>)
+
+ template <typename... Args>
+ TVM_FFI_INLINE auto operator()(Args &&...args) const {
+ return (*get())(std::forward<Args>(args)...);
+ }
+ template <typename... Args>
+ TVM_FFI_INLINE auto operator()(Args &&...args) {
+ return (*get())(std::forward<Args>(args)...);
+ }
+};
+
+template <std::size_t N>
+struct AnyViewArray {
+ template <typename... Args>
+ void Fill(Args &&...args);
+ AnyView v[N];
+};
+
+template <>
+struct AnyViewArray<0> {
+ template <typename... Args>
+ void Fill(Args &&...args);
+ AnyView *v = nullptr;
+};
+
+template <typename... Args>
+TVM_FFI_INLINE Any Func::operator()(Args &&...args) const {
+ constexpr size_t N = sizeof...(Args);
+ AnyViewArray<N> stack_args;
+ stack_args.Fill(std::forward<Args>(args)...);
+ return ::tvm::ffi::details::FuncInvoke(this, N, stack_args.v);
+}
+
+namespace details {
+
+template <typename, typename>
+struct UnpackCall;
+template <typename T>
+struct FuncFunctor;
+
+TVM_FFI_INLINE int32_t FuncInfo::SafeCallImpl(const Func *self, int32_t num_args,
+ const AnyView *args, Any *ret) {
+ try {
+ self->_extra.call(self, num_args, args, ret);
+ return 0;
+ } catch (TVMError &err) {
+ err.MoveToAny(ret);
+ return 1;
+ } catch (std::runtime_error &err) {
+ *ret = Ref<Str>::New(err.what());
+ return 1;
+ }
+ TVM_FFI_UNREACHABLE();
+}
+
+TVM_FFI_INLINE Any FuncInvoke(const void *self, int32_t num_args, const TVMFFIAny *args) {
+ const TVMFFIFunc *func = static_cast<const TVMFFIFunc *>(self);
+ FuncInfo::SafeCall *local_safe_call = FuncInfo::SafeCallImpl;
+ Any ret;
+ if (reinterpret_cast<void *>(func->safe_call) == reinterpret_cast<void *>(local_safe_call)) {
+ func->call(func, num_args, args, &ret);
+ return ret;
+ }
+ int32_t err_code = func->safe_call(func, num_args, args, &ret);
+ if (err_code == 0) {
+ return ret;
+ }
+ if (ret.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIError)) {
+ Ref<Error> err = Ref<Error>(std::move(ret));
+ throw TVMError(err);
+ }
+ TVM_FFI_THROW(InternalError) << "Error code: " << err_code;
+ TVM_FFI_UNREACHABLE();
+}
+
+template <typename FuncType>
+struct FuncImpl : public Func {
+ using TSelf = FuncImpl<FuncType>;
+ using Allocator = DefaultObjectAllocator<TSelf>;
+
+ TVM_FFI_INLINE static void CallPacked(const Func *obj, int32_t num_args, const AnyView *args,
+ Any *ret) {
+ static_cast<const TSelf *>(obj)->func_(num_args, args, ret);
+ }
+
+ TVM_FFI_INLINE static void CallUnpacked(const Func *obj, int32_t num_args, const AnyView *args,
+ Any *ret) {
+ using ArgType = typename FuncFunctor<FuncType>::ArgType;
+ constexpr int32_t N = std::tuple_size_v<ArgType>;
+ if (num_args != N) {
+ TVM_FFI_THROW(TypeError) << "Mismatched number of arguments when calling: `"
+ << FuncFunctor<FuncType>::Sig() << "`. Expected " << N << " but got "
+ << num_args << " arguments";
+ }
+ using IdxSeq = std::make_index_sequence<N>;
+ using RetType = typename FuncFunctor<FuncType>::RetType;
+ UnpackCall<RetType, ArgType>::template Run<FuncType>(&static_cast<const TSelf *>(obj)->func_,
+ args, ret, IdxSeq{});
+ }
+
+ TVM_FFI_INLINE explicit FuncImpl(FuncType func, details::FuncInfo::Call *f)
+ : Func(f), func_(std::forward<FuncType>(func)) {}
+
+ TVM_FFI_INLINE static Func *FromPacked(FuncType func) {
+ return Allocator::New(std::forward<FuncType>(func), TSelf::CallPacked);
+ }
+
+ TVM_FFI_INLINE static Func *FromUnpacked(FuncType func) {
+ return Allocator::New(std::forward<FuncType>(func), TSelf::CallUnpacked);
+ }
+
+ mutable std::decay_t<FuncType> func_;
+};
+
+template <typename Type>
+struct Type2Str {
+ [[maybe_unused]] static inline std::string type_str = TypeTraitsNoCR<Type>::Type2Str();
+};
+template <>
+struct Type2Str<Any> {
+ static constexpr const char *type_str = "Any";
+};
+template <>
+struct Type2Str<AnyView> {
+ static constexpr const char *type_str = "AnyView";
+};
+template <>
+struct Type2Str<void> {
+ static constexpr const char *type_str = "void";
+};
+
+template <typename ArgType>
+struct Arg2Str {
+ template <size_t i>
+ static TVM_FFI_INLINE void Apply(std::ostream &os) {
+ using Arg = std::tuple_element_t<i, ArgType>;
+ if constexpr (i != 0) {
+ os << ", ";
+ }
+ os << i << ": " << Type2Str<Arg>::type_str;
+ }
+ template <size_t... I>
+ static TVM_FFI_INLINE void Run(std::ostream &os, std::index_sequence<I...>) {
+ using TExpander = int[];
+ (void)TExpander{0, (Apply<I>(os), 0)...};
+ }
+};
+
+template <typename T>
+static constexpr bool ArgSupported = IsAnyOrView<RemoveCR<T>> || TypeTraitsNoCR<T>::enabled;
+template <typename T>
+static constexpr bool RetSupported =
+ IsAnyOrView<RemoveCR<T>> || std::is_void_v<T> || TypeTraitsNoCR<T>::enabled;
+
+template <typename R, typename... Args>
+struct FuncFunctorImpl {
+ using FType = R(Args...);
+ using ArgType = std::tuple<Args...>;
+ using RetType = R;
+ static constexpr bool packed =
+ std::is_convertible_v<FType, std::function<void(int32_t, const AnyView *, Any *)>>;
+ static constexpr bool unpacked = (ArgSupported<Args> && ...) && (RetSupported<R>);
+ static TVM_FFI_INLINE std::string Sig() {
+ using IdxSeq = std::make_index_sequence<sizeof...(Args)>;
+ std::ostringstream ss;
+ ss << "(";
+ Arg2Str<std::tuple<Args...>>::Run(ss, IdxSeq{});
+ ss << ") -> " << Type2Str<R>::type_str;
+ return ss.str();
+ }
+};
+
+template <typename R, typename... Args>
+struct FuncFunctor<R(Args...)> : FuncFunctorImpl<R, Args...> {};
+template <typename R, typename... Args>
+struct FuncFunctor<R (*)(Args...)> : FuncFunctorImpl<R, Args...> {};
+
+template <int32_t i, typename>
+struct PrependIntegerSeq;
+template <int32_t i, int32_t... Is>
+struct PrependIntegerSeq<i, std::integer_sequence<int32_t, Is...>> {
+ using type = std::integer_sequence<int32_t, i, Is...>;
+};
+template <int32_t I, typename>
+struct IndexIntSeq;
+template <int32_t I, int32_t Head, int32_t... Tail>
+struct IndexIntSeq<I, std::integer_sequence<int32_t, Head, Tail...>>
+ : IndexIntSeq<I - 1, std::integer_sequence<int32_t, Tail...>> {};
+template <int32_t Head, int32_t... Tail>
+struct IndexIntSeq<0, std::integer_sequence<int32_t, Head, Tail...>> {
+ static constexpr int32_t value = Head;
+};
+
+template <typename Function, typename StorageInfo>
+struct UnpackCallArgConverter {
+ template <typename _Type, size_t i>
+ struct AsType {
+ inline static auto Run(const AnyView &v, Any *storage) {
+ using Type = std::decay_t<_Type>;
+ constexpr int32_t storage_index = IndexIntSeq<i, typename StorageInfo::sum>::value;
+ try {
+ if constexpr (storage_index == -1) {
+ return v.operator Type();
+ } else {
+ return v.CastWithStorage<Type>(storage + storage_index);
+ }
+ } catch (const TVMError &) {
+ TVM_FFI_THROW(TypeError) << "Mismatched type on argument #" << i << " when calling: `"
+ << FuncFunctor<Function>::Sig() << "`. Expected `"
+ << Type2Str<Type>::type_str << "` but got `"
+ << TypeIndex2TypeKey(v.type_index) << "`";
+ }
+ TVM_FFI_UNREACHABLE();
+ }
+ };
+
+ template <size_t i>
+ struct AsType<AnyView, i> {
+ TVM_FFI_INLINE static AnyView Run(const AnyView &v, Any *) { return v; }
+ };
+
+ template <size_t i>
+ struct AsType<Any, i> {
+ TVM_FFI_INLINE static Any Run(const AnyView &v, Any *) { return v; }
+ };
+};
+
+template <typename T, typename = void>
+struct RequiresFFIStorage {
+ static constexpr int32_t value = 0;
+};
+template <typename T>
+struct RequiresFFIStorage<
+ T, std::void_t<decltype(TypeTraitsNoCR<T>::CopyFromTVMFFIAnyToTypeWithStorage)>> {
+ static constexpr int32_t value = 1;
+};
+
+template <typename... Args>
+struct FFIStorageInfo;
+template <>
+struct FFIStorageInfo<> {
+ constexpr static int32_t total = 0;
+ using sum = std::integer_sequence<int32_t>;
+};
+template <typename I, typename... Is>
+struct FFIStorageInfo<I, Is...> {
+private:
+ using Prev = FFIStorageInfo<Is...>;
+ constexpr static int32_t need_ffi_storage = RequiresFFIStorage<I>::value;
+
+public:
+ constexpr static int32_t total = Prev::total + need_ffi_storage;
+ using sum =
+ typename PrependIntegerSeq<(need_ffi_storage ? total : 0) - 1, typename Prev::sum>::type;
+};
+
+template <typename RetType, typename... Args>
+struct UnpackCall<RetType, std::tuple<Args...>> {
+ template <typename Function, typename FuncType, size_t... I>
+ TVM_FFI_INLINE static void Run(FuncType *func, const AnyView *args, Any *ret,
+ std::index_sequence<I...>) {
+ using Storage = FFIStorageInfo<Args...>;
+ using CVT = UnpackCallArgConverter<Function, Storage>;
+ if constexpr (Storage::total > 0 && std::is_void_v<RetType>) {
+ Any storage[Storage::total];
+ ret->Reset();
+ (*func)(CVT::template AsType<Args, I>::Run(args[I], storage)...);
+ } else if constexpr (Storage::total > 0 && !std::is_void_v<RetType>) {
+ Any storage[Storage::total];
+ *ret = (*func)(CVT::template AsType<Args, I>::Run(args[I], storage)...);
+ } else if constexpr (Storage::total == 0 && std::is_void_v<RetType>) {
+ ret->Reset();
+ (*func)(CVT::template AsType<Args, I>::Run(args[I], nullptr)...);
+ } else if constexpr (Storage::total == 0 && !std::is_void_v<RetType>) {
+ *ret = (*func)(CVT::template AsType<Args, I>::Run(args[I], nullptr)...);
+ }
+ }
+};
+
+template <typename... Args>
+struct AnyViewArrayFill {
+ template <std::size_t i, typename Arg>
+ TVM_FFI_INLINE static void Apply(AnyView *v, Arg &&arg) {
+ v[i] = AnyView(std::forward<Arg>(arg));
+ }
+ template <std::size_t... I>
+ TVM_FFI_INLINE static void Run(AnyView *v, Args &&...args, std::index_sequence<I...>) {
+ using TExpander = int[];
+ (void)TExpander{0, (Apply<I>(v, std::forward<Args>(args)), 0)...};
+ }
+};
+
+template <typename... Args>
+TVM_FFI_INLINE void FillAnyView(AnyView *v, Args &&...args) {
+ constexpr std::size_t N = sizeof...(args);
+ if constexpr (N > 0) {
+ using IdxSeq = std::make_index_sequence<N>;
+ ::tvm::ffi::details::AnyViewArrayFill<Args...>::Run(v, std::forward<Args>(args)..., IdxSeq{});
+ }
+}
+
+} // namespace details
+
+struct Func::Allocator {
+public:
+ template <typename FuncType, typename = void>
+ struct Impl {};
+
+ template <typename FuncType>
+ struct Impl<FuncType, std::enable_if_t<details::FuncFunctor<FuncType>::packed>> {
+ TVM_FFI_INLINE static Func *Run(FuncType func) {
+ return details::FuncImpl<FuncType>::FromPacked(std::forward<FuncType>(func));
+ }
+ };
+
+ template <typename FuncType>
+ struct Impl<FuncType, std::enable_if_t<details::FuncFunctor<FuncType>::unpacked>> {
+ TVM_FFI_INLINE static Func *Run(FuncType func) {
+ return details::FuncImpl<FuncType>::FromUnpacked(std::forward<FuncType>(func));
+ }
+ };
+
+ template <typename FuncType>
+ TVM_FFI_INLINE static Func *New(FuncType func) {
+ return Impl<FuncType>::Run(std::forward<FuncType>(func));
+ }
+};
+
+template <std::size_t N>
+template <typename... Args>
+TVM_FFI_INLINE void AnyViewArray<N>::Fill(Args &&...args) {
+ static_assert(sizeof...(args) == N, "Invalid number of arguments");
+ ::tvm::ffi::details::FillAnyView(v, std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+TVM_FFI_INLINE void AnyViewArray<0>::Fill(Args &&...args) {
+ static_assert(sizeof...(args) == 0, "Invalid number of arguments");
+}
+
+} // namespace ffi
+} // namespace tvm
+
+#endif // TVM_FFI_FUNC_H_
diff --git a/include/tvm/ffi/ext/list.h b/include/tvm/ffi/ext/list.h
new file mode 100644
index 0000000000..cc6daff35c
--- /dev/null
+++ b/include/tvm/ffi/ext/list.h
@@ -0,0 +1,457 @@
+#ifndef TVM_FFI_LIST_H_
+#define TVM_FFI_LIST_H_
+#include <cstddef>
+#include <cstring>
+#include <initializer_list>
+#include <iterator>
+#include <tvm/ffi/core/core.h>
+#include <vector>
+
+namespace tvm {
+namespace ffi {
+namespace details {
+struct _ListHeaeder {
+ int64_t list_capacity; // sizeof(TVMFFIAny*) per element
+ int64_t list_length;
+ int64_t pool_capacity; // sizeof(TVMFFIAny) per element
+ int64_t pool_length;
+};
+using ListHeader = AnyWithExtra<_ListHeaeder>;
+static_assert(sizeof(ListHeader) == sizeof(TVMFFIList));
+static_assert(offsetof(ListHeader, _extra.list_capacity) == offsetof(TVMFFIList, list_capacity));
+static_assert(offsetof(ListHeader, _extra.list_length) == offsetof(TVMFFIList, list_length));
+static_assert(offsetof(ListHeader, _extra.pool_capacity) == offsetof(TVMFFIList, pool_capacity));
+static_assert(offsetof(ListHeader, _extra.pool_length) == offsetof(TVMFFIList, pool_length));
+void ListRangeCheck(int64_t begin, int64_t end, int64_t length);
+} // namespace details
+
+#define TVM_FFI_LIST_EXEC_IF_POD(ListCls, TypeIndex, Exec) \
+ if constexpr (ListCls::all_items_are_pod) { \
+ Exec; \
+ } else if constexpr (!ListCls::all_items_are_obj) { \
+ if (details::IsTypeIndexPOD(TypeIndex)) { \
+ Exec; \
+ } \
+ }
+
+template <typename ListClass>
+struct ListMethods {
+ using ListItem = TVMFFIAny *;
+ using PoolItem = TVMFFIAny;
+
+private:
+ // Section 1: Iterator and utility classes
+
+ struct Iterator {
+ using iterator_category = std::random_access_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = AnyView;
+ using pointer = AnyView;
+ using reference = AnyView;
+
+ Iterator() = default;
+ Iterator(const Iterator &) = default;
+ Iterator(Iterator &&) = default;
+ Iterator &operator=(const Iterator &) = default;
+ Iterator &operator=(Iterator &&) = default;
+
+ Iterator &operator++() {
+ ++index;
+ return *this;
+ }
+ Iterator &operator--() {
+ --index;
+ return *this;
+ }
+ Iterator operator++(int) {
+ Iterator ret = *this;
+ ++index;
+ return ret;
+ }
+ Iterator operator--(int) {
+ Iterator ret = *this;
+ --index;
+ return ret;
+ }
+ Iterator operator+(difference_type n) const {
+ Iterator ret = *this;
+ ret.index += n;
+ return ret;
+ }
+ Iterator operator-(difference_type n) const {
+ Iterator ret = *this;
+ ret.index -= n;
+ return ret;
+ }
+ Iterator &operator+=(difference_type n) {
+ index += n;
+ return *this;
+ }
+ Iterator &operator-=(difference_type n) {
+ index -= n;
+ return *this;
+ }
+ AnyView operator[](difference_type n) const { return self->GetItem(index + n); }
+ difference_type operator-(const Iterator &other) const { return index - other.index; }
+ bool operator==(const Iterator &other) const { return index == other.index; }
+ bool operator!=(const Iterator &other) const { return index != other.index; }
+ reference operator*() const { return self->GetItem(index); }
+ pointer operator->() const = delete;
+
+ protected:
+ friend struct ListMethods;
+ Iterator(ListClass *self, int64_t index) : self(self), index(index) {}
+ ListClass *self;
+ int64_t index;
+ };
+
+ struct ReverseIterator : public std::reverse_iterator<Iterator> {
+ using std::reverse_iterator<Iterator>::reverse_iterator;
+ };
+
+ TVM_FFI_INLINE auto *Get() { return &(static_cast<ListClass *>(this)->_extra); }
+ TVM_FFI_INLINE auto *Get() const { return &(static_cast<const ListClass *>(this)->_extra); }
+ TVM_FFI_INLINE auto ListBegin() const {
+ return reinterpret_cast<const ListItem *>(static_cast<const ListClass *>(this) + 1);
+ }
+ TVM_FFI_INLINE auto PoolBegin() const {
+ return reinterpret_cast<const PoolItem *>(ListBegin() + Get()->list_capacity);
+ }
+ TVM_FFI_INLINE auto ListBegin() {
+ return reinterpret_cast<ListItem *>(static_cast<ListClass *>(this) + 1);
+ }
+ TVM_FFI_INLINE auto PoolBegin() {
+ return reinterpret_cast<PoolItem *>(ListBegin() + Get()->list_capacity);
+ }
+
+public:
+ // Section 2: Interfaces - exposed to Ref<ListClass>
+ void inplace_init_many(int64_t numel, Any *first) {
+ this->ReplaceRangeInplace(0, 0, numel, first);
+ }
+ void inplace_push_back(Any value) {
+ this->ReplaceRangeInplace(Get()->list_length, Get()->list_length, 1, &value);
+ }
+ void inplace_insert(int64_t index, Any value) {
+ this->ReplaceRangeInplace(index, index, 1, &value);
+ }
+ void inplace_insert_many(int64_t index, int64_t numel, Any *first) {
+ this->ReplaceRangeInplace(index, index, numel, first);
+ }
+ void inplace_move_from(ListClass *other) {
+ // Exception safety is guaranteed even if there is an exception thrown during
+ // this process, because neither `list_length` is modified.
+ int64_t &new_pool_len = Get()->pool_length;
+ int64_t &new_list_len = Get()->list_length;
+ ListItem *new_list_base = this->ListBegin();
+ PoolItem *new_pool_base = this->PoolBegin();
+ ListItem *old_list_base = other->ListBegin();
+ for (int64_t i = 0; i < other->_extra.list_length; ++i) {
+ ListItem &e = new_list_base[i] = old_list_base[i];
+ if (e) {
+ TVM_FFI_LIST_EXEC_IF_POD(ListClass, e->type_index, {
+ // Copy POD data to the new POD pool
+ e = &(new_pool_base[new_pool_len++] = *e);
+ });
+ }
+ }
+ // Critical section: no exception be thrown here. Otherwise ref counting would be wrong.
+ { std::swap(other->_extra.list_length, new_list_len); }
+ }
+ void inplace_resize(int64_t new_size) {
+ int64_t &list_length = Get()->list_length;
+ if (new_size < list_length) {
+ this->RangeDecRef(new_size, list_length);
+ } else {
+ ListItem *b = this->ListBegin();
+ for (int64_t i = list_length; i < new_size; ++i) {
+ b[i] = nullptr;
+ }
+ }
+ list_length = new_size;
+ }
+ void clear() {
+ this->RangeDecRef(0, Get()->list_length);
+ Get()->list_length = Get()->pool_length = 0;
+ }
+ void pop_back() {
+ int64_t n = Get()->list_length;
+ this->ReplaceRangeInplace(n - 1, n, 0, nullptr);
+ }
+ int64_t size() const { return Get()->list_length; }
+ int64_t capacity() const { return Get()->list_capacity; }
+ bool empty() const { return Get()->list_length == 0; }
+ void erase(int64_t index) { this->ReplaceRangeInplace(index, index + 1, 0, nullptr); }
+ void SetItem(int64_t i, Any data) { this->ReplaceRangeInplace(i, i + 1, 1, &data); }
+ AnyView GetItem(int64_t index) const {
+ details::ListRangeCheck(index, index + 1, Get()->list_length);
+ const AnyView *data = static_cast<const AnyView *>(this->ListBegin()[index]);
+ if (data == nullptr) {
+ return AnyView();
+ }
+ TVM_FFI_LIST_EXEC_IF_POD(ListClass, data->type_index, { return *data; });
+ AnyView ret;
+ ret.type_index = data->type_index;
+ ret.v_obj = const_cast<AnyView *>(data);
+ return ret;
+ }
+ Iterator begin() { return Iterator(static_cast<ListClass *>(this), 0); }
+ Iterator end() { return Iterator(static_cast<ListClass *>(this), Get()->list_length); }
+ ReverseIterator rbegin() { return ReverseIterator(end()); }
+ ReverseIterator rend() { return ReverseIterator(begin()); }
+
+public:
+ // Section 3: Core methods
+ void RangeDecRef(int64_t begin, int64_t end) {
+ if constexpr (ListClass::all_items_are_pod) {
+ return;
+ }
+ ListItem *b = this->ListBegin();
+ for (int64_t i = begin; i < end; ++i) {
+ ListItem &data = b[i];
+ if (data) {
+ TVM_FFI_LIST_EXEC_IF_POD(ListClass, data->type_index, { continue; });
+ details::DecRef(data);
+ }
+ }
+ }
+ void ReplaceRangeInplace(int64_t range_begin, int64_t range_end, int64_t numel, Any *first) {
+ int64_t &list_len = Get()->list_length;
+ int64_t &pool_len = Get()->pool_length;
+ int64_t delta = numel - (range_end - range_begin);
+ details::ListRangeCheck(range_begin, range_end, list_len);
+ RangeDecRef(range_begin, range_end);
+ ListItem *b = this->ListBegin();
+ // Step 1. Move [range_end, list_len) to [range_end + delta, list_len + delta)
+ // so there are exaclty `num_elems` free slots in [range_begin, range_end + delta)
+ std::memmove(b + range_end + delta, b + range_end, (list_len - range_end) * sizeof(ListItem));
+ list_len += delta;
+ // Step 2. Copy `num_elems` items to [range_begin, range_end + delta)
+ PoolItem *p = this->PoolBegin();
+ for (int64_t i = 0; i < numel; ++i) {
+ Any v = std::move(first[i]);
+ ListItem &d = b[range_begin + i] = nullptr;
+ if (details::IsTypeIndexNone(v.type_index)) {
+ continue;
+ }
+ TVM_FFI_LIST_EXEC_IF_POD(ListClass, v.type_index, {
+ d = &(p[pool_len++] = v);
+ continue;
+ });
+ // N.B. Transfer the ownership of `v` to the list
+ std::swap(d, v.v_obj);
+ v.type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone);
+ }
+ }
+ Ref<ListClass> MaybeMoveToMoreCapacity(int64_t list_len_delta, int64_t pool_len_delta) {
+ ListClass *self = static_cast<ListClass *>(this);
+ int64_t list_cap = self->_extra.list_capacity, pool_cap = self->_extra.pool_capacity;
+ int64_t new_list_cap = GrowCapacity(list_cap, self->_extra.list_length + list_len_delta);
+ int64_t new_pool_cap = GrowCapacity(pool_cap, self->_extra.pool_length + pool_len_delta);
+ if (new_list_cap > list_cap || new_pool_cap > pool_cap) {
+ Ref<ListClass> ret = ListClass::Allocator::New(new_list_cap, new_pool_cap);
+ ret->inplace_move_from(self);
+ return ret;
+ }
+ return Ref<ListClass>(self);
+ }
+ static int64_t GrowCapacity(int64_t current, int64_t required) {
+ for (; current < required; current = std::max(current * 2, ListClass::kMinCapacity)) {
+ }
+ return current;
+ }
+};
+
+struct List : private details::ListHeader, private ListMethods<List> {
+ TVM_FFI_DEF_STATIC_TYPE(List, Object, TVMFFITypeIndex::kTVMFFIList);
+ TVM_FFI_DEF_TYPE_FRIENDS();
+ constexpr static int64_t kMinCapacity = 4;
+ struct Allocator;
+
+ List(int64_t list_capacity, int64_t pool_capacity) {
+ this->_extra.list_capacity = list_capacity;
+ this->_extra.list_length = 0;
+ this->_extra.pool_capacity = pool_capacity;
+ this->_extra.pool_length = 0;
+ }
+ using TBase = ListMethods<List>;
+ using TBase::begin;
+ using TBase::capacity;
+ using TBase::clear;
+ using TBase::empty;
+ using TBase::end;
+ using TBase::erase;
+ using TBase::GetItem;
+ using TBase::inplace_init_many;
+ using TBase::inplace_insert;
+ using TBase::inplace_insert_many;
+ using TBase::inplace_push_back;
+ using TBase::inplace_resize;
+ using TBase::pop_back;
+ using TBase::rbegin;
+ using TBase::rend;
+ using TBase::SetItem;
+ using TBase::size;
+
+private:
+ constexpr static bool all_items_are_pod = false;
+ constexpr static bool all_items_are_obj = false;
+ ~List() { this->clear(); }
+
+ template <typename>
+ friend struct ListMethods;
+};
+
+struct List::Allocator {
+ static TVM_FFI_INLINE List *New() { return New(0, 0); }
+ static TVM_FFI_INLINE List *New(int64_t list_capacity, int64_t pool_capacity) {
+ static_assert(sizeof(PoolItem) % sizeof(ListItem) == 0);
+ constexpr int64_t ratio = sizeof(PoolItem) / sizeof(ListItem);
+ static_assert(ratio == 2);
+ return DefaultObjectAllocator<List>::NewWithPad<ListItem>(list_capacity + pool_capacity * ratio,
+ list_capacity, pool_capacity);
+ }
+};
+
+template <>
+struct Ref<List> : public RefBase<List> {
+ TVM_FFI_DEF_TYPE_FRIENDS();
+ TVM_FFI_REF_DEF_DELEGATE_CONSTRUCTORS(Ref<List>, RefBase<List>)
+
+private:
+ struct AccessProxy {
+ AccessProxy &operator=(Any data) {
+ self->SetItem(index, data);
+ return *this;
+ }
+ template <typename Type>
+ operator Type() const {
+ return Type(self->get()->GetItem(index));
+ }
+ operator AnyView() const { return self->get()->GetItem(index); }
+
+ friend std::ostream &operator<<(std::ostream &os, const AccessProxy &self) {
+ return os << self.self->get()->GetItem(self.index);
+ }
+
+ private:
+ friend struct Ref<List>;
+ AccessProxy(Ref<List> *self, int64_t index) : self(self), index(index) {}
+ Ref<List> *self;
+ int64_t index;
+ };
+
+public:
+ Ref() : TBase(List::Allocator::New(0, 0)) {}
+ Ref(std::initializer_list<Any> data) : Ref(data.begin(), data.end()) {}
+ template <typename Iter>
+ Ref<List>(Iter first, Iter last) : Ref(std::distance(first, last)) {
+ std::vector<Any> elems = this->EnsureCapacityWithList(first, last);
+ int64_t numel = static_cast<int64_t>(elems.size());
+ this->get()->inplace_init_many(numel, elems.data());
+ }
+ template <typename Iter>
+ void insert(int64_t index, Iter first, Iter last) {
+ std::vector<Any> elems = this->EnsureCapacityWithList(first, last);
+ int64_t numel = static_cast<int64_t>(elems.size());
+ this->get()->inplace_insert_many(index, numel, elems.data());
+ }
+ void push_back(Any data) {
+ this->EnsureCapacityWithOneMoreElement(data)->inplace_push_back(data);
+ }
+ void insert(int64_t index, Any data) {
+ this->EnsureCapacityWithOneMoreElement(data)->inplace_insert(index, data);
+ }
+ void resize(int64_t new_size) { this->EnsureCapacityAtLeast(new_size)->inplace_resize(new_size); }
+ void reserve(int64_t capacity) { this->EnsureCapacityAtLeast(capacity); }
+ void clear() { this->get()->clear(); }
+ void erase(int64_t index) { this->get()->erase(index); }
+ void pop_back() { this->get()->pop_back(); }
+ int64_t size() const { return this->get()->size(); }
+ int64_t capacity() const { return this->get()->capacity(); }
+ bool empty() const { return this->get()->empty(); }
+ AnyView front() const { return this->get()->GetItem(0); }
+ AnyView back() const { return this->get()->GetItem(this->get()->size() - 1); }
+ auto operator[](int64_t index) { return AccessProxy(this, index); }
+ auto operator[](int64_t index) const { return this->get()->GetItem(index); }
+ auto begin() { return this->get()->begin(); }
+ auto end() { return this->get()->end(); }
+ auto rbegin() { return this->get()->rbegin(); }
+ auto rend() { return this->get()->rend(); }
+ void SetItem(int64_t i, Any data) {
+ TVM_FFI_LIST_EXEC_IF_POD(List, data.type_index,
+ { this->get()->MaybeMoveToMoreCapacity(0, 1).Swap(*this); });
+ this->get()->SetItem(i, data);
+ }
+
+protected:
+ friend struct List;
+ using RefBase<List>::operator*;
+ using RefBase<List>::operator->;
+ using TSelf = Ref<List>;
+ using TBase = RefBase<List>;
+ friend struct List;
+ template <typename>
+ friend struct ListMethods;
+
+ Ref<List>(int64_t size) : TBase(List::Allocator::New(size, size)) {}
+
+ List *EnsureCapacityWithOneMoreElement(const Any &data) {
+ int64_t pool_len_delta = 0;
+ TVM_FFI_LIST_EXEC_IF_POD(List, data.type_index, { pool_len_delta = 1; });
+ this->get()->MaybeMoveToMoreCapacity(1, pool_len_delta).Swap(*this);
+ return this->get();
+ }
+
+ List *EnsureCapacityAtLeast(int64_t capacity) {
+ List *self = this->get();
+ int64_t list_len_delta = std::max<int64_t>(capacity - self->_extra.list_capacity, 0);
+ int64_t pool_len_delta = 0;
+ if constexpr (!List::all_items_are_obj) {
+ pool_len_delta = std::max<int64_t>(capacity - self->_extra.pool_capacity, 0);
+ }
+ this->get()->MaybeMoveToMoreCapacity(list_len_delta, pool_len_delta).Swap(*this);
+ return this->get();
+ }
+
+ template <typename Iter>
+ std::vector<Any> EnsureCapacityWithList(Iter first, Iter last) {
+ std::vector<Any> ret(first, last);
+ int64_t list_len_delta = ret.size();
+ int64_t pool_len_delta = 0;
+ if constexpr (List::all_items_are_pod) {
+ pool_len_delta = ret.size();
+ } else if constexpr (!List::all_items_are_obj) {
+ for (const Any &v : ret) {
+ if (details::IsTypeIndexPOD(v.type_index)) {
+ ++pool_len_delta;
+ }
+ }
+ }
+ this->get()->MaybeMoveToMoreCapacity(list_len_delta, pool_len_delta).Swap(*this);
+ return ret;
+ }
+};
+
+namespace details {
+TVM_FFI_INLINE void ListRangeCheck(int64_t begin, int64_t end, int64_t length) {
+ if (begin > end) {
+ TVM_FFI_THROW(IndexError) << "Invalid range [" << begin << ", " << end
+ << ") when indexing a list";
+ }
+ if (begin < 0 || end > length) {
+ if (begin == end || begin + 1 == end) {
+ TVM_FFI_THROW(IndexError) << "Indexing `" << begin << "` of a list of size " << length;
+ } else {
+ TVM_FFI_THROW(IndexError) << "Indexing [" << begin << ", " << end << ") of a list of size "
+ << length;
+ }
+ }
+}
+} // namespace details
+} // namespace ffi
+} // namespace tvm
+
+#undef TVM_FFI_LIST_EXEC_IF_POD
+
+#endif // TVM_FFI_LIST_H_
diff --git a/include/tvm/ffi/ext/str.h b/include/tvm/ffi/ext/str.h
new file mode 100644
index 0000000000..f5aa29391e
--- /dev/null
+++ b/include/tvm/ffi/ext/str.h
@@ -0,0 +1,245 @@
+#ifndef TVM_FFI_STR_H_
+#define TVM_FFI_STR_H_
+#include <cstring>
+#include <sstream>
+#include <tvm/ffi/core/core.h>
+
+namespace tvm {
+namespace ffi {
+namespace details {
+struct StrHeader {
+ int64_t length;
+ const char *data;
+};
+} // namespace details
+
+struct Str : private AnyWithExtra<details::StrHeader> {
+ struct Allocator;
+ using Header = AnyWithExtra<details::StrHeader>;
+ TVM_FFI_DEF_STATIC_TYPE(Str, Object, TVMFFITypeIndex::kTVMFFIStr);
+
+ TVM_FFI_INLINE const char *c_str() const { return this->_extra.data; }
+ TVM_FFI_INLINE const char *data() const { return this->_extra.data; }
+ TVM_FFI_INLINE int64_t length() const { return this->_extra.length; }
+ TVM_FFI_INLINE int64_t size() const { return this->_extra.length; }
+ inline uint64_t Hash() const {
+ return details::TVMFFIStrHash(reinterpret_cast<const TVMFFIStr *>(this));
+ }
+
+protected:
+ using Header::_extra;
+ TVM_FFI_INLINE Str() : Header() {}
+};
+
+static_assert(sizeof(Str) == sizeof(TVMFFIStr));
+static_assert(offsetof(Str::Header, _extra.length) == offsetof(TVMFFIStr, length));
+static_assert(offsetof(Str::Header, _extra.data) == offsetof(TVMFFIStr, data));
+
+namespace details {
+
+struct StrStd : public Str {
+ using Allocator = ::tvm::ffi::DefaultObjectAllocator<StrStd>;
+ template <typename>
+ friend struct ::tvm::ffi::DefaultObjectAllocator;
+
+protected:
+ TVM_FFI_INLINE StrStd(std::string &&str) : Str(), container(std::move(str)) {
+ this->_extra.length = static_cast<int64_t>(container.length());
+ this->_extra.data = container.data();
+ }
+
+ std::string container;
+};
+
+struct StrPad : public Str {
+ using Allocator = ::tvm::ffi::DefaultObjectAllocator<StrPad>;
+ template <typename>
+ friend struct ::tvm::ffi::DefaultObjectAllocator;
+
+protected:
+ TVM_FFI_INLINE StrPad(const char *str, size_t N) : Str() {
+ char *str_copy = reinterpret_cast<char *>(this) + sizeof(Str);
+ std::memcpy(str_copy, str, N);
+ str_copy[N - 1] = '\0';
+
+ this->_extra.length = static_cast<int64_t>(N) - 1;
+ this->_extra.data = str_copy;
+ }
+};
+} // namespace details
+
+struct Str::Allocator {
+ TVM_FFI_INLINE static Str *New(std::string &&str) {
+ return details::StrStd::Allocator::New(std::move(str));
+ }
+ TVM_FFI_INLINE static Str *New(const std::string &str) {
+ int64_t N = static_cast<int64_t>(str.length()) + 1;
+ return details::StrPad::Allocator::NewWithPad<char>(N, str.data(), N);
+ }
+ TVM_FFI_INLINE static Str *New(const char *str) {
+ int64_t N = static_cast<int64_t>(std::strlen(str)) + 1;
+ return details::StrPad::Allocator::NewWithPad<char>(N, str, N);
+ }
+ template <size_t N>
+ TVM_FFI_INLINE static Str *New(const CharArray<N> &str) {
+ return details::StrPad::Allocator::NewWithPad<char>(N, str, N);
+ }
+ TVM_FFI_INLINE static Str *New(const char *str, size_t N) {
+ return details::StrPad::Allocator::NewWithPad<char>(N, str, N);
+ }
+};
+
+template <>
+struct Ref<Str> : public RefBase<Str> {
+ TVM_FFI_DEF_TYPE_FRIENDS();
+ TVM_FFI_INLINE Ref() : RefBase<Str>() {}
+ TVM_FFI_REF_DEF_DELEGATE_CONSTRUCTORS(Ref<Str>, RefBase<Str>)
+
+ using RefBase<Str>::New;
+ template <size_t N>
+ TVM_FFI_INLINE static TSub New(const CharArray<N> &arg) {
+ return TSub(Str::Allocator::template New<N>(arg));
+ }
+};
+
+TVM_FFI_INLINE Ref<Str> Object::str() const {
+ std::ostringstream os;
+ os << *this;
+ return Ref<Str>::New(os.str());
+}
+
+template <typename ObjectType>
+TVM_FFI_INLINE Ref<Str> RefBase<ObjectType>::str() const {
+ std::ostringstream os;
+ os << *this;
+ return Ref<Str>::New(os.str());
+}
+
+TVM_FFI_INLINE Ref<Str> AnyView::str() const {
+ std::ostringstream os;
+ os << *this;
+ return Ref<Str>::New(os.str());
+}
+
+TVM_FFI_INLINE Ref<Str> Any::str() const {
+ std::ostringstream os;
+ os << *this;
+ return Ref<Str>::New(os.str());
+}
+
+/********** TypeTraits *********/
+
+template <>
+struct TypeTraits<Str *> : public TypeTraitsDefaultForObject<Str> {
+private:
+ using TypeTraitsDefaultForObject<Str>::MoveFromTVMFFIAnyToRef;
+ using TypeTraitsDefaultForObject<Str>::CopyFromTVMFFIAnyToRef;
+
+public:
+ TVM_FFI_INLINE static Str *CopyFromTVMFFIAnyToRef(const TVMFFIAny *v) {
+ if (details::IsTypeIndexNone(v->type_index)) {
+ return nullptr;
+ }
+ if (v->type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
+ return Str::Allocator::New(v->v_str, std::strlen(v->v_str) + 1);
+ }
+ if (!details::IsTypeIndexPOD(v->type_index) && details::IsInstanceOf<Str>(v->type_index)) {
+ return reinterpret_cast<Str *>(v->v_obj);
+ }
+ throw TemporaryTypeError();
+ }
+
+ TVM_FFI_INLINE static Str *CopyFromTVMFFIAnyToTypeWithStorage(const TVMFFIAny *v, Any *storage) {
+ if (details::IsTypeIndexNone(v->type_index)) {
+ return nullptr;
+ }
+ if (v->type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
+ Str *ret = Str::Allocator::New(v->v_str, std::strlen(v->v_str) + 1);
+ *storage = Ref<Str>(ret);
+ return ret;
+ }
+ if (!details::IsTypeIndexPOD(v->type_index) && details::IsInstanceOf<Str>(v->type_index)) {
+ return reinterpret_cast<Str *>(v->v_obj);
+ }
+ throw TemporaryTypeError();
+ }
+
+ TVM_FFI_INLINE static Str *MoveFromTVMFFIAnyToRef(TVMFFIAny *v) {
+ Str *ret = CopyFromTVMFFIAnyToRef(v);
+ v->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone);
+ v->v_obj = nullptr;
+ return ret;
+ }
+};
+
+namespace details {
+
+Any FuncInvoke(const void *func, int32_t num_args, const TVMFFIAny *args);
+
+inline int32_t TVMFFIStrCmp(const TVMFFIStr *a, const TVMFFIStr *b) {
+ if (a->length != b->length) {
+ return a->length - b->length;
+ }
+ return std::strncmp(a->data, b->data, a->length);
+}
+
+inline uint64_t TVMFFIStrHash(const TVMFFIStr *str) {
+ const constexpr uint64_t kMultiplier = 1099511628211ULL;
+ const constexpr uint64_t kMod = 2147483647ULL;
+ const char *it = str->data;
+ const char *end = it + str->length;
+ uint64_t result = 0;
+ for (; it + 8 <= end; it += 8) {
+ uint64_t b = (static_cast<uint64_t>(it[0]) << 56) | (static_cast<uint64_t>(it[1]) << 48) |
+ (static_cast<uint64_t>(it[2]) << 40) | (static_cast<uint64_t>(it[3]) << 32) |
+ (static_cast<uint64_t>(it[4]) << 24) | (static_cast<uint64_t>(it[5]) << 16) |
+ (static_cast<uint64_t>(it[6]) << 8) | static_cast<uint64_t>(it[7]);
+ result = (result * kMultiplier + b) % kMod;
+ }
+ if (it < end) {
+ uint64_t b = 0;
+ if (it + 4 <= end) {
+ b = (static_cast<uint64_t>(it[0]) << 24) | (static_cast<uint64_t>(it[1]) << 16) |
+ (static_cast<uint64_t>(it[2]) << 8) | static_cast<uint64_t>(it[3]);
+ it += 4;
+ }
+ if (it + 2 <= end) {
+ b = (b << 16) | (static_cast<uint64_t>(it[0]) << 8) | static_cast<uint64_t>(it[1]);
+ it += 2;
+ }
+ if (it + 1 <= end) {
+ b = (b << 8) | static_cast<uint64_t>(it[0]);
+ it += 1;
+ }
+ result = (result * kMultiplier + b) % kMod;
+ }
+ return result;
+}
+
+TVM_FFI_INLINE void AnyView2Str(std::ostream &os, const TVMFFIAny *v) {
+#if TVM_FFI_ALLOW_DYN_TYPE
+ AnyView attr = TypeGetAttr(v->type_index, "__str__");
+ if (details::IsTypeIndexNone(attr.type_index)) {
+ os << TypeIndex2TypeKey(v->type_index) << '@' << v->v_ptr;
+ } else {
+ os << Ref<Str>(FuncInvoke(attr.v_obj, 1, v))->c_str();
+ }
+#else
+ return StaticAnyView2Str(os, v);
+#endif
+}
+
+TVM_FFI_INLINE TVMFFIObject *StrMoveFromStdString(std::string &&source) {
+ return reinterpret_cast<TVMFFIObject *>(Str::Allocator::New(std::move(source)));
+}
+
+TVM_FFI_INLINE TVMFFIObject *StrCopyFromCharArray(const char *source, size_t length) {
+ return reinterpret_cast<TVMFFIObject *>(Str::Allocator::New(source, length + 1));
+}
+
+} // namespace details
+
+} // namespace ffi
+} // namespace tvm
+
+#endif // TVM_FFI_STR_H_
diff --git a/include/tvm/ffi/ffi.hpp b/include/tvm/ffi/ffi.hpp
new file mode 100644
index 0000000000..68532f7457
--- /dev/null
+++ b/include/tvm/ffi/ffi.hpp
@@ -0,0 +1,9 @@
+#ifndef TVM_FFI_H_
+#define TVM_FFI_H_
+#include "./core/core.h"
+#include "./ext/dict.h"
+#include "./ext/error.h"
+#include "./ext/func.h"
+#include "./ext/list.h"
+#include "./ext/str.h"
+#endif // TVM_FFI_H_
diff --git a/src/ffi/registry.cc b/src/ffi/registry.cc
new file mode 100644
index 0000000000..05c0280796
--- /dev/null
+++ b/src/ffi/registry.cc
@@ -0,0 +1,144 @@
+#include <memory>
+#include <string>
+#include <tvm/ffi/ffi.hpp>
+#include <unordered_map>
+#include <vector>
+
+namespace {
+
+using tvm::ffi::Any;
+using tvm::ffi::AnyView;
+using tvm::ffi::Func;
+using tvm::ffi::Ref;
+using tvm::ffi::TypeIndexTraits;
+using tvm::ffi::TypeTraits;
+using tvm::ffi::details::InitTypeTable;
+
+struct TypeInfoImpl : public TVMFFITypeInfo {
+ std::string type_key_data;
+ std::vector<int32_t> type_ancestors_data;
+};
+
+struct TypeTable {
+ int32_t num_types;
+ std::vector<std::shared_ptr<TypeInfoImpl>> type_table;
+ std::unordered_map<std::string, std::shared_ptr<TypeInfoImpl>> type_key_to_info;
+ std::unordered_map<std::string, std::vector<Any>> type_attrs;
+
+ static TypeTable *New();
+ static TypeTable *Global() {
+ static std::unique_ptr<TypeTable> instance(TypeTable::New());
+ return instance.get();
+ }
+};
+
+int32_t TypeDef(TypeTable *self, int32_t type_index, const char *type_key, int32_t type_depth,
+ const int32_t *type_ancestors) {
+ if (self == nullptr) {
+ TVM_FFI_THROW(ValueError) << "TypeTable is provided as nullptr.";
+ }
+ if (self->type_key_to_info.count(type_key) != 0) {
+ TVM_FFI_THROW(ValueError) << "Type key is already registered.";
+ }
+ if (type_index == -1) {
+ type_index = self->num_types++;
+ }
+ std::shared_ptr<TypeInfoImpl> info = std::make_shared<TypeInfoImpl>();
+ {
+ info->type_key_data = std::string(type_key);
+ info->type_ancestors_data = std::vector<int32_t>(type_ancestors, type_ancestors + type_depth);
+ info->type_index = type_index;
+ info->type_key = info->type_key_data.c_str();
+ info->type_depth = type_depth;
+ info->type_ancestors = info->type_ancestors_data.data();
+ }
+ if (type_index >= static_cast<int32_t>(self->type_table.size())) {
+ self->type_table.resize((type_index / 1024 + 1) * 1024);
+ }
+ self->type_table.at(type_index) = info;
+ self->type_key_to_info[type_key] = info;
+ return type_index;
+}
+
+struct InitFunctor {
+ template <enum TVMFFITypeIndex type_index, typename ObjectType>
+ void RegisterType(TypeTable *self) {
+ TypeDef(self, static_cast<int32_t>(type_index), TypeIndexTraits<type_index>::type_key,
+ std::max<int32_t>(ObjectType::_type_depth, 0), ObjectType::_type_ancestors.data());
+ }
+
+ template <enum TVMFFITypeIndex type_index, typename ObjectType>
+ void RegisterStr(TypeTable *self) {
+ Any value = Any(Ref<Func>::New(TypeTraits<ObjectType>::__str__));
+ TVMFFIDynTypeSetAttr(self, static_cast<int32_t>(type_index), "__str__", &value);
+ }
+};
+
+TypeTable *TypeTable::New() {
+ TypeTable *self = new TypeTable();
+ self->type_table.resize(1024);
+ self->type_key_to_info.reserve(1024);
+ self->num_types = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDynObjectBegin);
+ InitTypeTable(InitFunctor(), self);
+ return self;
+}
+
+} // namespace
+
+TVM_FFI_API void TVMFFIDynTypeDef(TVMFFITypeTableHandle _self, const char *type_key,
+ int32_t type_depth, const int32_t *type_ancestors,
+ int32_t *type_index) {
+ TypeTable *self = static_cast<TypeTable *>(_self);
+ if (self == nullptr) {
+ self = TypeTable::Global();
+ }
+ *type_index = TypeDef(self, -1, type_key, type_depth, type_ancestors);
+}
+
+TVM_FFI_API void TVMFFIDynTypeIndex2Info(TVMFFITypeTableHandle _self, int32_t type_index,
+ TVMFFITypeInfoHandle *ret) {
+ TypeTable *self = static_cast<TypeTable *>(_self);
+ if (self == nullptr) {
+ self = TypeTable::Global();
+ }
+ if (type_index < 0 || type_index >= static_cast<int32_t>(self->type_table.size())) {
+ *ret = nullptr;
+ } else {
+ *ret = self->type_table.at(type_index).get();
+ }
+}
+
+TVM_FFI_API void TVMFFIDynTypeSetAttr(TVMFFITypeTableHandle _self, int32_t type_index,
+ const char *key, TVMFFIAnyHandle value) {
+ TypeTable *self = static_cast<TypeTable *>(_self);
+ if (self == nullptr) {
+ self = TypeTable::Global();
+ }
+ std::vector<Any> &attrs = self->type_attrs[key];
+ if (type_index >= static_cast<int32_t>(attrs.size())) {
+ attrs.resize((type_index / 1024 + 1) * 1024);
+ }
+ attrs.at(type_index) = Any(*static_cast<AnyView *>(value));
+}
+
+TVM_FFI_API void TVMFFIDynTypeGetAttr(TVMFFITypeTableHandle _self, int32_t type_index,
+ const char *key, TVMFFIAnyHandle *ret) {
+ TypeTable *self = static_cast<TypeTable *>(_self);
+ if (self == nullptr) {
+ self = TypeTable::Global();
+ }
+ std::vector<Any> &attrs = self->type_attrs[key];
+ if (type_index >= static_cast<int32_t>(attrs.size())) {
+ *ret = nullptr;
+ } else {
+ *ret = &attrs.at(type_index);
+ }
+}
+
+TVM_FFI_API void TVMFFIDynTypeTypeTableCreate(TVMFFITypeTableHandle *ret) {
+ *ret = TypeTable::New();
+}
+
+TVM_FFI_API void TVMFFIDynTypeTypeTableDestroy(TVMFFITypeTableHandle handle) {
+ delete static_cast<TypeTable *>(handle);
+}
(tvm) 19/38: Cleanup and focus on the integrated version
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 4472f6ec3be9b4232407add6c4333a8c006b1d3a
Author: tqchen <tq...@gmail.com>
AuthorDate: Thu Feb 13 14:57:25 2025 -0500
Cleanup and focus on the integrated version
---
ffi/tests/backup_cpp/CMakeLists.txt | 24 --
ffi/tests/backup_cpp/test_any.cc | 636 -------------------------------
ffi/tests/backup_cpp/test_any_view.cc | 631 ------------------------------
ffi/tests/backup_cpp/test_dict.cc | 184 ---------
ffi/tests/backup_cpp/test_func.cc | 213 -----------
ffi/tests/backup_cpp/test_list.cc | 462 ----------------------
ffi/tests/backup_cpp/test_ref.cc | 294 --------------
ffi/tests/backup_cpp/test_str.cc | 49 ---
ffi/tests/backup_cpp/test_type_dyn.cc | 68 ----
ffi/tests/backup_cpp/test_type_static.cc | 116 ------
include/tvm/ffi/core/c_ffi_abi.h | 166 --------
include/tvm/ffi/core/core.h | 421 --------------------
include/tvm/ffi/core/traits.h | 613 -----------------------------
include/tvm/ffi/core/utils.h | 281 --------------
include/tvm/ffi/ext/dict.h | 515 -------------------------
include/tvm/ffi/ext/error.h | 89 -----
include/tvm/ffi/ext/func.h | 390 -------------------
include/tvm/ffi/ext/list.h | 457 ----------------------
include/tvm/ffi/ext/str.h | 245 ------------
include/tvm/ffi/ffi.hpp | 9 -
20 files changed, 5863 deletions(-)
diff --git a/ffi/tests/backup_cpp/CMakeLists.txt b/ffi/tests/backup_cpp/CMakeLists.txt
deleted file mode 100644
index cb17040090..0000000000
--- a/ffi/tests/backup_cpp/CMakeLists.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-file(GLOB _test_sources "${CMAKE_CURRENT_SOURCE_DIR}/test*.cc")
-add_executable(
- tvm_ffi_tests
- EXCLUDE_FROM_ALL
- ${_test_sources}
-)
-set_target_properties(
- tvm_ffi_tests PROPERTIES
- CXX_STANDARD 17
- CXX_STANDARD_REQUIRED ON
- CXX_EXTENSIONS OFF
- MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
- ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
- LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
- RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
-)
-add_cxx_warning(tvm_ffi_tests)
-add_sanitizer_address(tvm_ffi_tests)
-target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi)
-add_sanitizer_address(tvm_ffi_registry_shared)
-target_link_libraries(tvm_ffi_tests PRIVATE tvm_ffi_registry_shared)
-
-
-add_googletest(tvm_ffi_tests)
diff --git a/ffi/tests/backup_cpp/test_any.cc b/ffi/tests/backup_cpp/test_any.cc
deleted file mode 100644
index d3c1e17002..0000000000
--- a/ffi/tests/backup_cpp/test_any.cc
+++ /dev/null
@@ -1,636 +0,0 @@
-#include <gtest/gtest.h>
-
-#include <tvm/ffi/ffi.hpp>
-
-namespace {
-using namespace tvm::ffi;
-
-template <typename SrcType, typename Checker>
-void TestAnyConstructor(Checker check, TVMFFITypeIndex expected_type_index, const SrcType& source) {
- Any v(source);
- EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
- EXPECT_EQ(v.ref_cnt, 0);
- check(&v, source);
-};
-
-int64_t FuncCall(int64_t x) { return x + 1; }
-
-std::vector<Any> AnyArrayFactory() {
- return std::vector<Any>{
- Any(nullptr),
- Any(1),
- Any(2.5),
- Any(reinterpret_cast<void*>(FuncCall)),
- Any(DLDevice{kDLCPU, 0}),
- Any(DLDataType{kDLInt, 32, 1}),
- Any("Hello (raw str)"),
- Any(Ref<Object>::New()),
- Any(Ref<Func>::New(FuncCall)),
- Any(std::string("World (std::string)")),
- Any(Ref<Str>::New("Hello World (Ref<Str>)")),
- };
-}
-
-template <typename SrcType>
-void TestAnyStringify(const SrcType& source, TVMFFITypeIndex expected_type_index,
- const std::string& expected) {
- Any v(source);
- EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
- EXPECT_EQ(v.str()->c_str(), expected);
-}
-
-template <typename SrcType, typename Checker>
-void TestAnyStringifyChecker(const SrcType& source, TVMFFITypeIndex expected_type_index,
- Checker check) {
- Any v(source);
- EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
- check(v);
-}
-
-void CheckAnyRefCnt(const TVMFFIAny* v) {
- if (v->type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
- EXPECT_EQ(v->v_obj->ref_cnt, 1);
- }
-}
-
-TEST(Any_Constructor_0_Default, Default) {
- Any v;
- EXPECT_EQ(v.type_index, 0);
- EXPECT_EQ(v.ref_cnt, 0);
- EXPECT_EQ(v.v_int64, 0);
-}
-
-TEST(Any_Constructor_1_Any, Copy) {
- Any v1(1);
- Any v2(v1);
- EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v1.v_int64, 1);
- EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v2.v_int64, 1);
-}
-
-TEST(Any_Constructor_1_Any, Move) {
- Any v1(1);
- Any v2(std::move(v1));
- EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone));
- EXPECT_EQ(v1.v_int64, 0);
- EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v2.v_int64, 1);
-}
-
-TEST(Any_Constructor_2_AnyView, Copy) {
- AnyView v1(1);
- Any v2(v1);
- EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v1.v_int64, 1);
- EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v2.v_int64, 1);
-}
-
-TEST(Any_Constructor_2_AnyView, Move) {
- AnyView v1(1);
- Any v2(std::move(v1));
- EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone));
- EXPECT_EQ(v1.v_int64, 0);
- EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v2.v_int64, 1);
-}
-
-TEST(Any_Constructor_3_Ref, Copy) {
- Ref<Object> obj = Ref<Object>::New();
- Any v(obj);
- const TVMFFIAny* v_obj = v.v_obj;
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
- EXPECT_EQ(v.ref_cnt, 0);
- EXPECT_EQ(v_obj, static_cast<const void*>(obj.get()));
- EXPECT_EQ(v_obj->ref_cnt, 2);
-}
-
-TEST(Any_Constructor_3_Ref, Move) {
- Ref<Object> obj = Ref<Object>::New();
- Any v(std::move(obj));
- const TVMFFIAny* v_obj = v.v_obj;
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
- EXPECT_EQ(v.ref_cnt, 0);
- EXPECT_EQ(v_obj->ref_cnt, 1);
- EXPECT_EQ(obj.get(), nullptr);
-}
-
-TEST(Any_Constructor_4_TypeTraits, Integer) {
- auto check = [](TVMFFIAny* v, int64_t source) -> void { EXPECT_EQ(v->v_int64, source); };
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int8_t>(1));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int16_t>(2));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int32_t>(3));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int64_t>(4));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint8_t>(1));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint16_t>(2));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint32_t>(3));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint64_t>(4));
-}
-
-TEST(Any_Constructor_4_TypeTraits, Float) {
- auto check = [](TVMFFIAny* v, double source) -> void { EXPECT_EQ(v->v_float64, source); };
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(3));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(4));
-}
-
-TEST(Any_Constructor_4_TypeTraits, Ptr) {
- int p = 4;
- auto check = [](TVMFFIAny* v, void* source) -> void { EXPECT_EQ(v->v_ptr, source); };
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFINone, static_cast<void*>(nullptr));
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIPtr, static_cast<void*>(&p));
-}
-
-TEST(Any_Constructor_4_TypeTraits, Device) {
- auto check = [](TVMFFIAny* v, const DLDevice& source) -> void {
- EXPECT_EQ(v->v_device.device_type, source.device_type);
- EXPECT_EQ(v->v_device.device_id, source.device_id);
- };
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIDevice, DLDevice{kDLCPU, 0});
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIDevice, DLDevice{kDLCUDA, 1});
-}
-
-TEST(Any_Constructor_4_TypeTraits, DataType) {
- auto check = [](TVMFFIAny* v, const DLDataType& source) -> void {
- EXPECT_EQ(v->v_dtype.code, source.code);
- EXPECT_EQ(v->v_dtype.bits, source.bits);
- EXPECT_EQ(v->v_dtype.lanes, source.lanes);
- };
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIDataType, DLDataType{kDLInt, 32, 1});
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIDataType, DLDataType{kDLUInt, 0, 0});
-}
-
-TEST(Any_Constructor_4_TypeTraits, RawStr) {
- auto check = [](TVMFFIAny* v, const char* source) -> void {
- Str* str = static_cast<Str*>(v->v_ptr);
- EXPECT_STREQ(str->c_str(), source);
- };
- const char* empty = "";
- const char* hello = "hello";
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, empty);
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, hello);
-}
-
-TEST(Any_Constructor_4_TypeTraits, CharArray) {
- auto check = [](TVMFFIAny* v, const char* source) -> void {
- Str* str = static_cast<Str*>(v->v_ptr);
- EXPECT_STREQ(str->c_str(), source);
- };
- const char empty[] = "";
- const char hello[] = "hello";
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, empty);
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, hello);
-}
-
-TEST(Any_Constructor_4_TypeTraits, StdString) {
- auto check = [](TVMFFIAny* v, const std::string& source) -> void {
- Str* str = static_cast<Str*>(v->v_ptr);
- EXPECT_EQ(str->c_str(), source);
- };
- std::string empty = "";
- std::string hello = "hello";
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, hello);
- TestAnyConstructor(check, TVMFFITypeIndex::kTVMFFIStr, empty);
-}
-
-TEST(Any_Constructor_5_Object_Ptr, Object) {
- Ref<Object> obj = Ref<Object>::New();
- TVMFFIAny* ptr = reinterpret_cast<TVMFFIAny*>(obj.get());
- {
- Any v(obj.get());
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
- EXPECT_EQ(v.v_obj->ref_cnt, 2);
- EXPECT_EQ(v.v_obj, ptr);
- }
- EXPECT_EQ(ptr->ref_cnt, 1);
-}
-
-TEST(Any_Constructor_5_Object_Ptr, Func) {
- Ref<Func> func = Ref<Func>::New(FuncCall);
- TVMFFIAny* ptr = reinterpret_cast<TVMFFIAny*>(func.get());
- {
- Any v(func.get());
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
- EXPECT_EQ(v.v_obj->ref_cnt, 2);
- EXPECT_EQ(v.v_obj, ptr);
- }
- EXPECT_EQ(ptr->ref_cnt, 1);
-}
-
-TEST(Any_Constructor_5_Object_Ptr, Str) {
- Ref<Str> str = Ref<Str>::New("hello");
- TVMFFIAny* ptr = reinterpret_cast<TVMFFIAny*>(str.get());
- {
- Any v(str.get());
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
- EXPECT_EQ(v.v_obj->ref_cnt, 2);
- EXPECT_EQ(v.v_obj, ptr);
- }
- EXPECT_EQ(ptr->ref_cnt, 1);
-}
-
-TEST(Any_Converter_0_TypeTraits, Integer) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any& v : vs) {
- auto convert = [&]() -> int64_t { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
- EXPECT_EQ(convert(), 1);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `int`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_0_TypeTraits, Float) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any& v : vs) {
- auto convert = [&]() -> double { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
- EXPECT_EQ(convert(), 1.0);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFloat)) {
- EXPECT_EQ(convert(), 2.5);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `float`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_0_TypeTraits, Ptr) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any& v : vs) {
- auto convert = [&]() -> void* { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- EXPECT_EQ(convert(), nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIPtr)) {
- EXPECT_EQ(convert(), reinterpret_cast<void*>(&FuncCall));
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
- EXPECT_EQ(convert(), v.v_str);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Ptr`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_0_TypeTraits, Device) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any& v : vs) {
- auto convert = [&]() -> DLDevice { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDevice)) {
- EXPECT_EQ(convert().device_type, kDLCPU);
- EXPECT_EQ(convert().device_id, 0);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Device`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_0_TypeTraits, DataType) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any& v : vs) {
- auto convert = [&]() -> DLDataType { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDataType)) {
- EXPECT_EQ(convert().code, kDLInt);
- EXPECT_EQ(convert().bits, 32);
- EXPECT_EQ(convert().lanes, 1);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `dtype`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_0_TypeTraits, RawStr) {
- std::vector<Any> vs = AnyArrayFactory();
- int counter = 0;
- for (const Any& v : vs) {
- auto convert = [&]() -> const char* { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- ++counter;
- EXPECT_LE(counter, 3);
- if (counter == 1) {
- EXPECT_STREQ(convert(), "Hello (raw str)");
- } else if (counter == 2) {
- EXPECT_STREQ(convert(), "World (std::string)");
- } else if (counter == 3) {
- EXPECT_STREQ(convert(), "Hello World (Ref<Str>)");
- }
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `const char *`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_1_AnyView, Any) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any& v : vs) {
- auto convert = [&]() -> AnyView { return v; };
- {
- AnyView ret = convert();
- EXPECT_EQ(ret.type_index, v.type_index);
- EXPECT_EQ(ret.ref_cnt, 0);
- EXPECT_EQ(v.ref_cnt, 0);
- EXPECT_EQ(ret.v_obj, v.v_obj);
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_2_Ref, Object) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any& v : vs) {
- auto convert = [&]() -> Ref<Object> { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Ref<Object> ret = convert();
- EXPECT_EQ(ret.get(), nullptr);
- } else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
- Ref<Object> ret = convert();
- EXPECT_EQ(ret.get(), static_cast<void*>(v.v_obj));
- EXPECT_EQ(v.v_obj->ref_cnt, 2);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Object`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_2_Ref, Func) {
- std::vector<Any> views = AnyArrayFactory();
- for (const Any& v : views) {
- auto convert = [&]() -> Ref<Func> { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Ref<Func> ret = convert();
- EXPECT_EQ(ret.get(), nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
- Ref<Func> ret = convert();
- EXPECT_EQ(ret.get(), static_cast<void*>(v.v_obj));
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Func`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_2_Ref, Str) {
- int counter = 0;
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any& v : vs) {
- auto convert = [&]() -> Ref<Str> { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Ref<Str> ret = convert();
- EXPECT_EQ(ret.get(), nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- Ref<Str> ret = convert();
- ++counter;
- EXPECT_LE(counter, 3);
- if (counter == 1) {
- EXPECT_STREQ(ret->c_str(), "Hello (raw str)");
- } else if (counter == 2) {
- EXPECT_STREQ(ret->c_str(), "World (std::string)");
- } else {
- EXPECT_STREQ(ret->c_str(), "Hello World (Ref<Str>)");
- }
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Str`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_3_Object_Ptr, Object) {
- std::vector<Any> vs = AnyArrayFactory();
- for (const Any& v : vs) {
- auto convert = [&]() -> Object* { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Object* ret = convert();
- EXPECT_EQ(ret, nullptr);
- } else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
- Object* ret = convert();
- EXPECT_EQ(ret, static_cast<void*>(v.v_obj));
- EXPECT_EQ(v.v_obj->ref_cnt, 1);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Object *`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_3_Object_Ptr, Func) {
- std::vector<Any> views = AnyArrayFactory();
- for (const Any& v : views) {
- auto convert = [&]() -> Func* { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Func* ret = convert();
- EXPECT_EQ(ret, nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
- Func* ret = convert();
- EXPECT_EQ(ret, reinterpret_cast<Func*>(v.v_ptr));
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Func *`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Converter_3_Object_Ptr, Str) {
- std::vector<Any> vs = AnyArrayFactory();
- int counter = 0;
- for (const Any& v : vs) {
- auto convert = [&]() -> Str* { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Str* ret = convert();
- EXPECT_EQ(ret, nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- Str* ret = convert();
- ++counter;
- EXPECT_LE(counter, 3);
- if (counter == 1) {
- EXPECT_STREQ(ret->c_str(), "Hello (raw str)");
- } else if (counter == 2) {
- EXPECT_STREQ(ret->c_str(), "World (std::string)");
- } else {
- EXPECT_STREQ(ret->c_str(), "Hello World (Ref<Str>)");
- }
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Str *`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyRefCnt(&v);
- }
-}
-
-TEST(Any_Stringify, Integer) {
- TestAnyStringify<int8_t>(-13, TVMFFITypeIndex::kTVMFFIInt, "-13");
- TestAnyStringify<int16_t>(-5, TVMFFITypeIndex::kTVMFFIInt, "-5");
- TestAnyStringify<int32_t>(0, TVMFFITypeIndex::kTVMFFIInt, "0");
- TestAnyStringify<int64_t>(1, TVMFFITypeIndex::kTVMFFIInt, "1");
-}
-
-TEST(Any_Stringify, Float) {
- auto check = [](const Any& v) -> void {
- std::string str = v.str()->c_str();
- double f_str = std::stod(str);
- double f_src = v.v_float64;
- EXPECT_NEAR(f_src, f_str, 1e-5);
- };
- TestAnyStringifyChecker<float>(float(-3.14), TVMFFITypeIndex::kTVMFFIFloat, check);
- TestAnyStringifyChecker<double>(0.0, TVMFFITypeIndex::kTVMFFIFloat, check);
-}
-
-TEST(Any_Stringify, Ptr) {
- auto check = [](const Any& v) -> void {
- std::string str = v.str()->c_str();
- EXPECT_GT(str.size(), 2);
- };
- TestAnyStringify<void*>(nullptr, TVMFFITypeIndex::kTVMFFINone, "None");
- TestAnyStringifyChecker<void*>(reinterpret_cast<void*>(FuncCall), TVMFFITypeIndex::kTVMFFIPtr,
- check);
-}
-
-TEST(Any_Stringify, Device) {
- TestAnyStringify<DLDevice>(DLDevice{kDLCPU, 0}, TVMFFITypeIndex::kTVMFFIDevice, "cpu:0");
- TestAnyStringify<DLDevice>(DLDevice{kDLCUDA, 1}, TVMFFITypeIndex::kTVMFFIDevice, "cuda:1");
-}
-
-TEST(Any_Stringify, DataType) {
- TestAnyStringify<DLDataType>(DLDataType{kDLInt, 32, 1}, TVMFFITypeIndex::kTVMFFIDataType,
- "int32");
- TestAnyStringify<DLDataType>(DLDataType{kDLUInt, 1, 1}, TVMFFITypeIndex::kTVMFFIDataType, "bool");
- TestAnyStringify<DLDataType>(DLDataType{kDLOpaqueHandle, 0, 0}, TVMFFITypeIndex::kTVMFFIDataType,
- "void");
- TestAnyStringify<DLDataType>(DLDataType{kDLFloat, 8, 4}, TVMFFITypeIndex::kTVMFFIDataType,
- "float8x4");
-}
-
-TEST(Any_Stringify, RawStr) {
- TestAnyStringify<const char*>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
- TestAnyStringify<char[6]>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
- TestAnyStringify<const char[6]>("Hello", TVMFFITypeIndex::kTVMFFIStr, "\"Hello\"");
-}
-
-TEST(Any_Stringify, Object) {
- auto check = [](const Any& v) -> void {
- std::string expected_prefix = "object.Object@0";
- int n = static_cast<int>(expected_prefix.size());
- std::string str = v.str()->c_str();
- EXPECT_GT(str.size(), n);
- EXPECT_EQ(str.substr(0, n), expected_prefix);
- };
- TestAnyStringifyChecker<Ref<Object>>(Ref<Object>::New(), TVMFFITypeIndex::kTVMFFIObject, check);
-}
-
-TEST(Any_Stringify, Func) {
- auto check = [](const Any& v) -> void {
- std::string expected_prefix = "object.Func@0";
- int n = static_cast<int>(expected_prefix.size());
- std::string str = v.str()->c_str();
- EXPECT_GT(str.size(), n);
- EXPECT_EQ(str.substr(0, n), expected_prefix);
- };
- TestAnyStringifyChecker<Ref<Func>>(Ref<Func>::New(FuncCall), TVMFFITypeIndex::kTVMFFIFunc, check);
-}
-
-TEST(Any_Stringify, Str) {
- auto check = [](const Any& v) -> void {
- std::string str = v.str()->c_str();
- EXPECT_EQ(str, "\"Hello World\"");
- };
- TestAnyStringifyChecker<Ref<Str>>(Ref<Str>::New("Hello World"), TVMFFITypeIndex::kTVMFFIStr,
- check);
-}
-
-} // namespace
diff --git a/ffi/tests/backup_cpp/test_any_view.cc b/ffi/tests/backup_cpp/test_any_view.cc
deleted file mode 100644
index 122c9ec368..0000000000
--- a/ffi/tests/backup_cpp/test_any_view.cc
+++ /dev/null
@@ -1,631 +0,0 @@
-#include <gtest/gtest.h>
-
-#include <tvm/ffi/ffi.hpp>
-
-namespace {
-using namespace tvm::ffi;
-
-template <typename SrcType, typename Checker>
-void TestAnyViewConstructor(Checker check, TVMFFITypeIndex expected_type_index,
- const SrcType& source) {
- AnyView v(source);
- EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
- EXPECT_EQ(v.ref_cnt, 0);
- check(&v, source);
-};
-
-int64_t FuncCall(int64_t x) { return x + 1; }
-
-std::vector<AnyView> AnyViewArrayFactory() {
- static const char* raw_str = "Hello (raw str)";
- static std::string std_str = "World (std::string)";
- static std::string ref_str = "Hello World (Ref<Str>)";
- static Ref<Object> obj = Ref<Object>::New();
- static Ref<Func> func = Ref<Func>::New(FuncCall);
- static Ref<Str> str = Ref<Str>::New(ref_str);
- return std::vector<AnyView>{
- AnyView(nullptr),
- AnyView(1),
- AnyView(2.5),
- AnyView(reinterpret_cast<void*>(FuncCall)),
- AnyView(DLDevice{kDLCPU, 0}),
- AnyView(DLDataType{kDLInt, 32, 1}),
- AnyView(raw_str),
- AnyView(obj),
- AnyView(func),
- AnyView(std_str), // TODO: disable AnyView(std::string&&)
- AnyView(str),
- };
-}
-
-template <typename SrcType>
-void TestAnyViewStringify(const SrcType& source, TVMFFITypeIndex expected_type_index,
- const std::string& expected) {
- AnyView v(source);
- EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
- EXPECT_EQ(v.str()->c_str(), expected);
-}
-
-template <typename SrcType, typename Checker>
-void TestAnyViewStringifyChecker(const SrcType& source, TVMFFITypeIndex expected_type_index,
- Checker check) {
- AnyView v(source);
- EXPECT_EQ(v.type_index, static_cast<int32_t>(expected_type_index));
- check(v);
-}
-
-void CheckAnyViewRefCnt(const TVMFFIAny* v) {
- if (v->type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
- EXPECT_EQ(v->v_obj->ref_cnt, 1);
- }
-}
-
-TEST(AnyView_Constructor_0_Default, Default) {
- AnyView v;
- EXPECT_EQ(v.type_index, 0);
- EXPECT_EQ(v.ref_cnt, 0);
- EXPECT_EQ(v.v_int64, 0);
-}
-
-TEST(AnyView_Constructor_1_AnyView, Copy) {
- AnyView v1(1);
- AnyView v2(v1);
- EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v1.v_int64, 1);
- EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v2.v_int64, 1);
-}
-
-TEST(AnyView_Constructor_1_AnyView, Move) {
- AnyView v1(1);
- AnyView v2(std::move(v1));
- EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone));
- EXPECT_EQ(v1.v_int64, 0);
- EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v2.v_int64, 1);
-}
-
-TEST(AnyView_Constructor_2_Any, Copy) {
- Any v1(1);
- AnyView v2(v1);
- EXPECT_EQ(v1.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v1.v_int64, 1);
- EXPECT_EQ(v2.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt));
- EXPECT_EQ(v2.v_int64, 1);
-}
-
-TEST(AnyView_Constructor_2_Any, Move) {
- // ---- The following behavior is disallowed ----
- // Any v1(1);
- // AnyView v2(std::move(v1));
-}
-
-TEST(AnyView_Constructor_3_Ref, Copy) {
- Ref<Object> obj = Ref<Object>::New();
- AnyView v(obj);
- const TVMFFIAny* v_obj = v.v_obj;
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
- EXPECT_EQ(v.ref_cnt, 0);
- EXPECT_EQ(v_obj, static_cast<const void*>(obj.get()));
- EXPECT_EQ(v_obj->ref_cnt, 1);
-}
-
-TEST(AnyView_Constructor_3_Ref, Move) {
- // The following behavior is disallowed
- // Ref<Object> obj = Ref<Object>::New();
- // AnyView v(std::move(obj));
-}
-
-TEST(AnyView_Constructor_4_TypeTraits, Integer) {
- auto check = [](TVMFFIAny* v, int64_t source) -> void { EXPECT_EQ(v->v_int64, source); };
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int8_t>(1));
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int16_t>(2));
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int32_t>(3));
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<int64_t>(4));
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint8_t>(1));
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint16_t>(2));
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint32_t>(3));
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIInt, static_cast<uint64_t>(4));
-}
-
-TEST(AnyView_Constructor_4_TypeTraits, Float) {
- auto check = [](TVMFFIAny* v, double source) -> void { EXPECT_EQ(v->v_float64, source); };
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(3));
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIFloat, static_cast<float>(4));
-}
-
-TEST(AnyView_Constructor_4_TypeTraits, Ptr) {
- int p = 4;
- auto check = [](TVMFFIAny* v, void* source) -> void { EXPECT_EQ(v->v_ptr, source); };
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFINone, static_cast<void*>(nullptr));
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIPtr, static_cast<void*>(&p));
-}
-
-TEST(AnyView_Constructor_4_TypeTraits, Device) {
- auto check = [](TVMFFIAny* v, const DLDevice& source) -> void {
- EXPECT_EQ(v->v_device.device_type, source.device_type);
- EXPECT_EQ(v->v_device.device_id, source.device_id);
- };
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIDevice, DLDevice{kDLCPU, 0});
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIDevice, DLDevice{kDLCUDA, 1});
-}
-
-TEST(AnyView_Constructor_4_TypeTraits, DataType) {
- auto check = [](TVMFFIAny* v, const DLDataType& source) -> void {
- EXPECT_EQ(v->v_dtype.code, source.code);
- EXPECT_EQ(v->v_dtype.bits, source.bits);
- EXPECT_EQ(v->v_dtype.lanes, source.lanes);
- };
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIDataType, DLDataType{kDLInt, 32, 1});
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIDataType, DLDataType{kDLUInt, 0, 0});
-}
-
-TEST(AnyView_Constructor_4_TypeTraits, RawStr) {
- auto check = [](TVMFFIAny* v, const char* source) -> void { EXPECT_EQ(v->v_str, source); };
- const char* empty = "";
- const char* hello = "hello";
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, empty);
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, hello);
-}
-
-TEST(AnyView_Constructor_4_TypeTraits, CharArray) {
- auto check = [](TVMFFIAny* v, const char* source) -> void { EXPECT_EQ(v->v_str, source); };
- const char empty[] = "";
- const char hello[] = "hello";
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, empty);
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, hello);
-}
-
-TEST(AnyView_Constructor_4_TypeTraits, StdString) {
- auto check = [](TVMFFIAny* v, const std::string& source) -> void {
- EXPECT_EQ(v->v_str, source.data());
- };
- std::string empty = "";
- std::string hello = "hello";
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, hello);
- TestAnyViewConstructor(check, TVMFFITypeIndex::kTVMFFIRawStr, empty);
-}
-
-TEST(AnyView_Constructor_5_Object_Ptr, Object) {
- Ref<Object> obj = Ref<Object>::New();
- AnyView v(obj.get());
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
- EXPECT_EQ(v.v_obj->ref_cnt, 1);
- EXPECT_EQ(v.v_obj, static_cast<void*>(obj.get()));
-}
-
-TEST(AnyView_Constructor_5_Object_Ptr, Func) {
- Ref<Func> func = Ref<Func>::New(FuncCall);
- AnyView v(func.get());
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
- EXPECT_EQ(v.v_obj->ref_cnt, 1);
- EXPECT_EQ(v.v_ptr, static_cast<void*>(func.get()));
-}
-
-TEST(AnyView_Constructor_5_Object_Ptr, Str) {
- Ref<Str> str = Ref<Str>::New("hello");
- AnyView v(str.get());
- EXPECT_EQ(v.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
- EXPECT_EQ(v.v_obj->ref_cnt, 1);
- EXPECT_EQ(v.v_obj, static_cast<void*>(str.get()));
-}
-
-TEST(AnyView_Converter_0_TypeTraits, Integer) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView& v : views) {
- auto convert = [&]() -> int64_t { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
- EXPECT_EQ(convert(), 1);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `int`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyViewRefCnt(&v);
- }
-}
-
-TEST(AnyView_Converter_0_TypeTraits, Float) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView& v : views) {
- auto convert = [&]() -> double { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt)) {
- EXPECT_EQ(convert(), 1.0);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFloat)) {
- EXPECT_EQ(convert(), 2.5);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `float`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyViewRefCnt(&v);
- }
-}
-
-TEST(AnyView_Converter_0_TypeTraits, Ptr) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView& v : views) {
- auto convert = [&]() -> void* { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- EXPECT_EQ(convert(), nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIPtr)) {
- EXPECT_EQ(convert(), reinterpret_cast<void*>(&FuncCall));
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
- EXPECT_EQ(convert(), v.v_str);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Ptr`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyViewRefCnt(&v);
- }
-}
-
-TEST(AnyView_Converter_0_TypeTraits, Device) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView& v : views) {
- auto convert = [&]() -> DLDevice { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDevice)) {
- EXPECT_EQ(convert().device_type, kDLCPU);
- EXPECT_EQ(convert().device_id, 0);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `Device`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyViewRefCnt(&v);
- }
-}
-
-TEST(AnyView_Converter_0_TypeTraits, DataType) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView& v : views) {
- auto convert = [&]() -> DLDataType { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDataType)) {
- EXPECT_EQ(convert().code, kDLInt);
- EXPECT_EQ(convert().bits, 32);
- EXPECT_EQ(convert().lanes, 1);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index) << "` to `dtype`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyViewRefCnt(&v);
- }
-}
-
-TEST(AnyView_Converter_0_TypeTraits, RawStr) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- int counter = 0;
- for (const AnyView& v : views) {
- auto convert = [&]() -> const char* { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
- counter += 1;
- EXPECT_LT(counter, 3);
- if (counter == 1) {
- EXPECT_STREQ(convert(), "Hello (raw str)");
- } else if (counter == 2) {
- EXPECT_STREQ(convert(), "World (std::string)");
- }
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- EXPECT_STREQ(convert(), "Hello World (Ref<Str>)");
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `const char *`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyViewRefCnt(&v);
- }
-}
-
-TEST(AnyView_Converter_0_TypeTraits, RawStrToStrStar_Fail) {
- AnyView v = "Hello";
- try {
- Str* v_str = v;
- (void)v_str;
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- EXPECT_STREQ(ex.what(), "Cannot convert from type `const char *` to `object.Str *`");
- }
-}
-
-TEST(AnyView_Converter_0_TypeTraits, RawStrToStrStar_WrithStorage) {
- Any storage;
- AnyView v = "Hello";
- Str* v_str = v.CastWithStorage<Str*>(&storage);
- EXPECT_STREQ(v_str->c_str(), "Hello");
-}
-
-TEST(AnyView_Converter_1_Any, Any) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView& view : views) {
- auto convert = [&]() -> Any { return view; };
- {
- Any ret = convert();
- EXPECT_EQ(view.ref_cnt, 0);
- if (view.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
- Str* str = ret;
- EXPECT_EQ(ret.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
- EXPECT_STREQ(str->c_str(), view.v_str);
- EXPECT_EQ(ret.ref_cnt, 0);
- } else {
- EXPECT_EQ(ret.type_index, view.type_index);
- EXPECT_EQ(ret.ref_cnt, 0);
- EXPECT_EQ(ret.v_obj, view.v_obj);
- }
- }
- CheckAnyViewRefCnt(&view);
- }
-}
-
-TEST(AnyView_Converter_2_Ref, Object) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView& v : views) {
- auto convert = [&]() -> Ref<Object> { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Ref<Object> ret = convert();
- EXPECT_EQ(ret.get(), nullptr);
- } else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
- Ref<Object> ret = convert();
- EXPECT_EQ(ret.get(), static_cast<void*>(v.v_obj));
- EXPECT_EQ(v.v_obj->ref_cnt, 2);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Object`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyViewRefCnt(&v);
- }
-}
-
-TEST(AnyView_Converter_2_Ref, Func) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView& v : views) {
- auto convert = [&]() -> Ref<Func> { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Ref<Func> ret = convert();
- EXPECT_EQ(ret.get(), nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
- Ref<Func> ret = convert();
- EXPECT_EQ(ret.get(), static_cast<void*>(v.v_obj));
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Func`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyViewRefCnt(&v);
- }
-}
-
-TEST(AnyView_Converter_2_Ref, Str) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView& v : views) {
- auto convert = [&]() -> Ref<Str> { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Ref<Str> ret = convert();
- EXPECT_EQ(ret.get(), nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- Ref<Str> ret = convert();
- EXPECT_STREQ(ret->c_str(), "Hello World (Ref<Str>)");
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
- Ref<Str> ret = convert();
- EXPECT_EQ(reinterpret_cast<TVMFFIStr*>(ret.get())->ref_cnt, 1);
- EXPECT_STREQ(ret->c_str(), v.v_str);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Str`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyViewRefCnt(&v);
- }
-}
-
-TEST(AnyView_Converter_3_Object_Ptr, Object) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView& v : views) {
- auto convert = [&]() -> Object* { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Object* ret = convert();
- EXPECT_EQ(ret, nullptr);
- } else if (v.type_index >= static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin)) {
- Object* ret = convert();
- EXPECT_EQ(ret, static_cast<void*>(v.v_obj));
- EXPECT_EQ(v.v_obj->ref_cnt, 1);
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Object *`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyViewRefCnt(&v);
- }
-}
-
-TEST(AnyView_Converter_3_Object_Ptr, Func) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView& v : views) {
- auto convert = [&]() -> Func* { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Func* ret = convert();
- EXPECT_EQ(ret, nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc)) {
- Func* ret = convert();
- EXPECT_EQ(ret, reinterpret_cast<Func*>(v.v_ptr));
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Func *`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyViewRefCnt(&v);
- }
-}
-
-TEST(AnyView_Converter_3_Object_Ptr, Str) {
- std::vector<AnyView> views = AnyViewArrayFactory();
- for (const AnyView& v : views) {
- auto convert = [&]() -> Str* { return v; };
- if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)) {
- Str* ret = convert();
- EXPECT_EQ(ret, nullptr);
- } else if (v.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- Str* ret = convert();
- EXPECT_STREQ(ret->c_str(), "Hello World (Ref<Str>)");
- } else {
- try {
- convert();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- std::ostringstream os;
- os << "Cannot convert from type `" << TypeIndex2TypeKey(v.type_index)
- << "` to `object.Str *`";
- EXPECT_EQ(ex.what(), os.str());
- }
- }
- CheckAnyViewRefCnt(&v);
- }
-}
-
-TEST(AnyView_Stringify, Integer) {
- TestAnyViewStringify<int8_t>(-13, TVMFFITypeIndex::kTVMFFIInt, "-13");
- TestAnyViewStringify<int16_t>(-5, TVMFFITypeIndex::kTVMFFIInt, "-5");
- TestAnyViewStringify<int32_t>(0, TVMFFITypeIndex::kTVMFFIInt, "0");
- TestAnyViewStringify<int64_t>(1, TVMFFITypeIndex::kTVMFFIInt, "1");
-}
-
-TEST(AnyView_Stringify, Float) {
- auto check = [](const AnyView& v) -> void {
- std::string str = v.str()->c_str();
- double f_str = std::stod(str);
- double f_src = v.v_float64;
- EXPECT_NEAR(f_src, f_str, 1e-5);
- };
- TestAnyViewStringifyChecker<float>(float(-3.14), TVMFFITypeIndex::kTVMFFIFloat, check);
- TestAnyViewStringifyChecker<double>(0.0, TVMFFITypeIndex::kTVMFFIFloat, check);
-}
-
-TEST(AnyView_Stringify, Ptr) {
- auto check = [](const AnyView& v) -> void {
- std::string str = v.str()->c_str();
- EXPECT_GT(str.size(), 2);
- };
- TestAnyViewStringify<void*>(nullptr, TVMFFITypeIndex::kTVMFFINone, "None");
- TestAnyViewStringifyChecker<void*>(reinterpret_cast<void*>(FuncCall), TVMFFITypeIndex::kTVMFFIPtr,
- check);
-}
-
-TEST(AnyView_Stringify, Device) {
- TestAnyViewStringify<DLDevice>(DLDevice{kDLCPU, 0}, TVMFFITypeIndex::kTVMFFIDevice, "cpu:0");
- TestAnyViewStringify<DLDevice>(DLDevice{kDLCUDA, 1}, TVMFFITypeIndex::kTVMFFIDevice, "cuda:1");
-}
-
-TEST(AnyView_Stringify, DataType) {
- TestAnyViewStringify<DLDataType>(DLDataType{kDLInt, 32, 1}, TVMFFITypeIndex::kTVMFFIDataType,
- "int32");
- TestAnyViewStringify<DLDataType>(DLDataType{kDLUInt, 1, 1}, TVMFFITypeIndex::kTVMFFIDataType,
- "bool");
- TestAnyViewStringify<DLDataType>(DLDataType{kDLOpaqueHandle, 0, 0},
- TVMFFITypeIndex::kTVMFFIDataType, "void");
- TestAnyViewStringify<DLDataType>(DLDataType{kDLFloat, 8, 4}, TVMFFITypeIndex::kTVMFFIDataType,
- "float8x4");
-}
-
-TEST(AnyView_Stringify, RawStr) {
- TestAnyViewStringify<const char*>("Hello", TVMFFITypeIndex::kTVMFFIRawStr, "\"Hello\"");
- TestAnyViewStringify<char[6]>("Hello", TVMFFITypeIndex::kTVMFFIRawStr, "\"Hello\"");
- TestAnyViewStringify<const char[6]>("Hello", TVMFFITypeIndex::kTVMFFIRawStr, "\"Hello\"");
-}
-
-TEST(AnyView_Stringify, Object) {
- auto check = [](const AnyView& v) -> void {
- std::string expected_prefix = "object.Object@0";
- int n = static_cast<int>(expected_prefix.size());
- std::string str = v.str()->c_str();
- EXPECT_GT(str.size(), n);
- EXPECT_EQ(str.substr(0, n), expected_prefix);
- };
- TestAnyViewStringifyChecker<Ref<Object>>(Ref<Object>::New(), TVMFFITypeIndex::kTVMFFIObject,
- check);
-}
-
-TEST(AnyView_Stringify, Func) {
- auto check = [](const AnyView& v) -> void {
- std::string expected_prefix = "object.Func@0";
- int n = static_cast<int>(expected_prefix.size());
- std::string str = v.str()->c_str();
- EXPECT_GT(str.size(), n);
- EXPECT_EQ(str.substr(0, n), expected_prefix);
- };
- TestAnyViewStringifyChecker<Ref<Func>>(Ref<Func>::New(FuncCall), TVMFFITypeIndex::kTVMFFIFunc,
- check);
-}
-
-TEST(AnyView_Stringify, Str) {
- auto check = [](const AnyView& v) -> void {
- std::string str = v.str()->c_str();
- EXPECT_EQ(str, "\"Hello World\"");
- };
- TestAnyViewStringifyChecker<Ref<Str>>(Ref<Str>::New("Hello World"), TVMFFITypeIndex::kTVMFFIStr,
- check);
-}
-
-} // namespace
diff --git a/ffi/tests/backup_cpp/test_dict.cc b/ffi/tests/backup_cpp/test_dict.cc
deleted file mode 100644
index 53c4452401..0000000000
--- a/ffi/tests/backup_cpp/test_dict.cc
+++ /dev/null
@@ -1,184 +0,0 @@
-#include <gtest/gtest.h>
-
-#include <tvm/ffi/ffi.hpp>
-#include <unordered_set>
-
-namespace {
-
-using namespace tvm::ffi;
-
-bool DTypeEqual(DLDataType a, DLDataType b) {
- return a.code == b.code && a.bits == b.bits && a.lanes == b.lanes;
-}
-// bool DeviceEqual(DLDevice a, DLDevice b) {
-// return a.device_type == b.device_type && a.device_id == b.device_id;
-// }
-
-TEST(Dict_Construtor, Default) {
- Ref<Dict> dict;
- ASSERT_EQ(dict.size(), 0);
- TVMFFIDict* dict_ptr = reinterpret_cast<TVMFFIDict*>(dict.get());
- EXPECT_EQ(dict_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDict));
- EXPECT_EQ(dict_ptr->ref_cnt, 1);
- EXPECT_NE(dict_ptr->deleter, nullptr);
- EXPECT_EQ(dict_ptr->size, 0);
- EXPECT_EQ(dict_ptr->capacity, 0);
-}
-
-TEST(Dict_Construtor, InitializerList) {
- Ref<Dict> dict{{"key1", 1}, {"key2", "value2"}, {3, 4}};
- EXPECT_EQ(dict.size(), 3);
- EXPECT_EQ(int(dict["key1"]), 1);
- EXPECT_EQ(std::string(dict["key2"]), "value2");
- EXPECT_EQ(int(dict[3]), 4);
-
- bool found[3] = {false, false, false};
- for (const auto& kv : dict) {
- if (AnyEqual()(kv.first, Any("key1"))) {
- found[0] = true;
- EXPECT_EQ(int(kv.second), 1);
- } else if (AnyEqual()(kv.first, Any("key2"))) {
- found[1] = true;
- EXPECT_EQ(std::string(kv.second), "value2");
- } else if (AnyEqual()(kv.first, Any(3))) {
- found[2] = true;
- EXPECT_EQ(int(kv.second), 4);
- } else {
- FAIL() << "Unexpected key: " << kv.first;
- }
- }
- EXPECT_TRUE(found[0]);
- EXPECT_TRUE(found[1]);
- EXPECT_TRUE(found[2]);
-}
-
-TEST(Dict_Insert, New) {
- int64_t integer = 100;
- double fp = 1.0;
- std::string str = "Hi";
- DLDataType dtype{kDLInt, 32, 1};
- DLDevice device{kDLCPU, 0};
- Ref<Object> obj = Ref<Object>::New();
- Ref<Object> null_obj{nullptr};
- Ref<Dict> dict{{integer, fp}, {str, dtype}, {null_obj, 0}};
- dict[device] = null_obj;
- EXPECT_EQ(dict.size(), 4);
- EXPECT_DOUBLE_EQ(double(dict[integer]), fp);
- EXPECT_PRED2(DTypeEqual, DLDataType(dict[str]), dtype);
- EXPECT_EQ(int(dict[null_obj]), 0);
- EXPECT_EQ((Object*)(dict[device]), nullptr);
-}
-
-TEST(Dict_Insert, Override) {
- Ref<Dict> dict{{"key1", 1}, {"key2", "value2"}, {3, 4}};
- EXPECT_EQ(dict.size(), 3);
- dict["key1"] = 2;
- dict["key2"] = "new_value";
- dict[3] = 5;
- EXPECT_EQ(dict.size(), 3);
- EXPECT_EQ(int(dict["key1"]), 2);
- EXPECT_EQ(std::string(dict["key2"]), "new_value");
- EXPECT_EQ(int(dict[3]), 5);
-}
-
-TEST(Dict_At, Found) {
- int64_t integer = 100;
- double fp = 1.0;
- std::string str = "Hi";
- DLDataType dtype{kDLInt, 32, 1};
- Ref<Object> obj = Ref<Object>::New();
- Ref<Object> null_obj{nullptr};
- Ref<Dict> dict{{integer, fp}, {str, dtype}, {null_obj, 0}};
- EXPECT_DOUBLE_EQ(double(dict.at(integer)), fp);
- EXPECT_PRED2(DTypeEqual, DLDataType(dict.at(str)), dtype);
- EXPECT_EQ(int(dict.at(null_obj)), 0);
-}
-
-TEST(Dict_At, NotFound) {
- Ref<Dict> dict{{"key1", 1}, {"key2", "value2"}, {3, 4}};
- try {
- dict.at("key3");
- FAIL() << "Expected TVMError";
- } catch (const TVMError& e) {
- }
-}
-
-TEST(Dict_ReHash, POD) {
- Ref<Dict> dict;
- for (int j = 0; j < 1000; ++j) {
- dict[j] = j;
- }
- EXPECT_EQ(dict.size(), 1000);
- std::unordered_set<int64_t> keys;
- for (auto& kv : dict) {
- int64_t key = kv.first;
- int64_t value = kv.second;
- EXPECT_EQ(key, value);
- EXPECT_FALSE(keys.count(key));
- EXPECT_EQ(key, value);
- EXPECT_TRUE(0 <= key && key < 1000);
- }
- EXPECT_EQ(dict.size(), 1000);
-}
-
-TEST(Dict_ReHash, Object) {
- std::vector<Ref<Object>> objs;
- std::unordered_map<Object*, int64_t> obj_map;
- for (int j = 0; j < 1000; ++j) {
- objs.push_back(Ref<Object>::New());
- obj_map[objs[j].get()] = j;
- }
- Ref<Dict> dict;
- for (int j = 0; j < 1000; ++j) {
- dict[objs[j]] = j;
- }
- EXPECT_EQ(dict.size(), 1000);
- std::unordered_set<Object*> keys;
- for (auto& kv : dict) {
- Ref<Object> key = kv.first;
- int64_t value = kv.second;
- keys.insert(key.get());
- EXPECT_EQ(value, obj_map[key.get()]);
- }
- EXPECT_EQ(dict.size(), 1000);
-}
-
-TEST(Dict_Erase, POD) {
- Ref<Dict> dict;
- for (int j = 0; j < 1000; ++j) {
- dict[j] = j;
- }
- EXPECT_EQ(dict.size(), 1000);
- for (int j = 0; j < 1000; ++j) {
- dict.erase(j);
- EXPECT_EQ(dict.size(), 1000 - j - 1);
- }
- for (int j = 0; j < 1000; ++j) {
- dict[j] = j;
- EXPECT_EQ(dict.size(), j + 1);
- }
-}
-
-TEST(Dict_Erase, Object) {
- std::vector<Ref<Object>> objs;
- std::unordered_map<Object*, int64_t> obj_map;
- for (int j = 0; j < 1000; ++j) {
- objs.push_back(Ref<Object>::New());
- obj_map[objs[j].get()] = j;
- }
- Ref<Dict> dict;
- for (int j = 0; j < 1000; ++j) {
- dict[objs[j]] = j;
- }
- EXPECT_EQ(dict.size(), 1000);
- for (int j = 0; j < 1000; ++j) {
- dict.erase(objs[j]);
- EXPECT_EQ(dict.size(), 1000 - j - 1);
- }
- for (int j = 0; j < 1000; ++j) {
- dict[objs[j]] = j;
- EXPECT_EQ(dict.size(), j + 1);
- }
-}
-
-} // namespace
diff --git a/ffi/tests/backup_cpp/test_func.cc b/ffi/tests/backup_cpp/test_func.cc
deleted file mode 100644
index 0790692bb9..0000000000
--- a/ffi/tests/backup_cpp/test_func.cc
+++ /dev/null
@@ -1,213 +0,0 @@
-#include <gtest/gtest.h>
-
-#include <tvm/ffi/ffi.hpp>
-
-namespace {
-using namespace tvm::ffi;
-
-const char* c_str_raw = "Hello";
-
-double func_unpacked_0(int64_t a, double b, const char* c, const double& d) {
- EXPECT_STREQ(c, c_str_raw);
- return a + b + d;
-}
-
-void func_unpacked_1(DLDataType dtype, DLDevice device, std::string str) {
- (void)dtype;
- (void)device;
- (void)str;
-}
-
-void func_packed_0(int num_args, const AnyView*, Any* ret) { *ret = num_args; }
-
-template <enum TVMFFITypeIndex type_index>
-void func_unpacked_anyview_arg(AnyView a) {
- EXPECT_EQ(a.type_index, static_cast<int32_t>(type_index));
-}
-template <enum TVMFFITypeIndex type_index>
-void func_unpacked_any_arg(Any a) {
- EXPECT_EQ(a.type_index, static_cast<int32_t>(type_index));
-}
-AnyView func_unpacked_anyview_ret() { return AnyView(1); }
-Any func_unpacked_any_ret() { return Any(1); }
-
-std::string func_unpacked_str_obj(Str* str, const char* str_2) {
- EXPECT_EQ(reinterpret_cast<TVMFFIStr*>(str)->ref_cnt, 1);
- EXPECT_STREQ(str->c_str(), str_2);
- return str->c_str();
-}
-
-TEST(Func_Signature, 0) {
- EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_0)>::Sig(),
- "(0: int, 1: float, 2: const char *, 3: float) -> float");
-}
-
-TEST(Func_Signature, 1) {
- EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_1)>::Sig(),
- "(0: dtype, 1: Device, 2: str) -> void");
-}
-
-TEST(Func_Signature, AnyView_Arg) {
- EXPECT_EQ(
- details::FuncFunctor<decltype(func_unpacked_anyview_arg<TVMFFITypeIndex::kTVMFFIInt>)>::Sig(),
- "(0: AnyView) -> void");
-}
-
-TEST(Func_Signature, AnyView_Ret) {
- EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_anyview_ret)>::Sig(), "() -> AnyView");
-}
-
-TEST(Func_Signature, Any_Arg) {
- EXPECT_EQ(
- details::FuncFunctor<decltype(func_unpacked_any_arg<TVMFFITypeIndex::kTVMFFIInt>)>::Sig(),
- "(0: Any) -> void");
-}
-
-TEST(Func_Signature, Any_Ret) {
- EXPECT_EQ(details::FuncFunctor<decltype(func_unpacked_any_ret)>::Sig(), "() -> Any");
-}
-
-TEST(Func_Unpacked_Invoke, Func0_RawStr) {
- Ref<Func> func = Ref<Func>::New(func_unpacked_0);
- double ret = func(1, 2, c_str_raw, 4);
- EXPECT_DOUBLE_EQ(ret, 7);
- double ret2 = func(1, 2, std::string("Hello"), 4);
- EXPECT_DOUBLE_EQ(ret2, 7);
-}
-
-TEST(Func_Unpacked_Invoke, Func0_StdString_Move) {
- std::string str = c_str_raw;
- Ref<Func> func = Ref<Func>::New(func_unpacked_0);
- double ret = func(1, 2, std::move(str), 4);
- EXPECT_DOUBLE_EQ(ret, 7);
-}
-
-TEST(Func_Unpacked_Invoke, Func0_StdString_Copy) {
- std::string str = c_str_raw;
- Ref<Func> func = Ref<Func>::New(func_unpacked_0);
- double ret = func(1, 2, str, 4);
- EXPECT_DOUBLE_EQ(ret, 7);
- EXPECT_EQ(str, c_str_raw);
-}
-
-TEST(Func_Unpacked_Invoke, Func1) {
- Ref<Func> func = Ref<Func>::New(func_unpacked_1);
- func(DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, "Hello");
-}
-
-TEST(Func_Unpacked_Invoke, AnyView_Arg) {
- Ref<Func> func = Ref<Func>::New(func_unpacked_anyview_arg<TVMFFITypeIndex::kTVMFFIInt>);
- func(1);
-}
-
-TEST(Func_Unpacked_Invoke, AnyView_Ret) {
- Ref<Func> func = Ref<Func>::New(func_unpacked_anyview_ret);
- int ret = func();
- EXPECT_EQ(ret, 1);
-}
-
-TEST(Func_Unpacked_Invoke, Any_Arg) {
- Ref<Func> func = Ref<Func>::New(func_unpacked_any_arg<TVMFFITypeIndex::kTVMFFIInt>);
- func(1);
-}
-
-TEST(Func_Unpacked_Invoke, Any_Ret) {
- Ref<Func> func = Ref<Func>::New(func_unpacked_any_ret);
- int ret = func();
- EXPECT_EQ(ret, 1);
-}
-
-TEST(Func_Unpacked_Invoke, StrObj) {
- Ref<Func> func = Ref<Func>::New(func_unpacked_str_obj);
- std::string ret = func("Hello", "Hello");
- EXPECT_EQ(ret, "Hello");
-}
-
-TEST(Func_Packed_Invoke, 0) {
- Ref<Func> func = Ref<Func>::New(func_packed_0);
- int ret = func();
- EXPECT_EQ(ret, 0);
-}
-
-TEST(Func_Packed_Invoke, 1) {
- Ref<Func> func = Ref<Func>::New(func_packed_0);
- int ret = func(1.0);
- EXPECT_EQ(ret, 1);
-}
-
-TEST(Func_Packed_Invoke, 2) {
- Ref<Func> func = Ref<Func>::New(func_packed_0);
- int ret = func(1.0, "test");
- EXPECT_EQ(ret, 2);
-}
-
-TEST(Func_Packed_Invoke, 4) {
- Ref<Func> func = Ref<Func>::New(func_packed_0);
- int ret = func(1.0, "test", DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0});
- EXPECT_EQ(ret, 4);
-}
-
-TEST(Func_Unpacked_Invoke_TypeError, TypeMismatch_0) {
- Ref<Func> func = Ref<Func>::New(func_unpacked_0);
- try {
- func(1.0, 2, c_str_raw, 4);
- FAIL() << "No execption thrown";
- } catch (TVMError& ex) {
- EXPECT_STREQ(ex.what(),
- "Mismatched type on argument #0 when calling: "
- "`(0: int, 1: float, 2: const char *, 3: float) -> float`. "
- "Expected `int` but got `float`");
- }
-}
-
-TEST(Func_Unpacked_Invoke_TypeError, TypeMismatch_1) {
- Ref<Func> func = Ref<Func>::New(func_unpacked_1);
- try {
- func(DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, 1);
- FAIL() << "No execption thrown";
- } catch (TVMError& ex) {
- EXPECT_STREQ(ex.what(),
- "Mismatched type on argument #2 when calling: "
- "`(0: dtype, 1: Device, 2: str) -> void`. "
- "Expected `str` but got `int`");
- }
-}
-
-TEST(Func_Unpacked_Invoke_TypeError, ArgCountMismatch_0) {
- Ref<Func> func = Ref<Func>::New(func_unpacked_0);
- try {
- func(1, 2, c_str_raw);
- FAIL() << "No execption thrown";
- } catch (TVMError& ex) {
- EXPECT_STREQ(ex.what(),
- "Mismatched number of arguments when calling: "
- "`(0: int, 1: float, 2: const char *, 3: float) -> float`. "
- "Expected 4 but got 3 arguments");
- }
-}
-
-TEST(Func_Unpacked_Invoke_TypeError, ArgCountMismatch_1) {
- Ref<Func> func = Ref<Func>::New(func_unpacked_1);
- try {
- func(DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0});
- FAIL() << "No execption thrown";
- } catch (TVMError& ex) {
- EXPECT_STREQ(ex.what(),
- "Mismatched number of arguments when calling: "
- "`(0: dtype, 1: Device, 2: str) -> void`. "
- "Expected 3 but got 2 arguments");
- }
-}
-
-TEST(Func_Unpacked_Invoke_TypeError, ReturnTypeMismatch_0) {
- Ref<Func> func = Ref<Func>::New(func_unpacked_1);
- try {
- int ret = func(DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, "Hello");
- (void)ret;
- FAIL() << "No execption thrown";
- } catch (TVMError& ex) {
- EXPECT_STREQ(ex.what(), "Cannot convert from type `None` to `int`");
- }
-}
-
-} // namespace
diff --git a/ffi/tests/backup_cpp/test_list.cc b/ffi/tests/backup_cpp/test_list.cc
deleted file mode 100644
index 06b9d6a483..0000000000
--- a/ffi/tests/backup_cpp/test_list.cc
+++ /dev/null
@@ -1,462 +0,0 @@
-#include <gtest/gtest.h>
-
-#include <tvm/ffi/ffi.hpp>
-
-namespace {
-
-using namespace tvm::ffi;
-
-bool DTypeEqual(DLDataType a, DLDataType b) {
- return a.code == b.code && a.bits == b.bits && a.lanes == b.lanes;
-}
-bool DeviceEqual(DLDevice a, DLDevice b) {
- return a.device_type == b.device_type && a.device_id == b.device_id;
-}
-
-void TestSizeCapacityClear(Ref<List>* list, int64_t size, int64_t capacity) {
- EXPECT_EQ(list->size(), size);
- EXPECT_EQ(list->capacity(), capacity);
- EXPECT_EQ(list->empty(), size == 0);
- list->clear();
- EXPECT_EQ(list->size(), 0);
- EXPECT_EQ(list->capacity(), capacity);
- EXPECT_EQ(list->empty(), true);
-}
-
-TEST(List_Constructor, Default) {
- Ref<List> list = Ref<List>::New();
- TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
- ASSERT_NE(list_ptr, nullptr);
- EXPECT_EQ(list_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIList));
- EXPECT_EQ(list_ptr->ref_cnt, 1);
- EXPECT_NE(list_ptr->deleter, nullptr);
- EXPECT_EQ(list_ptr->list_capacity, 0);
- EXPECT_EQ(list_ptr->list_length, 0);
- EXPECT_EQ(list_ptr->pool_capacity, 0);
- EXPECT_EQ(list_ptr->pool_length, 0);
- TestSizeCapacityClear(&list, 0, 0);
-}
-
-TEST(List_Constructor, InitializerList) {
- Ref<List> list1{
- 100, 1.0f, "Hi", DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, Ref<Object>::New(),
- Ref<Object>()};
- Ref<List> list2 = {
- 100, 1.0f, "Hi", DLDataType{kDLInt, 32, 1}, DLDevice{kDLCPU, 0}, Ref<Object>::New(),
- Ref<Object>()};
-
- auto test = [](Ref<List>* src) {
- auto* list_ptr = reinterpret_cast<const TVMFFIList*>(src->get());
- ASSERT_NE(list_ptr, nullptr);
- EXPECT_EQ(list_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIList));
- EXPECT_EQ(list_ptr->ref_cnt, 1);
- EXPECT_NE(list_ptr->deleter, nullptr);
- EXPECT_EQ(list_ptr->list_capacity, 7);
- EXPECT_EQ(list_ptr->list_length, 7);
- EXPECT_EQ(list_ptr->pool_capacity, 7);
- EXPECT_EQ(list_ptr->pool_length, 4); // string is not in the POD pool
- EXPECT_EQ(src->size(), 7);
- EXPECT_EQ(src->capacity(), 7);
- EXPECT_EQ(src->empty(), false);
- TestSizeCapacityClear(src, 7, 7);
- };
- test(&list1);
- test(&list2);
-}
-
-TEST(List_PushBack, POD) {
- Ref<List> list;
- ASSERT_NE(list.get(), nullptr);
- list.push_back(100);
- list.push_back(1.0f);
- TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
- ASSERT_NE(list_ptr, nullptr);
- EXPECT_EQ(list_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIList));
- EXPECT_EQ(list_ptr->ref_cnt, 1);
- EXPECT_NE(list_ptr->deleter, nullptr);
- EXPECT_EQ(list_ptr->list_capacity, List::kMinCapacity);
- EXPECT_EQ(list_ptr->list_length, 2);
- EXPECT_EQ(list_ptr->pool_capacity, List::kMinCapacity);
- EXPECT_EQ(list_ptr->pool_length, 2);
- EXPECT_EQ(int32_t(list[0]), 100);
- EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
- TestSizeCapacityClear(&list, 2, List::kMinCapacity);
-}
-
-TEST(List_PushBack, Obj) {
- Ref<List> list;
- Ref<Object> obj1 = Ref<Object>::New();
- Ref<Object> obj2 = Ref<Object>::New();
- list.push_back(obj1);
- list.push_back(obj2);
- TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
- ASSERT_NE(list_ptr, nullptr);
- EXPECT_EQ(list_ptr->type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIList));
- EXPECT_EQ(list_ptr->ref_cnt, 1);
- EXPECT_NE(list_ptr->deleter, nullptr);
- EXPECT_EQ(list_ptr->list_capacity, List::kMinCapacity);
- EXPECT_EQ(list_ptr->list_length, 2);
- EXPECT_EQ(list_ptr->pool_capacity, 0);
- EXPECT_EQ(list_ptr->pool_length, 0);
- EXPECT_EQ((Object*)(list[0]), obj1.get());
- EXPECT_EQ((Object*)(list[1]), obj2.get());
- TestSizeCapacityClear(&list, 2, List::kMinCapacity);
-}
-
-TEST(List_PushBack, Heterogeneous) {
- constexpr int n = 128;
- constexpr int k = 8;
- constexpr int expected_size = n * k;
- constexpr int expected_capacity = 1024;
- constexpr int expected_pool_capacity = 1024;
- constexpr int expected_pool_length = 512;
- int64_t integer = 100;
- double fp = 1.0;
- std::string str = "Hi";
- DLDataType dtype{kDLInt, 32, 1};
- DLDevice device{kDLCPU, 0};
- Ref<Object> obj = Ref<Object>::New();
- Ref<Object> null_obj{nullptr};
- std::string long_str(1024, 'a');
-
- Ref<List> list = Ref<List>::New();
- {
- std::string long_str_copy(1024, 'a');
- for (int i = 0; i < n; ++i) {
- list.push_back(integer);
- list.push_back(fp);
- list.push_back(str);
- list.push_back(dtype);
- list.push_back(device);
- list.push_back(obj);
- list.push_back(null_obj);
- list.push_back(long_str_copy);
- }
- }
- for (int i = 0; i < n; ++i) {
- int64_t i_0 = list[i * k];
- double i_1 = list[i * k + 1];
- std::string i_2 = list[i * k + 2];
- DLDataType i_3 = list[i * k + 3];
- DLDevice i_4 = list[i * k + 4];
- Object* i_5 = list[i * k + 5];
- Object* i_6 = list[i * k + 6];
- const char* i_7 = list[i * k + 7];
- EXPECT_EQ(i_0, integer);
- EXPECT_DOUBLE_EQ(i_1, fp);
- EXPECT_EQ(i_2, str);
- EXPECT_PRED2(DTypeEqual, i_3, dtype);
- EXPECT_PRED2(DeviceEqual, i_4, device);
- EXPECT_EQ(i_5, obj.get());
- EXPECT_EQ(i_6, nullptr);
- EXPECT_STREQ(i_7, long_str.c_str());
- }
- auto* list_ptr = reinterpret_cast<const TVMFFIList*>(list.get());
- EXPECT_EQ(list_ptr->list_capacity, expected_capacity);
- EXPECT_EQ(list_ptr->list_length, expected_size);
- EXPECT_EQ(list_ptr->pool_capacity, expected_pool_capacity);
- EXPECT_EQ(list_ptr->pool_length, expected_pool_length);
-}
-
-TEST(List_Insert, Once) {
- Ref<List> values = {100,
- 1.0,
- "Hi", //
- DLDataType{kDLInt, 32, 1},
- DLDevice{kDLCPU, 0},
- Ref<Object>::New(),
- Ref<Object>(),
- std::string(1024, 'a')};
- int n = values.size();
- for (int pos = 0; pos <= n; ++pos) {
- for (AnyView data : values) {
- // Test: insert at `pos` with value `data`
- Ref<List> list(values.begin(), values.end());
- list.insert(pos, data);
- auto test = [](AnyView expected, AnyView actual) {
- EXPECT_EQ(expected.type_index, actual.type_index);
- EXPECT_EQ(expected.v_int64, actual.v_int64);
- };
- for (int i = 0; i < pos; ++i) {
- test(values[i], list[i]);
- }
- for (int i = pos; i < n; ++i) {
- test(values[i], list[i + 1]);
- }
- test(data, list[pos]);
- }
- }
-}
-
-TEST(List_Insert, Error_0) {
- Ref<List> list = {100, 1.0, "Hi"};
- try {
- list.insert(-1, 1.0);
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- EXPECT_STREQ(ex.what(), "Indexing `-1` of a list of size 3");
- }
-}
-
-TEST(List_Insert, Error_1) {
- Ref<List> list = {100, 1.0, "Hi"};
- try {
- list.insert(4, 1.0);
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- EXPECT_STREQ(ex.what(), "Indexing `4` of a list of size 3");
- }
-}
-
-TEST(List_Resize, Shrink) {
- Ref<List> list = {100, 1.0, "Hi"};
- list.resize(2);
- EXPECT_EQ(list.size(), 2);
- EXPECT_EQ(list.capacity(), 3);
- EXPECT_EQ(int32_t(list[0]), 100);
- EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
-}
-
-TEST(List_Resize, Expand) {
- Ref<List> list = {100, 1.0, "Hi"};
- list.resize(4);
- EXPECT_EQ(list.size(), 4);
- EXPECT_EQ(list.capacity(), 6);
- EXPECT_EQ(int32_t(list[0]), 100);
- EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
- EXPECT_STREQ(list[2], "Hi");
- EXPECT_EQ(list[3].operator void*(), nullptr);
-}
-
-TEST(List_Reserve, Shrink) {
- Ref<List> list = {100, 1.0, "Hi"};
- list.reserve(2);
- EXPECT_EQ(list.size(), 3);
- EXPECT_EQ(list.capacity(), 3);
- EXPECT_EQ(int32_t(list[0]), 100);
- EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
- EXPECT_STREQ(list[2], "Hi");
-}
-
-TEST(List_Reserve, Expand) {
- Ref<List> list = {100, 1.0, "Hi"};
- list.reserve(4);
- EXPECT_EQ(list.size(), 3);
- EXPECT_EQ(list.capacity(), 6);
- EXPECT_EQ(int32_t(list[0]), 100);
- EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
- EXPECT_STREQ(list[2], "Hi");
-}
-
-TEST(List_SetItem, PodToPod) {
- Ref<List> list = {100, 1.0, "Hi"};
- for (int i = 0; i < 16; ++i) {
- list[1] = i;
- EXPECT_EQ(list.size(), 3);
- EXPECT_EQ(list.capacity(), 3);
- EXPECT_EQ(int32_t(list[0]), 100);
- EXPECT_EQ(int32_t(list[1]), i);
- EXPECT_STREQ(list[2], "Hi");
- }
- EXPECT_EQ(list.size(), 3);
- EXPECT_EQ(list.capacity(), 3);
- TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
- EXPECT_EQ(list_ptr->list_capacity, 3);
- EXPECT_EQ(list_ptr->list_length, 3);
- EXPECT_EQ(list_ptr->pool_capacity, 24);
- EXPECT_EQ(list_ptr->pool_length, 3);
-}
-
-TEST(List_SetItem, ObjToPod) {
- Ref<List> list = {100, 1.0, "Hi"}; //
- for (int i = 0; i < 16; ++i) {
- list[2] = i;
- EXPECT_EQ(list.size(), 3);
- EXPECT_EQ(list.capacity(), 3);
- EXPECT_EQ(int32_t(list[0]), 100);
- EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
- EXPECT_EQ(int32_t(list[2]), i);
- }
- EXPECT_EQ(list.size(), 3);
- EXPECT_EQ(list.capacity(), 3);
- TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
- EXPECT_EQ(list_ptr->list_capacity, 3);
- EXPECT_EQ(list_ptr->list_length, 3);
- EXPECT_EQ(list_ptr->pool_capacity, 24);
- EXPECT_EQ(list_ptr->pool_length, 6);
-}
-
-TEST(List_SetItem, PodToObj) {
- Ref<List> list = {100, 1.0, "Hi"};
- for (int i = 0; i < 1; ++i) {
- Ref<Object> obj = Ref<Object>::New();
- list[0] = obj;
- EXPECT_EQ(list.size(), 3);
- EXPECT_EQ(list.capacity(), 3);
- EXPECT_EQ((Object*)(list[0]), obj.get());
- EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
- EXPECT_STREQ(list[2], "Hi");
- }
- EXPECT_EQ(list.size(), 3);
- EXPECT_EQ(list.capacity(), 3);
- TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
- EXPECT_EQ(list_ptr->list_capacity, 3);
- EXPECT_EQ(list_ptr->list_length, 3);
- EXPECT_EQ(list_ptr->pool_capacity, 3);
- EXPECT_EQ(list_ptr->pool_length, 2);
-}
-
-TEST(List_SetItem, ObjToObj) {
- Ref<List> list = {100, 1.0, "Hi"};
- for (int i = 0; i < 1; ++i) {
- Ref<Object> obj = Ref<Object>::New();
- list[2] = obj;
- EXPECT_EQ(list.size(), 3);
- EXPECT_EQ(list.capacity(), 3);
- EXPECT_EQ(int32_t(list[0]), 100);
- EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
- EXPECT_EQ((Object*)(list[2]), obj.get());
- }
- EXPECT_EQ(list.size(), 3);
- EXPECT_EQ(list.capacity(), 3);
- TVMFFIList* list_ptr = reinterpret_cast<TVMFFIList*>(list.get());
- EXPECT_EQ(list_ptr->list_capacity, 3);
- EXPECT_EQ(list_ptr->list_length, 3);
- EXPECT_EQ(list_ptr->pool_capacity, 3);
- EXPECT_EQ(list_ptr->pool_length, 2);
-}
-
-TEST(List_PopBack, Heterogeneous) {
- int64_t integer = 100;
- double fp = 1.0;
- std::string str = "Hi";
- DLDataType dtype{kDLInt, 32, 1};
- DLDevice device{kDLCPU, 0};
- Ref<Object> obj = Ref<Object>::New();
- Ref<Object> null_obj{nullptr};
- Ref<List> list{integer, fp, str, dtype, device, obj, null_obj};
- int n = static_cast<int32_t>(list.size());
- for (int i = 0; i < n; ++i) {
- list.pop_back();
- EXPECT_EQ(list.size(), n - 1 - i);
- EXPECT_EQ(list.capacity(), n);
- int m = static_cast<int32_t>(list.size());
- if (m > 0) {
- EXPECT_EQ(int32_t(list[0]), integer);
- }
- if (m > 1) {
- EXPECT_DOUBLE_EQ(double(list[1]), fp);
- }
- if (m > 2) {
- EXPECT_STREQ(list[2], str.c_str());
- }
- if (m > 3) {
- EXPECT_PRED2(DTypeEqual, DLDataType(list[3]), dtype);
- }
- if (m > 4) {
- EXPECT_PRED2(DeviceEqual, DLDevice(list[4]), device);
- }
- if (m > 5) {
- EXPECT_EQ((Object*)(list[5]), obj.get());
- }
- if (m > 6) {
- EXPECT_EQ((Object*)(list[6]), nullptr);
- }
- }
- EXPECT_EQ(list.size(), 0);
- EXPECT_EQ(list.capacity(), n);
- EXPECT_EQ(list.empty(), true);
- EXPECT_EQ(list.begin(), list.end());
- try {
- list.pop_back();
- FAIL() << "No exception thrown";
- } catch (TVMError& ex) {
- EXPECT_STREQ(ex.what(), "Indexing `-1` of a list of size 0");
- }
-}
-
-TEST(List_Erase, Front) {
- int64_t integer = 100;
- double fp = 1.0;
- std::string str = "Hi";
- DLDataType dtype{kDLInt, 32, 1};
- DLDevice device{kDLCPU, 0};
- Ref<Object> obj = Ref<Object>::New();
- Ref<Object> null_obj{nullptr};
- Ref<List> list{integer, fp, str, dtype, device, obj, null_obj};
- list.erase(0);
- EXPECT_EQ(list.size(), 6);
- EXPECT_EQ(list.capacity(), 7);
- EXPECT_EQ(double(list[0]), 1.0);
- EXPECT_STREQ(list[1], "Hi");
- EXPECT_PRED2(DTypeEqual, DLDataType(list[2]), dtype);
- EXPECT_PRED2(DeviceEqual, DLDevice(list[3]), device);
- EXPECT_EQ((Object*)(list[4]), obj.get());
- EXPECT_EQ((Object*)(list[5]), nullptr);
-}
-
-TEST(List_Erase, Back) {
- int64_t integer = 100;
- double fp = 1.0;
- std::string str = "Hi";
- DLDataType dtype{kDLInt, 32, 1};
- DLDevice device{kDLCPU, 0};
- Ref<Object> obj = Ref<Object>::New();
- Ref<Object> null_obj{nullptr};
- Ref<List> list{integer, fp, str, dtype, device, obj, null_obj};
- list.erase(0);
- EXPECT_EQ(list.size(), 6);
- EXPECT_EQ(list.capacity(), 7);
- EXPECT_EQ(double(list[0]), 1.0);
- EXPECT_STREQ(list[1], "Hi");
- EXPECT_PRED2(DTypeEqual, DLDataType(list[2]), dtype);
- EXPECT_PRED2(DeviceEqual, DLDevice(list[3]), device);
- EXPECT_EQ((Object*)(list[4]), obj.get());
- EXPECT_EQ((Object*)(list[5]), nullptr);
-}
-
-TEST(List_Erase, Mid) {
- int64_t integer = 100;
- double fp = 1.0;
- std::string str = "Hi";
- DLDataType dtype{kDLInt, 32, 1};
- DLDevice device{kDLCPU, 0};
- Ref<Object> obj = Ref<Object>::New();
- Ref<Object> null_obj{nullptr};
- Ref<List> list{integer, fp, str, dtype, device, obj, null_obj};
- list.erase(3);
- EXPECT_EQ(list.size(), 6);
- EXPECT_EQ(list.capacity(), 7);
- EXPECT_EQ(int32_t(list[0]), 100);
- EXPECT_DOUBLE_EQ(double(list[1]), 1.0);
- EXPECT_STREQ(list[2], "Hi");
- EXPECT_PRED2(DeviceEqual, DLDevice(list[3]), device);
- EXPECT_EQ((Object*)(list[4]), obj.get());
- EXPECT_EQ((Object*)(list[5]), nullptr);
-}
-
-TEST(List_Iter, Test) {
- Ref<List> list;
- for (int i = 0; i < 16; ++i) {
- list.push_back(i * i);
- }
- int i = 0;
- for (int item : list) {
- EXPECT_EQ(i * i, item);
- ++i;
- }
-}
-
-TEST(List_RevIter, Test) {
- Ref<List> list;
- for (int i = 0; i < 16; ++i) {
- list.push_back(i * i);
- }
- int i = list.size() - 1;
- for (auto it = list.rbegin(); it != list.rend(); ++it) {
- EXPECT_EQ(i * i, int32_t(*it));
- --i;
- }
-}
-
-} // namespace
diff --git a/ffi/tests/backup_cpp/test_ref.cc b/ffi/tests/backup_cpp/test_ref.cc
deleted file mode 100644
index 197f2def88..0000000000
--- a/ffi/tests/backup_cpp/test_ref.cc
+++ /dev/null
@@ -1,294 +0,0 @@
-#include <gtest/gtest.h>
-
-#include <tvm/ffi/ffi.hpp>
-#include <unordered_map>
-
-namespace {
-using namespace tvm::ffi;
-using tvm::ffi::details::StrPad;
-using tvm::ffi::details::StrStd;
-
-using ObjDeleter = void (*)(void*);
-
-struct AllocRecorder {
- std::unordered_map<void*, ObjDeleter> deleters;
-
- void Alloc(void* ptr) {
- deleters[ptr] = reinterpret_cast<TVMFFIAny*>(ptr)->deleter;
- reinterpret_cast<TVMFFIAny*>(ptr)->deleter = AllocRecorder::Deleter;
- }
-
- void Delete(void* ptr) {
- ASSERT_EQ(deleters.count(ptr), 1);
- ObjDeleter d = this->deleters[ptr];
- d(ptr);
- deleters.erase(ptr);
- }
-
- bool IsDeletedImpl(void* ptr) { return deleters.count(ptr) == 0; }
-
- static void Deleter(void* ptr) { AllocRecorder::Global()->Delete(ptr); }
-
- static bool IsDeleted(void* ptr) { return AllocRecorder::Global()->IsDeletedImpl(ptr); }
-
- static AllocRecorder* Global() {
- static AllocRecorder inst;
- return &inst;
- }
-};
-
-template <typename ObjectType>
-struct TestAllocator {
- using Allocator = typename ::tvm::ffi::GetAllocator<ObjectType>::Type;
-
- template <typename... Args>
- TVM_FFI_INLINE static ObjectType* New(Args&&... args) {
- ObjectType* ret = Allocator::New(std::forward<Args>(args)...);
- AllocRecorder::Global()->Alloc(ret);
- return ret;
- }
-
- template <typename PadType, typename... Args>
- TVM_FFI_INLINE static ObjectType* NewWithPad(size_t pad_size, Args&&... args) {
- ObjectType* ret =
- Allocator::template NewWithPad<PadType>(pad_size, std::forward<Args>(args)...);
- AllocRecorder::Global()->Alloc(ret);
- return ret;
- }
-};
-
-int64_t FuncCall(int64_t x) { return x + 1; }
-
-int32_t GetRefCount(void* obj) { return reinterpret_cast<TVMFFIAny*>(obj)->ref_cnt; }
-
-int32_t GetTypeIndex(void* obj) { return reinterpret_cast<TVMFFIAny*>(obj)->type_index; }
-
-ObjDeleter GetDeleter(void* obj) { return reinterpret_cast<TVMFFIAny*>(obj)->deleter; }
-
-TEST(Ref_Constructor_0_Default, Default) {
- Ref<Object> ref;
- EXPECT_EQ(ref.get(), nullptr);
-}
-
-TEST(Ref_Constructor_1_Ptr, SameType) {
- Object* obj = TestAllocator<Object>::New();
- {
- Ref<Object> ref(obj);
- EXPECT_EQ(ref.get(), obj);
- EXPECT_EQ(GetRefCount(obj), 1);
- }
- EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
-}
-
-TEST(Ref_Constructor_1_Ptr, SubType) {
- Str* obj = TestAllocator<Str>::New("Hello world");
- {
- Ref<Object> ref(obj);
- EXPECT_EQ(ref.get(), static_cast<void*>(obj));
- EXPECT_EQ(GetRefCount(obj), 1);
- }
- EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
-}
-
-TEST(Ref_Constructor_2_Ref, SameType_Copy) {
- Object* obj = TestAllocator<Object>::New();
- {
- Ref<Object> ref1(obj);
- EXPECT_EQ(GetRefCount(obj), 1);
- {
- Ref<Object> ref2(ref1);
- EXPECT_EQ(GetRefCount(obj), 2);
- }
- EXPECT_EQ(GetRefCount(ref1.get()), 1);
- }
- EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
-}
-
-TEST(Ref_Constructor_2_Ref, SameType_Move) {
- Object* obj = TestAllocator<Object>::New();
- {
- Ref<Object> ref1(obj);
- EXPECT_EQ(GetRefCount(obj), 1);
- {
- Ref<Object> ref2(std::move(ref1));
- EXPECT_EQ(GetRefCount(obj), 1);
- EXPECT_EQ(ref1.get(), nullptr);
- }
- }
- EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
-}
-
-TEST(Ref_Constructor_2_Ref, SubType_Copy) {
- Str* obj = TestAllocator<Str>::New("Hello world");
- {
- Ref<Str> ref1(obj);
- EXPECT_EQ(GetRefCount(obj), 1);
- {
- Ref<Object> ref2(ref1);
- EXPECT_EQ(GetRefCount(obj), 2);
- }
- EXPECT_EQ(GetRefCount(obj), 1);
- }
- EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
-}
-
-TEST(Ref_Constructor_2_Ref, SubType_Move) {
- Str* obj = TestAllocator<Str>::New("Hello world");
- {
- Ref<Str> ref1(obj);
- EXPECT_EQ(GetRefCount(obj), 1);
- {
- Ref<Object> ref2(std::move(ref1));
- EXPECT_EQ(GetRefCount(obj), 1);
- EXPECT_EQ(ref1.get(), nullptr);
- }
- }
- EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
-}
-
-TEST(Ref_Constructor_3_AnyView, Copy) {
- Object* obj = TestAllocator<Object>::New();
- {
- Ref<Object> ref1(obj);
- EXPECT_EQ(GetRefCount(obj), 1);
- {
- AnyView view(ref1);
- Ref<Object> ref2(view);
- EXPECT_EQ(GetRefCount(obj), 2);
- }
- EXPECT_EQ(GetRefCount(obj), 1);
- }
- EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
-}
-
-TEST(Ref_Constructor_3_AnyView, Move) {
- Object* obj = TestAllocator<Object>::New();
- {
- Ref<Object> ref1(obj);
- EXPECT_EQ(GetRefCount(obj), 1);
- {
- AnyView view(ref1);
- EXPECT_EQ(GetRefCount(obj), 1);
- {
- Ref<Object> ref2(std::move(view));
- EXPECT_EQ(GetRefCount(obj), 2);
- EXPECT_EQ(view.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone));
- EXPECT_EQ(view.ref_cnt, 0);
- EXPECT_EQ(view.v_int64, 0);
- }
- EXPECT_EQ(GetRefCount(obj), 1);
- }
- }
- EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
-}
-
-TEST(Ref_Constructor_4_Any, Copy) {
- Object* obj = TestAllocator<Object>::New();
- {
- Ref<Object> ref1(obj);
- EXPECT_EQ(GetRefCount(obj), 1);
- {
- Any any(ref1);
- EXPECT_EQ(GetRefCount(obj), 2);
- Ref<Object> ref2(any);
- EXPECT_EQ(GetRefCount(obj), 3);
- }
- EXPECT_EQ(GetRefCount(obj), 1);
- }
- EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
-}
-
-TEST(Ref_Constructor_4_Any, Move) {
- Object* obj = TestAllocator<Object>::New();
- {
- Ref<Object> ref1(obj);
- EXPECT_EQ(GetRefCount(obj), 1);
- {
- Any any(ref1);
- EXPECT_EQ(GetRefCount(obj), 2);
- {
- Ref<Object> ref2(std::move(any));
- EXPECT_EQ(GetRefCount(obj), 2);
- EXPECT_EQ(any.type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone));
- EXPECT_EQ(any.ref_cnt, 0);
- EXPECT_EQ(any.v_int64, 0);
- }
- EXPECT_EQ(GetRefCount(obj), 1);
- }
- }
- EXPECT_TRUE(AllocRecorder::IsDeleted(obj));
-}
-
-TEST(Ref_New, Object) {
- Ref<Object> ref = Ref<Object>::New();
- EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
- EXPECT_EQ(GetRefCount(ref.get()), 1);
-}
-
-TEST(Ref_New, Func) {
- Ref<Func> ref = Ref<Func>::New(FuncCall);
- EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
- EXPECT_EQ(GetRefCount(ref.get()), 1);
-}
-
-TEST(Ref_New, RawStr) {
- const char* str = "Hello world";
- Ref<Str> ref = Ref<Str>::New(str);
- EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
- EXPECT_EQ(GetRefCount(ref.get()), 1);
- EXPECT_EQ(GetDeleter(ref.get()), DefaultObjectAllocator<StrPad>::Deleter);
-}
-
-TEST(Ref_New, CharArray) {
- const char str[18] = "Hello world";
- Ref<Str> ref = Ref<Str>::New(str);
- EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
- EXPECT_EQ(GetRefCount(ref.get()), 1);
- EXPECT_EQ(GetDeleter(ref.get()), DefaultObjectAllocator<StrPad>::Deleter);
- EXPECT_EQ(ref->size(), 17);
-}
-
-TEST(Ref_New, StdString_Copy) {
- std::string str = "Hello world";
- Ref<Str> ref = Ref<Str>::New(str);
- EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
- EXPECT_EQ(GetRefCount(ref.get()), 1);
- EXPECT_EQ(GetDeleter(ref.get()), DefaultObjectAllocator<StrPad>::Deleter);
-}
-
-TEST(Ref_New, StdString_Move) {
- std::string str = "Hello world";
- Ref<Str> ref = Ref<Str>::New(std::move(str));
- EXPECT_EQ(GetTypeIndex(ref.get()), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
- EXPECT_EQ(GetRefCount(ref.get()), 1);
- EXPECT_EQ(GetDeleter(ref.get()), DefaultObjectAllocator<StrStd>::Deleter);
-}
-
-TEST(Ref_Stringify, Object) {
- std::string str = Ref<Object>::New().str()->c_str();
- std::string expected_prefix = "object.Object@0";
- EXPECT_GT(str.size(), expected_prefix.size());
- EXPECT_EQ(str.substr(0, expected_prefix.size()), expected_prefix);
-}
-
-TEST(Ref_Stringify, Func) {
- std::string str = Ref<Func>::New(FuncCall).str()->c_str();
- std::string expected_prefix = "object.Func@0";
- EXPECT_GT(str.size(), expected_prefix.size());
- EXPECT_EQ(str.substr(0, expected_prefix.size()), expected_prefix);
-}
-
-TEST(Ref_Stringify, Str) {
- std::string str = Ref<Str>::New("Hello world").str()->c_str();
- EXPECT_EQ(str, "\"Hello world\"");
-}
-
-TEST(Ref_Misc, MoveToRaw) {
- TVMFFIAny* str = reinterpret_cast<TVMFFIAny*>(Ref<Str>::New("Hello world").MoveToRawObjPtr());
- EXPECT_EQ(GetTypeIndex(str), static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
- EXPECT_EQ(GetRefCount(str), 1);
- EXPECT_EQ(GetDeleter(str), DefaultObjectAllocator<StrPad>::Deleter);
- str->deleter(str);
-}
-
-} // namespace
diff --git a/ffi/tests/backup_cpp/test_str.cc b/ffi/tests/backup_cpp/test_str.cc
deleted file mode 100644
index c0b38c2935..0000000000
--- a/ffi/tests/backup_cpp/test_str.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-#include <gtest/gtest.h>
-
-#include <tvm/ffi/ffi.hpp>
-
-namespace {
-using namespace tvm::ffi;
-
-const char c_str_long[] =
- "Hello, World! This is an extremely long string to "
- "avoid any on-stack optimization.";
-
-TEST(Str, CopyFromStdString) {
- std::string std_str = "Hello, World!";
- Ref<Str> str = Ref<Str>::New(std_str);
- EXPECT_EQ(str->size(), std_str.size());
- EXPECT_STREQ(str->c_str(), std_str.c_str());
- EXPECT_STREQ(str->data(), std_str.data());
-}
-
-TEST(Str, MoveFromStdString_0) {
- std::string std_str = c_str_long;
- const void* data = std_str.data();
- Ref<Str> str = Ref<Str>::New(std::move(std_str));
- EXPECT_EQ(static_cast<const void*>(str->data()), data);
- EXPECT_STREQ(str->c_str(), c_str_long);
-}
-
-TEST(Str, MoveFromStdString_1) {
- Ref<Str> str = Ref<Str>::New(std::string(c_str_long));
- EXPECT_STREQ(str->c_str(), c_str_long);
- EXPECT_EQ(str->size(), sizeof(c_str_long) - 1);
-}
-
-TEST(Str, CopyFromCStr) {
- Ref<Str> str = Ref<Str>::New(c_str_long);
- EXPECT_STREQ(str->c_str(), c_str_long);
- EXPECT_EQ(str->size(), sizeof(c_str_long) - 1);
-}
-
-TEST(Str, CopyFromCharArray) {
- const char c_str[18] = "Hello, World!";
- Ref<Str> str = Ref<Str>::New(c_str);
- EXPECT_EQ(sizeof(c_str), 18);
- EXPECT_EQ(strlen(c_str), 13);
- EXPECT_STREQ(str->c_str(), c_str);
- EXPECT_EQ(str->size(), 17);
-}
-
-} // namespace
diff --git a/ffi/tests/backup_cpp/test_type_dyn.cc b/ffi/tests/backup_cpp/test_type_dyn.cc
deleted file mode 100644
index b93595dc3b..0000000000
--- a/ffi/tests/backup_cpp/test_type_dyn.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-#include <gtest/gtest.h>
-
-#include <tvm/ffi/ffi.hpp>
-
-#if TVM_FFI_ALLOW_DYN_TYPE == 1
-
-namespace {
-using namespace tvm::ffi;
-
-struct TestObj : public Object {
- int x;
- explicit TestObj(int x) : x(x) {}
- TVM_FFI_DEF_DYN_TYPE(TestObj, Object, "test.TestObj");
-};
-
-struct SubTestObj : public TestObj {
- int y;
- explicit SubTestObj(int x, int y) : TestObj(x), y(y) {}
- TVM_FFI_DEF_DYN_TYPE(SubTestObj, TestObj, "test.SubTestObj");
-};
-
-void CheckAncestor(int32_t num, const int32_t* ancestors, std::vector<int32_t> expected) {
- EXPECT_EQ(num, expected.size());
- for (int i = 0; i < num; ++i) {
- EXPECT_EQ(ancestors[i], expected[i]);
- }
-}
-
-TEST(DynTypeInfo, TestObj) {
- EXPECT_GE(TestObj::_type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDynObjectBegin));
- EXPECT_STRCASEEQ(TestObj::_type_key, "test.TestObj");
- EXPECT_EQ(TestObj::_type_depth, 1);
- CheckAncestor(TestObj::_type_depth, TestObj::_type_ancestors.data(),
- {static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject)});
-}
-
-TEST(DynTypeInfo, SubTestObj) {
- EXPECT_GE(SubTestObj::_type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDynObjectBegin));
- EXPECT_NE(SubTestObj::_type_index, TestObj::_type_index);
- EXPECT_STRCASEEQ(SubTestObj::_type_key, "test.SubTestObj");
- EXPECT_EQ(SubTestObj::_type_depth, 2);
- CheckAncestor(SubTestObj::_type_depth, SubTestObj::_type_ancestors.data(),
- {static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject), TestObj::_type_index});
-}
-
-TEST(DynTypeInheritance, TestObj) {
- Ref<TestObj> obj = Ref<TestObj>::New(10);
- EXPECT_EQ(obj->x, 10);
- EXPECT_TRUE(obj->IsInstance<Object>());
- EXPECT_TRUE(obj->IsInstance<TestObj>());
- EXPECT_FALSE(obj->IsInstance<Func>());
- EXPECT_FALSE(obj->IsInstance<Str>());
-}
-
-TEST(DynTypeInheritance, SubTestObj) {
- Ref<SubTestObj> obj = Ref<SubTestObj>::New(10, 20);
- EXPECT_EQ(obj->x, 10);
- EXPECT_EQ(obj->y, 20);
- EXPECT_TRUE(obj->IsInstance<Object>());
- EXPECT_TRUE(obj->IsInstance<TestObj>());
- EXPECT_TRUE(obj->IsInstance<SubTestObj>());
- EXPECT_FALSE(obj->IsInstance<Func>());
- EXPECT_FALSE(obj->IsInstance<Str>());
-}
-
-} // namespace
-
-#endif
diff --git a/ffi/tests/backup_cpp/test_type_static.cc b/ffi/tests/backup_cpp/test_type_static.cc
deleted file mode 100644
index abb21af2f5..0000000000
--- a/ffi/tests/backup_cpp/test_type_static.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-#include <gtest/gtest.h>
-
-#include <tvm/ffi/ffi.hpp>
-
-namespace {
-using namespace tvm::ffi;
-
-struct SubType : public Object {
- int data;
- explicit SubType(int data) : data(data) {
- if (data == 1) {
- throw std::runtime_error("New Error");
- }
- }
-};
-
-int64_t FuncCall(int64_t x) { return x + 1; }
-
-void CheckAncestor(int32_t num, const int32_t* ancestors, std::vector<int32_t> expected) {
- EXPECT_EQ(num, expected.size());
- for (int i = 0; i < num; ++i) {
- EXPECT_EQ(ancestors[i], expected[i]);
- }
-}
-
-static_assert(IsObject<Object>, "IsObject<Object> == true");
-static_assert(IsObject<Func>, "IsObject<Func> == true");
-static_assert(IsObject<Str>, "IsObject<Str> == true");
-
-TEST(StaticTypeInfo, Object) {
- EXPECT_EQ(Object::_type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject));
- EXPECT_STRCASEEQ(Object::_type_key, "object.Object");
- EXPECT_EQ(Object::_type_depth, 0);
- CheckAncestor(Object::_type_depth, Object::_type_ancestors.data(), {});
-}
-
-TEST(StaticTypeInfo, Func) {
- EXPECT_EQ(Func::_type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFunc));
- EXPECT_STRCASEEQ(Func::_type_key, "object.Func");
- EXPECT_EQ(Func::_type_depth, 1);
- CheckAncestor(Func::_type_depth, Func::_type_ancestors.data(),
- {static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject)});
-}
-
-TEST(StaticTypeInfo, Str) {
- EXPECT_EQ(Str::_type_index, static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr));
- EXPECT_STRCASEEQ(Str::_type_key, "object.Str");
- EXPECT_EQ(Str::_type_depth, 1);
- CheckAncestor(Str::_type_depth, Str::_type_ancestors.data(),
- {static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIObject)});
-}
-
-TEST(StaticTypeInheritance, None) {
- Ref<Object> obj;
- // FIXME: The lines below are going to segfault
- // EXPECT_STREQ(obj->GetTypeKey(), "None");
- // EXPECT_FALSE(obj->IsInstance<Object>());
- // EXPECT_FALSE(obj->IsInstance<Func>());
- // EXPECT_FALSE(obj->IsInstance<Str>());
-}
-
-TEST(StaticTypeInheritance, Object) {
- Ref<Object> obj = Ref<Object>::New();
- EXPECT_STREQ(obj->GetTypeKey(), "object.Object");
- EXPECT_TRUE(obj->IsInstance<Object>());
- EXPECT_FALSE(obj->IsInstance<Func>());
- EXPECT_FALSE(obj->IsInstance<Str>());
-}
-
-TEST(StaticTypeInheritance, Func_0) {
- Ref<Func> obj = Ref<Func>::New(FuncCall);
- EXPECT_STREQ(obj->GetTypeKey(), "object.Func");
- EXPECT_TRUE(obj->IsInstance<Object>());
- EXPECT_TRUE(obj->IsInstance<Func>());
- EXPECT_FALSE(obj->IsInstance<Str>());
-}
-
-TEST(StaticTypeInheritance, Func_1) {
- Ref<Object> obj = Ref<Func>::New(FuncCall);
- EXPECT_STREQ(obj->GetTypeKey(), "object.Func");
- EXPECT_TRUE(obj->IsInstance<Object>());
- EXPECT_TRUE(obj->IsInstance<Func>());
- EXPECT_FALSE(obj->IsInstance<Str>());
-}
-
-TEST(StaticTypeInheritance, Str_0) {
- Ref<Str> obj = Ref<Str>::New("Hello, World!");
- EXPECT_STREQ(obj->GetTypeKey(), "object.Str");
- EXPECT_TRUE(obj->IsInstance<Object>());
- EXPECT_FALSE(obj->IsInstance<Func>());
- EXPECT_TRUE(obj->IsInstance<Str>());
-}
-
-TEST(StaticTypeInheritance, Str_1) {
- Ref<Object> obj = Ref<Str>::New("Hello, World!");
- EXPECT_STREQ(obj->GetTypeKey(), "object.Str");
- EXPECT_TRUE(obj->IsInstance<Object>());
- EXPECT_FALSE(obj->IsInstance<Func>());
- EXPECT_TRUE(obj->IsInstance<Str>());
-}
-
-TEST(StaticTypeSubclass, NoException) {
- Ref<SubType> obj = Ref<SubType>::New(0);
- EXPECT_EQ(obj->data, 0);
-}
-
-TEST(StaticTypeSubclass, Exception) {
- try {
- Ref<SubType>::New(1);
- FAIL() << "No exception thrown";
- } catch (std::runtime_error& ex) {
- EXPECT_STREQ(ex.what(), "New Error");
- }
-}
-
-} // namespace
diff --git a/include/tvm/ffi/core/c_ffi_abi.h b/include/tvm/ffi/core/c_ffi_abi.h
deleted file mode 100644
index fadbda220e..0000000000
--- a/include/tvm/ffi/core/c_ffi_abi.h
+++ /dev/null
@@ -1,166 +0,0 @@
-#ifndef TVM_FFI_C_FFI_ABI_H_
-#define TVM_FFI_C_FFI_ABI_H_
-
-#include <dlpack/dlpack.h>
-#include <stdint.h>
-
-#if !defined(TVM_FFI_API) && defined(__EMSCRIPTEN__)
-#include <emscripten/emscripten.h>
-#define TVM_FFI_API EMSCRIPTEN_KEEPALIVE
-#endif
-#if !defined(TVM_FFI_API) && defined(_MSC_VER)
-#ifdef TVM_FFI_EXPORTS
-#define TVM_FFI_API __declspec(dllexport)
-#else
-#define TVM_FFI_API __declspec(dllimport)
-#endif
-#endif
-#ifndef TVM_FFI_API
-#define TVM_FFI_API __attribute__((visibility("default")))
-#endif
-
-#ifndef TVM_FFI_ALLOW_DYN_TYPE
-#define TVM_FFI_ALLOW_DYN_TYPE 0
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef __cplusplus
-enum class TVMFFITypeIndex : int32_t {
-#else
-typedef enum {
-#endif
- // [Section] On-stack POD Types: [0, kTVMFFIStaticObjectBegin)
- // N.B. `kTVMFFIRawStr` is a string backed by a `\0`-terminated char array,
- // which is not owned by TVMFFIAny. It is required that the following
- // invariant holds:
- // - `Any::type_index` is never `kTVMFFIRawStr`
- // - `AnyView::type_index` can be `kTVMFFIRawStr`
- kTVMFFINone = 0,
- kTVMFFIInt = 1,
- kTVMFFIFloat = 2,
- kTVMFFIPtr = 3,
- kTVMFFIDataType = 4,
- kTVMFFIDevice = 5,
- kTVMFFIRawStr = 6,
- // [Section] Static Boxed: [kTVMFFIStaticObjectBegin, kTVMFFIDynObjectBegin)
- kTVMFFIStaticObjectBegin = 64,
- kTVMFFIObject = 64,
- kTVMFFIList = 65,
- kTVMFFIDict = 66,
- kTVMFFIError = 67,
- kTVMFFIFunc = 68,
- kTVMFFIStr = 69,
- // [Section] Dynamic Boxed: [kTVMFFIDynObjectBegin, +oo)
- kTVMFFIDynObjectBegin = 128,
-#ifdef __cplusplus
-};
-#else
-} TypeIndex;
-#endif
-
-struct TVMFFIAny;
-typedef TVMFFIAny TVMFFIObject;
-typedef TVMFFIObject *TVMFFIObjectHandle;
-typedef TVMFFIAny *TVMFFIAnyHandle;
-
-typedef struct TVMFFIAny {
- int32_t type_index;
- union { // 4 bytes
- int32_t ref_cnt; // reference counter for heap object
- int32_t small_len; // length for on-stack object
- };
- union { // 8 bytes
- int64_t v_int64; // integers
- double v_float64; // floating-point numbers
- DLDataType v_dtype; // data type
- DLDevice v_device; // device
- void *v_ptr; // typeless pointers
- const char *v_str; // raw string
- TVMFFIObjectHandle v_obj; // ref counted objects
- void (*deleter)(void *); // Deleter of the object
- char v_bytes[8]; // small string
- char32_t v_char32[2]; // UCS4 string and Unicode
- };
-} TVMFFIAny;
-
-typedef struct {
- int32_t type_index;
- const char *type_key;
- int32_t type_depth;
- int32_t *type_ancestors;
-} TVMFFITypeInfo;
-
-typedef TVMFFITypeInfo *TVMFFITypeInfoHandle;
-
-typedef struct {
- const char *filename;
- const char *func;
- int32_t lineno;
- void (*deleter)(void *);
-} TVMFFIStackFrame;
-
-typedef struct {
- int32_t type_index;
- int32_t ref_cnt;
- void (*deleter)(void *);
- const char *kind;
- int32_t num_frames;
- const char **linenos;
- const char *message;
-} TVMFFIError;
-
-typedef struct {
- int32_t type_index;
- int32_t ref_cnt;
- void (*deleter)(void *);
- int64_t length;
- char *data;
-} TVMFFIStr;
-
-typedef struct {
- int32_t type_index;
- int32_t ref_cnt;
- void (*deleter)(void *);
- void (*call)(const void *self, int32_t num_args, const TVMFFIAny *args, TVMFFIAny *ret);
- int32_t (*safe_call)(const void *self, int32_t num_args, const TVMFFIAny *args, TVMFFIAny *ret);
-} TVMFFIFunc;
-
-typedef struct {
- int32_t type_index;
- int32_t ref_cnt;
- void (*deleter)(void *);
- int64_t list_capacity;
- int64_t list_length;
- int64_t pool_capacity;
- int64_t pool_length;
-} TVMFFIList;
-
-typedef struct {
- int32_t type_index;
- int32_t ref_cnt;
- void (*deleter)(void *);
- int64_t capacity;
- int64_t size;
-} TVMFFIDict;
-
-#if TVM_FFI_ALLOW_DYN_TYPE
-typedef void *TVMFFITypeTableHandle;
-TVM_FFI_API void TVMFFIDynTypeIndex2Info(TVMFFITypeTableHandle self, int32_t type_index,
- TVMFFITypeInfoHandle *out_type_info);
-TVM_FFI_API void TVMFFIDynTypeDef(TVMFFITypeTableHandle self, const char *type_key,
- int32_t type_depth, const int32_t *type_ancestors,
- int32_t *out_type_index);
-TVM_FFI_API void TVMFFIDynTypeSetAttr(TVMFFITypeTableHandle self, int32_t type_index,
- const char *key, TVMFFIAnyHandle value);
-TVM_FFI_API void TVMFFIDynTypeGetAttr(TVMFFITypeTableHandle self, int32_t type_index,
- const char *key, TVMFFIAnyHandle *out_type_attr);
-#endif // TVM_FFI_ALLOW_DYN_TYPE
-
-#ifdef __cplusplus
-} // TVM_FFI_EXTERN_C
-#endif
-
-#endif // TVM_FFI_C_FFI_ABI_H_
diff --git a/include/tvm/ffi/core/core.h b/include/tvm/ffi/core/core.h
deleted file mode 100644
index 608e3e4c1b..0000000000
--- a/include/tvm/ffi/core/core.h
+++ /dev/null
@@ -1,421 +0,0 @@
-#ifndef TVM_FFI_CORE_H_
-#define TVM_FFI_CORE_H_
-
-#include "./c_ffi_abi.h"
-#include "./traits.h"
-#include "./utils.h"
-#include <cstring>
-#include <type_traits>
-
-namespace tvm {
-namespace ffi {
-
-#define TVM_FFI_DEF_TYPE_FRIENDS() \
- template <typename> \
- friend struct ::tvm::ffi::IsAnyWithExtra; \
- template <typename> \
- friend struct ::tvm::ffi::Ref; \
- template <typename> \
- friend struct ::tvm::ffi::RefBase; \
- template <typename, typename> \
- friend struct ::tvm::ffi::TypeTraits; \
- template <typename> \
- friend struct ::tvm::ffi::TypeTraitsDefaultForObject; \
- template <typename> \
- friend struct ::tvm::ffi::DefaultObjectAllocator
-
-/********** Section 1. Object *********/
-
-#define TVM_FFI_DEF_STATIC_TYPE(SelfType, ParentType, TypeIndex) \
-public: \
- TVM_FFI_DEF_TYPE_FRIENDS(); \
- friend struct ::tvm::ffi::Any; \
- friend struct ::tvm::ffi::AnyView; \
- [[maybe_unused]] static constexpr int32_t _type_index = static_cast<int32_t>(TypeIndex); \
- using _type_parent [[maybe_unused]] = ParentType; \
- [[maybe_unused]] static constexpr int32_t _type_depth = ParentType::_type_depth + 1; \
- [[maybe_unused]] static inline constexpr std::array<int32_t, _type_depth> _type_ancestors = \
- ::tvm::ffi::details::ObjectAncestorsConstExpr<ParentType, _type_depth>(); \
- template <typename DerivedType> \
- TVM_FFI_INLINE bool IsInstance() const { \
- return ::tvm::ffi::details::IsInstanceOf<DerivedType, SelfType>(this->type_index); \
- } \
- TVM_FFI_INLINE const char *GetTypeKey() const { return TypeIndex2TypeKey(this->type_index); } \
- [[maybe_unused]] static constexpr const char *_type_key = TypeIndexTraits<TypeIndex>::type_key
-
-#define TVM_FFI_DEF_DYN_TYPE(SelfType, ParentType, TypeKey) \
-public: \
- TVM_FFI_DEF_TYPE_FRIENDS(); \
- friend struct ::tvm::ffi::Any; \
- friend struct ::tvm::ffi::AnyView; \
- using _type_parent [[maybe_unused]] = ParentType; \
- [[maybe_unused]] static constexpr int32_t _type_depth = ParentType::_type_depth + 1; \
- [[maybe_unused]] static inline const std::array<int32_t, _type_depth> _type_ancestors = \
- ::tvm::ffi::details::ObjectAncestors<ParentType, _type_depth>(); \
- [[maybe_unused]] static inline int32_t _type_index = []() -> int32_t { \
- int32_t ret; \
- TVMFFIDynTypeDef(nullptr, TypeKey, _type_depth, _type_ancestors.data(), &ret); \
- return ret; \
- }(); \
- template <typename DerivedType> \
- TVM_FFI_INLINE bool IsInstance() const { \
- return ::tvm::ffi::details::IsInstanceOf<DerivedType, SelfType>(this->type_index); \
- } \
- TVM_FFI_INLINE const char *GetTypeKey() const { return TypeIndex2TypeKey(this->type_index); } \
- [[maybe_unused]] static constexpr const char *_type_key = TypeKey
-
-struct Object : protected TVMFFIAny {
- TVM_FFI_DEF_STATIC_TYPE(Object, details::DummyRoot, TVMFFITypeIndex::kTVMFFIObject);
-
- TVM_FFI_INLINE Object() : TVMFFIAny() {}
- TVM_FFI_INLINE Object(const Object &) : TVMFFIAny() {}
- TVM_FFI_INLINE Object(Object &&) {}
- TVM_FFI_INLINE Object &operator=(const Object &) { return *this; }
- TVM_FFI_INLINE Object &operator=(Object &&) { return *this; }
- Ref<Str> str() const;
- TVM_FFI_INLINE friend std::ostream &operator<<(std::ostream &os, const Object &src) {
- TVMFFIAny v{};
- v.type_index = src.type_index;
- v.v_obj = const_cast<Object *>(&src);
- details::AnyView2Str(os, &v);
- return os;
- }
-};
-
-/********** Section 2. Ref<ObjectType> *********/
-
-#define TVM_FFI_REF_DEF_DELEGATE_CONSTRUCTORS(SelfType, BaseType) \
- template <typename Other, typename = std::is_constructible<BaseType, Other &&>> \
- TVM_FFI_INLINE Ref(Other &&src) : BaseType(std::forward<Other>(src)) {} \
- template <typename Other, typename = std::is_constructible<BaseType, Other &&>> \
- TVM_FFI_DEF_ASSIGN(SelfType, const Other &)
-
-template <typename Type>
-struct RefBase {
-private:
- using TSelf = RefBase<Type>;
- using TSub = Ref<Type>;
- TVM_FFI_DEF_TYPE_FRIENDS();
- friend struct Any;
- friend struct AnyView;
- template <typename T>
- using EnableAnyOrViewOrRef = std::enable_if_t<details::IsAnyOrView<T> || details::IsRef<T>>;
- template <typename Base, typename Derived>
- using EnableDerivedObj =
- typename std::enable_if_t<std::is_base_of_v<Base, Derived> ||
- (std::is_same_v<Base, Object> && IsObject<Derived>)>;
-
-public:
- /***** Factory: the `new` operator *****/
- template <typename... Args>
- TVM_FFI_INLINE static TSub New(Args &&...args) {
- return TSub(Allocator::New(std::forward<Args>(args)...));
- }
- /***** Accessors *****/
- TVM_FFI_INLINE const Type *get() const { return reinterpret_cast<const Type *>(this->data_); }
- TVM_FFI_INLINE Type *get() { return reinterpret_cast<Type *>(data_); }
- TVM_FFI_INLINE const Type *operator->() const { return get(); }
- TVM_FFI_INLINE const Type &operator*() const { return *get(); }
- TVM_FFI_INLINE Type *operator->() { return get(); }
- TVM_FFI_INLINE Type &operator*() { return *get(); }
- /***** Misc *****/
- Ref<Str> str() const;
- TVM_FFI_INLINE friend std::ostream &operator<<(std::ostream &os, const TSelf &src) {
- TVMFFIAny v = src.AsTVMFFIAny();
- details::AnyView2Str(os, &v);
- return os;
- }
- template <typename BaseType = Type>
- BaseType *GetRawObjPtr() const {
- static_assert(std::is_same_v<BaseType, Object> || std::is_base_of_v<BaseType, Type>,
- "Only downcasting is allowed");
- return reinterpret_cast<BaseType *>(this->data_);
- }
- template <typename BaseType = Type>
- BaseType *MoveToRawObjPtr() {
- static_assert(std::is_same_v<BaseType, Object> || std::is_base_of_v<BaseType, Type>,
- "Only downcasting is allowed");
- BaseType *ret = reinterpret_cast<BaseType *>(this->data_);
- this->data_ = nullptr;
- return ret;
- }
-
-protected:
- using Allocator = typename GetAllocator<Type>::Type;
- /***** Destructor *****/
- TVM_FFI_INLINE ~RefBase() { this->DecRef(); }
- /***** Constructor 0: default *****/
- TVM_FFI_INLINE RefBase() : data_(nullptr) {}
- /***** Constructor 1: From raw pointers *****/
- TVM_FFI_INLINE RefBase(TVMFFIAny *data) : data_(data) { this->IncRef(); }
- template <typename Derived, typename = EnableDerivedObj<Type, Derived>>
- TVM_FFI_INLINE RefBase(Derived *data) : TSelf(reinterpret_cast<TVMFFIAny *>(data)) {}
- TVM_FFI_DEF_ASSIGN(TSelf, TVMFFIAny *)
- template <typename Derived, typename = EnableDerivedObj<Type, Derived>>
- TVM_FFI_DEF_ASSIGN(TSelf, Derived *)
- /***** Constructor 2: from RefBase<Type> *****/
- TVM_FFI_INLINE RefBase(const TSelf &other) : data_(other.data_) {
- this->IncRef();
- }
- TVM_FFI_INLINE RefBase(TSelf &&other) : data_(other.data_) { other.data_ = nullptr; }
- TVM_FFI_DEF_ASSIGN(TSelf, const TSelf &)
- TVM_FFI_DEF_ASSIGN(TSelf, TSelf &&)
- /***** Constructor 3: from Ref<SubType>, Any and AnyView *****/
- template <typename Other, typename = EnableAnyOrViewOrRef<Other>>
- TVM_FFI_DEF_ASSIGN(TSub, Other &&)
- template <typename Other, typename = EnableAnyOrViewOrRef<Other>>
- TVM_FFI_DEF_ASSIGN(TSub, const Other &)
- template <typename Other, typename = EnableAnyOrViewOrRef<Other>>
- TVM_FFI_INLINE RefBase(const Other &src) : RefBase(src.template GetRawObjPtr<Type>()) {}
- template <typename Other, typename = EnableAnyOrViewOrRef<Other>>
- TVM_FFI_INLINE RefBase(Other &&src)
- : data_(reinterpret_cast<TVMFFIAny *>(src.template MoveToRawObjPtr<Type>())) {
- if constexpr (std::is_same_v<Other, AnyView>) {
- this->IncRef();
- }
- }
- TVM_FFI_INLINE void IncRef() { details::IncRef(this->data_); }
- TVM_FFI_INLINE void DecRef() { details::DecRef(this->data_); }
- TVM_FFI_INLINE void Swap(TSelf &other) { std::swap(this->data_, other.data_); }
- TVM_FFI_INLINE TVMFFIAny AsTVMFFIAny() const {
- if (data_ == nullptr) {
- return TVMFFIAny();
- }
- TVMFFIAny ret{};
- ret.type_index = data_->type_index;
- ret.v_obj = data_;
- return ret;
- }
-
- TVMFFIAny *data_;
-};
-
-template <typename Type>
-struct Ref : public RefBase<Type> {
- TVM_FFI_DEF_TYPE_FRIENDS();
- TVM_FFI_INLINE Ref() : RefBase<Type>() {}
- TVM_FFI_REF_DEF_DELEGATE_CONSTRUCTORS(Ref<Type>, RefBase<Type>)
-};
-
-/********** Section 3. AnyView *********/
-
-struct AnyView : public TVMFFIAny {
- TVM_FFI_DEF_TYPE_FRIENDS();
- friend struct Any;
- /***** Destructor *****/
- TVM_FFI_INLINE ~AnyView() = default;
- TVM_FFI_INLINE void Reset() { *(static_cast<TVMFFIAny *>(this)) = TVMFFIAny(); }
- /***** Constructor 0: default *****/
- TVM_FFI_INLINE AnyView() : TVMFFIAny() {}
- /***** Constructor 1: from AnyView *****/
- TVM_FFI_INLINE AnyView(const AnyView &src) = default;
- TVM_FFI_INLINE AnyView &operator=(const AnyView &src) = default;
- TVM_FFI_INLINE AnyView(AnyView &&src) : TVMFFIAny(*&src) { src.Reset(); }
- TVM_FFI_DEF_ASSIGN(AnyView, AnyView &&)
- /***** Constructor 2: from Any *****/
- TVM_FFI_INLINE AnyView(const Any &src);
- TVM_FFI_DEF_ASSIGN(AnyView, const Any &)
- AnyView(Any &&src) = delete;
- AnyView &operator=(Any &&src) = delete;
- /***** Constructor 3: from Ref<T> *****/
- template <typename T>
- TVM_FFI_INLINE AnyView(const Ref<T> &src) : TVMFFIAny(src.AsTVMFFIAny()) {}
- template <typename T>
- TVM_FFI_DEF_ASSIGN(AnyView, const Ref<T> &)
- template <typename T>
- AnyView(Ref<T> &&src) = delete;
- template <typename T>
- AnyView &operator=(Ref<T> &&src) = delete;
- /***** Constructors 4: use TypeTraits<T> *****/
- template <typename Type, typename = HasTypeTraits<Type>>
- TVM_FFI_INLINE AnyView(const Type &src) : TVMFFIAny() {
- TypeTraitsNoCR<Type>::CopyFromTypeToTVMFFIAny(src, this);
- }
- template <typename Type, typename = HasTypeTraits<Type>>
- TVM_FFI_DEF_ASSIGN(AnyView, const Type &)
- /*** Converter 0: use TypeTraits<T> ***/
- template <typename Type, typename = HasTypeTraits<Type>>
- operator Type() const;
- template <typename Type, typename = HasTypeTraits<Type>>
- Type CastWithStorage(Any *storage) const;
- /***** Misc *****/
- Ref<Str> str() const;
- TVM_FFI_INLINE friend std::ostream &operator<<(std::ostream &os, const AnyView &src) {
- details::AnyView2Str(os, &src);
- return os;
- }
- template <typename Type>
- TVM_FFI_INLINE Type *GetRawObjPtr() const;
- template <typename Type>
- TVM_FFI_INLINE Type *MoveToRawObjPtr();
-
-protected:
- TVM_FFI_INLINE void Swap(TVMFFIAny &src) {
- TVMFFIAny tmp = *this;
- *static_cast<TVMFFIAny *>(this) = src;
- src = tmp;
- }
-};
-
-/********** Section 4. Any *********/
-
-struct Any : public TVMFFIAny {
- TVM_FFI_DEF_TYPE_FRIENDS();
- friend struct AnyView;
- /***** Destructor *****/
- TVM_FFI_INLINE ~Any() { this->Reset(); }
- TVM_FFI_INLINE void Reset() {
- this->DecRef();
- *(static_cast<TVMFFIAny *>(this)) = TVMFFIAny();
- }
- /***** Constructor 0: default *****/
- TVM_FFI_INLINE Any() : TVMFFIAny() {}
- /***** Constructor 1: from AnyView *****/
- TVM_FFI_INLINE Any(const AnyView &src) : TVMFFIAny(*static_cast<const TVMFFIAny *>(&src)) {
- if (this->type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
- // Special case: handle the case where `Any` needs to own a raw string.
- this->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr);
- this->v_obj = details::StrCopyFromCharArray(this->v_str, std::strlen(this->v_str));
- }
- this->IncRef();
- }
- TVM_FFI_INLINE Any(AnyView &&src) : Any(static_cast<const AnyView &>(src)) { // TODO: add test
- src.Reset();
- }
- TVM_FFI_DEF_ASSIGN(Any, const AnyView &)
- TVM_FFI_DEF_ASSIGN(Any, AnyView &&)
- /***** Constructor 2: from Any *****/
- TVM_FFI_INLINE Any(const Any &src) : TVMFFIAny(*static_cast<const TVMFFIAny *>(&src)) {
- this->IncRef();
- }
- TVM_FFI_INLINE Any(Any &&src) : TVMFFIAny(*static_cast<const TVMFFIAny *>(&src)) {
- *static_cast<TVMFFIAny *>(&src) = TVMFFIAny();
- }
- TVM_FFI_DEF_ASSIGN(Any, const Any &)
- TVM_FFI_DEF_ASSIGN(Any, Any &&)
- /***** Constructor 3: from Ref<T> *****/
- template <typename T>
- TVM_FFI_INLINE Any(const Ref<T> &src) : TVMFFIAny(src.AsTVMFFIAny()) {
- this->IncRef();
- }
- template <typename T>
- TVM_FFI_INLINE Any(Ref<T> &&src) : TVMFFIAny(src.AsTVMFFIAny()) {
- src.data_ = nullptr;
- }
- template <typename T>
- TVM_FFI_DEF_ASSIGN(Any, const Ref<T> &)
- template <typename T>
- TVM_FFI_DEF_ASSIGN(Any, Ref<T> &&)
- /***** Constructors 4: use TypeTraits<T> *****/
- template <typename Type, typename = HasTypeTraits<Type>>
- TVM_FFI_INLINE Any(const Type &src) : Any(AnyView(src)) {}
- template <typename Type, typename = HasTypeTraits<Type>>
- TVM_FFI_DEF_ASSIGN(Any, const Type &)
- /***** Constructors 5: Special handling for strings *****/
- TVM_FFI_INLINE Any(const std::string &s) : Any(AnyView(s)) {}
- TVM_FFI_INLINE Any(const char *s) : Any(AnyView(s)) {}
- TVM_FFI_INLINE Any(std::string &&s) : TVMFFIAny() {
- this->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr);
- this->v_obj = details::StrMoveFromStdString(std::move(s));
- this->IncRef();
- }
- template <size_t N>
- TVM_FFI_INLINE Any(const CharArray<N> &s) : TVMFFIAny() {
- this->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr);
- this->v_obj = details::StrCopyFromCharArray(s, N - 1);
- this->IncRef();
- }
- TVM_FFI_DEF_ASSIGN(Any, const std::string &)
- TVM_FFI_DEF_ASSIGN(Any, const char *&)
- TVM_FFI_DEF_ASSIGN(Any, std::string &&)
- template <size_t N>
- TVM_FFI_DEF_ASSIGN(Any, const CharArray<N> &)
- /*** Converter 0: use TypeTraits<T> ***/
- template <typename Type, typename = HasTypeTraits<Type>>
- operator Type() const;
- /***** Misc *****/
- Ref<Str> str() const;
- TVM_FFI_INLINE friend std::ostream &operator<<(std::ostream &os, const Any &src) {
- details::AnyView2Str(os, &src);
- return os;
- }
- template <typename Type>
- TVM_FFI_INLINE Type *GetRawObjPtr() const {
- return reinterpret_cast<const AnyView *>(this)->GetRawObjPtr<Type>();
- }
- template <typename Type>
- TVM_FFI_INLINE Type *MoveToRawObjPtr() {
- return reinterpret_cast<AnyView *>(this)->MoveToRawObjPtr<Type>();
- }
-
-protected:
- TVM_FFI_INLINE void Swap(TVMFFIAny &src) {
- TVMFFIAny tmp = *this;
- *static_cast<TVMFFIAny *>(this) = src;
- src = tmp;
- }
- TVM_FFI_INLINE void IncRef() {
- if (!details::IsTypeIndexPOD(this->type_index)) {
- details::IncRef(this->v_obj);
- }
- }
- TVM_FFI_INLINE void DecRef() {
- if (!details::IsTypeIndexPOD(this->type_index)) {
- details::DecRef(this->v_obj);
- }
- }
-};
-
-/********** Section 5. Type Conversion and Type Table *********/
-
-TVM_FFI_INLINE AnyView::AnyView(const Any &src) : TVMFFIAny(*&src) {}
-
-#define TVM_FFI_TRY_CONVERT(Expr, TypeStr) \
- try { \
- return Expr; \
- } catch (const TemporaryTypeError &) { \
- TVM_FFI_THROW(TypeError) << "Cannot convert from type `" \
- << TypeIndex2TypeKey(this->type_index) << "` to `" << TypeStr << "`"; \
- } \
- TVM_FFI_UNREACHABLE();
-template <typename Type, typename>
-inline AnyView::operator Type() const {
- TVM_FFI_TRY_CONVERT(TypeTraitsNoCR<Type>::CopyFromTVMFFIAnyToType(this),
- TypeTraitsNoCR<Type>::Type2Str());
-}
-template <typename Type, typename>
-inline Type AnyView::CastWithStorage(Any *storage) const {
- TVM_FFI_TRY_CONVERT(TypeTraitsNoCR<Type>::CopyFromTVMFFIAnyToTypeWithStorage(this, storage),
- TypeTraitsNoCR<Type>::Type2Str());
-}
-template <typename Type, typename>
-inline Any::operator Type() const {
- TVM_FFI_TRY_CONVERT(TypeTraitsNoCR<Type>::CopyFromTVMFFIAnyToType(this),
- TypeTraitsNoCR<Type>::Type2Str());
-}
-template <typename Type>
-inline Type *AnyView::GetRawObjPtr() const {
- TVM_FFI_TRY_CONVERT(TypeTraitsNoCR<Type *>::CopyFromTVMFFIAnyToRef(this), Type::_type_key);
-}
-template <typename Type>
-inline Type *AnyView::MoveToRawObjPtr() {
- TVM_FFI_TRY_CONVERT(TypeTraitsNoCR<Type *>::MoveFromTVMFFIAnyToRef(this), Type::_type_key);
-}
-#undef TVM_FFI_TRY_CONVERT
-
-#if TVM_FFI_ALLOW_DYN_TYPE
-TVM_FFI_INLINE void TypeSetAttr(int32_t type_index, const char *attr_key, AnyView attr_value) {
- TVMFFIDynTypeSetAttr(nullptr, type_index, attr_key, &attr_value);
-}
-
-TVM_FFI_INLINE AnyView TypeGetAttr(int32_t type_index, const char *attr_key) {
- TVMFFIAnyHandle attr;
- TVMFFIDynTypeGetAttr(nullptr, type_index, attr_key, &attr);
- return AnyView(*(static_cast<AnyView *>(attr)));
-}
-#endif
-
-} // namespace ffi
-} // namespace tvm
-
-#endif // TVM_FFI_CORE_H_
diff --git a/include/tvm/ffi/core/traits.h b/include/tvm/ffi/core/traits.h
deleted file mode 100644
index 8f80b77f49..0000000000
--- a/include/tvm/ffi/core/traits.h
+++ /dev/null
@@ -1,613 +0,0 @@
-#ifndef TVM_FFI_TRAITS_H_
-#define TVM_FFI_TRAITS_H_
-
-#include "./utils.h"
-#include <sstream>
-#include <string>
-
-namespace tvm {
-namespace ffi {
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4702)
-#endif
-
-/*!
- * \brief TypeTraits<T> is a template class that provides a set of static
- * methods that are associated with a specific type `T` for compile-time
- * dispatching.
- *
- * [Trait 0] Type2Str<T>: () -> std::string
- * Returns the string representation of the type `T`
- *
- * [Trait 1] CopyFromTVMFFIAnyToType<T>: (const TVMFFIAny *) -> T
- * Converts an `AnyView` or `Any` to `T`. It could incur copy when
- * inevitable, for example, copy the content of a Str obj if it's converted
- * to `std::string`.
- *
- * It is used in the following cases in the codebase:
- * 1) `AnyView::operator Type()`
- * 2) `Any::operator Type()`
- *
- * [Trait 2] CopyFromTypeToTVMFFIAny<T>: (T, TVMFFIAny *) -> void
- * Converts a value of type T to `AnyView`.
- *
- * It is used in the following case in the codebase:
- * 1) `AnyView::AnyView(const Type& src)`
- *
- * [Trait 3] CopyFromTVMFFIAnyToRef<T>: (const TVMFFIAny *) -> T*
- * Converts an `AnyView` or `Any` to `TVMFFIAny*`, which is subsequently used
- * to initialize `Ref<T>::data_`. Note that this method does not increment
- * the reference counter, which is supposed to be later handled automatically by
- * `Ref<T>`.
- *
- * It is used in the following cases in the codebase:
- * 1) `Ref<Type>::Ref(const AnyView& src)`
- * 2) `Ref<Type>::Ref(const Any& src)`
- *
- * [Trait 4] MoveFromTVMFFIAnyToRef<T>: (TVMFFIAny *) -> T*
- * Moves an `AnyView` or `Any` to `TVMFFIAny*`, which is subsequently used
- * to initialize `Ref<T>::data_`. Note that the reference counter will not
- * increment throughout the process because it corresponds to move semantics.
- *
- * It is used in the following cases in the codebase:
- * 1) `Ref<Type>::Ref(AnyView&& src)`
- * 2) `Ref<Type>::Ref(Any&& src)`
- *
- * [Trait 5] (Optional) CopyFromTVMFFIAnyToTypeWithStorage<T>:
- * (const TVMFFIAny *, Any *) -> T
- * It does similar thing as [Trait 1] that converts an `AnyView` or `Any` to
- * `T`, but is onyl used by TVM FFI's calling convention, where an additional
- * storage `Any*` is provided to retain ownership when unpacking `AnyView[]`
- * before calling into a C++ function.
- *
- * Example. When converting an `AnyView (kTVMFFIRawStr)` to `Str*`,
- * which consists of two intermediate steps `AnyView (kTVMFFIRawStr)` to
- * `Ref<Str>`, and `Ref<Str>` to `Str*`, this method will store the by-product
- * `Ref<Str>` into the given storage to lifespan expiration.
- *
- * It is used in the following case(s) in the codebase:
- * 1) `AnyView::CastWithStorage<Type>(Any *)`
- */
-template <typename, typename = void>
-struct TypeTraits {
- constexpr static bool enabled = false;
-};
-template <typename T>
-using TypeTraitsNoCR = TypeTraits<details::RemoveCR<T>>;
-template <typename T>
-using HasTypeTraits = std::enable_if_t<TypeTraitsNoCR<T>::enabled>;
-
-template <enum TVMFFITypeIndex type_index>
-struct TypeIndexTraits;
-
-#define TVM_FFI_DEF_TYPE_INDEX_TRAITS(TypeIndex_, TypeKey) \
- template <> \
- struct TypeIndexTraits<TypeIndex_> { \
- static constexpr const char *type_key = TypeKey; \
- }
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFINone, "None");
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIInt, "int");
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIFloat, "float");
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIPtr, "Ptr");
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIDataType, "dtype");
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIDevice, "Device");
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIRawStr, "const char *");
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIObject, "object.Object");
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIList, "object.List");
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIDict, "object.Dict");
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIError, "object.Error");
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIFunc, "object.Func");
-TVM_FFI_DEF_TYPE_INDEX_TRAITS(TVMFFITypeIndex::kTVMFFIStr, "object.Str");
-#undef TVM_FFI_DEF_TYPE_INDEX_TRAITS
-
-TVM_FFI_INLINE const char *TypeIndex2TypeKey(int32_t type_index) {
-#define TVM_FFI_TYPE_INDEX_SWITCH_CASE(TypeIndex_) \
- case TypeIndex_: \
- return TypeIndexTraits<TypeIndex_>::type_key;
- switch (static_cast<TVMFFITypeIndex>(type_index)) {
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFINone);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIInt);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIFloat);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIPtr);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIDataType);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIDevice);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIRawStr);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIObject);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIList);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIDict);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIError);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIFunc);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIStr);
- default:
-#if TVM_FFI_ALLOW_DYN_TYPE
- {
- TVMFFITypeInfoHandle type_info;
- TVMFFIDynTypeIndex2Info(nullptr, type_index, &type_info);
- return type_info ? type_info->type_key : "(undefined)";
- }
-#else
- return "Unknown";
-#endif
- }
-#undef TVM_FFI_TYPE_INDEX_SWITCH_CASE
- TVM_FFI_UNREACHABLE();
-}
-
-namespace details {
-struct DummyRoot {
- static constexpr bool _type_is_static = true;
- static constexpr int32_t _type_depth = -1;
- static constexpr int32_t _type_index = -1;
- static constexpr std::array<int32_t, 0> _type_ancestors = {};
-};
-template <typename F, typename TypeTableType>
-TVM_FFI_INLINE void InitTypeTable(F f, TypeTableType *self) {
- {
- f.template RegisterType<TVMFFITypeIndex::kTVMFFINone, DummyRoot>(self);
- f.template RegisterType<TVMFFITypeIndex::kTVMFFIInt, DummyRoot>(self);
- f.template RegisterType<TVMFFITypeIndex::kTVMFFIFloat, DummyRoot>(self);
- f.template RegisterType<TVMFFITypeIndex::kTVMFFIPtr, DummyRoot>(self);
- f.template RegisterType<TVMFFITypeIndex::kTVMFFIDevice, DummyRoot>(self);
- f.template RegisterType<TVMFFITypeIndex::kTVMFFIDataType, DummyRoot>(self);
- f.template RegisterType<TVMFFITypeIndex::kTVMFFIRawStr, DummyRoot>(self);
- f.template RegisterType<TVMFFITypeIndex::kTVMFFIObject, Object>(self);
- f.template RegisterType<TVMFFITypeIndex::kTVMFFIList, Object>(self);
- f.template RegisterType<TVMFFITypeIndex::kTVMFFIDict, Object>(self);
- f.template RegisterType<TVMFFITypeIndex::kTVMFFIError, Error>(self);
- f.template RegisterType<TVMFFITypeIndex::kTVMFFIStr, Str>(self);
- f.template RegisterType<TVMFFITypeIndex::kTVMFFIFunc, Func>(self);
- }
- {
- f.template RegisterStr<TVMFFITypeIndex::kTVMFFINone, void *>(self);
- f.template RegisterStr<TVMFFITypeIndex::kTVMFFIInt, int64_t>(self);
- f.template RegisterStr<TVMFFITypeIndex::kTVMFFIFloat, double>(self);
- f.template RegisterStr<TVMFFITypeIndex::kTVMFFIPtr, void *>(self);
- f.template RegisterStr<TVMFFITypeIndex::kTVMFFIDevice, DLDevice>(self);
- f.template RegisterStr<TVMFFITypeIndex::kTVMFFIDataType, DLDataType>(self);
- f.template RegisterStr<TVMFFITypeIndex::kTVMFFIRawStr, const char *>(self);
- f.template RegisterStr<TVMFFITypeIndex::kTVMFFIStr, const char *>(self);
- }
-}
-
-template <typename T>
-using Identity = T;
-
-template <template <typename> typename F = Identity>
-TVM_FFI_INLINE void StaticAnyView2Str(std::ostream &os, const TVMFFIAny *v) {
-#define TVM_FFI_TYPE_INDEX_SWITCH_CASE(TypeIndex_, Type) \
- case TypeIndex_: { \
- using Traits = F<TypeTraits<Type>>; \
- os << Traits::__str__(Traits::CopyFromTVMFFIAnyToType(v)); \
- break; \
- }
- switch (static_cast<TVMFFITypeIndex>(v->type_index)) {
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFINone, void *);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIInt, int64_t);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIFloat, double);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIPtr, void *);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIDevice, DLDevice);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIDataType, DLDataType);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIRawStr, const char *);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIStr, const char *);
- default: {
- os << TypeIndex2TypeKey(v->type_index) << '@' << v->v_ptr;
- break;
- }
- }
-#undef TVM_FFI_TYPE_INDEX_SWITCH_CASE
-}
-
-template <typename DerivedType>
-struct StaticIsInstanceOf {
- static TVM_FFI_INLINE bool Check(int32_t type_index) {
-#define TVM_FFI_TYPE_INDEX_SWITCH_CASE(TypeIndex_, Type) \
- case TypeIndex_: { \
- return StaticIsInstanceOf<DerivedType>::CheckImpl<Type>(); \
- }
- switch (static_cast<TVMFFITypeIndex>(type_index)) {
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIObject, Object);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIList, List);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIDict, Dict);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIError, Error);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIFunc, Func);
- TVM_FFI_TYPE_INDEX_SWITCH_CASE(TVMFFITypeIndex::kTVMFFIStr, Str);
- default:
- return false;
- }
-#undef TVM_FFI_TYPE_INDEX_SWITCH_CASE
- TVM_FFI_UNREACHABLE();
- }
-
-private:
- template <typename SelfType>
- static constexpr bool CheckImpl() {
- constexpr auto ancestors = SelfType::_type_ancestors;
- constexpr int32_t ancestors_depth = static_cast<int32_t>(ancestors.size());
- constexpr int32_t type_depth = DerivedType::_type_depth;
- return ancestors_depth > type_depth && ancestors[type_depth] == DerivedType::_type_index;
- }
-};
-
-template <typename DerivedType, typename SelfType = Object>
-TVM_FFI_INLINE bool IsInstanceOf(int32_t type_index) {
- // TODO: Support non-Object types
- if constexpr (std::is_same_v<DerivedType, Object> ||
- std::is_base_of_v</*base=*/DerivedType, /*derived=*/SelfType>) {
- return true;
- }
- // Special case: `DerivedType` is exactly the underlying type of `type_index`
- if (type_index == DerivedType::_type_index) {
- return true;
- }
- // Given an index `i = DerivedType::_type_index`,
- // and the underlying type of `T = *this`, we wanted to check if
- // `T::_type_ancestors[i] == DerivedType::_type_index`.
- //
- // There are 3 ways to reflect `T` out of `this->type_index`:
- // (Case 1) Use `SelfType` as a surrogate type if `SelfType::_type_ancestors`
- // is long enough, whose length is reflected by `SelfType::_type_depth`.
- if constexpr (SelfType::_type_depth > DerivedType::_type_depth) {
- return SelfType::_type_ancestors[DerivedType::_type_depth] == DerivedType::_type_index;
- }
- if constexpr (SelfType::_type_depth == DerivedType::_type_depth) {
- return SelfType::_type_index == DerivedType::_type_index;
- }
- // (Case 2) If `type_index` falls in static object section, use switch case to
- // enumerate all possibilities. It could be more efficient because checks can
- // potentially be simplified by the compiler.
- if (details::IsTypeIndexPOD(type_index)) {
- return ::tvm::ffi::details::StaticIsInstanceOf<DerivedType>::Check(type_index);
- }
- // (Case 3) Look up the type table for type hierarchy via `type_index`.
-#if TVM_FFI_ALLOW_DYN_TYPE
- TVMFFITypeInfoHandle info;
- TVMFFIDynTypeIndex2Info(nullptr, type_index, &info);
- if (info == nullptr) {
- TVM_FFI_THROW(InternalError) << "Undefined type index: " << type_index;
- }
- return info->type_depth > DerivedType::_type_depth &&
- info->type_ancestors[DerivedType::_type_depth] == DerivedType::_type_index;
-#else
- if (type_index > static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDynObjectBegin)) {
- TVM_FFI_THROW(BuildError) << "Dynamic type is not supported. Please set "
- "`TVM_FFI_ALLOW_DYN_TYPE=ON` when building TVM FFI";
- }
- return false;
-#endif
-}
-
-} // namespace details
-
-TVM_FFI_INLINE const char *DLDeviceType2Str(DLDeviceType type) {
- switch (type) {
- case kDLCPU:
- return "cpu";
- case kDLCUDA:
- return "cuda";
- case kDLCUDAHost:
- return "cuda_host";
- case kDLOpenCL:
- return "opencl";
- case kDLVulkan:
- return "vulkan";
- case kDLMetal:
- return "mps";
- case kDLVPI:
- return "vpi";
- case kDLROCM:
- return "rocm";
- case kDLROCMHost:
- return "rocm_host";
- case kDLExtDev:
- return "ext_dev";
- case kDLCUDAManaged:
- return "cuda_managed";
- case kDLOneAPI:
- return "oneapi";
- case kDLWebGPU:
- return "webgpu";
- case kDLHexagon:
- return "hexagon";
- case kDLMAIA:
- return "maia";
- }
- return "unknown";
-}
-
-TVM_FFI_INLINE const char *DLDataTypeCode2Str(DLDataTypeCode type_code) {
- switch (type_code) {
- case kDLInt:
- return "int";
- case kDLUInt:
- return "uint";
- case kDLFloat:
- return "float";
- case kDLOpaqueHandle:
- return "ptr";
- case kDLBfloat:
- return "bfloat";
- case kDLComplex:
- return "complex";
- case kDLBool:
- return "bool";
- }
- return "unknown";
-}
-
-/********** TypeTraits: Object *********/
-
-template <typename ObjectType>
-struct TypeTraitsDefaultForObject {
- static constexpr bool enabled = true;
-
- TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(ObjectType *src, TVMFFIAny *ret) {
- if (src == nullptr) {
- ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone);
- ret->v_obj = nullptr;
- } else {
- ret->type_index = src->type_index;
- ret->v_obj = const_cast<TVMFFIAny *>(reinterpret_cast<const TVMFFIAny *>(src));
- }
- }
-
- TVM_FFI_INLINE static ObjectType *CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
- if (details::IsTypeIndexNone(v->type_index)) {
- return nullptr;
- }
- if (!details::IsTypeIndexPOD(v->type_index) &&
- details::IsInstanceOf<ObjectType>(v->type_index)) {
- return reinterpret_cast<ObjectType *>(v->v_obj);
- }
- throw TemporaryTypeError();
- }
-
- TVM_FFI_INLINE static ObjectType *CopyFromTVMFFIAnyToRef(const TVMFFIAny *v) {
- return CopyFromTVMFFIAnyToType(v);
- }
-
- TVM_FFI_INLINE static ObjectType *MoveFromTVMFFIAnyToRef(TVMFFIAny *v) {
- if (details::IsTypeIndexNone(v->type_index)) {
- return nullptr;
- }
- if (!details::IsTypeIndexPOD(v->type_index) &&
- details::IsInstanceOf<ObjectType>(v->type_index)) {
- ObjectType *ret = reinterpret_cast<ObjectType *>(v->v_obj);
- v->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone);
- v->v_obj = nullptr;
- return ret;
- }
- throw TemporaryTypeError();
- }
-
- TVM_FFI_INLINE static std::string Type2Str() { return ObjectType::_type_key + std::string(" *"); }
-};
-
-template <typename ObjectType>
-struct TypeTraits<ObjectType *, std::enable_if_t<IsObject<ObjectType>>>
- : public TypeTraitsDefaultForObject<ObjectType> {};
-
-/********** TypeTraits: Integer *********/
-
-template <typename Int>
-struct TypeTraits<Int, std::enable_if_t<std::is_integral_v<Int>>> {
- static constexpr bool enabled = true;
- TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(Int src, TVMFFIAny *ret) {
- ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIInt);
- ret->v_int64 = static_cast<int64_t>(src);
- }
-
- TVM_FFI_INLINE static Int CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
- TVMFFITypeIndex type_index = static_cast<TVMFFITypeIndex>(v->type_index);
- if (type_index == TVMFFITypeIndex::kTVMFFIInt) {
- return static_cast<Int>(v->v_int64);
- }
- throw TemporaryTypeError();
- }
-
- TVM_FFI_INLINE static std::string Type2Str() {
- return TypeIndexTraits<TVMFFITypeIndex::kTVMFFIInt>::type_key;
- }
-
- TVM_FFI_INLINE static std::string __str__(int64_t src) { return std::to_string(src); }
-};
-
-/********** TypeTraits: Float *********/
-
-template <typename Float>
-struct TypeTraits<Float, std::enable_if_t<std::is_floating_point_v<Float>>> {
- static constexpr bool enabled = true;
- TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(Float src, TVMFFIAny *ret) {
- ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIFloat);
- ret->v_float64 = src;
- }
-
- TVM_FFI_INLINE static Float CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
- TVMFFITypeIndex type_index = static_cast<TVMFFITypeIndex>(v->type_index);
- if (type_index == TVMFFITypeIndex::kTVMFFIFloat) {
- return v->v_float64;
- } else if (type_index == TVMFFITypeIndex::kTVMFFIInt) {
- return static_cast<Float>(v->v_int64);
- }
- throw TemporaryTypeError();
- }
-
- TVM_FFI_INLINE static std::string Type2Str() {
- return TypeIndexTraits<TVMFFITypeIndex::kTVMFFIFloat>::type_key;
- }
-
- TVM_FFI_INLINE static std::string __str__(double src) { return std::to_string(src); }
-};
-
-/********** TypeTraits: Opaque Pointer *********/
-
-template <>
-struct TypeTraits<void *> {
- static constexpr bool enabled = true;
- using Ptr = void *;
-
- TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(Ptr src, TVMFFIAny *ret) {
- ret->type_index = (src == nullptr) ? static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone)
- : static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIPtr);
- ret->v_ptr = src;
- }
-
- TVM_FFI_INLINE static Ptr CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
- TVMFFITypeIndex type_index = static_cast<TVMFFITypeIndex>(v->type_index);
- if (type_index == TVMFFITypeIndex::kTVMFFIPtr || type_index == TVMFFITypeIndex::kTVMFFIRawStr ||
- type_index == TVMFFITypeIndex::kTVMFFINone) {
- return v->v_ptr;
- }
- throw TemporaryTypeError();
- }
-
- TVM_FFI_INLINE static std::string Type2Str() {
- return TypeIndexTraits<TVMFFITypeIndex::kTVMFFIPtr>::type_key;
- }
-
- TVM_FFI_INLINE static std::string __str__(Ptr src) {
- if (src == nullptr) {
- return "None";
- } else {
- std::ostringstream oss;
- oss << src;
- return oss.str();
- }
- }
-};
-
-template <>
-struct TypeTraits<std::nullptr_t> : public TypeTraits<void *> {};
-
-/********** TypeTraits: DLDevice *********/
-
-template <>
-struct TypeTraits<DLDevice> {
- static constexpr bool enabled = true;
- TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(DLDevice src, TVMFFIAny *ret) {
- ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDevice);
- ret->v_device = src;
- }
-
- TVM_FFI_INLINE static DLDevice CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
- TVMFFITypeIndex type_index = static_cast<TVMFFITypeIndex>(v->type_index);
- if (type_index == TVMFFITypeIndex::kTVMFFIDevice) {
- return v->v_device;
- }
- throw TemporaryTypeError();
- }
-
- TVM_FFI_INLINE static std::string Type2Str() {
- return TypeIndexTraits<TVMFFITypeIndex::kTVMFFIDevice>::type_key;
- }
-
- TVM_FFI_INLINE static std::string __str__(DLDevice device) {
- std::ostringstream os;
- os << DLDeviceType2Str(static_cast<DLDeviceType>(device.device_type)) << ":"
- << device.device_id;
- return os.str();
- }
-};
-
-/********** TypeTraits: DLDataType *********/
-
-template <>
-struct TypeTraits<DLDataType> {
- static constexpr bool enabled = true;
- TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(DLDataType src, TVMFFIAny *ret) {
- ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIDataType);
- ret->v_dtype = src;
- }
-
- TVM_FFI_INLINE static DLDataType CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
- TVMFFITypeIndex type_index = static_cast<TVMFFITypeIndex>(v->type_index);
- if (type_index == TVMFFITypeIndex::kTVMFFIDataType) {
- return v->v_dtype;
- }
- throw TemporaryTypeError();
- }
-
- TVM_FFI_INLINE static std::string Type2Str() {
- return TypeIndexTraits<TVMFFITypeIndex::kTVMFFIDataType>::type_key;
- }
-
- TVM_FFI_INLINE static std::string __str__(DLDataType dtype) {
- DLDataTypeCode code = static_cast<DLDataTypeCode>(dtype.code);
- int32_t bits = dtype.bits;
- int32_t lanes = dtype.lanes;
- if (code == kDLUInt && bits == 1 && lanes == 1) {
- return "bool";
- }
- if (code == kDLOpaqueHandle && bits == 0 && lanes == 0) {
- return "void";
- }
- std::ostringstream os;
- os << DLDataTypeCode2Str(code) << bits;
- if (lanes != 1) {
- os << "x" << lanes;
- }
- return os.str();
- }
-};
-
-/********** TypeTraits: String *********/
-
-template <>
-struct TypeTraits<const char *> {
- static constexpr bool enabled = true;
-
- TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(const char *src, TVMFFIAny *ret) {
- ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr);
- ret->v_str = src;
- }
-
- TVM_FFI_INLINE static const char *CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
- TVMFFITypeIndex type_index = static_cast<TVMFFITypeIndex>(v->type_index);
- if (type_index == TVMFFITypeIndex::kTVMFFIRawStr) {
- return v->v_str;
- }
- if (type_index == TVMFFITypeIndex::kTVMFFIStr) {
- return reinterpret_cast<TVMFFIStr *>(v->v_obj)->data;
- }
- throw TemporaryTypeError();
- }
-
- TVM_FFI_INLINE static std::string Type2Str() {
- return TypeIndexTraits<TVMFFITypeIndex::kTVMFFIRawStr>::type_key;
- }
-
- TVM_FFI_INLINE static std::string __str__(const char *src) {
- return '"' + std::string(src) + '"';
- }
-};
-
-template <size_t N>
-struct TypeTraits<char[N]> : public TypeTraits<const char *> {};
-
-template <>
-struct TypeTraits<std::string> {
- static constexpr bool enabled = true;
-
- TVM_FFI_INLINE static void CopyFromTypeToTVMFFIAny(const std::string &src, TVMFFIAny *ret) {
- ret->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr);
- ret->v_str = src.data();
- }
-
- TVM_FFI_INLINE static std::string CopyFromTVMFFIAnyToType(const TVMFFIAny *v) {
- return TypeTraits<const char *>::CopyFromTVMFFIAnyToType(v);
- }
-
- TVM_FFI_INLINE static std::string Type2Str() { return "str"; }
-
- TVM_FFI_INLINE static std::string __str__(const char *src) {
- return '"' + std::string(src) + '"';
- }
-};
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
-} // namespace ffi
-} // namespace tvm
-
-#endif // TVM_FFI_TRAITS_H_
diff --git a/include/tvm/ffi/core/utils.h b/include/tvm/ffi/core/utils.h
deleted file mode 100644
index 93630058e6..0000000000
--- a/include/tvm/ffi/core/utils.h
+++ /dev/null
@@ -1,281 +0,0 @@
-#ifndef TVM_FFI_UTILS_H_
-#define TVM_FFI_UTILS_H_
-#ifdef _MSC_VER
-#include <intrin.h>
-#pragma intrinsic(_BitScanReverse64)
-#pragma intrinsic(_InterlockedIncrement)
-#pragma intrinsic(_InterlockedDecrement)
-#endif
-#if __cplusplus >= 202002L
-#include <bit>
-#endif
-#include "./c_ffi_abi.h"
-#include <array>
-#include <cstddef>
-#include <sstream>
-#include <type_traits>
-
-namespace tvm {
-namespace ffi {
-
-/********** Section 1. Macros *********/
-
-#if defined(_MSC_VER)
-#define TVM_FFI_INLINE __forceinline
-#else
-#define TVM_FFI_INLINE inline __attribute__((always_inline))
-#endif
-
-#if defined(_MSC_VER)
-#define TVM_FFI_UNREACHABLE() __assume(false)
-#else
-#define TVM_FFI_UNREACHABLE() __builtin_unreachable()
-#endif
-
-#if defined(__GNUC__) || defined(__clang__)
-#define TVM_FFI_SYMBOL_HIDE __attribute__((visibility("hidden")))
-#else
-#define TVM_FFI_SYMBOL_HIDE
-#endif
-
-#if defined(__GNUC__) || defined(__clang__)
-#define TVM_FFI_FUNC_SIG __PRETTY_FUNCTION__
-#elif defined(_MSC_VER)
-#define TVM_FFI_FUNC_SIG __FUNCSIG__
-#else
-#define TVM_FFI_FUNC_SIG __func__
-#endif
-
-#define TVM_FFI_DEF_ASSIGN(SelfType, SourceType) \
- TVM_FFI_INLINE SelfType &operator=(SourceType other) { \
- SelfType(std::move(other)).Swap(*this); \
- return *this; \
- }
-
-#define TVM_FFI_THROW(ErrorKind) \
- ::tvm::ffi::details::ErrorBuilder(#ErrorKind, __FILE__, TVM_FFI_FUNC_SIG, __LINE__).Get()
-
-/********** Section 2. Any/Object: Extensions and inheritance *********/
-
-template <typename TExtra>
-struct AnyWithExtra {
- int32_t type_index;
- int32_t ref_cnt;
- void (*deleter)(void *);
- TExtra _extra;
- TVM_FFI_INLINE AnyWithExtra() = default;
- // TODO: add tests for copy/move constructors
- TVM_FFI_INLINE AnyWithExtra(const AnyWithExtra &other) : _extra(other._extra) {}
- TVM_FFI_INLINE AnyWithExtra(AnyWithExtra &&other) : _extra(std::move(other._extra)) {}
- TVM_FFI_INLINE AnyWithExtra &operator=(const AnyWithExtra &other) {
- this->_extra = other._extra;
- return *this;
- }
- TVM_FFI_INLINE AnyWithExtra &operator=(AnyWithExtra &&other) {
- this->_extra = std::move(other._extra);
- return *this;
- }
-};
-
-struct AnyView;
-struct Any;
-struct Object;
-struct Dict;
-struct Error;
-struct Str;
-struct Func;
-template <typename>
-struct Ref;
-template <typename>
-struct RefBase;
-struct List;
-
-template <typename T>
-struct IsAnyWithExtra {
- template <typename U>
- static auto test(AnyWithExtra<U> *) -> std::true_type;
- static auto test(...) -> std::false_type;
- using type = decltype(test(std::declval<T *>()));
-};
-template <typename T>
-constexpr static bool IsObject = std::is_base_of_v<Object, T> || IsAnyWithExtra<T>::type::value;
-
-template <typename ObjectType>
-struct DefaultObjectAllocator {
- using Storage = typename std::aligned_storage<sizeof(ObjectType), alignof(ObjectType)>::type;
-
- template <typename... Args>
- inline static ObjectType *NewImpl(size_t num_storages, Args &&...args) {
- Storage *data = new Storage[num_storages];
- try {
- new (data) ObjectType(std::forward<Args>(args)...);
- } catch (...) {
- delete[] data;
- throw;
- }
- ObjectType *ret = reinterpret_cast<ObjectType *>(data);
- ret->type_index = ObjectType::_type_index;
- ret->ref_cnt = 0;
- ret->deleter = DefaultObjectAllocator::Deleter;
- return ret;
- }
-
- template <typename... Args>
- TVM_FFI_INLINE static ObjectType *New(Args &&...args) {
- return NewImpl(1, std::forward<Args>(args)...);
- }
-
- template <typename PadType, typename... Args>
- TVM_FFI_INLINE static ObjectType *NewWithPad(size_t pad_size, Args &&...args) {
- return NewImpl((sizeof(ObjectType) + pad_size * sizeof(PadType) + sizeof(Storage) - 1) /
- sizeof(Storage),
- std::forward<Args>(args)...);
- }
-
- static void Deleter(void *objptr) {
- ObjectType *tptr = static_cast<ObjectType *>(objptr);
- tptr->ObjectType::~ObjectType();
- delete[] reinterpret_cast<Storage *>(tptr);
- }
-};
-
-template <typename T, typename = void>
-struct GetAllocator {
- using Type = DefaultObjectAllocator<T>;
-};
-template <typename T>
-struct GetAllocator<T, std::void_t<typename T::Allocator>> {
- using Type = typename T::Allocator;
-};
-
-template <size_t N>
-using CharArray = const char[N];
-
-/********** Section 3. Atomic Operations *********/
-
-namespace details {
-uint64_t TVMFFIStrHash(const TVMFFIStr *str);
-int32_t TVMFFIStrCmp(const TVMFFIStr *a, const TVMFFIStr *b);
-TVM_FFI_INLINE int32_t AtomicIncrementRelaxed(int32_t *ptr) {
-#ifdef _MSC_VER
- return _InterlockedIncrement(reinterpret_cast<volatile long *>(ptr)) - 1;
-#else
- return __atomic_fetch_add(ptr, 1, __ATOMIC_RELAXED);
-#endif
-}
-TVM_FFI_INLINE int32_t AtomicDecrementRelAcq(int32_t *ptr) {
-#ifdef _MSC_VER
- return _InterlockedDecrement(reinterpret_cast<volatile long *>(ptr)) + 1;
-#else
- return __atomic_fetch_sub(ptr, 1, __ATOMIC_ACQ_REL);
-#endif
-}
-
-TVM_FFI_INLINE void IncRef(TVMFFIObject *obj) {
- if (obj != nullptr) {
- AtomicIncrementRelaxed(&obj->ref_cnt);
- }
-}
-
-TVM_FFI_INLINE void DecRef(TVMFFIObject *obj) {
- if (obj != nullptr) {
- if (AtomicDecrementRelAcq(&obj->ref_cnt) == 1) {
- if (obj->deleter) {
- obj->deleter(obj);
- }
- }
- }
-}
-
-TVM_FFI_INLINE bool IsTypeIndexNone(int32_t type_index) {
- return type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone);
-}
-
-TVM_FFI_INLINE bool IsTypeIndexPOD(int32_t type_index) {
- return type_index < static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStaticObjectBegin);
-}
-
-} // namespace details
-
-/********** Section 4. Errors *********/
-
-struct TemporaryTypeError : public std::exception {};
-
-namespace details {
-
-template <typename T>
-using RemoveCR = std::remove_const_t<std::remove_reference_t<T>>;
-template <typename T>
-static constexpr bool IsAnyOrView = std::is_same_v<T, Any> || std::is_same_v<T, AnyView>;
-
-template <typename>
-struct IsRefImpl {
- static constexpr bool value = false;
-};
-
-template <typename T>
-struct IsRefImpl<Ref<T>> {
- static constexpr bool value = true;
-};
-template <typename T>
-constexpr bool IsRef = IsRefImpl<T>::value;
-
-void AnyView2Str(std::ostream &os, const TVMFFIAny *v);
-TVMFFIObject *StrMoveFromStdString(std::string &&source);
-TVMFFIObject *StrCopyFromCharArray(const char *source, size_t length);
-[[noreturn]] void TVMErrorFromBuilder(std::string &&kind, std::string &&lineno,
- std::string &&message) noexcept(false);
-
-template <typename ParentType, size_t size>
-inline constexpr std::array<int32_t, size> ObjectAncestorsConstExpr() {
- std::array<int32_t, size> ret{};
- for (size_t i = 0; i < size; ++i) {
- ret[i] = (i + 1 == size) ? (ParentType::_type_index) : (ParentType::_type_ancestors[i]);
- }
- return ret;
-}
-template <typename ParentType, size_t size>
-inline std::array<int32_t, size> ObjectAncestors() {
- std::array<int32_t, size> ret{};
- for (size_t i = 0; i < size; ++i) {
- ret[i] = (i + 1 == size) ? (ParentType::_type_index) : (ParentType::_type_ancestors[i]);
- }
- return ret;
-}
-
-// Disable warning about throwing in the destructor
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4722) // throw in the destructor, which is expected behavior
-#endif
-
-struct ErrorBuilder {
- const char *kind;
- TVMFFIStackFrame frame;
- std::ostringstream oss;
- std::string line_info;
-
- explicit ErrorBuilder(const char *kind, const char *filename, const char *func, int32_t lineno)
- : kind(kind), frame{filename, func, lineno, ErrorBuilder::StackFrameDeleter} {
- this->line_info = std::string(filename) + ":" + std::to_string(lineno);
- }
-
- [[noreturn]] ~ErrorBuilder() noexcept(false) {
- details::TVMErrorFromBuilder(this->kind, std::move(this->line_info), oss.str());
- throw;
- }
-
- std::ostringstream &Get() { return this->oss; }
-
-protected:
- static void StackFrameDeleter(void *self) { delete static_cast<TVMFFIStackFrame *>(self); }
-};
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
-} // namespace details
-} // namespace ffi
-} // namespace tvm
-#endif // TVM_FFI_UTILS_H_
diff --git a/include/tvm/ffi/ext/dict.h b/include/tvm/ffi/ext/dict.h
deleted file mode 100644
index e88a29b5e6..0000000000
--- a/include/tvm/ffi/ext/dict.h
+++ /dev/null
@@ -1,515 +0,0 @@
-#ifndef TVM_FFI_DICT_H_
-#define TVM_FFI_DICT_H_
-#include <iterator>
-#include <tvm/ffi/core/core.h>
-#include <type_traits>
-#include <unordered_map>
-
-namespace tvm {
-namespace ffi {
-namespace details {
-struct _DictHeader {
- int64_t capacity;
- int64_t size;
-};
-using DictHeader = AnyWithExtra<_DictHeader>;
-static_assert(sizeof(DictHeader) == sizeof(TVMFFIDict));
-static_assert(offsetof(DictHeader, _extra.capacity) == offsetof(TVMFFIDict, capacity));
-static_assert(offsetof(DictHeader, _extra.size) == offsetof(TVMFFIDict, size));
-int32_t CountLeadingZeros(uint64_t);
-uint64_t BitCeil(uint64_t);
-} // namespace details
-
-/*!
- * Dict is a flat hash table that uses quadratic/triangluar probing [1] for collision resolution.
- * Its underlying data structure is a consecutive array of `Block`s, where each `Block`
- * contains `kCapacity = 16` key-value pairs and their metadata (see also `Block`).
- * The capacity of a Dict is always a power of 2, and Fibonacci Hashing [2] is used to avoid integer
- * modulo and help improve the distribution of the hash values.
- *
- * Probing is conducted only within a fixed set of 125 possible offsets (see also
- * `kNextProbeLocation`), which effectively organizes the elements of collision chains in a linked
- * list. With the help of metadata, we can easily iterate through the linked list and relocate
- * elements whenever necessary.
- *
- * [1] Triangular numbers. https://fgiesen.wordpress.com/2015/02/22/triangular-numbers-mod-2n/
- * [2] Fibonacci Hashing: https://programmingpraxis.com/2018/06/19/fibonacci-hash/
- */
-template <typename Hash, typename Equal>
-struct DictBase : protected details::DictHeader {
-protected:
- /* clang-format off */
- inline static const constexpr uint64_t kNextProbeLocation[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- /* triangle numbers for quadratic probing */ 21, 28, 36, 45, 55, 66, 78, 91, 105, 120, 136, 153, 171, 190, 210, 231, 253, 276, 300, 325, 351, 378, 406, 435, 465, 496, 528, 561, 595, 630, 666, 703, 741, 780, 820, 861, 903, 946, 990, 1035, 1081, 1128, 1176, 1225, 1275, 1326, 1378, 1431, 1485, 1540, 1596, 1653, 1711, 1770, 1830, 1891, 1953, 2016, 2080, 2145, 2211, 2278, 2346, 2415, 2485, 2556, 2628,
- /* larger triangle numbers */ 8515, 19110, 42778, 96141, 216153, 486591, 1092981, 2458653, 5532801, 12442566, 27993903, 62983476, 141717030, 318844378, 717352503, 1614057336, 3631522476, 8170957530, 18384510628, 41364789378, 93070452520, 209408356380, 471168559170, 1060128894105, 2385289465695, 5366898840628, 12075518705635, 27169915244790, 61132312065111, 137547689707000, 309482283181501, 696335127828753, 1566753995631385, 3525196511162271, 7931691992677701, 17846306936293605, 40154 [...]
- };
- /* clang-format on */
- static constexpr int32_t kBlockCapacity = 16;
- static constexpr uint8_t kNumProbe = std::size(kNextProbeLocation);
- static constexpr uint8_t kEmptySlot = uint8_t(0b11111111);
- static constexpr uint8_t kProtectedSlot = uint8_t(0b11111110);
- static constexpr uint64_t kMinSize = 7;
- static constexpr uint8_t kNewHead = 0b00000000;
- static constexpr uint8_t kNewTail = 0b10000000;
- using KVPair = std::pair<TVMFFIAny, TVMFFIAny>;
-
- struct Block {
- /**
- * Each block has a 8-byte key (TVMFFIAny), 8-byte value (TVMFFIAny), and a 1-byte metadata.
- *
- * Metadata can be one of the following three cases:
- * - 1) Empty: 0xFF (0b11111111)_2. The slot is available and can be written in.
- * - 2) Protected: 0xFE (0b11111110)_2. The slot is empty but not allowed to be written. It is
- * only used during insertion when relocating certain elements.
- * - 3) Normal: (0bXYYYYYYY)_2. The highest bit `X` indicates if it is the head of a linked
- * list, where `0` means head, and `1` means not head. The next 7 bits `YYYYYYY` are used as
- * the "next pointer" (i.e. pointer to the next element), where `kNextProbeLocation[YYYYYYY]`
- * is the offset to the next element. And if `YYYYYYY == 0`, it means the end of the linked
- * list.
- */
- uint8_t meta[kBlockCapacity];
- KVPair data[kBlockCapacity];
- };
- static_assert(sizeof(Block) == kBlockCapacity * (1 + sizeof(TVMFFIAny) * 2), "ABI check");
- static_assert(std::is_aggregate_v<Block>, "ABI check");
- static_assert(std::is_trivially_destructible_v<Block>, "ABI check");
- static_assert(std::is_standard_layout_v<Block>, "ABI check");
- static_assert(kNumProbe == 126, "Check assumption");
-
- struct ListIter {
- static ListIter None() { return ListIter(0, nullptr); }
- static ListIter FromIndex(const DictBase *self, uint64_t i) {
- if (i >= self->Cap()) // TODO: remove this check
- TVM_FFI_THROW(InternalError) << "Indexing " << i << " of length " << self->Cap();
- return ListIter(i, self->Blocks() + (i / kBlockCapacity));
- }
- static ListIter FromHash(const DictBase *self, uint64_t h) {
- return ListIter::FromIndex(self, (11400714819323198485ull * h) >>
- (details::CountLeadingZeros(self->Cap()) + 1));
- }
- auto &Data() const { return cur->data[i % kBlockCapacity]; }
- uint8_t &Meta() const { return cur->meta[i % kBlockCapacity]; }
- uint64_t Offset() const { return kNextProbeLocation[Meta() & 0b01111111]; }
- bool IsHead() const { return (Meta() & 0b10000000) == 0b00000000; }
- void SetNext(uint8_t jump) const { (Meta() &= 0b10000000) |= jump; }
- void Advance(const DictBase *self) { *this = WithOffset(self, Offset()); }
- bool IsNone() const { return cur == nullptr; }
- ListIter WithOffset(const DictBase *self, uint64_t offset) const {
- return offset == 0 ? ListIter::None()
- : ListIter::FromIndex(self, (i + offset) & (self->Cap() - 1));
- }
-
- uint64_t i;
- Block *cur;
-
- protected:
- ListIter() = default;
- explicit ListIter(uint64_t i, Block *cur) : i(i), cur(cur) {}
- };
-
- ListIter Head(uint64_t hash) const {
- ListIter iter = ListIter::FromHash(this, hash);
- return iter.IsHead() ? iter : ListIter::None();
- }
-
- ListIter Prev(ListIter iter) const {
- ListIter prev = Head(Hash()(iter.Data().first));
- ListIter next = prev;
- for (next.Advance(this); next.i != iter.i; prev = next, next.Advance(this)) {
- }
- return prev;
- }
-
- uint64_t Cap() const { return static_cast<uint64_t>(this->_extra.capacity); }
- uint64_t Size() const { return this->_extra.size; }
- Block *Blocks() { return reinterpret_cast<Block *>(static_cast<DictBase *>(this) + 1); }
- Block *Blocks() const { return const_cast<DictBase *>(this)->Blocks(); }
-
- ListIter Probe(ListIter cur, uint8_t *result) const {
- for (uint8_t i = 1; i < kNumProbe && kNextProbeLocation[i] < this->Size(); ++i) {
- ListIter next = cur.WithOffset(this, kNextProbeLocation[i]);
- if (next.Meta() == kEmptySlot) {
- *result = i;
- return next;
- }
- }
- *result = 0;
- return ListIter::None();
- }
-
- ListIter Lookup(const TVMFFIAny &key) const {
- uint64_t hash = Hash()(key);
- for (ListIter iter = Head(hash); !iter.IsNone(); iter.Advance(this)) {
- if (Equal()(key, iter.Data().first)) {
- return iter;
- }
- }
- return ListIter::None();
- }
-
- template <typename Pred>
- void IterateAll(Pred pred) {
- Block *blocks_ = this->Blocks();
- int64_t num_blocks = this->_extra.capacity / kBlockCapacity;
- for (int64_t i = 0; i < num_blocks; ++i) {
- for (int j = 0; j < kBlockCapacity; ++j) {
- uint8_t &meta = blocks_[i].meta[j];
- auto &data = blocks_[i].data[j];
- if (meta != kEmptySlot && meta != kProtectedSlot) {
- pred(&meta, &data.first, &data.second);
- }
- }
- }
- }
-
- void Clear() {
- this->IterateAll([](uint8_t *meta, TVMFFIAny *key, TVMFFIAny *value) {
- static_cast<Any *>(key)->Reset();
- static_cast<Any *>(value)->Reset();
- *meta = kEmptySlot;
- });
- this->_extra.size = 0;
- }
-
- void Erase(int64_t index) {
- ListIter iter = ListIter::FromIndex(this, index);
- if (uint64_t offset = iter.Offset(); offset != 0) {
- ListIter prev = iter;
- ListIter next = iter.WithOffset(this, offset);
- while ((offset = next.Offset()) != 0) {
- prev = next;
- next = next.WithOffset(this, offset);
- }
- iter.Data() = next.Data();
- next.Meta() = kEmptySlot;
- prev.SetNext(0);
- } else {
- if (!iter.IsHead()) {
- Prev(iter).SetNext(0);
- }
- iter.Meta() = kEmptySlot;
- auto &data = iter.Data();
- static_cast<Any &>(data.first).Reset();
- static_cast<Any &>(data.second).Reset();
- }
- this->_extra.size -= 1;
- }
-
-public:
- struct Iterator;
- friend struct Iterator;
- struct Iterator {
- using iterator_category = std::bidirectional_iterator_tag;
- using difference_type = std::ptrdiff_t;
- using value_type = std::pair<const Any, Any>;
- using pointer = value_type *;
- using reference = value_type &;
- static_assert(sizeof(value_type) == sizeof(KVPair), "ABI check");
- static_assert(offsetof(value_type, first) == offsetof(KVPair, first), "ABI check");
- static_assert(offsetof(value_type, second) == offsetof(KVPair, second), "ABI check");
-
- Iterator() = default;
- Iterator(const Iterator &) = default;
- Iterator(Iterator &&) = default;
- Iterator &operator=(const Iterator &) = default;
- Iterator &operator=(Iterator &&) = default;
-
- Iterator &operator++() {
- int64_t cap = self->Cap();
- while (++index < cap) {
- if (ListIter::FromIndex(self, index).Meta() != DictBase::kEmptySlot) {
- return *this;
- }
- }
- return *this;
- }
- Iterator &operator--() {
- while (--index >= 0) {
- if (ListIter::FromIndex(self, index).Meta() != DictBase::kEmptySlot) {
- return *this;
- }
- }
- return *this;
- }
- Iterator operator++(int) {
- Iterator copy(*this);
- this->operator++();
- return copy;
- }
- Iterator operator--(int) {
- Iterator copy(*this);
- this->operator--();
- return copy;
- }
- bool operator==(const Iterator &other) const { return index == other.index; }
- bool operator!=(const Iterator &other) const { return index != other.index; }
- reference operator*() const { return *(this->operator->()); }
- pointer operator->() const {
- return reinterpret_cast<pointer>(&ListIter::FromIndex(self, this->index).Data());
- }
-
- protected:
- friend struct DictBase;
- Iterator(int64_t index, DictBase *self) : index(index), self(self) {}
-
- int64_t index;
- DictBase *self;
- };
-
- KVPair *TryInsertOrLookup(TVMFFIAny *key) {
- if (Cap() == Size() || Size() + 1 > Cap() * 0.99) {
- return nullptr;
- }
- // `iter` starts from the head of the linked list
- ListIter iter = ListIter::FromHash(this, Hash()(*key));
- uint8_t new_meta = kNewHead;
- // There are three cases over all:
- // 1) available - `iter` points to an empty slot that we could directly write in;
- // 2) hit - `iter` points to the head of the linked list which we want to iterate through
- // 3) relocate - `iter` points to the body of a different linked list, and in this case, we
- // will have to relocate the elements to make space for the new element
- if (iter.Meta() == kEmptySlot) { // (Case 1) available
- // Do nothing
- } else if (iter.IsHead()) { // (Case 2) hit
- // Point `iter` to the last element of the linked list
- for (ListIter prev = iter;; prev = iter) {
- if (Equal()(*key, iter.Data().first)) {
- return &iter.Data();
- }
- iter.Advance(this);
- if (iter.IsNone()) {
- iter = prev;
- break;
- }
- }
- // Prob at the end of the linked list for the next empty slot
- ListIter prev = iter;
- uint8_t jump;
- iter = Probe(iter, &jump);
- if (jump == 0) {
- return nullptr;
- }
- prev.SetNext(jump);
- new_meta = kNewTail;
- } else {
- // (Case 3) relocate: Chop the list starting from `iter`, and move it to other locations.
- ListIter next = iter, prev = Prev(iter);
- // Loop invariant:
- // - `next` points to the first element of the list we are going to relocate.
- // - `prev` points to the last element of the list that we are relocating to.
- for (uint8_t next_meta = kProtectedSlot; !next.IsNone(); next_meta = kEmptySlot) {
- // Step 1. Prob for the next empty slot `new_next` after `prev`
- uint8_t jump;
- ListIter new_next = Probe(prev, &jump);
- if (jump == 0) {
- return nullptr;
- }
- // Step 2. Relocate `next` to `new_next`
- new_next.Meta() = kNewTail;
- new_next.Data() = {next.Data().first, next.Data().second};
- std::swap(next_meta, next.Meta());
- prev.SetNext(jump);
- // Step 3. Update `prev` -> `new_next`, `next` -> `Advance(next)`
- prev = new_next;
- next = next.WithOffset(this, kNextProbeLocation[next_meta & 0b01111111]);
- }
- }
- this->_extra.size += 1;
- iter.Meta() = new_meta;
- auto &data = iter.Data() = {*key, TVMFFIAny()};
- key->type_index = 0;
- key->v_int64 = 0;
- return &data;
- }
-
- Any &at(const Any &key) {
- ListIter iter = Lookup(key);
- if (iter.IsNone()) {
- TVM_FFI_THROW(KeyError) << "Key not found";
- }
- return static_cast<Any &>(iter.Data().second);
- }
-
- const Any &at(const Any &key) const {
- ListIter iter = Lookup(key);
- if (iter.IsNone()) {
- TVM_FFI_THROW(KeyError) << "Key not found";
- }
- return static_cast<const Any &>(iter.Data().second);
- }
- Any &operator[](const Any &key) = delete; // May need rehashing
- const Any &operator[](const Any &key) const { return this->at(key); }
- int64_t size() const { return this->_extra.size; }
- int64_t count(const Any &key) const { return Lookup(key).IsNone() ? 0 : 1; }
- void clear() { this->Clear(); }
- Iterator begin() { return Iterator(-1, this).operator++(); }
- Iterator end() { return Iterator(this->Cap(), this); }
- Iterator find(const Any &key) {
- ListIter iter = Lookup(key);
- return iter.IsNone() ? end() : Iterator(iter.i, this);
- }
- void erase(const Any &key) {
- ListIter iter = Lookup(key);
- if (!iter.IsNone()) {
- Erase(iter.i);
- } else {
- TVM_FFI_THROW(KeyError) << "Key not found";
- }
- }
-};
-
-struct AnyHash {
- uint64_t operator()(const TVMFFIAny &a) const {
- if (a.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- return details::TVMFFIStrHash(reinterpret_cast<TVMFFIStr *>(a.v_obj));
- }
- union {
- int64_t i64;
- uint64_t u64;
- } cvt;
- cvt.i64 = a.v_int64;
- return cvt.u64;
- }
-};
-
-struct AnyEqual {
- bool operator()(const TVMFFIAny &a, const TVMFFIAny &b) const {
- if (a.type_index != b.type_index) {
- return false;
- }
- if (a.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIStr)) {
- return details::TVMFFIStrCmp(reinterpret_cast<TVMFFIStr *>(a.v_obj),
- reinterpret_cast<TVMFFIStr *>(b.v_obj)) == 0;
- }
- return a.v_int64 == b.v_int64;
- }
-};
-
-struct Dict : protected DictBase<AnyHash, AnyEqual> {
- TVM_FFI_DEF_STATIC_TYPE(Dict, Object, TVMFFITypeIndex::kTVMFFIDict);
- TVM_FFI_DEF_TYPE_FRIENDS();
- struct Allocator;
- friend struct Allocator;
-
-protected:
- explicit Dict(int64_t capacity) {
- this->_extra.capacity = capacity;
- this->_extra.size = 0;
- for (int64_t i = 0; i < capacity / kBlockCapacity; ++i) {
- Block &block = Blocks()[i];
- std::memset(block.meta, kEmptySlot, sizeof(block.meta));
- }
- }
- ~Dict() { this->Clear(); }
-};
-
-struct Dict::Allocator {
- static TVM_FFI_INLINE Dict *New(int64_t capacity) {
- if ((capacity & (capacity - 1)) != 0 || capacity % kBlockCapacity != 0) {
- TVM_FFI_THROW(InternalError) << "capacity: " << capacity;
- }
- int64_t num_blocks = capacity / Dict::kBlockCapacity;
- return DefaultObjectAllocator<Dict>::NewWithPad<Dict::Block>(num_blocks, capacity);
- }
- static TVM_FFI_INLINE Dict *New() { return New(0); }
-};
-
-template <>
-struct Ref<Dict> : public RefBase<Dict> {
- using TSelf = Ref<Dict>;
- using TBase = RefBase<Dict>;
- using Iterator = Dict::Iterator;
-
- TVM_FFI_REF_DEF_DELEGATE_CONSTRUCTORS(Ref<Dict>, RefBase<Dict>)
- TVM_FFI_DEF_TYPE_FRIENDS();
-
- Ref() : TBase(Dict::Allocator::New()) {}
- template <typename Iter>
- Ref(Iter begin, Iter end) : TBase(FromRange(begin, end)) {}
- Ref(std::initializer_list<std::pair<Any, Any>> init) : TSelf(init.begin(), init.end()) {}
- template <typename K, typename V, typename Hash, typename Equal>
- Ref(const std::unordered_map<K, V, Hash, Equal> &source) : TSelf(source.begin(), source.end()) {}
- const Any &at(const Any &key) const { return get()->at(key); }
- Any &at(const Any &key) { return get()->at(key); }
- const Any &operator[](const Any &key) const { return get()->at(key); }
- Any &operator[](Any key) { return static_cast<Any &>(InsertOrLookup(std::move(key))->second); }
- int64_t size() const { return get()->size(); }
- int64_t count(const Any &key) const { return get()->count(key); }
- bool empty() const { return size() == 0; }
- void clear() { get()->clear(); }
- Iterator begin() { return get()->begin(); }
- Iterator end() { return get()->end(); }
- Iterator find(const Any &key) { return get()->find(key); }
- void erase(const Any &key) { get()->erase(key); }
-
-protected:
- using TBase::operator*;
- using TBase::operator->;
-
- Dict::KVPair *InsertOrLookup(Any key) {
- for (;;) {
- if (Dict::KVPair *ret = get()->TryInsertOrLookup(&key)) {
- return ret;
- }
- Ref<Dict>::FromRange(begin(), end(), get()->Cap() * 2).Swap(*this);
- }
- TVM_FFI_UNREACHABLE();
- }
-
- template <typename Iter>
- static Ref<Dict> FromRange(Iter begin, Iter end) {
- int64_t num = std::distance(begin, end);
- int64_t capacity = details::BitCeil(num * 2);
- capacity = std::max<int64_t>(
- (capacity + Dict::kBlockCapacity - 1) & ~(Dict::kBlockCapacity - 1), Dict::kBlockCapacity);
- Ref<Dict> dict(Dict::Allocator::New(capacity));
- for (; begin != end; ++begin) {
- Dict::KVPair *ret = dict.InsertOrLookup(Any(begin->first));
- static_cast<Any &>(ret->second) = Any(begin->second);
- }
- return dict;
- }
-
- template <typename Iter>
- static Ref<Dict> FromRange(Iter begin, Iter end, int64_t capacity) {
- capacity = std::max<int64_t>(
- (capacity + Dict::kBlockCapacity - 1) & ~(Dict::kBlockCapacity - 1), Dict::kBlockCapacity);
- Ref<Dict> dict(Dict::Allocator::New(capacity));
- for (; begin != end; ++begin) {
- Dict::KVPair *ret = dict.InsertOrLookup(Any(begin->first));
- static_cast<Any &>(ret->second) = Any(begin->second);
- }
- return dict;
- }
-};
-
-namespace details {
-
-TVM_FFI_INLINE int32_t CountLeadingZeros(uint64_t x) {
-#if __cplusplus >= 202002L
- return std::countl_zero(x);
-#elif defined(_MSC_VER)
- DWORD leading_zero = 0;
- if (_BitScanReverse64(&leading_zero, value)) {
- return static_cast<int32_t>(63 - leading_zero);
- } else {
- return 64;
- }
-#else
- return x == 0 ? 64 : __builtin_clzll(x);
-#endif
-}
-
-TVM_FFI_INLINE uint64_t BitCeil(uint64_t x) {
-#if __cplusplus >= 202002L
- return std::bit_ceil(x);
-#else
- return x <= 1 ? 1 : static_cast<uint64_t>(1) << (64 - CountLeadingZeros(x - 1));
-#endif
-}
-
-} // namespace details
-} // namespace ffi
-} // namespace tvm
-#endif // TVM_FFI_DICT_H_
diff --git a/include/tvm/ffi/ext/error.h b/include/tvm/ffi/ext/error.h
deleted file mode 100644
index 5d8ec47582..0000000000
--- a/include/tvm/ffi/ext/error.h
+++ /dev/null
@@ -1,89 +0,0 @@
-#ifndef TVM_FFI_ERROR_H_
-#define TVM_FFI_ERROR_H_
-
-#include <string>
-#include <tvm/ffi/core/core.h>
-#include <vector>
-
-namespace tvm {
-namespace ffi {
-namespace details {
-struct _ErrorHeader {
- const char *kind;
- int32_t num_frames;
- const char **linenos;
- const char *message;
-};
-using ErrorHeader = AnyWithExtra<_ErrorHeader>;
-static_assert(sizeof(ErrorHeader) == sizeof(TVMFFIError));
-static_assert(offsetof(ErrorHeader, _extra.kind) == offsetof(TVMFFIError, kind));
-static_assert(offsetof(ErrorHeader, _extra.num_frames) == offsetof(TVMFFIError, num_frames));
-static_assert(offsetof(ErrorHeader, _extra.linenos) == offsetof(TVMFFIError, linenos));
-static_assert(offsetof(ErrorHeader, _extra.message) == offsetof(TVMFFIError, message));
-} // namespace details
-
-struct Error : private details::ErrorHeader {
- TVM_FFI_DEF_STATIC_TYPE(Error, Object, TVMFFITypeIndex::kTVMFFIError);
-
- std::string kind;
- std::vector<std::string> linenos;
- std::string message;
- std::vector<const char *> lineno_holder;
-
- Error(std::string kind, std::string lineno, std::string message)
- : kind(kind), linenos({lineno}), message(message), lineno_holder() {
- this->_extra.kind = this->kind.data();
- this->_extra.num_frames = static_cast<int32_t>(this->linenos.size());
- this->_extra.message = this->message.data();
- this->RefreshLinenoHolder();
- }
-
- void Append(std::string lineno) {
- linenos.push_back(lineno);
- this->RefreshLinenoHolder();
- }
-
-protected:
- void RefreshLinenoHolder() {
- this->lineno_holder.resize(linenos.size());
- for (size_t i = 0; i < linenos.size(); ++i) {
- this->lineno_holder[i] = linenos[i].data();
- }
- this->_extra.linenos = this->lineno_holder.data();
- }
-};
-
-struct TVMError : public std::exception {
- Ref<Error> data_;
-
- TVMError(Ref<Error> data) : data_(data) {}
- TVMError(const TVMError &other) : data_(other.data_) {}
- TVMError(TVMError &&other) : data_(std::move(other.data_)) {}
- TVMError &operator=(const TVMError &) = delete;
- TVMError &operator=(TVMError &&) = delete;
-
- const char *what() const noexcept(true) override {
- if (data_.get() == nullptr) {
- return "TVMError: Unspecified";
- }
- return data_->message.c_str();
- }
-
- void MoveToAny(Any *v) {
- *v = data_;
- data_ = Ref<Error>(nullptr);
- }
-};
-
-namespace details {
-[[noreturn]] TVM_FFI_INLINE void TVMErrorFromBuilder(std::string &&kind, std::string &&lineno,
- std::string &&message) noexcept(false) {
- Ref<Error> ret = Ref<Error>::New(std::move(kind), std::move(lineno), std::move(message));
- throw TVMError(ret);
-}
-} // namespace details
-
-} // namespace ffi
-} // namespace tvm
-
-#endif // TVM_FFI_ERROR_H_
diff --git a/include/tvm/ffi/ext/func.h b/include/tvm/ffi/ext/func.h
deleted file mode 100644
index 96dea9a259..0000000000
--- a/include/tvm/ffi/ext/func.h
+++ /dev/null
@@ -1,390 +0,0 @@
-#ifndef TVM_FFI_FUNC_H_
-#define TVM_FFI_FUNC_H_
-
-#include "./error.h"
-#include "./str.h"
-#include <functional>
-#include <sstream>
-#include <tvm/ffi/core/core.h>
-#include <type_traits>
-
-namespace tvm {
-namespace ffi {
-namespace details {
-struct FuncInfo {
- using Call = void(const Func *, int32_t, const AnyView *, Any *);
- using SafeCall = int32_t(const Func *, int32_t, const AnyView *, Any *);
- Call *call;
- SafeCall *safe_call;
- static TVM_FFI_SYMBOL_HIDE int32_t SafeCallImpl(const Func *, int32_t, const AnyView *, Any *);
-};
-} // namespace details
-
-struct Func : private AnyWithExtra<details::FuncInfo> {
- friend struct details::FuncInfo;
- using Header = AnyWithExtra<details::FuncInfo>;
- struct Allocator;
- TVM_FFI_DEF_STATIC_TYPE(Func, Object, TVMFFITypeIndex::kTVMFFIFunc);
-
- template <typename... Args>
- Any operator()(Args &&...args) const;
-
-protected:
- TVM_FFI_INLINE Func(details::FuncInfo::Call *f) : AnyWithExtra<details::FuncInfo>() {
- this->_extra.call = f;
- this->_extra.safe_call = details::FuncInfo::SafeCallImpl;
- }
-};
-static_assert(sizeof(Func) == sizeof(TVMFFIFunc));
-static_assert(offsetof(Func::Header, _extra.call) == offsetof(TVMFFIFunc, call));
-static_assert(offsetof(Func::Header, _extra.safe_call) == offsetof(TVMFFIFunc, safe_call));
-
-template <>
-struct Ref<Func> : public RefBase<Func> {
- TVM_FFI_DEF_TYPE_FRIENDS();
- TVM_FFI_INLINE Ref() : RefBase<Func>() {}
- TVM_FFI_REF_DEF_DELEGATE_CONSTRUCTORS(Ref<Func>, RefBase<Func>)
-
- template <typename... Args>
- TVM_FFI_INLINE auto operator()(Args &&...args) const {
- return (*get())(std::forward<Args>(args)...);
- }
- template <typename... Args>
- TVM_FFI_INLINE auto operator()(Args &&...args) {
- return (*get())(std::forward<Args>(args)...);
- }
-};
-
-template <std::size_t N>
-struct AnyViewArray {
- template <typename... Args>
- void Fill(Args &&...args);
- AnyView v[N];
-};
-
-template <>
-struct AnyViewArray<0> {
- template <typename... Args>
- void Fill(Args &&...args);
- AnyView *v = nullptr;
-};
-
-template <typename... Args>
-TVM_FFI_INLINE Any Func::operator()(Args &&...args) const {
- constexpr size_t N = sizeof...(Args);
- AnyViewArray<N> stack_args;
- stack_args.Fill(std::forward<Args>(args)...);
- return ::tvm::ffi::details::FuncInvoke(this, N, stack_args.v);
-}
-
-namespace details {
-
-template <typename, typename>
-struct UnpackCall;
-template <typename T>
-struct FuncFunctor;
-
-TVM_FFI_INLINE int32_t FuncInfo::SafeCallImpl(const Func *self, int32_t num_args,
- const AnyView *args, Any *ret) {
- try {
- self->_extra.call(self, num_args, args, ret);
- return 0;
- } catch (TVMError &err) {
- err.MoveToAny(ret);
- return 1;
- } catch (std::runtime_error &err) {
- *ret = Ref<Str>::New(err.what());
- return 1;
- }
- TVM_FFI_UNREACHABLE();
-}
-
-TVM_FFI_INLINE Any FuncInvoke(const void *self, int32_t num_args, const TVMFFIAny *args) {
- const TVMFFIFunc *func = static_cast<const TVMFFIFunc *>(self);
- FuncInfo::SafeCall *local_safe_call = FuncInfo::SafeCallImpl;
- Any ret;
- if (reinterpret_cast<void *>(func->safe_call) == reinterpret_cast<void *>(local_safe_call)) {
- func->call(func, num_args, args, &ret);
- return ret;
- }
- int32_t err_code = func->safe_call(func, num_args, args, &ret);
- if (err_code == 0) {
- return ret;
- }
- if (ret.type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIError)) {
- Ref<Error> err = Ref<Error>(std::move(ret));
- throw TVMError(err);
- }
- TVM_FFI_THROW(InternalError) << "Error code: " << err_code;
- TVM_FFI_UNREACHABLE();
-}
-
-template <typename FuncType>
-struct FuncImpl : public Func {
- using TSelf = FuncImpl<FuncType>;
- using Allocator = DefaultObjectAllocator<TSelf>;
-
- TVM_FFI_INLINE static void CallPacked(const Func *obj, int32_t num_args, const AnyView *args,
- Any *ret) {
- static_cast<const TSelf *>(obj)->func_(num_args, args, ret);
- }
-
- TVM_FFI_INLINE static void CallUnpacked(const Func *obj, int32_t num_args, const AnyView *args,
- Any *ret) {
- using ArgType = typename FuncFunctor<FuncType>::ArgType;
- constexpr int32_t N = std::tuple_size_v<ArgType>;
- if (num_args != N) {
- TVM_FFI_THROW(TypeError) << "Mismatched number of arguments when calling: `"
- << FuncFunctor<FuncType>::Sig() << "`. Expected " << N << " but got "
- << num_args << " arguments";
- }
- using IdxSeq = std::make_index_sequence<N>;
- using RetType = typename FuncFunctor<FuncType>::RetType;
- UnpackCall<RetType, ArgType>::template Run<FuncType>(&static_cast<const TSelf *>(obj)->func_,
- args, ret, IdxSeq{});
- }
-
- TVM_FFI_INLINE explicit FuncImpl(FuncType func, details::FuncInfo::Call *f)
- : Func(f), func_(std::forward<FuncType>(func)) {}
-
- TVM_FFI_INLINE static Func *FromPacked(FuncType func) {
- return Allocator::New(std::forward<FuncType>(func), TSelf::CallPacked);
- }
-
- TVM_FFI_INLINE static Func *FromUnpacked(FuncType func) {
- return Allocator::New(std::forward<FuncType>(func), TSelf::CallUnpacked);
- }
-
- mutable std::decay_t<FuncType> func_;
-};
-
-template <typename Type>
-struct Type2Str {
- [[maybe_unused]] static inline std::string type_str = TypeTraitsNoCR<Type>::Type2Str();
-};
-template <>
-struct Type2Str<Any> {
- static constexpr const char *type_str = "Any";
-};
-template <>
-struct Type2Str<AnyView> {
- static constexpr const char *type_str = "AnyView";
-};
-template <>
-struct Type2Str<void> {
- static constexpr const char *type_str = "void";
-};
-
-template <typename ArgType>
-struct Arg2Str {
- template <size_t i>
- static TVM_FFI_INLINE void Apply(std::ostream &os) {
- using Arg = std::tuple_element_t<i, ArgType>;
- if constexpr (i != 0) {
- os << ", ";
- }
- os << i << ": " << Type2Str<Arg>::type_str;
- }
- template <size_t... I>
- static TVM_FFI_INLINE void Run(std::ostream &os, std::index_sequence<I...>) {
- using TExpander = int[];
- (void)TExpander{0, (Apply<I>(os), 0)...};
- }
-};
-
-template <typename T>
-static constexpr bool ArgSupported = IsAnyOrView<RemoveCR<T>> || TypeTraitsNoCR<T>::enabled;
-template <typename T>
-static constexpr bool RetSupported =
- IsAnyOrView<RemoveCR<T>> || std::is_void_v<T> || TypeTraitsNoCR<T>::enabled;
-
-template <typename R, typename... Args>
-struct FuncFunctorImpl {
- using FType = R(Args...);
- using ArgType = std::tuple<Args...>;
- using RetType = R;
- static constexpr bool packed =
- std::is_convertible_v<FType, std::function<void(int32_t, const AnyView *, Any *)>>;
- static constexpr bool unpacked = (ArgSupported<Args> && ...) && (RetSupported<R>);
- static TVM_FFI_INLINE std::string Sig() {
- using IdxSeq = std::make_index_sequence<sizeof...(Args)>;
- std::ostringstream ss;
- ss << "(";
- Arg2Str<std::tuple<Args...>>::Run(ss, IdxSeq{});
- ss << ") -> " << Type2Str<R>::type_str;
- return ss.str();
- }
-};
-
-template <typename R, typename... Args>
-struct FuncFunctor<R(Args...)> : FuncFunctorImpl<R, Args...> {};
-template <typename R, typename... Args>
-struct FuncFunctor<R (*)(Args...)> : FuncFunctorImpl<R, Args...> {};
-
-template <int32_t i, typename>
-struct PrependIntegerSeq;
-template <int32_t i, int32_t... Is>
-struct PrependIntegerSeq<i, std::integer_sequence<int32_t, Is...>> {
- using type = std::integer_sequence<int32_t, i, Is...>;
-};
-template <int32_t I, typename>
-struct IndexIntSeq;
-template <int32_t I, int32_t Head, int32_t... Tail>
-struct IndexIntSeq<I, std::integer_sequence<int32_t, Head, Tail...>>
- : IndexIntSeq<I - 1, std::integer_sequence<int32_t, Tail...>> {};
-template <int32_t Head, int32_t... Tail>
-struct IndexIntSeq<0, std::integer_sequence<int32_t, Head, Tail...>> {
- static constexpr int32_t value = Head;
-};
-
-template <typename Function, typename StorageInfo>
-struct UnpackCallArgConverter {
- template <typename _Type, size_t i>
- struct AsType {
- inline static auto Run(const AnyView &v, Any *storage) {
- using Type = std::decay_t<_Type>;
- constexpr int32_t storage_index = IndexIntSeq<i, typename StorageInfo::sum>::value;
- try {
- if constexpr (storage_index == -1) {
- return v.operator Type();
- } else {
- return v.CastWithStorage<Type>(storage + storage_index);
- }
- } catch (const TVMError &) {
- TVM_FFI_THROW(TypeError) << "Mismatched type on argument #" << i << " when calling: `"
- << FuncFunctor<Function>::Sig() << "`. Expected `"
- << Type2Str<Type>::type_str << "` but got `"
- << TypeIndex2TypeKey(v.type_index) << "`";
- }
- TVM_FFI_UNREACHABLE();
- }
- };
-
- template <size_t i>
- struct AsType<AnyView, i> {
- TVM_FFI_INLINE static AnyView Run(const AnyView &v, Any *) { return v; }
- };
-
- template <size_t i>
- struct AsType<Any, i> {
- TVM_FFI_INLINE static Any Run(const AnyView &v, Any *) { return v; }
- };
-};
-
-template <typename T, typename = void>
-struct RequiresFFIStorage {
- static constexpr int32_t value = 0;
-};
-template <typename T>
-struct RequiresFFIStorage<
- T, std::void_t<decltype(TypeTraitsNoCR<T>::CopyFromTVMFFIAnyToTypeWithStorage)>> {
- static constexpr int32_t value = 1;
-};
-
-template <typename... Args>
-struct FFIStorageInfo;
-template <>
-struct FFIStorageInfo<> {
- constexpr static int32_t total = 0;
- using sum = std::integer_sequence<int32_t>;
-};
-template <typename I, typename... Is>
-struct FFIStorageInfo<I, Is...> {
-private:
- using Prev = FFIStorageInfo<Is...>;
- constexpr static int32_t need_ffi_storage = RequiresFFIStorage<I>::value;
-
-public:
- constexpr static int32_t total = Prev::total + need_ffi_storage;
- using sum =
- typename PrependIntegerSeq<(need_ffi_storage ? total : 0) - 1, typename Prev::sum>::type;
-};
-
-template <typename RetType, typename... Args>
-struct UnpackCall<RetType, std::tuple<Args...>> {
- template <typename Function, typename FuncType, size_t... I>
- TVM_FFI_INLINE static void Run(FuncType *func, const AnyView *args, Any *ret,
- std::index_sequence<I...>) {
- using Storage = FFIStorageInfo<Args...>;
- using CVT = UnpackCallArgConverter<Function, Storage>;
- if constexpr (Storage::total > 0 && std::is_void_v<RetType>) {
- Any storage[Storage::total];
- ret->Reset();
- (*func)(CVT::template AsType<Args, I>::Run(args[I], storage)...);
- } else if constexpr (Storage::total > 0 && !std::is_void_v<RetType>) {
- Any storage[Storage::total];
- *ret = (*func)(CVT::template AsType<Args, I>::Run(args[I], storage)...);
- } else if constexpr (Storage::total == 0 && std::is_void_v<RetType>) {
- ret->Reset();
- (*func)(CVT::template AsType<Args, I>::Run(args[I], nullptr)...);
- } else if constexpr (Storage::total == 0 && !std::is_void_v<RetType>) {
- *ret = (*func)(CVT::template AsType<Args, I>::Run(args[I], nullptr)...);
- }
- }
-};
-
-template <typename... Args>
-struct AnyViewArrayFill {
- template <std::size_t i, typename Arg>
- TVM_FFI_INLINE static void Apply(AnyView *v, Arg &&arg) {
- v[i] = AnyView(std::forward<Arg>(arg));
- }
- template <std::size_t... I>
- TVM_FFI_INLINE static void Run(AnyView *v, Args &&...args, std::index_sequence<I...>) {
- using TExpander = int[];
- (void)TExpander{0, (Apply<I>(v, std::forward<Args>(args)), 0)...};
- }
-};
-
-template <typename... Args>
-TVM_FFI_INLINE void FillAnyView(AnyView *v, Args &&...args) {
- constexpr std::size_t N = sizeof...(args);
- if constexpr (N > 0) {
- using IdxSeq = std::make_index_sequence<N>;
- ::tvm::ffi::details::AnyViewArrayFill<Args...>::Run(v, std::forward<Args>(args)..., IdxSeq{});
- }
-}
-
-} // namespace details
-
-struct Func::Allocator {
-public:
- template <typename FuncType, typename = void>
- struct Impl {};
-
- template <typename FuncType>
- struct Impl<FuncType, std::enable_if_t<details::FuncFunctor<FuncType>::packed>> {
- TVM_FFI_INLINE static Func *Run(FuncType func) {
- return details::FuncImpl<FuncType>::FromPacked(std::forward<FuncType>(func));
- }
- };
-
- template <typename FuncType>
- struct Impl<FuncType, std::enable_if_t<details::FuncFunctor<FuncType>::unpacked>> {
- TVM_FFI_INLINE static Func *Run(FuncType func) {
- return details::FuncImpl<FuncType>::FromUnpacked(std::forward<FuncType>(func));
- }
- };
-
- template <typename FuncType>
- TVM_FFI_INLINE static Func *New(FuncType func) {
- return Impl<FuncType>::Run(std::forward<FuncType>(func));
- }
-};
-
-template <std::size_t N>
-template <typename... Args>
-TVM_FFI_INLINE void AnyViewArray<N>::Fill(Args &&...args) {
- static_assert(sizeof...(args) == N, "Invalid number of arguments");
- ::tvm::ffi::details::FillAnyView(v, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-TVM_FFI_INLINE void AnyViewArray<0>::Fill(Args &&...args) {
- static_assert(sizeof...(args) == 0, "Invalid number of arguments");
-}
-
-} // namespace ffi
-} // namespace tvm
-
-#endif // TVM_FFI_FUNC_H_
diff --git a/include/tvm/ffi/ext/list.h b/include/tvm/ffi/ext/list.h
deleted file mode 100644
index cc6daff35c..0000000000
--- a/include/tvm/ffi/ext/list.h
+++ /dev/null
@@ -1,457 +0,0 @@
-#ifndef TVM_FFI_LIST_H_
-#define TVM_FFI_LIST_H_
-#include <cstddef>
-#include <cstring>
-#include <initializer_list>
-#include <iterator>
-#include <tvm/ffi/core/core.h>
-#include <vector>
-
-namespace tvm {
-namespace ffi {
-namespace details {
-struct _ListHeaeder {
- int64_t list_capacity; // sizeof(TVMFFIAny*) per element
- int64_t list_length;
- int64_t pool_capacity; // sizeof(TVMFFIAny) per element
- int64_t pool_length;
-};
-using ListHeader = AnyWithExtra<_ListHeaeder>;
-static_assert(sizeof(ListHeader) == sizeof(TVMFFIList));
-static_assert(offsetof(ListHeader, _extra.list_capacity) == offsetof(TVMFFIList, list_capacity));
-static_assert(offsetof(ListHeader, _extra.list_length) == offsetof(TVMFFIList, list_length));
-static_assert(offsetof(ListHeader, _extra.pool_capacity) == offsetof(TVMFFIList, pool_capacity));
-static_assert(offsetof(ListHeader, _extra.pool_length) == offsetof(TVMFFIList, pool_length));
-void ListRangeCheck(int64_t begin, int64_t end, int64_t length);
-} // namespace details
-
-#define TVM_FFI_LIST_EXEC_IF_POD(ListCls, TypeIndex, Exec) \
- if constexpr (ListCls::all_items_are_pod) { \
- Exec; \
- } else if constexpr (!ListCls::all_items_are_obj) { \
- if (details::IsTypeIndexPOD(TypeIndex)) { \
- Exec; \
- } \
- }
-
-template <typename ListClass>
-struct ListMethods {
- using ListItem = TVMFFIAny *;
- using PoolItem = TVMFFIAny;
-
-private:
- // Section 1: Iterator and utility classes
-
- struct Iterator {
- using iterator_category = std::random_access_iterator_tag;
- using difference_type = std::ptrdiff_t;
- using value_type = AnyView;
- using pointer = AnyView;
- using reference = AnyView;
-
- Iterator() = default;
- Iterator(const Iterator &) = default;
- Iterator(Iterator &&) = default;
- Iterator &operator=(const Iterator &) = default;
- Iterator &operator=(Iterator &&) = default;
-
- Iterator &operator++() {
- ++index;
- return *this;
- }
- Iterator &operator--() {
- --index;
- return *this;
- }
- Iterator operator++(int) {
- Iterator ret = *this;
- ++index;
- return ret;
- }
- Iterator operator--(int) {
- Iterator ret = *this;
- --index;
- return ret;
- }
- Iterator operator+(difference_type n) const {
- Iterator ret = *this;
- ret.index += n;
- return ret;
- }
- Iterator operator-(difference_type n) const {
- Iterator ret = *this;
- ret.index -= n;
- return ret;
- }
- Iterator &operator+=(difference_type n) {
- index += n;
- return *this;
- }
- Iterator &operator-=(difference_type n) {
- index -= n;
- return *this;
- }
- AnyView operator[](difference_type n) const { return self->GetItem(index + n); }
- difference_type operator-(const Iterator &other) const { return index - other.index; }
- bool operator==(const Iterator &other) const { return index == other.index; }
- bool operator!=(const Iterator &other) const { return index != other.index; }
- reference operator*() const { return self->GetItem(index); }
- pointer operator->() const = delete;
-
- protected:
- friend struct ListMethods;
- Iterator(ListClass *self, int64_t index) : self(self), index(index) {}
- ListClass *self;
- int64_t index;
- };
-
- struct ReverseIterator : public std::reverse_iterator<Iterator> {
- using std::reverse_iterator<Iterator>::reverse_iterator;
- };
-
- TVM_FFI_INLINE auto *Get() { return &(static_cast<ListClass *>(this)->_extra); }
- TVM_FFI_INLINE auto *Get() const { return &(static_cast<const ListClass *>(this)->_extra); }
- TVM_FFI_INLINE auto ListBegin() const {
- return reinterpret_cast<const ListItem *>(static_cast<const ListClass *>(this) + 1);
- }
- TVM_FFI_INLINE auto PoolBegin() const {
- return reinterpret_cast<const PoolItem *>(ListBegin() + Get()->list_capacity);
- }
- TVM_FFI_INLINE auto ListBegin() {
- return reinterpret_cast<ListItem *>(static_cast<ListClass *>(this) + 1);
- }
- TVM_FFI_INLINE auto PoolBegin() {
- return reinterpret_cast<PoolItem *>(ListBegin() + Get()->list_capacity);
- }
-
-public:
- // Section 2: Interfaces - exposed to Ref<ListClass>
- void inplace_init_many(int64_t numel, Any *first) {
- this->ReplaceRangeInplace(0, 0, numel, first);
- }
- void inplace_push_back(Any value) {
- this->ReplaceRangeInplace(Get()->list_length, Get()->list_length, 1, &value);
- }
- void inplace_insert(int64_t index, Any value) {
- this->ReplaceRangeInplace(index, index, 1, &value);
- }
- void inplace_insert_many(int64_t index, int64_t numel, Any *first) {
- this->ReplaceRangeInplace(index, index, numel, first);
- }
- void inplace_move_from(ListClass *other) {
- // Exception safety is guaranteed even if there is an exception thrown during
- // this process, because neither `list_length` is modified.
- int64_t &new_pool_len = Get()->pool_length;
- int64_t &new_list_len = Get()->list_length;
- ListItem *new_list_base = this->ListBegin();
- PoolItem *new_pool_base = this->PoolBegin();
- ListItem *old_list_base = other->ListBegin();
- for (int64_t i = 0; i < other->_extra.list_length; ++i) {
- ListItem &e = new_list_base[i] = old_list_base[i];
- if (e) {
- TVM_FFI_LIST_EXEC_IF_POD(ListClass, e->type_index, {
- // Copy POD data to the new POD pool
- e = &(new_pool_base[new_pool_len++] = *e);
- });
- }
- }
- // Critical section: no exception be thrown here. Otherwise ref counting would be wrong.
- { std::swap(other->_extra.list_length, new_list_len); }
- }
- void inplace_resize(int64_t new_size) {
- int64_t &list_length = Get()->list_length;
- if (new_size < list_length) {
- this->RangeDecRef(new_size, list_length);
- } else {
- ListItem *b = this->ListBegin();
- for (int64_t i = list_length; i < new_size; ++i) {
- b[i] = nullptr;
- }
- }
- list_length = new_size;
- }
- void clear() {
- this->RangeDecRef(0, Get()->list_length);
- Get()->list_length = Get()->pool_length = 0;
- }
- void pop_back() {
- int64_t n = Get()->list_length;
- this->ReplaceRangeInplace(n - 1, n, 0, nullptr);
- }
- int64_t size() const { return Get()->list_length; }
- int64_t capacity() const { return Get()->list_capacity; }
- bool empty() const { return Get()->list_length == 0; }
- void erase(int64_t index) { this->ReplaceRangeInplace(index, index + 1, 0, nullptr); }
- void SetItem(int64_t i, Any data) { this->ReplaceRangeInplace(i, i + 1, 1, &data); }
- AnyView GetItem(int64_t index) const {
- details::ListRangeCheck(index, index + 1, Get()->list_length);
- const AnyView *data = static_cast<const AnyView *>(this->ListBegin()[index]);
- if (data == nullptr) {
- return AnyView();
- }
- TVM_FFI_LIST_EXEC_IF_POD(ListClass, data->type_index, { return *data; });
- AnyView ret;
- ret.type_index = data->type_index;
- ret.v_obj = const_cast<AnyView *>(data);
- return ret;
- }
- Iterator begin() { return Iterator(static_cast<ListClass *>(this), 0); }
- Iterator end() { return Iterator(static_cast<ListClass *>(this), Get()->list_length); }
- ReverseIterator rbegin() { return ReverseIterator(end()); }
- ReverseIterator rend() { return ReverseIterator(begin()); }
-
-public:
- // Section 3: Core methods
- void RangeDecRef(int64_t begin, int64_t end) {
- if constexpr (ListClass::all_items_are_pod) {
- return;
- }
- ListItem *b = this->ListBegin();
- for (int64_t i = begin; i < end; ++i) {
- ListItem &data = b[i];
- if (data) {
- TVM_FFI_LIST_EXEC_IF_POD(ListClass, data->type_index, { continue; });
- details::DecRef(data);
- }
- }
- }
- void ReplaceRangeInplace(int64_t range_begin, int64_t range_end, int64_t numel, Any *first) {
- int64_t &list_len = Get()->list_length;
- int64_t &pool_len = Get()->pool_length;
- int64_t delta = numel - (range_end - range_begin);
- details::ListRangeCheck(range_begin, range_end, list_len);
- RangeDecRef(range_begin, range_end);
- ListItem *b = this->ListBegin();
- // Step 1. Move [range_end, list_len) to [range_end + delta, list_len + delta)
- // so there are exaclty `num_elems` free slots in [range_begin, range_end + delta)
- std::memmove(b + range_end + delta, b + range_end, (list_len - range_end) * sizeof(ListItem));
- list_len += delta;
- // Step 2. Copy `num_elems` items to [range_begin, range_end + delta)
- PoolItem *p = this->PoolBegin();
- for (int64_t i = 0; i < numel; ++i) {
- Any v = std::move(first[i]);
- ListItem &d = b[range_begin + i] = nullptr;
- if (details::IsTypeIndexNone(v.type_index)) {
- continue;
- }
- TVM_FFI_LIST_EXEC_IF_POD(ListClass, v.type_index, {
- d = &(p[pool_len++] = v);
- continue;
- });
- // N.B. Transfer the ownership of `v` to the list
- std::swap(d, v.v_obj);
- v.type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone);
- }
- }
- Ref<ListClass> MaybeMoveToMoreCapacity(int64_t list_len_delta, int64_t pool_len_delta) {
- ListClass *self = static_cast<ListClass *>(this);
- int64_t list_cap = self->_extra.list_capacity, pool_cap = self->_extra.pool_capacity;
- int64_t new_list_cap = GrowCapacity(list_cap, self->_extra.list_length + list_len_delta);
- int64_t new_pool_cap = GrowCapacity(pool_cap, self->_extra.pool_length + pool_len_delta);
- if (new_list_cap > list_cap || new_pool_cap > pool_cap) {
- Ref<ListClass> ret = ListClass::Allocator::New(new_list_cap, new_pool_cap);
- ret->inplace_move_from(self);
- return ret;
- }
- return Ref<ListClass>(self);
- }
- static int64_t GrowCapacity(int64_t current, int64_t required) {
- for (; current < required; current = std::max(current * 2, ListClass::kMinCapacity)) {
- }
- return current;
- }
-};
-
-struct List : private details::ListHeader, private ListMethods<List> {
- TVM_FFI_DEF_STATIC_TYPE(List, Object, TVMFFITypeIndex::kTVMFFIList);
- TVM_FFI_DEF_TYPE_FRIENDS();
- constexpr static int64_t kMinCapacity = 4;
- struct Allocator;
-
- List(int64_t list_capacity, int64_t pool_capacity) {
- this->_extra.list_capacity = list_capacity;
- this->_extra.list_length = 0;
- this->_extra.pool_capacity = pool_capacity;
- this->_extra.pool_length = 0;
- }
- using TBase = ListMethods<List>;
- using TBase::begin;
- using TBase::capacity;
- using TBase::clear;
- using TBase::empty;
- using TBase::end;
- using TBase::erase;
- using TBase::GetItem;
- using TBase::inplace_init_many;
- using TBase::inplace_insert;
- using TBase::inplace_insert_many;
- using TBase::inplace_push_back;
- using TBase::inplace_resize;
- using TBase::pop_back;
- using TBase::rbegin;
- using TBase::rend;
- using TBase::SetItem;
- using TBase::size;
-
-private:
- constexpr static bool all_items_are_pod = false;
- constexpr static bool all_items_are_obj = false;
- ~List() { this->clear(); }
-
- template <typename>
- friend struct ListMethods;
-};
-
-struct List::Allocator {
- static TVM_FFI_INLINE List *New() { return New(0, 0); }
- static TVM_FFI_INLINE List *New(int64_t list_capacity, int64_t pool_capacity) {
- static_assert(sizeof(PoolItem) % sizeof(ListItem) == 0);
- constexpr int64_t ratio = sizeof(PoolItem) / sizeof(ListItem);
- static_assert(ratio == 2);
- return DefaultObjectAllocator<List>::NewWithPad<ListItem>(list_capacity + pool_capacity * ratio,
- list_capacity, pool_capacity);
- }
-};
-
-template <>
-struct Ref<List> : public RefBase<List> {
- TVM_FFI_DEF_TYPE_FRIENDS();
- TVM_FFI_REF_DEF_DELEGATE_CONSTRUCTORS(Ref<List>, RefBase<List>)
-
-private:
- struct AccessProxy {
- AccessProxy &operator=(Any data) {
- self->SetItem(index, data);
- return *this;
- }
- template <typename Type>
- operator Type() const {
- return Type(self->get()->GetItem(index));
- }
- operator AnyView() const { return self->get()->GetItem(index); }
-
- friend std::ostream &operator<<(std::ostream &os, const AccessProxy &self) {
- return os << self.self->get()->GetItem(self.index);
- }
-
- private:
- friend struct Ref<List>;
- AccessProxy(Ref<List> *self, int64_t index) : self(self), index(index) {}
- Ref<List> *self;
- int64_t index;
- };
-
-public:
- Ref() : TBase(List::Allocator::New(0, 0)) {}
- Ref(std::initializer_list<Any> data) : Ref(data.begin(), data.end()) {}
- template <typename Iter>
- Ref<List>(Iter first, Iter last) : Ref(std::distance(first, last)) {
- std::vector<Any> elems = this->EnsureCapacityWithList(first, last);
- int64_t numel = static_cast<int64_t>(elems.size());
- this->get()->inplace_init_many(numel, elems.data());
- }
- template <typename Iter>
- void insert(int64_t index, Iter first, Iter last) {
- std::vector<Any> elems = this->EnsureCapacityWithList(first, last);
- int64_t numel = static_cast<int64_t>(elems.size());
- this->get()->inplace_insert_many(index, numel, elems.data());
- }
- void push_back(Any data) {
- this->EnsureCapacityWithOneMoreElement(data)->inplace_push_back(data);
- }
- void insert(int64_t index, Any data) {
- this->EnsureCapacityWithOneMoreElement(data)->inplace_insert(index, data);
- }
- void resize(int64_t new_size) { this->EnsureCapacityAtLeast(new_size)->inplace_resize(new_size); }
- void reserve(int64_t capacity) { this->EnsureCapacityAtLeast(capacity); }
- void clear() { this->get()->clear(); }
- void erase(int64_t index) { this->get()->erase(index); }
- void pop_back() { this->get()->pop_back(); }
- int64_t size() const { return this->get()->size(); }
- int64_t capacity() const { return this->get()->capacity(); }
- bool empty() const { return this->get()->empty(); }
- AnyView front() const { return this->get()->GetItem(0); }
- AnyView back() const { return this->get()->GetItem(this->get()->size() - 1); }
- auto operator[](int64_t index) { return AccessProxy(this, index); }
- auto operator[](int64_t index) const { return this->get()->GetItem(index); }
- auto begin() { return this->get()->begin(); }
- auto end() { return this->get()->end(); }
- auto rbegin() { return this->get()->rbegin(); }
- auto rend() { return this->get()->rend(); }
- void SetItem(int64_t i, Any data) {
- TVM_FFI_LIST_EXEC_IF_POD(List, data.type_index,
- { this->get()->MaybeMoveToMoreCapacity(0, 1).Swap(*this); });
- this->get()->SetItem(i, data);
- }
-
-protected:
- friend struct List;
- using RefBase<List>::operator*;
- using RefBase<List>::operator->;
- using TSelf = Ref<List>;
- using TBase = RefBase<List>;
- friend struct List;
- template <typename>
- friend struct ListMethods;
-
- Ref<List>(int64_t size) : TBase(List::Allocator::New(size, size)) {}
-
- List *EnsureCapacityWithOneMoreElement(const Any &data) {
- int64_t pool_len_delta = 0;
- TVM_FFI_LIST_EXEC_IF_POD(List, data.type_index, { pool_len_delta = 1; });
- this->get()->MaybeMoveToMoreCapacity(1, pool_len_delta).Swap(*this);
- return this->get();
- }
-
- List *EnsureCapacityAtLeast(int64_t capacity) {
- List *self = this->get();
- int64_t list_len_delta = std::max<int64_t>(capacity - self->_extra.list_capacity, 0);
- int64_t pool_len_delta = 0;
- if constexpr (!List::all_items_are_obj) {
- pool_len_delta = std::max<int64_t>(capacity - self->_extra.pool_capacity, 0);
- }
- this->get()->MaybeMoveToMoreCapacity(list_len_delta, pool_len_delta).Swap(*this);
- return this->get();
- }
-
- template <typename Iter>
- std::vector<Any> EnsureCapacityWithList(Iter first, Iter last) {
- std::vector<Any> ret(first, last);
- int64_t list_len_delta = ret.size();
- int64_t pool_len_delta = 0;
- if constexpr (List::all_items_are_pod) {
- pool_len_delta = ret.size();
- } else if constexpr (!List::all_items_are_obj) {
- for (const Any &v : ret) {
- if (details::IsTypeIndexPOD(v.type_index)) {
- ++pool_len_delta;
- }
- }
- }
- this->get()->MaybeMoveToMoreCapacity(list_len_delta, pool_len_delta).Swap(*this);
- return ret;
- }
-};
-
-namespace details {
-TVM_FFI_INLINE void ListRangeCheck(int64_t begin, int64_t end, int64_t length) {
- if (begin > end) {
- TVM_FFI_THROW(IndexError) << "Invalid range [" << begin << ", " << end
- << ") when indexing a list";
- }
- if (begin < 0 || end > length) {
- if (begin == end || begin + 1 == end) {
- TVM_FFI_THROW(IndexError) << "Indexing `" << begin << "` of a list of size " << length;
- } else {
- TVM_FFI_THROW(IndexError) << "Indexing [" << begin << ", " << end << ") of a list of size "
- << length;
- }
- }
-}
-} // namespace details
-} // namespace ffi
-} // namespace tvm
-
-#undef TVM_FFI_LIST_EXEC_IF_POD
-
-#endif // TVM_FFI_LIST_H_
diff --git a/include/tvm/ffi/ext/str.h b/include/tvm/ffi/ext/str.h
deleted file mode 100644
index f5aa29391e..0000000000
--- a/include/tvm/ffi/ext/str.h
+++ /dev/null
@@ -1,245 +0,0 @@
-#ifndef TVM_FFI_STR_H_
-#define TVM_FFI_STR_H_
-#include <cstring>
-#include <sstream>
-#include <tvm/ffi/core/core.h>
-
-namespace tvm {
-namespace ffi {
-namespace details {
-struct StrHeader {
- int64_t length;
- const char *data;
-};
-} // namespace details
-
-struct Str : private AnyWithExtra<details::StrHeader> {
- struct Allocator;
- using Header = AnyWithExtra<details::StrHeader>;
- TVM_FFI_DEF_STATIC_TYPE(Str, Object, TVMFFITypeIndex::kTVMFFIStr);
-
- TVM_FFI_INLINE const char *c_str() const { return this->_extra.data; }
- TVM_FFI_INLINE const char *data() const { return this->_extra.data; }
- TVM_FFI_INLINE int64_t length() const { return this->_extra.length; }
- TVM_FFI_INLINE int64_t size() const { return this->_extra.length; }
- inline uint64_t Hash() const {
- return details::TVMFFIStrHash(reinterpret_cast<const TVMFFIStr *>(this));
- }
-
-protected:
- using Header::_extra;
- TVM_FFI_INLINE Str() : Header() {}
-};
-
-static_assert(sizeof(Str) == sizeof(TVMFFIStr));
-static_assert(offsetof(Str::Header, _extra.length) == offsetof(TVMFFIStr, length));
-static_assert(offsetof(Str::Header, _extra.data) == offsetof(TVMFFIStr, data));
-
-namespace details {
-
-struct StrStd : public Str {
- using Allocator = ::tvm::ffi::DefaultObjectAllocator<StrStd>;
- template <typename>
- friend struct ::tvm::ffi::DefaultObjectAllocator;
-
-protected:
- TVM_FFI_INLINE StrStd(std::string &&str) : Str(), container(std::move(str)) {
- this->_extra.length = static_cast<int64_t>(container.length());
- this->_extra.data = container.data();
- }
-
- std::string container;
-};
-
-struct StrPad : public Str {
- using Allocator = ::tvm::ffi::DefaultObjectAllocator<StrPad>;
- template <typename>
- friend struct ::tvm::ffi::DefaultObjectAllocator;
-
-protected:
- TVM_FFI_INLINE StrPad(const char *str, size_t N) : Str() {
- char *str_copy = reinterpret_cast<char *>(this) + sizeof(Str);
- std::memcpy(str_copy, str, N);
- str_copy[N - 1] = '\0';
-
- this->_extra.length = static_cast<int64_t>(N) - 1;
- this->_extra.data = str_copy;
- }
-};
-} // namespace details
-
-struct Str::Allocator {
- TVM_FFI_INLINE static Str *New(std::string &&str) {
- return details::StrStd::Allocator::New(std::move(str));
- }
- TVM_FFI_INLINE static Str *New(const std::string &str) {
- int64_t N = static_cast<int64_t>(str.length()) + 1;
- return details::StrPad::Allocator::NewWithPad<char>(N, str.data(), N);
- }
- TVM_FFI_INLINE static Str *New(const char *str) {
- int64_t N = static_cast<int64_t>(std::strlen(str)) + 1;
- return details::StrPad::Allocator::NewWithPad<char>(N, str, N);
- }
- template <size_t N>
- TVM_FFI_INLINE static Str *New(const CharArray<N> &str) {
- return details::StrPad::Allocator::NewWithPad<char>(N, str, N);
- }
- TVM_FFI_INLINE static Str *New(const char *str, size_t N) {
- return details::StrPad::Allocator::NewWithPad<char>(N, str, N);
- }
-};
-
-template <>
-struct Ref<Str> : public RefBase<Str> {
- TVM_FFI_DEF_TYPE_FRIENDS();
- TVM_FFI_INLINE Ref() : RefBase<Str>() {}
- TVM_FFI_REF_DEF_DELEGATE_CONSTRUCTORS(Ref<Str>, RefBase<Str>)
-
- using RefBase<Str>::New;
- template <size_t N>
- TVM_FFI_INLINE static TSub New(const CharArray<N> &arg) {
- return TSub(Str::Allocator::template New<N>(arg));
- }
-};
-
-TVM_FFI_INLINE Ref<Str> Object::str() const {
- std::ostringstream os;
- os << *this;
- return Ref<Str>::New(os.str());
-}
-
-template <typename ObjectType>
-TVM_FFI_INLINE Ref<Str> RefBase<ObjectType>::str() const {
- std::ostringstream os;
- os << *this;
- return Ref<Str>::New(os.str());
-}
-
-TVM_FFI_INLINE Ref<Str> AnyView::str() const {
- std::ostringstream os;
- os << *this;
- return Ref<Str>::New(os.str());
-}
-
-TVM_FFI_INLINE Ref<Str> Any::str() const {
- std::ostringstream os;
- os << *this;
- return Ref<Str>::New(os.str());
-}
-
-/********** TypeTraits *********/
-
-template <>
-struct TypeTraits<Str *> : public TypeTraitsDefaultForObject<Str> {
-private:
- using TypeTraitsDefaultForObject<Str>::MoveFromTVMFFIAnyToRef;
- using TypeTraitsDefaultForObject<Str>::CopyFromTVMFFIAnyToRef;
-
-public:
- TVM_FFI_INLINE static Str *CopyFromTVMFFIAnyToRef(const TVMFFIAny *v) {
- if (details::IsTypeIndexNone(v->type_index)) {
- return nullptr;
- }
- if (v->type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
- return Str::Allocator::New(v->v_str, std::strlen(v->v_str) + 1);
- }
- if (!details::IsTypeIndexPOD(v->type_index) && details::IsInstanceOf<Str>(v->type_index)) {
- return reinterpret_cast<Str *>(v->v_obj);
- }
- throw TemporaryTypeError();
- }
-
- TVM_FFI_INLINE static Str *CopyFromTVMFFIAnyToTypeWithStorage(const TVMFFIAny *v, Any *storage) {
- if (details::IsTypeIndexNone(v->type_index)) {
- return nullptr;
- }
- if (v->type_index == static_cast<int32_t>(TVMFFITypeIndex::kTVMFFIRawStr)) {
- Str *ret = Str::Allocator::New(v->v_str, std::strlen(v->v_str) + 1);
- *storage = Ref<Str>(ret);
- return ret;
- }
- if (!details::IsTypeIndexPOD(v->type_index) && details::IsInstanceOf<Str>(v->type_index)) {
- return reinterpret_cast<Str *>(v->v_obj);
- }
- throw TemporaryTypeError();
- }
-
- TVM_FFI_INLINE static Str *MoveFromTVMFFIAnyToRef(TVMFFIAny *v) {
- Str *ret = CopyFromTVMFFIAnyToRef(v);
- v->type_index = static_cast<int32_t>(TVMFFITypeIndex::kTVMFFINone);
- v->v_obj = nullptr;
- return ret;
- }
-};
-
-namespace details {
-
-Any FuncInvoke(const void *func, int32_t num_args, const TVMFFIAny *args);
-
-inline int32_t TVMFFIStrCmp(const TVMFFIStr *a, const TVMFFIStr *b) {
- if (a->length != b->length) {
- return a->length - b->length;
- }
- return std::strncmp(a->data, b->data, a->length);
-}
-
-inline uint64_t TVMFFIStrHash(const TVMFFIStr *str) {
- const constexpr uint64_t kMultiplier = 1099511628211ULL;
- const constexpr uint64_t kMod = 2147483647ULL;
- const char *it = str->data;
- const char *end = it + str->length;
- uint64_t result = 0;
- for (; it + 8 <= end; it += 8) {
- uint64_t b = (static_cast<uint64_t>(it[0]) << 56) | (static_cast<uint64_t>(it[1]) << 48) |
- (static_cast<uint64_t>(it[2]) << 40) | (static_cast<uint64_t>(it[3]) << 32) |
- (static_cast<uint64_t>(it[4]) << 24) | (static_cast<uint64_t>(it[5]) << 16) |
- (static_cast<uint64_t>(it[6]) << 8) | static_cast<uint64_t>(it[7]);
- result = (result * kMultiplier + b) % kMod;
- }
- if (it < end) {
- uint64_t b = 0;
- if (it + 4 <= end) {
- b = (static_cast<uint64_t>(it[0]) << 24) | (static_cast<uint64_t>(it[1]) << 16) |
- (static_cast<uint64_t>(it[2]) << 8) | static_cast<uint64_t>(it[3]);
- it += 4;
- }
- if (it + 2 <= end) {
- b = (b << 16) | (static_cast<uint64_t>(it[0]) << 8) | static_cast<uint64_t>(it[1]);
- it += 2;
- }
- if (it + 1 <= end) {
- b = (b << 8) | static_cast<uint64_t>(it[0]);
- it += 1;
- }
- result = (result * kMultiplier + b) % kMod;
- }
- return result;
-}
-
-TVM_FFI_INLINE void AnyView2Str(std::ostream &os, const TVMFFIAny *v) {
-#if TVM_FFI_ALLOW_DYN_TYPE
- AnyView attr = TypeGetAttr(v->type_index, "__str__");
- if (details::IsTypeIndexNone(attr.type_index)) {
- os << TypeIndex2TypeKey(v->type_index) << '@' << v->v_ptr;
- } else {
- os << Ref<Str>(FuncInvoke(attr.v_obj, 1, v))->c_str();
- }
-#else
- return StaticAnyView2Str(os, v);
-#endif
-}
-
-TVM_FFI_INLINE TVMFFIObject *StrMoveFromStdString(std::string &&source) {
- return reinterpret_cast<TVMFFIObject *>(Str::Allocator::New(std::move(source)));
-}
-
-TVM_FFI_INLINE TVMFFIObject *StrCopyFromCharArray(const char *source, size_t length) {
- return reinterpret_cast<TVMFFIObject *>(Str::Allocator::New(source, length + 1));
-}
-
-} // namespace details
-
-} // namespace ffi
-} // namespace tvm
-
-#endif // TVM_FFI_STR_H_
diff --git a/include/tvm/ffi/ffi.hpp b/include/tvm/ffi/ffi.hpp
deleted file mode 100644
index 68532f7457..0000000000
--- a/include/tvm/ffi/ffi.hpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef TVM_FFI_H_
-#define TVM_FFI_H_
-#include "./core/core.h"
-#include "./ext/dict.h"
-#include "./ext/error.h"
-#include "./ext/func.h"
-#include "./ext/list.h"
-#include "./ext/str.h"
-#endif // TVM_FFI_H_
(tvm) 20/38: [FFI] Enhance the deleter and reflection to be aware of padding
Posted by tq...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch refactor-s0
in repository https://gitbox.apache.org/repos/asf/tvm.git
commit 661cf0bd48b64542998e19702b287700b1a1a2af
Author: tqchen <ti...@gmail.com>
AuthorDate: Mon Mar 10 09:43:07 2025 -0400
[FFI] Enhance the deleter and reflection to be aware of padding
Ensure correct calculation of class ptr based on obj ptr.
---
ffi/include/tvm/ffi/c_api.h | 2 +-
ffi/include/tvm/ffi/memory.h | 23 +++++++++--------------
ffi/include/tvm/ffi/object.h | 21 ++++++++++++++++++---
ffi/include/tvm/ffi/reflection.h | 7 ++++---
ffi/include/tvm/ffi/type_traits.h | 2 +-
ffi/tests/cpp/test_object.cc | 4 ++++
ffi/tests/cpp/test_reflection.cc | 7 ++++---
ffi/tests/cpp/testing_object.h | 12 +++++++++++-
8 files changed, 52 insertions(+), 26 deletions(-)
diff --git a/ffi/include/tvm/ffi/c_api.h b/ffi/include/tvm/ffi/c_api.h
index 5a626d03c0..a1cf753448 100644
--- a/ffi/include/tvm/ffi/c_api.h
+++ b/ffi/include/tvm/ffi/c_api.h
@@ -111,7 +111,7 @@ typedef struct TVMFFIObject {
/*! \brief Reference counter of the object. */
int32_t ref_counter;
/*! \brief Deleter to be invoked when reference counter goes to zero. */
- void (*deleter)(void* self);
+ void (*deleter)(struct TVMFFIObject* self);
} TVMFFIObject;
/*!
diff --git a/ffi/include/tvm/ffi/memory.h b/ffi/include/tvm/ffi/memory.h
index d0e5995fa5..d3af2dd490 100644
--- a/ffi/include/tvm/ffi/memory.h
+++ b/ffi/include/tvm/ffi/memory.h
@@ -33,7 +33,7 @@ namespace tvm {
namespace ffi {
/*! \brief Deleter function for obeject */
-typedef void (*FObjectDeleter)(void* obj);
+typedef void (*FObjectDeleter)(TVMFFIObject* obj);
/*!
* \brief Allocate an object using default allocator.
@@ -75,10 +75,10 @@ class ObjAllocatorBase {
static_assert(std::is_base_of<Object, T>::value, "make can only be used to create Object");
T* ptr = Handler::New(static_cast<Derived*>(this), std::forward<Args>(args)...);
TVMFFIObject* ffi_ptr = details::ObjectUnsafe::GetHeader(ptr);
- // NOTE: ref_counter is initialized in object constructor
+ ffi_ptr->ref_counter = 1;
ffi_ptr->type_index = T::RuntimeTypeIndex();
ffi_ptr->deleter = Handler::Deleter();
- return details::ObjectUnsafe::ObjectPtrFromUnowned<T>(ptr);
+ return details::ObjectUnsafe::ObjectPtrFromOwned<T>(ptr);
}
/*!
@@ -96,9 +96,10 @@ class ObjAllocatorBase {
ArrayType* ptr =
Handler::New(static_cast<Derived*>(this), num_elems, std::forward<Args>(args)...);
TVMFFIObject* ffi_ptr = details::ObjectUnsafe::GetHeader(ptr);
+ ffi_ptr->ref_counter = 1;
ffi_ptr->type_index = ArrayType::RuntimeTypeIndex();
ffi_ptr->deleter = Handler::Deleter();
- return details::ObjectUnsafe::ObjectPtrFromUnowned<ArrayType>(ptr);
+ return details::ObjectUnsafe::ObjectPtrFromOwned<ArrayType>(ptr);
}
};
@@ -133,11 +134,8 @@ class SimpleObjAllocator : public ObjAllocatorBase<SimpleObjAllocator> {
static FObjectDeleter Deleter() { return Deleter_; }
private:
- static void Deleter_(void* objptr) {
- // NOTE: this is important to cast back to T*
- // because objptr and tptr may not be the same
- // depending on how sub-class allocates the space.
- T* tptr = static_cast<T*>(objptr);
+ static void Deleter_(TVMFFIObject* objptr) {
+ T* tptr = details::ObjectUnsafe::RawObjectPtrFromUnowned<T>(objptr);
// It is important to do tptr->T::~T(),
// so that we explicitly call the specific destructor
// instead of tptr->~T(), which could mean the intention
@@ -182,11 +180,8 @@ class SimpleObjAllocator : public ObjAllocatorBase<SimpleObjAllocator> {
static FObjectDeleter Deleter() { return Deleter_; }
private:
- static void Deleter_(void* objptr) {
- // NOTE: this is important to cast back to ArrayType*
- // because objptr and tptr may not be the same
- // depending on how sub-class allocates the space.
- ArrayType* tptr = static_cast<ArrayType*>(objptr);
+ static void Deleter_(TVMFFIObject* objptr) {
+ ArrayType* tptr = details::ObjectUnsafe::RawObjectPtrFromUnowned<ArrayType>(objptr);
// It is important to do tptr->ArrayType::~ArrayType(),
// so that we explicitly call the specific destructor
// instead of tptr->~ArrayType(), which could mean the intention
diff --git a/ffi/include/tvm/ffi/object.h b/ffi/include/tvm/ffi/object.h
index 70b68f6fc6..8173a362e2 100644
--- a/ffi/include/tvm/ffi/object.h
+++ b/ffi/include/tvm/ffi/object.h
@@ -104,7 +104,6 @@ class Object {
header_.ref_counter = 0;
header_.deleter = nullptr;
}
-
/*!
* Check if the object is an instance of TargetType.
* \tparam TargetType The target type to be checked.
@@ -156,7 +155,7 @@ class Object {
void DecRef() {
if (details::AtomicDecrementRelAcq(&(header_.ref_counter)) == 1) {
if (header_.deleter != nullptr) {
- header_.deleter(this);
+ header_.deleter(&(this->header_));
}
}
}
@@ -520,6 +519,14 @@ class ObjectUnsafe {
return const_cast<TVMFFIObject*>(&(src->header_));
}
+ template <typename Class>
+ static TVM_FFI_INLINE int64_t GetObjectOffsetToSubclass() {
+ return (
+ reinterpret_cast<int64_t>(&(static_cast<Class*>(nullptr)->header_)) -
+ reinterpret_cast<int64_t>(&(static_cast<Object*>(nullptr)->header_))
+ );
+ }
+
template <typename T>
static TVM_FFI_INLINE ObjectPtr<T> ObjectPtrFromOwned(Object* raw_ptr) {
tvm::ffi::ObjectPtr<T> ptr;
@@ -527,7 +534,15 @@ class ObjectUnsafe {
return ptr;
}
- // Create ObjectPtr from unknowned ptr
+ template <typename T>
+ static TVM_FFI_INLINE T* RawObjectPtrFromUnowned(TVMFFIObject* obj_ptr) {
+ // NOTE: this is important to first cast to Object*
+ // then cast back to T* because objptr and tptr may not be the same
+ // depending on how sub-class allocates the space.
+ return static_cast<T*>(reinterpret_cast<Object*>(obj_ptr));
+ }
+
+ // Create ObjectPtr from unowned ptr
template <typename T>
static TVM_FFI_INLINE ObjectPtr<T> ObjectPtrFromUnowned(Object* raw_ptr) {
return tvm::ffi::ObjectPtr<T>(raw_ptr);
diff --git a/ffi/include/tvm/ffi/reflection.h b/ffi/include/tvm/ffi/reflection.h
index 3e6a1ecc37..6da880ab1f 100644
--- a/ffi/include/tvm/ffi/reflection.h
+++ b/ffi/include/tvm/ffi/reflection.h
@@ -52,8 +52,9 @@ struct Type2FieldStaticTypeIndex<T, std::enable_if_t<TypeTraits<T>::enabled>> {
* \returns The byteoffset
*/
template <typename Class, typename T>
-inline int64_t GetFieldByteOffset(T Class::* field_ptr) {
- return reinterpret_cast<int64_t>(&(static_cast<Class*>(nullptr)->*field_ptr));
+inline int64_t GetFieldByteOffsetToObject(T Class::* field_ptr) {
+ int64_t field_offset_to_class = reinterpret_cast<int64_t>(&(static_cast<Class*>(nullptr)->*field_ptr));
+ return field_offset_to_class - details::ObjectUnsafe::GetObjectOffsetToSubclass<Class>();
}
class ReflectionDef {
@@ -82,7 +83,7 @@ class ReflectionDef {
info.field_static_type_index = Type2FieldStaticTypeIndex<T>::value;
// store byte offset and setter, getter
// so the same setter can be reused for all the same type
- info.byte_offset = GetFieldByteOffset<Class, T>(field_ptr);
+ info.byte_offset = GetFieldByteOffsetToObject<Class, T>(field_ptr);
info.readonly = readonly;
info.getter = FieldGetter<T>;
info.setter = FieldSetter<T>;
diff --git a/ffi/include/tvm/ffi/type_traits.h b/ffi/include/tvm/ffi/type_traits.h
index 69b3738be0..c418dfb900 100644
--- a/ffi/include/tvm/ffi/type_traits.h
+++ b/ffi/include/tvm/ffi/type_traits.h
@@ -530,7 +530,7 @@ struct TypeTraits<const TObject*, std::enable_if_t<std::is_base_of_v<Object, TOb
}
static TVM_FFI_INLINE const TObject* CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
- return reinterpret_cast<const TObject*>(src->v_obj);
+ return details::ObjectUnsafe::RawObjectPtrFromUnowned<TObject>(src->v_obj);
}
static TVM_FFI_INLINE std::optional<const TObject*> TryCopyFromAnyView(const TVMFFIAny* src) {
diff --git a/ffi/tests/cpp/test_object.cc b/ffi/tests/cpp/test_object.cc
index ed3361a0ba..1e05731f48 100644
--- a/ffi/tests/cpp/test_object.cc
+++ b/ffi/tests/cpp/test_object.cc
@@ -34,6 +34,10 @@ TEST(Object, RefCounter) {
EXPECT_EQ(a->value, 11);
EXPECT_EQ(a.use_count(), 2);
+ ObjectPtr<TIntObj> aa = make_object<TIntObj>(*a);
+ EXPECT_EQ(aa.use_count(), 1);
+ EXPECT_EQ(aa->value, 11);
+
b.reset();
EXPECT_EQ(a.use_count(), 1);
EXPECT_TRUE(b == nullptr);
diff --git a/ffi/tests/cpp/test_reflection.cc b/ffi/tests/cpp/test_reflection.cc
index 9de0015009..a69505935c 100644
--- a/ffi/tests/cpp/test_reflection.cc
+++ b/ffi/tests/cpp/test_reflection.cc
@@ -28,15 +28,16 @@ namespace {
using namespace tvm::ffi;
using namespace tvm::ffi::testing;
-struct A {
+struct A : public Object {
ObjectRef obj;
int32_t x;
int32_t y;
};
TEST(Reflection, GetFieldByteOffset) {
- EXPECT_EQ(details::GetFieldByteOffset(&A::x), 8);
- EXPECT_EQ(details::GetFieldByteOffset(&A::y), 12);
+ EXPECT_EQ(details::GetFieldByteOffsetToObject(&A::x), 8 + sizeof(TVMFFIObject));
+ EXPECT_EQ(details::GetFieldByteOffsetToObject(&A::y), 12 + sizeof(TVMFFIObject));
+ EXPECT_EQ(details::GetFieldByteOffsetToObject(&TIntObj::value), sizeof(TVMFFIObject));
}
TEST(Reflection, FieldGetter) {
diff --git a/ffi/tests/cpp/testing_object.h b/ffi/tests/cpp/testing_object.h
index a4d6b1353f..cd6055ec1f 100644
--- a/ffi/tests/cpp/testing_object.h
+++ b/ffi/tests/cpp/testing_object.h
@@ -28,7 +28,17 @@ namespace tvm {
namespace ffi {
namespace testing {
-class TNumberObj : public Object {
+// We deliberately pad extra
+// in the header to test cases
+// where the object subclass address
+// do not align with the base object address
+// not handling properly will cause buffer overflow
+class BasePad {
+ public:
+ int64_t extra[4];
+};
+
+class TNumberObj : public BasePad, public Object {
public:
// declare as one slot, with float as overflow
static constexpr uint32_t _type_child_slots = 1;