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