blob: 74f0f4c47b22e4c028f68b0e7ddb0dc775499455 [file] [log] [blame]
//===- DebugAction.h - Debug Action Support ---------------------*- 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 contains definitions for the debug action framework. This framework
// allows for external entities to control certain actions taken by the compiler
// by registering handler functions. A debug action handler provides the
// internal implementation for the various queries on a debug action, such as
// whether it should execute or not.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_SUPPORT_DEBUGACTION_H
#define MLIR_SUPPORT_DEBUGACTION_H
#include "mlir/Support/LogicalResult.h"
#include "mlir/Support/TypeID.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/TypeName.h"
#include "llvm/Support/raw_ostream.h"
#include <functional>
namespace mlir {
//===----------------------------------------------------------------------===//
// DebugActionManager
//===----------------------------------------------------------------------===//
/// This class represents manages debug actions, and orchestrates the
/// communication between action queries and action handlers. An action handler
/// is either an action specific handler, i.e. a derived class of
/// `MyActionType::Handler`, or a generic handler, i.e. a derived class of
/// `DebugActionManager::GenericHandler`. For more details on action specific
/// handlers, see the definition of `DebugAction::Handler` below. For more
/// details on generic handlers, see `DebugActionManager::GenericHandler` below.
class DebugActionManager {
public:
//===--------------------------------------------------------------------===//
// Handlers
//===--------------------------------------------------------------------===//
/// This class represents the base class of a debug action handler.
class HandlerBase {
public:
virtual ~HandlerBase() {}
/// Return the unique handler id of this handler, use for casting
/// functionality.
TypeID getHandlerID() const { return handlerID; }
protected:
HandlerBase(TypeID handlerID) : handlerID(handlerID) {}
/// The type of the derived handler class. This allows for detecting if a
/// handler can handle a given action type.
TypeID handlerID;
};
/// This class represents a generic action handler. A generic handler allows
/// for handling any action type. Handlers of this type are useful for
/// implementing general functionality that doesn't necessarily need to
/// interpret the exact action parameters, or can rely on an external
/// interpreter (such as the user). Given that these handlers are generic,
/// they take a set of opaque parameters that try to map the context of the
/// action type in a generic way.
class GenericHandler : public HandlerBase {
public:
GenericHandler() : HandlerBase(TypeID::get<GenericHandler>()) {}
/// This hook allows for controlling whether an action should execute or
/// not. It should return failure if the handler could not process the
/// action, passing it to the next registered handler.
virtual FailureOr<bool> shouldExecute(StringRef actionTag,
StringRef description) {
return failure();
}
/// Provide classof to allow casting between handler types.
static bool classof(const DebugActionManager::HandlerBase *handler) {
return handler->getHandlerID() == TypeID::get<GenericHandler>();
}
};
/// Register the given action handler with the manager.
void registerActionHandler(std::unique_ptr<HandlerBase> handler) {
// The manager is always disabled if built without debug.
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
actionHandlers.emplace_back(std::move(handler));
#endif
}
template <typename T>
void registerActionHandler() {
registerActionHandler(std::make_unique<T>());
}
//===--------------------------------------------------------------------===//
// Action Queries
//===--------------------------------------------------------------------===//
/// Returns true if the given action type should be executed, false otherwise.
/// `Args` are a set of parameters used by handlers of `ActionType` to
/// determine if the action should be executed.
template <typename ActionType, typename... Args>
bool shouldExecute(Args &&... args) {
// The manager is always disabled if built without debug.
#if !LLVM_ENABLE_ABI_BREAKING_CHECKS
return true;
#else
// Invoke the `shouldExecute` method on the provided handler.
auto shouldExecuteFn = [&](auto *handler, auto &&... handlerParams) {
return handler->shouldExecute(
std::forward<decltype(handlerParams)>(handlerParams)...);
};
FailureOr<bool> result = dispatchToHandler<ActionType, bool>(
shouldExecuteFn, std::forward<Args>(args)...);
// If the action wasn't handled, execute the action by default.
return succeeded(result) ? *result : true;
#endif
}
private:
// The manager is always disabled if built without debug.
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
//===--------------------------------------------------------------------===//
// Query to Handler Dispatch
//===--------------------------------------------------------------------===//
/// Dispath a given callback on any handlers that are able to process queries
/// on the given action type. This method returns failure if no handlers could
/// process the action, or success(with a result) if a handler processed the
/// action.
template <typename ActionType, typename ResultT, typename HandlerCallbackT,
typename... Args>
FailureOr<ResultT> dispatchToHandler(HandlerCallbackT &&handlerCallback,
Args &&... args) {
static_assert(ActionType::template canHandleWith<Args...>(),
"cannot execute action with the given set of parameters");
// Process any generic or action specific handlers.
// TODO: We currently just pick the first handler that gives us a result,
// but in the future we may want to employ a reduction over all of the
// values returned.
for (std::unique_ptr<HandlerBase> &it : llvm::reverse(actionHandlers)) {
FailureOr<ResultT> result = failure();
if (auto *handler = dyn_cast<typename ActionType::Handler>(&*it)) {
result = handlerCallback(handler, std::forward<Args>(args)...);
} else if (auto *genericHandler = dyn_cast<GenericHandler>(&*it)) {
result = handlerCallback(genericHandler, ActionType::getTag(),
ActionType::getDescription());
}
// If the handler succeeded, return the result. Otherwise, try a new
// handler.
if (succeeded(result))
return result;
}
return failure();
}
/// The set of action handlers that have been registered with the manager.
SmallVector<std::unique_ptr<HandlerBase>> actionHandlers;
#endif
};
//===----------------------------------------------------------------------===//
// DebugAction
//===----------------------------------------------------------------------===//
/// A debug action is a specific action that is to be taken by the compiler,
/// that can be toggled and controlled by an external user. There are no
/// constraints on the granularity of an action, it could be as simple as
/// "perform this fold" and as complex as "run this pass pipeline". Via template
/// parameters `ParameterTs`, a user may provide the set of argument types that
/// are provided when handling a query on this action. Derived classes are
/// expected to provide the following:
/// * static llvm::StringRef getTag()
/// - This method returns a unique string identifier, similar to a command
/// line flag or DEBUG_TYPE.
/// * static llvm::StringRef getDescription()
/// - This method returns a short description of what the action represents.
///
/// This class provides a handler class that can be derived from to handle
/// instances of this action. The parameters to its query methods map 1-1 to the
/// types on the action type.
template <typename... ParameterTs> class DebugAction {
public:
class Handler : public DebugActionManager::HandlerBase {
public:
Handler() : HandlerBase(TypeID::get<Handler>()) {}
/// This hook allows for controlling whether an action should execute or
/// not. `parameters` correspond to the set of values provided by the
/// action as context. It should return failure if the handler could not
/// process the action, passing it to the next registered handler.
virtual FailureOr<bool> shouldExecute(ParameterTs... parameters) {
return failure();
}
/// Provide classof to allow casting between handler types.
static bool classof(const DebugActionManager::HandlerBase *handler) {
return handler->getHandlerID() ==
TypeID::get<DebugAction<ParameterTs...>::Handler>();
}
};
private:
/// Returns true if the action can be handled within the given set of
/// parameter types.
template <typename... CallerParameterTs>
static constexpr bool canHandleWith() {
return llvm::is_invocable<function_ref<void(ParameterTs...)>,
CallerParameterTs...>::value;
}
/// Allow access to `canHandleWith`.
friend class DebugActionManager;
};
} // end namespace mlir
#endif // MLIR_SUPPORT_DEBUGACTION_H