//===- StateStack.h - Utility for storing a stack of state ------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines utilities for storing a stack of generic context. // The context can be arbitrary data, possibly including file-scoped types. Data // must be derived from StateStackFrameBase and implement MLIR TypeID. // //===----------------------------------------------------------------------===// #ifndef MLIR_SUPPORT_STACKFRAME_H #define MLIR_SUPPORT_STACKFRAME_H #include "mlir/Support/TypeID.h" #include "mlir/Support/WalkResult.h" #include namespace mlir { /// Common CRTP base class for StateStack frames. class StateStackFrame { public: virtual ~StateStackFrame() = default; TypeID getTypeID() const { return typeID; } protected: explicit StateStackFrame(TypeID typeID) : typeID(typeID) {} private: const TypeID typeID; virtual void anchor(); }; /// Concrete CRTP base class for StateStack frames. This is used for keeping a /// stack of common state useful for recursive IR conversions. For example, when /// translating operations with regions, users of StateStack can store state on /// StateStack before entering the region and inspect it when converting /// operations nested within that region. Users are expected to derive this /// class and put any relevant information into fields of the derived class. The /// usual isa/dyn_cast functionality is available for instances of derived /// classes. template class StateStackFrameBase : public StateStackFrame { public: explicit StateStackFrameBase() : StateStackFrame(TypeID::get()) {} }; class StateStack { public: /// Creates a stack frame of type `T` on StateStack. `T` must /// be derived from `StackFrameBase` and constructible from the provided /// arguments. Doing this before entering the region of the op being /// translated makes the frame available when translating ops within that /// region. template void stackPush(Args &&...args) { static_assert(std::is_base_of::value, "can only push instances of StackFrame on StateStack"); stack.push_back(std::make_unique(std::forward(args)...)); } /// Pops the last element from the StateStack. void stackPop() { stack.pop_back(); } /// Calls `callback` for every StateStack frame of type `T` /// starting from the top of the stack. template WalkResult stackWalk(llvm::function_ref callback) { static_assert(std::is_base_of::value, "expected T derived from StackFrame"); if (!callback) return WalkResult::skip(); for (std::unique_ptr &frame : llvm::reverse(stack)) { if (T *ptr = dyn_cast_or_null(frame.get())) { WalkResult result = callback(*ptr); if (result.wasInterrupted()) return result; } } return WalkResult::advance(); } /// Get the top instance of frame type `T` or nullptr if none are found template T *getStackTop() { T *top = nullptr; stackWalk([&](T &frame) -> mlir::WalkResult { top = &frame; return mlir::WalkResult::interrupt(); }); return top; } private: SmallVector> stack; }; /// RAII object calling stackPush/stackPop on construction/destruction. /// HostClass could be a StateStack or some other class which forwards calls to /// one. template struct SaveStateStack { template explicit SaveStateStack(HostClass &host, Args &&...args) : host(host) { host.template stackPush(std::forward(args)...); } ~SaveStateStack() { host.stackPop(); } private: HostClass &host; }; } // namespace mlir namespace llvm { template struct isa_impl { static inline bool doit(const ::mlir::StateStackFrame &frame) { return frame.getTypeID() == ::mlir::TypeID::get(); } }; } // namespace llvm #endif // MLIR_SUPPORT_STACKFRAME_H