#pragma once
#include <executorch/runtime/core/exec_aten/exec_aten.h>
#include <executorch/runtime/core/result.h>
#include <executorch/runtime/core/tag.h>
#include <executorch/runtime/platform/assert.h>
namespace executorch {
namespace runtime {
struct EValue;
namespace internal {
template <typename T>
struct evalue_to_const_ref_overload_return {
using type = T;
};
template <>
struct evalue_to_const_ref_overload_return<executorch::aten::Tensor> {
using type = const executorch::aten::Tensor&;
};
template <typename T>
struct evalue_to_ref_overload_return {
using type = T;
};
template <>
struct evalue_to_ref_overload_return<executorch::aten::Tensor> {
using type = executorch::aten::Tensor&;
};
}
template <typename T>
class BoxedEvalueList {
public:
BoxedEvalueList() = default;
BoxedEvalueList(EValue** wrapped_vals, T* unwrapped_vals, int size)
: wrapped_vals_(checkWrappedVals(wrapped_vals, size), size),
unwrapped_vals_(checkUnwrappedVals(unwrapped_vals)) {}
executorch::aten::ArrayRef<T> get() const;
Result<executorch::aten::ArrayRef<T>> tryGet() const;
void destroy_elements() {
for (typename executorch::aten::ArrayRef<T>::size_type i = 0;
i < wrapped_vals_.size();
i++) {
unwrapped_vals_[i].~T();
}
}
private:
static EValue** checkWrappedVals(EValue** wrapped_vals, int size) {
ET_CHECK_MSG(wrapped_vals != nullptr, "wrapped_vals cannot be null");
ET_CHECK_MSG(size >= 0, "size cannot be negative");
return wrapped_vals;
}
static T* checkUnwrappedVals(T* unwrapped_vals) {
ET_CHECK_MSG(unwrapped_vals != nullptr, "unwrapped_vals cannot be null");
return unwrapped_vals;
}
executorch::aten::ArrayRef<EValue*> wrapped_vals_;
mutable T* unwrapped_vals_;
};
template <>
executorch::aten::ArrayRef<std::optional<executorch::aten::Tensor>>
BoxedEvalueList<std::optional<executorch::aten::Tensor>>::get() const;
template <>
Result<executorch::aten::ArrayRef<std::optional<executorch::aten::Tensor>>>
BoxedEvalueList<std::optional<executorch::aten::Tensor>>::tryGet() const;
struct EValue {
union Payload {
union TriviallyCopyablePayload {
TriviallyCopyablePayload() : as_int(0) {}
int64_t as_int;
double as_double;
bool as_bool;
executorch::aten::ArrayRef<char>* as_string_ptr;
executorch::aten::ArrayRef<double>* as_double_list_ptr;
executorch::aten::ArrayRef<bool>* as_bool_list_ptr;
BoxedEvalueList<int64_t>* as_int_list_ptr;
BoxedEvalueList<executorch::aten::Tensor>* as_tensor_list_ptr;
BoxedEvalueList<std::optional<executorch::aten::Tensor>>*
as_list_optional_tensor_ptr;
} copyable_union;
executorch::aten::Tensor as_tensor;
Payload() {}
~Payload() {}
};
Payload payload;
Tag tag;
EValue(const EValue& rhs) : EValue(rhs.payload, rhs.tag) {}
EValue(EValue&& rhs) noexcept : tag(rhs.tag) {
moveFrom(std::move(rhs));
}
EValue& operator=(EValue&& rhs) & noexcept {
if (&rhs == this) {
return *this;
}
destroy();
moveFrom(std::move(rhs));
return *this;
}
EValue& operator=(EValue const& rhs) & {
*this = EValue(rhs);
return *this;
}
~EValue() {
destroy();
}
EValue() : tag(Tag::None) {
payload.copyable_union.as_int = 0;
}
bool isNone() const {
return tag == Tag::None;
}
EValue(int64_t i) : tag(Tag::Int) {
payload.copyable_union.as_int = i;
}
bool isInt() const {
return tag == Tag::Int;
}
int64_t toInt() const {
ET_CHECK_MSG(isInt(), "EValue is not an int.");
return payload.copyable_union.as_int;
}
Result<int64_t> tryToInt() const {
if (!isInt()) {
return Error::InvalidType;
}
return payload.copyable_union.as_int;
}
EValue(double d) : tag(Tag::Double) {
payload.copyable_union.as_double = d;
}
bool isDouble() const {
return tag == Tag::Double;
}
double toDouble() const {
ET_CHECK_MSG(isDouble(), "EValue is not a Double.");
return payload.copyable_union.as_double;
}
Result<double> tryToDouble() const {
if (!isDouble()) {
return Error::InvalidType;
}
return payload.copyable_union.as_double;
}
EValue(bool b) : tag(Tag::Bool) {
payload.copyable_union.as_bool = b;
}
bool isBool() const {
return tag == Tag::Bool;
}
bool toBool() const {
ET_CHECK_MSG(isBool(), "EValue is not a Bool.");
return payload.copyable_union.as_bool;
}
Result<bool> tryToBool() const {
if (!isBool()) {
return Error::InvalidType;
}
return payload.copyable_union.as_bool;
}
EValue(executorch::aten::Scalar s) {
if (s.isIntegral(false)) {
tag = Tag::Int;
payload.copyable_union.as_int = s.to<int64_t>();
} else if (s.isFloatingPoint()) {
tag = Tag::Double;
payload.copyable_union.as_double = s.to<double>();
} else if (s.isBoolean()) {
tag = Tag::Bool;
payload.copyable_union.as_bool = s.to<bool>();
} else {
ET_CHECK_MSG(false, "Scalar passed to EValue is not initialized.");
}
}
bool isScalar() const {
return tag == Tag::Int || tag == Tag::Double || tag == Tag::Bool;
}
executorch::aten::Scalar toScalar() const {
if (isDouble()) {
return toDouble();
} else if (isInt()) {
return toInt();
} else if (isBool()) {
return toBool();
} else {
ET_CHECK_MSG(false, "EValue is not a Scalar.");
}
}
Result<executorch::aten::Scalar> tryToScalar() const {
if (isDouble()) {
return executorch::aten::Scalar(payload.copyable_union.as_double);
}
if (isInt()) {
return executorch::aten::Scalar(payload.copyable_union.as_int);
}
if (isBool()) {
return executorch::aten::Scalar(payload.copyable_union.as_bool);
}
return Error::InvalidType;
}
EValue(executorch::aten::Tensor t) : tag(Tag::Tensor) {
new (&payload.as_tensor) executorch::aten::Tensor(t);
}
template <
typename T,
typename = typename std::enable_if<std::is_convertible<
decltype(*std::forward<T>(std::declval<T>())), EValue>::value>::type>
EValue(T&& value) {
ET_CHECK_MSG(value != nullptr, "Pointer is null.");
moveFrom(*std::forward<T>(value));
}
template <typename T>
explicit EValue(T* value) = delete;
bool isTensor() const {
return tag == Tag::Tensor;
}
executorch::aten::Tensor toTensor() && {
ET_CHECK_MSG(isTensor(), "EValue is not a Tensor.");
auto res = std::move(payload.as_tensor);
clearToNone();
return res;
}
executorch::aten::Tensor& toTensor() & {
ET_CHECK_MSG(isTensor(), "EValue is not a Tensor.");
return payload.as_tensor;
}
const executorch::aten::Tensor& toTensor() const& {
ET_CHECK_MSG(isTensor(), "EValue is not a Tensor.");
return payload.as_tensor;
}
Result<executorch::aten::Tensor> tryToTensor() const {
if (!isTensor()) {
return Error::InvalidType;
}
return payload.as_tensor;
}
EValue(executorch::aten::ArrayRef<char>* s) : tag(Tag::String) {
ET_CHECK_MSG(s != nullptr, "ArrayRef<char> pointer cannot be null");
payload.copyable_union.as_string_ptr = s;
}
bool isString() const {
return tag == Tag::String;
}
std::string_view toString() const {
ET_CHECK_MSG(isString(), "EValue is not a String.");
ET_CHECK_MSG(
payload.copyable_union.as_string_ptr != nullptr,
"EValue string pointer is null.");
return std::string_view(
payload.copyable_union.as_string_ptr->data(),
payload.copyable_union.as_string_ptr->size());
}
Result<std::string_view> tryToString() const {
if (!isString()) {
return Error::InvalidType;
}
if (payload.copyable_union.as_string_ptr == nullptr) {
return Error::InvalidState;
}
return std::string_view(
payload.copyable_union.as_string_ptr->data(),
payload.copyable_union.as_string_ptr->size());
}
EValue(BoxedEvalueList<int64_t>* i) : tag(Tag::ListInt) {
ET_CHECK_MSG(
i != nullptr, "BoxedEvalueList<int64_t> pointer cannot be null");
payload.copyable_union.as_int_list_ptr = i;
}
bool isIntList() const {
return tag == Tag::ListInt;
}
executorch::aten::ArrayRef<int64_t> toIntList() const {
ET_CHECK_MSG(isIntList(), "EValue is not an Int List.");
ET_CHECK_MSG(
payload.copyable_union.as_int_list_ptr != nullptr,
"EValue int list pointer is null.");
return (payload.copyable_union.as_int_list_ptr)->get();
}
Result<executorch::aten::ArrayRef<int64_t>> tryToIntList() const {
if (!isIntList()) {
return Error::InvalidType;
}
if (payload.copyable_union.as_int_list_ptr == nullptr) {
return Error::InvalidState;
}
return (payload.copyable_union.as_int_list_ptr)->tryGet();
}
EValue(executorch::aten::ArrayRef<bool>* b)
: tag(Tag::ListBool) {
ET_CHECK_MSG(b != nullptr, "ArrayRef<bool> pointer cannot be null");
payload.copyable_union.as_bool_list_ptr = b;
}
bool isBoolList() const {
return tag == Tag::ListBool;
}
executorch::aten::ArrayRef<bool> toBoolList() const {
ET_CHECK_MSG(isBoolList(), "EValue is not a Bool List.");
ET_CHECK_MSG(
payload.copyable_union.as_bool_list_ptr != nullptr,
"EValue bool list pointer is null.");
return *(payload.copyable_union.as_bool_list_ptr);
}
Result<executorch::aten::ArrayRef<bool>> tryToBoolList() const {
if (!isBoolList()) {
return Error::InvalidType;
}
if (payload.copyable_union.as_bool_list_ptr == nullptr) {
return Error::InvalidState;
}
return *(payload.copyable_union.as_bool_list_ptr);
}
EValue(executorch::aten::ArrayRef<double>* d)
: tag(Tag::ListDouble) {
ET_CHECK_MSG(d != nullptr, "ArrayRef<double> pointer cannot be null");
payload.copyable_union.as_double_list_ptr = d;
}
bool isDoubleList() const {
return tag == Tag::ListDouble;
}
executorch::aten::ArrayRef<double> toDoubleList() const {
ET_CHECK_MSG(isDoubleList(), "EValue is not a Double List.");
ET_CHECK_MSG(
payload.copyable_union.as_double_list_ptr != nullptr,
"EValue double list pointer is null.");
return *(payload.copyable_union.as_double_list_ptr);
}
Result<executorch::aten::ArrayRef<double>> tryToDoubleList() const {
if (!isDoubleList()) {
return Error::InvalidType;
}
if (payload.copyable_union.as_double_list_ptr == nullptr) {
return Error::InvalidState;
}
return *(payload.copyable_union.as_double_list_ptr);
}
EValue(BoxedEvalueList<executorch::aten::Tensor>* t)
: tag(Tag::ListTensor) {
ET_CHECK_MSG(
t != nullptr, "BoxedEvalueList<Tensor> pointer cannot be null");
payload.copyable_union.as_tensor_list_ptr = t;
}
bool isTensorList() const {
return tag == Tag::ListTensor;
}
executorch::aten::ArrayRef<executorch::aten::Tensor> toTensorList() const {
ET_CHECK_MSG(isTensorList(), "EValue is not a Tensor List.");
ET_CHECK_MSG(
payload.copyable_union.as_tensor_list_ptr != nullptr,
"EValue tensor list pointer is null.");
return payload.copyable_union.as_tensor_list_ptr->get();
}
Result<executorch::aten::ArrayRef<executorch::aten::Tensor>> tryToTensorList()
const {
if (!isTensorList()) {
return Error::InvalidType;
}
if (payload.copyable_union.as_tensor_list_ptr == nullptr) {
return Error::InvalidState;
}
return payload.copyable_union.as_tensor_list_ptr->tryGet();
}
EValue(
BoxedEvalueList<std::optional<executorch::aten::Tensor>>* t)
: tag(Tag::ListOptionalTensor) {
ET_CHECK_MSG(
t != nullptr,
"BoxedEvalueList<optional<Tensor>> pointer cannot be null");
payload.copyable_union.as_list_optional_tensor_ptr = t;
}
bool isListOptionalTensor() const {
return tag == Tag::ListOptionalTensor;
}
executorch::aten::ArrayRef<std::optional<executorch::aten::Tensor>>
toListOptionalTensor() const {
ET_CHECK_MSG(
isListOptionalTensor(), "EValue is not a List Optional Tensor.");
ET_CHECK_MSG(
payload.copyable_union.as_list_optional_tensor_ptr != nullptr,
"EValue list optional tensor pointer is null.");
return payload.copyable_union.as_list_optional_tensor_ptr->get();
}
Result<executorch::aten::ArrayRef<std::optional<executorch::aten::Tensor>>>
tryToListOptionalTensor() const {
if (!isListOptionalTensor()) {
return Error::InvalidType;
}
if (payload.copyable_union.as_list_optional_tensor_ptr == nullptr) {
return Error::InvalidState;
}
return payload.copyable_union.as_list_optional_tensor_ptr->tryGet();
}
executorch::aten::ScalarType toScalarType() const {
ET_CHECK_MSG(isInt(), "EValue is not a ScalarType.");
return static_cast<executorch::aten::ScalarType>(
payload.copyable_union.as_int);
}
Result<executorch::aten::ScalarType> tryToScalarType() const {
if (!isInt()) {
return Error::InvalidType;
}
return static_cast<executorch::aten::ScalarType>(
payload.copyable_union.as_int);
}
executorch::aten::MemoryFormat toMemoryFormat() const {
ET_CHECK_MSG(isInt(), "EValue is not a MemoryFormat.");
return static_cast<executorch::aten::MemoryFormat>(
payload.copyable_union.as_int);
}
Result<executorch::aten::MemoryFormat> tryToMemoryFormat() const {
if (!isInt()) {
return Error::InvalidType;
}
return static_cast<executorch::aten::MemoryFormat>(
payload.copyable_union.as_int);
}
executorch::aten::Layout toLayout() const {
ET_CHECK_MSG(isInt(), "EValue is not a Layout.");
return static_cast<executorch::aten::Layout>(payload.copyable_union.as_int);
}
Result<executorch::aten::Layout> tryToLayout() const {
if (!isInt()) {
return Error::InvalidType;
}
return static_cast<executorch::aten::Layout>(payload.copyable_union.as_int);
}
executorch::aten::Device toDevice() const {
ET_CHECK_MSG(isInt(), "EValue is not a Device.");
return executorch::aten::Device(
static_cast<executorch::aten::DeviceType>(
payload.copyable_union.as_int),
-1);
}
Result<executorch::aten::Device> tryToDevice() const {
if (!isInt()) {
return Error::InvalidType;
}
return executorch::aten::Device(
static_cast<executorch::aten::DeviceType>(
payload.copyable_union.as_int),
-1);
}
template <typename T>
T to() &&;
template <typename T>
typename internal::evalue_to_const_ref_overload_return<T>::type to() const&;
template <typename T>
typename internal::evalue_to_ref_overload_return<T>::type to() &;
template <typename T>
Result<T> tryTo() const;
template <typename T>
inline std::optional<T> toOptional() const {
if (this->isNone()) {
return executorch::aten::nullopt;
}
return this->to<T>();
}
template <typename T>
inline Result<std::optional<T>> tryToOptional() const {
if (this->isNone()) {
return std::optional<T>(std::nullopt);
}
auto r = this->tryTo<T>();
if (!r.ok()) {
return r.error();
}
return std::optional<T>(std::move(r.get()));
}
private:
void clearToNone() noexcept {
payload.copyable_union.as_int = 0;
tag = Tag::None;
}
void moveFrom(EValue&& rhs) noexcept {
if (rhs.isTensor()) {
new (&payload.as_tensor)
executorch::aten::Tensor(std::move(rhs.payload.as_tensor));
rhs.payload.as_tensor.~Tensor();
} else {
payload.copyable_union = rhs.payload.copyable_union;
}
tag = rhs.tag;
rhs.clearToNone();
}
void destroy() {
if (isTensor()) {
payload.as_tensor.~Tensor();
} else if (
isTensorList() &&
payload.copyable_union.as_tensor_list_ptr != nullptr) {
payload.copyable_union.as_tensor_list_ptr->destroy_elements();
} else if (
isListOptionalTensor() &&
payload.copyable_union.as_list_optional_tensor_ptr != nullptr) {
payload.copyable_union.as_list_optional_tensor_ptr->destroy_elements();
}
}
EValue(const Payload& p, Tag t) : tag(t) {
if (isTensor()) {
new (&payload.as_tensor) executorch::aten::Tensor(p.as_tensor);
} else {
payload.copyable_union = p.copyable_union;
}
}
};
#define EVALUE_DEFINE_TO(T, method_name) \
template <> \
inline T EValue::to<T>()&& { \
return static_cast<T>(std::move(*this).method_name()); \
} \
template <> \
inline ::executorch::runtime::internal::evalue_to_const_ref_overload_return< \
T>::type \
EValue::to<T>() const& { \
typedef ::executorch::runtime::internal:: \
evalue_to_const_ref_overload_return<T>::type return_type; \
return static_cast<return_type>(this->method_name()); \
} \
template <> \
inline ::executorch::runtime::internal::evalue_to_ref_overload_return< \
T>::type \
EValue::to<T>()& { \
typedef ::executorch::runtime::internal::evalue_to_ref_overload_return< \
T>::type return_type; \
return static_cast<return_type>(this->method_name()); \
}
EVALUE_DEFINE_TO(executorch::aten::Scalar, toScalar)
EVALUE_DEFINE_TO(int64_t, toInt)
EVALUE_DEFINE_TO(bool, toBool)
EVALUE_DEFINE_TO(double, toDouble)
EVALUE_DEFINE_TO(std::string_view, toString)
EVALUE_DEFINE_TO(executorch::aten::ScalarType, toScalarType)
EVALUE_DEFINE_TO(executorch::aten::MemoryFormat, toMemoryFormat)
EVALUE_DEFINE_TO(executorch::aten::Layout, toLayout)
EVALUE_DEFINE_TO(executorch::aten::Device, toDevice)
EVALUE_DEFINE_TO(
std::optional<executorch::aten::Tensor>,
toOptional<executorch::aten::Tensor>)
EVALUE_DEFINE_TO(executorch::aten::Tensor, toTensor)
EVALUE_DEFINE_TO(executorch::aten::ArrayRef<int64_t>, toIntList)
EVALUE_DEFINE_TO(
std::optional<executorch::aten::ArrayRef<int64_t>>,
toOptional<executorch::aten::ArrayRef<int64_t>>)
EVALUE_DEFINE_TO(executorch::aten::ArrayRef<double>, toDoubleList)
EVALUE_DEFINE_TO(
std::optional<executorch::aten::ArrayRef<double>>,
toOptional<executorch::aten::ArrayRef<double>>)
EVALUE_DEFINE_TO(executorch::aten::ArrayRef<bool>, toBoolList)
EVALUE_DEFINE_TO(
std::optional<executorch::aten::ArrayRef<bool>>,
toOptional<executorch::aten::ArrayRef<bool>>)
EVALUE_DEFINE_TO(
executorch::aten::ArrayRef<executorch::aten::Tensor>,
toTensorList)
EVALUE_DEFINE_TO(
std::optional<executorch::aten::ArrayRef<executorch::aten::Tensor>>,
toOptional<executorch::aten::ArrayRef<executorch::aten::Tensor>>)
EVALUE_DEFINE_TO(
executorch::aten::ArrayRef<std::optional<executorch::aten::Tensor>>,
toListOptionalTensor)
#undef EVALUE_DEFINE_TO
#define EVALUE_DEFINE_TRY_TO(T, method_name) \
template <> \
inline Result<T> EValue::tryTo<T>() const { \
return this->method_name(); \
}
EVALUE_DEFINE_TRY_TO(executorch::aten::Scalar, tryToScalar)
EVALUE_DEFINE_TRY_TO(int64_t, tryToInt)
EVALUE_DEFINE_TRY_TO(bool, tryToBool)
EVALUE_DEFINE_TRY_TO(double, tryToDouble)
EVALUE_DEFINE_TRY_TO(std::string_view, tryToString)
EVALUE_DEFINE_TRY_TO(executorch::aten::ScalarType, tryToScalarType)
EVALUE_DEFINE_TRY_TO(executorch::aten::MemoryFormat, tryToMemoryFormat)
EVALUE_DEFINE_TRY_TO(executorch::aten::Layout, tryToLayout)
EVALUE_DEFINE_TRY_TO(executorch::aten::Device, tryToDevice)
EVALUE_DEFINE_TRY_TO(executorch::aten::Tensor, tryToTensor)
EVALUE_DEFINE_TRY_TO(
std::optional<executorch::aten::Tensor>,
tryToOptional<executorch::aten::Tensor>)
EVALUE_DEFINE_TRY_TO(executorch::aten::ArrayRef<int64_t>, tryToIntList)
EVALUE_DEFINE_TRY_TO(
std::optional<executorch::aten::ArrayRef<int64_t>>,
tryToOptional<executorch::aten::ArrayRef<int64_t>>)
EVALUE_DEFINE_TRY_TO(executorch::aten::ArrayRef<double>, tryToDoubleList)
EVALUE_DEFINE_TRY_TO(
std::optional<executorch::aten::ArrayRef<double>>,
tryToOptional<executorch::aten::ArrayRef<double>>)
EVALUE_DEFINE_TRY_TO(executorch::aten::ArrayRef<bool>, tryToBoolList)
EVALUE_DEFINE_TRY_TO(
std::optional<executorch::aten::ArrayRef<bool>>,
tryToOptional<executorch::aten::ArrayRef<bool>>)
EVALUE_DEFINE_TRY_TO(
executorch::aten::ArrayRef<executorch::aten::Tensor>,
tryToTensorList)
EVALUE_DEFINE_TRY_TO(
std::optional<executorch::aten::ArrayRef<executorch::aten::Tensor>>,
tryToOptional<executorch::aten::ArrayRef<executorch::aten::Tensor>>)
EVALUE_DEFINE_TRY_TO(
executorch::aten::ArrayRef<std::optional<executorch::aten::Tensor>>,
tryToListOptionalTensor)
#undef EVALUE_DEFINE_TRY_TO
template <typename T>
executorch::aten::ArrayRef<T> BoxedEvalueList<T>::get() const {
for (typename executorch::aten::ArrayRef<T>::size_type i = 0;
i < wrapped_vals_.size();
i++) {
ET_CHECK(wrapped_vals_[i] != nullptr);
unwrapped_vals_[i] = wrapped_vals_[i]->template to<T>();
}
return executorch::aten::ArrayRef<T>{unwrapped_vals_, wrapped_vals_.size()};
}
template <typename T>
Result<executorch::aten::ArrayRef<T>> BoxedEvalueList<T>::tryGet() const {
for (typename executorch::aten::ArrayRef<T>::size_type i = 0;
i < wrapped_vals_.size();
i++) {
if (wrapped_vals_[i] == nullptr) {
return Error::InvalidState;
}
auto r = wrapped_vals_[i]->template tryTo<T>();
if (!r.ok()) {
return r.error();
}
unwrapped_vals_[i] = std::move(r.get());
}
return executorch::aten::ArrayRef<T>{unwrapped_vals_, wrapped_vals_.size()};
}
} }
namespace torch {
namespace executor {
using ::executorch::runtime::BoxedEvalueList;
using ::executorch::runtime::EValue;
} }