Files
wehub-resource-sync 26446540fa
Lint / lint (push) Has been cancelled
CI / MacOS (push) Has been cancelled
CI / Windows (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

387 lines
12 KiB
C++

/*
* 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/tirx/var.h
* \brief Variables in the TIR.
*/
#ifndef TVM_TIR_VAR_H_
#define TVM_TIR_VAR_H_
#include <tvm/ffi/dtype.h>
#include <tvm/ir/cow.h>
#include <tvm/ir/expr.h>
#include <tvm/ir/type.h>
#include <functional>
#include <string>
namespace tvm {
namespace tirx {
/*!
* \brief A variable node in the IR.
*
* A variable is uniquely identified by its address.
*
* Each variable is only bound once in the following nodes:
* - Allocate
* - For
* - Let
* - Bind
*/
class VarNode : public ExprNode {
public:
/*!
* \brief The hint to the variable name.
* \note Each variable is uniquely identified by its address.
*/
ffi::String name_hint;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<VarNode>().def_ro("name", &VarNode::name_hint,
refl::AttachFieldFlag::SEqHashIgnore());
}
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindFreeVar;
static constexpr const uint32_t _type_child_slots = 1;
TVM_FFI_DECLARE_OBJECT_INFO("tirx.Var", VarNode, ExprNode);
};
/*! \brief a named variable in TIR */
class Var : public Expr {
public:
explicit Var(ffi::UnsafeInit tag) : Expr(tag) {}
explicit Var(ffi::ObjectPtr<VarNode> n) : Expr(n) {}
/*!
* \brief Constructor
* \param name_hint variable name
* \param dtype data type
* \param span The location of this object in the source code.
*/
TVM_DLL explicit Var(ffi::String name_hint = "v", PrimType dtype = PrimType::Int(32),
Span span = Span());
/*!
* \brief Constructor which provides a more detailed type annotation.
* \param name_hint variable name.
* \param type_annotation The type annotation.
* \param span The location of this object in the source code.
*/
TVM_DLL explicit Var(ffi::String name_hint, Type type_annotation, Span span = Span());
/*!
* \brief Make a new copy of var with same type, but a different nam
* \param name The new name to be used.
* \return the new Var copy
*/
TVM_DLL Var copy_with_name(const ffi::String& name) const;
/*!
* \brief Make a new copy of var with same type, append suffix
* \param suffix The suffix to be appended.
* \return the new Var copy
*/
TVM_DLL Var CopyWithSuffix(const ffi::String& suffix) const;
/*!
* \brief Make a new copy of the variable with specified dtype
* \param dtype The specified dtype
* \return The new variable
*/
TVM_DLL Var copy_with_dtype(PrimType dtype) const;
/*!
* \brief Get pointer to the internal value.
* \return the corresponding Variable.
*/
const VarNode* operator->() const { return get(); }
/*!
* \brief Get pointer to the internal value.
* \return the corresponding Variable.
*/
const VarNode* get() const { return static_cast<const VarNode*>(data_.get()); }
/*! \brief type indicate the container type */
using ContainerType = VarNode;
static constexpr bool _type_container_is_exact = true;
};
/*!
* \brief Checked scalar view over a VarNode.
*
* PrimVar is a zero-state reference view over the same VarNode as Var. It additionally
* guarantees that the inherited ExprNode::ty is PrimType.
*/
class PrimVar : public PrimExpr {
public:
/*! \brief Construct a scalar variable directly from a primitive type. */
explicit PrimVar(ffi::String name_hint, PrimType dtype = PrimType::Int(32), Span span = Span())
: PrimExpr(
Var(std::move(name_hint), std::move(dtype), std::move(span)).as_or_throw<PrimExpr>()) {}
/*! \brief Construct a scalar variable directly from a checked type annotation. */
explicit PrimVar(ffi::String name_hint, Type type_annotation, Span span = Span())
: PrimExpr(Var(std::move(name_hint), std::move(type_annotation), std::move(span))
.as_or_throw<PrimExpr>()) {}
/*! \brief Safe widening to a general Var view over the same node. */
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 copy_with_dtype(PrimType dtype) const {
return this->as_or_throw<Var>().copy_with_dtype(dtype).as_or_throw<PrimVar>();
}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(PrimVar, PrimExpr, VarNode);
static constexpr bool _type_container_is_exact = true;
};
using Region = ffi::Array<Range>;
/*!
* \brief Type of iteration variable.
* Each IterVar have a specific type.
*
* The type of iter var can be overriden via
* stage.iter_var_attrs given they are compatible.
*/
enum IterVarType : int {
/*!
* \brief Data parallel iteration.
* This normally corresponds to axis of Tensor.
* Allow all IterVar manipulations.
*
* \note This does not mean the loop
* have to be executed in parallel fashion.
*/
kDataPar = 0,
/*!
* \brief The IterVar itself is a thread-index
* of a fixed thread launching group.
* Note that this is already assumed to be parallelized.
*
* Disallow: split/fuse/vectorize/parallel
*/
kThreadIndex = 1,
/*!
* \brief Communicative reduction.
* Cannot be directly parallelized.
*
* Disallow: parallel/vectorize
*/
kCommReduce = 2,
/*!
* \brief Serial loops with loop carry dependency,
* the iteration must execute in order.
* Cannot be re-ordered.
*
* Disallow: reorder/parallel/vectorize
*/
kOrdered = 3,
/*!
* \brief IterVar is opaque,
*
* May not corresponds to any generated loop
* Disallow all IterVar manipulations and compute_at
*
* \note This is usually used to implement composite op
* or external op, where the
*/
kOpaque = 4,
// The following are possible additional
// types that are provided during schedule
/*!
* \brief The execution is unrolled.
*/
kUnrolled = 5,
/*!
* \brief The loop is vectorized.
*/
kVectorized = 6,
/*!
* \brief The loop is parallelized.
*/
kParallelized = 7,
/*!
* \brief Marks boundary of tensorization intrinsic.
*/
kTensorized = 8
};
/*!
* \brief An iteration variable representing an iteration
* over a one dimensional interval.
*
* The dtype of the extent of the `dom` of the IterVar must match the dtype of the internal Var.
*/
class IterVarNode : public PrimExprConvertibleNode {
public:
/*!
* \brief the domain of iteration, if known, can be None
* For the intermediate schedule node, before schedule.
*/
Range dom;
/*! \brief The looping variable */
PrimVar var;
/*! \brief The type of the IterVar */
IterVarType iter_type;
/*!
* \brief additional tag on the iteration variable,
* set this if this is bound already to a known thread tag.
*/
ffi::String thread_tag;
/*!
* \brief Span that points to the original source code.
* Reserved debug information.
*/
mutable Span span;
PrimExpr ToPrimExpr() const final { return var; }
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<IterVarNode>()
.def_ro("dom", &IterVarNode::dom)
.def_ro("var", &IterVarNode::var, refl::AttachFieldFlag::SEqHashDefRecursive())
.def_ro("iter_type", &IterVarNode::iter_type)
.def_ro("thread_tag", &IterVarNode::thread_tag);
}
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("tirx.IterVar", IterVarNode, PrimExprConvertibleNode);
};
/*!
* \brief Iteration Variable,
* represents an iteration over an integer interval.
*
* The dtype of the extent of the `dom` of the IterVar must match the dtype of the internal Var.
*/
class IterVar : public PrimExprConvertible {
public:
TVM_DLL IterVar(Range dom, PrimVar var, IterVarType iter_type, ffi::String thread_tag = "",
Span span = Span());
/*!
* \return the corresponding var in the IterVar.
*/
inline operator PrimExpr() const;
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(IterVar, PrimExprConvertible, IterVarNode);
TVM_DEFINE_OBJECT_REF_COW_METHOD(IterVarNode);
};
// inline implementations
inline IterVar::operator PrimExpr() const { return (*this)->var; }
inline const char* IterVarType2String(IterVarType t) {
switch (t) {
case kDataPar:
return "DataPar";
case kThreadIndex:
return "ThreadIndex";
case kCommReduce:
return "CommReduce";
case kOrdered:
return "Ordered";
case kOpaque:
return "Opaque";
case kUnrolled:
return "Unrolled";
case kVectorized:
return "Vectorized";
case kParallelized:
return "Parallelized";
case kTensorized:
return "Tensorized";
}
return "Unknown";
}
} // namespace tirx
} // namespace tvm
namespace tvm::ffi {
template <>
inline constexpr bool use_default_type_traits_v<tirx::PrimVar> = false;
template <>
struct TypeTraits<tirx::PrimVar> : public ObjectRefTypeTraitsBase<tirx::PrimVar> {
using Base = ObjectRefTypeTraitsBase<tirx::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 == TypeIndex::kTVMFFINone) {
return tirx::PrimVar::_type_is_nullable;
}
if (src->type_index < TypeIndex::kTVMFFIStaticObjectBegin ||
!details::IsObjectInstance<tirx::VarNode>(src->type_index)) {
return false;
}
const auto* var = static_cast<const tirx::VarNode*>(
details::ObjectUnsafe::ObjectPtrFromUnowned<Object>(src->v_obj).get());
return details::AnyUnsafe::CheckAnyStrict<PrimType>(var->ExprNode::ty);
}
TVM_FFI_INLINE static std::optional<tirx::PrimVar> TryCastFromAnyView(const TVMFFIAny* src) {
if (CheckAnyStrict(src)) {
if (src->type_index == TypeIndex::kTVMFFINone) {
return details::ObjectUnsafe::ObjectRefFromObjectPtr<tirx::PrimVar>(nullptr);
}
return details::ObjectUnsafe::ObjectRefFromObjectPtr<tirx::PrimVar>(
details::ObjectUnsafe::ObjectPtrFromUnowned<tirx::VarNode>(src->v_obj));
}
return std::nullopt;
}
};
} // namespace tvm::ffi
/* \brief Allow tirx.Var as key in STL tables
*
* For most TIR 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
* `tirx::Var` allows it to be used as a key in STL tables. For
* `PrimExpr`, 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::tirx::Var> {
std::size_t operator()(const tvm::tirx::Var& var) const { return tvm::ffi::ObjectPtrHash()(var); }
};
template <>
struct std::equal_to<tvm::tirx::Var> {
bool operator()(const tvm::tirx::Var& var_a, const tvm::tirx::Var& var_b) const {
return tvm::ffi::ObjectPtrEqual()(var_a, var_b);
}
};
#endif // TVM_TIR_VAR_H_