Skip to content
28 changes: 27 additions & 1 deletion src/ir/expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ TVMFFIAny RangeMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyVi
return ffi::Unchanged().CopyToTVMFFIAny();
}

// DataflowVarNode duplicates this protocol because structural hooks do not inherit. Keep the two
// hook triples in lockstep when changing remap, PrimType-skip, or definition-region behavior.
TVMFFIAny VarVisit(ffi::StructuralVisitorObj* visitor, ffi::AnyView value) noexcept {
// skips: name
const VarNode* self =
Expand Down Expand Up @@ -381,6 +383,22 @@ TVMFFIAny VarMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView
return ffi::details::UnchangedOrUnsafe::MoveToTVMFFIAny(std::move(result));
}

TVMFFIAny GlobalVarVisit(ffi::StructuralVisitorObj*, ffi::AnyView) noexcept {
// GlobalVar is a module-level symbol. name_hint is scalar identity and ty is derived from the
// referenced function, matching GlobalVarNode's custom structural equality/hash definition.
// It has no definition site where this hook could establish a VarRemap. A callback that renames
// GlobalVars is therefore responsible for returning one stable replacement per module symbol.
return ffi::AnyView(nullptr).CopyToTVMFFIAny();
}

TVMFFIAny GlobalVarMutate(ffi::StructuralMutatorObj*, ffi::AnyView) noexcept {
return ffi::Unchanged().CopyToTVMFFIAny();
}

TVMFFIAny GlobalVarMaybeInplaceMutate(ffi::StructuralMutatorObj*, ffi::AnyView) noexcept {
return ffi::Unchanged().CopyToTVMFFIAny();
}

TVMFFIAny CallVisit(ffi::StructuralVisitorObj* visitor, ffi::AnyView value) noexcept {
// skips: attrs, constant metadata left untouched like the classic Expr functors.
const CallNode* self =
Expand Down Expand Up @@ -859,7 +877,15 @@ GlobalVar::GlobalVar(ffi::String name_hint, Span span) {
data_ = std::move(n);
}

TVM_FFI_STATIC_INIT_BLOCK() { GlobalVarNode::RegisterReflection(); }
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
GlobalVarNode::RegisterReflection();
refl::TypeAttrDef<GlobalVarNode>()
.attr(refl::type_attr::kStructuralVisit, reinterpret_cast<void*>(&GlobalVarVisit))
.attr(refl::type_attr::kStructuralMutate, reinterpret_cast<void*>(&GlobalVarMutate))
.attr(refl::type_attr::kStructuralMaybeInplaceMutate,
reinterpret_cast<void*>(&GlobalVarMaybeInplaceMutate));
}

// Call
Call::Call(Type ret_ty, Expr op, ffi::Array<Expr> args, Attrs attrs, ffi::Array<Type> ty_args,
Expand Down
25 changes: 25 additions & 0 deletions src/ir/op.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
* \file src/ir/op.cc
* \brief Primitive operators and intrinsics.
*/
#include <tvm/ffi/extra/structural_mutate.h>
#include <tvm/ffi/extra/structural_visit.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ir/op.h>
Expand All @@ -33,9 +35,32 @@

namespace tvm {

namespace {

TVMFFIAny OpVisit(ffi::StructuralVisitorObj*, ffi::AnyView) noexcept {
// Ops are unique registry atoms. Avoid reflecting through their registry metadata.
return ffi::AnyView(nullptr).CopyToTVMFFIAny();
}

TVMFFIAny OpMutate(ffi::StructuralMutatorObj*, ffi::AnyView) noexcept {
return ffi::Unchanged().CopyToTVMFFIAny();
}

TVMFFIAny OpMaybeInplaceMutate(ffi::StructuralMutatorObj*, ffi::AnyView) noexcept {
return ffi::Unchanged().CopyToTVMFFIAny();
}

} // namespace

TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
ArgumentInfoNode::RegisterReflection();
OpNode::RegisterReflection();
refl::TypeAttrDef<OpNode>()
.attr(refl::type_attr::kStructuralVisit, reinterpret_cast<void*>(&OpVisit))
.attr(refl::type_attr::kStructuralMutate, reinterpret_cast<void*>(&OpMutate))
.attr(refl::type_attr::kStructuralMaybeInplaceMutate,
reinterpret_cast<void*>(&OpMaybeInplaceMutate));
}

using ffi::Any;
Expand Down
189 changes: 187 additions & 2 deletions src/ir/type.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,32 @@ ffi::ObjectPtr<PrimTypeNode> GetCachedPrimTypeNode(DLDataType dtype) {

// Structural traversal hooks

TVMFFIAny TypeVisit(ffi::StructuralVisitorObj*, ffi::AnyView) noexcept {
// Type::Missing() is the only concrete TypeNode value; span is ignored debug metadata.
return ffi::AnyView(nullptr).CopyToTVMFFIAny();
}

TVMFFIAny TypeMutate(ffi::StructuralMutatorObj*, ffi::AnyView) noexcept {
return ffi::Unchanged().CopyToTVMFFIAny();
}

TVMFFIAny TypeMaybeInplaceMutate(ffi::StructuralMutatorObj*, ffi::AnyView) noexcept {
return ffi::Unchanged().CopyToTVMFFIAny();
}

TVMFFIAny OpaqueTypeVisit(ffi::StructuralVisitorObj*, ffi::AnyView) noexcept {
// OpaqueType is a field-less construction-time marker; span is ignored debug metadata.
return ffi::AnyView(nullptr).CopyToTVMFFIAny();
}

TVMFFIAny OpaqueTypeMutate(ffi::StructuralMutatorObj*, ffi::AnyView) noexcept {
return ffi::Unchanged().CopyToTVMFFIAny();
}

TVMFFIAny OpaqueTypeMaybeInplaceMutate(ffi::StructuralMutatorObj*, ffi::AnyView) noexcept {
return ffi::Unchanged().CopyToTVMFFIAny();
}

TVMFFIAny PrimTypeVisit(ffi::StructuralVisitorObj*, ffi::AnyView) noexcept {
// dtype is a constant: reflected for StructuralEqual/Hash,
// not traversed by the visitor/mutator contract.
Expand All @@ -87,6 +113,129 @@ TVMFFIAny PrimTypeMaybeInplaceMutate(ffi::StructuralMutatorObj*, ffi::AnyView) n
return ffi::Unchanged().CopyToTVMFFIAny();
}

TVMFFIAny PointerTypeVisit(ffi::StructuralVisitorObj* visitor, ffi::AnyView value) noexcept {
// skips: storage_scope (scalar)
const PointerTypeNode* self =
ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const PointerTypeNode>(value);
TVM_FFI_S_VISIT_MAYBE_EARLY_RETURN(visitor->VisitExpected(self->element_type));
return ffi::AnyView(nullptr).CopyToTVMFFIAny();
}

TVMFFIAny PointerTypeMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView value) noexcept {
// skips: storage_scope (scalar)
const PointerTypeNode* self =
ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const PointerTypeNode>(value);
TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_element_type,
mutator->MutateExpected(self->element_type));
if (mapped_element_type.UnchangedOrSameAs(self->element_type)) {
return ffi::Unchanged().CopyToTVMFFIAny();
}
ffi::ObjectPtr<PointerTypeNode> copy = ffi::make_object<PointerTypeNode>(*self);
copy->element_type =
std::move(mapped_element_type).ValueOrUnchanged(std::move(copy->element_type));
return ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
}

TVMFFIAny PointerTypeMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
ffi::AnyView value) noexcept {
// skips: storage_scope (scalar)
PointerTypeNode* self = const_cast<PointerTypeNode*>(
ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const PointerTypeNode>(value));
TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
ffi::UnchangedOr<Type>, mapped_element_type,
mutator->MaybeInplaceMutateIfUniqueExpected(self->element_type));
if (!mapped_element_type.UnchangedOrSameAs(self->element_type)) {
self->element_type = std::move(mapped_element_type).ValueUnchecked();
}
return ffi::Unchanged().CopyToTVMFFIAny();
}

TVMFFIAny FuncTypeVisit(ffi::StructuralVisitorObj* visitor, ffi::AnyView value) noexcept {
const FuncTypeNode* self =
ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const FuncTypeNode>(value);
TVM_FFI_S_VISIT_MAYBE_EARLY_RETURN(visitor->VisitExpected(self->arg_types));
TVM_FFI_S_VISIT_MAYBE_EARLY_RETURN(visitor->VisitExpected(self->ret_type));
return ffi::AnyView(nullptr).CopyToTVMFFIAny();
}

TVMFFIAny FuncTypeMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView value) noexcept {
const FuncTypeNode* self =
ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const FuncTypeNode>(value);
TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Type>>, mapped_arg_types,
mutator->MutateExpected(self->arg_types));
TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ret_type,
mutator->MutateExpected(self->ret_type));
if (mapped_arg_types.UnchangedOrSameAs(self->arg_types) &&
mapped_ret_type.UnchangedOrSameAs(self->ret_type)) {
return ffi::Unchanged().CopyToTVMFFIAny();
}
ffi::ObjectPtr<FuncTypeNode> copy = ffi::make_object<FuncTypeNode>(*self);
copy->arg_types = std::move(mapped_arg_types).ValueOrUnchanged(std::move(copy->arg_types));
copy->ret_type = std::move(mapped_ret_type).ValueOrUnchanged(std::move(copy->ret_type));
return ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
}

TVMFFIAny FuncTypeMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
ffi::AnyView value) noexcept {
FuncTypeNode* self = const_cast<FuncTypeNode*>(
ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const FuncTypeNode>(value));
TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Type>>, mapped_arg_types,
mutator->MaybeInplaceMutateIfUniqueExpected(self->arg_types));
TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ret_type,
mutator->MaybeInplaceMutateIfUniqueExpected(self->ret_type));
if (!mapped_arg_types.UnchangedOrSameAs(self->arg_types)) {
self->arg_types = std::move(mapped_arg_types).ValueUnchecked();
}
if (!mapped_ret_type.UnchangedOrSameAs(self->ret_type)) {
self->ret_type = std::move(mapped_ret_type).ValueUnchecked();
}
return ffi::Unchanged().CopyToTVMFFIAny();
}

TVMFFIAny TupleTypeVisit(ffi::StructuralVisitorObj* visitor, ffi::AnyView value) noexcept {
const TupleTypeNode* self =
ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const TupleTypeNode>(value);
TVM_FFI_S_VISIT_MAYBE_EARLY_RETURN(visitor->VisitExpected(self->fields));
return ffi::AnyView(nullptr).CopyToTVMFFIAny();
}

TVMFFIAny TupleTypeMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView value) noexcept {
const TupleTypeNode* self =
ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const TupleTypeNode>(value);
TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Type>>, mapped_fields,
mutator->MutateExpected(self->fields));
if (mapped_fields.UnchangedOrSameAs(self->fields)) {
return ffi::Unchanged().CopyToTVMFFIAny();
}
ffi::ObjectPtr<TupleTypeNode> copy = ffi::make_object<TupleTypeNode>(*self);
copy->fields = std::move(mapped_fields).ValueOrUnchanged(std::move(copy->fields));
return ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
}

TVMFFIAny TupleTypeMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
ffi::AnyView value) noexcept {
TupleTypeNode* self = const_cast<TupleTypeNode*>(
ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const TupleTypeNode>(value));
TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Type>>, mapped_fields,
mutator->MaybeInplaceMutateIfUniqueExpected(self->fields));
if (!mapped_fields.UnchangedOrSameAs(self->fields)) {
self->fields = std::move(mapped_fields).ValueUnchecked();
}
return ffi::Unchanged().CopyToTVMFFIAny();
}

TVMFFIAny TensorMapTypeVisit(ffi::StructuralVisitorObj*, ffi::AnyView) noexcept {
return ffi::AnyView(nullptr).CopyToTVMFFIAny();
}

TVMFFIAny TensorMapTypeMutate(ffi::StructuralMutatorObj*, ffi::AnyView) noexcept {
return ffi::Unchanged().CopyToTVMFFIAny();
}

TVMFFIAny TensorMapTypeMaybeInplaceMutate(ffi::StructuralMutatorObj*, ffi::AnyView) noexcept {
return ffi::Unchanged().CopyToTVMFFIAny();
}

} // namespace

Type Type::Missing() {
Expand All @@ -102,9 +251,25 @@ bool Type::IsMissing() const { return this->same_as(Type::Missing()); }

OpaqueType::OpaqueType() : Type(ffi::UnsafeInit{}) { data_ = ffi::make_object<OpaqueTypeNode>(); }

TVM_FFI_STATIC_INIT_BLOCK() { TypeNode::RegisterReflection(); }
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
TypeNode::RegisterReflection();
refl::TypeAttrDef<TypeNode>()
.attr(refl::type_attr::kStructuralVisit, reinterpret_cast<void*>(&TypeVisit))
.attr(refl::type_attr::kStructuralMutate, reinterpret_cast<void*>(&TypeMutate))
.attr(refl::type_attr::kStructuralMaybeInplaceMutate,
reinterpret_cast<void*>(&TypeMaybeInplaceMutate));
}

TVM_FFI_STATIC_INIT_BLOCK() { OpaqueTypeNode::RegisterReflection(); }
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
OpaqueTypeNode::RegisterReflection();
refl::TypeAttrDef<OpaqueTypeNode>()
.attr(refl::type_attr::kStructuralVisit, reinterpret_cast<void*>(&OpaqueTypeVisit))
.attr(refl::type_attr::kStructuralMutate, reinterpret_cast<void*>(&OpaqueTypeMutate))
.attr(refl::type_attr::kStructuralMaybeInplaceMutate,
reinterpret_cast<void*>(&OpaqueTypeMaybeInplaceMutate));
}

// PrimType
PrimType::PrimType(DLDataType dtype) : Type(ffi::UnsafeInit{}) {
Expand Down Expand Up @@ -227,6 +392,11 @@ TVM_FFI_STATIC_INIT_BLOCK() {
refl::GlobalDef().def("ir.PointerType", [](Type element_type, ffi::String storage_scope = "") {
return PointerType(element_type, storage_scope);
});
refl::TypeAttrDef<PointerTypeNode>()
.attr(refl::type_attr::kStructuralVisit, reinterpret_cast<void*>(&PointerTypeVisit))
.attr(refl::type_attr::kStructuralMutate, reinterpret_cast<void*>(&PointerTypeMutate))
.attr(refl::type_attr::kStructuralMaybeInplaceMutate,
reinterpret_cast<void*>(&PointerTypeMaybeInplaceMutate));
}

TVM_FFI_STATIC_INIT_BLOCK() {
Expand All @@ -235,6 +405,11 @@ TVM_FFI_STATIC_INIT_BLOCK() {
refl::GlobalDef().def("ir.FuncType", [](tvm::ffi::Array<Type> arg_types, Type ret_type) {
return FuncType(arg_types, ret_type);
});
refl::TypeAttrDef<FuncTypeNode>()
.attr(refl::type_attr::kStructuralVisit, reinterpret_cast<void*>(&FuncTypeVisit))
.attr(refl::type_attr::kStructuralMutate, reinterpret_cast<void*>(&FuncTypeMutate))
.attr(refl::type_attr::kStructuralMaybeInplaceMutate,
reinterpret_cast<void*>(&FuncTypeMaybeInplaceMutate));
}

TVM_FFI_STATIC_INIT_BLOCK() {
Expand All @@ -245,6 +420,16 @@ TVM_FFI_STATIC_INIT_BLOCK() {
.def("ir.TupleType",
[](ffi::Array<Type> fields, Span span) { return TupleType(fields, span); })
.def("ir.TensorMapType", [](Span span) { return TensorMapType(span); });
refl::TypeAttrDef<TupleTypeNode>()
.attr(refl::type_attr::kStructuralVisit, reinterpret_cast<void*>(&TupleTypeVisit))
.attr(refl::type_attr::kStructuralMutate, reinterpret_cast<void*>(&TupleTypeMutate))
.attr(refl::type_attr::kStructuralMaybeInplaceMutate,
reinterpret_cast<void*>(&TupleTypeMaybeInplaceMutate));
refl::TypeAttrDef<TensorMapTypeNode>()
.attr(refl::type_attr::kStructuralVisit, reinterpret_cast<void*>(&TensorMapTypeVisit))
.attr(refl::type_attr::kStructuralMutate, reinterpret_cast<void*>(&TensorMapTypeMutate))
.attr(refl::type_attr::kStructuralMaybeInplaceMutate,
reinterpret_cast<void*>(&TensorMapTypeMaybeInplaceMutate));
}

} // namespace tvm
Loading
Loading