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