MLIR  21.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"
23 #include "mlir/IR/AsmState.h"
24 #include "mlir/IR/Attributes.h"
25 #include "mlir/IR/BuiltinOps.h"
26 #include "mlir/IR/Diagnostics.h"
27 #include "mlir/IR/Dialect.h"
28 #include "mlir/IR/Location.h"
29 #include "mlir/IR/MLIRContext.h"
30 #include "mlir/Parser/Parser.h"
31 #include "mlir/Pass/Pass.h"
32 #include "mlir/Pass/PassManager.h"
33 #include "mlir/Pass/PassRegistry.h"
35 #include "mlir/Support/Timing.h"
40 #include "llvm/ADT/StringRef.h"
41 #include "llvm/Support/CommandLine.h"
42 #include "llvm/Support/FileUtilities.h"
43 #include "llvm/Support/InitLLVM.h"
44 #include "llvm/Support/LogicalResult.h"
45 #include "llvm/Support/ManagedStatic.h"
46 #include "llvm/Support/Process.h"
47 #include "llvm/Support/Regex.h"
48 #include "llvm/Support/SourceMgr.h"
49 #include "llvm/Support/StringSaver.h"
50 #include "llvm/Support/ThreadPool.h"
51 #include "llvm/Support/ToolOutputFile.h"
52 
53 using namespace mlir;
54 using namespace llvm;
55 
56 namespace {
57 class BytecodeVersionParser : public cl::parser<std::optional<int64_t>> {
58 public:
59  BytecodeVersionParser(cl::Option &o)
60  : cl::parser<std::optional<int64_t>>(o) {}
61 
62  bool parse(cl::Option &o, StringRef /*argName*/, StringRef arg,
63  std::optional<int64_t> &v) {
64  long long w;
65  if (getAsSignedInteger(arg, 10, w))
66  return o.error("Invalid argument '" + arg +
67  "', only integer is supported.");
68  v = w;
69  return false;
70  }
71 };
72 
73 /// This class is intended to manage the handling of command line options for
74 /// creating a *-opt config. This is a singleton.
75 struct MlirOptMainConfigCLOptions : public MlirOptMainConfig {
76  MlirOptMainConfigCLOptions() {
77  // These options are static but all uses ExternalStorage to initialize the
78  // members of the parent class. This is unusual but since this class is a
79  // singleton it basically attaches command line option to the singleton
80  // members.
81 
82  static cl::opt<bool, /*ExternalStorage=*/true> allowUnregisteredDialects(
83  "allow-unregistered-dialect",
84  cl::desc("Allow operation with no registered dialects"),
85  cl::location(allowUnregisteredDialectsFlag), cl::init(false));
86 
87  static cl::opt<bool, /*ExternalStorage=*/true> dumpPassPipeline(
88  "dump-pass-pipeline", cl::desc("Print the pipeline that will be run"),
89  cl::location(dumpPassPipelineFlag), cl::init(false));
90 
91  static cl::opt<bool, /*ExternalStorage=*/true> emitBytecode(
92  "emit-bytecode", cl::desc("Emit bytecode when generating output"),
93  cl::location(emitBytecodeFlag), cl::init(false));
94 
95  static cl::opt<bool, /*ExternalStorage=*/true> elideResourcesFromBytecode(
96  "elide-resource-data-from-bytecode",
97  cl::desc("Elide resources when generating bytecode"),
98  cl::location(elideResourceDataFromBytecodeFlag), cl::init(false));
99 
100  static cl::opt<std::optional<int64_t>, /*ExternalStorage=*/true,
101  BytecodeVersionParser>
102  bytecodeVersion(
103  "emit-bytecode-version",
104  cl::desc("Use specified bytecode when generating output"),
105  cl::location(emitBytecodeVersion), cl::init(std::nullopt));
106 
107  static cl::opt<std::string, /*ExternalStorage=*/true> irdlFile(
108  "irdl-file",
109  cl::desc("IRDL file to register before processing the input"),
110  cl::location(irdlFileFlag), cl::init(""), cl::value_desc("filename"));
111 
112  static cl::opt<VerbosityLevel, /*ExternalStorage=*/true>
113  diagnosticVerbosityLevel(
114  "mlir-diagnostic-verbosity-level",
115  cl::desc("Choose level of diagnostic information"),
116  cl::location(diagnosticVerbosityLevelFlag),
118  cl::values(
119  clEnumValN(VerbosityLevel::ErrorsOnly, "errors", "Errors only"),
120  clEnumValN(VerbosityLevel::ErrorsAndWarnings, "warnings",
121  "Errors and warnings"),
122  clEnumValN(VerbosityLevel::ErrorsWarningsAndRemarks, "remarks",
123  "Errors, warnings and remarks")));
124 
125  static cl::opt<bool, /*ExternalStorage=*/true> disableDiagnosticNotes(
126  "mlir-disable-diagnostic-notes", cl::desc("Disable diagnostic notes."),
127  cl::location(disableDiagnosticNotesFlag), cl::init(false));
128 
129  static cl::opt<bool, /*ExternalStorage=*/true> explicitModule(
130  "no-implicit-module",
131  cl::desc("Disable implicit addition of a top-level module op during "
132  "parsing"),
133  cl::location(useExplicitModuleFlag), cl::init(false));
134 
135  static cl::opt<bool, /*ExternalStorage=*/true> listPasses(
136  "list-passes", cl::desc("Print the list of registered passes and exit"),
137  cl::location(listPassesFlag), cl::init(false));
138 
139  static cl::opt<bool, /*ExternalStorage=*/true> runReproducer(
140  "run-reproducer", cl::desc("Run the pipeline stored in the reproducer"),
141  cl::location(runReproducerFlag), cl::init(false));
142 
143  static cl::opt<bool, /*ExternalStorage=*/true> showDialects(
144  "show-dialects",
145  cl::desc("Print the list of registered dialects and exit"),
146  cl::location(showDialectsFlag), cl::init(false));
147 
148  static cl::opt<std::string, /*ExternalStorage=*/true> splitInputFile{
149  "split-input-file",
150  llvm::cl::ValueOptional,
151  cl::callback([&](const std::string &str) {
152  // Implicit value: use default marker if flag was used without value.
153  if (str.empty())
154  splitInputFile.setValue(kDefaultSplitMarker);
155  }),
156  cl::desc("Split the input file into chunks using the given or "
157  "default marker and process each chunk independently"),
158  cl::location(splitInputFileFlag),
159  cl::init("")};
160 
161  static cl::opt<std::string, /*ExternalStorage=*/true> outputSplitMarker(
162  "output-split-marker",
163  cl::desc("Split marker to use for merging the ouput"),
164  cl::location(outputSplitMarkerFlag), cl::init(kDefaultSplitMarker));
165 
167  /*ExternalStorage=*/true>
168  verifyDiagnostics{
169  "verify-diagnostics", llvm::cl::ValueOptional,
170  cl::desc("Check that emitted diagnostics match expected-* lines on "
171  "the corresponding line"),
172  cl::location(verifyDiagnosticsFlag),
173  cl::values(
175  "all",
176  "Check all diagnostics (expected, unexpected, "
177  "near-misses)"),
178  // Implicit value: when passed with no arguments, e.g.
179  // `--verify-diagnostics` or `--verify-diagnostics=`.
181  "Check all diagnostics (expected, unexpected, "
182  "near-misses)"),
183  clEnumValN(
185  "only-expected", "Check only expected diagnostics"))};
186 
187  static cl::opt<bool, /*ExternalStorage=*/true> verifyPasses(
188  "verify-each",
189  cl::desc("Run the verifier after each transformation pass"),
190  cl::location(verifyPassesFlag), cl::init(true));
191 
192  static cl::opt<bool, /*ExternalStorage=*/true> disableVerifyOnParsing(
193  "mlir-very-unsafe-disable-verifier-on-parsing",
194  cl::desc("Disable the verifier on parsing (very unsafe)"),
195  cl::location(disableVerifierOnParsingFlag), cl::init(false));
196 
197  static cl::opt<bool, /*ExternalStorage=*/true> verifyRoundtrip(
198  "verify-roundtrip",
199  cl::desc("Round-trip the IR after parsing and ensure it succeeds"),
200  cl::location(verifyRoundtripFlag), cl::init(false));
201 
202  static cl::list<std::string> passPlugins(
203  "load-pass-plugin", cl::desc("Load passes from plugin library"));
204 
205  static cl::opt<std::string, /*ExternalStorage=*/true>
206  generateReproducerFile(
207  "mlir-generate-reproducer",
208  llvm::cl::desc(
209  "Generate an mlir reproducer at the provided filename"
210  " (no crash required)"),
211  cl::location(generateReproducerFileFlag), cl::init(""),
212  cl::value_desc("filename"));
213 
214  /// Set the callback to load a pass plugin.
215  passPlugins.setCallback([&](const std::string &pluginPath) {
216  auto plugin = PassPlugin::load(pluginPath);
217  if (!plugin) {
218  errs() << "Failed to load passes from '" << pluginPath
219  << "'. Request ignored.\n";
220  return;
221  }
222  plugin.get().registerPassRegistryCallbacks();
223  });
224 
225  static cl::list<std::string> dialectPlugins(
226  "load-dialect-plugin", cl::desc("Load dialects from plugin library"));
227  this->dialectPlugins = std::addressof(dialectPlugins);
228 
229  static PassPipelineCLParser passPipeline("", "Compiler passes to run", "p");
230  setPassPipelineParser(passPipeline);
231  }
232 
233  /// Set the callback to load a dialect plugin.
234  void setDialectPluginsCallback(DialectRegistry &registry);
235 
236  /// Pointer to static dialectPlugins variable in constructor, needed by
237  /// setDialectPluginsCallback(DialectRegistry&).
238  cl::list<std::string> *dialectPlugins = nullptr;
239 };
240 
241 /// A scoped diagnostic handler that suppresses certain diagnostics based on
242 /// the verbosity level and whether the diagnostic is a note.
243 class DiagnosticFilter : public ScopedDiagnosticHandler {
244 public:
245  DiagnosticFilter(MLIRContext *ctx, VerbosityLevel verbosityLevel,
246  bool showNotes = true)
247  : ScopedDiagnosticHandler(ctx) {
248  setHandler([verbosityLevel, showNotes](Diagnostic &diag) {
249  auto severity = diag.getSeverity();
250  switch (severity) {
252  // failure indicates that the error is not handled by the filter and
253  // goes through to the default handler. Therefore, the error can be
254  // successfully printed.
255  return failure();
257  if (verbosityLevel == VerbosityLevel::ErrorsOnly)
258  return success();
259  else
260  return failure();
262  if (verbosityLevel == VerbosityLevel::ErrorsOnly ||
263  verbosityLevel == VerbosityLevel::ErrorsAndWarnings)
264  return success();
265  else
266  return failure();
268  if (showNotes)
269  return failure();
270  else
271  return success();
272  }
273  llvm_unreachable("Unknown diagnostic severity");
274  });
275  }
276 };
277 } // namespace
278 
279 ManagedStatic<MlirOptMainConfigCLOptions> clOptionsConfig;
280 
282  clOptionsConfig->setDialectPluginsCallback(registry);
284 }
285 
288  return *clOptionsConfig;
289 }
290 
292  const PassPipelineCLParser &passPipeline) {
293  passPipelineCallback = [&](PassManager &pm) {
294  auto errorHandler = [&](const Twine &msg) {
295  emitError(UnknownLoc::get(pm.getContext())) << msg;
296  return failure();
297  };
298  if (failed(passPipeline.addToPipeline(pm, errorHandler)))
299  return failure();
300  if (this->shouldDumpPassPipeline()) {
301 
302  pm.dump();
303  llvm::errs() << "\n";
304  }
305  return success();
306  };
307  return *this;
308 }
309 
310 void MlirOptMainConfigCLOptions::setDialectPluginsCallback(
311  DialectRegistry &registry) {
312  dialectPlugins->setCallback([&](const std::string &pluginPath) {
313  auto plugin = DialectPlugin::load(pluginPath);
314  if (!plugin) {
315  errs() << "Failed to load dialect plugin from '" << pluginPath
316  << "'. Request ignored.\n";
317  return;
318  };
319  plugin.get().registerDialectRegistryCallbacks(registry);
320  });
321 }
322 
323 LogicalResult loadIRDLDialects(StringRef irdlFile, MLIRContext &ctx) {
324  DialectRegistry registry;
325  registry.insert<irdl::IRDLDialect>();
326  ctx.appendDialectRegistry(registry);
327 
328  // Set up the input file.
329  std::string errorMessage;
330  std::unique_ptr<MemoryBuffer> file = openInputFile(irdlFile, &errorMessage);
331  if (!file) {
332  emitError(UnknownLoc::get(&ctx)) << errorMessage;
333  return failure();
334  }
335 
336  // Give the buffer to the source manager.
337  // This will be picked up by the parser.
338  SourceMgr sourceMgr;
339  sourceMgr.AddNewSourceBuffer(std::move(file), SMLoc());
340 
341  SourceMgrDiagnosticHandler sourceMgrHandler(sourceMgr, &ctx);
342 
343  // Parse the input file.
344  OwningOpRef<ModuleOp> module(parseSourceFile<ModuleOp>(sourceMgr, &ctx));
345  if (!module)
346  return failure();
347 
348  // Load IRDL dialects.
349  return irdl::loadDialects(module.get());
350 }
351 
352 // Return success if the module can correctly round-trip. This intended to test
353 // that the custom printers/parsers are complete.
354 static LogicalResult doVerifyRoundTrip(Operation *op,
355  const MlirOptMainConfig &config,
356  bool useBytecode) {
357  // We use a new context to avoid resource handle renaming issue in the diff.
358  MLIRContext roundtripContext;
359  OwningOpRef<Operation *> roundtripModule;
360  roundtripContext.appendDialectRegistry(
361  op->getContext()->getDialectRegistry());
363  roundtripContext.allowUnregisteredDialects();
364  StringRef irdlFile = config.getIrdlFile();
365  if (!irdlFile.empty() && failed(loadIRDLDialects(irdlFile, roundtripContext)))
366  return failure();
367 
368  std::string testType = (useBytecode) ? "bytecode" : "textual";
369  // Print a first time with custom format (or bytecode) and parse it back to
370  // the roundtripModule.
371  {
372  std::string buffer;
373  llvm::raw_string_ostream ostream(buffer);
374  if (useBytecode) {
375  if (failed(writeBytecodeToFile(op, ostream))) {
376  op->emitOpError()
377  << "failed to write bytecode, cannot verify round-trip.\n";
378  return failure();
379  }
380  } else {
381  op->print(ostream,
382  OpPrintingFlags().printGenericOpForm().enableDebugInfo());
383  }
384  FallbackAsmResourceMap fallbackResourceMap;
385  ParserConfig parseConfig(&roundtripContext, config.shouldVerifyOnParsing(),
386  &fallbackResourceMap);
387  roundtripModule = parseSourceString<Operation *>(buffer, parseConfig);
388  if (!roundtripModule) {
389  op->emitOpError() << "failed to parse " << testType
390  << " content back, cannot verify round-trip.\n";
391  return failure();
392  }
393  }
394 
395  // Print in the generic form for the reference module and the round-tripped
396  // one and compare the outputs.
397  std::string reference, roundtrip;
398  {
399  llvm::raw_string_ostream ostreamref(reference);
400  op->print(ostreamref,
401  OpPrintingFlags().printGenericOpForm().enableDebugInfo());
402  llvm::raw_string_ostream ostreamrndtrip(roundtrip);
403  roundtripModule.get()->print(
404  ostreamrndtrip,
405  OpPrintingFlags().printGenericOpForm().enableDebugInfo());
406  }
407  if (reference != roundtrip) {
408  // TODO implement a diff.
409  return op->emitOpError()
410  << testType
411  << " roundTrip testing roundtripped module differs "
412  "from reference:\n<<<<<<Reference\n"
413  << reference << "\n=====\n"
414  << roundtrip << "\n>>>>>roundtripped\n";
415  }
416 
417  return success();
418 }
419 
420 static LogicalResult doVerifyRoundTrip(Operation *op,
421  const MlirOptMainConfig &config) {
422  auto txtStatus = doVerifyRoundTrip(op, config, /*useBytecode=*/false);
423  auto bcStatus = doVerifyRoundTrip(op, config, /*useBytecode=*/true);
424  return success(succeeded(txtStatus) && succeeded(bcStatus));
425 }
426 
427 /// Perform the actions on the input file indicated by the command line flags
428 /// within the specified context.
429 ///
430 /// This typically parses the main source file, runs zero or more optimization
431 /// passes, then prints the output.
432 ///
433 static LogicalResult
434 performActions(raw_ostream &os,
435  const std::shared_ptr<llvm::SourceMgr> &sourceMgr,
436  MLIRContext *context, const MlirOptMainConfig &config) {
439  TimingScope timing = tm.getRootScope();
440 
441  // Disable multi-threading when parsing the input file. This removes the
442  // unnecessary/costly context synchronization when parsing.
443  bool wasThreadingEnabled = context->isMultithreadingEnabled();
444  context->disableMultithreading();
445 
446  // Prepare the parser config, and attach any useful/necessary resource
447  // handlers. Unhandled external resources are treated as passthrough, i.e.
448  // they are not processed and will be emitted directly to the output
449  // untouched.
450  PassReproducerOptions reproOptions;
451  FallbackAsmResourceMap fallbackResourceMap;
452  ParserConfig parseConfig(context, config.shouldVerifyOnParsing(),
453  &fallbackResourceMap);
454  if (config.shouldRunReproducer())
455  reproOptions.attachResourceParser(parseConfig);
456 
457  // Parse the input file and reset the context threading state.
458  TimingScope parserTiming = timing.nest("Parser");
460  sourceMgr, parseConfig, !config.shouldUseExplicitModule());
461  parserTiming.stop();
462  if (!op)
463  return failure();
464 
465  // Perform round-trip verification if requested
466  if (config.shouldVerifyRoundtrip() &&
467  failed(doVerifyRoundTrip(op.get(), config)))
468  return failure();
469 
470  context->enableMultithreading(wasThreadingEnabled);
471 
472  // Prepare the pass manager, applying command-line and reproducer options.
473  PassManager pm(op.get()->getName(), PassManager::Nesting::Implicit);
474  pm.enableVerifier(config.shouldVerifyPasses());
475  if (failed(applyPassManagerCLOptions(pm)))
476  return failure();
477  pm.enableTiming(timing);
478  if (config.shouldRunReproducer() && failed(reproOptions.apply(pm)))
479  return failure();
480  if (failed(config.setupPassPipeline(pm)))
481  return failure();
482 
483  // Run the pipeline.
484  if (failed(pm.run(*op)))
485  return failure();
486 
487  // Generate reproducers if requested
488  if (!config.getReproducerFilename().empty()) {
489  StringRef anchorName = pm.getAnyOpAnchorName();
490  const auto &passes = pm.getPasses();
491  makeReproducer(anchorName, passes, op.get(),
492  config.getReproducerFilename());
493  }
494 
495  // Print the output.
496  TimingScope outputTiming = timing.nest("Output");
497  if (config.shouldEmitBytecode()) {
498  BytecodeWriterConfig writerConfig(fallbackResourceMap);
499  if (auto v = config.bytecodeVersionToEmit())
500  writerConfig.setDesiredBytecodeVersion(*v);
501  if (config.shouldElideResourceDataFromBytecode())
502  writerConfig.setElideResourceDataFlag();
503  return writeBytecodeToFile(op.get(), os, writerConfig);
504  }
505 
506  if (config.bytecodeVersionToEmit().has_value())
507  return emitError(UnknownLoc::get(pm.getContext()))
508  << "bytecode version while not emitting bytecode";
509  AsmState asmState(op.get(), OpPrintingFlags(), /*locationMap=*/nullptr,
510  &fallbackResourceMap);
511  op.get()->print(os, asmState);
512  os << '\n';
513  return success();
514 }
515 
516 /// Parses the memory buffer. If successfully, run a series of passes against
517 /// it and print the result.
518 static LogicalResult processBuffer(raw_ostream &os,
519  std::unique_ptr<MemoryBuffer> ownedBuffer,
520  const MlirOptMainConfig &config,
521  DialectRegistry &registry,
522  llvm::ThreadPoolInterface *threadPool) {
523  // Tell sourceMgr about this buffer, which is what the parser will pick up.
524  auto sourceMgr = std::make_shared<SourceMgr>();
525  sourceMgr->AddNewSourceBuffer(std::move(ownedBuffer), SMLoc());
526 
527  // Create a context just for the current buffer. Disable threading on creation
528  // since we'll inject the thread-pool separately.
530  if (threadPool)
531  context.setThreadPool(*threadPool);
532 
533  StringRef irdlFile = config.getIrdlFile();
534  if (!irdlFile.empty() && failed(loadIRDLDialects(irdlFile, context)))
535  return failure();
536 
537  // Parse the input file.
538  context.allowUnregisteredDialects(config.shouldAllowUnregisteredDialects());
539  if (config.shouldVerifyDiagnostics())
540  context.printOpOnDiagnostic(false);
541 
542  tracing::InstallDebugHandler installDebugHandler(context,
543  config.getDebugConfig());
544 
545  // If we are in verify diagnostics mode then we have a lot of work to do,
546  // otherwise just perform the actions without worrying about it.
547  if (!config.shouldVerifyDiagnostics()) {
548  SourceMgrDiagnosticHandler sourceMgrHandler(*sourceMgr, &context);
549  DiagnosticFilter diagnosticFilter(&context,
550  config.getDiagnosticVerbosityLevel(),
551  config.shouldShowNotes());
552  return performActions(os, sourceMgr, &context, config);
553  }
554 
555  SourceMgrDiagnosticVerifierHandler sourceMgrHandler(
556  *sourceMgr, &context, config.verifyDiagnosticsLevel());
557 
558  // Do any processing requested by command line flags. We don't care whether
559  // these actions succeed or fail, we only care what diagnostics they produce
560  // and whether they match our expectations.
561  (void)performActions(os, sourceMgr, &context, config);
562 
563  // Verify the diagnostic handler to make sure that each of the diagnostics
564  // matched.
565  return sourceMgrHandler.verify();
566 }
567 
568 std::pair<std::string, std::string>
569 mlir::registerAndParseCLIOptions(int argc, char **argv,
570  llvm::StringRef toolName,
571  DialectRegistry &registry) {
572  static cl::opt<std::string> inputFilename(
573  cl::Positional, cl::desc("<input file>"), cl::init("-"));
574 
575  static cl::opt<std::string> outputFilename("o", cl::desc("Output filename"),
576  cl::value_desc("filename"),
577  cl::init("-"));
578  // Register any command line options.
585 
586  // Build the list of dialects as a header for the --help message.
587  std::string helpHeader = (toolName + "\nAvailable Dialects: ").str();
588  {
589  llvm::raw_string_ostream os(helpHeader);
590  interleaveComma(registry.getDialectNames(), os,
591  [&](auto name) { os << name; });
592  }
593  // Parse pass names in main to ensure static initialization completed.
594  cl::ParseCommandLineOptions(argc, argv, helpHeader);
595  return std::make_pair(inputFilename.getValue(), outputFilename.getValue());
596 }
597 
598 static LogicalResult printRegisteredDialects(DialectRegistry &registry) {
599  llvm::outs() << "Available Dialects: ";
600  interleave(registry.getDialectNames(), llvm::outs(), ",");
601  llvm::outs() << "\n";
602  return success();
603 }
604 
605 static LogicalResult printRegisteredPassesAndReturn() {
607  return success();
608 }
609 
610 LogicalResult mlir::MlirOptMain(llvm::raw_ostream &outputStream,
611  std::unique_ptr<llvm::MemoryBuffer> buffer,
612  DialectRegistry &registry,
613  const MlirOptMainConfig &config) {
614  if (config.shouldShowDialects())
615  return printRegisteredDialects(registry);
616 
617  if (config.shouldListPasses())
619 
620  // The split-input-file mode is a very specific mode that slices the file
621  // up into small pieces and checks each independently.
622  // We use an explicit threadpool to avoid creating and joining/destroying
623  // threads for each of the split.
624  ThreadPoolInterface *threadPool = nullptr;
625 
626  // Create a temporary context for the sake of checking if
627  // --mlir-disable-threading was passed on the command line.
628  // We use the thread-pool this context is creating, and avoid
629  // creating any thread when disabled.
630  MLIRContext threadPoolCtx;
631  if (threadPoolCtx.isMultithreadingEnabled())
632  threadPool = &threadPoolCtx.getThreadPool();
633 
634  auto chunkFn = [&](std::unique_ptr<MemoryBuffer> chunkBuffer,
635  raw_ostream &os) {
636  return processBuffer(os, std::move(chunkBuffer), config, registry,
637  threadPool);
638  };
639  return splitAndProcessBuffer(std::move(buffer), chunkFn, outputStream,
640  config.inputSplitMarker(),
641  config.outputSplitMarker());
642 }
643 
644 LogicalResult mlir::MlirOptMain(int argc, char **argv,
645  llvm::StringRef inputFilename,
646  llvm::StringRef outputFilename,
647  DialectRegistry &registry) {
648 
649  InitLLVM y(argc, argv);
650 
652 
653  if (config.shouldShowDialects())
654  return printRegisteredDialects(registry);
655 
656  if (config.shouldListPasses())
658 
659  // When reading from stdin and the input is a tty, it is often a user mistake
660  // and the process "appears to be stuck". Print a message to let the user know
661  // about it!
662  if (inputFilename == "-" &&
663  sys::Process::FileDescriptorIsDisplayed(fileno(stdin)))
664  llvm::errs() << "(processing input from stdin now, hit ctrl-c/ctrl-d to "
665  "interrupt)\n";
666 
667  // Set up the input file.
668  std::string errorMessage;
669  auto file = openInputFile(inputFilename, &errorMessage);
670  if (!file) {
671  llvm::errs() << errorMessage << "\n";
672  return failure();
673  }
674 
675  auto output = openOutputFile(outputFilename, &errorMessage);
676  if (!output) {
677  llvm::errs() << errorMessage << "\n";
678  return failure();
679  }
680  if (failed(MlirOptMain(output->os(), std::move(file), registry, config)))
681  return failure();
682 
683  // Keep the output file if the invocation of MlirOptMain was successful.
684  output->keep();
685  return success();
686 }
687 
688 LogicalResult mlir::MlirOptMain(int argc, char **argv, llvm::StringRef toolName,
689  DialectRegistry &registry) {
690 
691  // Register and parse command line options.
692  std::string inputFilename, outputFilename;
693  std::tie(inputFilename, outputFilename) =
694  registerAndParseCLIOptions(argc, argv, toolName, registry);
695 
696  return MlirOptMain(argc, argv, inputFilename, outputFilename, registry);
697 }
static LogicalResult printRegisteredDialects(DialectRegistry &registry)
LogicalResult loadIRDLDialects(StringRef irdlFile, MLIRContext &ctx)
static LogicalResult doVerifyRoundTrip(Operation *op, const MlirOptMainConfig &config, bool useBytecode)
static LogicalResult printRegisteredPassesAndReturn()
ManagedStatic< MlirOptMainConfigCLOptions > clOptionsConfig
static LogicalResult processBuffer(raw_ostream &os, std::unique_ptr< MemoryBuffer > ownedBuffer, const MlirOptMainConfig &config, DialectRegistry &registry, llvm::ThreadPoolInterface *threadPool)
Parses the memory buffer.
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:60
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:152
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:48
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.
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
void print(raw_ostream &os, const OpPrintingFlags &flags=std::nullopt)
MLIRContext * getContext()
Return the context this operation is associated with.
Definition: Operation.h:216
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Definition: Operation.cpp:671
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:230
MLIRContext * getContext() const
Return an instance of the context.
Definition: PassManager.h:255
LogicalResult run(Operation *op)
Run the passes within this manager on the provided operation.
Definition: Pass.cpp:847
void enableVerifier(bool enabled=true)
Runs the verifier after each individual pass.
Definition: Pass.cpp:844
void enableTiming(TimingScope &timingScope)
Add an instrumentation to time the execution of passes and the computation of analyses.
Definition: PassTiming.cpp:148
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
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:74
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:20
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:614
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:90
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:212
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:619
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
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.