/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef TVM_SCRIPT_IR_BUILDER_BASE_H_ #define TVM_SCRIPT_IR_BUILDER_BASE_H_ #include #include #include #include #include namespace tvm { namespace script { namespace ir_builder { ////////////////////////////// IRBuilderFrame ////////////////////////////// /*! * \brief A stack frame of the IRBuilder used to keep track of the current scope. * Furthermore, the information stored in each stack frame can be useful for context-dependent * IR construction. * * \example * * The `T::MatchBuffer` below adds an element in `PrimFuncNode::buffer_map`: * * \code {.cpp} * * using T = tvm::script::ir_builder::tirx; * With _(...); * Buffer buffer = T::MatchBuffer(...); * * \endcode * * The `T::MatchBuffer` below instead generates `MatchBufferRegion` in a TIR block: * * \code {.cpp} * * using T = tvm::script::ir_builder::tirx; * With _(...); * { * With _2(...); * Buffer buffer = T::MatchBuffer(...); * } * * \endcode */ class IRBuilderFrameNode : public ffi::Object { public: /*! \brief A list of callbacks used when exiting the frame. */ std::vector> callbacks; static void RegisterReflection() { namespace refl = tvm::ffi::reflection; refl::ObjectDef(); // `callbacks` is not registered as it's not visited. } static constexpr const bool _type_mutable = true; TVM_FFI_DECLARE_OBJECT_INFO("script.ir_builder.IRBuilderFrame", IRBuilderFrameNode, ffi::Object); public: /*! \brief Default destructor. */ virtual ~IRBuilderFrameNode() = default; /*! * \brief The method called when entering RAII scope. * \sa tvm::support::With */ virtual void EnterWithScope(); /*! * \brief The method called when exiting RAII scope. * \sa tvm::support::With */ virtual void ExitWithScope(); /*! * \brief Add a callback method invoked when exiting the RAII scope. * \param callback The callback to be added. */ void AddCallback(ffi::TypedFunction callback); }; /*! * \brief Managed reference to an IRBuilderFrameNode. * \sa IRBuilderFrameNode */ class IRBuilderFrame : public ffi::ObjectRef { public: TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(IRBuilderFrame, ffi::ObjectRef, IRBuilderFrameNode); protected: /*! \brief Disallow direct construction of this object. */ IRBuilderFrame() = default; explicit IRBuilderFrame(ffi::ObjectPtr data) : ffi::ObjectRef(data) {} public: /*! * \brief Redirected to `IRBuilderFrameNode::EnterWithScope`. * \sa IRBuilderFrameNode::EnterWithScope */ inline void EnterWithScope() { TVM_FFI_ICHECK(data_ != nullptr); static_cast(data_.get())->EnterWithScope(); } /*! * \brief Redirected to `IRBuilderFrameNode::ExitWithScope`. * \sa IRBuilderFrameNode::ExitWithScope */ inline void ExitWithScope() { TVM_FFI_ICHECK(data_ != nullptr); static_cast(data_.get())->ExitWithScope(); data_.reset(); } }; ////////////////////////////// IRBuilder ////////////////////////////// /*! * \brief A dialect-agnostic IRBuilder that constructs any IR of TVM. * An idiomatic use of this class is to put this inside the RAII with-scope, * call dialect-specific methods accordingly. Upon exiting the scope. * * \code * * PrimFunc ConstructPrimFunc() { * using tvm::script::ir_builder::IRBuilder; * using T = tvm::script::ir_builder::tirx; * IRBuilder builder; * // Step 1. Place IRBuilder inside the with-scope. * { * With _(builder); * // Step 2. Call dialect-specific methods. * With _2(...); * T::MatchBuffer(...); * } * // Step 3. Return the constructed PrimFunc. * return builder->Get(); * } * * \endcode */ class IRBuilderNode : public ffi::Object { public: /*! \brief A stack of context frames in the IRBuilder */ ffi::Array frames; /*! \brief The outcome of IR construction */ ffi::Optional result; static void RegisterReflection() { namespace refl = tvm::ffi::reflection; refl::ObjectDef() .def_ro("frames", &IRBuilderNode::frames) .def_ro("result", &IRBuilderNode::result); } static constexpr const bool _type_mutable = true; TVM_FFI_DECLARE_OBJECT_INFO_FINAL("script.ir_builder.IRBuilder", IRBuilderNode, ffi::Object); public: /*! * \brief Find a frame of the given type in the stack `this->frames` from top to bottom. * \tparam T The type of the frame to find. * \return The frame if found, otherwise std::nullopt. */ template inline ffi::Optional FindFrame() const; /*! * \brief Get the frame on top of the stack `this->frames` if its type is `TFrame`. * \tparam TFrame The assumed type of the last frame on stack. * \return The frame if the stack is non-empty and the top of the stack is of type `TFrame`. * Otherwise std::nullopt. */ template inline ffi::Optional GetLastFrame() const; /*! * \brief Get the IR being constructed. * \tparam TObjectRef The type of the IR being constructed. * \return The resulting IR. Throw an exception if the IR is not constructed yet. */ template inline TObjectRef Get() const; }; /*! * \brief Managed reference to an IRBuilderNode. * \sa IRBuilderNode */ class IRBuilder : public ffi::ObjectRef { public: /*! \brief Creates an IRBuilder. */ IRBuilder(); TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(IRBuilder, ffi::ObjectRef, IRBuilderNode); public: /*! * \brief Puts the current IRBuilder into a thread-local scope, which can be retrieved using * `IRBuilder::Current()`. * * \code {.cpp} * IRBuilder builder; * { * With _(builder); * // IRBuilder::Current() == builder * } * // IRBuilder::Current() == nullptr * \endcode * * \sa IRBuilder::Current * \sa IRBuilder::ExitWithScope * \sa tvm::support::With */ void EnterWithScope(); /*! * \brief Exit the RAII scope. * \sa IRBuilder::EnterWithScope * \sa IRBuilder::Current * \sa tvm::support::With */ void ExitWithScope(); /*! * \brief Get the current IRBuilder in the current thread-local scope. * \return The current IRBuilder. * \sa IRBuilder::EnterWithScope * \sa IRBuilder::ExitWithScope * \sa tvm::support::With */ static IRBuilder Current(); /*! \brief See if the current thread-local scope has an IRBuilder. */ static bool IsInScope(); /*! * \brief Give a string name to the `obj` * \tparam TObjectRef The type of the object to name. * \param name The name to give to the object. * \param obj The object to name. */ template inline static TObjectRef Name(ffi::String name, TObjectRef obj); }; ////////////////////////////// Details ////////////////////////////// namespace details { class Namer { public: using FType = NodeFunctor; static FType& vtable(); static void Name(ffi::ObjectRef node, ffi::String name); }; } // namespace details template inline TObjectRef IRBuilder::Name(ffi::String name, TObjectRef obj) { details::Namer::Name(obj, name); return obj.template as_or_throw(); } template inline ffi::Optional IRBuilderNode::FindFrame() const { using TFrameNode = typename TFrame::ContainerType; for (auto it = frames.rbegin(); it != frames.rend(); ++it) { if (const TFrameNode* p = (*it).template as()) { return ffi::GetRef(p); } } return std::nullopt; } template inline ffi::Optional IRBuilderNode::GetLastFrame() const { using TFrameNode = typename TFrame::ContainerType; if (!frames.empty() && frames.back()->IsInstance()) { return frames.back().as_or_throw(); } return std::nullopt; } template inline TObjectRef IRBuilderNode::Get() const { using TObject = typename TObjectRef::ContainerType; TVM_FFI_CHECK(result.has_value(), IndexError) << "No result exists in IRBuilder yet"; const auto* n = result.as(); TVM_FFI_CHECK(n != nullptr, TypeError) << "IRBuilder result is not of type: " << TObject::_type_key; return ffi::GetRef(n); } } // namespace ir_builder } // namespace script } // namespace tvm #endif // TVM_SCRIPT_IR_BUILDER_BASE_H_