MLIR  22.0.0git
MlirOptMain.cpp
Go to the documentation of this file.
1 //===- MlirOptMain.cpp - MLIR Optimizer Driver ----------------------------===//
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 is a utility that runs an optimization pass and prints the result back
10 // out. It is designed to support unit testing.
11 //
12 //===----------------------------------------------------------------------===//
13 
17 #include "mlir/Debug/Counter.h"
20 #include "mlir/IR/AsmState.h"
21 #include "mlir/IR/Attributes.h"
22 #include "mlir/IR/BuiltinOps.h"
23 #include "mlir/IR/Diagnostics.h"
24 #include "mlir/IR/Location.h"
25 #include "mlir/IR/MLIRContext.h"
26 #include "mlir/IR/Remarks.h"
27 #include "mlir/Parser/Parser.h"
28 #include "mlir/Pass/PassManager.h"
29 #include "mlir/Pass/PassRegistry.h"
32 #include "mlir/Support/Timing.h"
37 #include "llvm/ADT/StringRef.h"
38 #include "llvm/Remarks/RemarkFormat.h"
39 #include "llvm/Support/CommandLine.h"
40 #include "llvm/Support/InitLLVM.h"
41 #include "llvm/Support/LogicalResult.h"
42 #include "llvm/Support/ManagedStatic.h"
43 #include "llvm/Support/Process.h"
44 #include "llvm/Support/Regex.h"
45 #include "llvm/Support/SourceMgr.h"
46 #include "llvm/Support/ThreadPool.h"
47 #include "llvm/Support/ToolOutputFile.h"
48 
49 using namespace mlir;
50 using namespace llvm;
51 
52 namespace {
53 class BytecodeVersionParser : public cl::parser<std::optional<int64_t>> {
54 public:
55  BytecodeVersionParser(cl::Option &o)
56  : cl::parser<std::optional<int64_t>>(o) {}
57 
58  bool parse(cl::Option &o, StringRef /*argName*/, StringRef arg,
59  std::optional<int64_t> &v) {
60  long long w;
61  if (getAsSignedInteger(arg, 10, w))
62  return o.error("Invalid argument '" + arg +
63  "', only integer is supported.");
64  v = w;
65  return false;
66  }
67 };
68 
69 /// This class is intended to manage the handling of command line options for
70 /// creating a *-opt config. This is a singleton.
71 struct MlirOptMainConfigCLOptions : public MlirOptMainConfig {
72  MlirOptMainConfigCLOptions() {
73  // These options are static but all uses ExternalStorage to initialize the
74  // members of the parent class. This is unusual but since this class is a
75  // singleton it basically attaches command line option to the singleton
76  // members.
77 
78  static cl::opt<bool, /*ExternalStorage=*/true> allowUnregisteredDialects(
79  "allow-unregistered-dialect",
80  cl::desc("Allow operation with no registered dialects"),
81  cl::location(allowUnregisteredDialectsFlag), cl::init(false));
82 
83  static cl::opt<bool, /*ExternalStorage=*/true> dumpPassPipeline(
84  "dump-pass-pipeline", cl::desc("Print the pipeline that will be run"),
85  cl::location(dumpPassPipelineFlag), cl::init(false));
86 
87  static cl::opt<bool, /*ExternalStorage=*/true> emitBytecode(
88  "emit-bytecode", cl::desc("Emit bytecode when generating output"),
89  cl::location(emitBytecodeFlag), cl::init(false));
90 
91  static cl::opt<bool, /*ExternalStorage=*/true> elideResourcesFromBytecode(
92  "elide-resource-data-from-bytecode",
93  cl::desc("Elide resources when generating bytecode"),
94  cl::location(elideResourceDataFromBytecodeFlag), cl::init(false));
95 
96  static cl::opt<std::optional<int64_t>, /*ExternalStorage=*/true,
97  BytecodeVersionParser>
98  bytecodeVersion(
99  "emit-bytecode-version",
100  cl::desc("Use specified bytecode when generating output"),
101  cl::location(emitBytecodeVersion), cl::init(std::nullopt));
102 
103  static cl::opt<std::string, /*ExternalStorage=*/true> irdlFile(
104  "irdl-file",
105  cl::desc("IRDL file to register before processing the input"),
106  cl::location(irdlFileFlag), cl::init(""), cl::value_desc("filename"));
107 
108  static cl::opt<VerbosityLevel, /*ExternalStorage=*/true>
109  diagnosticVerbosityLevel(
110  "mlir-diagnostic-verbosity-level",
111  cl::desc("Choose level of diagnostic information"),
112  cl::location(diagnosticVerbosityLevelFlag),
114  cl::values(
115  clEnumValN(VerbosityLevel::ErrorsOnly, "errors", "Errors only"),
116  clEnumValN(VerbosityLevel::ErrorsAndWarnings, "warnings",
117  "Errors and warnings"),
118  clEnumValN(VerbosityLevel::ErrorsWarningsAndRemarks, "remarks",
119  "Errors, warnings and remarks")));
120 
121  static cl::opt<bool, /*ExternalStorage=*/true> disableDiagnosticNotes(
122  "mlir-disable-diagnostic-notes", cl::desc("Disable diagnostic notes."),
123  cl::location(disableDiagnosticNotesFlag), cl::init(false));
124 
125  static cl::opt<bool, /*ExternalStorage=*/true> explicitModule(
126  "no-implicit-module",
127  cl::desc("Disable implicit addition of a top-level module op during "
128  "parsing"),
129  cl::location(useExplicitModuleFlag), cl::init(false));
130 
131  static cl::opt<bool, /*ExternalStorage=*/true> listPasses(
132  "list-passes", cl::desc("Print the list of registered passes and exit"),
133  cl::location(listPassesFlag), cl::init(false));
134 
135  static cl::opt<bool, /*ExternalStorage=*/true> runReproducer(
136  "run-reproducer", cl::desc("Run the pipeline stored in the reproducer"),
137  cl::location(runReproducerFlag), cl::init(false));
138 
139  static cl::opt<bool, /*ExternalStorage=*/true> showDialects(
140  "show-dialects",
141  cl::desc("Print the list of registered dialects and exit"),
142  cl::location(showDialectsFlag), cl::init(false));
143 
144  static cl::opt<std::string, /*ExternalStorage=*/true> splitInputFile{
145  "split-input-file",
146  llvm::cl::ValueOptional,
147  cl::callback([&](const std::string &str) {
148  // Implicit value: use default marker if flag was used without value.
149  if (str.empty())
150  splitInputFile.setValue(kDefaultSplitMarker);
151  }),
152  cl::desc("Split the input file into chunks using the given or "
153  "default marker and process each chunk independently"),
154  cl::location(splitInputFileFlag),
155  cl::init("")};
156 
157  static cl::opt<std::string, /*ExternalStorage=*/true> outputSplitMarker(
158  "output-split-marker",
159  cl::desc("Split marker to use for merging the ouput"),
160  cl::location(outputSplitMarkerFlag), cl::init(kDefaultSplitMarker));
161 
163  /*ExternalStorage=*/true>
164  verifyDiagnostics{
165  "verify-diagnostics", llvm::cl::ValueOptional,
166  cl::desc("Check that emitted diagnostics match expected-* lines on "
167  "the corresponding line"),
168  cl::location(verifyDiagnosticsFlag),
169  cl::values(
171  "all",
172  "Check all diagnostics (expected, unexpected, "
173  "near-misses)"),
174  // Implicit value: when passed with no arguments, e.g.
175  // `--verify-diagnostics` or `--verify-diagnostics=`.
177  "Check all diagnostics (expected, unexpected, "
178  "near-misses)"),
179  clEnumValN(
181  "only-expected", "Check only expected diagnostics"))};
182 
183  static cl::opt<bool, /*ExternalStorage=*/true> verifyPasses(
184  "verify-each",
185  cl::desc("Run the verifier after each transformation pass"),
186  cl::location(verifyPassesFlag), cl::init(true));
187 
188  static cl::opt<bool, /*ExternalStorage=*/true> disableVerifyOnParsing(
189  "mlir-very-unsafe-disable-verifier-on-parsing",
190  cl::desc("Disable the verifier on parsing (very unsafe)"),
191  cl::location(disableVerifierOnParsingFlag), cl::init(false));
192 
193  static cl::opt<bool, /*ExternalStorage=*/true> verifyRoundtrip(
194  "verify-roundtrip",
195  cl::desc("Round-trip the IR after parsing and ensure it succeeds"),
196  cl::location(verifyRoundtripFlag), cl::init(false));
197 
198  static cl::list<std::string> passPlugins(
199  "load-pass-plugin", cl::desc("Load passes from plugin library"));
200 
201  static cl::opt<std::string, /*ExternalStorage=*/true>
202  generateReproducerFile(
203  "mlir-generate-reproducer",
204  llvm::cl::desc(
205  "Generate an mlir reproducer at the provided filename"
206  " (no crash required)"),
207  cl::location(generateReproducerFileFlag), cl::init(""),
208  cl::value_desc("filename"));
209 
210  static cl::OptionCategory remarkCategory(
211  "Remark Options",
212  "Filter remarks by regular expression (llvm::Regex syntax).");
213 
214  static llvm::cl::opt<RemarkFormat, /*ExternalStorage=*/true> remarkFormat{
215  "remark-format",
216  llvm::cl::desc("Specify the format for remark output."),
217  cl::location(remarkFormatFlag),
218  llvm::cl::value_desc("format"),
219  llvm::cl::init(RemarkFormat::REMARK_FORMAT_STDOUT),
220  llvm::cl::values(clEnumValN(RemarkFormat::REMARK_FORMAT_STDOUT,
221  "emitRemark",
222  "Print as emitRemark to command-line"),
223  clEnumValN(RemarkFormat::REMARK_FORMAT_YAML, "yaml",
224  "Print yaml file"),
226  "bitstream", "Print bitstream file")),
227  llvm::cl::cat(remarkCategory)};
228 
229  static cl::opt<std::string, /*ExternalStorage=*/true> remarksAll(
230  "remarks-filter",
231  cl::desc("Show all remarks: passed, missed, failed, analysis"),
232  cl::location(remarksAllFilterFlag), cl::init(""),
233  cl::cat(remarkCategory));
234 
235  static cl::opt<std::string, /*ExternalStorage=*/true> remarksFile(
236  "remarks-output-file",
237  cl::desc(
238  "Output file for yaml and bitstream remark formats. Default is "
239  "mlir-remarks.yaml or mlir-remarks.bitstream"),
240  cl::location(remarksOutputFileFlag), cl::init(""),
241  cl::cat(remarkCategory));
242 
243  static cl::opt<std::string, /*ExternalStorage=*/true> remarksPassed(
244  "remarks-filter-passed", cl::desc("Show passed remarks"),
245  cl::location(remarksPassedFilterFlag), cl::init(""),
246  cl::cat(remarkCategory));
247 
248  static cl::opt<std::string, /*ExternalStorage=*/true> remarksFailed(
249  "remarks-filter-failed", cl::desc("Show failed remarks"),
250  cl::location(remarksFailedFilterFlag), cl::init(""),
251  cl::cat(remarkCategory));
252 
253  static cl::opt<std::string, /*ExternalStorage=*/true> remarksMissed(
254  "remarks-filter-missed", cl::desc("Show missed remarks"),
255  cl::location(remarksMissedFilterFlag), cl::init(""),
256  cl::cat(remarkCategory));
257 
258  static cl::opt<std::string, /*ExternalStorage=*/true> remarksAnalyse(
259  "remarks-filter-analyse", cl::desc("Show analysis remarks"),
260  cl::location(remarksAnalyseFilterFlag), cl::init(""),
261  cl::cat(remarkCategory));
262 
263  /// Set the callback to load a pass plugin.
264  passPlugins.setCallback([&](const std::string &pluginPath) {
265  auto plugin = PassPlugin::load(pluginPath);
266  if (!plugin) {
267  errs() << "Failed to load passes from '" << pluginPath
268  << "'. Request ignored.\n";
269  return;
270  }
271  plugin.get().registerPassRegistryCallbacks();
272  });
273 
274  static cl::list<std::string> dialectPlugins(
275  "load-dialect-plugin", cl::desc("Load dialects from plugin library"));
276  this->dialectPlugins = std::addressof(dialectPlugins);
277 
278  static PassPipelineCLParser passPipeline("", "Compiler passes to run", "p");
279  setPassPipelineParser(passPipeline);
280  }
281 
282  /// Set the callback to load a dialect plugin.
283  void setDialectPluginsCallback(DialectRegistry &registry);
284 
285  /// Pointer to static dialectPlugins variable in constructor, needed by
286  /// setDialectPluginsCallback(DialectRegistry&).
287  cl::list<std::string> *dialectPlugins = nullptr;
288 };
289 
290 /// A scoped diagnostic handler that suppresses certain diagnostics based on
291 /// the verbosity level and whether the diagnostic is a note.
292 class DiagnosticFilter : public ScopedDiagnosticHandler {
293 public:
294  DiagnosticFilter(MLIRContext *ctx, VerbosityLevel verbosityLevel,
295  bool showNotes = true)
296  : ScopedDiagnosticHandler(ctx) {
297  setHandler([verbosityLevel, showNotes](Diagnostic &diag) {
298  auto severity = diag.getSeverity();
299  switch (severity) {
301  // failure indicates that the error is not handled by the filter and
302  // goes through to the default handler. Therefore, the error can be
303  // successfully printed.
304  return failure();
306  if (verbosityLevel == VerbosityLevel::ErrorsOnly)
307  return success();
308  else
309  return failure();
311  if (verbosityLevel == VerbosityLevel::ErrorsOnly ||
312  verbosityLevel == VerbosityLevel::ErrorsAndWarnings)
313  return success();
314  else
315  return failure();
317  if (showNotes)
318  return failure();
319  else
320  return success();
321  }
322  llvm_unreachable("Unknown diagnostic severity");
323  });
324  }
325 };
326 } // namespace
327 
328 ManagedStatic<MlirOptMainConfigCLOptions> clOptionsConfig;
329 
331  clOptionsConfig->setDialectPluginsCallback(registry);
333 }
334 
337  return *clOptionsConfig;
338 }
339 
341  const PassPipelineCLParser &passPipeline) {
342  passPipelineCallback = [&](PassManager &pm) {
343  auto errorHandler = [&](const Twine &msg) {
344  emitError(UnknownLoc::get(pm.getContext())) << msg;
345  return failure();
346  };
347  if (failed(passPipeline.addToPipeline(pm, errorHandler)))
348  return failure();
349  if (this->shouldDumpPassPipeline()) {
350 
351  pm.dump();
352  llvm::errs() << "\n";
353  }
354  return success();
355  };
356  return *this;
357 }
358 
359 void MlirOptMainConfigCLOptions::setDialectPluginsCallback(
360  DialectRegistry &registry) {
361  dialectPlugins->setCallback([&](const std::string &pluginPath) {
362  auto plugin = DialectPlugin::load(pluginPath);
363  if (!plugin) {
364  errs() << "Failed to load dialect plugin from '" << pluginPath
365  << "'. Request ignored.\n";
366  return;
367  };
368  plugin.get().registerDialectRegistryCallbacks(registry);
369  });
370 }
371 
372 LogicalResult loadIRDLDialects(StringRef irdlFile, MLIRContext &ctx) {
373  DialectRegistry registry;
374  registry.insert<irdl::IRDLDialect>();
375  ctx.appendDialectRegistry(registry);
376 
377  // Set up the input file.
378  std::string errorMessage;
379  std::unique_ptr<MemoryBuffer> file = openInputFile(irdlFile, &errorMessage);
380  if (!file) {
381  emitError(UnknownLoc::get(&ctx)) << errorMessage;
382  return failure();
383  }
384 
385  // Give the buffer to the source manager.
386  // This will be picked up by the parser.
387  SourceMgr sourceMgr;
388  sourceMgr.AddNewSourceBuffer(std::move(file), SMLoc());
389 
390  SourceMgrDiagnosticHandler sourceMgrHandler(sourceMgr, &ctx);
391 
392  // Parse the input file.
393  OwningOpRef<ModuleOp> module(parseSourceFile<ModuleOp>(sourceMgr, &ctx));
394  if (!module)
395  return failure();
396 
397  // Load IRDL dialects.
398  return irdl::loadDialects(module.get());
399 }
400 
401 // Return success if the module can correctly round-trip. This intended to test
402 // that the custom printers/parsers are complete.
403 static LogicalResult doVerifyRoundTrip(Operation *op,
404  const MlirOptMainConfig &config,
405  bool useBytecode) {
406  // We use a new context to avoid resource handle renaming issue in the diff.
407  MLIRContext roundtripContext;
408  OwningOpRef<Operation *> roundtripModule;
409  roundtripContext.appendDialectRegistry(
410  op->getContext()->getDialectRegistry());
412  roundtripContext.allowUnregisteredDialects();
413  StringRef irdlFile = config.getIrdlFile();
414  if (!irdlFile.empty() && failed(loadIRDLDialects(irdlFile, roundtripContext)))
415  return failure();
416 
417  std::string testType = (useBytecode) ? "bytecode" : "textual";
418  // Print a first time with custom format (or bytecode) and parse it back to
419  // the roundtripModule.
420  {
421  std::string buffer;
422  llvm::raw_string_ostream ostream(buffer);
423  if (useBytecode) {
424  if (failed(writeBytecodeToFile(op, ostream))) {
425  op->emitOpError()
426  << "failed to write bytecode, cannot verify round-trip.\n";
427  return failure();
428  }
429  } else {
430  op->print(ostream,
431  OpPrintingFlags().printGenericOpForm().enableDebugInfo());
432  }
433  FallbackAsmResourceMap fallbackResourceMap;
434  ParserConfig parseConfig(&roundtripContext, config.shouldVerifyOnParsing(),
435  &fallbackResourceMap);
436  roundtripModule = parseSourceString<Operation *>(buffer, parseConfig);
437  if (!roundtripModule) {
438  op->emitOpError() << "failed to parse " << testType
439  << " content back, cannot verify round-trip.\n";
440  return failure();
441  }
442  }
443 
444  // Print in the generic form for the reference module and the round-tripped
445  // one and compare the outputs.
446  std::string reference, roundtrip;
447  {
448  llvm::raw_string_ostream ostreamref(reference);
449  op->print(ostreamref,
450  OpPrintingFlags().printGenericOpForm().enableDebugInfo());
451  llvm::raw_string_ostream ostreamrndtrip(roundtrip);
452  roundtripModule.get()->print(
453  ostreamrndtrip,
454  OpPrintingFlags().printGenericOpForm().enableDebugInfo());
455  }
456  if (reference != roundtrip) {
457  // TODO implement a diff.
458  return op->emitOpError()
459  << testType
460  << " roundTrip testing roundtripped module differs "
461  "from reference:\n<<<<<<Reference\n"
462  << reference << "\n=====\n"
463  << roundtrip << "\n>>>>>roundtripped\n";
464  }
465 
466  return success();
467 }
468 
469 static LogicalResult doVerifyRoundTrip(Operation *op,
470  const MlirOptMainConfig &config) {
471  auto txtStatus = doVerifyRoundTrip(op, config, /*useBytecode=*/false);
472  auto bcStatus = doVerifyRoundTrip(op, config, /*useBytecode=*/true);
473  return success(succeeded(txtStatus) && succeeded(bcStatus));
474 }
475 
476 /// Perform the actions on the input file indicated by the command line flags
477 /// within the specified context.
478 ///
479 /// This typically parses the main source file, runs zero or more optimization
480 /// passes, then prints the output.
481 ///
482 static LogicalResult
483 performActions(raw_ostream &os,
484  const std::shared_ptr<llvm::SourceMgr> &sourceMgr,
485  MLIRContext *context, const MlirOptMainConfig &config) {
488  TimingScope timing = tm.getRootScope();
489 
490  // Disable multi-threading when parsing the input file. This removes the
491  // unnecessary/costly context synchronization when parsing.
492  bool wasThreadingEnabled = context->isMultithreadingEnabled();
493  context->disableMultithreading();
494 
495  // Prepare the parser config, and attach any useful/necessary resource
496  // handlers. Unhandled external resources are treated as passthrough, i.e.
497  // they are not processed and will be emitted directly to the output
498  // untouched.
499  PassReproducerOptions reproOptions;
500  FallbackAsmResourceMap fallbackResourceMap;
501  ParserConfig parseConfig(context, config.shouldVerifyOnParsing(),
502  &fallbackResourceMap);
503  if (config.shouldRunReproducer())
504  reproOptions.attachResourceParser(parseConfig);
505 
506  // Parse the input file and reset the context threading state.
507  TimingScope parserTiming = timing.nest("Parser");
509  sourceMgr, parseConfig, !config.shouldUseExplicitModule());
510  parserTiming.stop();
511  if (!op)
512  return failure();
513 
514  // Perform round-trip verification if requested
515  if (config.shouldVerifyRoundtrip() &&
517  return failure();
518 
519  context->enableMultithreading(wasThreadingEnabled);
520 
522  config.getRemarksAllFilter(), config.getRemarksPassedFilter(),
523  config.getRemarksMissedFilter(), config.getRemarksAnalyseFilter(),
524  config.getRemarksFailedFilter()};
525 
526  mlir::MLIRContext &ctx = *context;
527 
528  switch (config.getRemarkFormat()) {
531  ctx, nullptr, cats, true /*printAsEmitRemarks*/)))
532  return failure();
533  break;
534 
536  std::string file = config.getRemarksOutputFile().empty()
537  ? "mlir-remarks.yaml"
538  : config.getRemarksOutputFile();
540  ctx, file, llvm::remarks::Format::YAML, cats)))
541  return failure();
542  break;
543  }
544 
546  std::string file = config.getRemarksOutputFile().empty()
547  ? "mlir-remarks.bitstream"
548  : config.getRemarksOutputFile();
550  ctx, file, llvm::remarks::Format::Bitstream, cats)))
551  return failure();
552  break;
553  }
554  }
555 
556  // Prepare the pass manager, applying command-line and reproducer options.
557  PassManager pm(op.get()->getName(), PassManager::Nesting::Implicit);
558  pm.enableVerifier(config.shouldVerifyPasses());
560  return failure();
561  pm.enableTiming(timing);
562  if (config.shouldRunReproducer() && failed(reproOptions.apply(pm)))
563  return failure();
564  if (failed(config.setupPassPipeline(pm)))
565  return failure();
566 
567  // Run the pipeline.
568  if (failed(pm.run(*op)))
569  return failure();
570 
571  // Generate reproducers if requested
572  if (!config.getReproducerFilename().empty()) {
573  StringRef anchorName = pm.getAnyOpAnchorName();
574  const auto &passes = pm.getPasses();
575  makeReproducer(anchorName, passes, op.get(),
576  config.getReproducerFilename());
577  }
578 
579  // Print the output.
580  TimingScope outputTiming = timing.nest("Output");
581  if (config.shouldEmitBytecode()) {
582  BytecodeWriterConfig writerConfig(fallbackResourceMap);
583  if (auto v = config.bytecodeVersionToEmit())
584  writerConfig.setDesiredBytecodeVersion(*v);
585  if (config.shouldElideResourceDataFromBytecode())
586  writerConfig.setElideResourceDataFlag();
587  return writeBytecodeToFile(op.get(), os, writerConfig);
588  }
589 
590  if (config.bytecodeVersionToEmit().has_value())
591  return emitError(UnknownLoc::get(pm.getContext()))
592  << "bytecode version while not emitting bytecode";
593  AsmState asmState(op.get(), OpPrintingFlags(), /*locationMap=*/nullptr,
594  &fallbackResourceMap);
595  os << OpWithState(op.get(), asmState) << '\n';
596  return success();
597 }
598 
599 /// Parses the memory buffer. If successfully, run a series of passes against
600 /// it and print the result.
601 static LogicalResult
602 processBuffer(raw_ostream &os, std::unique_ptr<MemoryBuffer> ownedBuffer,
603  llvm::MemoryBufferRef sourceBuffer,
604  const MlirOptMainConfig &config, DialectRegistry &registry,
605  SourceMgrDiagnosticVerifierHandler *verifyHandler,
606  llvm::ThreadPoolInterface *threadPool) {
607  // Tell sourceMgr about this buffer, which is what the parser will pick up.
608  auto sourceMgr = std::make_shared<SourceMgr>();
609  // Add the original buffer to the source manager to use for determining
610  // locations.
611  sourceMgr->AddNewSourceBuffer(
612  llvm::MemoryBuffer::getMemBuffer(sourceBuffer,
613  /*RequiresNullTerminator=*/false),
614  SMLoc());
615  sourceMgr->AddNewSourceBuffer(std::move(ownedBuffer), SMLoc());
616 
617  // Create a context just for the current buffer. Disable threading on
618  // creation since we'll inject the thread-pool separately.
620  if (threadPool)
621  context.setThreadPool(*threadPool);
622  if (verifyHandler)
623  verifyHandler->registerInContext(&context);
624 
625  StringRef irdlFile = config.getIrdlFile();
626  if (!irdlFile.empty() && failed(loadIRDLDialects(irdlFile, context)))
627  return failure();
628 
629  // Parse the input file.
630  context.allowUnregisteredDialects(config.shouldAllowUnregisteredDialects());
631  if (config.shouldVerifyDiagnostics())
632  context.printOpOnDiagnostic(false);
633 
634  tracing::InstallDebugHandler installDebugHandler(context,
635  config.getDebugConfig());
636 
637  // If we are in verify diagnostics mode then we have a lot of work to do,
638  // otherwise just perform the actions without worrying about it.
639  if (!config.shouldVerifyDiagnostics()) {
640  SourceMgrDiagnosticHandler sourceMgrHandler(*sourceMgr, &context);
641  DiagnosticFilter diagnosticFilter(&context,
642  config.getDiagnosticVerbosityLevel(),
643  config.shouldShowNotes());
644  return performActions(os, sourceMgr, &context, config);
645  }
646 
647  // Do any processing requested by command line flags. We don't care whether
648  // these actions succeed or fail, we only care what diagnostics they produce
649  // and whether they match our expectations.
650  (void)performActions(os, sourceMgr, &context, config);
651 
652  return success();
653 }
654 
655 std::pair<std::string, std::string>
656 mlir::registerAndParseCLIOptions(int argc, char **argv,
657  llvm::StringRef toolName,
658  DialectRegistry &registry) {
659  static cl::opt<std::string> inputFilename(
660  cl::Positional, cl::desc("<input file>"), cl::init("-"));
661 
662  static cl::opt<std::string> outputFilename("o", cl::desc("Output filename"),
663  cl::value_desc("filename"),
664  cl::init("-"));
665  // Register any command line options.
672 
673  // Build the list of dialects as a header for the --help message.
674  std::string helpHeader = (toolName + "\nAvailable Dialects: ").str();
675  {
676  llvm::raw_string_ostream os(helpHeader);
677  interleaveComma(registry.getDialectNames(), os,
678  [&](auto name) { os << name; });
679  }
680  // Parse pass names in main to ensure static initialization completed.
681  cl::ParseCommandLineOptions(argc, argv, helpHeader);
682  return std::make_pair(inputFilename.getValue(), outputFilename.getValue());
683 }
684 
685 static LogicalResult printRegisteredDialects(DialectRegistry &registry) {
686  llvm::outs() << "Available Dialects: ";
687  interleave(registry.getDialectNames(), llvm::outs(), ",");
688  llvm::outs() << "\n";
689  return success();
690 }
691 
692 static LogicalResult printRegisteredPassesAndReturn() {
694  return success();
695 }
696 
697 LogicalResult mlir::MlirOptMain(llvm::raw_ostream &outputStream,
698  std::unique_ptr<llvm::MemoryBuffer> buffer,
699  DialectRegistry &registry,
700  const MlirOptMainConfig &config) {
701  if (config.shouldShowDialects())
702  return printRegisteredDialects(registry);
703 
704  if (config.shouldListPasses())
706 
707  // The split-input-file mode is a very specific mode that slices the file
708  // up into small pieces and checks each independently.
709  // We use an explicit threadpool to avoid creating and joining/destroying
710  // threads for each of the split.
711  ThreadPoolInterface *threadPool = nullptr;
712 
713  // Create a temporary context for the sake of checking if
714  // --mlir-disable-threading was passed on the command line.
715  // We use the thread-pool this context is creating, and avoid
716  // creating any thread when disabled.
717  MLIRContext threadPoolCtx;
718  if (threadPoolCtx.isMultithreadingEnabled())
719  threadPool = &threadPoolCtx.getThreadPool();
720 
721  SourceMgr sourceMgr;
722  sourceMgr.AddNewSourceBuffer(
723  llvm::MemoryBuffer::getMemBuffer(buffer->getMemBufferRef(),
724  /*RequiresNullTerminator=*/false),
725  SMLoc());
726  // Note: this creates a verifier handler independent of the the flag set, as
727  // internally if the flag is not set, a new scoped diagnostic handler is
728  // created which would intercept the diagnostics and verify them.
729  SourceMgrDiagnosticVerifierHandler sourceMgrHandler(
730  sourceMgr, &threadPoolCtx, config.verifyDiagnosticsLevel());
731  auto chunkFn = [&](std::unique_ptr<MemoryBuffer> chunkBuffer,
732  llvm::MemoryBufferRef sourceBuffer, raw_ostream &os) {
733  return processBuffer(
734  os, std::move(chunkBuffer), sourceBuffer, config, registry,
735  config.shouldVerifyDiagnostics() ? &sourceMgrHandler : nullptr,
736  threadPool);
737  };
738  LogicalResult status = splitAndProcessBuffer(
739  llvm::MemoryBuffer::getMemBuffer(buffer->getMemBufferRef(),
740  /*RequiresNullTerminator=*/false),
741  chunkFn, outputStream, config.inputSplitMarker(),
742  config.outputSplitMarker());
743  if (config.shouldVerifyDiagnostics() && failed(sourceMgrHandler.verify()))
744  status = failure();
745  return status;
746 }
747 
748 LogicalResult mlir::MlirOptMain(int argc, char **argv,
749  llvm::StringRef inputFilename,
750  llvm::StringRef outputFilename,
751  DialectRegistry &registry) {
752 
753  InitLLVM y(argc, argv);
754 
756 
757  if (config.shouldShowDialects())
758  return printRegisteredDialects(registry);
759 
760  if (config.shouldListPasses())
762 
763  // When reading from stdin and the input is a tty, it is often a user
764  // mistake and the process "appears to be stuck". Print a message to let the
765  // user know about it!
766  if (inputFilename == "-" &&
767  sys::Process::FileDescriptorIsDisplayed(fileno(stdin)))
768  llvm::errs() << "(processing input from stdin now, hit ctrl-c/ctrl-d to "
769  "interrupt)\n";
770 
771  // Set up the input file.
772  std::string errorMessage;
773  auto file = openInputFile(inputFilename, &errorMessage);
774  if (!file) {
775  llvm::errs() << errorMessage << "\n";
776  return failure();
777  }
778 
779  auto output = openOutputFile(outputFilename, &errorMessage);
780  if (!output) {
781  llvm::errs() << errorMessage << "\n";
782  return failure();
783  }
784  if (failed(MlirOptMain(output->os(), std::move(file), registry, config)))
785  return failure();
786 
787  // Keep the output file if the invocation of MlirOptMain was successful.
788  output->keep();
789  return success();
790 }
791 
792 LogicalResult mlir::MlirOptMain(int argc, char **argv, llvm::StringRef toolName,
793  DialectRegistry &registry) {
794 
795  // Register and parse command line options.
796  std::string inputFilename, outputFilename;
797  std::tie(inputFilename, outputFilename) =
798  registerAndParseCLIOptions(argc, argv, toolName, registry);
799 
800  return MlirOptMain(argc, argv, inputFilename, outputFilename, registry);
801 }
static LogicalResult printRegisteredDialects(DialectRegistry &registry)
LogicalResult loadIRDLDialects(StringRef irdlFile, MLIRContext &ctx)
static LogicalResult doVerifyRoundTrip(Operation *op, const MlirOptMainConfig &config, bool useBytecode)
static LogicalResult processBuffer(raw_ostream &os, std::unique_ptr< MemoryBuffer > ownedBuffer, llvm::MemoryBufferRef sourceBuffer, const MlirOptMainConfig &config, DialectRegistry &registry, SourceMgrDiagnosticVerifierHandler *verifyHandler, llvm::ThreadPoolInterface *threadPool)
Parses the memory buffer.
static LogicalResult printRegisteredPassesAndReturn()
ManagedStatic< MlirOptMainConfigCLOptions > clOptionsConfig
static LogicalResult performActions(raw_ostream &os, const std::shared_ptr< llvm::SourceMgr > &sourceMgr, MLIRContext *context, const MlirOptMainConfig &config)
Perform the actions on the input file indicated by the command line flags within the specified contex...
static std::string diag(const llvm::Value &value)
This class provides management for the lifetime of the state used when printing the IR.
Definition: AsmState.h:542
This class contains the configuration used for the bytecode writer.
void setElideResourceDataFlag(bool shouldElideResourceData=true)
Set a boolean flag to skip emission of resources into the bytecode file.
void setDesiredBytecodeVersion(int64_t bytecodeVersion)
Set the desired bytecode version to emit.
Facilities for time measurement and report printing to an output stream.
Definition: Timing.h:388
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
Definition: Diagnostics.h:155
static llvm::Expected< DialectPlugin > load(const std::string &filename)
Attempts to load a dialect plugin from a given file.
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
auto getDialectNames() const
Return the names of dialects known to this registry.
A fallback map containing external resources not explicitly handled by another parser/printer.
Definition: AsmState.h:421
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:63
void appendDialectRegistry(const DialectRegistry &registry)
Append the contents of the given dialect registry to the registry associated with this context.
void disableMultithreading(bool disable=true)
Set the flag specifying if multi-threading is disabled by the context.
void setThreadPool(llvm::ThreadPoolInterface &pool)
Set a new thread pool to be used in this context.
void enableMultithreading(bool enable=true)
Definition: MLIRContext.h:155
void printOpOnDiagnostic(bool enable)
Set the flag specifying if we should attach the operation to diagnostics emitted via Operation::emit.
const DialectRegistry & getDialectRegistry()
Return the dialect registry associated with this context.
llvm::ThreadPoolInterface & getThreadPool()
Return the thread pool used by this context.
bool isMultithreadingEnabled()
Return true if multi-threading is enabled by the context.
void allowUnregisteredDialects(bool allow=true)
Enables creating operations in unregistered dialects.
bool allowsUnregisteredDialects()
Return true if we allow to create operation for unregistered dialects.
Configuration options for the mlir-opt tool.
Definition: MlirOptMain.h:54
static MlirOptMainConfig createFromCLOptions()
Create a new config with the default set from the CL options.
static void registerCLOptions(DialectRegistry &dialectRegistry)
Register the options as global LLVM command line options.
MlirOptMainConfig & setPassPipelineParser(const PassPipelineCLParser &parser)
Set the parser to use to populate the pass manager.
@ Implicit
Implicit nesting behavior.
iterator_range< pass_iterator > getPasses()
Definition: PassManager.h:79
static StringRef getAnyOpAnchorName()
Return the string name used to anchor op-agnostic pass managers that operate generically on any viabl...
Definition: PassManager.h:137
Set of flags used to control the behavior of the various IR print methods (e.g.
A wrapper class that allows for printing an operation with a custom AsmState, useful to act as a "str...
Definition: Operation.h:1134
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
MLIRContext * getContext()
Return the context this operation is associated with.
Definition: Operation.h:216
void print(raw_ostream &os, const OpPrintingFlags &flags={})
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Definition: Operation.cpp:672
OpTy get() const
Allow accessing the internal op.
Definition: OwningOpRef.h:51
This class represents a configuration for the MLIR assembly parser.
Definition: AsmState.h:469
The main pass manager and pipeline builder.
Definition: PassManager.h:232
MLIRContext * getContext() const
Return an instance of the context.
Definition: PassManager.h:257
LogicalResult run(Operation *op)
Run the passes within this manager on the provided operation.
Definition: Pass.cpp:1029
void enableVerifier(bool enabled=true)
Runs the verifier after each individual pass.
Definition: Pass.cpp:1026
void enableTiming(TimingScope &timingScope)
Add an instrumentation to time the execution of passes and the computation of analyses.
Definition: PassTiming.cpp:147
This class implements a command-line parser for MLIR passes.
Definition: PassRegistry.h:247
LogicalResult addToPipeline(OpPassManager &pm, function_ref< LogicalResult(const Twine &)> errorHandler) const
Adds the passes defined by this parser entry to the given pass manager.
static llvm::Expected< PassPlugin > load(const std::string &filename)
Attempts to load a pass plugin from a given file.
Definition: PassPlugin.cpp:16
This diagnostic handler is a simple RAII class that registers and erases a diagnostic handler on a gi...
Definition: Diagnostics.h:522
This class is a utility diagnostic handler for use with llvm::SourceMgr.
Definition: Diagnostics.h:559
This class is a utility diagnostic handler for use with llvm::SourceMgr that verifies that emitted di...
Definition: Diagnostics.h:627
void registerInContext(MLIRContext *ctx)
Register this handler with the given context.
LogicalResult verify()
Returns the status of the handler and verifies that all expected diagnostics were emitted.
TimingScope getRootScope()
Get the root timer of this timing manager wrapped in a TimingScope for convenience.
Definition: Timing.cpp:72
An RAII-style wrapper around a timer that ensures the timer is properly started and stopped.
Definition: Timing.h:272
TimingScope nest(Args... args)
Create a nested timing scope.
Definition: Timing.h:311
void stop()
Manually stop the timer early.
Definition: Timing.h:300
static DebugConfig createFromCLOptions()
Create a new config with the default set from the CL options.
static void registerCLOptions()
Register the options as global LLVM command line options.
static void registerCLOptions()
Register the command line options for debug counters.
This is a RAII class that installs the debug handlers on the context based on the provided configurat...
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition: CallGraph.h:229
llvm::LogicalResult loadDialects(ModuleOp op)
Load all the dialects defined in the module.
QueryRef parse(llvm::StringRef line, const QuerySession &qs)
Definition: Query.cpp:21
LogicalResult enableOptimizationRemarksWithLLVMStreamer(MLIRContext &ctx, StringRef filePath, llvm::remarks::Format fmt, const RemarkCategories &cat, bool printAsEmitRemarks=false)
Enable optimization remarks to a file with the given path and format.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:491
LogicalResult enableOptimizationRemarks(MLIRContext &ctx, std::unique_ptr< remark::detail::MLIRRemarkStreamerBase > streamer, const remark::RemarkCategories &cats, bool printAsEmitRemarks=false)
Setup remarks for the context.
Include the generated interface declarations.
std::unique_ptr< llvm::MemoryBuffer > openInputFile(llvm::StringRef inputFilename, std::string *errorMessage=nullptr)
Open the file specified by its name for reading.
LogicalResult applyPassManagerCLOptions(PassManager &pm)
Apply any values provided to the pass manager options that were registered with 'registerPassManagerO...
const char *const kDefaultSplitMarker
void registerDefaultTimingManagerCLOptions()
Register a set of useful command-line options that can be used to configure a DefaultTimingManager.
Definition: Timing.cpp:612
const FrozenRewritePatternSet GreedyRewriteConfig config
void printRegisteredPasses()
Prints the passes that were previously registered and stored in passRegistry.
LogicalResult MlirOptMain(llvm::raw_ostream &outputStream, std::unique_ptr< llvm::MemoryBuffer > buffer, DialectRegistry &registry, const MlirOptMainConfig &config)
Perform the core processing behind mlir-opt.
std::unique_ptr< llvm::ToolOutputFile > openOutputFile(llvm::StringRef outputFilename, std::string *errorMessage=nullptr)
Open the file specified by its name for writing.
std::string makeReproducer(StringRef anchorName, const llvm::iterator_range< OpPassManager::pass_iterator > &passes, Operation *op, StringRef outputFile, bool disableThreads=false, bool verifyPasses=false)
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
void registerMLIRContextCLOptions()
Register a set of useful command-line options that can be used to configure various flags within the ...
Definition: MLIRContext.cpp:88
LogicalResult splitAndProcessBuffer(std::unique_ptr< llvm::MemoryBuffer > originalBuffer, ChunkBufferHandler processChunkBuffer, raw_ostream &os, llvm::StringRef inputSplitMarker=kDefaultSplitMarker, llvm::StringRef outputSplitMarker="")
Splits the specified buffer on a marker (// ----- by default), processes each chunk independently acc...
OwningOpRef< Operation * > parseSourceFileForTool(const std::shared_ptr< llvm::SourceMgr > &sourceMgr, const ParserConfig &config, bool insertImplicitModule)
This parses the file specified by the indicated SourceMgr.
void registerAsmPrinterCLOptions()
Register a set of useful command-line options that can be used to configure various flags within the ...
Definition: AsmPrinter.cpp:211
void registerPassManagerCLOptions()
Register a set of useful command-line options that can be used to configure a pass manager.
void applyDefaultTimingManagerCLOptions(DefaultTimingManager &tm)
Apply any values that were registered with 'registerDefaultTimingManagerOptions' to a DefaultTimingMa...
Definition: Timing.cpp:617
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
RemarkFormat
Definition: MlirOptMain.h:41
std::pair< std::string, std::string > registerAndParseCLIOptions(int argc, char **argv, llvm::StringRef toolName, DialectRegistry &registry)
Register and parse command line options.
VerbosityLevel
enum class to indicate the verbosity level of the diagnostic filter.
Definition: MlirOptMain.h:35
LogicalResult writeBytecodeToFile(Operation *op, raw_ostream &os, const BytecodeWriterConfig &config={})
Write the bytecode for the given operation to the provided output stream.
void attachResourceParser(ParserConfig &config)
Attach an assembly resource parser to 'config' that collects the MLIR reproducer configuration into t...
LogicalResult apply(PassManager &pm) const
Apply the reproducer options to 'pm' and its context.
Define an the set of categories to accept.
Definition: Remarks.h:31