Change the INLINEASM_BR MachineInstr to be a non-terminating instruction.
[llvm-project.git] / mlir / docs / Diagnostics.md
blob9e1e4f9156c3cb7646e4ccf892385add5d0d4cf0
1 # Diagnostic Infrastructure
3 [TOC]
5 This document presents an introduction to using and interfacing with MLIR's
6 diagnostics infrastructure.
8 See [MLIR specification](LangRef.md) for more information about MLIR, the
9 structure of the IR, operations, etc.
11 ## Source Locations
13 Source location information is extremely important for any compiler, because it
14 provides a baseline for debuggability and error-reporting. MLIR provides several
15 different location types depending on the situational need.
17 ### CallSite Location
19 ```
20 callsite-location ::= 'callsite' '(' location 'at' location ')'
21 ```
23 An instance of this location allows for representing a directed stack of
24 location usages. This connects a location of a `callee` with the location of a
25 `caller`.
27 ### FileLineCol Location
29 ```
30 filelinecol-location ::= string-literal ':' integer-literal ':' integer-literal
31 ```
33 An instance of this location represents a tuple of file, line number, and column
34 number. This is similar to the type of location that you get from most source
35 languages.
37 ### Fused Location
39 ```
40 fused-location ::= `fused` fusion-metadata? '[' location (location ',')* ']'
41 fusion-metadata ::= '<' attribute-value '>'
42 ```
44 An instance of a `fused` location represents a grouping of several other source
45 locations, with optional metadata that describes the context of the fusion.
46 There are many places within a compiler in which several constructs may be fused
47 together, e.g. pattern rewriting, that normally result partial or even total
48 loss of location information. With `fused` locations, this is a non-issue.
50 ### Name Location
52 ```
53 name-location ::= string-literal ('(' location ')')?
54 ```
56 An instance of this location allows for attaching a name to a child location.
57 This can be useful for representing the locations of variable, or node,
58 definitions.
60 ### Opaque Location
62 An instance of this location essentially contains a pointer to some data
63 structure that is external to MLIR and an optional location that can be used if
64 the first one is not suitable. Since it contains an external structure, only the
65 optional location is used during serialization.
67 ### Unknown Location
69 ```
70 unknown-location ::= `unknown`
71 ```
73 Source location information is an extremely integral part of the MLIR
74 infrastructure. As such, location information is always present in the IR, and
75 must explicitly be set to unknown. Thus an instance of the `unknown` location,
76 represents an unspecified source location.
78 ## Diagnostic Engine
80 The `DiagnosticEngine` acts as the main interface for diagnostics in MLIR. It
81 manages the registration of diagnostic handlers, as well as the core API for
82 diagnostic emission. Handlers generally take the form of
83 `LogicalResult(Diagnostic &)`. If the result is `success`, it signals that the
84 diagnostic has been fully processed and consumed. If `failure`, it signals that
85 the diagnostic should be propagated to any previously registered handlers. It
86 can be interfaced with via an `MLIRContext` instance.
88 ```c++
89 DiagnosticEngine engine = ctx->getDiagEngine();
91 /// Handle the reported diagnostic.
92 // Return success to signal that the diagnostic has either been fully processed,
93 // or failure if the diagnostic should be propagated to the previous handlers.
94 DiagnosticEngine::HandlerID id = engine.registerHandler(
95     [](Diagnostic &diag) -> LogicalResult {
96   bool should_propagate_diagnostic = ...;
97   return failure(should_propagate_diagnostic);
98 });
101 // We can also elide the return value completely, in which the engine assumes
102 // that all diagnostics are consumed(i.e. a success() result).
103 DiagnosticEngine::HandlerID id = engine.registerHandler([](Diagnostic &diag) {
104   return;
107 // Unregister this handler when we are done.
108 engine.eraseHandler(id);
111 ### Constructing a Diagnostic
113 As stated above, the `DiagnosticEngine` holds the core API for diagnostic
114 emission. A new diagnostic can be emitted with the engine via `emit`. This
115 method returns an [InFlightDiagnostic](#inflight-diagnostic) that can be
116 modified further.
118 ```c++
119 InFlightDiagnostic emit(Location loc, DiagnosticSeverity severity);
122 Using the `DiagnosticEngine`, though, is generally not the preferred way to emit
123 diagnostics in MLIR. [`operation`](LangRef.md#operations) provides utility
124 methods for emitting diagnostics:
126 ```c++
127 // `emit` methods available in the mlir namespace.
128 InFlightDiagnostic emitError/Remark/Warning(Location);
130 // These methods use the location attached to the operation.
131 InFlightDiagnostic Operation::emitError/Remark/Warning();
133 // This method creates a diagnostic prefixed with "'op-name' op ".
134 InFlightDiagnostic Operation::emitOpError();
137 ## Diagnostic
139 A `Diagnostic` in MLIR contains all of the necessary information for reporting a
140 message to the user. A `Diagnostic` essentially boils down to three main
141 components:
143 *   [Source Location](#source-locations)
144 *   Severity Level
145     -   Error, Note, Remark, Warning
146 *   Diagnostic Arguments
147     -   The diagnostic arguments are used when constructing the output message.
149 ### Appending arguments
151 One a diagnostic has been constructed, the user can start composing it. The
152 output message of a diagnostic is composed of a set of diagnostic arguments that
153 have been attached to it. New arguments can be attached to a diagnostic in a few
154 different ways:
156 ```c++
157 // A few interesting things to use when composing a diagnostic.
158 Attribute fooAttr;
159 Type fooType;
160 SmallVector<int> fooInts;
162 // Diagnostics can be composed via the streaming operators.
163 op->emitError() << "Compose an interesting error: " << fooAttr << ", " << fooType
164                 << ", (" << fooInts << ')';
166 // This could generate something like (FuncAttr:@foo, IntegerType:i32, {0,1,2}):
167 "Compose an interesting error: @foo, i32, (0, 1, 2)"
170 ### Attaching notes
172 Unlike many other compiler frameworks, notes in MLIR cannot be emitted directly.
173 They must be explicitly attached to another diagnostic non-note diagnostic. When
174 emitting a diagnostic, notes can be directly attached via `attachNote`. When
175 attaching a note, if the user does not provide an explicit source location the
176 note will inherit the location of the parent diagnostic.
178 ```c++
179 // Emit a note with an explicit source location.
180 op->emitError("...").attachNote(noteLoc) << "...";
182 // Emit a note that inherits the parent location.
183 op->emitError("...").attachNote() << "...";
186 ## InFlight Diagnostic
188 Now that [Diagnostics](#diagnostic) have been explained, we introduce the
189 `InFlightDiagnostic`, an RAII wrapper around a diagnostic that is set to be
190 reported. This allows for modifying a diagnostic while it is still in flight. If
191 it is not reported directly by the user it will automatically report when
192 destroyed.
194 ```c++
196   InFlightDiagnostic diag = op->emitError() << "...";
197 }  // The diagnostic is automatically reported here.
200 ## Diagnostic Configuration Options
202 Several options are provided to help control and enhance the behavior of
203 diagnostics. These options can be configured via the MLIRContext, and registered
204 to the command line with the `registerMLIRContextCLOptions` method. These
205 options are listed below:
207 ### Print Operation On Diagnostic
209 Command Line Flag: `-mlir-print-op-on-diagnostic`
211 When a diagnostic is emitted on an operation, via `Operation::emitError/...`,
212 the textual form of that operation is printed and attached as a note to the
213 diagnostic. This option is useful for understanding the current form of an
214 operation that may be invalid, especially when debugging verifier failures. An
215 example output is shown below:
217 ```shell
218 test.mlir:3:3: error: 'module_terminator' op expects parent op 'module'
219   "module_terminator"() : () -> ()
220   ^
221 test.mlir:3:3: note: see current operation: "module_terminator"() : () -> ()
222   "module_terminator"() : () -> ()
223   ^
226 ### Print StackTrace On Diagnostic
228 Command Line Flag: `-mlir-print-stacktrace-on-diagnostic`
230 When a diagnostic is emitted, attach the current stack trace as a note to the
231 diagnostic. This option is useful for understanding which part of the compiler
232 generated certain diagnostics. An example output is shown below:
234 ```shell
235 test.mlir:3:3: error: 'module_terminator' op expects parent op 'module'
236   "module_terminator"() : () -> ()
237   ^
238 test.mlir:3:3: note: diagnostic emitted with trace:
239  #0 0x000055dd40543805 llvm::sys::PrintStackTrace(llvm::raw_ostream&) llvm/lib/Support/Unix/Signals.inc:553:11
240  #1 0x000055dd3f8ac162 emitDiag(mlir::Location, mlir::DiagnosticSeverity, llvm::Twine const&) /lib/IR/Diagnostics.cpp:292:7
241  #2 0x000055dd3f8abe8e mlir::emitError(mlir::Location, llvm::Twine const&) /lib/IR/Diagnostics.cpp:304:10
242  #3 0x000055dd3f998e87 mlir::Operation::emitError(llvm::Twine const&) /lib/IR/Operation.cpp:324:29
243  #4 0x000055dd3f99d21c mlir::Operation::emitOpError(llvm::Twine const&) /lib/IR/Operation.cpp:652:10
244  #5 0x000055dd3f96b01c mlir::OpTrait::HasParent<mlir::ModuleOp>::Impl<mlir::ModuleTerminatorOp>::verifyTrait(mlir::Operation*) /mlir/IR/OpDefinition.h:897:18
245  #6 0x000055dd3f96ab38 mlir::Op<mlir::ModuleTerminatorOp, mlir::OpTrait::ZeroOperands, mlir::OpTrait::ZeroResult, mlir::OpTrait::HasParent<mlir::ModuleOp>::Impl, mlir::OpTrait::IsTerminator>::BaseVerifier<mlir::OpTrait::HasParent<mlir::ModuleOp>::Impl<mlir::ModuleTerminatorOp>, mlir::OpTrait::IsTerminator<mlir::ModuleTerminatorOp> >::verifyTrait(mlir::Operation*) /mlir/IR/OpDefinition.h:1052:29
246  #  ...
247   "module_terminator"() : () -> ()
248   ^
251 ## Common Diagnostic Handlers
253 To interface with the diagnostics infrastructure, users will need to register a
254 diagnostic handler with the [`DiagnosticEngine`](#diagnostic-engine).
255 Recognizing the many users will want the same handler functionality, MLIR
256 provides several common diagnostic handlers for immediate use.
258 ### Scoped Diagnostic Handler
260 This diagnostic handler is a simple RAII class that registers and unregisters a
261 given diagnostic handler. This class can be either be used directly, or in
262 conjunction with a derived diagnostic handler.
264 ```c++
265 // Construct the handler directly.
266 MLIRContext context;
267 ScopedDiagnosticHandler scopedHandler(&context, [](Diagnostic &diag) {
268   ...
271 // Use this handler in conjunction with another.
272 class MyDerivedHandler : public ScopedDiagnosticHandler {
273   MyDerivedHandler(MLIRContext *ctx) : ScopedDiagnosticHandler(ctx) {
274     // Set the handler that should be RAII managed.
275     setHandler([&](Diagnostic diag) {
276       ...
277     });
278   }
282 ### SourceMgr Diagnostic Handler
284 This diagnostic handler is a wrapper around an llvm::SourceMgr instance. It
285 provides support for displaying diagnostic messages inline with a line of a
286 respective source file. This handler will also automatically load newly seen
287 source files into the SourceMgr when attempting to display the source line of a
288 diagnostic. Example usage of this handler can be seen in the `mlir-opt` tool.
290 ```shell
291 $ mlir-opt foo.mlir
293 /tmp/test.mlir:6:24: error: expected non-function type
294 func @foo() -> (index, ind) {
295                        ^
298 To use this handler in your tool, add the following:
300 ```c++
301 SourceMgr sourceMgr;
302 MLIRContext context;
303 SourceMgrDiagnosticHandler sourceMgrHandler(sourceMgr, &context);
306 ### SourceMgr Diagnostic Verifier Handler
308 This handler is a wrapper around a llvm::SourceMgr that is used to verify that
309 certain diagnostics have been emitted to the context. To use this handler,
310 annotate your source file with expected diagnostics in the form of:
312 *   `expected-(error|note|remark|warning) {{ message }}`
314 A few examples are shown below:
316 ```mlir
317 // Expect an error on the same line.
318 func @bad_branch() {
319   br ^missing  // expected-error {{reference to an undefined block}}
322 // Expect an error on an adjacent line.
323 func @foo(%a : f32) {
324   // expected-error@+1 {{unknown comparison predicate "foo"}}
325   %result = cmpf "foo", %a, %a : f32
326   return
329 // Expect an error on the next line that does not contain a designator.
330 // expected-remark@below {{remark on function below}}
331 // expected-remark@below {{another remark on function below}}
332 func @bar(%a : f32)
334 // Expect an error on the previous line that does not contain a designator.
335 func @baz(%a : f32)
336 // expected-remark@above {{remark on function above}}
337 // expected-remark@above {{another remark on function above}}
341 The handler will report an error if any unexpected diagnostics were seen, or if
342 any expected diagnostics weren't.
344 ```shell
345 $ mlir-opt foo.mlir
347 /tmp/test.mlir:6:24: error: unexpected error: expected non-function type
348 func @foo() -> (index, ind) {
349                        ^
351 /tmp/test.mlir:15:4: error: expected remark "expected some remark" was not produced
352 // expected-remark {{expected some remark}}
353    ^~~~~~~~~~~~~~~~~~~~~~~~~~
356 Similarly to the [SourceMgr Diagnostic Handler](#sourcemgr-diagnostic-handler),
357 this handler can be added to any tool via the following:
359 ```c++
360 SourceMgr sourceMgr;
361 MLIRContext context;
362 SourceMgrDiagnosticVerifierHandler sourceMgrHandler(sourceMgr, &context);
365 ### Parallel Diagnostic Handler
367 MLIR is designed from the ground up to be multi-threaded. One important to thing
368 to keep in mind when multi-threading is determinism. This means that the
369 behavior seen when operating on multiple threads is the same as when operating
370 on a single thread. For diagnostics, this means that the ordering of the
371 diagnostics is the same regardless of the amount of threads being operated on.
372 The ParallelDiagnosticHandler is introduced to solve this problem.
374 After creating a handler of this type, the only remaining step is to ensure that
375 each thread that will be emitting diagnostics to the handler sets a respective
376 'orderID'. The orderID corresponds to the order in which diagnostics would be
377 emitted when executing synchronously. For example, if we were processing a list
378 of operations [a, b, c] on a single-thread. Diagnostics emitted while processing
379 operation 'a' would be emitted before those for 'b' or 'c'. This corresponds 1-1
380 with the 'orderID'. The thread that is processing 'a' should set the orderID to
381 '0'; the thread processing 'b' should set it to '1'; and so on and so forth.
382 This provides a way for the handler to deterministically order the diagnostics
383 that it receives given the thread that it is receiving on.
385 A simple example is shown below:
387 ```c++
388 MLIRContext *context = ...;
389 ParallelDiagnosticHandler handler(context);
391 // Process a list of operations in parallel.
392 std::vector<Operation *> opsToProcess = ...;
393 llvm::parallelForEachN(0, opsToProcess.size(), [&](size_t i) {
394   // Notify the handler that we are processing the i'th operation.
395   handler.setOrderIDForThread(i);
396   auto *op = opsToProcess[i];
397   ...
399   // Notify the handler that we are finished processing diagnostics on this
400   // thread.
401   handler.eraseOrderIDForThread();