#pragma once
#include <ATen/core/TensorBody.h>
#include <ATen/core/blob.h>
#include <c10/util/C++17.h>
#include <c10/util/intrusive_ptr.h>
#include <torch/csrc/WindowsTorchApiMacro.h>
#include <typeindex>
namespace torch {
class TORCH_API CustomClassHolder : public c10::intrusive_ptr_target {};
namespace jit {
using ::torch::CustomClassHolder;
struct Function;
struct CompilationUnit;
struct Module;
} // namespace jit
} // namespace torch
namespace c10 {
template <class Key, class Value>
class Dict;
template <class T>
class List;
struct IValue;
struct ClassType;
struct Type;
class RRefInterface;
using TypePtr = std::shared_ptr<Type>;
struct ClassType;
using ClassTypePtr = std::shared_ptr<ClassType>;
TORCH_API bool _fastEqualsForContainer(const IValue& lhs, const IValue& rhs);
TORCH_API torch::jit::Function* checkObjectSortSchema(
const c10::ClassTypePtr& t,
std::stringstream& why_not);
// A comparator that checks ordering of two IValues of same type.
typedef std::function<bool(const IValue& a, const IValue& b)> IValueComparator;
TORCH_API IValueComparator getLessThanComparator(const IValue& v);
TORCH_API IValueComparator getGreaterThanComparator(const IValue& v);
namespace ivalue {
struct Tuple;
struct Future;
struct ConstantString;
struct GenericDict;
struct Object;
struct PyObjectHolder;
struct EnumHolder;
// We need a ComplexHolder because currently the payloads in the Union
// only take 64 bits. Since ComplexDouble takes up 128 bits, and is too big
// to fit in the IValue directly, we indirect complex numbers through an intrusive
// pointer to ComplexHolder (which contains a c10::complex).
struct ComplexHolder : c10::intrusive_ptr_target {
public:
template <typename T>
ComplexHolder(c10::complex<T> c) {
val = convert<decltype(val), c10::complex<T>>(c);
}
ComplexHolder() {}
c10::complex<double> val;
};
} // namespace ivalue
// This is an owning wrapper for a c10::optional<std::vector<T>>
// that can be implicitly converted to a (non-owning) optional<ArrayRef<T>>.
// Its purpose is to be used in generated code to keep the vector alive
// either until the end of a statement (as a temporary), or as a saved arg
// in autograd.
template <typename T>
struct OptionalArray {
c10::optional<std::vector<T>> list;
OptionalArray(){}
OptionalArray(std::vector<T> val) : list(std::move(val)) {}
// Used when saving an argument for the backwards pass.
OptionalArray& operator=(c10::optional<ArrayRef<T>> ref) {
if (ref) {
list = std::vector<T>(ref->begin(), ref->end());
} else {
list = nullopt;
}
return *this;
}
operator c10::optional<c10::ArrayRef<T>>() {
if (!list) {
return nullopt;
}
return *list;
}
};
// Capsule is an internal implementation detail of custom C++ classes. We
// define it as an owning wrapper for
// c10::intrusive_ptr<torch::CustomClassHolder> This wrapper is here to serve as
// an abstraction of the type erased custom class object pointer. It also allow
// pybind11 to treat this as a standalone class to register as a separate type
// caster, instead of a custom pointer holder which the pointer holder type
// caster try to "unwrap" it automatically.
struct Capsule {
c10::intrusive_ptr<torch::CustomClassHolder> obj_ptr;
explicit Capsule(c10::intrusive_ptr<torch::CustomClassHolder> ptr)
: obj_ptr(std::move(ptr)) {}
};
// IValue is the generic tagged union used by the interpreter to hold
// all value types.
// It is a 16-byte object with an 8-byte payload and an 8-byte tag.
// The tag is currently 4 bytes to determine the type, and 1 byte
// to mark whether that type is a subtype of c10::intrusive_ptr_target and needs
// retain/release calls.
#define TORCH_FORALL_TAGS(_) \
_(None) \
_(Tensor) \
_(Storage) \
_(Double) \
_(ComplexDouble) \
_(Int) \
_(Bool) \
_(Tuple) \
_(String) \
_(Blob) \
_(GenericList) \
_(GenericDict) \
_(Future) \
_(Device) \
_(Stream) \
_(Object) \
_(PyObject) \
_(Uninitialized) \
_(Capsule) \
_(RRef) \
_(Quantizer) \
_(Generator) \
_(Enum)
// [doxygen private]
// These methods are not actually private but we don't want to document them, so
// they are marked `@private`, which hides them on the doxygen documentation for
// this page.
/// IValue (Interpreter Value) is a tagged union over the types
/// supported by the TorchScript interpreter. IValues contain their
/// values as an `IValue::Payload`, which holds primitive types
/// (`int64_t`, `bool`, `double`, `Device`) and `Tensor` as values,
/// and all other types as a `c10::intrusive_ptr`. In order to
/// optimize performance of the destructor and related operations by
/// making the `Tensor` and `c10::intrusive_ptr` paths generate the
/// same code, we represent a null `c10::intrusive_ptr` as
/// `UndefinedTensorImpl::singleton()`, *not* `nullptr`.
///
/// IValues are used as inputs to and outputs from the TorchScript interpreter.
/// To retrieve the value contained within an IValue, use the `.toX()` methods,
/// where `X` is the type you are trying to get. Note that neither the `.toX()`
/// methods nor the templated `.to<T>` functions do any kind of casting, they
/// only unwrap the contained value. For example:
///
/// \rst
/// .. code-block:: cpp
///
/// // Make the IValue
/// torch::IValue my_ivalue(26);
/// std::cout << my_ivalue << "\n";
///
/// // Unwrap the IValue
/// int64_t my_int = my_ivalue.toInt()
/// std::cout << my_int << "\n";
///
/// // This will throw an error!
/// // `my_ivalue` is tagged as an int and cannot be used as another type
/// torch::Tensor my_tensor = my_ivalue.toTensor()
/// \endrst
struct TORCH_API IValue final {
IValue(const IValue& rhs)
: IValue(rhs.payload, rhs.tag, rhs.is_intrusive_ptr) {
if (is_intrusive_ptr && payload.u.as_intrusive_ptr != c10::UndefinedTensorImpl::singleton()) {
c10::raw::intrusive_ptr::incref(payload.u.as_intrusive_ptr);
}
}
IValue(IValue&& rhs) noexcept : tag(rhs.tag), is_intrusive_ptr(rhs.is_intrusive_ptr) {
moveFrom(std::move(rhs));
}
/// @private [doxygen private]
~IValue() {
destroy();
}
C10_ALWAYS_INLINE IValue& operator=(IValue&& rhs) & noexcept {
if (&rhs == this) {
return *this;
}
destroy();
moveFrom(std::move(rhs));
return *this;
}
IValue& operator=(IValue const& rhs) & {
IValue(rhs).swap(*this);
return *this;
}
void dump() const;
/**
* Equality comparison. The semantics are the same as Python's `==`:
* 1. Numerical types are compared by value.
* 2. Tensors compute element-wise equality, returning a BoolTensor (see:
* `torch.eq()`)
* 3. Strings are compared by value.
* 4. Sequence types (list, tuple) are compared lexicographically by
* comparing their elements. Different sequence types never compare equal.
* 5. Mappings (dict) must have equal (key, value) pairs.
* 6. If not listed above, the default behavior for is to test identity
* equality (e.g. pointer equality).
*
* Why does this return an IValue instead of a bool? Because in PyTorch,
* `tensor1 == tensor2` returns a `BoolTensor`, not a bool.
*
* NOTE: we (like Python) assume that identity equality implies value equality
* for efficiency.
* TODO: need to support customizing equality
*/
IValue equals(const IValue& rhs) const;
/**
* This implements the same semantics as `bool(lhs == rhs)` in Python. which
* is the same as `equals()` except for Tensor types.
*/
TORCH_API friend bool operator==(const IValue& lhs, const IValue& rhs);
TORCH_API friend bool operator!=(const IValue& lhs, const IValue& rhs);
/**
* Identity comparison. Checks if `this` is the same object as `rhs`. The
* semantics are the same as Python's `is` operator.
*
* NOTE: Like in Python, this operation is poorly defined for primitive types
* like numbers and strings. Prefer to use `==` unless you really want to
* check identity equality.
*/
bool is(const IValue& rhs) const;
/**
* Hashing for IValues. Returns an IValue-boxed int.
*
* Some notes:
* - Like eager, Tensors are hashed by looking at the pointer. This is not
* strictly correct because two value-equal tensors with different tensor
* pointers will hash differently, but we choose to reproduce the eager
* semantics.
* - Hashing is not defined on all built-in IValue types (e.g. list and
* dict), following Python. Calling `hash()` on these types will throw.
*/
IValue hash() const {
return (int64_t)IValue::hash(*this);
}
// This is defined because `c10::hash` dispatches to a function of this
// signature. See the member function `hash()`.
static size_t hash(const IValue& iv);
/**
* @private [doxygen private]
* [container equality]
* This is an equality implementation that assumes objects with the same
* identity equal themselves, for efficiency reasons. We primarily have this
* for consistency, because Python does the same thing. This actually
* provokes user-visible changes in behavior due to quirks in torch:
* [tensor1] == [tensor1] -> True (because container equality will first
* compare identity) [tensor1] == [tensor1_copy] -> RuntimeError: bool value
* of Tensor is ambiguous
*/
TORCH_API friend bool _fastEqualsForContainer(
const IValue& lhs,
const IValue& rhs);
/// @private [doxygen private]
bool isAliasOf(const IValue& rhs) const {
if (this->tag != rhs.tag) {
// Trivially don't alias if the type is different
return false;
}
// Tensors should be compared based on internal storage
if (this->isTensor()) {
const auto& thisTensor = this->toTensor();
const auto& rhsTensor = rhs.toTensor();
return thisTensor.is_alias_of(rhsTensor);
}
if (!this->is_intrusive_ptr) {
// Primitive types don't alias anything
return false;
}
AT_ASSERT(rhs.is_intrusive_ptr);
// Other types can be compared by their ptr value
return this->payload.u.as_intrusive_ptr == rhs.payload.u.as_intrusive_ptr;
}
/// @private [doxygen private]
size_t use_count() const noexcept {
if (isTensor()) {
return payload.as_tensor.use_count();
}
if (!is_intrusive_ptr) {
return 1;
}
if (payload.u.as_intrusive_ptr == c10::UndefinedTensorImpl::singleton()) {
return 0;
}
return c10::raw::intrusive_ptr::use_count(payload.u.as_intrusive_ptr);
}
/// @private [doxygen private]
void swap(IValue& rhs) noexcept {
if (isTensor() && rhs.isTensor()) {
std::swap(payload.as_tensor, rhs.payload.as_tensor);
} else if (isTensor()) {
at::Tensor t = std::move(payload.as_tensor);
// As far as I can tell, omitting the usual explicit destructor call
// is not UB in and of itself, and it's a slight perf win. The
// destructor is a no-op, because the moved-from Tensor is
// effectively an intrusive_ptr in the null state, so we don't need
// the behavior for correctness reasons either. Leaving this
// explanatory comment, including commented-out destructor call, to
// make this abundantly clear.
//
// payload.as_tensor.~Tensor();
payload.u = rhs.payload.u;
new (&rhs.payload.as_tensor) at::Tensor(std::move(t));
} else if (rhs.isTensor()) {
rhs.swap(*this);
return;
} else {
std::swap(payload.u, rhs.payload.u);
Loading ...