Program Listing for File expr.h#
↰ Return to documentation for file (tvm/ir/expr.h)
/*
* 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.
*/
#ifndef TVM_IR_EXPR_H_
#define TVM_IR_EXPR_H_
#include <tvm/ffi/big_int.h>
#include <tvm/ffi/cast.h>
#include <tvm/ffi/container/array.h>
#include <tvm/ffi/container/tuple.h>
#include <tvm/ffi/container/variant.h>
#include <tvm/ffi/dtype.h>
#include <tvm/ffi/error.h>
#include <tvm/ffi/extra/dataclass.h>
#include <tvm/ffi/extra/structural_equal.h>
#include <tvm/ffi/extra/structural_hash.h>
#include <tvm/ffi/extra/structural_mutate.h>
#include <tvm/ffi/extra/structural_visit.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/optional.h>
#include <tvm/ffi/reflection/native_function.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ffi/string.h>
#include <tvm/ir/attrs.h>
#include <tvm/ir/cow.h>
#include <tvm/ir/location.h>
#include <tvm/ir/type.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <limits>
#include <optional>
#include <ostream>
#include <string>
#include <type_traits>
#include <utility>
namespace tvm {
class ExprObj : public ffi::Object {
public:
mutable Location loc = UnknownLoc();
mutable Type ty = MissingType();
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
// loc does not participate in structural equal and hash.
refl::ObjectDef<ExprObj>()
.def_ro("loc", &ExprObj::loc, refl::DefaultValue(UnknownLoc()),
refl::AttachFieldFlag::SEqHashIgnore())
.def_ro("ty", &ExprObj::ty, refl::DefaultValue(MissingType()));
}
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
static constexpr const uint32_t _type_child_slots = 64;
TVM_FFI_DECLARE_OBJECT_INFO("ir.Expr", ExprObj, ffi::Object);
};
class Expr : public ffi::ObjectRef {
public:
// Expressions do not implicitly compare by object identity or address. Callers must name
// whether they intend object identity, structural equality, or primitive symbolic comparison.
bool operator==(const Expr& other) const = delete;
bool operator!=(const Expr& other) const = delete;
bool operator<(const Expr& other) const = delete;
explicit Expr(ffi::ObjectPtr<ExprObj> node) : ffi::ObjectRef(std::move(node)) {
TVM_FFI_CHECK(defined(), ValueError) << "Expr expects a defined node";
}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Expr, ffi::ObjectRef, ExprObj);
};
class StagingExprObj : public ExprObj {
public:
static void RegisterReflection() { ffi::reflection::ObjectDef<StagingExprObj>(); }
static constexpr const uint32_t _type_child_slots = 4;
TVM_FFI_DECLARE_OBJECT_INFO("ir.StagingExpr", StagingExprObj, ExprObj);
};
class StagingExpr : public Expr {
public:
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(StagingExpr, Expr, StagingExprObj);
};
class OpaqueExprObj : public ExprObj {
public:
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<OpaqueExprObj>();
}
static constexpr const uint32_t _type_child_slots = 2;
TVM_FFI_DECLARE_OBJECT_INFO("ir.OpaqueExpr", OpaqueExprObj, ExprObj);
};
class OpaqueExpr : public Expr {
public:
explicit OpaqueExpr(ffi::ObjectPtr<OpaqueExprObj> node) : Expr(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(OpaqueExpr, Expr, OpaqueExprObj);
};
template <typename ExpectedType>
class TypedExpr : public Expr {
public:
ExpectedType ty() const {
const auto* node = get();
TVM_FFI_DCHECK(node != nullptr);
const auto* ty_node = node->ExprObj::ty.template as<typename ExpectedType::ContainerType>();
TVM_FFI_DCHECK(ty_node != nullptr);
return ffi::GetRef<ExpectedType>(ty_node);
}
explicit TypedExpr(ffi::ObjectPtr<ExprObj> node) : Expr(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(TypedExpr, Expr, ExprObj);
static constexpr bool _type_container_is_exact = false;
};
class PrimExpr : public TypedExpr<PrimType> {
public:
using TypedExpr<PrimType>::ty;
TVM_TIRX_DLL PrimExpr(int32_t value); // NOLINT(*)
TVM_TIRX_DLL PrimExpr(float value); // NOLINT(*)
explicit PrimExpr(ffi::ObjectPtr<ExprObj> node) : TypedExpr<PrimType>(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(PrimExpr, TypedExpr<PrimType>, ExprObj);
static constexpr bool _type_container_is_exact = false;
};
class IntExpr : public PrimExpr {
public:
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(IntExpr, PrimExpr, ExprObj);
static constexpr bool _type_container_is_exact = false;
};
namespace ffi::detail {
// Array members require storage availability before the expression traits below are complete.
template <>
inline constexpr bool storage_enabled_v<Expr> = true;
template <>
inline constexpr bool storage_enabled_v<PrimExpr> = true;
} // namespace ffi::detail
class ConstantObj : public ExprObj {
public:
static constexpr uint32_t _type_child_slots = 5;
static void RegisterReflection() { ffi::reflection::ObjectDef<ConstantObj>(); }
TVM_FFI_DECLARE_OBJECT_INFO("ir.Constant", ConstantObj, ExprObj);
};
class Constant : public Expr {
public:
explicit Constant(ffi::ObjectPtr<ConstantObj> node) : Expr(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Constant, Expr, ConstantObj);
};
class GenericConstObj : public ConstantObj {
public:
ffi::Any value;
static void RegisterReflection() {
ffi::reflection::ObjectDef<GenericConstObj>().def_ro("value", &GenericConstObj::value);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.GenericConst", GenericConstObj, ConstantObj);
};
class GenericConst : public Constant {
public:
TVM_TIRX_DLL GenericConst(Type ty, ffi::Any value, Location loc = UnknownLoc());
explicit GenericConst(ffi::ObjectPtr<GenericConstObj> node) : Constant(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(GenericConst, Constant, GenericConstObj);
TVM_DEFINE_OBJECT_REF_COW_METHOD(GenericConstObj);
};
class StringImmObj : public ConstantObj {
public:
ffi::String value;
static void RegisterReflection() {
ffi::reflection::ObjectDef<StringImmObj>().def_ro("value", &StringImmObj::value);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.StringImm", StringImmObj, ConstantObj);
};
class StringImm : public Constant {
public:
TVM_TIRX_DLL explicit StringImm(ffi::String value, Location loc = UnknownLoc());
explicit StringImm(ffi::ObjectPtr<StringImmObj> node) : Constant(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(StringImm, Constant, StringImmObj);
TVM_DEFINE_OBJECT_REF_COW_METHOD(StringImmObj);
};
class DataTypeImmObj : public ConstantObj {
public:
DLDataType value;
static void RegisterReflection() {
ffi::reflection::ObjectDef<DataTypeImmObj>().def_ro("value", &DataTypeImmObj::value);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.DataTypeImm", DataTypeImmObj, ConstantObj);
};
class DataTypeImm : public Constant {
public:
TVM_TIRX_DLL explicit DataTypeImm(DLDataType value, Location loc = UnknownLoc());
explicit DataTypeImm(ffi::ObjectPtr<DataTypeImmObj> node) : Constant(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(DataTypeImm, Constant, DataTypeImmObj);
TVM_DEFINE_OBJECT_REF_COW_METHOD(DataTypeImmObj);
};
class IntImmObj : public ConstantObj {
public:
ffi::BigInt value;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<IntImmObj>().def_ro("value", &IntImmObj::value);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.IntImm", IntImmObj, ConstantObj);
};
class IntImm : public PrimExpr {
public:
TVM_TIRX_DLL IntImm(PrimType value_ty, ffi::BigInt value, Location loc = UnknownLoc());
template <typename Enum, std::enable_if_t<std::is_enum_v<Enum>, int> = 0>
IntImm(PrimType value_ty, Enum value, Location loc = UnknownLoc())
: IntImm(std::move(value_ty), ffi::BigInt(static_cast<std::underlying_type_t<Enum>>(value)),
std::move(loc)) {}
static IntImm Bool(bool value, Location loc = UnknownLoc()) {
return IntImm(PrimType::Bool(), value, loc);
}
static IntImm Int32(ffi::BigInt value, Location loc = UnknownLoc()) {
return IntImm(PrimType::Int(32), std::move(value), loc);
}
template <typename Enum, std::enable_if_t<std::is_enum_v<Enum>, int> = 0>
static IntImm Int32(Enum value, Location loc = UnknownLoc()) {
return IntImm(PrimType::Int(32), value, std::move(loc));
}
static IntImm Int64(ffi::BigInt value, Location loc = UnknownLoc()) {
return IntImm(PrimType::Int(64), std::move(value), loc);
}
template <typename Enum, std::enable_if_t<std::is_enum_v<Enum>, int> = 0>
static IntImm Int64(Enum value, Location loc = UnknownLoc()) {
return IntImm(PrimType::Int(64), value, std::move(loc));
}
explicit IntImm(ffi::ObjectPtr<IntImmObj> node) : PrimExpr(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(IntImm, PrimExpr, IntImmObj);
static constexpr bool _type_container_is_exact = true;
TVM_DEFINE_OBJECT_REF_COW_METHOD(IntImmObj);
};
class FloatImmObj : public ConstantObj {
public:
double value;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<FloatImmObj>().def_ro("value", &FloatImmObj::value);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.FloatImm", FloatImmObj, ConstantObj);
};
class FloatImm : public PrimExpr {
public:
TVM_TIRX_DLL FloatImm(PrimType value_ty, double value, Location loc = UnknownLoc());
explicit FloatImm(ffi::ObjectPtr<FloatImmObj> node) : PrimExpr(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(FloatImm, PrimExpr, FloatImmObj);
static constexpr bool _type_container_is_exact = true;
TVM_DEFINE_OBJECT_REF_COW_METHOD(FloatImmObj);
};
class TupleObj : public ExprObj {
public:
ffi::Array<Expr> fields;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<TupleObj>().def_ro("fields", &TupleObj::fields);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.Tuple", TupleObj, ExprObj);
};
class Tuple : public Expr {
public:
TVM_TIRX_DLL explicit Tuple(ffi::Array<Expr> fields, Location loc = UnknownLoc());
explicit Tuple(ffi::ObjectPtr<TupleObj> node) : Expr(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Tuple, Expr, TupleObj);
TVM_DEFINE_OBJECT_REF_COW_METHOD(TupleObj);
};
class TupleGetItemObj : public ExprObj {
public:
explicit TupleGetItemObj(Expr tuple) : tuple(std::move(tuple)) {}
explicit TupleGetItemObj(ffi::UnsafeInit tag) : tuple(tag) {}
Expr tuple;
int index;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<TupleGetItemObj>()
.def_ro("tuple_value", &TupleGetItemObj::tuple)
.def_ro("index", &TupleGetItemObj::index);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.TupleGetItem", TupleGetItemObj, ExprObj);
};
class TupleGetItem : public Expr {
public:
TVM_TIRX_DLL TupleGetItem(Expr tuple, int index, Location loc = UnknownLoc());
explicit TupleGetItem(ffi::ObjectPtr<TupleGetItemObj> node) : Expr(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(TupleGetItem, Expr, TupleGetItemObj);
TVM_DEFINE_OBJECT_REF_COW_METHOD(TupleGetItemObj);
};
class TensorLoadObj : public ExprObj {
public:
explicit TensorLoadObj(Expr source) : source(std::move(source)) {}
explicit TensorLoadObj(ffi::UnsafeInit tag) : source(tag) {}
Expr source;
ffi::Array<PrimExpr> indices;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<TensorLoadObj>()
.def_ro("source", &TensorLoadObj::source)
.def_ro("indices", &TensorLoadObj::indices);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.TensorLoad", TensorLoadObj, ExprObj);
};
namespace type_attr {
inline constexpr ffi::reflection::AttrKey<
ffi::reflection::NativeFunctionView<PrimType(const TensorLoadObj*)>>
kTensorLoadInferType{"__tvm_tensor_load_infer_type__"};
} // namespace type_attr
class TensorLoad : public PrimExpr {
public:
TVM_TIRX_DLL TensorLoad(Expr source, ffi::Array<PrimExpr> indices, Location loc = UnknownLoc());
TVM_TIRX_DLL static TensorLoad Unchecked(PrimType ty, Expr source, ffi::Array<PrimExpr> indices,
Location loc = UnknownLoc());
TVM_TIRX_DLL explicit TensorLoad(ffi::ObjectPtr<TensorLoadObj> node);
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(TensorLoad, PrimExpr, TensorLoadObj);
static constexpr bool _type_container_is_exact = true;
TVM_DEFINE_OBJECT_REF_COW_METHOD(TensorLoadObj);
};
class VarObj : public ExprObj {
public:
ffi::String name;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<VarObj>().def_ro("name", &VarObj::name, refl::AttachFieldFlag::SEqHashIgnore());
}
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindFreeVar;
static constexpr const uint32_t _type_child_slots = 1;
// VarObj reserves its sole child slot for the final relax::DataflowVarNode subtype, so its
// descendant type-index range cannot overflow.
static constexpr bool _type_child_slots_can_overflow = false;
TVM_FFI_DECLARE_OBJECT_INFO("ir.Var", VarObj, ExprObj);
};
class Var : public Expr {
public:
TVM_TIRX_DLL explicit Var(ffi::String name, Type ty, Location loc = UnknownLoc());
TVM_TIRX_DLL Var CopyWithName(const ffi::String& name) const;
TVM_TIRX_DLL Var CopyWithSuffix(const ffi::String& suffix) const;
TVM_TIRX_DLL Var CopyWithDType(PrimType dtype) const;
TVM_TIRX_DLL Var CopyWithType(Type type) const;
explicit Var(ffi::ObjectPtr<VarObj> node) : Expr(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Var, Expr, VarObj);
};
class PrimVar : public PrimExpr {
public:
explicit PrimVar(ffi::String name, PrimType dtype = PrimType::Int(32),
Location loc = UnknownLoc())
: PrimExpr(Var(std::move(name), std::move(dtype), std::move(loc)).as_or_throw<PrimExpr>()) {}
explicit PrimVar(ffi::String name, Type type_annotation, Location loc = UnknownLoc())
: PrimExpr(Var(std::move(name), std::move(type_annotation), std::move(loc))
.as_or_throw<PrimExpr>()) {}
operator Var() const { return this->as_or_throw<Var>(); }
PrimVar CopyWithSuffix(const ffi::String& suffix) const {
return this->as_or_throw<Var>().CopyWithSuffix(suffix).as_or_throw<PrimVar>();
}
PrimVar CopyWithDType(PrimType dtype) const {
return this->as_or_throw<Var>().CopyWithDType(dtype).as_or_throw<PrimVar>();
}
explicit PrimVar(ffi::ObjectPtr<VarObj> node) : PrimExpr(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(PrimVar, PrimExpr, VarObj);
static constexpr bool _type_container_is_exact = false;
};
class LambdaExprObj : public StagingExprObj {
public:
ffi::Array<Var> vars;
Expr body;
explicit LambdaExprObj(ffi::UnsafeInit tag) : body(tag) {}
explicit LambdaExprObj(Expr body) : body(std::move(body)) {}
TVM_TIRX_DLL Expr Apply(const ffi::Array<Expr>& arguments) const;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<LambdaExprObj>()
.def_ro("vars", &LambdaExprObj::vars, refl::AttachFieldFlag::SEqHashDefSimple())
.def_ro("body", &LambdaExprObj::body);
}
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.LambdaExpr", LambdaExprObj, StagingExprObj);
};
class LambdaExpr : public StagingExpr {
public:
TVM_TIRX_DLL explicit LambdaExpr(ffi::Array<Var> vars, Expr body, Location loc = UnknownLoc());
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(LambdaExpr, StagingExpr, LambdaExprObj);
};
class GlobalVar;
class GlobalVarObj : public ExprObj {
public:
ffi::String name_hint;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<GlobalVarObj>().def_ro("name_hint", &GlobalVarObj::name_hint);
// A GlobalVar identifies a module-level symbol. Its type is derived from the
// corresponding function definition and is not part of the symbol identity.
refl::TypeAttrDef<GlobalVarObj>()
.def(tvm::ffi::type_attr::kSEqual.name, &GlobalVarObj::SEqual)
.def(tvm::ffi::type_attr::kSHash.name, &GlobalVarObj::SHash);
}
bool SEqual(const GlobalVarObj* other,
ffi::TypedFunction<bool(AnyView, AnyView, bool, AnyView)> equal) const {
return equal(name_hint, other->name_hint, false, "name_hint");
}
int64_t SHash(int64_t init_hash, ffi::TypedFunction<int64_t(AnyView, int64_t, bool)> hash) const {
return hash(name_hint, init_hash, false);
}
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindFreeVar;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.GlobalVar", GlobalVarObj, ExprObj);
};
class GlobalVar : public Expr {
public:
TVM_TIRX_DLL explicit GlobalVar(ffi::String name_hint, Location loc = UnknownLoc());
explicit GlobalVar(ffi::ObjectPtr<GlobalVarObj> node) : Expr(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(GlobalVar, Expr, GlobalVarObj);
TVM_DEFINE_OBJECT_REF_COW_METHOD(GlobalVarObj);
};
enum class CallEffectKind : int {
kExprAnnotation = 0,
kPure = 1,
kReadState = 2,
kUpdateState = 3,
kOpaque = kUpdateState,
kSpecialCallArg = 4,
kEmbedInfo = 5,
kControlJump = 6,
};
inline std::ostream& operator<<(std::ostream& os, CallEffectKind side_effect) {
switch (side_effect) {
case CallEffectKind::kExprAnnotation:
return os << "kExprAnnotation";
case CallEffectKind::kPure:
return os << "kPure";
case CallEffectKind::kReadState:
return os << "kReadState";
case CallEffectKind::kUpdateState:
return os << "kUpdateState";
case CallEffectKind::kSpecialCallArg:
return os << "kSpecialCallArg";
case CallEffectKind::kEmbedInfo:
return os << "kEmbedInfo";
case CallEffectKind::kControlJump:
return os << "kControlJump";
default:
TVM_FFI_THROW(InternalError) << "Unknown CallEffectKind: " << static_cast<int>(side_effect);
}
}
class CallObj : public ExprObj {
public:
explicit CallObj(Expr op) : op(std::move(op)) {}
explicit CallObj(ffi::UnsafeInit tag) : op(tag) {}
Expr op;
ffi::Array<Expr> args;
ffi::Optional<Attrs> attrs;
ffi::Array<Type> ty_args;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<CallObj>()
.def_ro("op", &CallObj::op)
.def_ro("args", &CallObj::args)
.def_ro("attrs", &CallObj::attrs)
.def_ro("ty_args", &CallObj::ty_args);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.Call", CallObj, ExprObj);
};
class Call : public Expr {
public:
TVM_TIRX_DLL Call(ffi::Optional<Type> ret_ty, Expr op, ffi::Array<Expr> args,
ffi::Optional<Attrs> attrs = std::nullopt,
ffi::Array<Type> ty_args = ffi::Array<Type>(), Location loc = UnknownLoc());
TVM_TIRX_DLL void Validate() const;
TVM_TIRX_DLL static Type ReinferType(const CallObj* call);
explicit Call(ffi::ObjectPtr<CallObj> node) : Expr(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Call, Expr, CallObj);
TVM_DEFINE_OBJECT_REF_COW_METHOD(CallObj);
};
namespace op_attr {
inline constexpr ffi::reflection::AttrKey<CallEffectKind> kCallEffectKind{"TCallEffectKind"};
} // namespace op_attr
class RangeObj : public ffi::Object {
public:
PrimExpr begin;
PrimExpr extent;
mutable Location loc = UnknownLoc();
explicit RangeObj(ffi::UnsafeInit tag) : begin(tag), extent(tag) {}
RangeObj(PrimExpr begin, PrimExpr extent, Location loc = UnknownLoc())
: begin(begin), extent(extent), loc(loc) {}
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<RangeObj>()
.def_ro("begin", &RangeObj::begin)
.def_ro("extent", &RangeObj::extent)
.def_ro("loc", &RangeObj::loc, refl::AttachFieldFlag::SEqHashIgnore());
}
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.Range", RangeObj, ffi::Object);
};
class Range : public ffi::ObjectRef {
public:
TVM_TIRX_DLL Range(PrimExpr begin, PrimExpr end, Location loc = UnknownLoc());
TVM_TIRX_DLL static Range FromMinExtent(PrimExpr min, PrimExpr extent,
Location loc = UnknownLoc());
TVM_TIRX_DLL static PrimExpr GetExtentFromBeginEnd(const PrimExpr& begin, const PrimExpr& end);
// declare range.
explicit Range(ffi::UnsafeInit tag, ffi::ObjectPtr<RangeObj> n) : ffi::ObjectRef(tag) {
data_ = std::move(n);
}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Range, ffi::ObjectRef, RangeObj);
};
class TensorRegionObj : public ExprObj {
public:
explicit TensorRegionObj(Expr source) : source(std::move(source)) {}
explicit TensorRegionObj(ffi::UnsafeInit tag) : source(tag) {}
Expr source;
ffi::Array<Range> region;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
// A region's source and symbolic source type form a definition pattern;
// ranges then refer to those same identities.
refl::ObjectDef<TensorRegionObj>()
.def_ro("source", &TensorRegionObj::source, refl::AttachFieldFlag::SEqHashDefPattern())
.def_ro("region", &TensorRegionObj::region);
}
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.TensorRegion", TensorRegionObj, ExprObj);
};
class TensorRegion : public Expr {
public:
TVM_TIRX_DLL TensorRegion(Expr source, ffi::Array<Range> region, Type ty,
Location loc = UnknownLoc());
TVM_TIRX_DLL static TensorRegion FromTensorLoad(TensorLoad value);
explicit TensorRegion(ffi::ObjectPtr<TensorRegionObj> node) : Expr(std::move(node)) {}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(TensorRegion, Expr, TensorRegionObj);
TVM_DEFINE_OBJECT_REF_COW_METHOD(TensorRegionObj);
};
TVM_TIRX_DLL CallEffectKind SideEffect(const Expr& expr);
namespace ffi {
template <>
inline constexpr bool use_default_type_traits_v<tvm::StringImm> = false;
template <>
struct TypeTraits<tvm::StringImm>
: public ObjectRefWithFallbackTraitsBase<tvm::StringImm, ffi::String> {
TVM_FFI_INLINE static tvm::StringImm ConvertFallbackValue(ffi::String value) {
return tvm::StringImm(value);
}
};
template <>
inline constexpr bool use_default_type_traits_v<IntImm> = false;
template <>
struct TypeTraits<IntImm> : public ObjectRefWithFallbackTraitsBase<IntImm, int64_t> {
TVM_FFI_INLINE static IntImm ConvertFallbackValue(int64_t value) {
auto value_ty =
(value > std::numeric_limits<int>::max() || value < std::numeric_limits<int>::min())
? PrimType::Int(64)
: PrimType::Int(32);
return IntImm(value_ty, value);
}
};
template <>
inline constexpr bool object_ref_contains_v<PrimExpr, IntImmObj> = true;
template <>
inline constexpr bool use_default_type_traits_v<FloatImm> = false;
template <>
struct TypeTraits<FloatImm> : public ObjectRefWithFallbackTraitsBase<FloatImm, double> {
TVM_FFI_INLINE static FloatImm ConvertFallbackValue(double value) {
return FloatImm(PrimType::Float(32), value);
}
};
template <>
inline constexpr bool object_ref_contains_v<PrimExpr, FloatImmObj> = true;
template <>
inline constexpr bool object_ref_contains_v<PrimExpr, TensorLoadObj> = true;
template <typename ExpectedType>
inline constexpr bool use_default_type_traits_v<TypedExpr<ExpectedType>> = false;
template <typename ExpectedType>
struct TypeTraits<TypedExpr<ExpectedType>>
: public ObjectRefTypeTraitsBase<TypedExpr<ExpectedType>> {
TVM_FFI_INLINE static bool CheckAnyStrict(const TVMFFIAny* src) {
if (src->type_index < TypeIndex::kTVMFFIStaticObjectBegin ||
!detail::IsObjectInstance<ExprObj>(src->type_index)) {
return false;
}
// Non-owning: this only reads `ty`, and the owning form's incref/decref pair costs two
// atomics per check on a path every typed field assignment takes.
const auto* expr = detail::ObjectUnsafe::RawObjectPtrFromUnowned<ExprObj>(src->v_obj);
return detail::AnyUnsafe::CheckAnyViewStrict<ExpectedType>(expr->ty);
}
TVM_FFI_INLINE static std::string GetMismatchTypeInfo(const TVMFFIAny* src) {
if (src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin && src->v_obj != nullptr &&
detail::IsObjectInstance<ExprObj>(src->type_index)) {
const auto* expr = detail::ObjectUnsafe::RawObjectPtrFromUnowned<ExprObj>(src->v_obj);
if (expr->ty.defined()) {
const auto* prim_ty = expr->ty.as<PrimTypeObj>();
std::string ty =
prim_ty ? std::string(ffi::DLDataTypeToString(prim_ty->dtype)) : expr->ty.GetTypeKey();
return TypeIndexToTypeKey(src->type_index) + "[ty=" + ty + "]";
}
}
return TypeTraitsBase::GetMismatchTypeInfo(src);
}
};
// Primitive expression arguments also accept bool, integer, and floating-point literals.
template <>
inline constexpr bool use_default_type_traits_v<PrimExpr> = false;
template <>
struct TypeTraits<PrimExpr>
: public ObjectRefWithFallbackTraitsBase<PrimExpr, StrictBool, int64_t, double> {
TVM_FFI_INLINE static bool CheckAnyStrict(const TVMFFIAny* src) {
return TypeTraits<TypedExpr<PrimType>>::CheckAnyStrict(src);
}
TVM_FFI_INLINE static std::string TypeStr() { return "ir.PrimExpr"; }
TVM_FFI_INLINE static std::string GetMismatchTypeInfo(const TVMFFIAny* src) {
return TypeTraits<TypedExpr<PrimType>>::GetMismatchTypeInfo(src);
}
TVM_FFI_INLINE static PrimExpr ConvertFallbackValue(StrictBool value) {
return IntImm::Bool(value);
}
TVM_FFI_INLINE static PrimExpr ConvertFallbackValue(int64_t value) {
return TypeTraits<IntImm>::ConvertFallbackValue(value);
}
TVM_FFI_INLINE static PrimExpr ConvertFallbackValue(double value) {
return TypeTraits<FloatImm>::ConvertFallbackValue(value);
}
};
// Generic expression arguments accept primitive/string literals and arrays of expressions.
template <>
inline constexpr bool use_default_type_traits_v<Expr> = false;
template <>
struct TypeTraits<Expr> : public ObjectRefWithFallbackTraitsBase<Expr, PrimExpr, ffi::String> {
TVM_FFI_INLINE static Expr ConvertFallbackValue(PrimExpr value) { return value; }
TVM_FFI_INLINE static Expr ConvertFallbackValue(ffi::String value) {
return StringImm(std::move(value));
}
static std::optional<Expr> TryCastFromAnyView(const TVMFFIAny* src) {
if (auto value =
ObjectRefWithFallbackTraitsBase<Expr, PrimExpr, ffi::String>::TryCastFromAnyView(src)) {
return value;
}
if (auto fields = TypeTraits<ffi::Array<Expr>>::TryCastFromAnyView(src)) {
return tvm::Tuple(std::move(*fields));
}
return std::nullopt;
}
};
template <>
inline constexpr bool use_default_type_traits_v<IntExpr> = false;
template <>
struct TypeTraits<IntExpr> : public ObjectRefWithFallbackTraitsBase<IntExpr, int64_t> {
using Base = ObjectRefWithFallbackTraitsBase<IntExpr, int64_t>;
TVM_FFI_INLINE static bool CheckAnyStrict(const TVMFFIAny* src) {
if (!Base::CheckAnyStrict(src)) return false;
const auto* expr = detail::ObjectUnsafe::RawObjectPtrFromUnowned<ExprObj>(src->v_obj);
const auto* ty = expr->ty.as<PrimTypeObj>();
return ty != nullptr && ty->dtype.lanes == 1 &&
(ty->dtype.code == kDLInt || ty->dtype.code == kDLUInt);
}
TVM_FFI_INLINE static IntExpr ConvertFallbackValue(int64_t value) {
return TypeTraits<PrimExpr>::ConvertFallbackValue(value).as_or_throw<IntExpr>();
}
TVM_FFI_INLINE static std::string TypeStr() { return "ir.IntExpr"; }
TVM_FFI_INLINE static std::string GetMismatchTypeInfo(const TVMFFIAny* src) {
return TypeTraits<TypedExpr<PrimType>>::GetMismatchTypeInfo(src);
}
};
template <>
inline constexpr bool use_default_type_traits_v<PrimVar> = false;
template <>
struct TypeTraits<PrimVar> : public ObjectRefTypeTraitsBase<PrimVar> {
using Base = ObjectRefTypeTraitsBase<PrimVar>;
using Base::CopyFromAnyViewAfterCheck;
using Base::CopyToAnyView;
using Base::GetMismatchTypeInfo;
using Base::MoveFromAnyAfterCheck;
using Base::MoveToAny;
using Base::TypeSchema;
using Base::TypeStr;
TVM_FFI_INLINE static bool CheckAnyStrict(const TVMFFIAny* src) {
if (src->type_index != VarObj::RuntimeTypeIndex()) {
return false;
}
const auto* var = static_cast<const VarObj*>(
detail::ObjectUnsafe::ObjectPtrFromUnowned<Object>(src->v_obj).get());
return detail::AnyUnsafe::CheckAnyStrict<PrimType>(var->ExprObj::ty);
}
TVM_FFI_INLINE static std::optional<PrimVar> TryCastFromAnyView(const TVMFFIAny* src) {
if (CheckAnyStrict(src)) {
return detail::ObjectUnsafe::ObjectRefFromObjectPtr<PrimVar>(
detail::ObjectUnsafe::ObjectPtrFromUnowned<VarObj>(src->v_obj));
}
return std::nullopt;
}
};
template <>
inline constexpr bool use_default_type_traits_v<TensorRegion> = false;
template <>
struct TypeTraits<TensorRegion> : public ObjectRefWithFallbackTraitsBase<TensorRegion, TensorLoad> {
TVM_FFI_INLINE static TensorRegion ConvertFallbackValue(TensorLoad value) {
return TensorRegion::FromTensorLoad(std::move(value));
}
};
} // namespace ffi
using SubscriptSlice = ffi::Array<ffi::Variant<
ffi::Tuple<ffi::Optional<PrimExpr>, ffi::Optional<PrimExpr>, ffi::Optional<PrimExpr>>,
PrimExpr>>;
namespace type_attr {
inline constexpr ffi::reflection::AttrKey<
ffi::TypedFunction<ffi::ObjectRef(Expr, SubscriptSlice, Location)>>
kExprSubscriptRealizeByTy{"__tvm_expr_subscript_realize__"};
} // namespace type_attr
} // namespace tvm
/* \brief Allow tvm.Var and tvm.GlobalVar as keys in STL tables
*
* For most IR expressions, it would be ambiguous whether the
* expression should follow reference equality or structural equality.
* This is not the case for variables, which do not contain nested
* internal structure, and are frequently used as keys in lookup
* tables.
*
* Providing `std::hash` and `std::equal_to` specializations for
* `tvm::Var` and `tvm::GlobalVar` allows them to be used as keys in STL tables. For
* other IR expressions, the user must specify the type of equality
* used (e.g. `std::unordered_set<T, StructuralHash, StructuralEqual>`
* or `std::unordered_set<T, ffi::ObjectPtrHash, ffi::ObjectPtrEqual>`).
*/
template <>
struct std::hash<tvm::Var> {
std::size_t operator()(const tvm::Var& var) const { return tvm::ffi::ObjectPtrHash()(var); }
};
template <>
struct std::equal_to<tvm::Var> {
bool operator()(const tvm::Var& var_a, const tvm::Var& var_b) const {
return tvm::ffi::ObjectPtrEqual()(var_a, var_b);
}
};
template <>
struct std::hash<tvm::GlobalVar> {
std::size_t operator()(const tvm::GlobalVar& var) const { return tvm::ffi::ObjectPtrHash()(var); }
};
template <>
struct std::equal_to<tvm::GlobalVar> {
bool operator()(const tvm::GlobalVar& var_a, const tvm::GlobalVar& var_b) const {
return tvm::ffi::ObjectPtrEqual()(var_a, var_b);
}
};
#endif // TVM_IR_EXPR_H_