tvm
Loading...
Searching...
No Matches
object_functor.h
Go to the documentation of this file.
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
23#ifndef TVM_IR_OBJECT_FUNCTOR_H_
24#define TVM_IR_OBJECT_FUNCTOR_H_
25
26#include <tvm/ffi/error.h>
27#include <tvm/ffi/extra/structural_mutate.h>
28#include <tvm/ffi/extra/structural_visit.h>
29#include <tvm/runtime/base.h>
30
31#include <cstring>
32#include <type_traits>
33#include <utility>
34#include <vector>
35
36namespace tvm {
37
84template <typename FType>
86
87template <typename R, typename NodeArg, typename... Args>
88class ObjectFunctor<R(NodeArg, Args...)> {
89 static_assert(std::is_same_v<NodeArg, const ffi::ObjectRef&> ||
90 std::is_same_v<NodeArg, const ffi::Object*>,
91 "ObjectFunctor requires an ObjectRef reference or borrowed Object pointer");
92
93 public:
95 using result_type = R;
102 template <typename T>
103 TVM_FFI_INLINE bool CanDispatch(const T& n) const {
104 uint32_t type_index = GetTypeIndex(n);
105 if (type_index < begin_type_index_) return false;
106 type_index -= begin_type_index_;
107 return type_index < func_.size() && func_[type_index] != nullptr;
108 }
115 TVM_FFI_INLINE R operator()(NodeArg n, Args... args) const {
116 uint32_t type_index = n->type_index();
117 if (type_index >= begin_type_index_) {
118 uint32_t index = type_index - begin_type_index_;
119 if (index < func_.size() && func_[index] != nullptr) {
120 return (*func_[index])(n, std::forward<Args>(args)...);
121 }
122 }
123
124 const TVMFFITypeInfo* type_info = TVMFFIGetTypeInfo(type_index);
125 for (int32_t i = type_info->type_depth - 1; i >= 0; --i) {
126 type_index = type_info->type_ancestors[i]->type_index;
127 if (type_index >= begin_type_index_) {
128 uint32_t index = type_index - begin_type_index_;
129 if (index < func_.size() && func_[index] != nullptr) {
130 return (*func_[index])(n, std::forward<Args>(args)...);
131 }
132 }
133 }
134 ThrowUnregistered(n);
135 }
142 template <typename TNode>
143 ObjectFunctor& SetDispatch(R (*f)(NodeArg n, Args...)) {
144 static_assert(std::is_base_of_v<ffi::Object, TNode>,
145 "ObjectFunctor dispatch requires an object node type");
146 uint32_t tindex = TNode::RuntimeTypeIndex();
147 if (func_.size() <= tindex) {
148 func_.resize(tindex + 1, nullptr);
149 }
150 TVM_FFI_ICHECK(func_[tindex] == nullptr)
151 << "Dispatch for " << TNode::_type_key << " is already set";
152 TVM_FFI_ICHECK_EQ(begin_type_index_, 0) << " Cannot call SetDispatch after calling Finalize";
153 func_[tindex] = f;
154 return *this;
155 }
162 template <typename TNode>
164 uint32_t tindex = TNode::RuntimeTypeIndex();
165 TVM_FFI_ICHECK_LT(tindex, func_.size()) << "ClearDispatch: index out of range";
166 TVM_FFI_ICHECK_EQ(begin_type_index_, 0) << " Cannot call ClearDispatch after calling Finalize";
167 func_[tindex] = nullptr;
168 return *this;
169 }
175 void Finalize() {
176 TVM_FFI_ICHECK_EQ(begin_type_index_, 0) << "Can only call Finalize once";
177 while (begin_type_index_ < func_.size() && func_[begin_type_index_] == nullptr) {
178 ++begin_type_index_;
179 }
180 // shift up the function value
181 size_t new_ftable_size = func_.size() - begin_type_index_;
182 if (begin_type_index_ != 0) {
183 std::memmove(func_.data(), func_.data() + begin_type_index_,
184 new_ftable_size * sizeof(FPointer));
185 }
186 func_.resize(new_ftable_size);
187 func_.shrink_to_fit();
188 }
189
190 private:
191 // Map null pointers and undefined handles to None so CanDispatch rejects them without
192 // dereferencing.
193 TVM_FFI_INLINE static uint32_t GetTypeIndex(NodeArg n) {
194 if constexpr (std::is_pointer_v<NodeArg>) {
195 return n != nullptr ? n->type_index() : ffi::TypeIndex::kTVMFFINone;
196 } else {
197 return n.defined() ? n->type_index() : ffi::TypeIndex::kTVMFFINone;
198 }
199 }
200 TVM_FFI_INLINE static uint32_t GetTypeIndex(ffi::AnyView n) { return n.type_index(); }
201
202 [[noreturn]] TVM_FFI_COLD_CODE static void ThrowUnregistered(NodeArg n) {
203 TVM_FFI_THROW(InternalError) << "ObjectFunctor calls un-registered function on type "
204 << n->GetTypeKey();
205 TVM_FFI_UNREACHABLE();
206 }
207
209 using FPointer = R (*)(NodeArg n, Args...);
211 std::vector<FPointer> func_;
213 uint32_t begin_type_index_{0};
214};
215
216using ffi::Expected;
217using ffi::InplaceMode;
218using ffi::UnchangedOr;
219using ffi::VisitInterrupt;
220
228class TVM_TIRX_DLL ObjectVisitor : public ffi::StructuralVisitorObj {
229 public:
231 ObjectVisitor() : ObjectVisitor(GlobalVTable()) {}
232 ObjectVisitor(const ObjectVisitor& other) = delete;
233 ObjectVisitor& operator=(const ObjectVisitor& other) = delete;
234
242 virtual ffi::Optional<VisitInterrupt> Visit(ffi::AnyView value) {
243 if (native_vtable_->CanDispatch(value)) {
244 const auto* object =
245 ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const ffi::Object>(value);
246 try {
247 return (*native_vtable_)(object, this);
248 } catch (ffi::Error& error) {
249 ffi::details::UpdateVisitErrorContext(error, ffi::GetRef<ffi::ObjectRef>(object));
250 throw;
251 }
252 }
253 return StructuralVisitDefault(value);
254 }
255
263 Expected<ffi::Optional<VisitInterrupt>> VisitExpected(ffi::AnyView value) noexcept {
264 try {
265 return Visit(value);
266 } catch (ffi::Error& error) {
267 return ffi::Unexpected(std::move(error));
268 } catch (const std::exception& error) {
269 return ffi::Unexpected(ffi::Error("InternalError", error.what(), ""));
270 }
271 }
272
273 protected:
276
281 explicit ObjectVisitor(const VTable* vtable)
282 : ffi::StructuralVisitorObj(StructuralVTable()), native_vtable_(vtable) {}
287 static void InitVTable(VTable* vtable) {}
288
296 template <typename Self, typename Node>
297 static void SetDispatch(VTable* vtable) {
298 vtable->template SetDispatch<Node>(DispatchNode<Self, Node>);
299 }
300
301 private:
302 // Native table callback; core instantiations may be shared by the library.
303 template <typename Self, typename Node>
304 static ffi::Optional<VisitInterrupt> DispatchNode(const ffi::Object* node, ObjectVisitor* self) {
305 return static_cast<Self*>(self)->Visit_(static_cast<const Node*>(node));
306 }
307
308 ffi::Optional<VisitInterrupt> StructuralVisitDefault(ffi::AnyView value) {
309 try {
310 // DefaultVisitExpected does not name the current node; DefaultMutateExpected does.
311 return ffi::StructuralVisitorObj::DefaultVisitExpected(value).value();
312 } catch (ffi::Error& error) {
313 ffi::details::UpdateVisitErrorContext(error, value);
314 throw;
315 }
316 }
317 static const VTable* GlobalVTable() {
318 static const VTable table = [] {
319 VTable table;
320 InitVTable(&table);
321 table.Finalize();
322 return table;
323 }();
324 return &table;
325 }
326 static const ffi::StructuralVisitorVTable* StructuralVTable() {
327 static const ffi::StructuralVisitorVTable table{StructuralVTableVisitImpl};
328 return &table;
329 }
330 static TVMFFIAny StructuralVTableVisitImpl(ffi::StructuralVisitorObj* self,
331 ffi::AnyView value) noexcept {
332 return ffi::details::ExpectedUnsafe::MoveToTVMFFIAny(
333 static_cast<ObjectVisitor*>(self)->VisitExpected(value));
334 }
335
336 const VTable* const native_vtable_;
337};
338
346class TVM_TIRX_DLL ObjectMutator : public ffi::StructuralMapEngineBase {
347 public:
349 ObjectMutator() : ObjectMutator(GlobalVTable()) {}
350 ObjectMutator(const ObjectMutator& other) = delete;
351 ObjectMutator& operator=(const ObjectMutator& other) = delete;
352
380 virtual UnchangedOr<ffi::Any> Mutate(ffi::AnyView value,
381 InplaceMode inplace_mode = InplaceMode::kDisallow) {
382 const auto* object = value.as<ffi::Object>();
383 if (object == nullptr) return ffi::Any(value);
384 if (!object->unique()) inplace_mode = InplaceMode::kDisallow;
385 if (native_vtable_->CanDispatch(object)) {
386 try {
387 return (*native_vtable_)(object, this, inplace_mode);
388 } catch (ffi::Error& error) {
389 ffi::details::UpdateVisitErrorContext(error, ffi::GetRef<ffi::ObjectRef>(object));
390 throw;
391 }
392 }
393 // Default descent already supplies the current node's error context.
394 return ffi::StructuralMutatorObj::DefaultMutateExpected(value, inplace_mode).value();
395 }
396
405 Expected<UnchangedOr<ffi::Any>> MutateExpected(
406 ffi::AnyView value, InplaceMode inplace_mode = InplaceMode::kDisallow) noexcept {
407 try {
408 return Mutate(value, inplace_mode);
409 } catch (ffi::Error& error) {
410 return ffi::Unexpected(std::move(error));
411 } catch (const std::exception& error) {
412 return ffi::Unexpected(ffi::Error("InternalError", error.what(), ""));
413 }
414 }
415
417 ffi::Any VarRemapGet(ffi::AnyView var) { return VarRemapGetExpected(var).value(); }
419 void VarRemapSet(ffi::AnyView var, ffi::AnyView value) {
420 VarRemapSetExpected(var, value).value();
421 }
422
428 TVM_FFI_INLINE Expected<ffi::Any> VarRemapGetExpected(ffi::AnyView var) noexcept {
429 return VarRemapGetImpl(var);
430 }
437 TVM_FFI_INLINE Expected<void> VarRemapSetExpected(ffi::AnyView var,
438 ffi::AnyView mapped_value) noexcept {
439 return VarRemapSetImpl(var, mapped_value);
440 }
441
442 protected:
444 using VTable =
445 ObjectFunctor<UnchangedOr<ffi::Any>(const ffi::Object*, ObjectMutator*, InplaceMode)>;
446
451 explicit ObjectMutator(const VTable* vtable)
452 : ffi::StructuralMapEngineBase(StructuralVTable()), native_vtable_(vtable) {}
457 static void InitVTable(VTable* vtable) {}
458
466 template <typename Self, typename Node>
467 static void SetDispatch(VTable* vtable) {
468 vtable->template SetDispatch<Node>(DispatchNode<Self, Node>);
469 }
470
471 private:
472 // Native table callback; core instantiations may be shared by the library.
473 template <typename Self, typename Node>
474 static UnchangedOr<ffi::Any> DispatchNode(const ffi::Object* node, ObjectMutator* self,
475 InplaceMode inplace_mode) {
476 return ffi::details::UnchangedOrUnsafe::MoveFromTVMFFIAny<ffi::Any>(
477 ffi::details::UnchangedOrUnsafe::MoveToTVMFFIAny(
478 static_cast<Self*>(self)->Mutate_(static_cast<const Node*>(node), inplace_mode)));
479 }
480
481 using ffi::StructuralMutatorObj::DefaultMutateExpected;
482
483 static const VTable* GlobalVTable() {
484 static const VTable table = [] {
485 VTable table;
486 InitVTable(&table);
487 table.Finalize();
488 return table;
489 }();
490 return &table;
491 }
492 static const ffi::StructuralMutatorVTable* StructuralVTable() {
493 static const ffi::StructuralMutatorVTable table{
494 StructuralVTableMutateImpl, StructuralVTableMaybeInplaceMutateImpl,
495 StructuralVTableVarRemapGetImpl, StructuralVTableVarRemapSetImpl};
496 return &table;
497 }
498 static TVMFFIAny StructuralVTableMutateImpl(ffi::StructuralMutatorObj* self,
499 ffi::AnyView value) noexcept {
500 return ffi::details::ExpectedUnsafe::MoveToTVMFFIAny(
501 static_cast<ObjectMutator*>(self)->MutateExpected(value, InplaceMode::kDisallow));
502 }
503 static TVMFFIAny StructuralVTableMaybeInplaceMutateImpl(ffi::StructuralMutatorObj* self,
504 ffi::AnyView value) noexcept {
505 return ffi::details::ExpectedUnsafe::MoveToTVMFFIAny(
506 static_cast<ObjectMutator*>(self)->MutateExpected(value, InplaceMode::kAllow));
507 }
508 static TVMFFIAny StructuralVTableVarRemapGetImpl(ffi::StructuralMutatorObj* self,
509 ffi::AnyView key) noexcept {
510 return ffi::details::ExpectedUnsafe::MoveToTVMFFIAny(
511 static_cast<ObjectMutator*>(self)->VarRemapGetImpl(key));
512 }
513 static TVMFFIAny StructuralVTableVarRemapSetImpl(ffi::StructuralMutatorObj* self,
514 ffi::AnyView key, ffi::AnyView value) noexcept {
515 return ffi::details::ExpectedUnsafe::MoveToTVMFFIAny(
516 static_cast<ObjectMutator*>(self)->VarRemapSetImpl(key, value));
517 }
518
519 const VTable* const native_vtable_;
520};
521
529#define TVM_DEFINE_OBJECT_FUNCTOR_DEFAULT_CONSTRUCTOR(Class, Parent) \
530 Class() \
531 : Parent([] { \
532 static const VTable table = [] { \
533 VTable table; \
534 Class::InitVTable(&table); \
535 table.Finalize(); \
536 return table; \
537 }(); \
538 return &table; \
539 }()) {}
540
541} // namespace tvm
542#endif // TVM_IR_OBJECT_FUNCTOR_H_
void Finalize()
Finalize the functor after calling sequence of SetDispatch This function will attempt to find the min...
Definition object_functor.h:175
ObjectFunctor & ClearDispatch()
unset the dispatcher for type TNode
Definition object_functor.h:163
TVM_FFI_INLINE bool CanDispatch(const T &n) const
Whether a dispatch function is registered for the exact runtime type.
Definition object_functor.h:103
ObjectFunctor & SetDispatch(R(*f)(NodeArg n, Args...))
set the dispatcher for type TNode
Definition object_functor.h:143
R result_type
the result type of this functor
Definition object_functor.h:95
TVM_FFI_INLINE R operator()(NodeArg n, Args... args) const
invoke the functor, dispatch on type of n
Definition object_functor.h:115
A dynamically dispatched functor on the type of the first argument.
Definition object_functor.h:85
Native mutation with exact dispatch and structural fallback.
Definition object_functor.h:346
ObjectMutator(const ObjectMutator &other)=delete
static void SetDispatch(VTable *vtable)
Register an exact node type in a fresh table initialized by its parent. Derived node types require se...
Definition object_functor.h:467
TVM_FFI_INLINE Expected< void > VarRemapSetExpected(ffi::AnyView var, ffi::AnyView mapped_value) noexcept
Record a replacement in the variable-remap environment.
Definition object_functor.h:437
TVM_FFI_INLINE Expected< ffi::Any > VarRemapGetExpected(ffi::AnyView var) noexcept
Look up a replacement in the variable-remap environment.
Definition object_functor.h:428
ObjectMutator()
Construct a mutator using structural fallback for every object.
Definition object_functor.h:349
ObjectMutator & operator=(const ObjectMutator &other)=delete
virtual UnchangedOr< ffi::Any > Mutate(ffi::AnyView value, InplaceMode inplace_mode=InplaceMode::kDisallow)
Mutate a borrowed value, throwing on failure.
Definition object_functor.h:380
ffi::Any VarRemapGet(ffi::AnyView var)
Look up a variable replacement, throwing on failure.
Definition object_functor.h:417
ObjectMutator(const VTable *vtable)
Construct a mutator with a finalized native dispatch table.
Definition object_functor.h:451
static void InitVTable(VTable *vtable)
Initialize the base registrations in a fresh mutable table.
Definition object_functor.h:457
Expected< UnchangedOr< ffi::Any > > MutateExpected(ffi::AnyView value, InplaceMode inplace_mode=InplaceMode::kDisallow) noexcept
Adapt the virtual throwing entrypoint to the structural ABI.
Definition object_functor.h:405
void VarRemapSet(ffi::AnyView var, ffi::AnyView value)
Record a variable replacement, throwing on failure.
Definition object_functor.h:419
Native visiting with exact dispatch and structural fallback.
Definition object_functor.h:228
ObjectVisitor(const ObjectVisitor &other)=delete
ObjectVisitor & operator=(const ObjectVisitor &other)=delete
static void InitVTable(VTable *vtable)
Initialize the base registrations in a fresh mutable table.
Definition object_functor.h:287
ObjectVisitor(const VTable *vtable)
Construct a visitor with a finalized native dispatch table.
Definition object_functor.h:281
static void SetDispatch(VTable *vtable)
Register an exact node type in a fresh table initialized by its parent. Derived node types require se...
Definition object_functor.h:297
virtual ffi::Optional< VisitInterrupt > Visit(ffi::AnyView value)
Visit a borrowed object or inline value, throwing on failure.
Definition object_functor.h:242
Expected< ffi::Optional< VisitInterrupt > > VisitExpected(ffi::AnyView value) noexcept
Adapt the virtual throwing entrypoint to the structural ABI.
Definition object_functor.h:263
ObjectVisitor()
Construct a visitor using structural fallback for every value.
Definition object_functor.h:231
An object that builds and maintains block scope and StmtSref mapping for Dependence analysis.
Definition attr.h:23
#define TVM_TIRX_DLL
Definition base.h:83