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