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