MLIR 24.0.0git
Diagnostics.cpp
Go to the documentation of this file.
1//===- Diagnostics.cpp - MLIR Diagnostics ---------------------------------===//
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
10#include "mlir/IR/Attributes.h"
11#include "mlir/IR/Location.h"
12#include "mlir/IR/MLIRContext.h"
13#include "mlir/IR/Operation.h"
14#include "mlir/IR/Types.h"
15#include "llvm/ADT/MapVector.h"
16#include "llvm/ADT/SmallString.h"
17#include "llvm/ADT/StringMap.h"
18#include "llvm/ADT/TypeSwitch.h"
19#include "llvm/Support/Mutex.h"
20#include "llvm/Support/PrettyStackTrace.h"
21#include "llvm/Support/Regex.h"
22#include "llvm/Support/Signals.h"
23#include "llvm/Support/SourceMgr.h"
24#include "llvm/Support/SourceMgrDiagnosticVerifier.h"
25#include "llvm/Support/raw_ostream.h"
26#include <atomic>
27#include <optional>
28
29using namespace mlir;
30using namespace mlir::detail;
31
32//===----------------------------------------------------------------------===//
33// DiagnosticArgument
34//===----------------------------------------------------------------------===//
35
36/// Construct from an Attribute.
39 opaqueVal(reinterpret_cast<intptr_t>(attr.getAsOpaquePointer())) {}
40
41/// Construct from a Type.
44 opaqueVal(reinterpret_cast<intptr_t>(val.getAsOpaquePointer())) {}
45
46/// Returns this argument as an Attribute.
50 reinterpret_cast<const void *>(opaqueVal));
51}
52
53/// Returns this argument as a Type.
56 return Type::getFromOpaquePointer(reinterpret_cast<const void *>(opaqueVal));
57}
58
59/// Outputs this argument to a stream.
61 switch (kind) {
63 os << getAsAttribute();
64 break;
66 os << getAsDouble();
67 break;
69 os << getAsInteger();
70 break;
72 os << getAsString();
73 break;
75 os << '\'' << getAsType() << '\'';
76 break;
78 os << getAsUnsigned();
79 break;
80 }
81}
82
83//===----------------------------------------------------------------------===//
84// Diagnostic
85//===----------------------------------------------------------------------===//
86
87/// Convert a Twine to a StringRef. Memory used for generating the StringRef is
88/// stored in 'strings'.
89static StringRef
90twineToStrRef(const Twine &val,
91 SmallVectorImpl<std::unique_ptr<char[]>> &strings) {
92 // Allocate memory to hold this string.
93 SmallString<64> data;
94 auto strRef = val.toStringRef(data);
95 if (strRef.empty())
96 return strRef;
97
98 strings.push_back(std::unique_ptr<char[]>(new char[strRef.size()]));
99 memcpy(&strings.back()[0], strRef.data(), strRef.size());
100 // Return a reference to the new string.
101 return StringRef(&strings.back()[0], strRef.size());
102}
103
104/// Stream in a Twine argument.
105Diagnostic &Diagnostic::operator<<(char val) { return *this << Twine(val); }
107 arguments.push_back(DiagnosticArgument(twineToStrRef(val, strings)));
108 return *this;
109}
111 arguments.push_back(DiagnosticArgument(twineToStrRef(val, strings)));
112 return *this;
113}
114
116 arguments.push_back(DiagnosticArgument(val));
117 return *this;
118}
119
120/// Stream in an OperationName.
122 // An OperationName is stored in the context, so we don't need to worry about
123 // the lifetime of its data.
124 arguments.push_back(DiagnosticArgument(val.getStringRef()));
125 return *this;
126}
127
128/// Adjusts operation printing flags used in diagnostics for the given severity
129/// level.
131 DiagnosticSeverity severity) {
132 flags.useLocalScope();
134 if (severity == DiagnosticSeverity::Error)
135 flags.printGenericOpForm();
136 return flags;
137}
138
139/// Stream in an Operation.
143
147
149 std::string str;
150 llvm::raw_string_ostream os(str);
151 op.print(os, adjustPrintingFlags(flags, severity));
152 // Print on a new line for better readability if the op will be printed on
153 // multiple lines.
154 if (str.find('\n') != std::string::npos)
155 *this << '\n';
156 return *this << str;
157}
158
159/// Stream in a Value.
161 std::string str;
162 llvm::raw_string_ostream os(str);
163 val.print(os, adjustPrintingFlags(OpPrintingFlags(), severity));
164 return *this << str;
165}
166
167/// Outputs this diagnostic to a stream.
169 std::optional<int64_t> messagePartIndex) const {
170 if (!messagePartIndex.has_value()) {
171 for (auto &arg : getArguments())
172 arg.print(os);
173 return;
174 }
175
176 assert(0 <= *messagePartIndex &&
177 *messagePartIndex <= static_cast<int64_t>(messagePartEnds.size()));
178 size_t argumentStart =
179 *messagePartIndex == 0 ? 0 : messagePartEnds[*messagePartIndex - 1];
180 size_t argumentEnd =
181 *messagePartIndex == static_cast<int64_t>(messagePartEnds.size())
182 ? arguments.size()
183 : messagePartEnds[*messagePartIndex];
184 for (auto &arg :
185 getArguments().slice(argumentStart, argumentEnd - argumentStart))
186 arg.print(os);
187}
188
189/// Convert the diagnostic to a string.
190std::string Diagnostic::str() const {
191 std::string str;
192 llvm::raw_string_ostream os(str);
193 print(os);
194 return str;
195}
196
197/// Converts each message part to a separate string.
200 size_t numMessageParts = messagePartEnds.size();
201
202 // Include the current message part if there are no completed parts or if it
203 // contains arguments after the last completed part.
204 if (messagePartEnds.empty() || messagePartEnds.back() != arguments.size())
205 ++numMessageParts;
206 for (size_t i = 0; i < numMessageParts; ++i) {
207 std::string str;
208 llvm::raw_string_ostream os(str);
209 print(os, i);
210 strs.push_back(str);
211 }
212 return strs;
213}
214
215/// Attaches a note to this diagnostic. A new location may be optionally
216/// provided, if not, then the location defaults to the one specified for this
217/// diagnostic. Notes may not be attached to other notes.
218Diagnostic &Diagnostic::attachNote(std::optional<Location> noteLoc) {
219 // We don't allow attaching notes to notes.
220 assert(severity != DiagnosticSeverity::Note &&
221 "cannot attach a note to a note");
222
223 // If a location wasn't provided then reuse our location.
224 if (!noteLoc)
225 noteLoc = loc;
226
227 /// Append and return a new note.
228 notes.push_back(
229 std::make_unique<Diagnostic>(*noteLoc, DiagnosticSeverity::Note));
230 return *notes.back();
231}
232
233/// Allow a diagnostic to be converted to 'failure'.
234Diagnostic::operator LogicalResult() const { return failure(); }
235
236/// Starts a new message part.
238 if (arguments.empty())
239 return;
240 if (!messagePartEnds.empty() && messagePartEnds.back() == arguments.size())
241 return;
242 messagePartEnds.push_back(arguments.size());
243}
244
245//===----------------------------------------------------------------------===//
246// InFlightDiagnostic
247//===----------------------------------------------------------------------===//
248
249/// Allow an inflight diagnostic to be converted to 'failure', otherwise
250/// 'success' if this is an empty diagnostic.
251InFlightDiagnostic::operator LogicalResult() const {
252 return failure(isActive());
253}
254
255/// Reports the diagnostic to the engine.
257 // If this diagnostic is still inflight and it hasn't been abandoned, then
258 // report it.
259 if (isInFlight()) {
260 owner->emit(std::move(*impl));
261 owner = nullptr;
262 }
263 impl.reset();
264}
265
266/// Abandons this diagnostic.
267void InFlightDiagnostic::abandon() { owner = nullptr; }
268
269//===----------------------------------------------------------------------===//
270// DiagnosticEngineImpl
271//===----------------------------------------------------------------------===//
272
273namespace mlir {
274namespace detail {
276 /// Emit a diagnostic using the registered issue handle if present, or with
277 /// the default behavior if not.
278 void emit(Diagnostic &&diag);
279
280 /// A mutex to ensure that diagnostics emission is thread-safe.
282
283 /// These are the handlers used to report diagnostics.
285 2>
287
288 /// This is a unique identifier counter for diagnostic handlers in the
289 /// context. This id starts at 1 to allow for 0 to be used as a sentinel.
291};
292} // namespace detail
293} // namespace mlir
294
295/// Emit a diagnostic using the registered issue handle if present, or with
296/// the default behavior if not.
298 llvm::sys::SmartScopedLock<true> lock(mutex);
299
300 // Try to process the given diagnostic on one of the registered handlers.
301 // Handlers are walked in reverse order, so that the most recent handler is
302 // processed first.
303 for (auto &handlerIt : llvm::reverse(handlers))
304 if (succeeded(handlerIt.second(diag)))
305 return;
306
307 // Otherwise, if this is an error we emit it to stderr.
308 if (diag.getSeverity() != DiagnosticSeverity::Error)
309 return;
310
311 auto &os = llvm::errs();
312 if (!llvm::isa<UnknownLoc>(diag.getLocation()))
313 os << diag.getLocation() << ": ";
314 os << "error: ";
315
316 // The default behavior for errors is to emit them to stderr.
317 os << diag << '\n';
318 os.flush();
319}
320
321//===----------------------------------------------------------------------===//
322// DiagnosticEngine
323//===----------------------------------------------------------------------===//
324
325DiagnosticEngine::DiagnosticEngine() : impl(new DiagnosticEngineImpl()) {}
327
328/// Register a new handler for diagnostics to the engine. This function returns
329/// a unique identifier for the registered handler, which can be used to
330/// unregister this handler at a later time.
332 llvm::sys::SmartScopedLock<true> lock(impl->mutex);
333 auto uniqueID = impl->uniqueHandlerId++;
334 impl->handlers.insert({uniqueID, std::move(handler)});
335 return uniqueID;
336}
337
338/// Erase the registered diagnostic handler with the given identifier.
340 llvm::sys::SmartScopedLock<true> lock(impl->mutex);
341 impl->handlers.erase(handlerID);
342}
343
344/// Emit a diagnostic using the registered issue handler if present, or with
345/// the default behavior if not.
347 assert(diag.getSeverity() != DiagnosticSeverity::Note &&
348 "notes should not be emitted directly");
349 impl->emit(std::move(diag));
350}
351
352/// Helper function used to emit a diagnostic with an optionally empty twine
353/// message. If the message is empty, then it is not inserted into the
354/// diagnostic.
356emitDiag(Location location, DiagnosticSeverity severity, const Twine &message) {
357 MLIRContext *ctx = location->getContext();
358 auto &diagEngine = ctx->getDiagEngine();
359 auto diag = diagEngine.emit(location, severity);
360 if (!message.isTriviallyEmpty())
361 diag << message;
362
363 // Add the stack trace as a note if necessary.
365 std::string bt;
366 {
367 llvm::raw_string_ostream stream(bt);
368 llvm::sys::PrintStackTrace(stream);
369 }
370 if (!bt.empty())
371 diag.attachNote() << "diagnostic emitted with trace:\n" << bt;
372 }
373
374 return diag;
375}
376
377/// Emit an error message using this location.
379InFlightDiagnostic mlir::emitError(Location loc, const Twine &message) {
380 return emitDiag(loc, DiagnosticSeverity::Error, message);
381}
382
383/// Emit a warning message using this location.
388 return emitDiag(loc, DiagnosticSeverity::Warning, message);
389}
390
391/// Emit a remark message using this location.
395InFlightDiagnostic mlir::emitRemark(Location loc, const Twine &message) {
396 return emitDiag(loc, DiagnosticSeverity::Remark, message);
397}
398
399//===----------------------------------------------------------------------===//
400// ScopedDiagnosticHandler
401//===----------------------------------------------------------------------===//
402
404 if (handlerID)
405 ctx->getDiagEngine().eraseHandler(handlerID);
406}
407
408//===----------------------------------------------------------------------===//
409// SourceMgrDiagnosticHandler
410//===----------------------------------------------------------------------===//
411namespace mlir {
412namespace detail {
414 /// Return the SrcManager buffer id for the specified file, or zero if none
415 /// can be found.
416 unsigned getSourceMgrBufferIDForFile(llvm::SourceMgr &mgr,
417 StringRef filename) {
418 // Check for an existing mapping to the buffer id for this file.
419 auto bufferIt = filenameToBufId.find(filename);
420 if (bufferIt != filenameToBufId.end())
421 return bufferIt->second;
422
423 // Look for a buffer in the manager that has this filename.
424 for (unsigned i = 1, e = mgr.getNumBuffers() + 1; i != e; ++i) {
425 auto *buf = mgr.getMemoryBuffer(i);
426 if (buf->getBufferIdentifier() == filename)
427 return filenameToBufId[filename] = i;
428 }
429
430 // Otherwise, try to load the source file.
431 std::string ignored;
432 unsigned id = mgr.AddIncludeFile(std::string(filename), SMLoc(), ignored);
433 filenameToBufId[filename] = id;
434 return id;
435 }
436
437 /// Mapping between file name and buffer ID's.
438 llvm::StringMap<unsigned> filenameToBufId;
439};
440} // namespace detail
441} // namespace mlir
442
443/// Return a processable CallSiteLoc from the given location.
444static std::optional<CallSiteLoc> getCallSiteLoc(Location loc) {
445 if (isa<NameLoc>(loc))
446 return getCallSiteLoc(cast<NameLoc>(loc).getChildLoc());
447 if (auto callLoc = dyn_cast<CallSiteLoc>(loc))
448 return callLoc;
449 if (isa<FusedLoc>(loc)) {
450 for (auto subLoc : cast<FusedLoc>(loc).getLocations()) {
451 if (auto callLoc = getCallSiteLoc(subLoc)) {
452 return callLoc;
453 }
454 }
455 return std::nullopt;
456 }
457 return std::nullopt;
458}
459
460/// Given a diagnostic kind, returns the LLVM DiagKind.
461static llvm::SourceMgr::DiagKind getDiagKind(DiagnosticSeverity kind) {
462 switch (kind) {
464 return llvm::SourceMgr::DK_Note;
466 return llvm::SourceMgr::DK_Warning;
468 return llvm::SourceMgr::DK_Error;
470 return llvm::SourceMgr::DK_Remark;
471 }
472 llvm_unreachable("Unknown DiagnosticSeverity");
473}
474
483
488
490
493 bool displaySourceLine) {
494 // Extract a file location from this loc.
495 auto fileLoc = loc->findInstanceOf<FileLineColLoc>();
496
497 // If one doesn't exist, then print the raw message without a source location.
498 if (!fileLoc) {
499 std::string str;
500 llvm::raw_string_ostream strOS(str);
501 if (!llvm::isa<UnknownLoc>(loc))
502 strOS << loc << ": ";
503 strOS << message;
504 return mgr.PrintMessage(os, SMLoc(), getDiagKind(kind), str);
505 }
506
507 // Otherwise if we are displaying the source line, try to convert the file
508 // location to an SMLoc.
509 if (displaySourceLine) {
510 auto smloc = convertLocToSMLoc(fileLoc);
511 if (smloc.isValid())
512 return mgr.PrintMessage(os, smloc, getDiagKind(kind), message);
513 }
514
515 // If the conversion was unsuccessful, create a diagnostic with the file
516 // information. We manually combine the line and column to avoid asserts in
517 // the constructor of SMDiagnostic that takes a location.
518 std::string locStr;
519 llvm::raw_string_ostream locOS(locStr);
520 locOS << fileLoc.getFilename().getValue() << ":" << fileLoc.getLine() << ":"
521 << fileLoc.getColumn();
522 llvm::SMDiagnostic diag(locStr, getDiagKind(kind), message.str());
523 diag.print(nullptr, os);
524}
525
526/// Emit the given diagnostic with the held source manager.
529 auto addLocToStack = [&](Location loc, StringRef locContext) {
530 if (std::optional<Location> showableLoc = findLocToShow(loc))
531 locationStack.emplace_back(*showableLoc, locContext);
532 };
533
534 // Add locations to display for this diagnostic.
535 Location loc = diag.getLocation();
536 addLocToStack(loc, /*locContext=*/{});
537
538 // If the diagnostic location was a call site location, add the call stack as
539 // well.
540 if (auto callLoc = getCallSiteLoc(loc)) {
541 // Print the call stack while valid, or until the limit is reached.
542 loc = callLoc->getCaller();
543 for (unsigned curDepth = 0; curDepth < callStackLimit; ++curDepth) {
544 addLocToStack(loc, "called from");
545 if ((callLoc = getCallSiteLoc(loc)))
546 loc = callLoc->getCaller();
547 else
548 break;
549 }
550 }
551
552 // If the location stack is empty, use the initial location.
553 if (locationStack.empty()) {
554 for (const std::string &str : diag.strs())
555 emitDiagnostic(diag.getLocation(), str, diag.getSeverity());
556
557 // Otherwise, use the location stack.
558 } else {
559 for (const std::string &str : diag.strs())
560 emitDiagnostic(locationStack.front().first, str, diag.getSeverity());
561 for (auto &it : llvm::drop_begin(locationStack))
562 emitDiagnostic(it.first, it.second, DiagnosticSeverity::Note);
563 }
564
565 // Emit each of the notes. Only display the source code if the location is
566 // different from the previous location.
567 for (auto &note : diag.getNotes()) {
568 emitDiagnostic(note.getLocation(), note.str(), note.getSeverity(),
569 /*displaySourceLine=*/loc != note.getLocation());
570 loc = note.getLocation();
571 }
572}
573
575 callStackLimit = limit;
576}
577
578/// Get a memory buffer for the given file, or nullptr if one is not found.
579const llvm::MemoryBuffer *
581 if (unsigned id = impl->getSourceMgrBufferIDForFile(mgr, filename))
582 return mgr.getMemoryBuffer(id);
583 return nullptr;
584}
585
586std::optional<Location>
587SourceMgrDiagnosticHandler::findLocToShow(Location loc) {
588 if (!shouldShowLocFn)
589 return loc;
590 if (!shouldShowLocFn(loc))
591 return std::nullopt;
592
593 // Recurse into the child locations of some of location types.
595 .Case([&](CallSiteLoc callLoc) -> std::optional<Location> {
596 // We recurse into the callee of a call site, as the caller will be
597 // emitted in a different note on the main diagnostic.
598 return findLocToShow(callLoc.getCallee());
599 })
600 .Case([&](FileLineColLoc) -> std::optional<Location> { return loc; })
601 .Case([&](FusedLoc fusedLoc) -> std::optional<Location> {
602 // Fused location is unique in that we try to find a sub-location to
603 // show, rather than the top-level location itself.
604 for (Location childLoc : fusedLoc.getLocations())
605 if (std::optional<Location> showableLoc = findLocToShow(childLoc))
606 return showableLoc;
607 return std::nullopt;
608 })
609 .Case([&](NameLoc nameLoc) -> std::optional<Location> {
610 return findLocToShow(nameLoc.getChildLoc());
611 })
612 .Case([&](OpaqueLoc opaqueLoc) -> std::optional<Location> {
613 // OpaqueLoc always falls back to a different source location.
614 return findLocToShow(opaqueLoc.getFallbackLocation());
615 })
616 .Case([](UnknownLoc) -> std::optional<Location> {
617 // Prefer not to show unknown locations.
618 return std::nullopt;
619 });
620}
621
622/// Get a memory buffer for the given file, or the main file of the source
623/// manager if one doesn't exist. This always returns non-null.
624SMLoc SourceMgrDiagnosticHandler::convertLocToSMLoc(FileLineColLoc loc) {
625 // The column and line may be zero to represent unknown column and/or unknown
626 /// line/column information.
627 if (loc.getLine() == 0 || loc.getColumn() == 0)
628 return SMLoc();
629
630 unsigned bufferId = impl->getSourceMgrBufferIDForFile(mgr, loc.getFilename());
631 if (!bufferId)
632 return SMLoc();
633 return mgr.FindLocForLineAndColumn(bufferId, loc.getLine(), loc.getColumn());
634}
635
636//===----------------------------------------------------------------------===//
637// SourceMgrDiagnosticVerifierHandler
638//===----------------------------------------------------------------------===//
639
640namespace mlir {
641namespace detail {
646
647 /// The generic SourceMgr-based verifier core that this handler delegates
648 /// scanning and matching of 'expected-*' diagnostics to.
649 llvm::SourceMgrDiagnosticVerifier verifier;
650
651 /// Verification level.
653};
654} // namespace detail
655} // namespace mlir
656
657/// Given a diagnostic kind, return a human readable string for it.
658static StringRef getDiagKindStr(DiagnosticSeverity kind) {
659 switch (kind) {
661 return "note";
663 return "warning";
665 return "error";
667 return "remark";
668 }
669 llvm_unreachable("Unknown DiagnosticSeverity");
670}
671
673 llvm::SourceMgr &srcMgr, MLIRContext *ctx, raw_ostream &out, Level level)
674 : SourceMgrDiagnosticHandler(srcMgr, ctx, out),
676 // Compute the expected diagnostics for each of the current files in the
677 // source manager.
678 for (unsigned i = 0, e = mgr.getNumBuffers(); i != e; ++i)
679 (void)impl->verifier.computeExpectedDiags(out, mgr,
680 mgr.getMemoryBuffer(i + 1));
681
682 // The base class registered a handler that prints every diagnostic. The
683 // verifier takes its place: it consumes diagnostics and reports unexpected
684 // ones itself.
685 setHandler([this](Diagnostic &diag) { process(diag); });
686}
687
689 llvm::SourceMgr &srcMgr, MLIRContext *ctx, Level level)
690 : SourceMgrDiagnosticVerifierHandler(srcMgr, ctx, llvm::errs(), level) {}
691
693 // Ensure that all expected diagnostics were handled.
694 (void)verify();
695}
696
697/// Returns the status of the verifier and verifies that all expected
698/// diagnostics were emitted. This return success if all diagnostics were
699/// verified correctly, failure otherwise.
701 return success(impl->verifier.verify(os, mgr));
702}
703
704std::unique_ptr<ScopedDiagnosticHandler>
706 return std::make_unique<ScopedDiagnosticHandler>(
707 ctx, [this](Diagnostic &diag) { process(diag); });
708}
709
710/// Process a diagnostic and its notes.
711void SourceMgrDiagnosticVerifierHandler::process(Diagnostic &diag) {
712 for (const std::string &str : diag.strs())
713 process(diag.getLocation(), str, diag.getSeverity());
714 for (auto &note : diag.getNotes())
715 process(note);
716}
717
718/// Process a diagnostic at a certain location.
719void SourceMgrDiagnosticVerifierHandler::process(LocationAttr loc,
720 StringRef msg,
721 DiagnosticSeverity kind) {
723 const llvm::MemoryBuffer *buf =
724 fileLoc ? getBufferForFile(fileLoc.getFilename()) : nullptr;
725 bool reportUnexpected = impl->level != Level::OnlyExpected;
726
727 using MatchResult = llvm::SourceMgrDiagnosticVerifier::MatchResult;
728 MatchResult result = impl->verifier.process(
729 os, mgr, getDiagKind(kind), /*hasLoc=*/static_cast<bool>(fileLoc), buf,
730 fileLoc ? fileLoc.getLine() : 0, msg, reportUnexpected);
731 if (result == MatchResult::Unexpected)
732 emitDiagnostic(loc, "unexpected " + getDiagKindStr(kind) + ": " + msg,
734}
735
736//===----------------------------------------------------------------------===//
737// ParallelDiagnosticHandler
738//===----------------------------------------------------------------------===//
739
740namespace mlir {
741namespace detail {
742/// Unique ids for parallel diagnostic handlers, so the thread-local slot
743/// below can name a handler without holding a raw pointer to it: a handler
744/// may be destroyed while a thread that skipped its erase still names it.
745static std::atomic<uint64_t> nextHandlerUniqueID{1};
746
747/// One entry of the thread-local nesting stack: the handler an element
748/// belongs to and its order id. Plain data so the thread-locals below stay
749/// trivially destructible.
751 uint64_t handlerID;
752 size_t orderID;
753};
754
755/// How many nested parallel loops one thread tracks without a lock. Past
756/// this the handler's map is the overflow.
757constexpr unsigned kMaxThreadOrderEntries = 4;
758
759/// The order ids for the current thread, one entry per enclosing parallel
760/// loop, innermost last. A parallel loop sets and erases a thread's order id
761/// once per element, and doing that under the handler's mutex made the mutex
762/// the most contended lock of a loop over many small elements. A thread
763/// processes one element at a time for each handler, so a small stack covers
764/// every realistic nesting depth. Erases are LIFO, matching loop nesting.
765/// Reentrant use of the same handler on one thread remains unsupported, as
766/// before.
767static LLVM_THREAD_LOCAL ThreadOrderEntry
769static LLVM_THREAD_LOCAL unsigned threadOrderDepth = 0;
770
771struct ParallelDiagnosticHandlerImpl : public llvm::PrettyStackTraceEntry {
774 : id(id), diag(std::move(diag)) {}
775 bool operator<(const ThreadDiagnostic &rhs) const { return id < rhs.id; }
776
777 /// The id for this diagnostic, this is used for ordering.
778 /// Note: This id corresponds to the ordered position of the current element
779 /// being processed by a given thread.
780 size_t id;
781
782 /// The diagnostic.
784 };
785
787 handlerID = ctx->getDiagEngine().registerHandler([this](Diagnostic &diag) {
788 // Fast path: scan the thread-local stack for this handler, no lock.
789 // Appending below still locks.
790 for (unsigned i = threadOrderDepth; i > 0; --i) {
792 llvm::sys::SmartScopedLock<true> lock(mutex);
793 diagnostics.emplace_back(threadOrderStack[i - 1].orderID,
794 std::move(diag));
795 return success();
796 }
797 }
798 // Slow path: nested past the stack, consult the map. If this
799 // thread is not tracked, return failure to let another handler
800 // process this diagnostic.
801 uint64_t tid = llvm::get_threadid();
802 llvm::sys::SmartScopedLock<true> lock(mutex);
803 auto it = threadToOrderID.find(tid);
804 if (it == threadToOrderID.end())
805 return failure();
806 diagnostics.emplace_back(it->second, std::move(diag));
807 return success();
808 });
809 }
810
812 // Erase this handler from the context.
813 context->getDiagEngine().eraseHandler(handlerID);
814
815 // Early exit if there are no diagnostics, this is the common case.
816 if (diagnostics.empty())
817 return;
818
819 // Emit the diagnostics back to the context.
821 return context->getDiagEngine().emit(std::move(diag));
822 });
823 }
824
825 /// Utility method to emit any held diagnostics.
826 void emitDiagnostics(llvm::function_ref<void(Diagnostic &)> emitFn) const {
827 // Stable sort all of the diagnostics that were emitted. This creates a
828 // deterministic ordering for the diagnostics based upon which order id they
829 // were emitted for.
830 llvm::stable_sort(diagnostics);
831
832 // Emit each diagnostic to the context again.
834 emitFn(diag.diag);
835 }
836
837 /// Set the order id for the current thread.
838 void setOrderIDForThread(size_t orderID) {
839 if (threadOrderDepth > 0 &&
841 threadOrderStack[threadOrderDepth - 1].orderID = orderID;
842 return;
843 }
846 return;
847 }
848 uint64_t tid = llvm::get_threadid();
849 llvm::sys::SmartScopedLock<true> lock(mutex);
850 threadToOrderID[tid] = orderID;
851 }
852
853 /// Remove the order id for the current thread.
855 if (threadOrderDepth > 0 &&
858 return;
859 }
860 uint64_t tid = llvm::get_threadid();
861 llvm::sys::SmartScopedLock<true> lock(mutex);
862 threadToOrderID.erase(tid);
863 }
864
865 /// Dump the current diagnostics that were inflight.
866 void print(raw_ostream &os) const override {
867 // Early exit if there are no diagnostics, this is the common case.
868 if (diagnostics.empty())
869 return;
870
871 os << "In-Flight Diagnostics:\n";
872 emitDiagnostics([&](const Diagnostic &diag) {
873 os.indent(4);
874
875 // Print each diagnostic with the format:
876 // "<location>: <kind>: <msg>"
877 if (!llvm::isa<UnknownLoc>(diag.getLocation()))
878 os << diag.getLocation() << ": ";
879 switch (diag.getSeverity()) {
881 os << "error: ";
882 break;
884 os << "warning: ";
885 break;
887 os << "note: ";
888 break;
890 os << "remark: ";
891 break;
892 }
893 os << diag << '\n';
894 });
895 }
896
897 /// A smart mutex to lock access to the internal state.
899
900 /// Order ids, keyed by thread id, for threads nested past what the
901 /// thread-local stack holds: more enclosing parallel loops on one thread
902 /// than entries. Guarded by `mutex`. A thread in neither the stack nor
903 /// this map is not tracked, and its diagnostics pass on to the next
904 /// handler.
906
907 /// An unordered list of diagnostics that were emitted.
908 mutable std::vector<ThreadDiagnostic> diagnostics;
909
910 /// The unique id for the parallel handler.
912
913 /// Unique id naming this handler in the thread-local order-id stack.
914 const uint64_t handlerUniqueID =
915 nextHandlerUniqueID.fetch_add(1, std::memory_order_relaxed);
916
917 /// The context to emit the diagnostics to.
919};
920} // namespace detail
921} // namespace mlir
922
926
927/// Set the order id for the current thread.
929 impl->setOrderIDForThread(orderID);
930}
931
932/// Remove the order id for the current thread. This removes the thread from
933/// diagnostics tracking.
935 impl->eraseOrderIDForThread();
936}
return success()
static OpPrintingFlags adjustPrintingFlags(OpPrintingFlags flags, DiagnosticSeverity severity)
Adjusts operation printing flags used in diagnostics for the given severity level.
static StringRef twineToStrRef(const Twine &val, SmallVectorImpl< std::unique_ptr< char[]> > &strings)
Convert a Twine to a StringRef.
static InFlightDiagnostic emitDiag(Location location, DiagnosticSeverity severity, const Twine &message)
Helper function used to emit a diagnostic with an optionally empty twine message.
static StringRef getDiagKindStr(DiagnosticSeverity kind)
Given a diagnostic kind, return a human readable string for it.
static std::optional< CallSiteLoc > getCallSiteLoc(Location loc)
Return a processable CallSiteLoc from the given location.
static llvm::SourceMgr::DiagKind getDiagKind(DiagnosticSeverity kind)
Given a diagnostic kind, returns the LLVM DiagKind.
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
MLIRContext * getContext() const
Return the context this attribute belongs to.
static Attribute getFromOpaquePointer(const void *ptr)
Construct an attribute from the opaque pointer representation.
Definition Attributes.h:75
A variant type that holds a single argument for a diagnostic.
Definition Diagnostics.h:53
DiagnosticArgumentKind
Enum that represents the different kinds of diagnostic arguments supported.
Definition Diagnostics.h:85
StringRef getAsString() const
Returns this argument as a string.
double getAsDouble() const
Returns this argument as a double.
DiagnosticArgument(Attribute attr)
Note: The constructors below are only exposed due to problems accessing constructors from type traits...
Type getAsType() const
Returns this argument as a Type.
int64_t getAsInteger() const
Returns this argument as a signed integer.
DiagnosticArgumentKind getKind() const
Returns the kind of this argument.
Definition Diagnostics.h:98
Attribute getAsAttribute() const
Returns this argument as an Attribute.
void print(raw_ostream &os) const
Outputs this argument to a stream.
uint64_t getAsUnsigned() const
Returns this argument as an unsigned integer.
uint64_t HandlerID
A handle to a specific registered handler object.
InFlightDiagnostic emit(Location loc, DiagnosticSeverity severity)
Create a new inflight diagnostic with the given location and severity.
void eraseHandler(HandlerID id)
Erase the registered diagnostic handler with the given identifier.
llvm::unique_function< LogicalResult(Diagnostic &)> HandlerTy
The handler type for MLIR diagnostics.
HandlerID registerHandler(HandlerTy handler)
Register a new handler for diagnostics to the engine.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
std::string str() const
Converts the diagnostic to a string, concatenating all the parts if there are multiples.
void startNewMessagePart()
Starts a new message part.
SmallVector< std::string > strs() const
Converts each message part to a separate string.
Diagnostic & attachNote(std::optional< Location > noteLoc=std::nullopt)
Attaches a note to this diagnostic.
MutableArrayRef< DiagnosticArgument > getArguments()
Returns the current list of diagnostic arguments.
Diagnostic(Location loc, DiagnosticSeverity severity)
std::enable_if_t<!std::is_convertible< Arg, StringRef >::value &&std::is_constructible< DiagnosticArgument, Arg >::value, Diagnostic & > operator<<(Arg &&val)
Stream operator for inserting new diagnostic arguments.
void print(raw_ostream &os, std::optional< int64_t > messagePartIndex=std::nullopt) const
Outputs this diagnostic to a stream.
Diagnostic & appendOp(Operation &op, const OpPrintingFlags &flags)
Append an operation with the given printing flags.
An instance of this location represents a tuple of file, line number, and column number.
Definition Location.h:174
unsigned getLine() const
Definition Location.cpp:173
StringAttr getFilename() const
Definition Location.cpp:169
unsigned getColumn() const
Definition Location.cpp:175
This class represents a diagnostic that is inflight and set to be reported.
void report()
Reports the diagnostic to the engine.
void abandon()
Abandons this diagnostic so that it will no longer be reported.
Location objects represent source locations information in MLIR.
Definition Location.h:32
T findInstanceOf()
Return an instance of the given location type if one is nested under the current location.
Definition Location.h:45
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
bool shouldPrintStackTraceOnDiagnostic()
Return true if we should attach the current stacktrace to diagnostics when emitted.
DiagnosticEngine & getDiagEngine()
Returns the diagnostic engine for this context.
Set of flags used to control the behavior of the various IR print methods (e.g.
OpPrintingFlags & elideLargeElementsAttrs(int64_t largeElementLimit=16)
Enables the elision of large elements attributes by printing a lexically valid but otherwise meaningl...
OpPrintingFlags & printGenericOpForm(bool enable=true)
Always print operations in the generic form.
OpPrintingFlags & useLocalScope(bool enable=true)
Use local scope when printing the operation.
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
Definition Operation.h:1169
Operation * getOperation() const
Definition Operation.h:1175
OpPrintingFlags & flags()
Definition Operation.h:1173
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
void print(raw_ostream &os, const OpPrintingFlags &flags={})
void eraseOrderIDForThread()
Remove the order id for the current thread.
ParallelDiagnosticHandler(MLIRContext *ctx)
void setOrderIDForThread(size_t orderID)
Set the order id for the current thread.
ScopedDiagnosticHandler(MLIRContext *ctx)
void setHandler(FuncTy &&handler)
Set the handler to manage via RAII.
void setCallStackLimit(unsigned limit)
Set the maximum depth that a call stack will be printed. Defaults to 10.
void emitDiagnostic(Location loc, Twine message, DiagnosticSeverity kind, bool displaySourceLine=true)
Emit the given diagnostic information with the held source manager.
raw_ostream & os
The output stream to use when printing diagnostics.
SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr, MLIRContext *ctx, raw_ostream &os, ShouldShowLocFn &&shouldShowLocFn={})
ShouldShowLocFn shouldShowLocFn
A functor used when determining if a location for a diagnostic should be shown.
const llvm::MemoryBuffer * getBufferForFile(StringRef filename)
Get a memory buffer for the given file, or nullptr if no file is available.
llvm::SourceMgr & mgr
The source manager that we are wrapping.
llvm::unique_function< bool(Location)> ShouldShowLocFn
This type represents a functor used to filter out locations when printing a diagnostic.
SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx, raw_ostream &out, Level level=Level::All)
std::unique_ptr< ScopedDiagnosticHandler > registerInContext(MLIRContext *ctx)
Register this handler with ctx and return a scoped registration.
LogicalResult verify()
Returns the status of the handler and verifies that all expected diagnostics were emitted.
static Type getFromOpaquePointer(const void *pointer)
Definition Types.h:170
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
void print(raw_ostream &os) const
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition CallGraph.h:227
AttrTypeReplacer.
constexpr unsigned kMaxThreadOrderEntries
How many nested parallel loops one thread tracks without a lock.
static LLVM_THREAD_LOCAL ThreadOrderEntry threadOrderStack[kMaxThreadOrderEntries]
The order ids for the current thread, one entry per enclosing parallel loop, innermost last.
static LLVM_THREAD_LOCAL unsigned threadOrderDepth
static std::atomic< uint64_t > nextHandlerUniqueID
Unique ids for parallel diagnostic handlers, so the thread-local slot below can name a handler withou...
Include the generated interface declarations.
InFlightDiagnostic emitWarning(Location loc)
Utility method to emit a warning message using this location.
DiagnosticSeverity
Defines the different supported severity of a diagnostic.
Definition Diagnostics.h:41
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
InFlightDiagnostic emitRemark(Location loc)
Utility method to emit a remark message using this location.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:147
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:123
DiagnosticEngine::HandlerID uniqueHandlerId
This is a unique identifier counter for diagnostic handlers in the context.
llvm::sys::SmartMutex< true > mutex
A mutex to ensure that diagnostics emission is thread-safe.
void emit(Diagnostic &&diag)
Emit a diagnostic using the registered issue handle if present, or with the default behavior if not.
llvm::SmallMapVector< DiagnosticEngine::HandlerID, DiagnosticEngine::HandlerTy, 2 > handlers
These are the handlers used to report diagnostics.
size_t id
The id for this diagnostic, this is used for ordering.
void print(raw_ostream &os) const override
Dump the current diagnostics that were inflight.
const uint64_t handlerUniqueID
Unique id naming this handler in the thread-local order-id stack.
MLIRContext * context
The context to emit the diagnostics to.
void emitDiagnostics(llvm::function_ref< void(Diagnostic &)> emitFn) const
Utility method to emit any held diagnostics.
DiagnosticEngine::HandlerID handlerID
The unique id for the parallel handler.
DenseMap< uint64_t, size_t > threadToOrderID
Order ids, keyed by thread id, for threads nested past what the thread-local stack holds: more enclos...
void setOrderIDForThread(size_t orderID)
Set the order id for the current thread.
std::vector< ThreadDiagnostic > diagnostics
An unordered list of diagnostics that were emitted.
void eraseOrderIDForThread()
Remove the order id for the current thread.
llvm::sys::SmartMutex< true > mutex
A smart mutex to lock access to the internal state.
llvm::StringMap< unsigned > filenameToBufId
Mapping between file name and buffer ID's.
unsigned getSourceMgrBufferIDForFile(llvm::SourceMgr &mgr, StringRef filename)
Return the SrcManager buffer id for the specified file, or zero if none can be found.
SourceMgrDiagnosticVerifierHandler::Level level
Verification level.
SourceMgrDiagnosticVerifierHandlerImpl(SourceMgrDiagnosticVerifierHandler::Level level)
llvm::SourceMgrDiagnosticVerifier verifier
The generic SourceMgr-based verifier core that this handler delegates scanning and matching of 'expec...
One entry of the thread-local nesting stack: the handler an element belongs to and its order id.