1 //===- Diagnostics.h - MLIR Diagnostics -------------------------*- C++ -*-===// 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 // 9 // This file defines utilities for emitting diagnostics. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef MLIR_IR_DIAGNOSTICS_H 14 #define MLIR_IR_DIAGNOSTICS_H 15 16 #include "mlir/IR/Location.h" 17 #include <functional> 18 #include <optional> 19 20 namespace llvm { 21 class MemoryBuffer; 22 class SMLoc; 23 class SourceMgr; 24 } // namespace llvm 25 26 namespace mlir { 27 class DiagnosticEngine; 28 class MLIRContext; 29 class Operation; 30 class OperationName; 31 class OpPrintingFlags; 32 class Type; 33 class Value; 34 35 namespace detail { 36 struct DiagnosticEngineImpl; 37 } // namespace detail 38 39 /// Defines the different supported severity of a diagnostic. 40 enum class DiagnosticSeverity { 41 Note, 42 Warning, 43 Error, 44 Remark, 45 }; 46 47 //===----------------------------------------------------------------------===// 48 // DiagnosticArgument 49 //===----------------------------------------------------------------------===// 50 51 /// A variant type that holds a single argument for a diagnostic. 52 class DiagnosticArgument { 53 public: 54 /// Note: The constructors below are only exposed due to problems accessing 55 /// constructors from type traits, they should not be used directly by users. 56 // Construct from an Attribute. 57 explicit DiagnosticArgument(Attribute attr); 58 // Construct from a floating point number. 59 explicit DiagnosticArgument(double val) 60 : kind(DiagnosticArgumentKind::Double), doubleVal(val) {} 61 explicit DiagnosticArgument(float val) : DiagnosticArgument(double(val)) {} 62 // Construct from a signed integer. 63 template <typename T> 64 explicit DiagnosticArgument( 65 T val, std::enable_if_t<std::is_signed<T>::value && 66 std::numeric_limits<T>::is_integer && 67 sizeof(T) <= sizeof(int64_t)> * = nullptr) 68 : kind(DiagnosticArgumentKind::Integer), opaqueVal(int64_t(val)) {} 69 // Construct from an unsigned integer. 70 template <typename T> 71 explicit DiagnosticArgument( 72 T val, std::enable_if_t<std::is_unsigned<T>::value && 73 std::numeric_limits<T>::is_integer && 74 sizeof(T) <= sizeof(uint64_t)> * = nullptr) 75 : kind(DiagnosticArgumentKind::Unsigned), opaqueVal(uint64_t(val)) {} 76 // Construct from a string reference. 77 explicit DiagnosticArgument(StringRef val) 78 : kind(DiagnosticArgumentKind::String), stringVal(val) {} 79 // Construct from a Type. 80 explicit DiagnosticArgument(Type val); 81 82 /// Enum that represents the different kinds of diagnostic arguments 83 /// supported. 84 enum class DiagnosticArgumentKind { 85 Attribute, 86 Double, 87 Integer, 88 String, 89 Type, 90 Unsigned, 91 }; 92 93 /// Outputs this argument to a stream. 94 void print(raw_ostream &os) const; 95 96 /// Returns the kind of this argument. 97 DiagnosticArgumentKind getKind() const { return kind; } 98 99 /// Returns this argument as an Attribute. 100 Attribute getAsAttribute() const; 101 102 /// Returns this argument as a double. 103 double getAsDouble() const { 104 assert(getKind() == DiagnosticArgumentKind::Double); 105 return doubleVal; 106 } 107 108 /// Returns this argument as a signed integer. 109 int64_t getAsInteger() const { 110 assert(getKind() == DiagnosticArgumentKind::Integer); 111 return static_cast<int64_t>(opaqueVal); 112 } 113 114 /// Returns this argument as a string. 115 StringRef getAsString() const { 116 assert(getKind() == DiagnosticArgumentKind::String); 117 return stringVal; 118 } 119 120 /// Returns this argument as a Type. 121 Type getAsType() const; 122 123 /// Returns this argument as an unsigned integer. 124 uint64_t getAsUnsigned() const { 125 assert(getKind() == DiagnosticArgumentKind::Unsigned); 126 return static_cast<uint64_t>(opaqueVal); 127 } 128 129 private: 130 friend class Diagnostic; 131 132 /// The kind of this argument. 133 DiagnosticArgumentKind kind; 134 135 /// The value of this argument. 136 union { 137 double doubleVal; 138 intptr_t opaqueVal; 139 StringRef stringVal; 140 }; 141 }; 142 143 inline raw_ostream &operator<<(raw_ostream &os, const DiagnosticArgument &arg) { 144 arg.print(os); 145 return os; 146 } 147 148 //===----------------------------------------------------------------------===// 149 // Diagnostic 150 //===----------------------------------------------------------------------===// 151 152 /// This class contains all of the information necessary to report a diagnostic 153 /// to the DiagnosticEngine. It should generally not be constructed directly, 154 /// and instead used transitively via InFlightDiagnostic. 155 class Diagnostic { 156 using NoteVector = std::vector<std::unique_ptr<Diagnostic>>; 157 158 public: 159 Diagnostic(Location loc, DiagnosticSeverity severity) 160 : loc(loc), severity(severity) {} 161 Diagnostic(Diagnostic &&) = default; 162 Diagnostic &operator=(Diagnostic &&) = default; 163 164 /// Returns the severity of this diagnostic. 165 DiagnosticSeverity getSeverity() const { return severity; } 166 167 /// Returns the source location for this diagnostic. 168 Location getLocation() const { return loc; } 169 170 /// Returns the current list of diagnostic arguments. 171 MutableArrayRef<DiagnosticArgument> getArguments() { return arguments; } 172 ArrayRef<DiagnosticArgument> getArguments() const { return arguments; } 173 174 /// Stream operator for inserting new diagnostic arguments. 175 template <typename Arg> 176 std::enable_if_t<!std::is_convertible<Arg, StringRef>::value && 177 std::is_constructible<DiagnosticArgument, Arg>::value, 178 Diagnostic &> 179 operator<<(Arg &&val) { 180 arguments.push_back(DiagnosticArgument(std::forward<Arg>(val))); 181 return *this; 182 } 183 Diagnostic &operator<<(StringAttr val); 184 185 /// Stream in a string literal. 186 template <size_t n> 187 Diagnostic &operator<<(const char (&val)[n]) { 188 arguments.push_back(DiagnosticArgument(val)); 189 return *this; 190 } 191 192 /// Stream in a Twine argument. 193 Diagnostic &operator<<(char val); 194 Diagnostic &operator<<(const Twine &val); 195 Diagnostic &operator<<(Twine &&val); 196 197 /// Stream in an OperationName. 198 Diagnostic &operator<<(OperationName val); 199 200 /// Stream in an Operation. 201 Diagnostic &operator<<(Operation &op); 202 Diagnostic &operator<<(Operation *op) { return *this << *op; } 203 /// Append an operation with the given printing flags. 204 Diagnostic &appendOp(Operation &op, const OpPrintingFlags &flags); 205 206 /// Stream in a Value. 207 Diagnostic &operator<<(Value val); 208 209 /// Stream in a range. 210 template <typename T, typename ValueT = llvm::detail::ValueOfRange<T>> 211 std::enable_if_t<!std::is_constructible<DiagnosticArgument, T>::value, 212 Diagnostic &> 213 operator<<(T &&range) { 214 return appendRange(range); 215 } 216 217 /// Append a range to the diagnostic. The default delimiter between elements 218 /// is ','. 219 template <typename T> 220 Diagnostic &appendRange(const T &c, const char *delim = ", ") { 221 llvm::interleave( 222 c, [this](const auto &a) { *this << a; }, [&]() { *this << delim; }); 223 return *this; 224 } 225 226 /// Append arguments to the diagnostic. 227 template <typename Arg1, typename Arg2, typename... Args> 228 Diagnostic &append(Arg1 &&arg1, Arg2 &&arg2, Args &&...args) { 229 append(std::forward<Arg1>(arg1)); 230 return append(std::forward<Arg2>(arg2), std::forward<Args>(args)...); 231 } 232 /// Append one argument to the diagnostic. 233 template <typename Arg> 234 Diagnostic &append(Arg &&arg) { 235 *this << std::forward<Arg>(arg); 236 return *this; 237 } 238 239 /// Outputs this diagnostic to a stream. 240 void print(raw_ostream &os) const; 241 242 /// Converts the diagnostic to a string. 243 std::string str() const; 244 245 /// Attaches a note to this diagnostic. A new location may be optionally 246 /// provided, if not, then the location defaults to the one specified for this 247 /// diagnostic. Notes may not be attached to other notes. 248 Diagnostic &attachNote(std::optional<Location> noteLoc = std::nullopt); 249 250 using note_iterator = llvm::pointee_iterator<NoteVector::iterator>; 251 using const_note_iterator = 252 llvm::pointee_iterator<NoteVector::const_iterator>; 253 254 /// Returns the notes held by this diagnostic. 255 iterator_range<note_iterator> getNotes() { 256 return llvm::make_pointee_range(notes); 257 } 258 iterator_range<const_note_iterator> getNotes() const { 259 return llvm::make_pointee_range(notes); 260 } 261 262 /// Allow a diagnostic to be converted to 'failure'. 263 operator LogicalResult() const; 264 265 /// Allow a diagnostic to be converted to 'failure'. 266 operator ParseResult() const { return ParseResult(LogicalResult(*this)); } 267 268 /// Allow a diagnostic to be converted to FailureOr<T>. Always results in 269 /// 'failure' because this cast cannot possibly return an object of 'T'. 270 template <typename T> 271 operator FailureOr<T>() const { 272 return failure(); 273 } 274 275 /// Returns the current list of diagnostic metadata. 276 SmallVectorImpl<DiagnosticArgument> &getMetadata() { return metadata; } 277 278 private: 279 Diagnostic(const Diagnostic &rhs) = delete; 280 Diagnostic &operator=(const Diagnostic &rhs) = delete; 281 282 /// The source location. 283 Location loc; 284 285 /// The severity of this diagnostic. 286 DiagnosticSeverity severity; 287 288 /// The current list of arguments. 289 SmallVector<DiagnosticArgument, 4> arguments; 290 291 /// A list of string values used as arguments. This is used to guarantee the 292 /// liveness of non-constant strings used in diagnostics. 293 std::vector<std::unique_ptr<char[]>> strings; 294 295 /// A list of attached notes. 296 NoteVector notes; 297 298 /// A list of metadata attached to this Diagnostic. 299 SmallVector<DiagnosticArgument, 0> metadata; 300 }; 301 302 inline raw_ostream &operator<<(raw_ostream &os, const Diagnostic &diag) { 303 diag.print(os); 304 return os; 305 } 306 307 //===----------------------------------------------------------------------===// 308 // InFlightDiagnostic 309 //===----------------------------------------------------------------------===// 310 311 /// This class represents a diagnostic that is inflight and set to be reported. 312 /// This allows for last minute modifications of the diagnostic before it is 313 /// emitted by a DiagnosticEngine. 314 class InFlightDiagnostic { 315 public: 316 InFlightDiagnostic() = default; 317 InFlightDiagnostic(InFlightDiagnostic &&rhs) 318 : owner(rhs.owner), impl(std::move(rhs.impl)) { 319 // Reset the rhs diagnostic. 320 rhs.impl.reset(); 321 rhs.abandon(); 322 } 323 ~InFlightDiagnostic() { 324 if (isInFlight()) 325 report(); 326 } 327 328 /// Stream operator for new diagnostic arguments. 329 template <typename Arg> 330 InFlightDiagnostic &operator<<(Arg &&arg) & { 331 return append(std::forward<Arg>(arg)); 332 } 333 template <typename Arg> 334 InFlightDiagnostic &&operator<<(Arg &&arg) && { 335 return std::move(append(std::forward<Arg>(arg))); 336 } 337 338 /// Append arguments to the diagnostic. 339 template <typename... Args> 340 InFlightDiagnostic &append(Args &&...args) & { 341 assert(isActive() && "diagnostic not active"); 342 if (isInFlight()) 343 impl->append(std::forward<Args>(args)...); 344 return *this; 345 } 346 template <typename... Args> 347 InFlightDiagnostic &&append(Args &&...args) && { 348 return std::move(append(std::forward<Args>(args)...)); 349 } 350 351 /// Attaches a note to this diagnostic. 352 Diagnostic &attachNote(std::optional<Location> noteLoc = std::nullopt) { 353 assert(isActive() && "diagnostic not active"); 354 return impl->attachNote(noteLoc); 355 } 356 357 /// Returns the underlying diagnostic or nullptr if this diagnostic isn't 358 /// active. 359 Diagnostic *getUnderlyingDiagnostic() { return impl ? &*impl : nullptr; } 360 361 /// Reports the diagnostic to the engine. 362 void report(); 363 364 /// Abandons this diagnostic so that it will no longer be reported. 365 void abandon(); 366 367 /// Allow an inflight diagnostic to be converted to 'failure', otherwise 368 /// 'success' if this is an empty diagnostic. 369 operator LogicalResult() const; 370 371 /// Allow an inflight diagnostic to be converted to 'failure', otherwise 372 /// 'success' if this is an empty diagnostic. 373 operator ParseResult() const { return ParseResult(LogicalResult(*this)); } 374 375 /// Allow an inflight diagnostic to be converted to FailureOr<T>. Always 376 /// results in 'failure' because this cast cannot possibly return an object of 377 /// 'T'. 378 template <typename T> 379 operator FailureOr<T>() const { 380 return failure(); 381 } 382 383 private: 384 InFlightDiagnostic &operator=(const InFlightDiagnostic &) = delete; 385 InFlightDiagnostic &operator=(InFlightDiagnostic &&) = delete; 386 InFlightDiagnostic(DiagnosticEngine *owner, Diagnostic &&rhs) 387 : owner(owner), impl(std::move(rhs)) {} 388 389 /// Returns true if the diagnostic is still active, i.e. it has a live 390 /// diagnostic. 391 bool isActive() const { return impl.has_value(); } 392 393 /// Returns true if the diagnostic is still in flight to be reported. 394 bool isInFlight() const { return owner; } 395 396 // Allow access to the constructor. 397 friend DiagnosticEngine; 398 399 /// The engine that this diagnostic is to report to. 400 DiagnosticEngine *owner = nullptr; 401 402 /// The raw diagnostic that is inflight to be reported. 403 std::optional<Diagnostic> impl; 404 }; 405 406 //===----------------------------------------------------------------------===// 407 // DiagnosticEngine 408 //===----------------------------------------------------------------------===// 409 410 /// This class is the main interface for diagnostics. The DiagnosticEngine 411 /// manages the registration of diagnostic handlers as well as the core API for 412 /// diagnostic emission. This class should not be constructed directly, but 413 /// instead interfaced with via an MLIRContext instance. 414 class DiagnosticEngine { 415 public: 416 ~DiagnosticEngine(); 417 418 // Diagnostic handler registration and use. MLIR supports the ability for the 419 // IR to carry arbitrary metadata about operation location information. If a 420 // problem is detected by the compiler, it can invoke the emitError / 421 // emitWarning / emitRemark method on an Operation and have it get reported 422 // through this interface. 423 // 424 // Tools using MLIR are encouraged to register error handlers and define a 425 // schema for their location information. If they don't, then warnings and 426 // notes will be dropped and errors will be emitted to errs. 427 428 /// The handler type for MLIR diagnostics. This function takes a diagnostic as 429 /// input, and returns success if the handler has fully processed this 430 /// diagnostic. Returns failure otherwise. 431 using HandlerTy = llvm::unique_function<LogicalResult(Diagnostic &)>; 432 433 /// A handle to a specific registered handler object. 434 using HandlerID = uint64_t; 435 436 /// Register a new handler for diagnostics to the engine. Diagnostics are 437 /// process by handlers in stack-like order, meaning that the last added 438 /// handlers will process diagnostics first. This function returns a unique 439 /// identifier for the registered handler, which can be used to unregister 440 /// this handler at a later time. 441 HandlerID registerHandler(HandlerTy handler); 442 443 /// Set the diagnostic handler with a function that returns void. This is a 444 /// convenient wrapper for handlers that always completely process the given 445 /// diagnostic. 446 template <typename FuncTy, typename RetT = decltype(std::declval<FuncTy>()( 447 std::declval<Diagnostic &>()))> 448 std::enable_if_t<std::is_same<RetT, void>::value, HandlerID> 449 registerHandler(FuncTy &&handler) { 450 return registerHandler([=](Diagnostic &diag) { 451 handler(diag); 452 return success(); 453 }); 454 } 455 456 /// Erase the registered diagnostic handler with the given identifier. 457 void eraseHandler(HandlerID id); 458 459 /// Create a new inflight diagnostic with the given location and severity. 460 InFlightDiagnostic emit(Location loc, DiagnosticSeverity severity) { 461 assert(severity != DiagnosticSeverity::Note && 462 "notes should not be emitted directly"); 463 return InFlightDiagnostic(this, Diagnostic(loc, severity)); 464 } 465 466 /// Emit a diagnostic using the registered issue handler if present, or with 467 /// the default behavior if not. The diagnostic instance is consumed in the 468 /// process. 469 void emit(Diagnostic &&diag); 470 471 private: 472 friend class MLIRContextImpl; 473 DiagnosticEngine(); 474 475 /// The internal implementation of the DiagnosticEngine. 476 std::unique_ptr<detail::DiagnosticEngineImpl> impl; 477 }; 478 479 /// Utility method to emit an error message using this location. 480 InFlightDiagnostic emitError(Location loc); 481 InFlightDiagnostic emitError(Location loc, const Twine &message); 482 483 /// Utility method to emit a warning message using this location. 484 InFlightDiagnostic emitWarning(Location loc); 485 InFlightDiagnostic emitWarning(Location loc, const Twine &message); 486 487 /// Utility method to emit a remark message using this location. 488 InFlightDiagnostic emitRemark(Location loc); 489 InFlightDiagnostic emitRemark(Location loc, const Twine &message); 490 491 /// Overloads of the above emission functions that take an optionally null 492 /// location. If the location is null, no diagnostic is emitted and a failure is 493 /// returned. Given that the provided location may be null, these methods take 494 /// the diagnostic arguments directly instead of relying on the returned 495 /// InFlightDiagnostic. 496 template <typename... Args> 497 LogicalResult emitOptionalError(std::optional<Location> loc, Args &&...args) { 498 if (loc) 499 return emitError(*loc).append(std::forward<Args>(args)...); 500 return failure(); 501 } 502 template <typename... Args> 503 LogicalResult emitOptionalWarning(std::optional<Location> loc, Args &&...args) { 504 if (loc) 505 return emitWarning(*loc).append(std::forward<Args>(args)...); 506 return failure(); 507 } 508 template <typename... Args> 509 LogicalResult emitOptionalRemark(std::optional<Location> loc, Args &&...args) { 510 if (loc) 511 return emitRemark(*loc).append(std::forward<Args>(args)...); 512 return failure(); 513 } 514 515 //===----------------------------------------------------------------------===// 516 // ScopedDiagnosticHandler 517 //===----------------------------------------------------------------------===// 518 519 /// This diagnostic handler is a simple RAII class that registers and erases a 520 /// diagnostic handler on a given context. This class can be either be used 521 /// directly, or in conjunction with a derived diagnostic handler. 522 class ScopedDiagnosticHandler { 523 public: 524 explicit ScopedDiagnosticHandler(MLIRContext *ctx) : handlerID(0), ctx(ctx) {} 525 template <typename FuncTy> 526 ScopedDiagnosticHandler(MLIRContext *ctx, FuncTy &&handler) 527 : handlerID(0), ctx(ctx) { 528 setHandler(std::forward<FuncTy>(handler)); 529 } 530 ~ScopedDiagnosticHandler(); 531 532 protected: 533 /// Set the handler to manage via RAII. 534 template <typename FuncTy> 535 void setHandler(FuncTy &&handler) { 536 auto &diagEngine = ctx->getDiagEngine(); 537 if (handlerID) 538 diagEngine.eraseHandler(handlerID); 539 handlerID = diagEngine.registerHandler(std::forward<FuncTy>(handler)); 540 } 541 542 private: 543 /// The unique id for the scoped handler. 544 DiagnosticEngine::HandlerID handlerID; 545 546 /// The context to erase the handler from. 547 MLIRContext *ctx; 548 }; 549 550 //===----------------------------------------------------------------------===// 551 // SourceMgrDiagnosticHandler 552 //===----------------------------------------------------------------------===// 553 554 namespace detail { 555 struct SourceMgrDiagnosticHandlerImpl; 556 } // namespace detail 557 558 /// This class is a utility diagnostic handler for use with llvm::SourceMgr. 559 class SourceMgrDiagnosticHandler : public ScopedDiagnosticHandler { 560 public: 561 /// This type represents a functor used to filter out locations when printing 562 /// a diagnostic. It should return true if the provided location is okay to 563 /// display, false otherwise. If all locations in a diagnostic are filtered 564 /// out, the first location is used as the sole location. When deciding 565 /// whether or not to filter a location, this function should not recurse into 566 /// any nested location. This recursion is handled automatically by the 567 /// caller. 568 using ShouldShowLocFn = llvm::unique_function<bool(Location)>; 569 570 SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr, MLIRContext *ctx, 571 raw_ostream &os, 572 ShouldShowLocFn &&shouldShowLocFn = {}); 573 SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr, MLIRContext *ctx, 574 ShouldShowLocFn &&shouldShowLocFn = {}); 575 ~SourceMgrDiagnosticHandler(); 576 577 /// Emit the given diagnostic information with the held source manager. 578 void emitDiagnostic(Location loc, Twine message, DiagnosticSeverity kind, 579 bool displaySourceLine = true); 580 581 /// Set the maximum depth that a call stack will be printed. Defaults to 10. 582 void setCallStackLimit(unsigned limit); 583 584 protected: 585 /// Emit the given diagnostic with the held source manager. 586 void emitDiagnostic(Diagnostic &diag); 587 588 /// Get a memory buffer for the given file, or nullptr if no file is 589 /// available. 590 const llvm::MemoryBuffer *getBufferForFile(StringRef filename); 591 592 /// The source manager that we are wrapping. 593 llvm::SourceMgr &mgr; 594 595 /// The output stream to use when printing diagnostics. 596 raw_ostream &os; 597 598 /// A functor used when determining if a location for a diagnostic should be 599 /// shown. If null, all locations should be shown. 600 ShouldShowLocFn shouldShowLocFn; 601 602 private: 603 /// Convert a location into the given memory buffer into an SMLoc. 604 SMLoc convertLocToSMLoc(FileLineColLoc loc); 605 606 /// Given a location, returns the first nested location (including 'loc') that 607 /// can be shown to the user. 608 std::optional<Location> findLocToShow(Location loc); 609 610 /// The maximum depth that a call stack will be printed. 611 unsigned callStackLimit = 10; 612 613 std::unique_ptr<detail::SourceMgrDiagnosticHandlerImpl> impl; 614 }; 615 616 //===----------------------------------------------------------------------===// 617 // SourceMgrDiagnosticVerifierHandler 618 //===----------------------------------------------------------------------===// 619 620 namespace detail { 621 struct SourceMgrDiagnosticVerifierHandlerImpl; 622 } // namespace detail 623 624 /// This class is a utility diagnostic handler for use with llvm::SourceMgr that 625 /// verifies that emitted diagnostics match 'expected-*' lines on the 626 /// corresponding line of the source file. 627 class SourceMgrDiagnosticVerifierHandler : public SourceMgrDiagnosticHandler { 628 public: 629 SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx, 630 raw_ostream &out); 631 SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx); 632 ~SourceMgrDiagnosticVerifierHandler(); 633 634 /// Returns the status of the handler and verifies that all expected 635 /// diagnostics were emitted. This return success if all diagnostics were 636 /// verified correctly, failure otherwise. 637 LogicalResult verify(); 638 639 private: 640 /// Process a single diagnostic. 641 void process(Diagnostic &diag); 642 643 /// Process a FileLineColLoc diagnostic. 644 void process(FileLineColLoc loc, StringRef msg, DiagnosticSeverity kind); 645 646 std::unique_ptr<detail::SourceMgrDiagnosticVerifierHandlerImpl> impl; 647 }; 648 649 //===----------------------------------------------------------------------===// 650 // ParallelDiagnosticHandler 651 //===----------------------------------------------------------------------===// 652 653 namespace detail { 654 struct ParallelDiagnosticHandlerImpl; 655 } // namespace detail 656 657 /// This class is a utility diagnostic handler for use when multi-threading some 658 /// part of the compiler where diagnostics may be emitted. This handler ensures 659 /// a deterministic ordering to the emitted diagnostics that mirrors that of a 660 /// single-threaded compilation. 661 class ParallelDiagnosticHandler { 662 public: 663 ParallelDiagnosticHandler(MLIRContext *ctx); 664 ~ParallelDiagnosticHandler(); 665 666 /// Set the order id for the current thread. This is required to be set by 667 /// each thread that will be emitting diagnostics to this handler. The orderID 668 /// corresponds to the order in which diagnostics would be emitted when 669 /// executing synchronously. For example, if we were processing a list 670 /// of operations [a, b, c] on a single-thread. Diagnostics emitted while 671 /// processing operation 'a' would be emitted before those for 'b' or 'c'. 672 /// This corresponds 1-1 with the 'orderID'. The thread that is processing 'a' 673 /// should set the orderID to '0'; the thread processing 'b' should set it to 674 /// '1'; and so on and so forth. This provides a way for the handler to 675 /// deterministically order the diagnostics that it receives given the thread 676 /// that it is receiving on. 677 void setOrderIDForThread(size_t orderID); 678 679 /// Remove the order id for the current thread. This removes the thread from 680 /// diagnostics tracking. 681 void eraseOrderIDForThread(); 682 683 private: 684 std::unique_ptr<detail::ParallelDiagnosticHandlerImpl> impl; 685 }; 686 } // namespace mlir 687 688 #endif 689