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 ea2549e0a8716b7984723a425a8071be3de9980e Author: tqchen <[email protected]> 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
