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