1 //===- DebugAction.h - Debug Action Support ---------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file contains definitions for the debug action framework. This framework 10 // allows for external entities to control certain actions taken by the compiler 11 // by registering handler functions. A debug action handler provides the 12 // internal implementation for the various queries on a debug action, such as 13 // whether it should execute or not. 14 // 15 //===----------------------------------------------------------------------===// 16 17 #ifndef MLIR_SUPPORT_DEBUGACTION_H 18 #define MLIR_SUPPORT_DEBUGACTION_H 19 20 #include "mlir/Support/LogicalResult.h" 21 #include "mlir/Support/TypeID.h" 22 #include "llvm/ADT/ArrayRef.h" 23 #include "llvm/ADT/Sequence.h" 24 #include "llvm/ADT/StringMap.h" 25 #include "llvm/Support/TypeName.h" 26 #include "llvm/Support/raw_ostream.h" 27 #include <functional> 28 29 namespace mlir { 30 31 //===----------------------------------------------------------------------===// 32 // DebugActionManager 33 //===----------------------------------------------------------------------===// 34 35 /// This class represents manages debug actions, and orchestrates the 36 /// communication between action queries and action handlers. An action handler 37 /// is either an action specific handler, i.e. a derived class of 38 /// `MyActionType::Handler`, or a generic handler, i.e. a derived class of 39 /// `DebugActionManager::GenericHandler`. For more details on action specific 40 /// handlers, see the definition of `DebugAction::Handler` below. For more 41 /// details on generic handlers, see `DebugActionManager::GenericHandler` below. 42 class DebugActionManager { 43 public: 44 //===--------------------------------------------------------------------===// 45 // Handlers 46 //===--------------------------------------------------------------------===// 47 48 /// This class represents the base class of a debug action handler. 49 class HandlerBase { 50 public: 51 virtual ~HandlerBase() = default; 52 53 /// Return the unique handler id of this handler, use for casting 54 /// functionality. getHandlerID()55 TypeID getHandlerID() const { return handlerID; } 56 57 protected: HandlerBase(TypeID handlerID)58 HandlerBase(TypeID handlerID) : handlerID(handlerID) {} 59 60 /// The type of the derived handler class. This allows for detecting if a 61 /// handler can handle a given action type. 62 TypeID handlerID; 63 }; 64 65 /// This class represents a generic action handler. A generic handler allows 66 /// for handling any action type. Handlers of this type are useful for 67 /// implementing general functionality that doesn't necessarily need to 68 /// interpret the exact action parameters, or can rely on an external 69 /// interpreter (such as the user). Given that these handlers are generic, 70 /// they take a set of opaque parameters that try to map the context of the 71 /// action type in a generic way. 72 class GenericHandler : public HandlerBase { 73 public: GenericHandler()74 GenericHandler() : HandlerBase(TypeID::get<GenericHandler>()) {} 75 76 /// This hook allows for controlling whether an action should execute or 77 /// not. It should return failure if the handler could not process the 78 /// action, passing it to the next registered handler. shouldExecute(StringRef actionTag,StringRef description)79 virtual FailureOr<bool> shouldExecute(StringRef actionTag, 80 StringRef description) { 81 return failure(); 82 } 83 84 /// Provide classof to allow casting between handler types. classof(const DebugActionManager::HandlerBase * handler)85 static bool classof(const DebugActionManager::HandlerBase *handler) { 86 return handler->getHandlerID() == TypeID::get<GenericHandler>(); 87 } 88 }; 89 90 /// Register the given action handler with the manager. registerActionHandler(std::unique_ptr<HandlerBase> handler)91 void registerActionHandler(std::unique_ptr<HandlerBase> handler) { 92 // The manager is always disabled if built without debug. 93 #if LLVM_ENABLE_ABI_BREAKING_CHECKS 94 actionHandlers.emplace_back(std::move(handler)); 95 #endif 96 } 97 template <typename T> registerActionHandler()98 void registerActionHandler() { 99 registerActionHandler(std::make_unique<T>()); 100 } 101 102 //===--------------------------------------------------------------------===// 103 // Action Queries 104 //===--------------------------------------------------------------------===// 105 106 /// Returns true if the given action type should be executed, false otherwise. 107 /// `Args` are a set of parameters used by handlers of `ActionType` to 108 /// determine if the action should be executed. 109 template <typename ActionType, typename... Args> shouldExecute(Args &&...args)110 bool shouldExecute(Args &&...args) { 111 // The manager is always disabled if built without debug. 112 #if !LLVM_ENABLE_ABI_BREAKING_CHECKS 113 return true; 114 #else 115 // Invoke the `shouldExecute` method on the provided handler. 116 auto shouldExecuteFn = [&](auto *handler, auto &&...handlerParams) { 117 return handler->shouldExecute( 118 std::forward<decltype(handlerParams)>(handlerParams)...); 119 }; 120 FailureOr<bool> result = dispatchToHandler<ActionType, bool>( 121 shouldExecuteFn, std::forward<Args>(args)...); 122 123 // If the action wasn't handled, execute the action by default. 124 return succeeded(result) ? *result : true; 125 #endif 126 } 127 128 private: 129 // The manager is always disabled if built without debug. 130 #if LLVM_ENABLE_ABI_BREAKING_CHECKS 131 //===--------------------------------------------------------------------===// 132 // Query to Handler Dispatch 133 //===--------------------------------------------------------------------===// 134 135 /// Dispath a given callback on any handlers that are able to process queries 136 /// on the given action type. This method returns failure if no handlers could 137 /// process the action, or success(with a result) if a handler processed the 138 /// action. 139 template <typename ActionType, typename ResultT, typename HandlerCallbackT, 140 typename... Args> dispatchToHandler(HandlerCallbackT && handlerCallback,Args &&...args)141 FailureOr<ResultT> dispatchToHandler(HandlerCallbackT &&handlerCallback, 142 Args &&...args) { 143 static_assert(ActionType::template canHandleWith<Args...>(), 144 "cannot execute action with the given set of parameters"); 145 146 // Process any generic or action specific handlers. 147 // TODO: We currently just pick the first handler that gives us a result, 148 // but in the future we may want to employ a reduction over all of the 149 // values returned. 150 for (std::unique_ptr<HandlerBase> &it : llvm::reverse(actionHandlers)) { 151 FailureOr<ResultT> result = failure(); 152 if (auto *handler = dyn_cast<typename ActionType::Handler>(&*it)) { 153 result = handlerCallback(handler, std::forward<Args>(args)...); 154 } else if (auto *genericHandler = dyn_cast<GenericHandler>(&*it)) { 155 result = handlerCallback(genericHandler, ActionType::getTag(), 156 ActionType::getDescription()); 157 } 158 159 // If the handler succeeded, return the result. Otherwise, try a new 160 // handler. 161 if (succeeded(result)) 162 return result; 163 } 164 return failure(); 165 } 166 167 /// The set of action handlers that have been registered with the manager. 168 SmallVector<std::unique_ptr<HandlerBase>> actionHandlers; 169 #endif 170 }; 171 172 //===----------------------------------------------------------------------===// 173 // DebugAction 174 //===----------------------------------------------------------------------===// 175 176 /// A debug action is a specific action that is to be taken by the compiler, 177 /// that can be toggled and controlled by an external user. There are no 178 /// constraints on the granularity of an action, it could be as simple as 179 /// "perform this fold" and as complex as "run this pass pipeline". Via template 180 /// parameters `ParameterTs`, a user may provide the set of argument types that 181 /// are provided when handling a query on this action. Derived classes are 182 /// expected to provide the following: 183 /// * static llvm::StringRef getTag() 184 /// - This method returns a unique string identifier, similar to a command 185 /// line flag or DEBUG_TYPE. 186 /// * static llvm::StringRef getDescription() 187 /// - This method returns a short description of what the action represents. 188 /// 189 /// This class provides a handler class that can be derived from to handle 190 /// instances of this action. The parameters to its query methods map 1-1 to the 191 /// types on the action type. 192 template <typename... ParameterTs> 193 class DebugAction { 194 public: 195 class Handler : public DebugActionManager::HandlerBase { 196 public: Handler()197 Handler() 198 : HandlerBase( 199 TypeID::get<typename DebugAction<ParameterTs...>::Handler>()) {} 200 201 /// This hook allows for controlling whether an action should execute or 202 /// not. `parameters` correspond to the set of values provided by the 203 /// action as context. It should return failure if the handler could not 204 /// process the action, passing it to the next registered handler. shouldExecute(ParameterTs...parameters)205 virtual FailureOr<bool> shouldExecute(ParameterTs... parameters) { 206 return failure(); 207 } 208 209 /// Provide classof to allow casting between handler types. classof(const DebugActionManager::HandlerBase * handler)210 static bool classof(const DebugActionManager::HandlerBase *handler) { 211 return handler->getHandlerID() == 212 TypeID::get<typename DebugAction<ParameterTs...>::Handler>(); 213 } 214 }; 215 216 private: 217 /// Returns true if the action can be handled within the given set of 218 /// parameter types. 219 template <typename... CallerParameterTs> canHandleWith()220 static constexpr bool canHandleWith() { 221 return llvm::is_invocable<function_ref<void(ParameterTs...)>, 222 CallerParameterTs...>::value; 223 } 224 225 /// Allow access to `canHandleWith`. 226 friend class DebugActionManager; 227 }; 228 229 } // namespace mlir 230 231 #endif // MLIR_SUPPORT_DEBUGACTION_H 232