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