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(const_cast<void *>(attr.getAsOpaquePointer())) {}
40
41/// Construct from a Type.
44 opaqueVal(const_cast<void *>(val.getAsOpaquePointer())) {}
45
46/// Returns this argument as an Attribute.
51
52/// Returns this argument as a Type.
57
58/// Outputs this argument to a stream.
60 switch (kind) {
62 os << getAsAttribute();
63 break;
65 os << getAsDouble();
66 break;
68 os << getAsInteger();
69 break;
71 os << getAsString();
72 break;
74 os << '\'' << getAsType() << '\'';
75 break;
77 os << getAsUnsigned();
78 break;
79 }
80}
81
82//===----------------------------------------------------------------------===//
83// Diagnostic
84//===----------------------------------------------------------------------===//
85
86/// Convert a Twine to a StringRef. Memory used for generating the StringRef is
87/// stored in 'strings'.
88static StringRef
89twineToStrRef(const Twine &val,
90 SmallVectorImpl<std::unique_ptr<char[]>> &strings) {
91 // Allocate memory to hold this string.
92 SmallString<64> data;
93 auto strRef = val.toStringRef(data);
94 if (strRef.empty())
95 return strRef;
96
97 strings.push_back(std::unique_ptr<char[]>(new char[strRef.size()]));
98 memcpy(&strings.back()[0], strRef.data(), strRef.size());
99 // Return a reference to the new string.
100 return StringRef(&strings.back()[0], strRef.size());
101}
102
104 return DiagnosticArgument(twineToStrRef(val, strings));
105}
106
107/// Stream in a Twine argument.
108Diagnostic &Diagnostic::operator<<(char val) { return *this << Twine(val); }
110 arguments.push_back(getStringArgument(val));
111 return *this;
112}
114 arguments.push_back(getStringArgument(val));
115 return *this;
116}
117
119 arguments.push_back(DiagnosticArgument(val));
120 return *this;
121}
122
123/// Stream in an OperationName.
125 // An OperationName is stored in the context, so we don't need to worry about
126 // the lifetime of its data.
127 arguments.push_back(DiagnosticArgument(val.getStringRef()));
128 return *this;
129}
130
131/// Adjusts operation printing flags used in diagnostics for the given severity
132/// level.
134 DiagnosticSeverity severity) {
135 flags.useLocalScope();
137 if (severity == DiagnosticSeverity::Error)
138 flags.printGenericOpForm();
139 return flags;
140}
141
142/// Stream in an Operation.
146
150
152 std::string str;
153 llvm::raw_string_ostream os(str);
154 op.print(os, adjustPrintingFlags(flags, severity));
155 // Print on a new line for better readability if the op will be printed on
156 // multiple lines.
157 if (str.find('\n') != std::string::npos)
158 *this << '\n';
159 return *this << str;
160}
161
162/// Stream in a Value.
164 std::string str;
165 llvm::raw_string_ostream os(str);
166 val.print(os, adjustPrintingFlags(OpPrintingFlags(), severity));
167 return *this << str;
168}
169
170/// Outputs this diagnostic to a stream.
172 std::optional<int64_t> messagePartIndex) const {
173 if (!messagePartIndex.has_value()) {
174 for (auto &arg : getArguments())
175 arg.print(os);
176 return;
177 }
178
179 assert(0 <= *messagePartIndex &&
180 *messagePartIndex <= static_cast<int64_t>(messagePartEnds.size()));
181 size_t argumentStart =
182 *messagePartIndex == 0 ? 0 : messagePartEnds[*messagePartIndex - 1];
183 size_t argumentEnd =
184 *messagePartIndex == static_cast<int64_t>(messagePartEnds.size())
185 ? arguments.size()
186 : messagePartEnds[*messagePartIndex];
187 for (auto &arg :
188 getArguments().slice(argumentStart, argumentEnd - argumentStart))
189 arg.print(os);
190}
191
192/// Convert the diagnostic to a string.
193std::string Diagnostic::str() const {
194 std::string str;
195 llvm::raw_string_ostream os(str);
196 print(os);
197 return str;
198}
199
200/// Converts each message part to a separate string.
203 size_t numMessageParts = messagePartEnds.size();
204
205 // Include the current message part if there are no completed parts or if it
206 // contains arguments after the last completed part.
207 if (messagePartEnds.empty() || messagePartEnds.back() != arguments.size())
208 ++numMessageParts;
209 for (size_t i = 0; i < numMessageParts; ++i) {
210 std::string str;
211 llvm::raw_string_ostream os(str);
212 print(os, i);
213 strs.push_back(str);
214 }
215 return strs;
216}
217
218/// Attaches a note to this diagnostic. A new location may be optionally
219/// provided, if not, then the location defaults to the one specified for this
220/// diagnostic. Notes may not be attached to other notes.
221Diagnostic &Diagnostic::attachNote(std::optional<Location> noteLoc) {
222 // We don't allow attaching notes to notes.
223 assert(severity != DiagnosticSeverity::Note &&
224 "cannot attach a note to a note");
225
226 // If a location wasn't provided then reuse our location.
227 if (!noteLoc)
228 noteLoc = loc;
229
230 /// Append and return a new note.
231 notes.push_back(
232 std::make_unique<Diagnostic>(*noteLoc, DiagnosticSeverity::Note));
233 return *notes.back();
234}
235
236/// Allow a diagnostic to be converted to 'failure'.
237Diagnostic::operator LogicalResult() const { return failure(); }
238
239/// Starts a new message part.
241 if (arguments.empty())
242 return;
243 if (!messagePartEnds.empty() && messagePartEnds.back() == arguments.size())
244 return;
245 messagePartEnds.push_back(arguments.size());
246}
247
248//===----------------------------------------------------------------------===//
249// InFlightDiagnostic
250//===----------------------------------------------------------------------===//
251
252/// Allow an inflight diagnostic to be converted to 'failure', otherwise
253/// 'success' if this is an empty diagnostic.
254InFlightDiagnostic::operator LogicalResult() const {
255 return failure(isActive());
256}
257
258/// Reports the diagnostic to the engine.
260 // If this diagnostic is still inflight and it hasn't been abandoned, then
261 // report it.
262 if (isInFlight()) {
263 owner->emit(std::move(*impl));
264 owner = nullptr;
265 }
266 impl.reset();
267}
268
269/// Abandons this diagnostic.
270void InFlightDiagnostic::abandon() { owner = nullptr; }
271
272//===----------------------------------------------------------------------===//
273// DiagnosticEngineImpl
274//===----------------------------------------------------------------------===//
275
276namespace mlir {
277namespace detail {
279 /// Emit a diagnostic using the registered issue handle if present, or with
280 /// the default behavior if not.
281 void emit(Diagnostic &&diag);
282
283 /// A mutex to ensure that diagnostics emission is thread-safe.
285
286 /// These are the handlers used to report diagnostics.
288 2>
290
291 /// This is a unique identifier counter for diagnostic handlers in the
292 /// context. This id starts at 1 to allow for 0 to be used as a sentinel.
294};
295} // namespace detail
296} // namespace mlir
297
298/// Emit a diagnostic using the registered issue handle if present, or with
299/// the default behavior if not.
301 llvm::sys::SmartScopedLock<true> lock(mutex);
302
303 // Try to process the given diagnostic on one of the registered handlers.
304 // Handlers are walked in reverse order, so that the most recent handler is
305 // processed first.
306 for (auto &handlerIt : llvm::reverse(handlers))
307 if (succeeded(handlerIt.second(diag)))
308 return;
309
310 // Otherwise, if this is an error we emit it to stderr.
311 if (diag.getSeverity() != DiagnosticSeverity::Error)
312 return;
313
314 auto &os = llvm::errs();
315 if (!llvm::isa<UnknownLoc>(diag.getLocation()))
316 os << diag.getLocation() << ": ";
317 os << "error: ";
318
319 // The default behavior for errors is to emit them to stderr.
320 os << diag << '\n';
321 os.flush();
322}
323
324//===----------------------------------------------------------------------===//
325// DiagnosticEngine
326//===----------------------------------------------------------------------===//
327
328DiagnosticEngine::DiagnosticEngine() : impl(new DiagnosticEngineImpl()) {}
330
331/// Register a new handler for diagnostics to the engine. This function returns
332/// a unique identifier for the registered handler, which can be used to
333/// unregister this handler at a later time.
335 llvm::sys::SmartScopedLock<true> lock(impl->mutex);
336 auto uniqueID = impl->uniqueHandlerId++;
337 impl->handlers.insert({uniqueID, std::move(handler)});
338 return uniqueID;
339}
340
341/// Erase the registered diagnostic handler with the given identifier.
343 llvm::sys::SmartScopedLock<true> lock(impl->mutex);
344 impl->handlers.erase(handlerID);
345}
346
347/// Emit a diagnostic using the registered issue handler if present, or with
348/// the default behavior if not.
350 assert(diag.getSeverity() != DiagnosticSeverity::Note &&
351 "notes should not be emitted directly");
352 impl->emit(std::move(diag));
353}
354
355/// Helper function used to emit a diagnostic with an optionally empty twine
356/// message. If the message is empty, then it is not inserted into the
357/// diagnostic.
359emitDiag(Location location, DiagnosticSeverity severity, const Twine &message) {
360 MLIRContext *ctx = location->getContext();
361 auto &diagEngine = ctx->getDiagEngine();
362 auto diag = diagEngine.emit(location, severity);
363 if (!message.isTriviallyEmpty())
364 diag << message;
365
366 // Add the stack trace as a note if necessary.
368 std::string bt;
369 {
370 llvm::raw_string_ostream stream(bt);
371 llvm::sys::PrintStackTrace(stream);
372 }
373 if (!bt.empty())
374 diag.attachNote() << "diagnostic emitted with trace:\n" << bt;
375 }
376
377 return diag;
378}
379
380/// Emit an error message using this location.
382InFlightDiagnostic mlir::emitError(Location loc, const Twine &message) {
383 return emitDiag(loc, DiagnosticSeverity::Error, message);
384}
385
386/// Emit a warning message using this location.
391 return emitDiag(loc, DiagnosticSeverity::Warning, message);
392}
393
394/// Emit a remark message using this location.
398InFlightDiagnostic mlir::emitRemark(Location loc, const Twine &message) {
399 return emitDiag(loc, DiagnosticSeverity::Remark, message);
400}
401
402//===----------------------------------------------------------------------===//
403// ScopedDiagnosticHandler
404//===----------------------------------------------------------------------===//
405
407 if (handlerID)
408 ctx->getDiagEngine().eraseHandler(handlerID);
409}
410
411//===----------------------------------------------------------------------===//
412// SourceMgrDiagnosticHandler
413//===----------------------------------------------------------------------===//
414namespace mlir {
415namespace detail {
417 /// Return the SrcManager buffer id for the specified file, or zero if none
418 /// can be found.
419 unsigned getSourceMgrBufferIDForFile(llvm::SourceMgr &mgr,
420 StringRef filename) {
421 // Check for an existing mapping to the buffer id for this file.
422 auto bufferIt = filenameToBufId.find(filename);
423 if (bufferIt != filenameToBufId.end())
424 return bufferIt->second;
425
426 // Look for a buffer in the manager that has this filename.
427 for (unsigned i = 1, e = mgr.getNumBuffers() + 1; i != e; ++i) {
428 auto *buf = mgr.getMemoryBuffer(i);
429 if (buf->getBufferIdentifier() == filename)
430 return filenameToBufId[filename] = i;
431 }
432
433 // Otherwise, try to load the source file.
434 std::string ignored;
435 unsigned id = mgr.AddIncludeFile(std::string(filename), SMLoc(), ignored);
436 filenameToBufId[filename] = id;
437 return id;
438 }
439
440 /// Mapping between file name and buffer ID's.
441 llvm::StringMap<unsigned> filenameToBufId;
442};
443} // namespace detail
444} // namespace mlir
445
446/// Return a processable CallSiteLoc from the given location.
447static std::optional<CallSiteLoc> getCallSiteLoc(Location loc) {
448 if (isa<NameLoc>(loc))
449 return getCallSiteLoc(cast<NameLoc>(loc).getChildLoc());
450 if (auto callLoc = dyn_cast<CallSiteLoc>(loc))
451 return callLoc;
452 if (isa<FusedLoc>(loc)) {
453 for (auto subLoc : cast<FusedLoc>(loc).getLocations()) {
454 if (auto callLoc = getCallSiteLoc(subLoc)) {
455 return callLoc;
456 }
457 }
458 return std::nullopt;
459 }
460 return std::nullopt;
461}
462
463/// Given a diagnostic kind, returns the LLVM DiagKind.
464static llvm::SourceMgr::DiagKind getDiagKind(DiagnosticSeverity kind) {
465 switch (kind) {
467 return llvm::SourceMgr::DK_Note;
469 return llvm::SourceMgr::DK_Warning;
471 return llvm::SourceMgr::DK_Error;
473 return llvm::SourceMgr::DK_Remark;
474 }
475 llvm_unreachable("Unknown DiagnosticSeverity");
476}
477
486
491
493
496 bool displaySourceLine) {
497 // Extract a file location from this loc.
498 auto fileLoc = loc->findInstanceOf<FileLineColLoc>();
499
500 // If one doesn't exist, then print the raw message without a source location.
501 if (!fileLoc) {
502 std::string str;
503 llvm::raw_string_ostream strOS(str);
504 if (!llvm::isa<UnknownLoc>(loc))
505 strOS << loc << ": ";
506 strOS << message;
507 return mgr.PrintMessage(os, SMLoc(), getDiagKind(kind), str);
508 }
509
510 // Otherwise if we are displaying the source line, try to convert the file
511 // location to an SMLoc.
512 if (displaySourceLine) {
513 auto smloc = convertLocToSMLoc(fileLoc);
514 if (smloc.isValid())
515 return mgr.PrintMessage(os, smloc, getDiagKind(kind), message);
516 }
517
518 // If the conversion was unsuccessful, create a diagnostic with the file
519 // information. We manually combine the line and column to avoid asserts in
520 // the constructor of SMDiagnostic that takes a location.
521 std::string locStr;
522 llvm::raw_string_ostream locOS(locStr);
523 locOS << fileLoc.getFilename().getValue() << ":" << fileLoc.getLine() << ":"
524 << fileLoc.getColumn();
525 llvm::SMDiagnostic diag(locStr, getDiagKind(kind), message.str());
526 diag.print(nullptr, os);
527}
528
529/// Emit the given diagnostic with the held source manager.
532 auto addLocToStack = [&](Location loc, StringRef locContext) {
533 if (std::optional<Location> showableLoc = findLocToShow(loc))
534 locationStack.emplace_back(*showableLoc, locContext);
535 };
536
537 // Add locations to display for this diagnostic.
538 Location loc = diag.getLocation();
539 addLocToStack(loc, /*locContext=*/{});
540
541 // If the diagnostic location was a call site location, add the call stack as
542 // well.
543 if (auto callLoc = getCallSiteLoc(loc)) {
544 // Print the call stack while valid, or until the limit is reached.
545 loc = callLoc->getCaller();
546 for (unsigned curDepth = 0; curDepth < callStackLimit; ++curDepth) {
547 addLocToStack(loc, "called from");
548 if ((callLoc = getCallSiteLoc(loc)))
549 loc = callLoc->getCaller();
550 else
551 break;
552 }
553 }
554
555 // If the location stack is empty, use the initial location.
556 if (locationStack.empty()) {
557 for (const std::string &str : diag.strs())
558 emitDiagnostic(diag.getLocation(), str, diag.getSeverity());
559
560 // Otherwise, use the location stack.
561 } else {
562 for (const std::string &str : diag.strs())
563 emitDiagnostic(locationStack.front().first, str, diag.getSeverity());
564 for (auto &it : llvm::drop_begin(locationStack))
565 emitDiagnostic(it.first, it.second, DiagnosticSeverity::Note);
566 }
567
568 // Emit each of the notes. Only display the source code if the location is
569 // different from the previous location.
570 for (auto &note : diag.getNotes()) {
571 emitDiagnostic(note.getLocation(), note.str(), note.getSeverity(),
572 /*displaySourceLine=*/loc != note.getLocation());
573 loc = note.getLocation();
574 }
575}
576
578 callStackLimit = limit;
579}
580
581/// Get a memory buffer for the given file, or nullptr if one is not found.
582const llvm::MemoryBuffer *
584 if (unsigned id = impl->getSourceMgrBufferIDForFile(mgr, filename))
585 return mgr.getMemoryBuffer(id);
586 return nullptr;
587}
588
589std::optional<Location>
590SourceMgrDiagnosticHandler::findLocToShow(Location loc) {
591 if (!shouldShowLocFn)
592 return loc;
593 if (!shouldShowLocFn(loc))
594 return std::nullopt;
595
596 // Recurse into the child locations of some of location types.
598 .Case([&](CallSiteLoc callLoc) -> std::optional<Location> {
599 // We recurse into the callee of a call site, as the caller will be
600 // emitted in a different note on the main diagnostic.
601 return findLocToShow(callLoc.getCallee());
602 })
603 .Case([&](FileLineColLoc) -> std::optional<Location> { return loc; })
604 .Case([&](FusedLoc fusedLoc) -> std::optional<Location> {
605 // Fused location is unique in that we try to find a sub-location to
606 // show, rather than the top-level location itself.
607 for (Location childLoc : fusedLoc.getLocations())
608 if (std::optional<Location> showableLoc = findLocToShow(childLoc))
609 return showableLoc;
610 return std::nullopt;
611 })
612 .Case([&](NameLoc nameLoc) -> std::optional<Location> {
613 return findLocToShow(nameLoc.getChildLoc());
614 })
615 .Case([&](OpaqueLoc opaqueLoc) -> std::optional<Location> {
616 // OpaqueLoc always falls back to a different source location.
617 return findLocToShow(opaqueLoc.getFallbackLocation());
618 })
619 .Case([](UnknownLoc) -> std::optional<Location> {
620 // Prefer not to show unknown locations.
621 return std::nullopt;
622 });
623}
624
625/// Get a memory buffer for the given file, or the main file of the source
626/// manager if one doesn't exist. This always returns non-null.
627SMLoc SourceMgrDiagnosticHandler::convertLocToSMLoc(FileLineColLoc loc) {
628 // The column and line may be zero to represent unknown column and/or unknown
629 /// line/column information.
630 if (loc.getLine() == 0 || loc.getColumn() == 0)
631 return SMLoc();
632
633 unsigned bufferId = impl->getSourceMgrBufferIDForFile(mgr, loc.getFilename());
634 if (!bufferId)
635 return SMLoc();
636 return mgr.FindLocForLineAndColumn(bufferId, loc.getLine(), loc.getColumn());
637}
638
639//===----------------------------------------------------------------------===//
640// SourceMgrDiagnosticVerifierHandler
641//===----------------------------------------------------------------------===//
642
643namespace mlir {
644namespace detail {
649
650 /// The generic SourceMgr-based verifier core that this handler delegates
651 /// scanning and matching of 'expected-*' diagnostics to.
652 llvm::SourceMgrDiagnosticVerifier verifier;
653
654 /// Verification level.
656};
657} // namespace detail
658} // namespace mlir
659
660/// Given a diagnostic kind, return a human readable string for it.
661static StringRef getDiagKindStr(DiagnosticSeverity kind) {
662 switch (kind) {
664 return "note";
666 return "warning";
668 return "error";
670 return "remark";
671 }
672 llvm_unreachable("Unknown DiagnosticSeverity");
673}
674
676 llvm::SourceMgr &srcMgr, MLIRContext *ctx, raw_ostream &out, Level level)
677 : SourceMgrDiagnosticHandler(srcMgr, ctx, out),
679 // Compute the expected diagnostics for each of the current files in the
680 // source manager.
681 for (unsigned i = 0, e = mgr.getNumBuffers(); i != e; ++i)
682 (void)impl->verifier.computeExpectedDiags(out, mgr,
683 mgr.getMemoryBuffer(i + 1));
684
685 // The base class registered a handler that prints every diagnostic. The
686 // verifier takes its place: it consumes diagnostics and reports unexpected
687 // ones itself.
688 setHandler([this](Diagnostic &diag) { process(diag); });
689}
690
692 llvm::SourceMgr &srcMgr, MLIRContext *ctx, Level level)
693 : SourceMgrDiagnosticVerifierHandler(srcMgr, ctx, llvm::errs(), level) {}
694
696 // Ensure that all expected diagnostics were handled.
697 (void)verify();
698}
699
700/// Returns the status of the verifier and verifies that all expected
701/// diagnostics were emitted. This return success if all diagnostics were
702/// verified correctly, failure otherwise.
704 return success(impl->verifier.verify(os, mgr));
705}
706
707std::unique_ptr<ScopedDiagnosticHandler>
709 return std::make_unique<ScopedDiagnosticHandler>(
710 ctx, [this](Diagnostic &diag) { process(diag); });
711}
712
713/// Process a diagnostic and its notes.
714void SourceMgrDiagnosticVerifierHandler::process(Diagnostic &diag) {
715 for (const std::string &str : diag.strs())
716 process(diag.getLocation(), str, diag.getSeverity());
717 for (auto &note : diag.getNotes())
718 process(note);
719}
720
721/// Process a diagnostic at a certain location.
722void SourceMgrDiagnosticVerifierHandler::process(LocationAttr loc,
723 StringRef msg,
724 DiagnosticSeverity kind) {
726 const llvm::MemoryBuffer *buf =
727 fileLoc ? getBufferForFile(fileLoc.getFilename()) : nullptr;
728 bool reportUnexpected = impl->level != Level::OnlyExpected;
729
730 using MatchResult = llvm::SourceMgrDiagnosticVerifier::MatchResult;
731 MatchResult result = impl->verifier.process(
732 os, mgr, getDiagKind(kind), /*hasLoc=*/static_cast<bool>(fileLoc), buf,
733 fileLoc ? fileLoc.getLine() : 0, fileLoc ? fileLoc.getColumn() : 0, msg,
734 reportUnexpected);
735 if (result == MatchResult::Unexpected)
736 emitDiagnostic(loc, "unexpected " + getDiagKindStr(kind) + ": " + msg,
738}
739
740//===----------------------------------------------------------------------===//
741// ParallelDiagnosticHandler
742//===----------------------------------------------------------------------===//
743
744namespace mlir {
745namespace detail {
746/// Unique ids for parallel diagnostic handlers, so the thread-local slot
747/// below can name a handler without holding a raw pointer to it: a handler
748/// may be destroyed while a thread that skipped its erase still names it.
749static std::atomic<uint64_t> nextHandlerUniqueID{1};
750
751/// One entry of the thread-local nesting stack: the handler an element
752/// belongs to and its order id. Plain data so the thread-locals below stay
753/// trivially destructible.
755 uint64_t handlerID;
756 size_t orderID;
757};
758
759/// How many nested parallel loops one thread tracks without a lock. Past
760/// this the handler's map is the overflow.
761constexpr unsigned kMaxThreadOrderEntries = 4;
762
763/// The order ids for the current thread, one entry per enclosing parallel
764/// loop, innermost last. A parallel loop sets and erases a thread's order id
765/// once per element, and doing that under the handler's mutex made the mutex
766/// the most contended lock of a loop over many small elements. A thread
767/// processes one element at a time for each handler, so a small stack covers
768/// every realistic nesting depth. Erases are LIFO, matching loop nesting.
769/// Reentrant use of the same handler on one thread remains unsupported, as
770/// before.
771static LLVM_THREAD_LOCAL ThreadOrderEntry
773static LLVM_THREAD_LOCAL unsigned threadOrderDepth = 0;
774
775struct ParallelDiagnosticHandlerImpl : public llvm::PrettyStackTraceEntry {
778 : id(id), diag(std::move(diag)) {}
779 bool operator<(const ThreadDiagnostic &rhs) const { return id < rhs.id; }
780
781 /// The id for this diagnostic, this is used for ordering.
782 /// Note: This id corresponds to the ordered position of the current element
783 /// being processed by a given thread.
784 size_t id;
785
786 /// The diagnostic.
788 };
789
791 handlerID = ctx->getDiagEngine().registerHandler([this](Diagnostic &diag) {
792 // Fast path: scan the thread-local stack for this handler, no lock.
793 // Appending below still locks.
794 for (unsigned i = threadOrderDepth; i > 0; --i) {
796 llvm::sys::SmartScopedLock<true> lock(mutex);
797 diagnostics.emplace_back(threadOrderStack[i - 1].orderID,
798 std::move(diag));
799 return success();
800 }
801 }
802 // Slow path: nested past the stack, consult the map. If this
803 // thread is not tracked, return failure to let another handler
804 // process this diagnostic.
805 uint64_t tid = llvm::get_threadid();
806 llvm::sys::SmartScopedLock<true> lock(mutex);
807 auto it = threadToOrderID.find(tid);
808 if (it == threadToOrderID.end())
809 return failure();
810 diagnostics.emplace_back(it->second, std::move(diag));
811 return success();
812 });
813 }
814
816 // Erase this handler from the context.
817 context->getDiagEngine().eraseHandler(handlerID);
818
819 // Early exit if there are no diagnostics, this is the common case.
820 if (diagnostics.empty())
821 return;
822
823 // Emit the diagnostics back to the context.
825 return context->getDiagEngine().emit(std::move(diag));
826 });
827 }
828
829 /// Utility method to emit any held diagnostics.
830 void emitDiagnostics(llvm::function_ref<void(Diagnostic &)> emitFn) const {
831 // Stable sort all of the diagnostics that were emitted. This creates a
832 // deterministic ordering for the diagnostics based upon which order id they
833 // were emitted for.
834 llvm::stable_sort(diagnostics);
835
836 // Emit each diagnostic to the context again.
838 emitFn(diag.diag);
839 }
840
841 /// Set the order id for the current thread.
842 void setOrderIDForThread(size_t orderID) {
843 if (threadOrderDepth > 0 &&
845 threadOrderStack[threadOrderDepth - 1].orderID = orderID;
846 return;
847 }
850 return;
851 }
852 uint64_t tid = llvm::get_threadid();
853 llvm::sys::SmartScopedLock<true> lock(mutex);
854 threadToOrderID[tid] = orderID;
855 }
856
857 /// Remove the order id for the current thread.
859 if (threadOrderDepth > 0 &&
862 return;
863 }
864 uint64_t tid = llvm::get_threadid();
865 llvm::sys::SmartScopedLock<true> lock(mutex);
866 threadToOrderID.erase(tid);
867 }
868
869 /// Dump the current diagnostics that were inflight.
870 void print(raw_ostream &os) const override {
871 // Early exit if there are no diagnostics, this is the common case.
872 if (diagnostics.empty())
873 return;
874
875 os << "In-Flight Diagnostics:\n";
876 emitDiagnostics([&](const Diagnostic &diag) {
877 os.indent(4);
878
879 // Print each diagnostic with the format:
880 // "<location>: <kind>: <msg>"
881 if (!llvm::isa<UnknownLoc>(diag.getLocation()))
882 os << diag.getLocation() << ": ";
883 switch (diag.getSeverity()) {
885 os << "error: ";
886 break;
888 os << "warning: ";
889 break;
891 os << "note: ";
892 break;
894 os << "remark: ";
895 break;
896 }
897 os << diag << '\n';
898 });
899 }
900
901 /// A smart mutex to lock access to the internal state.
903
904 /// Order ids, keyed by thread id, for threads nested past what the
905 /// thread-local stack holds: more enclosing parallel loops on one thread
906 /// than entries. Guarded by `mutex`. A thread in neither the stack nor
907 /// this map is not tracked, and its diagnostics pass on to the next
908 /// handler.
910
911 /// An unordered list of diagnostics that were emitted.
912 mutable std::vector<ThreadDiagnostic> diagnostics;
913
914 /// The unique id for the parallel handler.
916
917 /// Unique id naming this handler in the thread-local order-id stack.
918 const uint64_t handlerUniqueID =
919 nextHandlerUniqueID.fetch_add(1, std::memory_order_relaxed);
920
921 /// The context to emit the diagnostics to.
923};
924} // namespace detail
925} // namespace mlir
926
930
931/// Set the order id for the current thread.
933 impl->setOrderIDForThread(orderID);
934}
935
936/// Remove the order id for the current thread. This removes the thread from
937/// diagnostics tracking.
939 impl->eraseOrderIDForThread();
940}
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:77
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.
DiagnosticArgument getStringArgument(const Twine &val)
Returns a string argument that refers to a copy of val owned by this diagnostic, so the argument rema...
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:176
unsigned getLine() const
Definition Location.cpp:177
StringAttr getFilename() const
Definition Location.cpp:173
unsigned getColumn() const
Definition Location.cpp:179
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:71
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:1171
Operation * getOperation() const
Definition Operation.h:1177
OpPrintingFlags & flags()
Definition Operation.h:1175
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:172
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:229
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.