This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-tirx.git
The following commit(s) were added to refs/heads/main by this push:
new b7555970 Consolidate expression headers and inline FFI conversion
traits (#10)
b7555970 is described below
commit b7555970a9aa35121c3b18354aebf0ef3bb7e04a
Author: Tianqi Chen <[email protected]>
AuthorDate: Wed Oct 21 12:38:04 2026 -0400
Consolidate expression headers and inline FFI conversion traits (#10)
Place core type definitions in `type.h` and all expression definitions
in `expr.h`, removing the intermediate `base_expr.h` split. Define
expression FFI conversion traits alongside their complete node types and
remove their unnecessary exported functions and dead nullable branches.
Require explicit checked conversion from `Call` to `PrimExpr`,
preserving literal, tuple, string, and tensor-region conversion
behavior.
---
include/tvm/ir/base_expr.h | 596 -------------------------------
include/tvm/ir/expr.h | 340 ++++++++++++++++--
include/tvm/ir/type.h | 290 ++++++++++++++-
src/backend/cuda/transform/lower_iket.cc | 10 +-
src/ir/expr.cc | 39 --
src/tirx/ir/ir_mutator_with_analyzer.cc | 5 +-
src/tirx/ir/tensor.cc | 4 -
src/tirx/transform/vectorize_loop.cc | 3 +-
tests/cpp/ir/pattern_match_test.cc | 3 +-
9 files changed, 607 insertions(+), 683 deletions(-)
diff --git a/include/tvm/ir/base_expr.h b/include/tvm/ir/base_expr.h
deleted file mode 100644
index e7a15bc4..00000000
--- a/include/tvm/ir/base_expr.h
+++ /dev/null
@@ -1,596 +0,0 @@
-/*
- * 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/ir/base_expr.h
- * \brief Base expression and primitive type nodes.
- */
-#ifndef TVM_IR_BASE_EXPR_H_
-#define TVM_IR_BASE_EXPR_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/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/registry.h>
-#include <tvm/ffi/string.h>
-#include <tvm/ir/location.h>
-
-#include <cstddef>
-#include <cstdint>
-#include <optional>
-#include <type_traits>
-
-namespace tvm {
-
-/*!
- * \brief Type is the base type of all types.
- *
- * TVM's type system contains following subclasses:
- *
- * - PrimType: type of primitive type values used in the low-level IR.
- * - FuncType: type of a function.
- * - TensorType: type of certain Tensor values in the expression.
- *
- * There are also advanced types to support generic(polymorphic types).
- * \sa Type
- */
-class TypeNode : public ffi::Object {
- public:
- /*!
- * \brief Location that points to the original source code.
- * Reserved debug information.
- */
- mutable Location loc = UnknownLoc();
-
- static void RegisterReflection() {
- namespace refl = tvm::ffi::reflection;
- // loc do not participate in structural equal and hash.
- refl::ObjectDef<TypeNode>().def_ro("loc", &TypeNode::loc,
refl::DefaultValue(UnknownLoc()),
- refl::init(false),
refl::AttachFieldFlag::SEqHashIgnore());
- }
-
- static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind =
kTVMFFISEqHashKindTreeNode;
- static constexpr bool _type_s_eq_hash_subclass_kind_fixed = true;
-
- static constexpr const uint32_t _type_child_slots = 14;
- TVM_FFI_DECLARE_OBJECT_INFO("ir.Type", TypeNode, ffi::Object);
-};
-
-/*!
- * \brief Managed reference to TypeNode.
- * \sa TypeNode
- */
-class Type : public ffi::ObjectRef {
- public:
- /*! \brief Construct a MissingType for type information not yet populated. */
- TVM_TIRX_DLL static Type Missing();
-
- TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Type, ffi::ObjectRef,
TypeNode);
-};
-
-/*!
- * \brief Type information that has not been supplied or computed.
- *
- * MissingType is not a concrete type, wildcard, or inference variable. Unlike
- * AnyType and Void, it must be resolved before a fully typed IR boundary.
- */
-class MissingTypeNode final : public TypeNode {
- public:
- static void RegisterReflection() {
- namespace refl = tvm::ffi::reflection;
- refl::ObjectDef<MissingTypeNode>();
- }
-
- TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.MissingType", MissingTypeNode,
TypeNode);
-};
-
-/*! \brief Managed reference to the MissingTypeNode singleton. */
-class MissingType final : public Type {
- public:
- TVM_TIRX_DLL MissingType();
-
- TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(MissingType, Type,
MissingTypeNode);
-};
-
-/*!
- * \brief Type marker for opaque construction-time expressions.
- *
- * Opaque values may be used while constructing IR, but must be lowered away
- * before the IR is considered complete.
- */
-class OpaqueTypeNode final : public TypeNode {
- public:
- static void RegisterReflection() {
- namespace refl = tvm::ffi::reflection;
- refl::ObjectDef<OpaqueTypeNode>();
- }
-
- TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.OpaqueType", OpaqueTypeNode, TypeNode);
-};
-
-/*! \brief Managed reference to OpaqueTypeNode. */
-class OpaqueType final : public Type {
- public:
- TVM_TIRX_DLL OpaqueType();
-
- TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(OpaqueType, Type,
OpaqueTypeNode);
-};
-
-/*!
- * \brief Primitive data types used in the low-level IR.
- *
- * PrimType represents primitive POD values and the void sentinel.
- *
- * \sa PrimType
- */
-class PrimTypeNode final : public TypeNode {
- public:
- /*!
- * \brief The raw DLPack dtype represented by this primitive type.
- */
- DLDataType dtype;
-
- static void RegisterReflection() {
- namespace refl = tvm::ffi::reflection;
- refl::ObjectDef<PrimTypeNode>().def_ro("dtype", &PrimTypeNode::dtype);
- }
- TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.PrimType", PrimTypeNode, TypeNode);
-};
-
-/*
- * \brief Managed reference to PrimTypeNode.
- * \sa PrimTypeNode
- */
-class PrimType final : public Type {
- public:
- /*!
- * \brief Construct from a raw DLPack dtype.
- * \param dtype The corresponding DLPack dtype.
- */
- TVM_TIRX_DLL explicit PrimType(DLDataType dtype);
-
- /*!
- * \brief Construct from DLPack dtype fields.
- * \param code The DLPack dtype code.
- * \param bits The scalar bit width.
- * \param lanes The fixed lane count.
- */
- TVM_TIRX_DLL PrimType(DLDataTypeCode code, int bits, int lanes = 1);
-
- /*! \brief Construct a signed integer type with fixed lanes. */
- TVM_TIRX_DLL static PrimType Int(int bits, int lanes = 1);
- /*! \brief Construct an unsigned integer type with fixed lanes. */
- TVM_TIRX_DLL static PrimType UInt(int bits, int lanes = 1);
- /*! \brief Construct a floating-point type with fixed lanes. */
- TVM_TIRX_DLL static PrimType Float(int bits, int lanes = 1);
- /*! \brief Construct a bfloat type with fixed lanes. */
- TVM_TIRX_DLL static PrimType BFloat(int bits, int lanes = 1);
- /*! \brief Construct a boolean type with fixed lanes. */
- TVM_TIRX_DLL static PrimType Bool(int lanes = 1);
- /*! \brief Construct the void sentinel type, encoded as handle(0, 0). */
- TVM_TIRX_DLL static PrimType Void();
- /*!
- * \brief Construct a scalable vector type.
- * \param code The DLPack dtype code.
- * \param bits The scalar bit width.
- * \param lanes The positive vscale factor to encode in the DLPack lane
field.
- */
- TVM_TIRX_DLL static PrimType ScalableVector(DLDataTypeCode code, int bits,
int lanes);
-
- /*! \return The DLPack dtype code. */
- TVM_FFI_INLINE DLDataTypeCode code() const {
- return static_cast<DLDataTypeCode>(static_cast<int>(get()->dtype.code));
- }
-
- /*! \return The scalar bit width. */
- TVM_FFI_INLINE int32_t bits() const { return get()->dtype.bits; }
-
- /*!
- * \return The fixed lane count.
- * \note Throws on scalable vector types, where the encoded lane field
stores a vscale factor.
- */
- TVM_FFI_INLINE int32_t lanes() const {
- int16_t encoded_lanes = static_cast<int16_t>(get()->dtype.lanes);
- if (TVM_FFI_PREDICT_FALSE(encoded_lanes < 0)) {
- TVM_FFI_THROW(InternalError)
- << "Can't fetch the lanes of a scalable vector at a compile time.";
- }
- return encoded_lanes;
- }
-
- /*!
- * \brief Check the scalar element code and bit width.
- * \note Lane count and scalable-vector encoding are intentionally ignored.
- */
- TVM_FFI_INLINE bool MatchesElementType(DLDataTypeCode code, int bits) const {
- DLDataType dtype = get()->dtype;
- return dtype.code == static_cast<uint8_t>(code) && dtype.bits == bits;
- }
-
- /*!
- * \brief Check whether the dtype code matches any of the provided DLPack
codes.
- * \note Bit width and lanes are intentionally ignored.
- */
- template <typename... Codes>
- TVM_FFI_INLINE bool MatchesCode(Codes... codes) const {
- uint8_t dtype_code = get()->dtype.code;
- return ((dtype_code == static_cast<uint8_t>(codes)) || ...);
- }
-
- /*! \brief Whether this type is a scalar, excluding fixed and scalable
vectors. */
- TVM_FFI_INLINE bool IsScalar() const {
- int16_t encoded_lanes = static_cast<int16_t>(get()->dtype.lanes);
- return encoded_lanes == 1;
- }
-
- /*! \brief Whether this type is the void sentinel `handle(0, 0)`. */
- TVM_FFI_INLINE bool IsVoid() const {
- DLDataType dtype = get()->dtype;
- return dtype.code == static_cast<uint8_t>(DLDataTypeCode::kDLOpaqueHandle)
&& dtype.bits == 0 &&
- static_cast<int16_t>(dtype.lanes) == 0;
- }
-
- /*! \brief Whether this type is a scalable vector. */
- TVM_FFI_INLINE bool IsScalableVector() const {
- return static_cast<int16_t>(get()->dtype.lanes) < -1;
- }
-
- /*! \brief Whether this type is a fixed-length vector. */
- TVM_FFI_INLINE bool IsFixedLengthVector() const {
- return static_cast<int16_t>(get()->dtype.lanes) > 1;
- }
-
- /*!
- * \brief Return the number of bytes needed to store one value of this type.
- *
- * This uses the same packed sub-byte dtype sizing rule as runtime tensors.
- * Scalable vector types have no compile-time storage size and are rejected.
- */
- TVM_FFI_INLINE size_t StorageBytes() const {
- DLDataType dtype = get()->dtype;
- int16_t encoded_lanes = static_cast<int16_t>(dtype.lanes);
- if (TVM_FFI_PREDICT_FALSE(encoded_lanes < 0)) {
- TVM_FFI_THROW(InternalError)
- << "Cannot compute compile-time storage bytes for non-fixed vector
type " << dtype;
- }
- return static_cast<size_t>(
- (static_cast<uint64_t>(dtype.bits) *
static_cast<uint64_t>(dtype.lanes) + 7) / 8);
- }
-
- /*! \brief Return the same type with a different dtype code, preserving bits
and lanes. */
- TVM_FFI_INLINE PrimType WithCode(DLDataTypeCode code) const {
- DLDataType dtype = get()->dtype;
- int16_t encoded_lanes = static_cast<int16_t>(dtype.lanes);
- if (encoded_lanes < -1) {
- return ScalableVector(code, dtype.bits, -encoded_lanes);
- }
- return PrimType(code, dtype.bits, encoded_lanes);
- }
-
- /*! \brief Return the same type with a different scalar bit width,
preserving code and lanes. */
- TVM_FFI_INLINE PrimType WithBits(int bits) const {
- DLDataType dtype = get()->dtype;
- int16_t encoded_lanes = static_cast<int16_t>(dtype.lanes);
- if (encoded_lanes < -1) {
- return ScalableVector(this->code(), bits, -encoded_lanes);
- }
- return PrimType(this->code(), bits, encoded_lanes);
- }
-
- /*! \brief Return the same scalar element type with a fixed lane count. */
- TVM_FFI_INLINE PrimType WithLanes(int lanes) const {
- return PrimType(this->code(), this->bits(), lanes);
- }
-
- /*! \return The vscale factor encoded in a scalable vector type. */
- TVM_FFI_INLINE int32_t VScaleFactor() const {
- int16_t encoded_lanes = static_cast<int16_t>(get()->dtype.lanes);
- if (encoded_lanes >= -1) {
- TVM_FFI_THROW(InternalError) << "A fixed length vector doesn't have a
vscale factor.";
- }
- return -encoded_lanes;
- }
-
- TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(PrimType, Type, PrimTypeNode);
-};
-
-inline bool operator==(const PrimType& lhs, const PrimType& rhs) {
- return lhs->dtype == rhs->dtype;
-}
-
-inline bool operator!=(const PrimType& lhs, const PrimType& rhs) { return
!(lhs == rhs); }
-
-/*!
- * \brief Base type of all the expressions.
- * \sa Expr
- */
-class ExprNode : public ffi::Object {
- public:
- /*!
- * \brief Location that points to the original source code.
- * Reserved debug information.
- */
- mutable Location loc = UnknownLoc();
-
- /*!
- * \brief The deduced or annotated type of the expression.
- *
- * MissingType() denotes type information that will be populated by
- * later analysis passes instead of expression constructors.
- */
- mutable Type ty = MissingType();
-
- static void RegisterReflection() {
- namespace refl = tvm::ffi::reflection;
- // loc does not participate in structural equal and hash.
- refl::ObjectDef<ExprNode>()
- .def_ro("loc", &ExprNode::loc, refl::DefaultValue(UnknownLoc()),
- refl::AttachFieldFlag::SEqHashIgnore())
- .def_ro("ty", &ExprNode::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", ExprNode, ffi::Object);
-};
-
-/*!
- * \brief Managed reference to ExprNode.
- * \sa ExprNode
- */
-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<ExprNode> 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,
ExprNode);
-};
-
-/*! \brief Base node for traversable expressions eliminated during
compilation. */
-class StagingExprNode : public ExprNode {
- public:
- static void RegisterReflection() {
ffi::reflection::ObjectDef<StagingExprNode>(); }
- static constexpr const uint32_t _type_child_slots = 4;
- TVM_FFI_DECLARE_OBJECT_INFO("ir.StagingExpr", StagingExprNode, ExprNode);
-};
-
-/*! \brief Managed reference to a staging expression. */
-class StagingExpr : public Expr {
- public:
- TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(StagingExpr, Expr,
StagingExprNode);
-};
-
-/*!
- * \brief Base node for opaque construction-time expressions.
- *
- * Subclasses are passed through by generic expression visitors and mutators.
- * They must not remain in finished IR.
- */
-class OpaqueExprNode : public ExprNode {
- public:
- static void RegisterReflection() {
- namespace refl = tvm::ffi::reflection;
- refl::ObjectDef<OpaqueExprNode>();
- }
-
- static constexpr const uint32_t _type_child_slots = 2;
- TVM_FFI_DECLARE_OBJECT_INFO("ir.OpaqueExpr", OpaqueExprNode, ExprNode);
-};
-
-/*! \brief Managed reference to OpaqueExprNode. */
-class OpaqueExpr : public Expr {
- public:
- explicit OpaqueExpr(ffi::ObjectPtr<OpaqueExprNode> node) :
Expr(std::move(node)) {}
-
- TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(OpaqueExpr, Expr,
OpaqueExprNode);
-};
-
-class Call;
-
-/*!
- * \brief Typed reference/view over an expression whose result type is a
- * specific Type subtype.
- * \tparam ExpectedType The expected expression result type.
- */
-template <typename ExpectedType>
-class TypedExpr : public Expr {
- public:
- /*! \return the typed result of this expression. */
- ExpectedType ty() const {
- const auto* node = get();
- TVM_FFI_DCHECK(node != nullptr);
- const auto* ty_node = node->ExprNode::ty.template as<typename
ExpectedType::ContainerType>();
- TVM_FFI_DCHECK(ty_node != nullptr);
- return ffi::GetRef<ExpectedType>(ty_node);
- }
-
- explicit TypedExpr(ffi::ObjectPtr<ExprNode> node) : Expr(std::move(node)) {}
-
- TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(TypedExpr, Expr, ExprNode);
- static constexpr bool _type_container_is_exact = false;
-};
-
-/*!
- * \brief Typed reference/view over any Expr whose `ExprNode::ty` is PrimType.
- *
- * PrimExpr is a type category rather than a dedicated runtime node category.
- * It can contain intrinsic primitive nodes such as IntImmNode and
FloatImmNode,
- * or a general ExprNode such as CallNode, when that expression's `ty` field is
- * a PrimType. This keeps primitive-only APIs explicit while allowing shared
- * Expr nodes for cross-dialect values with richer result types when needed.
- */
-class PrimExpr : public TypedExpr<PrimType> {
- public:
- using TypedExpr<PrimType>::ty;
-
- /*!
- * \brief Construct from a call after checking that its result type is
- * PrimType.
- * \param call The call to view as a primitive expression.
- */
- TVM_TIRX_DLL PrimExpr(Call call); // NOLINT(*)
-
- /*!
- * \brief construct from integer.
- * \param value The value to be constructed.
- */
- TVM_TIRX_DLL PrimExpr(int32_t value); // NOLINT(*)
- /*!
- * \brief construct from float.
- * \param value The value to be constructed.
- */
- TVM_TIRX_DLL PrimExpr(float value); // NOLINT(*)
-
- explicit PrimExpr(ffi::ObjectPtr<ExprNode> node) :
TypedExpr<PrimType>(std::move(node)) {}
-
- TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(PrimExpr, TypedExpr<PrimType>,
ExprNode);
- static constexpr bool _type_container_is_exact = false;
-};
-
-/*!
- * \brief View over a scalar signed or unsigned integer expression of any bit
width.
- *
- * IntExpr validates integer operands in operator signatures without changing
the
- * underlying expression node or its PrimExpr implementation API.
- */
-class IntExpr : public PrimExpr {
- public:
- TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(IntExpr, PrimExpr, ExprNode);
- static constexpr bool _type_container_is_exact = false;
-};
-
-namespace ffi {
-template <>
-inline constexpr bool use_default_type_traits_v<PrimType> = false;
-
-template <>
-struct TypeTraits<PrimType> : public ObjectRefWithFallbackTraitsBase<PrimType,
DLDataType> {
- TVM_FFI_INLINE static PrimType ConvertFallbackValue(DLDataType dtype) {
return PrimType(dtype); }
-};
-
-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::kTVMFFINone) {
- return TypedExpr<ExpectedType>::_type_is_nullable;
- }
- if (src->type_index < TypeIndex::kTVMFFIStaticObjectBegin ||
- !details::IsObjectInstance<ExprNode>(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 =
details::ObjectUnsafe::RawObjectPtrFromUnowned<ExprNode>(src->v_obj);
- return details::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 &&
- details::IsObjectInstance<ExprNode>(src->type_index)) {
- const auto* expr =
details::ObjectUnsafe::RawObjectPtrFromUnowned<ExprNode>(src->v_obj);
- if (expr->ty.defined()) {
- const auto* prim_ty = expr->ty.as<PrimTypeNode>();
- 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);
- }
-};
-
-// Declare these traits before containers such as Array<Expr> and
SubscriptSlice instantiate them.
-// Conversion bodies that require literal or tuple nodes are defined in
expr.cc.
-// 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_TIRX_DLL static Expr ConvertFallbackValue(ffi::String value);
- TVM_TIRX_DLL static std::optional<Expr> TryCastFromAnyView(const TVMFFIAny*
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> {
- using Base = ObjectRefWithFallbackTraitsBase<PrimExpr, StrictBool, int64_t,
double>;
- TVM_FFI_INLINE static bool CheckAnyStrict(const TVMFFIAny* src) {
- if (src->type_index == TypeIndex::kTVMFFINone) return
PrimExpr::_type_is_nullable;
- return TypeTraits<TypedExpr<PrimType>>::CheckAnyStrict(src);
- }
-
- TVM_TIRX_DLL static std::optional<PrimExpr> TryCastFromAnyView(const
TVMFFIAny* 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_TIRX_DLL static PrimExpr ConvertFallbackValue(StrictBool value);
- TVM_TIRX_DLL static PrimExpr ConvertFallbackValue(int64_t value);
- TVM_TIRX_DLL static PrimExpr ConvertFallbackValue(double value);
-};
-
-} // namespace ffi
-
-/*! \brief Expression subscripts: point indices or (start, stop, step) slices.
*/
-using SubscriptSlice = ffi::Array<ffi::Variant<
- ffi::Tuple<ffi::Optional<PrimExpr>, ffi::Optional<PrimExpr>,
ffi::Optional<PrimExpr>>,
- PrimExpr>>;
-
-namespace type_attr {
-/*!
- * \brief Realize a subscript of an expression, dispatched on the type of the
expression.
- *
- * The callback receives the expression, its point indices or slices, and the
source location.
- * It returns a defined object representing the realized subscription.
- */
-inline constexpr ffi::reflection::AttrKey<
- ffi::TypedFunction<ffi::ObjectRef(Expr, SubscriptSlice, Location)>>
- kExprSubscriptRealizeByTy{"__tvm_expr_subscript_realize__"};
-} // namespace type_attr
-
-} // namespace tvm
-
-#endif // TVM_IR_BASE_EXPR_H_
diff --git a/include/tvm/ir/expr.h b/include/tvm/ir/expr.h
index c19b5621..879f8168 100644
--- a/include/tvm/ir/expr.h
+++ b/include/tvm/ir/expr.h
@@ -25,30 +25,206 @@
#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/base_expr.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 {
+/*!
+ * \brief Base type of all the expressions.
+ * \sa Expr
+ */
+class ExprNode : public ffi::Object {
+ public:
+ /*!
+ * \brief Location that points to the original source code.
+ * Reserved debug information.
+ */
+ mutable Location loc = UnknownLoc();
+
+ /*!
+ * \brief The deduced or annotated type of the expression.
+ *
+ * MissingType() denotes type information that will be populated by
+ * later analysis passes instead of expression constructors.
+ */
+ mutable Type ty = MissingType();
+
+ static void RegisterReflection() {
+ namespace refl = tvm::ffi::reflection;
+ // loc does not participate in structural equal and hash.
+ refl::ObjectDef<ExprNode>()
+ .def_ro("loc", &ExprNode::loc, refl::DefaultValue(UnknownLoc()),
+ refl::AttachFieldFlag::SEqHashIgnore())
+ .def_ro("ty", &ExprNode::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", ExprNode, ffi::Object);
+};
+
+/*!
+ * \brief Managed reference to ExprNode.
+ * \sa ExprNode
+ */
+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<ExprNode> 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,
ExprNode);
+};
+
+/*! \brief Base node for traversable expressions eliminated during
compilation. */
+class StagingExprNode : public ExprNode {
+ public:
+ static void RegisterReflection() {
ffi::reflection::ObjectDef<StagingExprNode>(); }
+ static constexpr const uint32_t _type_child_slots = 4;
+ TVM_FFI_DECLARE_OBJECT_INFO("ir.StagingExpr", StagingExprNode, ExprNode);
+};
+
+/*! \brief Managed reference to a staging expression. */
+class StagingExpr : public Expr {
+ public:
+ TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(StagingExpr, Expr,
StagingExprNode);
+};
+
+/*!
+ * \brief Base node for opaque construction-time expressions.
+ *
+ * Subclasses are passed through by generic expression visitors and mutators.
+ * They must not remain in finished IR.
+ */
+class OpaqueExprNode : public ExprNode {
+ public:
+ static void RegisterReflection() {
+ namespace refl = tvm::ffi::reflection;
+ refl::ObjectDef<OpaqueExprNode>();
+ }
+
+ static constexpr const uint32_t _type_child_slots = 2;
+ TVM_FFI_DECLARE_OBJECT_INFO("ir.OpaqueExpr", OpaqueExprNode, ExprNode);
+};
+
+/*! \brief Managed reference to OpaqueExprNode. */
+class OpaqueExpr : public Expr {
+ public:
+ explicit OpaqueExpr(ffi::ObjectPtr<OpaqueExprNode> node) :
Expr(std::move(node)) {}
+
+ TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(OpaqueExpr, Expr,
OpaqueExprNode);
+};
+
+/*!
+ * \brief Typed reference/view over an expression whose result type is a
+ * specific Type subtype.
+ * \tparam ExpectedType The expected expression result type.
+ */
+template <typename ExpectedType>
+class TypedExpr : public Expr {
+ public:
+ /*! \return the typed result of this expression. */
+ ExpectedType ty() const {
+ const auto* node = get();
+ TVM_FFI_DCHECK(node != nullptr);
+ const auto* ty_node = node->ExprNode::ty.template as<typename
ExpectedType::ContainerType>();
+ TVM_FFI_DCHECK(ty_node != nullptr);
+ return ffi::GetRef<ExpectedType>(ty_node);
+ }
+
+ explicit TypedExpr(ffi::ObjectPtr<ExprNode> node) : Expr(std::move(node)) {}
+
+ TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(TypedExpr, Expr, ExprNode);
+ static constexpr bool _type_container_is_exact = false;
+};
+
+/*!
+ * \brief Typed reference/view over any Expr whose `ExprNode::ty` is PrimType.
+ *
+ * PrimExpr is a type category rather than a dedicated runtime node category.
+ * It can contain intrinsic primitive nodes such as IntImmNode and
FloatImmNode,
+ * or a general ExprNode such as CallNode, when that expression's `ty` field is
+ * a PrimType. This keeps primitive-only APIs explicit while allowing shared
+ * Expr nodes for cross-dialect values with richer result types when needed.
+ */
+class PrimExpr : public TypedExpr<PrimType> {
+ public:
+ using TypedExpr<PrimType>::ty;
+
+ /*!
+ * \brief construct from integer.
+ * \param value The value to be constructed.
+ */
+ TVM_TIRX_DLL PrimExpr(int32_t value); // NOLINT(*)
+ /*!
+ * \brief construct from float.
+ * \param value The value to be constructed.
+ */
+ TVM_TIRX_DLL PrimExpr(float value); // NOLINT(*)
+
+ explicit PrimExpr(ffi::ObjectPtr<ExprNode> node) :
TypedExpr<PrimType>(std::move(node)) {}
+
+ TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(PrimExpr, TypedExpr<PrimType>,
ExprNode);
+ static constexpr bool _type_container_is_exact = false;
+};
+
+/*!
+ * \brief View over a scalar signed or unsigned integer expression of any bit
width.
+ *
+ * IntExpr validates integer operands in operator signatures without changing
the
+ * underlying expression node or its PrimExpr implementation API.
+ */
+class IntExpr : public PrimExpr {
+ public:
+ TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(IntExpr, PrimExpr, ExprNode);
+ static constexpr bool _type_container_is_exact = false;
+};
+
+namespace ffi::details {
+// 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::details
+
/*! \brief Base node for literal constants. */
class ConstantNode : public ExprNode {
public:
@@ -803,33 +979,6 @@ TVM_TIRX_DLL CallEffectKind SideEffect(const Expr& expr);
namespace ffi {
-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;
- if (src->type_index == TypeIndex::kTVMFFINone) return true;
- const auto* expr =
details::ObjectUnsafe::RawObjectPtrFromUnowned<ExprNode>(src->v_obj);
- const auto* ty = expr->ty.as<PrimTypeNode>();
- 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<tvm::StringImm> = false;
@@ -874,6 +1023,114 @@ inline constexpr bool object_ref_contains_v<PrimExpr,
FloatImmNode> = true;
template <>
inline constexpr bool object_ref_contains_v<PrimExpr, TensorLoadNode> = 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 ||
+ !details::IsObjectInstance<ExprNode>(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 =
details::ObjectUnsafe::RawObjectPtrFromUnowned<ExprNode>(src->v_obj);
+ return details::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 &&
+ details::IsObjectInstance<ExprNode>(src->type_index)) {
+ const auto* expr =
details::ObjectUnsafe::RawObjectPtrFromUnowned<ExprNode>(src->v_obj);
+ if (expr->ty.defined()) {
+ const auto* prim_ty = expr->ty.as<PrimTypeNode>();
+ 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 =
details::ObjectUnsafe::RawObjectPtrFromUnowned<ExprNode>(src->v_obj);
+ const auto* ty = expr->ty.as<PrimTypeNode>();
+ 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;
@@ -889,9 +1146,6 @@ struct TypeTraits<PrimVar> : public
ObjectRefTypeTraitsBase<PrimVar> {
using Base::TypeStr;
TVM_FFI_INLINE static bool CheckAnyStrict(const TVMFFIAny* src) {
- if (src->type_index == TypeIndex::kTVMFFINone) {
- return PrimVar::_type_is_nullable;
- }
if (src->type_index != VarNode::RuntimeTypeIndex()) {
return false;
}
@@ -902,9 +1156,6 @@ struct TypeTraits<PrimVar> : public
ObjectRefTypeTraitsBase<PrimVar> {
TVM_FFI_INLINE static std::optional<PrimVar> TryCastFromAnyView(const
TVMFFIAny* src) {
if (CheckAnyStrict(src)) {
- if (src->type_index == TypeIndex::kTVMFFINone) {
- return details::ObjectUnsafe::ObjectRefFromObjectPtr<PrimVar>(nullptr);
- }
return details::ObjectUnsafe::ObjectRefFromObjectPtr<PrimVar>(
details::ObjectUnsafe::ObjectPtrFromUnowned<VarNode>(src->v_obj));
}
@@ -917,11 +1168,30 @@ inline constexpr bool
use_default_type_traits_v<TensorRegion> = false;
template <>
struct TypeTraits<TensorRegion> : public
ObjectRefWithFallbackTraitsBase<TensorRegion, TensorLoad> {
- TVM_TIRX_DLL static TensorRegion ConvertFallbackValue(TensorLoad value);
+ TVM_FFI_INLINE static TensorRegion ConvertFallbackValue(TensorLoad value) {
+ return TensorRegion::FromTensorLoad(std::move(value));
+ }
};
} // namespace ffi
+/*! \brief Expression subscripts: point indices or (start, stop, step) slices.
*/
+using SubscriptSlice = ffi::Array<ffi::Variant<
+ ffi::Tuple<ffi::Optional<PrimExpr>, ffi::Optional<PrimExpr>,
ffi::Optional<PrimExpr>>,
+ PrimExpr>>;
+
+namespace type_attr {
+/*!
+ * \brief Realize a subscript of an expression, dispatched on the type of the
expression.
+ *
+ * The callback receives the expression, its point indices or slices, and the
source location.
+ * It returns a defined object representing the realized subscription.
+ */
+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
diff --git a/include/tvm/ir/type.h b/include/tvm/ir/type.h
index b961b975..cada269a 100644
--- a/include/tvm/ir/type.h
+++ b/include/tvm/ir/type.h
@@ -27,7 +27,6 @@
#include <tvm/ffi/container/array.h>
#include <tvm/ffi/dtype.h>
#include <tvm/ffi/reflection/registry.h>
-#include <tvm/ir/base_expr.h>
#include <tvm/ir/location.h>
#include <tvm/runtime/memory_scope.h>
@@ -35,6 +34,295 @@
namespace tvm {
+/*!
+ * \brief Type is the base type of all types.
+ *
+ * TVM's type system contains following subclasses:
+ *
+ * - PrimType: type of primitive type values used in the low-level IR.
+ * - FuncType: type of a function.
+ * - TensorType: type of certain Tensor values in the expression.
+ *
+ * There are also advanced types to support generic(polymorphic types).
+ * \sa Type
+ */
+class TypeNode : public ffi::Object {
+ public:
+ /*!
+ * \brief Location that points to the original source code.
+ * Reserved debug information.
+ */
+ mutable Location loc = UnknownLoc();
+
+ static void RegisterReflection() {
+ namespace refl = tvm::ffi::reflection;
+ // loc do not participate in structural equal and hash.
+ refl::ObjectDef<TypeNode>().def_ro("loc", &TypeNode::loc,
refl::DefaultValue(UnknownLoc()),
+ refl::init(false),
refl::AttachFieldFlag::SEqHashIgnore());
+ }
+
+ static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind =
kTVMFFISEqHashKindTreeNode;
+ static constexpr bool _type_s_eq_hash_subclass_kind_fixed = true;
+
+ static constexpr const uint32_t _type_child_slots = 14;
+ TVM_FFI_DECLARE_OBJECT_INFO("ir.Type", TypeNode, ffi::Object);
+};
+
+/*!
+ * \brief Managed reference to TypeNode.
+ * \sa TypeNode
+ */
+class Type : public ffi::ObjectRef {
+ public:
+ /*! \brief Construct a MissingType for type information not yet populated. */
+ TVM_TIRX_DLL static Type Missing();
+
+ TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Type, ffi::ObjectRef,
TypeNode);
+};
+
+/*!
+ * \brief Type information that has not been supplied or computed.
+ *
+ * MissingType is not a concrete type, wildcard, or inference variable. Unlike
+ * AnyType and Void, it must be resolved before a fully typed IR boundary.
+ */
+class MissingTypeNode final : public TypeNode {
+ public:
+ static void RegisterReflection() {
+ namespace refl = tvm::ffi::reflection;
+ refl::ObjectDef<MissingTypeNode>();
+ }
+
+ TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.MissingType", MissingTypeNode,
TypeNode);
+};
+
+/*! \brief Managed reference to the MissingTypeNode singleton. */
+class MissingType final : public Type {
+ public:
+ TVM_TIRX_DLL MissingType();
+
+ TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(MissingType, Type,
MissingTypeNode);
+};
+
+/*!
+ * \brief Type marker for opaque construction-time expressions.
+ *
+ * Opaque values may be used while constructing IR, but must be lowered away
+ * before the IR is considered complete.
+ */
+class OpaqueTypeNode final : public TypeNode {
+ public:
+ static void RegisterReflection() {
+ namespace refl = tvm::ffi::reflection;
+ refl::ObjectDef<OpaqueTypeNode>();
+ }
+
+ TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.OpaqueType", OpaqueTypeNode, TypeNode);
+};
+
+/*! \brief Managed reference to OpaqueTypeNode. */
+class OpaqueType final : public Type {
+ public:
+ TVM_TIRX_DLL OpaqueType();
+
+ TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(OpaqueType, Type,
OpaqueTypeNode);
+};
+
+/*!
+ * \brief Primitive data types used in the low-level IR.
+ *
+ * PrimType represents primitive POD values and the void sentinel.
+ *
+ * \sa PrimType
+ */
+class PrimTypeNode final : public TypeNode {
+ public:
+ /*!
+ * \brief The raw DLPack dtype represented by this primitive type.
+ */
+ DLDataType dtype;
+
+ static void RegisterReflection() {
+ namespace refl = tvm::ffi::reflection;
+ refl::ObjectDef<PrimTypeNode>().def_ro("dtype", &PrimTypeNode::dtype);
+ }
+ TVM_FFI_DECLARE_OBJECT_INFO_FINAL("ir.PrimType", PrimTypeNode, TypeNode);
+};
+
+/*
+ * \brief Managed reference to PrimTypeNode.
+ * \sa PrimTypeNode
+ */
+class PrimType final : public Type {
+ public:
+ /*!
+ * \brief Construct from a raw DLPack dtype.
+ * \param dtype The corresponding DLPack dtype.
+ */
+ TVM_TIRX_DLL explicit PrimType(DLDataType dtype);
+
+ /*!
+ * \brief Construct from DLPack dtype fields.
+ * \param code The DLPack dtype code.
+ * \param bits The scalar bit width.
+ * \param lanes The fixed lane count.
+ */
+ TVM_TIRX_DLL PrimType(DLDataTypeCode code, int bits, int lanes = 1);
+
+ /*! \brief Construct a signed integer type with fixed lanes. */
+ TVM_TIRX_DLL static PrimType Int(int bits, int lanes = 1);
+ /*! \brief Construct an unsigned integer type with fixed lanes. */
+ TVM_TIRX_DLL static PrimType UInt(int bits, int lanes = 1);
+ /*! \brief Construct a floating-point type with fixed lanes. */
+ TVM_TIRX_DLL static PrimType Float(int bits, int lanes = 1);
+ /*! \brief Construct a bfloat type with fixed lanes. */
+ TVM_TIRX_DLL static PrimType BFloat(int bits, int lanes = 1);
+ /*! \brief Construct a boolean type with fixed lanes. */
+ TVM_TIRX_DLL static PrimType Bool(int lanes = 1);
+ /*! \brief Construct the void sentinel type, encoded as handle(0, 0). */
+ TVM_TIRX_DLL static PrimType Void();
+ /*!
+ * \brief Construct a scalable vector type.
+ * \param code The DLPack dtype code.
+ * \param bits The scalar bit width.
+ * \param lanes The positive vscale factor to encode in the DLPack lane
field.
+ */
+ TVM_TIRX_DLL static PrimType ScalableVector(DLDataTypeCode code, int bits,
int lanes);
+
+ /*! \return The DLPack dtype code. */
+ TVM_FFI_INLINE DLDataTypeCode code() const {
+ return static_cast<DLDataTypeCode>(static_cast<int>(get()->dtype.code));
+ }
+
+ /*! \return The scalar bit width. */
+ TVM_FFI_INLINE int32_t bits() const { return get()->dtype.bits; }
+
+ /*!
+ * \return The fixed lane count.
+ * \note Throws on scalable vector types, where the encoded lane field
stores a vscale factor.
+ */
+ TVM_FFI_INLINE int32_t lanes() const {
+ int16_t encoded_lanes = static_cast<int16_t>(get()->dtype.lanes);
+ if (TVM_FFI_PREDICT_FALSE(encoded_lanes < 0)) {
+ TVM_FFI_THROW(InternalError)
+ << "Can't fetch the lanes of a scalable vector at a compile time.";
+ }
+ return encoded_lanes;
+ }
+
+ /*!
+ * \brief Check the scalar element code and bit width.
+ * \note Lane count and scalable-vector encoding are intentionally ignored.
+ */
+ TVM_FFI_INLINE bool MatchesElementType(DLDataTypeCode code, int bits) const {
+ DLDataType dtype = get()->dtype;
+ return dtype.code == static_cast<uint8_t>(code) && dtype.bits == bits;
+ }
+
+ /*!
+ * \brief Check whether the dtype code matches any of the provided DLPack
codes.
+ * \note Bit width and lanes are intentionally ignored.
+ */
+ template <typename... Codes>
+ TVM_FFI_INLINE bool MatchesCode(Codes... codes) const {
+ uint8_t dtype_code = get()->dtype.code;
+ return ((dtype_code == static_cast<uint8_t>(codes)) || ...);
+ }
+
+ /*! \brief Whether this type is a scalar, excluding fixed and scalable
vectors. */
+ TVM_FFI_INLINE bool IsScalar() const {
+ int16_t encoded_lanes = static_cast<int16_t>(get()->dtype.lanes);
+ return encoded_lanes == 1;
+ }
+
+ /*! \brief Whether this type is the void sentinel `handle(0, 0)`. */
+ TVM_FFI_INLINE bool IsVoid() const {
+ DLDataType dtype = get()->dtype;
+ return dtype.code == static_cast<uint8_t>(DLDataTypeCode::kDLOpaqueHandle)
&& dtype.bits == 0 &&
+ static_cast<int16_t>(dtype.lanes) == 0;
+ }
+
+ /*! \brief Whether this type is a scalable vector. */
+ TVM_FFI_INLINE bool IsScalableVector() const {
+ return static_cast<int16_t>(get()->dtype.lanes) < -1;
+ }
+
+ /*! \brief Whether this type is a fixed-length vector. */
+ TVM_FFI_INLINE bool IsFixedLengthVector() const {
+ return static_cast<int16_t>(get()->dtype.lanes) > 1;
+ }
+
+ /*!
+ * \brief Return the number of bytes needed to store one value of this type.
+ *
+ * This uses the same packed sub-byte dtype sizing rule as runtime tensors.
+ * Scalable vector types have no compile-time storage size and are rejected.
+ */
+ TVM_FFI_INLINE size_t StorageBytes() const {
+ DLDataType dtype = get()->dtype;
+ int16_t encoded_lanes = static_cast<int16_t>(dtype.lanes);
+ if (TVM_FFI_PREDICT_FALSE(encoded_lanes < 0)) {
+ TVM_FFI_THROW(InternalError)
+ << "Cannot compute compile-time storage bytes for non-fixed vector
type " << dtype;
+ }
+ return static_cast<size_t>(
+ (static_cast<uint64_t>(dtype.bits) *
static_cast<uint64_t>(dtype.lanes) + 7) / 8);
+ }
+
+ /*! \brief Return the same type with a different dtype code, preserving bits
and lanes. */
+ TVM_FFI_INLINE PrimType WithCode(DLDataTypeCode code) const {
+ DLDataType dtype = get()->dtype;
+ int16_t encoded_lanes = static_cast<int16_t>(dtype.lanes);
+ if (encoded_lanes < -1) {
+ return ScalableVector(code, dtype.bits, -encoded_lanes);
+ }
+ return PrimType(code, dtype.bits, encoded_lanes);
+ }
+
+ /*! \brief Return the same type with a different scalar bit width,
preserving code and lanes. */
+ TVM_FFI_INLINE PrimType WithBits(int bits) const {
+ DLDataType dtype = get()->dtype;
+ int16_t encoded_lanes = static_cast<int16_t>(dtype.lanes);
+ if (encoded_lanes < -1) {
+ return ScalableVector(this->code(), bits, -encoded_lanes);
+ }
+ return PrimType(this->code(), bits, encoded_lanes);
+ }
+
+ /*! \brief Return the same scalar element type with a fixed lane count. */
+ TVM_FFI_INLINE PrimType WithLanes(int lanes) const {
+ return PrimType(this->code(), this->bits(), lanes);
+ }
+
+ /*! \return The vscale factor encoded in a scalable vector type. */
+ TVM_FFI_INLINE int32_t VScaleFactor() const {
+ int16_t encoded_lanes = static_cast<int16_t>(get()->dtype.lanes);
+ if (encoded_lanes >= -1) {
+ TVM_FFI_THROW(InternalError) << "A fixed length vector doesn't have a
vscale factor.";
+ }
+ return -encoded_lanes;
+ }
+
+ TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(PrimType, Type, PrimTypeNode);
+};
+
+inline bool operator==(const PrimType& lhs, const PrimType& rhs) {
+ return lhs->dtype == rhs->dtype;
+}
+
+inline bool operator!=(const PrimType& lhs, const PrimType& rhs) { return
!(lhs == rhs); }
+
+namespace ffi {
+template <>
+inline constexpr bool use_default_type_traits_v<PrimType> = false;
+
+template <>
+struct TypeTraits<PrimType> : public ObjectRefWithFallbackTraitsBase<PrimType,
DLDataType> {
+ TVM_FFI_INLINE static PrimType ConvertFallbackValue(DLDataType dtype) {
return PrimType(dtype); }
+};
+
+} // namespace ffi
+
/*! \brief The type whose only value is None. */
class NoneTypeNode : public TypeNode {
public:
diff --git a/src/backend/cuda/transform/lower_iket.cc
b/src/backend/cuda/transform/lower_iket.cc
index 70e395b4..aa056263 100644
--- a/src/backend/cuda/transform/lower_iket.cc
+++ b/src/backend/cuda/transform/lower_iket.cc
@@ -1097,14 +1097,16 @@ class InstrumentOfficialKernel : public StmtExprMutator
{
PrimExpr Event(PrimExpr event_id) const {
static const Op event_op = Op::Get("tirx.cuda.iket_official_event");
return Call(PrimType::UInt(32), event_op,
- {prim::cast(event_id, PrimType::UInt(32)),
StringImm(device_source_)});
+ {prim::cast(event_id, PrimType::UInt(32)),
StringImm(device_source_)})
+ .as_or_throw<PrimExpr>();
}
PrimExpr Event(PrimExpr event_id, PrimExpr payload) const {
static const Op event_op = Op::Get("tirx.cuda.iket_official_event");
- return Call(
- PrimType::UInt(32), event_op,
- {prim::cast(event_id, PrimType::UInt(32)), StringImm(device_source_),
std::move(payload)});
+ return Call(PrimType::UInt(32), event_op,
+ {prim::cast(event_id, PrimType::UInt(32)),
StringImm(device_source_),
+ std::move(payload)})
+ .as_or_throw<PrimExpr>();
}
PrimExpr NormalizePayload(PrimExpr payload, PayloadType type) const {
diff --git a/src/ir/expr.cc b/src/ir/expr.cc
index a23aa87f..3ac973f7 100644
--- a/src/ir/expr.cc
+++ b/src/ir/expr.cc
@@ -39,43 +39,6 @@
namespace tvm {
-namespace ffi {
-
-Expr TypeTraits<Expr>::ConvertFallbackValue(ffi::String value) {
- return StringImm(std::move(value));
-}
-
-std::optional<Expr> TypeTraits<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;
-}
-
-std::optional<PrimExpr> TypeTraits<PrimExpr>::TryCastFromAnyView(const
TVMFFIAny* src) {
- // A required expression cannot accept None.
- if (src->type_index == TypeIndex::kTVMFFINone) return std::nullopt;
- return Base::TryCastFromAnyView(src);
-}
-
-PrimExpr TypeTraits<PrimExpr>::ConvertFallbackValue(StrictBool value) {
- return IntImm::Bool(value);
-}
-
-PrimExpr TypeTraits<PrimExpr>::ConvertFallbackValue(int64_t value) {
- return TypeTraits<IntImm>::ConvertFallbackValue(value);
-}
-
-PrimExpr TypeTraits<PrimExpr>::ConvertFallbackValue(double value) {
- return TypeTraits<FloatImm>::ConvertFallbackValue(value);
-}
-
-} // namespace ffi
-
namespace {
template <typename TNode>
@@ -913,8 +876,6 @@ TVM_FFI_STATIC_INIT_BLOCK() {
});
}
-PrimExpr::PrimExpr(Call call) :
PrimExpr(std::move(call).as_or_throw<PrimExpr>()) {}
-
PrimExpr::PrimExpr(int32_t value) : PrimExpr(IntImm::Int32(value)) {}
PrimExpr::PrimExpr(float value) : PrimExpr(FloatImm(PrimType::Float(32),
value)) {}
diff --git a/src/tirx/ir/ir_mutator_with_analyzer.cc
b/src/tirx/ir/ir_mutator_with_analyzer.cc
index 4101094c..fd1e22bb 100644
--- a/src/tirx/ir/ir_mutator_with_analyzer.cc
+++ b/src/tirx/ir/ir_mutator_with_analyzer.cc
@@ -234,8 +234,9 @@ UnchangedOr<Stmt>
StmtExprMutatorWithAnalyzer::Mutate_(const RegionStmtNode* op,
if (field.rfind("grid.", 0) == 0) intrinsic = "tirx.cuda.block_idx";
if (field.rfind("block.", 0) == 0) intrinsic = "tirx.cuda.thread_idx";
if (intrinsic.empty()) continue;
- PrimExpr index = Call(PrimType::Int(32), Op::Get(intrinsic),
- {StringImm(field.substr(field.size() - 1))});
+ PrimExpr index =
+ Call(PrimType::Int(32), Op::Get(intrinsic),
{StringImm(field.substr(field.size() - 1))})
+ .as_or_throw<PrimExpr>();
constraint_scope_.Current().Emplace(analyzer_, index >= 0);
constraint_scope_.Current().Emplace(analyzer_, index <
op->args[i].as_or_throw<PrimExpr>());
}
diff --git a/src/tirx/ir/tensor.cc b/src/tirx/ir/tensor.cc
index 2537e867..e8e7b3bc 100644
--- a/src/tirx/ir/tensor.cc
+++ b/src/tirx/ir/tensor.cc
@@ -48,10 +48,6 @@ TensorRegion TensorRegion::FromTensorLoad(TensorLoad value) {
value->loc);
}
-TensorRegion ffi::TypeTraits<TensorRegion>::ConvertFallbackValue(TensorLoad
value) {
- return TensorRegion::FromTensorLoad(std::move(value));
-}
-
namespace tirx {
using namespace tvm::prim;
diff --git a/src/tirx/transform/vectorize_loop.cc
b/src/tirx/transform/vectorize_loop.cc
index d21ffde0..b0428b2b 100644
--- a/src/tirx/transform/vectorize_loop.cc
+++ b/src/tirx/transform/vectorize_loop.cc
@@ -299,7 +299,8 @@ class TryPredicateTensorAccesses : public StmtExprMutator {
ffi::Array<Expr> args{load->source.as_or_throw<tvm::tirx::TensorVar>()};
for (const PrimExpr& index : load->indices) args.push_back(index);
args.push_back(mask.value());
- return Call(load->ty, tirx::masked_load_op(), args, {}, {}, load->loc);
+ return Call(load->ty, tirx::masked_load_op(), args, {}, {}, load->loc)
+ .as_or_throw<PrimExpr>();
}
return load;
}
diff --git a/tests/cpp/ir/pattern_match_test.cc
b/tests/cpp/ir/pattern_match_test.cc
index 24477477..d2c7073e 100644
--- a/tests/cpp/ir/pattern_match_test.cc
+++ b/tests/cpp/ir/pattern_match_test.cc
@@ -24,7 +24,8 @@
TEST(Pattern, Basic) {
using namespace tvm;
tvm::PrimVar x("x"), y("y"), z("z");
- PrimExpr scalable_lanes = prim::Mul(Call(PrimType::Int(32),
prim::vscale_op(), {}), 4);
+ PrimExpr scalable_lanes =
+ prim::Mul(Call(PrimType::Int(32), prim::vscale_op(),
{}).as_or_throw<PrimExpr>(), 4);
tvm::PVar<PrimExpr> px, py, pz;
tvm::PVar<DLDataType> pt;
tvm::PVar<PrimExpr> planes;