| /* |
| * 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/any.h> |
| #include <tvm/ffi/c_api.h> |
| #include <tvm/ffi/container/array.h> |
| #include <tvm/ffi/container/dict.h> |
| #include <tvm/ffi/container/list.h> |
| #include <tvm/ffi/container/map.h> |
| #include <tvm/ffi/container/tensor.h> |
| #include <tvm/ffi/enum.h> |
| #include <tvm/ffi/error.h> |
| #include <tvm/ffi/function.h> |
| #include <tvm/ffi/memory.h> |
| #include <tvm/ffi/object.h> |
| #include <tvm/ffi/reflection/accessor.h> |
| #include <tvm/ffi/reflection/creator.h> |
| #include <tvm/ffi/reflection/registry.h> |
| #include <tvm/ffi/string.h> |
| |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| #include "object_internal.h" |
| |
| namespace tvm { |
| namespace ffi { |
| |
| /*! |
| * \brief Global registry that manages |
| * |
| * \note We do not use mutex to guard updating of TypeTable |
| * |
| * 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 |
| */ |
| class TypeTable { |
| public: |
| /*! \brief Type information */ |
| struct Entry : public TypeInfo { |
| /*! \brief stored type key */ |
| String type_key_data; |
| /*! \brief acenstor information */ |
| std::vector<const TVMFFITypeInfo*> type_ancestors_data; |
| /*! \brief type fields informaton */ |
| std::vector<TVMFFIFieldInfo> type_fields_data; |
| /*! \brief type methods informaton */ |
| std::vector<TVMFFIMethodInfo> type_methods_data; |
| /*! \brief extra information */ |
| TVMFFITypeMetadata metadata_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, 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_ancestors_data.resize(type_depth); |
| // copy over parent's type information |
| for (int32_t i = 0; i < parent->type_depth; ++i) { |
| type_ancestors_data[i] = parent->type_ancestors[i]; |
| } |
| // set last type information to be parent |
| type_ancestors_data[parent->type_depth] = parent; |
| } |
| // initialize type info: no change to type_key and type_ancestors fields |
| // after this line |
| this->type_index = type_index; |
| this->type_depth = type_depth; |
| this->type_key = TVMFFIByteArray{this->type_key_data.data(), this->type_key_data.length()}; |
| this->type_key_hash = std::hash<String>()(this->type_key_data); |
| this->type_ancestors = type_ancestors_data.data(); |
| // initialize the reflection information |
| this->num_fields = 0; |
| this->num_methods = 0; |
| this->fields = nullptr; |
| this->methods = nullptr; |
| this->metadata = nullptr; |
| } |
| }; |
| |
| struct TypeAttrColumnData : public TVMFFITypeAttrColumn { |
| std::vector<Any> data_; |
| }; |
| |
| int32_t GetOrAllocTypeIndex(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 type_table_[(*it).second]->type_index; |
| } |
| |
| // 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 " |
| << ToStringView(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 " << ToStringView(parent->type_key); |
| // allocate new entries. |
| int32_t allocated_tindex = static_cast<int32_t>(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 (parent != nullptr && !(parent->child_slots_can_overflow)) { |
| child_slots_can_overflow = false; |
| } |
| // total number of slots include the type itself. |
| |
| if (parent != nullptr) { |
| TVM_FFI_ICHECK_GT(allocated_tindex, parent->type_index); |
| } |
| |
| 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_.Set(type_key, allocated_tindex); |
| return allocated_tindex; |
| } |
| |
| int32_t TypeKeyToIndex(const TVMFFIByteArray* type_key) { |
| String type_key_str(type_key->data, type_key->size); |
| auto it = type_key2index_.find(type_key_str); |
| TVM_FFI_ICHECK(it != type_key2index_.end()) << "Cannot find type `" << type_key_str << "`"; |
| return static_cast<int32_t>((*it).second); |
| } |
| |
| 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(); |
| } |
| TVM_FFI_ICHECK(entry != nullptr) << "Cannot find type info for type_index=" << type_index; |
| return entry; |
| } |
| |
| Array<String> GetRegisteredTypeKeys() const { |
| Array<String> ret; |
| for (const auto& entry : type_table_) { |
| if (entry) { |
| ret.push_back(entry->type_key_data); |
| } |
| } |
| return ret; |
| } |
| |
| void RegisterTypeField(int32_t type_index, const TVMFFIFieldInfo* info) { |
| Entry* entry = GetTypeEntry(type_index); |
| std::string_view new_name(info->name.data, info->name.size); |
| std::string_view type_key(entry->type_key.data, entry->type_key.size); |
| // Check: no duplicate field name within this type's own fields. |
| for (const auto& existing : entry->type_fields_data) { |
| TVM_FFI_ICHECK(std::string_view(existing.name.data, existing.name.size) != new_name) |
| << "Duplicate field name \"" << new_name << "\" in type \"" << type_key << "\""; |
| } |
| // Warn: field name should not shadow any ancestor field. |
| for (int32_t d = 0; d < entry->type_depth; ++d) { |
| const TVMFFITypeInfo* ancestor = entry->type_ancestors[d]; |
| for (int32_t i = 0; i < ancestor->num_fields; ++i) { |
| if (std::string_view(ancestor->fields[i].name.data, ancestor->fields[i].name.size) == |
| new_name) { |
| std::cerr << "[WARNING] Field \"" << new_name << "\" in type \"" << type_key |
| << "\" duplicates an ancestor field in \"" |
| << std::string_view(ancestor->type_key.data, ancestor->type_key.size) |
| << "\". Child types should not re-register inherited fields." << std::endl; |
| } |
| } |
| } |
| TVMFFIFieldInfo field_data = *info; |
| // Retain FunctionObj setter via any_pool_ so it outlives the Entry. |
| if ((field_data.flags & kTVMFFIFieldFlagBitSetterIsFunctionObj) && |
| field_data.setter != nullptr) { |
| TVMFFIAny setter_ref; |
| setter_ref.type_index = kTVMFFIFunction; |
| setter_ref.zero_padding = 0; |
| setter_ref.v_obj = static_cast<TVMFFIObject*>(field_data.setter); |
| any_pool_.emplace_back(AnyView::CopyFromTVMFFIAny(setter_ref)); |
| } |
| field_data.name = this->CopyString(info->name); |
| field_data.doc = this->CopyString(info->doc); |
| field_data.metadata = this->CopyString(info->metadata); |
| if (info->flags & kTVMFFIFieldFlagBitMaskHasDefault) { |
| field_data.default_value_or_factory = |
| this->CopyAny(AnyView::CopyFromTVMFFIAny(info->default_value_or_factory)) |
| .CopyToTVMFFIAny(); |
| } else { |
| field_data.default_value_or_factory = AnyView(nullptr).CopyToTVMFFIAny(); |
| } |
| 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 RegisterTypeMethod(int32_t type_index, const TVMFFIMethodInfo* info) { |
| Entry* entry = GetTypeEntry(type_index); |
| TVMFFIMethodInfo method_data = *info; |
| method_data.name = this->CopyString(info->name); |
| method_data.doc = this->CopyString(info->doc); |
| method_data.metadata = this->CopyString(info->metadata); |
| method_data.method = this->CopyAny(AnyView::CopyFromTVMFFIAny(info->method)).CopyToTVMFFIAny(); |
| entry->type_methods_data.push_back(method_data); |
| entry->methods = entry->type_methods_data.data(); |
| entry->num_methods = static_cast<int32_t>(entry->type_methods_data.size()); |
| } |
| |
| void RegisterTypeMetadata(int32_t type_index, const TVMFFITypeMetadata* metadata) { |
| Entry* entry = GetTypeEntry(type_index); |
| if (entry->metadata != nullptr) { |
| TVM_FFI_LOG_AND_THROW(RuntimeError) |
| << "Overriding " << ToStringView(entry->type_key) << ", possible causes:\n" |
| << "- two ObjectDef<T>() calls for the same T \n" |
| << "- when we forget to assign _type_key to ObjectRef<Y> that inherits from T\n" |
| << "- another type with the same key is already registered\n" |
| << "Cross check the reflection registration."; |
| } |
| entry->metadata_data = *metadata; |
| entry->metadata_data.doc = this->CopyString(metadata->doc); |
| entry->metadata = &(entry->metadata_data); |
| } |
| |
| void RegisterTypeAttr(int32_t type_index, const TVMFFIByteArray* name, const TVMFFIAny* value) { |
| AnyView value_view = AnyView::CopyFromTVMFFIAny(*value); |
| String name_str(*name); |
| size_t column_index = 0; |
| auto it = type_attr_name_to_column_index_.find(name_str); |
| if (it == type_attr_name_to_column_index_.end()) { |
| column_index = type_attr_columns_.size(); |
| type_attr_columns_.emplace_back(std::make_unique<TypeAttrColumnData>()); |
| type_attr_name_to_column_index_.Set(name_str, static_cast<int64_t>(column_index)); |
| } else { |
| column_index = (*it).second; |
| } |
| TypeAttrColumnData* column = type_attr_columns_[column_index].get(); |
| if (type_index == kTVMFFINone) { |
| // Sentinel: just ensure the column exists without registering a value. |
| if (column->data_.empty()) { |
| column->data = reinterpret_cast<const TVMFFIAny*>(column->data_.data()); |
| column->size = 0; |
| column->begin_index = 0; |
| } |
| return; |
| } |
| // TODO(1.0): set begin_index to first registered type_index for sparse column storage |
| // For now, begin_index is always 0 (resize from index 0). |
| if (static_cast<size_t>(type_index) >= column->data_.size()) { |
| // Extend back from index 0. |
| column->data_.resize(static_cast<size_t>(type_index) + 1, Any(nullptr)); |
| } |
| column->data = reinterpret_cast<const TVMFFIAny*>(column->data_.data()); |
| column->size = static_cast<int32_t>(column->data_.size()); |
| column->begin_index = 0; |
| Any& slot = column->data_[type_index - column->begin_index]; |
| if (slot.type_index() != kTVMFFINone) { |
| TVM_FFI_THROW(RuntimeError) |
| << "TypeAttr `" << name_str << "` is already registered for type index " << type_index |
| << ". To update the stored value, register a mutable container (e.g., Dict/List) " |
| << "once and mutate it in place on subsequent calls."; |
| } |
| slot = value_view; |
| } |
| const TVMFFITypeAttrColumn* GetTypeAttrColumn(const TVMFFIByteArray* name) { |
| String name_str(*name); |
| auto it = type_attr_name_to_column_index_.find(name_str); |
| if (it == type_attr_name_to_column_index_.end()) return nullptr; |
| return type_attr_columns_[(*it).second].get(); |
| } |
| |
| 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) { |
| const Entry* ptr = it->get(); |
| if (ptr != nullptr && ptr->type_depth != 0) { |
| int parent_index = ptr->type_ancestors[ptr->type_depth - 1]->type_index; |
| num_children[parent_index] += num_children[ptr->type_index] + 1; |
| if (expected_child_slots[ptr->type_index] + 1 < ptr->num_slots) { |
| expected_child_slots[ptr->type_index] = ptr->num_slots - 1; |
| } |
| expected_child_slots[parent_index] += expected_child_slots[ptr->type_index] + 1; |
| } |
| } |
| |
| for (const auto& ptr : type_table_) { |
| if (ptr != nullptr && num_children[ptr->type_index] >= min_children_count) { |
| std::cerr << '[' << ptr->type_index << "]\t" << ToStringView(ptr->type_key); |
| if (ptr->type_depth != 0) { |
| int32_t parent_index = ptr->type_ancestors[ptr->type_depth - 1]->type_index; |
| std::cerr << "\tparent=" << ToStringView(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] |
| << "\texpected_child_slots=" << expected_child_slots[ptr->type_index] |
| << std::endl; |
| } |
| } |
| } |
| |
| static TypeTable* Global() { |
| // deliberately create a new instance via raw new |
| // to ensure table lives longer in case unloading |
| // still need the table info |
| // memory will be recycled by the OS at program exit |
| static TypeTable* inst = new TypeTable(); |
| return inst; |
| } |
| |
| private: |
| TypeTable() : type_attr_columns_{}, type_attr_name_to_column_index_{} { |
| 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(String(Object::_type_key), Object::_type_index, Object::_type_depth, |
| Object::_type_child_slots, Object::_type_child_slots_can_overflow, |
| -1); |
| TVMFFITypeMetadata info; |
| info.total_size = sizeof(Object); |
| info.structural_eq_hash_kind = kTVMFFISEqHashKindUnsupported; |
| info.creator = nullptr; |
| info.doc = TVMFFIByteArray{nullptr, 0}; |
| RegisterTypeMetadata(Object::_type_index, &info); |
| // reserve the static types |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFINone, TypeIndex::kTVMFFINone); |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFIInt, TypeIndex::kTVMFFIInt); |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFIBool, TypeIndex::kTVMFFIBool); |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFIFloat, TypeIndex::kTVMFFIFloat); |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFIOpaquePtr, TypeIndex::kTVMFFIOpaquePtr); |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFIDataType, TypeIndex::kTVMFFIDataType); |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFIDevice, TypeIndex::kTVMFFIDevice); |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFIDLTensorPtr, TypeIndex::kTVMFFIDLTensorPtr); |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFIRawStr, TypeIndex::kTVMFFIRawStr); |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFIByteArrayPtr, TypeIndex::kTVMFFIByteArrayPtr); |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFIObjectRValueRef, |
| TypeIndex::kTVMFFIObjectRValueRef); |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFISmallStr, TypeIndex::kTVMFFISmallStr); |
| ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFISmallBytes, TypeIndex::kTVMFFISmallBytes); |
| // reserved static type indices for depth 1 object types |
| ReserveDepthOneObjectTypeIndex(StaticTypeKey::kTVMFFIStr, TypeIndex::kTVMFFIStr); |
| ReserveDepthOneObjectTypeIndex(StaticTypeKey::kTVMFFIBytes, TypeIndex::kTVMFFIBytes); |
| ReserveDepthOneObjectTypeIndex(StaticTypeKey::kTVMFFIError, TypeIndex::kTVMFFIError); |
| ReserveDepthOneObjectTypeIndex(StaticTypeKey::kTVMFFIFunction, TypeIndex::kTVMFFIFunction); |
| ReserveDepthOneObjectTypeIndex(StaticTypeKey::kTVMFFIShape, TypeIndex::kTVMFFIShape); |
| ReserveDepthOneObjectTypeIndex(StaticTypeKey::kTVMFFITensor, TypeIndex::kTVMFFITensor); |
| ReserveDepthOneObjectTypeIndex(StaticTypeKey::kTVMFFIArray, TypeIndex::kTVMFFIArray); |
| ReserveDepthOneObjectTypeIndex(StaticTypeKey::kTVMFFIList, TypeIndex::kTVMFFIList); |
| ReserveDepthOneObjectTypeIndex(StaticTypeKey::kTVMFFIMap, TypeIndex::kTVMFFIMap); |
| ReserveDepthOneObjectTypeIndex(StaticTypeKey::kTVMFFIDict, TypeIndex::kTVMFFIDict); |
| ReserveDepthOneObjectTypeIndex(StaticTypeKey::kTVMFFIModule, TypeIndex::kTVMFFIModule); |
| ReserveDepthOneObjectTypeIndex(StaticTypeKey::kTVMFFIOpaquePyObject, |
| TypeIndex::kTVMFFIOpaquePyObject); |
| } |
| |
| void ReserveBuiltinTypeIndex(const char* type_key, int32_t static_type_index) { |
| this->GetOrAllocTypeIndex(String(type_key), static_type_index, 0, 0, false, -1); |
| } |
| |
| void ReserveDepthOneObjectTypeIndex(const char* type_key, int32_t static_type_index) { |
| this->GetOrAllocTypeIndex(String(type_key), static_type_index, 1, 0, false, |
| TypeIndex::kTVMFFIObject); |
| } |
| |
| static ObjectPtr<details::StringObj> MakeInplaceString(const char* data, size_t length) { |
| ObjectPtr<details::StringObj> p = |
| make_inplace_array_object<details::StringObj, char>(length + 1); |
| static_assert(alignof(details::StringObj) % alignof(char) == 0); |
| static_assert(sizeof(details::StringObj) % alignof(char) == 0); |
| char* dest_data = reinterpret_cast<char*>(p.get()) + sizeof(details::StringObj); |
| p->data = dest_data; |
| p->size = length; |
| std::memcpy(dest_data, data, length); |
| dest_data[length] = '\0'; |
| return p; |
| } |
| |
| TVMFFIByteArray CopyString(TVMFFIByteArray str) { |
| if (str.size == 0) { |
| return TVMFFIByteArray{nullptr, 0}; |
| } |
| // use explicit object creation to ensure the space pointer to not move |
| auto str_obj = MakeInplaceString(str.data, str.size); |
| TVMFFIByteArray c_val{str_obj->data, str_obj->size}; |
| any_pool_.emplace_back(ObjectRef(std::move(str_obj))); |
| return c_val; |
| } |
| |
| AnyView CopyAny(Any val) { |
| AnyView view = AnyView(val); |
| any_pool_.emplace_back(std::move(val)); |
| return view; |
| } |
| |
| int64_t type_counter_{TypeIndex::kTVMFFIDynObjectBegin}; |
| std::vector<std::unique_ptr<Entry>> type_table_; |
| Map<String, int64_t> type_key2index_; |
| std::vector<Any> any_pool_; |
| // type attribute columns |
| std::vector<std::unique_ptr<TypeAttrColumnData>> type_attr_columns_; |
| Map<String, int64_t> type_attr_name_to_column_index_; |
| }; |
| |
| /** |
| * \brief Opaque implementation |
| */ |
| class OpaqueObjectImpl : public Object, public TVMFFIOpaqueObjectCell { |
| public: |
| OpaqueObjectImpl(void* handle, void (*deleter)(void* handle)) : deleter_(deleter) { |
| this->handle = handle; |
| } |
| |
| void SetTypeIndex(int32_t type_index) { |
| details::ObjectUnsafe::GetHeader(this)->type_index = type_index; |
| } |
| |
| ~OpaqueObjectImpl() { |
| if (deleter_ != nullptr) { |
| deleter_(handle); |
| } |
| } |
| |
| private: |
| void (*deleter_)(void* handle); |
| }; |
| |
| ObjectRef GetMissingObject() { |
| static ObjectRef missing_obj(make_object<Object>()); |
| return missing_obj; |
| } |
| |
| ObjectRef GetKwargsObject() { |
| static ObjectRef kwargs_obj(make_object<Object>()); |
| return kwargs_obj; |
| } |
| |
| } // namespace ffi |
| } // namespace tvm |
| |
| void TVMFFIGetVersion(TVMFFIVersion* out_version) { |
| out_version->major = TVM_FFI_VERSION_MAJOR; |
| out_version->minor = TVM_FFI_VERSION_MINOR; |
| out_version->patch = TVM_FFI_VERSION_PATCH; |
| } |
| |
| int TVMFFIObjectDecRef(TVMFFIObjectHandle handle) { |
| TVM_FFI_SAFE_CALL_BEGIN(); |
| tvm::ffi::details::ObjectUnsafe::DecRefObjectHandle(handle); |
| TVM_FFI_SAFE_CALL_END(); |
| } |
| |
| int TVMFFIObjectIncRef(TVMFFIObjectHandle handle) { |
| TVM_FFI_SAFE_CALL_BEGIN(); |
| tvm::ffi::details::ObjectUnsafe::IncRefObjectHandle(handle); |
| TVM_FFI_SAFE_CALL_END(); |
| } |
| |
| int TVMFFIObjectCreateOpaque(void* handle, int32_t type_index, void (*deleter)(void* handle), |
| TVMFFIObjectHandle* out) { |
| TVM_FFI_SAFE_CALL_BEGIN(); |
| if (type_index != kTVMFFIOpaquePyObject) { |
| TVM_FFI_THROW(RuntimeError) << "Only kTVMFFIOpaquePyObject is supported for now"; |
| } |
| // create initial opaque object |
| tvm::ffi::ObjectPtr<tvm::ffi::OpaqueObjectImpl> p = |
| tvm::ffi::make_object<tvm::ffi::OpaqueObjectImpl>(handle, deleter); |
| // need to set the type index after creation, because the set to RuntimeTypeIndex() |
| // happens after the constructor is called |
| p->SetTypeIndex(type_index); |
| *out = tvm::ffi::details::ObjectUnsafe::MoveObjectPtrToTVMFFIObjectPtr(std::move(p)); |
| TVM_FFI_SAFE_CALL_END(); |
| } |
| |
| int TVMFFITypeKeyToIndex(const TVMFFIByteArray* type_key, int32_t* out_tindex) { |
| TVM_FFI_SAFE_CALL_BEGIN(); |
| out_tindex[0] = tvm::ffi::TypeTable::Global()->TypeKeyToIndex(type_key); |
| TVM_FFI_SAFE_CALL_END(); |
| } |
| |
| int TVMFFITypeRegisterField(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(); |
| } |
| |
| int TVMFFITypeRegisterMethod(int32_t type_index, const TVMFFIMethodInfo* info) { |
| TVM_FFI_SAFE_CALL_BEGIN(); |
| tvm::ffi::TypeTable::Global()->RegisterTypeMethod(type_index, info); |
| TVM_FFI_SAFE_CALL_END(); |
| } |
| |
| int TVMFFITypeRegisterMetadata(int32_t type_index, const TVMFFITypeMetadata* metadata) { |
| TVM_FFI_SAFE_CALL_BEGIN(); |
| tvm::ffi::TypeTable::Global()->RegisterTypeMetadata(type_index, metadata); |
| TVM_FFI_SAFE_CALL_END(); |
| } |
| |
| int TVMFFITypeRegisterAttr(int32_t type_index, const TVMFFIByteArray* name, |
| const TVMFFIAny* value) { |
| TVM_FFI_SAFE_CALL_BEGIN(); |
| tvm::ffi::TypeTable::Global()->RegisterTypeAttr(type_index, name, value); |
| TVM_FFI_SAFE_CALL_END(); |
| } |
| |
| const TVMFFITypeAttrColumn* TVMFFIGetTypeAttrColumn(const TVMFFIByteArray* name) { |
| TVM_FFI_LOG_EXCEPTION_CALL_BEGIN(); |
| return tvm::ffi::TypeTable::Global()->GetTypeAttrColumn(name); |
| TVM_FFI_LOG_EXCEPTION_CALL_END(TVMFFIGetTypeAttrColumn); |
| } |
| |
| int32_t TVMFFITypeGetOrAllocIndex(const TVMFFIByteArray* 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) { |
| TVM_FFI_LOG_EXCEPTION_CALL_BEGIN(); |
| tvm::ffi::String s_type_key(type_key->data, type_key->size); |
| return tvm::ffi::TypeTable::Global()->GetOrAllocTypeIndex( |
| s_type_key, static_type_index, type_depth, num_child_slots, child_slots_can_overflow, |
| parent_type_index); |
| TVM_FFI_LOG_EXCEPTION_CALL_END(TVMFFITypeGetOrAllocIndex); |
| } |
| |
| const TVMFFITypeInfo* TVMFFIGetTypeInfo(int32_t type_index) { |
| TVM_FFI_LOG_EXCEPTION_CALL_BEGIN(); |
| return tvm::ffi::TypeTable::Global()->GetTypeEntry(type_index); |
| TVM_FFI_LOG_EXCEPTION_CALL_END(TVMFFIGetTypeInfo); |
| } |
| |
| // string APIs, we blend into object.cc to keep things simple |
| int TVMFFIStringFromByteArray(const TVMFFIByteArray* input, TVMFFIAny* out) { |
| TVM_FFI_SAFE_CALL_BEGIN(); |
| // must set to none first |
| out->type_index = kTVMFFINone; |
| tvm::ffi::TypeTraits<tvm::ffi::String>::MoveToAny(tvm::ffi::String(input->data, input->size), |
| out); |
| TVM_FFI_SAFE_CALL_END(); |
| } |
| |
| int TVMFFIBytesFromByteArray(const TVMFFIByteArray* input, TVMFFIAny* out) { |
| TVM_FFI_SAFE_CALL_BEGIN(); |
| // must set to none first |
| out->type_index = kTVMFFINone; |
| tvm::ffi::TypeTraits<tvm::ffi::Bytes>::MoveToAny(tvm::ffi::Bytes(input->data, input->size), out); |
| TVM_FFI_SAFE_CALL_END(); |
| } |
| |
| namespace { |
| TVM_FFI_STATIC_INIT_BLOCK() { |
| namespace ffi = ::tvm::ffi; |
| namespace refl = ::tvm::ffi::reflection; |
| refl::TypeAttrDef<ffi::Object>().def( |
| refl::type_attr::kConvert, &refl::details::FFIConvertFromAnyViewToObjectRef<ffi::ObjectRef>); |
| refl::TypeAttrDef<ffi::details::StringObj>().def( |
| refl::type_attr::kConvert, &refl::details::FFIConvertFromAnyViewToObjectRef<ffi::String>); |
| refl::TypeAttrDef<ffi::details::BytesObj>().def( |
| refl::type_attr::kConvert, &refl::details::FFIConvertFromAnyViewToObjectRef<ffi::Bytes>); |
| refl::TypeAttrDef<ffi::ErrorObj>().def( |
| refl::type_attr::kConvert, &refl::details::FFIConvertFromAnyViewToObjectRef<ffi::Error>); |
| refl::TypeAttrDef<ffi::FunctionObj>().def( |
| refl::type_attr::kConvert, &refl::details::FFIConvertFromAnyViewToObjectRef<ffi::Function>); |
| refl::TypeAttrDef<ffi::ShapeObj>().def( |
| refl::type_attr::kConvert, &refl::details::FFIConvertFromAnyViewToObjectRef<ffi::Shape>); |
| refl::TypeAttrDef<ffi::TensorObj>().def( |
| refl::type_attr::kConvert, &refl::details::FFIConvertFromAnyViewToObjectRef<ffi::Tensor>); |
| refl::TypeAttrDef<ffi::ArrayObj>().def( |
| refl::type_attr::kConvert, |
| &refl::details::FFIConvertFromAnyViewToObjectRef<ffi::Array<ffi::Any>>); |
| refl::TypeAttrDef<ffi::MapObj>().def( |
| refl::type_attr::kConvert, |
| &refl::details::FFIConvertFromAnyViewToObjectRef<ffi::Map<ffi::Any, ffi::Any>>); |
| // Skipped: TypeIndex::kTVMFFIModule |
| // Skipped: TypeIndex::kTVMFFIOpaquePyObject |
| refl::TypeAttrDef<ffi::ListObj>().def( |
| refl::type_attr::kConvert, |
| &refl::details::FFIConvertFromAnyViewToObjectRef<ffi::List<ffi::Any>>); |
| refl::TypeAttrDef<ffi::DictObj>().def( |
| refl::type_attr::kConvert, |
| &refl::details::FFIConvertFromAnyViewToObjectRef<ffi::Dict<ffi::Any, ffi::Any>>); |
| refl::ObjectDef<ffi::EnumObj>(refl::init(false)) |
| .def_ro("_value", &ffi::EnumObj::_value, "Ordinal assigned at registration.", |
| refl::AttachFieldFlag::SEqHashIgnore()) |
| .def_ro("_name", &ffi::EnumObj::_name, "Instance name."); |
| refl::EnsureTypeAttrColumn(refl::type_attr::kEnumEntries); |
| refl::EnsureTypeAttrColumn(refl::type_attr::kEnumAttrs); |
| refl::EnsureTypeAttrColumn(refl::type_attr::kEnumValueEntries); |
| refl::GlobalDef() |
| .def_method("ffi.GetRegisteredTypeKeys", |
| []() -> ffi::Array<ffi::String> { |
| return ffi::TypeTable::Global()->GetRegisteredTypeKeys(); |
| }) |
| .def("ffi.GetInvalidObject", ffi::GetMissingObject) |
| .def("ffi.GetKwargsObject", ffi::GetKwargsObject); |
| } |
| } // namespace |