Skip to content

Commit 72d5afa

Browse files
committed
[mlir] Add a new debug action framework.
This revision adds the infrastructure for `Debug Actions`. This is a DEBUG only API that allows for external entities to control various aspects of compiler execution. This is conceptually similar to something like DebugCounters in LLVM, but at a lower level. This framework doesn't make any assumptions about how the higher level driver is controlling the execution, it merely provides a framework for connecting the two together. This means that on top of DebugCounter functionality, we could also provide more interesting drivers such as interactive execution. A high level overview of the workflow surrounding debug actions is shown below: * Compiler developer defines an `action` that is taken by the a pass, transformation, utility that they are developing. * Depending on the needs, the developer dispatches various queries, pertaining to this action, to an `action manager` that will provide an answer as to what behavior the action should do. * An external entity registers an `action handler` with the action manager, and provides the logic to resolve queries on actions. The exact definition of an `external entity` is left opaque, to allow for more interesting handlers. This framework was proposed here: https://llvm.discourse.group/t/rfc-debug-actions-in-mlir-debug-counters-for-the-modern-world Differential Revision: https://reviews.llvm.org/D84986
1 parent 7c9c0a8 commit 72d5afa

File tree

6 files changed

+510
-0
lines changed

6 files changed

+510
-0
lines changed

mlir/docs/DebugActions.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Debug Actions
2+
3+
[TOC]
4+
5+
This file documents the infrastructure for `Debug Actions`. This is a DEBUG only
6+
API that allows for external entities to control various aspects of compiler
7+
execution. This is conceptually similar to something like `DebugCounters` in
8+
LLVM, but at a lower level. This framework doesn't make any assumptions about
9+
how the higher level driver is controlling the execution, it merely provides a
10+
framework for connecting the two together. A high level overview of the workflow
11+
surrounding debug actions is shown below:
12+
13+
* Compiler developer defines an [`action`](#debug-action) that is taken by the
14+
a pass, transformation, utility that they are developing.
15+
* Depending on the needs, the developer dispatches various queries, pertaining
16+
to this action, to an [`action manager`](#debug-action-manager) that will
17+
provide an answer as to what behavior the action should take.
18+
* An external entity registers an [`action handler`](#debug-action-handler)
19+
with the action manager, and provides the logic to resolve queries on
20+
actions.
21+
22+
The exact definition of an `external entity` is left opaque, to allow for more
23+
interesting handlers. The set of possible action queries is detailed in the
24+
[`action manager`](#debug-action-manager) section below.
25+
26+
(TODO: Add connection to existing handlers when they are added)
27+
28+
## Debug Action
29+
30+
A `debug action` is essentially a marker for a type of action that may be
31+
performed within the compiler. There are no constraints on the granularity of an
32+
“action”, it can be as simple as “perform this fold” and as complex as “run this
33+
pass pipeline”. An action is comprised of the following:
34+
35+
* Tag:
36+
37+
- A unique string identifier, similar to a command line flag or
38+
DEBUG_TYPE.
39+
40+
* Description:
41+
42+
- A short description of what the action represents.
43+
44+
* Parameter Types:
45+
46+
- The types of values that are passed to queries related to this action,
47+
to help guide decisions.
48+
49+
Below is an example action that may be provided by the
50+
[pattern rewriter](PatternRewriter.md) framework to control the application of
51+
rewrite patterns.
52+
53+
```c++
54+
/// A debug action that allows for controlling the application of patterns.
55+
/// A new action type can be defined by inheriting from `DebugAction`.
56+
/// * The Tag is specified via a static `StringRef getTag()` method.
57+
/// * The Description is specified via a static `StringRef getDescription()`
58+
/// method.
59+
/// * The parameters for the action are provided via template parameters when
60+
/// inheriting from `DebugAction`.
61+
struct ApplyPatternAction
62+
: public DebugAction<Operation *, const Pattern &> {
63+
static StringRef getTag() { return "apply-pattern"; }
64+
static StringRef getDescription() {
65+
return "Control the application of rewrite patterns";
66+
}
67+
};
68+
```
69+
70+
## Debug Action Manager
71+
72+
The `DebugActionManager` orchestrates the various different queries relating to
73+
debug actions, and is accessible via the `MLIRContext`. These queries allow for
74+
external entities to control various aspects of the compiler via
75+
[action handlers](#debug-action-handler). When resolving a query for an action,
76+
the result from the most recently registered handler is used.
77+
78+
TODO: It may be interesting to support merging results from multiple action
79+
handlers, but this is left for future work when driven by a real use case.
80+
81+
The set of available queries are shown below:
82+
83+
```c++
84+
class DebugActionManager {
85+
public:
86+
/// Returns true if the given action type should be executed, false otherwise.
87+
/// `Params` correspond to any action specific parameters that may be used to
88+
/// guide the decision.
89+
template <typename ActionType, typename... Params>
90+
bool shouldExecute(Params &&... params);
91+
};
92+
```
93+
94+
Building on the example from the [previous section](#debug-action), an example
95+
usage of the `shouldExecute` query is shown below:
96+
97+
```c++
98+
/// A debug action that allows for controlling the application of patterns.
99+
struct ApplyPatternAction
100+
: public DebugAction<Operation *, const Pattern &> {
101+
static StringRef getTag() { return "apply-pattern"; }
102+
static StringRef getDescription() {
103+
return "Control the application of rewrite patterns";
104+
}
105+
};
106+
107+
// ...
108+
109+
bool shouldApplyPattern(Operation *currentOp, const Pattern &currentPattern) {
110+
MLIRContext *context = currentOp->getContext();
111+
DebugActionManager &manager = context->getDebugActionManager();
112+
113+
// Query the action manager to see if `currentPattern` should be applied to
114+
// `currentOp`.
115+
return manager.shouldExecute<ApplyPatternAction>(currentOp, currentPattern);
116+
}
117+
```
118+
119+
## Debug Action Handler
120+
121+
A debug action handler provides the internal implementation for the various
122+
action related queries within the [`DebugActionManager`](debug-action-manager).
123+
Action handlers allow for external entities to control and inject external
124+
information into the compiler. Handlers can be registered with the
125+
`DebugActionManager` using `registerActionHandler`. There are two types of
126+
handlers; action-specific handlers and generic handlers.
127+
128+
### Action Specific Handlers
129+
130+
Action specific handlers handle a specific debug action type, with the
131+
parameters to its query methods mapping 1-1 to the parameter types of the action
132+
type. An action specific handler can be defined by inheriting from the handler
133+
base class defined at `ActionType::Handler` where `ActionType` is the specific
134+
action that should be handled. An example using our running pattern example is
135+
shown below:
136+
137+
```c++
138+
struct MyPatternHandler : public ApplyPatternAction::Handler {
139+
/// A variant of `shouldExecute` shown in the `DebugActionManager` class
140+
/// above.
141+
/// This method returns a FailureOr<bool>, where failure signifies that the
142+
/// action was not handled (allowing for other handlers to process it), or the
143+
/// boolean true/false signifying if the action should execute or not.
144+
FailureOr<bool> shouldExecute(Operation *op,
145+
const RewritePattern &pattern) final;
146+
};
147+
```
148+
149+
### Generic Handlers
150+
151+
A generic handler allows for handling queries on any action type. These types of
152+
handlers are useful for implementing general functionality that doesn’t
153+
necessarily need to interpret the exact action parameters, or can rely on an
154+
external interpreter (such as the user). As these handlers are generic, they
155+
take a set of opaque parameters that try to map the context of the action type
156+
in a generic way. A generic handler can be defined by inheriting from
157+
`DebugActionManager::GenericHandler`. An example is shown below:
158+
159+
```c++
160+
struct MyPatternHandler : public DebugActionManager::GenericHandler {
161+
/// The return type of this method is the same as the action-specific handler.
162+
/// The arguments to this method map the concepts of an action type in an
163+
/// opaque way. The arguments are provided in such a way so that the context
164+
/// of the action is still somewhat user readable, or at least loggable as
165+
/// such.
166+
/// - actionTag: The tag specified by the action type.
167+
/// - actionDesc: The description specified by the action type.
168+
virtual FailureOr<bool> shouldExecute(StringRef actionTag,
169+
StringRef actionDesc);
170+
};
171+
```

mlir/include/mlir/IR/MLIRContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
namespace mlir {
1919
class AbstractOperation;
20+
class DebugActionManager;
2021
class DiagnosticEngine;
2122
class Dialect;
2223
class DialectRegistry;
@@ -155,6 +156,9 @@ class MLIRContext {
155156
/// instances. This should not be used directly.
156157
StorageUniquer &getAttributeUniquer();
157158

159+
/// Returns the manager of debug actions within the context.
160+
DebugActionManager &getDebugActionManager();
161+
158162
/// These APIs are tracking whether the context will be used in a
159163
/// multithreading environment: this has no effect other than enabling
160164
/// assertions on misuses of some APIs.

0 commit comments

Comments
 (0)