| // Copyright 2019 The IREE Authors |
| // |
| // Licensed under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| #ifndef IREE_VM_MODULE_ABI_PACKING_H_ |
| #define IREE_VM_MODULE_ABI_PACKING_H_ |
| |
| #include <memory> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include "iree/base/api.h" |
| #include "iree/base/internal/span.h" |
| #include "iree/base/status_cc.h" |
| #include "iree/vm/builtin_types.h" |
| #include "iree/vm/module.h" |
| #include "iree/vm/ref.h" |
| #include "iree/vm/ref_cc.h" |
| #include "iree/vm/stack.h" |
| |
| // std::string_view is available starting in C++17. |
| // Prior to that only IREE's C iree_string_view_t is available. |
| #if defined(__has_include) |
| #if __has_include(<string_view>) && __cplusplus >= 201703L |
| #define IREE_HAVE_STD_STRING_VIEW 1 |
| #include <string_view> |
| #endif // __has_include(<string_view>) |
| #endif // __has_include |
| |
| namespace iree { |
| namespace vm { |
| namespace packing { |
| |
| namespace impl { |
| |
| // Workaround required to ensure proper evaluation order of parameter packs. |
| // MSVC (and other compilers, like clang-cl in MSVC compat mode) may evaluate |
| // parameter pack function arguments in any order. This shim allows us to expand |
| // the parameter pack inside of an initializer list, which unlike function |
| // arguments must be evaluated by the compiler in the order the elements appear |
| // in the list. |
| // |
| // Example: |
| // impl::order_sequence{(ExpandedAction(), 0)...}; |
| // |
| // More information: |
| // https://stackoverflow.com/questions/29194858/order-of-function-calls-in-variadic-template-expansion |
| struct order_sequence { |
| template <typename... T> |
| order_sequence(T&&...) {} |
| }; |
| |
| // Coming in C++20, but not widely available yet. |
| template <class T> |
| struct remove_cvref { |
| typedef std::remove_cv_t<std::remove_reference_t<T>> type; |
| }; |
| |
| } // namespace impl |
| |
| template <typename T> |
| using enable_if_primitive = |
| typename std::enable_if<std::is_arithmetic<T>::value || |
| std::is_enum<T>::value>::type; |
| template <typename T> |
| using enable_if_not_primitive = typename std::enable_if<!( |
| std::is_arithmetic<T>::value || std::is_enum<T>::value)>::type; |
| |
| //===----------------------------------------------------------------------===// |
| // Compile-time string literals |
| //===----------------------------------------------------------------------===// |
| |
| // Compile-time constant string. |
| // This allows us to concat string literals and produce a single flattened |
| // char[] containing the results. Includes a \0 so the character storage is |
| // length N + 1 and can be accessed as a c_str. |
| // |
| // Use the `literal` helper function to define a const string literal without |
| // needing the size. |
| // |
| // Example: |
| // // produces: const_string<2>("ab") |
| // constexpr const auto str = literal("a") + literal("b"); |
| template <size_t N> |
| class const_string { |
| public: |
| constexpr const_string(const char (&data)[N + 1]) |
| : const_string(data, std::make_index_sequence<N>()) {} |
| template <size_t N1, typename std::enable_if<(N1 <= N), bool>::type = true> |
| constexpr const_string(const const_string<N1>& lhs, |
| const const_string<N - N1>& rhs) |
| : const_string{lhs, rhs, std::make_index_sequence<N1>{}, |
| std::make_index_sequence<N - N1>{}} {} |
| |
| constexpr std::size_t size() const { return N; } |
| constexpr const char* data() const { return data_; } |
| constexpr const char* c_str() const { return data_; } |
| constexpr operator const char*() const { return data_; } |
| constexpr char operator[](size_t i) const { return data_[i]; } |
| |
| private: |
| template <size_t... PACK> |
| constexpr const_string(const char (&data)[N + 1], |
| std::index_sequence<PACK...>) |
| : data_{data[PACK]..., '\0'} {} |
| template <size_t N1, size_t... PACK1, size_t... PACK2> |
| constexpr const_string(const const_string<N1>& lhs, |
| const const_string<N - N1>& rhs, |
| std::index_sequence<PACK1...>, |
| std::index_sequence<PACK2...>) |
| : data_{lhs[PACK1]..., rhs[PACK2]..., '\0'} {} |
| |
| const char data_[N + 1]; |
| }; |
| |
| template <size_t N1, size_t N2> |
| constexpr auto operator+(const const_string<N1>& lhs, |
| const const_string<N2>& rhs) { |
| return const_string<N1 + N2>(lhs, rhs); |
| } |
| |
| // Defines a compile-time constant string literal. |
| template <size_t N_PLUS_1> |
| constexpr auto literal(const char (&data)[N_PLUS_1]) { |
| return const_string<N_PLUS_1 - 1>(data); |
| } |
| |
| constexpr auto concat_impl() { return literal(""); } |
| template <typename T> |
| constexpr auto concat_impl(const T& lhs) { |
| return lhs; |
| } |
| template <typename T, typename... Ts> |
| constexpr auto concat_impl(const T& lhs, const Ts&... s) { |
| return lhs + concat_impl(s...); |
| } |
| |
| // Concatenates one or more const_string values into a new const_string. |
| // |
| // Example: |
| // constexpr const auto abc = concat_literals(literal("a"), |
| // literal("b"), |
| // literal("c")); |
| template <typename... Ts> |
| constexpr auto concat_literals(const Ts&... s) { |
| return concat_impl(s...); |
| } |
| |
| template <size_t C, typename T> |
| struct splat_impl { |
| static constexpr auto apply(const T& v) { |
| return concat_literals(v, splat_impl<C - 1, T>::apply(v)); |
| } |
| }; |
| template <typename T> |
| struct splat_impl<1, T> { |
| static constexpr auto apply(const T& v) { return v; } |
| }; |
| |
| // Splats a single const_string value C times. |
| // |
| // Example: |
| // constexpr const auto aaa = splat_literal<3>(literal("a")); |
| template <size_t C, typename T> |
| constexpr auto splat_literal(const T& v) { |
| return splat_impl<C, T>::apply(v); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Calling convention format generation |
| //===----------------------------------------------------------------------===// |
| // Prototyped here: https://godbolt.org/z/Tvhh7M |
| |
| template <typename T> |
| struct cconv_map; |
| |
| template <typename T> |
| struct cconv_map { |
| static constexpr const auto conv_chars = literal("i"); |
| }; |
| |
| template <> |
| struct cconv_map<int64_t> { |
| static constexpr const auto conv_chars = literal("I"); |
| }; |
| template <> |
| struct cconv_map<uint64_t> { |
| static constexpr const auto conv_chars = literal("I"); |
| }; |
| |
| template <> |
| struct cconv_map<opaque_ref> { |
| static constexpr const auto conv_chars = literal("r"); |
| }; |
| template <typename T> |
| struct cconv_map<ref<T>> { |
| static constexpr const auto conv_chars = literal("r"); |
| }; |
| template <> |
| struct cconv_map<iree_string_view_t> { |
| static constexpr const auto conv_chars = literal("r"); |
| }; |
| #if defined(IREE_HAVE_STD_STRING_VIEW) |
| template <> |
| struct cconv_map<std::string_view> { |
| static constexpr const auto conv_chars = literal("r"); |
| }; |
| #endif // IREE_HAVE_STD_STRING_VIEW |
| |
| template <typename U, size_t S> |
| struct cconv_map<std::array<U, S>> { |
| static constexpr const auto conv_chars = splat_literal<S>( |
| cconv_map<typename impl::remove_cvref<U>::type>::conv_chars); |
| }; |
| |
| template <typename... Ts> |
| struct cconv_map<std::tuple<Ts...>> { |
| static constexpr const auto conv_chars = concat_literals( |
| cconv_map<typename impl::remove_cvref<Ts>::type>::conv_chars...); |
| }; |
| |
| template <typename U> |
| struct cconv_map<iree::span<U>> { |
| static constexpr const auto conv_chars = concat_literals( |
| literal("C"), cconv_map<typename impl::remove_cvref<U>::type>::conv_chars, |
| literal("D")); |
| }; |
| |
| template <typename Result, size_t ParamsCount, typename... Params> |
| struct cconv_storage { |
| static const iree_string_view_t value() { |
| static constexpr const auto value = concat_literals( |
| literal("0"), |
| concat_literals( |
| cconv_map< |
| typename impl::remove_cvref<Params>::type>::conv_chars...), |
| literal("_"), |
| concat_literals( |
| cconv_map<typename impl::remove_cvref<Result>::type>::conv_chars)); |
| static constexpr const auto str = |
| iree_string_view_t{value.data(), value.size()}; |
| return str; |
| } |
| }; |
| |
| template <typename Result> |
| struct cconv_storage<Result, 0> { |
| static const iree_string_view_t value() { |
| static constexpr const auto value = concat_literals( |
| literal("0v_"), |
| concat_literals( |
| cconv_map<typename impl::remove_cvref<Result>::type>::conv_chars)); |
| static constexpr const auto str = |
| iree_string_view_t{value.data(), value.size()}; |
| return str; |
| } |
| }; |
| |
| template <size_t ParamsCount, typename... Params> |
| struct cconv_storage_void { |
| static const iree_string_view_t value() { |
| static constexpr const auto value = concat_literals( |
| literal("0"), |
| concat_literals( |
| cconv_map< |
| typename impl::remove_cvref<Params>::type>::conv_chars...), |
| literal("_v")); |
| static constexpr const auto str = |
| iree_string_view_t{value.data(), value.size()}; |
| return str; |
| } |
| }; |
| |
| template <> |
| struct cconv_storage_void<0> { |
| static const iree_string_view_t value() { |
| static constexpr const auto value = concat_literals(literal("0v_v")); |
| static constexpr const auto str = |
| iree_string_view_t{value.data(), value.size()}; |
| return str; |
| } |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // Parameter unpacking |
| //===----------------------------------------------------------------------===// |
| |
| // TODO(benvanik): see if we can't use `extern template` to share |
| // implementations of these and prevent code bloat across many modules. |
| // We can also try some non-templated base functions (like "UnpackI32") that the |
| // templated ones simply wrap with type casts. |
| |
| namespace impl { |
| |
| using params_ptr_t = uint8_t*; |
| |
| template <typename T, typename EN = void> |
| struct ParamUnpack; |
| template <> |
| struct ParamUnpack<opaque_ref>; |
| template <typename T> |
| struct ParamUnpack<ref<T>>; |
| template <typename T> |
| struct ParamUnpack<const ref<T>>; |
| template <> |
| struct ParamUnpack<iree_string_view_t>; |
| #if defined(IREE_HAVE_STD_STRING_VIEW) |
| template <> |
| struct ParamUnpack<std::string_view>; |
| #endif // IREE_HAVE_STD_STRING_VIEW |
| template <typename U, size_t S> |
| struct ParamUnpack<std::array<U, S>>; |
| template <typename... Ts> |
| struct ParamUnpack<std::tuple<Ts...>>; |
| template <typename U> |
| struct ParamUnpack<iree::span<U>, enable_if_not_primitive<U>>; |
| template <typename U> |
| struct ParamUnpack<iree::span<U>, enable_if_primitive<U>>; |
| |
| struct Unpacker { |
| template <typename... Ts> |
| static StatusOr<std::tuple<typename impl::ParamUnpack< |
| typename std::remove_reference<Ts>::type>::storage_type...>> |
| LoadSequence(iree_byte_span_t storage) { |
| auto params = std::make_tuple( |
| typename impl::ParamUnpack< |
| typename impl::remove_cvref<Ts>::type>::storage_type()...); |
| Status status; |
| params_ptr_t ptr = storage.data; |
| ApplyLoad<Ts...>(status, ptr, params, |
| std::make_index_sequence<sizeof...(Ts)>()); |
| IREE_RETURN_IF_ERROR(std::move(status)); |
| params_ptr_t limit = storage.data + storage.data_length; |
| if (IREE_UNLIKELY(ptr != limit)) { |
| return iree_make_status( |
| IREE_STATUS_INVALID_ARGUMENT, |
| "argument buffer unpacking failure; consumed %zu of %zu bytes", |
| (reinterpret_cast<intptr_t>(ptr) - |
| reinterpret_cast<intptr_t>(storage.data)), |
| storage.data_length); |
| } |
| return std::move(params); |
| } |
| |
| private: |
| template <typename... Ts, typename T, size_t... I> |
| static void ApplyLoad(Status& status, params_ptr_t& ptr, T&& params, |
| std::index_sequence<I...>) { |
| impl::order_sequence{ |
| (impl::ParamUnpack<typename impl::remove_cvref< |
| typename std::tuple_element<I, std::tuple<Ts...>>::type>::type>:: |
| Load(status, ptr, std::get<I>(params)), |
| 0)...}; |
| } |
| }; |
| |
| // Common primitive types (`i32`, `i64`, `f32`, enums, etc). |
| template <typename T> |
| struct ParamUnpack<T, enable_if_primitive<T>> { |
| using storage_type = T; |
| static void Load(Status& status, params_ptr_t& ptr, storage_type& out_param) { |
| out_param = *reinterpret_cast<const T*>(ptr); |
| ptr += sizeof(T); |
| } |
| }; |
| |
| // An opaque ref type (`vm.ref<?>`), possibly null. |
| template <> |
| struct ParamUnpack<opaque_ref> { |
| using storage_type = opaque_ref; |
| static void Load(Status& status, params_ptr_t& ptr, storage_type& out_param) { |
| iree_vm_ref_retain(reinterpret_cast<iree_vm_ref_t*>(ptr), &out_param); |
| ptr += sizeof(iree_vm_ref_t); |
| } |
| }; |
| |
| // A `vm.ref<T>` type, possibly null. |
| // Ownership is transferred to the parameter. |
| template <typename T> |
| struct ParamUnpack<ref<T>> { |
| using storage_type = ref<T>; |
| static void Load(Status& status, params_ptr_t& ptr, storage_type& out_param) { |
| auto* reg_ptr = reinterpret_cast<iree_vm_ref_t*>(ptr); |
| ptr += sizeof(iree_vm_ref_t); |
| if (reg_ptr->type == ref_type_descriptor<T>::get()->type) { |
| out_param = vm::retain_ref(reinterpret_cast<T*>(reg_ptr->ptr)); |
| memset(reg_ptr, 0, sizeof(*reg_ptr)); |
| } else if (IREE_UNLIKELY(reg_ptr->type != IREE_VM_REF_TYPE_NULL)) { |
| status = |
| iree_make_status(IREE_STATUS_INVALID_ARGUMENT, |
| "parameter contains a reference to the wrong type; " |
| "have %.*s but expected %.*s", |
| (int)iree_vm_ref_type_name(reg_ptr->type).size, |
| iree_vm_ref_type_name(reg_ptr->type).data, |
| (int)ref_type_descriptor<T>::get()->type_name.size, |
| ref_type_descriptor<T>::get()->type_name.data); |
| } else { |
| out_param = {}; |
| } |
| } |
| }; |
| |
| // TODO(benvanik): merge with above somehow? |
| template <typename T> |
| struct ParamUnpack<const ref<T>> { |
| using storage_type = ref<T>; |
| static void Load(Status& status, params_ptr_t& ptr, storage_type& out_param) { |
| auto* reg_ptr = reinterpret_cast<iree_vm_ref_t*>(ptr); |
| ptr += sizeof(iree_vm_ref_t); |
| if (reg_ptr->type == ref_type_descriptor<T>::get()->type) { |
| out_param = vm::retain_ref(reinterpret_cast<T*>(reg_ptr->ptr)); |
| memset(reg_ptr, 0, sizeof(*reg_ptr)); |
| } else if (IREE_UNLIKELY(reg_ptr->type != IREE_VM_REF_TYPE_NULL)) { |
| status = |
| iree_make_status(IREE_STATUS_INVALID_ARGUMENT, |
| "parameter contains a reference to the wrong type; " |
| "have %.*s but expected %.*s", |
| (int)iree_vm_ref_type_name(reg_ptr->type).size, |
| iree_vm_ref_type_name(reg_ptr->type).data, |
| (int)ref_type_descriptor<T>::get()->type_name.size, |
| ref_type_descriptor<T>::get()->type_name.data); |
| } else { |
| out_param = {}; |
| } |
| } |
| }; |
| |
| // An `util.byte_buffer` containing a string. |
| // The string view is aliased directly into the underlying byte buffer. |
| template <> |
| struct ParamUnpack<iree_string_view_t> { |
| using storage_type = iree_string_view_t; |
| static void Load(Status& status, params_ptr_t& ptr, storage_type& out_param) { |
| auto* reg_ptr = reinterpret_cast<iree_vm_ref_t*>(ptr); |
| ptr += sizeof(iree_vm_ref_t); |
| if (reg_ptr->type == ref_type_descriptor<iree_vm_buffer_t>::get()->type) { |
| auto byte_span = reinterpret_cast<iree_vm_buffer_t*>(reg_ptr->ptr)->data; |
| out_param = iree_make_string_view( |
| reinterpret_cast<const char*>(byte_span.data), byte_span.data_length); |
| } else if (IREE_UNLIKELY(reg_ptr->type != IREE_VM_REF_TYPE_NULL)) { |
| status = iree_make_status( |
| IREE_STATUS_INVALID_ARGUMENT, |
| "parameter contains a reference to the wrong type; " |
| "have %.*s but expected %.*s", |
| (int)iree_vm_ref_type_name(reg_ptr->type).size, |
| iree_vm_ref_type_name(reg_ptr->type).data, |
| (int)ref_type_descriptor<iree_vm_buffer_t>::get()->type_name.size, |
| ref_type_descriptor<iree_vm_buffer_t>::get()->type_name.data); |
| } else { |
| // NOTE: empty string is allowed here! |
| out_param = iree_string_view_empty(); |
| } |
| } |
| }; |
| #if defined(IREE_HAVE_STD_STRING_VIEW) |
| template <> |
| struct ParamUnpack<std::string_view> { |
| using storage_type = std::string_view; |
| static void Load(Status& status, params_ptr_t& ptr, storage_type& out_param) { |
| auto* reg_ptr = reinterpret_cast<iree_vm_ref_t*>(ptr); |
| ptr += sizeof(iree_vm_ref_t); |
| if (reg_ptr->type == ref_type_descriptor<iree_vm_buffer_t>::get()->type) { |
| auto byte_span = reinterpret_cast<iree_vm_buffer_t*>(reg_ptr->ptr)->data; |
| out_param = std::string_view{ |
| reinterpret_cast<const char*>(byte_span.data), byte_span.data_length}; |
| } else if (IREE_UNLIKELY(reg_ptr->type != IREE_VM_REF_TYPE_NULL)) { |
| status = iree_make_status( |
| IREE_STATUS_INVALID_ARGUMENT, |
| "parameter contains a reference to the wrong type; " |
| "have %.*s but expected %.*s", |
| (int)iree_vm_ref_type_name(reg_ptr->type).size, |
| iree_vm_ref_type_name(reg_ptr->type).data, |
| (int)ref_type_descriptor<iree_vm_buffer_t>::get()->type_name.size, |
| ref_type_descriptor<iree_vm_buffer_t>::get()->type_name.data); |
| } else { |
| // NOTE: empty string is allowed here! |
| out_param = {}; |
| } |
| } |
| }; |
| #endif // IREE_HAVE_STD_STRING_VIEW |
| |
| // Arrays are C++ ABI only representing a fixed repeated field (`i32, i32`). |
| template <typename U, size_t S> |
| struct ParamUnpack<std::array<U, S>> { |
| using element_type = typename impl::remove_cvref<U>::type; |
| using storage_type = std::array<element_type, S>; |
| static void Load(Status& status, params_ptr_t& ptr, storage_type& out_param) { |
| for (size_t i = 0; i < S; ++i) { |
| ParamUnpack::Load(status, ptr, out_param[i]); |
| } |
| } |
| }; |
| |
| // Tuples (`tuple<i32, i64>`) expand to just their flattened contents. |
| template <typename... Ts> |
| struct ParamUnpack<std::tuple<Ts...>> { |
| using storage_type = std::tuple<typename impl::remove_cvref<Ts>::type...>; |
| static void Load(Status& status, params_ptr_t& ptr, storage_type& out_param) { |
| UnpackTuple(status, ptr, out_param, |
| std::make_index_sequence<sizeof...(Ts)>()); |
| } |
| template <size_t... I> |
| static void UnpackTuple(Status& status, params_ptr_t& ptr, |
| storage_type& params, std::index_sequence<I...>) { |
| impl::order_sequence{ |
| (ParamUnpack<typename std::tuple_element<I, std::tuple<Ts...>>::type>:: |
| Load(status, ptr, std::get<I>(params)), |
| 0)...}; |
| } |
| }; |
| |
| // Complex variadic span (like `tuple<i32, tuple<ref<...>, i64>>...`). |
| // We need to allocate storage here so that we can marshal the element type out. |
| // In the future we could check that all subelements are primitives and alias if |
| // the host machine endianness is the same. |
| template <typename U> |
| struct ParamUnpack<iree::span<U>, enable_if_not_primitive<U>> { |
| using element_type = typename impl::remove_cvref<U>::type; |
| using storage_type = std::vector<element_type>; |
| static void Load(Status& status, params_ptr_t& ptr, storage_type& out_param) { |
| iree_host_size_t count = *reinterpret_cast<const int32_t*>(ptr); |
| ptr += sizeof(int32_t); |
| out_param.resize(count); |
| for (iree_host_size_t i = 0; i < count; ++i) { |
| ParamUnpack<element_type>::Load(status, ptr, out_param[i]); |
| } |
| } |
| }; |
| |
| // Simple primitive variadic span (like `i32...`). We can alias directly into |
| // the argument buffer so long as endianness matches. |
| template <typename U> |
| struct ParamUnpack<iree::span<U>, enable_if_primitive<U>> { |
| using element_type = U; |
| using storage_type = iree::span<const element_type>; |
| static void Load(Status& status, params_ptr_t& ptr, storage_type& out_param) { |
| iree_host_size_t count = *reinterpret_cast<const int32_t*>(ptr); |
| ptr += sizeof(int32_t); |
| out_param = |
| iree::span<U>(reinterpret_cast<const element_type*>(ptr), count); |
| ptr += sizeof(element_type) * count; |
| } |
| }; |
| |
| } // namespace impl |
| |
| //===----------------------------------------------------------------------===// |
| // Result packing |
| //===----------------------------------------------------------------------===// |
| |
| namespace impl { |
| |
| using result_ptr_t = uint8_t*; |
| |
| template <typename T> |
| struct ResultPack { |
| static void Store(result_ptr_t& ptr, T value) { |
| *reinterpret_cast<T*>(ptr) = value; |
| ptr += sizeof(T); |
| } |
| }; |
| |
| template <> |
| struct ResultPack<opaque_ref> { |
| static void Store(result_ptr_t& ptr, opaque_ref value) { |
| iree_vm_ref_move(value.get(), reinterpret_cast<iree_vm_ref_t*>(ptr)); |
| ptr += sizeof(iree_vm_ref_t); |
| } |
| }; |
| |
| template <typename T> |
| struct ResultPack<ref<T>> { |
| static void Store(result_ptr_t& ptr, ref<T> value) { |
| iree_vm_ref_wrap_assign(value.release(), value.type(), |
| reinterpret_cast<iree_vm_ref_t*>(ptr)); |
| ptr += sizeof(iree_vm_ref_t); |
| } |
| }; |
| |
| template <typename U, size_t S> |
| struct ResultPack<std::array<U, S>>; |
| template <typename... Ts> |
| struct ResultPack<std::tuple<Ts...>>; |
| |
| template <typename U, size_t S> |
| struct ResultPack<std::array<U, S>> { |
| static void Store(result_ptr_t& ptr, std::array<U, S> value) { |
| for (size_t i = 0; i < S; ++i) { |
| ResultPack<U>::Store(ptr, std::move(value[i])); |
| } |
| } |
| }; |
| |
| template <typename... Ts> |
| struct ResultPack<std::tuple<Ts...>> { |
| static void Store(result_ptr_t& ptr, std::tuple<Ts...> results) { |
| PackTuple(ptr, results, std::make_index_sequence<sizeof...(Ts)>()); |
| } |
| template <typename... T, size_t... I> |
| static inline void PackTuple(result_ptr_t& ptr, std::tuple<T...>& value, |
| std::index_sequence<I...>) { |
| impl::order_sequence{ |
| (ResultPack<typename std::tuple_element<I, std::tuple<T...>>::type>:: |
| Store(ptr, std::move(std::get<I>(value))), |
| 0)...}; |
| } |
| }; |
| |
| } // namespace impl |
| |
| //===----------------------------------------------------------------------===// |
| // Function wrapping |
| //===----------------------------------------------------------------------===// |
| |
| template <typename Owner, typename Results, typename... Params> |
| struct DispatchFunctor { |
| using FnPtr = StatusOr<Results> (Owner::*)(Params...); |
| |
| static Status Call(void (Owner::*ptr)(), Owner* self, iree_vm_stack_t* stack, |
| const iree_vm_function_call_t* call, |
| iree_vm_execution_result_t* out_result) { |
| // Marshal arguments into types/locals we can forward to the function. |
| IREE_ASSIGN_OR_RETURN( |
| auto params, impl::Unpacker::LoadSequence<Params...>(call->arguments)); |
| |
| // Call the target function with the params. |
| IREE_ASSIGN_OR_RETURN( |
| auto results, |
| ApplyFn(reinterpret_cast<FnPtr>(ptr), self, std::move(params), |
| std::make_index_sequence<sizeof...(Params)>())); |
| |
| // Marshal call results back into the ABI results buffer. |
| impl::result_ptr_t result_ptr = call->results.data; |
| impl::ResultPack<Results>::Store(result_ptr, std::move(results)); |
| |
| return OkStatus(); |
| } |
| |
| template <typename T, size_t... I> |
| static StatusOr<Results> ApplyFn(FnPtr ptr, Owner* self, T&& params, |
| std::index_sequence<I...>) { |
| return (self->*ptr)(std::move(std::get<I>(params))...); |
| } |
| }; |
| |
| // A DispatchFunctor specialization for methods with no return values. |
| template <typename Owner, typename... Params> |
| struct DispatchFunctorVoid { |
| using FnPtr = Status (Owner::*)(Params...); |
| |
| static Status Call(void (Owner::*ptr)(), Owner* self, iree_vm_stack_t* stack, |
| const iree_vm_function_call_t* call, |
| iree_vm_execution_result_t* out_result) { |
| IREE_ASSIGN_OR_RETURN( |
| auto params, impl::Unpacker::LoadSequence<Params...>(call->arguments)); |
| return ApplyFn(reinterpret_cast<FnPtr>(ptr), self, std::move(params), |
| std::make_index_sequence<sizeof...(Params)>()); |
| } |
| |
| template <typename T, size_t... I> |
| static Status ApplyFn(FnPtr ptr, Owner* self, T&& params, |
| std::index_sequence<I...>) { |
| return (self->*ptr)(std::move(std::get<I>(params))...); |
| } |
| }; |
| |
| } // namespace packing |
| |
| template <typename Owner> |
| struct NativeFunction { |
| iree_string_view_t name; |
| iree_string_view_t cconv; |
| void (Owner::*const ptr)(); |
| Status (*const call)(void (Owner::*ptr)(), Owner* self, |
| iree_vm_stack_t* stack, |
| const iree_vm_function_call_t* call, |
| iree_vm_execution_result_t* out_result); |
| }; |
| |
| template <typename Owner, typename Result, typename... Params> |
| constexpr NativeFunction<Owner> MakeNativeFunction( |
| const char* name, StatusOr<Result> (Owner::*fn)(Params...)) { |
| using dispatch_functor_t = packing::DispatchFunctor<Owner, Result, Params...>; |
| return {iree_make_cstring_view(name), |
| packing::cconv_storage<Result, sizeof...(Params), Params...>::value(), |
| (void (Owner::*)())fn, &dispatch_functor_t::Call}; |
| } |
| |
| template <typename Owner, typename... Params> |
| constexpr NativeFunction<Owner> MakeNativeFunction( |
| const char* name, Status (Owner::*fn)(Params...)) { |
| using dispatch_functor_t = packing::DispatchFunctorVoid<Owner, Params...>; |
| return {iree_make_cstring_view(name), |
| packing::cconv_storage_void<sizeof...(Params), Params...>::value(), |
| (void (Owner::*)())fn, &dispatch_functor_t::Call}; |
| } |
| |
| } // namespace vm |
| } // namespace iree |
| |
| #endif // IREE_VM_MODULE_ABI_PACKING_H_ |