MLIR 23.0.0git
PassRegistry.cpp
Go to the documentation of this file.
1//===- PassRegistry.cpp - Pass Registration Utilities ---------------------===//
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
10
11#include "mlir/Pass/Pass.h"
13#include "llvm/ADT/ScopeExit.h"
14#include "llvm/ADT/StringRef.h"
15#include "llvm/Support/Format.h"
16#include "llvm/Support/ManagedStatic.h"
17#include "llvm/Support/MemoryBuffer.h"
18#include "llvm/Support/SourceMgr.h"
19
20#include <optional>
21#include <utility>
22
23using namespace mlir;
24using namespace detail;
25
26/// Static mapping of all of the registered passes.
27static llvm::ManagedStatic<llvm::StringMap<PassInfo>> passRegistry;
28
29/// A mapping of the above pass registry entries to the corresponding TypeID
30/// of the pass that they generate.
31static llvm::ManagedStatic<llvm::StringMap<TypeID>> passRegistryTypeIDs;
32
33/// Static mapping of all of the registered pass pipelines.
34static llvm::ManagedStatic<llvm::StringMap<PassPipelineInfo>>
36
37/// Utility to create a default registry function from a pass instance.
40 return [=](OpPassManager &pm, StringRef options,
41 function_ref<LogicalResult(const Twine &)> errorHandler) {
42 std::unique_ptr<Pass> pass = allocator();
43 LogicalResult result = pass->initializeOptions(options, errorHandler);
44
45 std::optional<StringRef> pmOpName = pm.getOpName();
46 std::optional<StringRef> passOpName = pass->getOpName();
47 if ((pm.getNesting() == OpPassManager::Nesting::Explicit) && pmOpName &&
48 passOpName && *pmOpName != *passOpName) {
49 return errorHandler(llvm::Twine("Can't add pass '") + pass->getName() +
50 "' restricted to '" + *pass->getOpName() +
51 "' on a PassManager intended to run on '" +
52 pm.getOpAnchorName() + "', did you intend to nest?");
53 }
54 pm.addPass(std::move(pass));
55 return result;
56 };
57}
58
59/// Utility to print the help string for a specific option.
60static void printOptionHelp(StringRef arg, StringRef desc, size_t indent,
61 size_t descIndent, bool isTopLevel) {
62 size_t numSpaces = descIndent - indent - 4;
63 llvm::outs().indent(indent)
64 << "--" << llvm::left_justify(arg, numSpaces) << "- " << desc << '\n';
65}
66
67//===----------------------------------------------------------------------===//
68// PassRegistry
69//===----------------------------------------------------------------------===//
70
71/// Prints the passes that were previously registered and stored in passRegistry
73 size_t maxWidth = 0;
74 for (auto &entry : *passRegistry)
75 maxWidth = std::max(maxWidth, entry.second.getOptionWidth() + 4);
76
77 // Functor used to print the ordered entries of a registration map.
78 auto printOrderedEntries = [&](StringRef header, auto &map) {
80 for (auto &kv : map)
81 orderedEntries.push_back(&kv.second);
82 llvm::array_pod_sort(
83 orderedEntries.begin(), orderedEntries.end(),
84 [](PassRegistryEntry *const *lhs, PassRegistryEntry *const *rhs) {
85 return (*lhs)->getPassArgument().compare((*rhs)->getPassArgument());
86 });
87
88 llvm::outs().indent(0) << header << ":\n";
89 for (PassRegistryEntry *entry : orderedEntries)
90 entry->printHelpStr(/*indent=*/2, maxWidth);
91 };
92
93 // Print the available passes.
94 printOrderedEntries("Passes", *passRegistry);
95}
96
97/// Print the help information for this pass. This includes the argument,
98/// description, and any pass options. `descIndent` is the indent that the
99/// descriptions should be aligned.
100void PassRegistryEntry::printHelpStr(size_t indent, size_t descIndent) const {
101 printOptionHelp(getPassArgument(), getPassDescription(), indent, descIndent,
102 /*isTopLevel=*/true);
103 // If this entry has options, print the help for those as well.
104 optHandler([=](const PassOptions &options) {
105 options.printHelp(indent, descIndent);
106 });
107}
108
109/// Return the maximum width required when printing the options of this
110/// entry.
112 size_t maxLen = 0;
113 optHandler([&](const PassOptions &options) mutable {
114 maxLen = options.getOptionWidth() + 2;
115 });
116 return maxLen;
117}
118
119//===----------------------------------------------------------------------===//
120// PassPipelineInfo
121//===----------------------------------------------------------------------===//
122
124 StringRef arg, StringRef description, const PassRegistryFunction &function,
125 std::function<void(function_ref<void(const PassOptions &)>)> optHandler) {
126 PassPipelineInfo pipelineInfo(arg, description, function,
127 std::move(optHandler));
128 bool inserted = passPipelineRegistry->try_emplace(arg, pipelineInfo).second;
129#ifndef NDEBUG
130 if (!inserted)
131 report_fatal_error("Pass pipeline " + arg + " registered multiple times");
132#endif
133 (void)inserted;
134}
135
136//===----------------------------------------------------------------------===//
137// PassInfo
138//===----------------------------------------------------------------------===//
139
140PassInfo::PassInfo(StringRef arg, StringRef description,
141 const PassAllocatorFunction &allocator)
143 arg, description, buildDefaultRegistryFn(allocator),
144 // Use a temporary pass to provide an options instance.
145 [=](function_ref<void(const PassOptions &)> optHandler) {
146 optHandler(allocator()->passOptions);
147 }) {}
148
150 std::unique_ptr<Pass> pass = function();
151 StringRef arg = pass->getArgument();
152 if (arg.empty())
153 llvm::report_fatal_error(llvm::Twine("Trying to register '") +
154 pass->getName() +
155 "' pass that does not override `getArgument()`");
156 StringRef description = pass->getDescription();
157 PassInfo passInfo(arg, description, function);
158 passRegistry->try_emplace(arg, passInfo);
159
160 // Verify that the registered pass has the same ID as any registered to this
161 // arg before it.
162 TypeID entryTypeID = pass->getTypeID();
163 auto it = passRegistryTypeIDs->try_emplace(arg, entryTypeID).first;
164 if (it->second != entryTypeID)
165 llvm::report_fatal_error(
166 "pass allocator creates a different pass than previously "
167 "registered for pass " +
168 arg);
169}
170
171/// Returns the pass info for the specified pass argument or null if unknown.
172const PassInfo *mlir::PassInfo::lookup(StringRef passArg) {
173 auto it = passRegistry->find(passArg);
174 return it == passRegistry->end() ? nullptr : &it->second;
175}
176
177/// Returns the pass pipeline info for the specified pass pipeline argument or
178/// null if unknown.
180 auto it = passPipelineRegistry->find(pipelineArg);
181 return it == passPipelineRegistry->end() ? nullptr : &it->second;
182}
183
184//===----------------------------------------------------------------------===//
185// PassOptions
186//===----------------------------------------------------------------------===//
187
188/// Attempt to find the next occurance of character 'c' in the string starting
189/// from the `index`-th position , omitting any occurances that appear within
190/// intervening ranges or literals.
191static size_t findChar(StringRef str, size_t index, char c) {
192 for (size_t i = index, e = str.size(); i < e; ++i) {
193 if (str[i] == c)
194 return i;
195 // Check for various range characters.
196 if (str[i] == '{')
197 i = findChar(str, i + 1, '}');
198 else if (str[i] == '(')
199 i = findChar(str, i + 1, ')');
200 else if (str[i] == '[')
201 i = findChar(str, i + 1, ']');
202 else if (str[i] == '\"')
203 i = str.find_first_of('\"', i + 1);
204 else if (str[i] == '\'')
205 i = str.find_first_of('\'', i + 1);
206 if (i == StringRef::npos)
207 return StringRef::npos;
208 }
209 return StringRef::npos;
210}
211
212/// Extract an argument from 'options' and update it to point after the arg.
213/// Returns the cleaned argument string.
214static StringRef extractArgAndUpdateOptions(StringRef &options,
215 size_t argSize) {
216 StringRef str = options.take_front(argSize).trim();
217 options = options.drop_front(argSize).ltrim();
218
219 // Early exit if there's no escape sequence.
220 if (str.size() <= 1)
221 return str;
222
223 const auto escapePairs = {std::make_pair('\'', '\''),
224 std::make_pair('"', '"')};
225 for (const auto &escape : escapePairs) {
226 if (str.front() == escape.first && str.back() == escape.second) {
227 // Drop the escape characters and trim.
228 // Don't process additional escape sequences.
229 return str.drop_front().drop_back().trim();
230 }
231 }
232
233 // Arguments may be wrapped in `{...}`. Unlike the quotation markers that
234 // denote literals, we respect scoping here. The outer `{...}` should not
235 // be stripped in cases such as "arg={...},{...}", which can be used to denote
236 // lists of nested option structs.
237 if (str.front() == '{') {
238 unsigned match = findChar(str, 1, '}');
239 if (match == str.size() - 1)
240 str = str.drop_front().drop_back().trim();
241 }
242
243 return str;
244}
245
247 llvm::cl::Option &opt, StringRef argName, StringRef optionStr,
248 function_ref<LogicalResult(StringRef)> elementParseFn) {
249 if (optionStr.empty())
250 return success();
251
252 size_t nextElePos = findChar(optionStr, 0, ',');
253 while (nextElePos != StringRef::npos) {
254 // Process the portion before the comma.
255 if (failed(
256 elementParseFn(extractArgAndUpdateOptions(optionStr, nextElePos))))
257 return failure();
258
259 // Drop the leading ','
260 optionStr = optionStr.drop_front();
261 nextElePos = findChar(optionStr, 0, ',');
262 }
263 return elementParseFn(
264 extractArgAndUpdateOptions(optionStr, optionStr.size()));
265}
266
267/// Out of line virtual function to provide home for the class.
268void detail::PassOptions::OptionBase::anchor() {}
269
270/// Copy the option values from 'other'.
271void detail::PassOptions::copyOptionValuesFrom(const PassOptions &other) {
272 assert(options.size() == other.options.size());
273 if (options.empty())
274 return;
275 for (auto optionsIt : llvm::zip(options, other.options))
276 std::get<0>(optionsIt)->copyValueFrom(*std::get<1>(optionsIt));
277}
278
279/// Parse in the next argument from the given options string. Returns a tuple
280/// containing [the key of the option, the value of the option, updated
281/// `options` string pointing after the parsed option].
282static std::tuple<StringRef, StringRef, StringRef>
284 // Try to process the given punctuation, properly escaping any contained
285 // characters.
286 auto tryProcessPunct = [&](size_t &currentPos, char punct) {
287 if (options[currentPos] != punct)
288 return false;
289 size_t nextIt = options.find_first_of(punct, currentPos + 1);
290 if (nextIt != StringRef::npos)
291 currentPos = nextIt;
292 return true;
293 };
294
295 // Parse the argument name of the option.
296 StringRef argName;
297 for (size_t argEndIt = 0, optionsE = options.size();; ++argEndIt) {
298 // Check for the end of the full option.
299 if (argEndIt == optionsE || options[argEndIt] == ' ') {
300 argName = extractArgAndUpdateOptions(options, argEndIt);
301 return std::make_tuple(argName, StringRef(), options);
302 }
303
304 // Check for the end of the name and the start of the value.
305 if (options[argEndIt] == '=') {
306 argName = extractArgAndUpdateOptions(options, argEndIt);
307 options = options.drop_front();
308 break;
309 }
310 }
311
312 // Parse the value of the option.
313 for (size_t argEndIt = 0, optionsE = options.size();; ++argEndIt) {
314 // Handle the end of the options string.
315 if (argEndIt == optionsE || options[argEndIt] == ' ') {
316 StringRef value = extractArgAndUpdateOptions(options, argEndIt);
317 return std::make_tuple(argName, value, options);
318 }
319
320 // Skip over escaped sequences.
321 char c = options[argEndIt];
322 if (tryProcessPunct(argEndIt, '\'') || tryProcessPunct(argEndIt, '"'))
323 continue;
324 // '{...}' is used to specify options to passes, properly escape it so
325 // that we don't accidentally split any nested options.
326 if (c == '{') {
327 size_t braceCount = 1;
328 for (++argEndIt; argEndIt != optionsE; ++argEndIt) {
329 // Allow nested punctuation.
330 if (tryProcessPunct(argEndIt, '\'') || tryProcessPunct(argEndIt, '"'))
331 continue;
332 if (options[argEndIt] == '{')
333 ++braceCount;
334 else if (options[argEndIt] == '}' && --braceCount == 0)
335 break;
336 }
337 // Account for the increment at the top of the loop.
338 --argEndIt;
339 }
340 }
341 llvm_unreachable("unexpected control flow in pass option parsing");
342}
343
344LogicalResult detail::PassOptions::parseFromString(StringRef options,
345 raw_ostream &errorStream) {
346 // NOTE: `options` is modified in place to always refer to the unprocessed
347 // part of the string.
348 while (!options.empty()) {
349 StringRef key, value;
350 std::tie(key, value, options) = parseNextArg(options);
351 if (key.empty())
352 continue;
353
354 auto it = OptionsMap.find(key);
355 if (it == OptionsMap.end()) {
356 errorStream << "<Pass-Options-Parser>: no such option " << key << "\n";
357 return failure();
358 }
359 if (llvm::cl::ProvidePositionalOption(it->second, value, 0))
360 return failure();
361 }
362
363 return success();
364}
365
366/// Print the options held by this struct in a form that can be parsed via
367/// 'parseFromString'.
368void detail::PassOptions::print(raw_ostream &os) const {
369 // If there are no options, there is nothing left to do.
370 if (OptionsMap.empty())
371 return;
372
373 // Sort the options to make the ordering deterministic.
374 SmallVector<OptionBase *, 4> orderedOps(options.begin(), options.end());
375 auto compareOptionArgs = [](OptionBase *const *lhs, OptionBase *const *rhs) {
376 return (*lhs)->getArgStr().compare((*rhs)->getArgStr());
377 };
378 llvm::array_pod_sort(orderedOps.begin(), orderedOps.end(), compareOptionArgs);
379
380 // Interleave the options with ' '.
381 os << '{';
382 llvm::interleave(
383 orderedOps, os, [&](OptionBase *option) { option->print(os); }, " ");
384 os << '}';
385}
386
387/// Print the help string for the options held by this struct. `descIndent` is
388/// the indent within the stream that the descriptions should be aligned.
389void detail::PassOptions::printHelp(size_t indent, size_t descIndent) const {
390 // Sort the options to make the ordering deterministic.
391 SmallVector<OptionBase *, 4> orderedOps(options.begin(), options.end());
392 auto compareOptionArgs = [](OptionBase *const *lhs, OptionBase *const *rhs) {
393 return (*lhs)->getArgStr().compare((*rhs)->getArgStr());
394 };
395 llvm::array_pod_sort(orderedOps.begin(), orderedOps.end(), compareOptionArgs);
396 for (OptionBase *option : orderedOps) {
397 // TODO: printOptionInfo assumes a specific indent and will
398 // print options with values with incorrect indentation. We should add
399 // support to llvm::cl::Option for passing in a base indent to use when
400 // printing.
401 llvm::outs().indent(indent);
402 option->getOption()->printOptionInfo(descIndent - indent);
403 }
404}
405
406/// Return the maximum width required when printing the help string.
407size_t detail::PassOptions::getOptionWidth() const {
408 size_t max = 0;
409 for (auto *option : options)
410 max = std::max(max, option->getOption()->getOptionWidth());
411 return max;
412}
413
414//===----------------------------------------------------------------------===//
415// MLIR Options
416//===----------------------------------------------------------------------===//
417
418//===----------------------------------------------------------------------===//
419// OpPassManager: OptionValue
420//===----------------------------------------------------------------------===//
421
422namespace llvm::cl {
423
424OptionValue<OpPassManager>::OptionValue() = default;
425OptionValue<OpPassManager>::OptionValue(const mlir::OpPassManager &value) {
426 setValue(value);
427}
428OptionValue<OpPassManager>::OptionValue(
429 const OptionValue<mlir::OpPassManager> &rhs) {
430 if (rhs.hasValue())
431 setValue(rhs.getValue());
432}
433OptionValue<OpPassManager> &
434OptionValue<OpPassManager>::operator=(const mlir::OpPassManager &rhs) {
435 setValue(rhs);
436 return *this;
437}
438
439OptionValue<OpPassManager>::~OptionValue() = default;
440
441void OptionValue<OpPassManager>::setValue(const OpPassManager &newValue) {
442 if (hasValue())
443 *value = newValue;
444 else
445 value = std::make_unique<mlir::OpPassManager>(newValue);
446}
447void OptionValue<OpPassManager>::setValue(StringRef pipelineStr) {
448 FailureOr<OpPassManager> pipeline = parsePassPipeline(pipelineStr);
449 assert(succeeded(pipeline) && "invalid pass pipeline");
450 setValue(*pipeline);
451}
452
453bool OptionValue<OpPassManager>::compare(const mlir::OpPassManager &rhs) const {
454 std::string lhsStr, rhsStr;
455 {
456 raw_string_ostream lhsStream(lhsStr);
457 value->printAsTextualPipeline(lhsStream);
458
459 raw_string_ostream rhsStream(rhsStr);
460 rhs.printAsTextualPipeline(rhsStream);
461 }
462
463 // Use the textual format for pipeline comparisons.
464 return lhsStr == rhsStr;
465}
466
467void OptionValue<OpPassManager>::anchor() {}
468
469} // namespace llvm::cl
470
471//===----------------------------------------------------------------------===//
472// OpPassManager: Parser
473//===----------------------------------------------------------------------===//
474
475namespace llvm::cl {
476template class basic_parser<OpPassManager>;
477} // namespace llvm::cl
478
479bool llvm::cl::parser<OpPassManager>::parse(Option &, StringRef, StringRef arg,
480 ParsedPassManager &value) {
481 FailureOr<OpPassManager> pipeline = parsePassPipeline(arg);
482 if (failed(pipeline))
483 return true;
484 value.value = std::make_unique<OpPassManager>(std::move(*pipeline));
485 return false;
486}
487
488void llvm::cl::parser<OpPassManager>::print(raw_ostream &os,
489 const OpPassManager &value) {
490 value.printAsTextualPipeline(os);
491}
492
493void llvm::cl::parser<OpPassManager>::printOptionDiff(
494 const Option &opt, OpPassManager &pm, const OptVal &defaultValue,
495 size_t globalWidth) const {
496 printOptionName(opt, globalWidth);
497 outs() << "= ";
498 pm.printAsTextualPipeline(outs());
499
500 if (defaultValue.hasValue()) {
501 outs().indent(2) << " (default: ";
502 defaultValue.getValue().printAsTextualPipeline(outs());
503 outs() << ")";
504 }
505 outs() << "\n";
506}
507
508void llvm::cl::parser<OpPassManager>::anchor() {}
509
510llvm::cl::parser<OpPassManager>::ParsedPassManager::ParsedPassManager() =
511 default;
512llvm::cl::parser<OpPassManager>::ParsedPassManager::ParsedPassManager(
513 ParsedPassManager &&) = default;
514llvm::cl::parser<OpPassManager>::ParsedPassManager::~ParsedPassManager() =
515 default;
516
517//===----------------------------------------------------------------------===//
518// TextualPassPipeline Parser
519//===----------------------------------------------------------------------===//
520
521namespace {
522/// This class represents a textual description of a pass pipeline.
523class TextualPipeline {
524public:
525 /// Try to initialize this pipeline with the given pipeline text.
526 /// `errorStream` is the output stream to emit errors to.
527 LogicalResult initialize(StringRef text, raw_ostream &errorStream);
528
529 /// Add the internal pipeline elements to the provided pass manager.
530 LogicalResult
531 addToPipeline(OpPassManager &pm,
532 function_ref<LogicalResult(const Twine &)> errorHandler) const;
533
534private:
535 /// A functor used to emit errors found during pipeline handling. The first
536 /// parameter corresponds to the raw location within the pipeline string. This
537 /// should always return failure.
538 using ErrorHandlerT = function_ref<LogicalResult(const char *, Twine)>;
539
540 /// A struct to capture parsed pass pipeline names.
541 ///
542 /// A pipeline is defined as a series of names, each of which may in itself
543 /// recursively contain a nested pipeline. A name is either the name of a pass
544 /// (e.g. "cse") or the name of an operation type (e.g. "buitin.module"). If
545 /// the name is the name of a pass, the InnerPipeline is empty, since passes
546 /// cannot contain inner pipelines.
547 struct PipelineElement {
548 PipelineElement(StringRef name) : name(name) {}
549
550 StringRef name;
551 StringRef options;
552 const PassRegistryEntry *registryEntry = nullptr;
553 std::vector<PipelineElement> innerPipeline;
554 };
555
556 /// Parse the given pipeline text into the internal pipeline vector. This
557 /// function only parses the structure of the pipeline, and does not resolve
558 /// its elements.
559 LogicalResult parsePipelineText(StringRef text, ErrorHandlerT errorHandler);
560
561 /// Resolve the elements of the pipeline, i.e. connect passes and pipelines to
562 /// the corresponding registry entry.
563 LogicalResult
564 resolvePipelineElements(MutableArrayRef<PipelineElement> elements,
565 ErrorHandlerT errorHandler);
566
567 /// Resolve a single element of the pipeline.
568 LogicalResult resolvePipelineElement(PipelineElement &element,
569 ErrorHandlerT errorHandler);
570
571 /// Add the given pipeline elements to the provided pass manager.
572 LogicalResult
573 addToPipeline(ArrayRef<PipelineElement> elements, OpPassManager &pm,
574 function_ref<LogicalResult(const Twine &)> errorHandler) const;
575
576 std::vector<PipelineElement> pipeline;
577};
578
579} // namespace
580
581/// Try to initialize this pipeline with the given pipeline text. An option is
582/// given to enable accurate error reporting.
583LogicalResult TextualPipeline::initialize(StringRef text,
584 raw_ostream &errorStream) {
585 if (text.empty())
586 return success();
587
588 // Build a source manager to use for error reporting.
589 llvm::SourceMgr pipelineMgr;
590 pipelineMgr.AddNewSourceBuffer(
591 llvm::MemoryBuffer::getMemBuffer(text, "MLIR Textual PassPipeline Parser",
592 /*RequiresNullTerminator=*/false),
593 SMLoc());
594 auto errorHandler = [&](const char *rawLoc, Twine msg) {
595 pipelineMgr.PrintMessage(errorStream, SMLoc::getFromPointer(rawLoc),
596 llvm::SourceMgr::DK_Error, msg);
597 return failure();
598 };
599
600 // Parse the provided pipeline string.
601 if (failed(parsePipelineText(text, errorHandler)))
602 return failure();
603 return resolvePipelineElements(pipeline, errorHandler);
604}
605
606/// Add the internal pipeline elements to the provided pass manager.
607LogicalResult TextualPipeline::addToPipeline(
608 OpPassManager &pm,
609 function_ref<LogicalResult(const Twine &)> errorHandler) const {
610 // Temporarily disable implicit nesting while we append to the pipeline. We
611 // want the created pipeline to exactly match the parsed text pipeline, so
612 // it's preferrable to just error out if implicit nesting would be required.
613 OpPassManager::Nesting nesting = pm.getNesting();
615 llvm::scope_exit restore([&]() { pm.setNesting(nesting); });
616
617 return addToPipeline(pipeline, pm, errorHandler);
618}
619
620/// Parse the given pipeline text into the internal pipeline vector. This
621/// function only parses the structure of the pipeline, and does not resolve
622/// its elements.
623LogicalResult TextualPipeline::parsePipelineText(StringRef text,
624 ErrorHandlerT errorHandler) {
625 SmallVector<std::vector<PipelineElement> *, 4> pipelineStack = {&pipeline};
626 for (;;) {
627 std::vector<PipelineElement> &pipeline = *pipelineStack.back();
628 size_t pos = text.find_first_of(",(){");
629 pipeline.emplace_back(/*name=*/text.substr(0, pos).trim());
630
631 // If we have a single terminating name, we're done.
632 if (pos == StringRef::npos)
633 break;
634
635 text = text.substr(pos);
636 char sep = text[0];
637
638 // Handle pulling ... from 'pass{...}' out as PipelineElement.options.
639 if (sep == '{') {
640 text = text.substr(1);
641
642 // Skip over everything until the closing '}' and store as options.
643 size_t close = StringRef::npos;
644 for (unsigned i = 0, e = text.size(), braceCount = 1; i < e; ++i) {
645 if (text[i] == '{') {
646 ++braceCount;
647 continue;
648 }
649 if (text[i] == '}' && --braceCount == 0) {
650 close = i;
651 break;
652 }
653 }
654
655 // Check to see if a closing options brace was found.
656 if (close == StringRef::npos) {
657 return errorHandler(
658 /*rawLoc=*/text.data() - 1,
659 "missing closing '}' while processing pass options");
660 }
661 pipeline.back().options = text.substr(0, close);
662 text = text.substr(close + 1);
663
664 // Consume space characters that an user might add for readability.
665 text = text.ltrim();
666
667 // Skip checking for '(' because nested pipelines cannot have options.
668 } else if (sep == '(') {
669 text = text.substr(1);
670
671 // Push the inner pipeline onto the stack to continue processing.
672 pipelineStack.push_back(&pipeline.back().innerPipeline);
673 continue;
674 }
675
676 // When handling the close parenthesis, we greedily consume them to avoid
677 // empty strings in the pipeline.
678 while (text.consume_front(")")) {
679 // If we try to pop the outer pipeline we have unbalanced parentheses.
680 if (pipelineStack.size() == 1)
681 return errorHandler(/*rawLoc=*/text.data() - 1,
682 "encountered extra closing ')' creating unbalanced "
683 "parentheses while parsing pipeline");
684
685 pipelineStack.pop_back();
686 // Consume space characters that an user might add for readability.
687 text = text.ltrim();
688 }
689
690 // Check if we've finished parsing.
691 if (text.empty())
692 break;
693
694 // Otherwise, the end of an inner pipeline always has to be followed by
695 // a comma, and then we can continue.
696 if (!text.consume_front(","))
697 return errorHandler(text.data(), "expected ',' after parsing pipeline");
698 }
699
700 // Check for unbalanced parentheses.
701 if (pipelineStack.size() > 1)
702 return errorHandler(
703 text.data(),
704 "encountered unbalanced parentheses while parsing pipeline");
705
706 assert(pipelineStack.back() == &pipeline &&
707 "wrong pipeline at the bottom of the stack");
708 return success();
709}
710
711/// Resolve the elements of the pipeline, i.e. connect passes and pipelines to
712/// the corresponding registry entry.
713LogicalResult TextualPipeline::resolvePipelineElements(
714 MutableArrayRef<PipelineElement> elements, ErrorHandlerT errorHandler) {
715 for (auto &elt : elements)
716 if (failed(resolvePipelineElement(elt, errorHandler)))
717 return failure();
718 return success();
719}
720
721/// Resolve a single element of the pipeline.
722LogicalResult
723TextualPipeline::resolvePipelineElement(PipelineElement &element,
724 ErrorHandlerT errorHandler) {
725 // If the inner pipeline of this element is not empty, this is an operation
726 // pipeline.
727 if (!element.innerPipeline.empty())
728 return resolvePipelineElements(element.innerPipeline, errorHandler);
729
730 // Otherwise, this must be a pass or pass pipeline.
731 // Check to see if a pipeline was registered with this name.
732 if ((element.registryEntry = PassPipelineInfo::lookup(element.name)))
733 return success();
734
735 // If not, then this must be a specific pass name.
736 if ((element.registryEntry = PassInfo::lookup(element.name)))
737 return success();
738
739 // Emit an error for the unknown pass.
740 auto *rawLoc = element.name.data();
741 return errorHandler(rawLoc, "'" + element.name +
742 "' does not refer to a "
743 "registered pass or pass pipeline");
744}
745
746/// Add the given pipeline elements to the provided pass manager.
747LogicalResult TextualPipeline::addToPipeline(
748 ArrayRef<PipelineElement> elements, OpPassManager &pm,
749 function_ref<LogicalResult(const Twine &)> errorHandler) const {
750 for (auto &elt : elements) {
751 if (elt.registryEntry) {
752 if (failed(elt.registryEntry->addToPipeline(pm, elt.options,
753 errorHandler))) {
754 return errorHandler("failed to add `" + elt.name + "` with options `" +
755 elt.options + "`");
756 }
757 } else if (failed(addToPipeline(elt.innerPipeline, pm.nest(elt.name),
758 errorHandler))) {
759 return errorHandler("failed to add `" + elt.name + "` with options `" +
760 elt.options + "` to inner pipeline");
761 }
762 }
763 return success();
764}
765
766LogicalResult mlir::parsePassPipeline(StringRef pipeline, OpPassManager &pm,
767 raw_ostream &errorStream) {
768 TextualPipeline pipelineParser;
769 if (failed(pipelineParser.initialize(pipeline, errorStream)))
770 return failure();
771 auto errorHandler = [&](Twine msg) {
772 errorStream << msg << "\n";
773 return failure();
774 };
775 if (failed(pipelineParser.addToPipeline(pm, errorHandler)))
776 return failure();
777 return success();
778}
779
780FailureOr<OpPassManager> mlir::parsePassPipeline(StringRef pipeline,
781 raw_ostream &errorStream) {
782 pipeline = pipeline.trim();
783 // Pipelines are expected to be of the form `<op-name>(<pipeline>)`.
784 size_t pipelineStart = pipeline.find_first_of('(');
785 if (pipelineStart == 0 || pipelineStart == StringRef::npos ||
786 !pipeline.consume_back(")")) {
787 errorStream << "expected pass pipeline to be wrapped with the anchor "
788 "operation type, e.g. 'builtin.module(...)'";
789 return failure();
790 }
791
792 StringRef opName = pipeline.take_front(pipelineStart).rtrim();
793 OpPassManager pm(opName);
794 if (failed(parsePassPipeline(pipeline.drop_front(1 + pipelineStart), pm,
795 errorStream)))
796 return failure();
797 return pm;
798}
799
800//===----------------------------------------------------------------------===//
801// PassNameParser
802//===----------------------------------------------------------------------===//
803
804namespace {
805/// This struct represents the possible data entries in a parsed pass pipeline
806/// list.
807struct PassArgData {
808 PassArgData() = default;
809 PassArgData(const PassRegistryEntry *registryEntry)
810 : registryEntry(registryEntry) {}
811
812 /// This field is used when the parsed option corresponds to a registered pass
813 /// or pass pipeline.
814 const PassRegistryEntry *registryEntry{nullptr};
815
816 /// This field is set when instance specific pass options have been provided
817 /// on the command line.
818 StringRef options;
819};
820} // namespace
821
822namespace llvm {
823namespace cl {
824/// Define a valid OptionValue for the command line pass argument.
825template <>
827 : OptionValueBase<PassArgData, /*isClass=*/true> {
828 OptionValue(const PassArgData &value) { this->setValue(value); }
829 OptionValue() = default;
830 void anchor() override {}
831
832 bool hasValue() const { return true; }
833 const PassArgData &getValue() const { return value; }
834 void setValue(const PassArgData &value) { this->value = value; }
835
836 PassArgData value;
837};
838} // namespace cl
839} // namespace llvm
840
841namespace {
842
843/// The name for the command line option used for parsing the textual pass
844/// pipeline.
845#define PASS_PIPELINE_ARG "pass-pipeline"
846
847/// Adds command line option for each registered pass or pass pipeline, as well
848/// as textual pass pipelines.
849struct PassNameParser : public llvm::cl::parser<PassArgData> {
850 PassNameParser(llvm::cl::Option &opt) : llvm::cl::parser<PassArgData>(opt) {}
851
852 void initialize();
853 void printOptionInfo(const llvm::cl::Option &opt,
854 size_t globalWidth) const override;
855 size_t getOptionWidth(const llvm::cl::Option &opt) const override;
856 bool parse(llvm::cl::Option &opt, StringRef argName, StringRef arg,
857 PassArgData &value);
858
859 /// If true, this parser only parses entries that correspond to a concrete
860 /// pass registry entry, and does not include pipeline entries or the options
861 /// for pass entries.
862 bool passNamesOnly = false;
863};
864} // namespace
865
866void PassNameParser::initialize() {
867 llvm::cl::parser<PassArgData>::initialize();
868
869 /// Add the pass entries.
870 for (const auto &kv : *passRegistry) {
871 addLiteralOption(kv.second.getPassArgument(), &kv.second,
872 kv.second.getPassDescription());
873 }
874 /// Add the pass pipeline entries.
875 if (!passNamesOnly) {
876 for (const auto &kv : *passPipelineRegistry) {
877 addLiteralOption(kv.second.getPassArgument(), &kv.second,
878 kv.second.getPassDescription());
879 }
880 }
881}
882
883void PassNameParser::printOptionInfo(const llvm::cl::Option &opt,
884 size_t globalWidth) const {
885 // If this parser is just parsing pass names, print a simplified option
886 // string.
887 if (passNamesOnly) {
888 llvm::outs() << " --" << opt.ArgStr << "=<pass-arg>";
889 opt.printHelpStr(opt.HelpStr, globalWidth, opt.ArgStr.size() + 18);
890 return;
891 }
892
893 // Print the information for the top-level option.
894 if (opt.hasArgStr()) {
895 llvm::outs() << " --" << opt.ArgStr;
896 opt.printHelpStr(opt.HelpStr, globalWidth, opt.ArgStr.size() + 7);
897 } else {
898 llvm::outs() << " " << opt.HelpStr << '\n';
899 }
900
901 // Functor used to print the ordered entries of a registration map.
902 auto printOrderedEntries = [&](StringRef header, auto &map) {
903 llvm::SmallVector<PassRegistryEntry *, 32> orderedEntries;
904 for (auto &kv : map)
905 orderedEntries.push_back(&kv.second);
906 llvm::array_pod_sort(
907 orderedEntries.begin(), orderedEntries.end(),
908 [](PassRegistryEntry *const *lhs, PassRegistryEntry *const *rhs) {
909 return (*lhs)->getPassArgument().compare((*rhs)->getPassArgument());
910 });
911
912 llvm::outs().indent(4) << header << ":\n";
913 for (PassRegistryEntry *entry : orderedEntries)
914 entry->printHelpStr(/*indent=*/6, globalWidth);
915 };
916
917 // Print the available passes.
918 printOrderedEntries("Passes", *passRegistry);
919
920 // Print the available pass pipelines.
921 if (!passPipelineRegistry->empty())
922 printOrderedEntries("Pass Pipelines", *passPipelineRegistry);
923}
924
925size_t PassNameParser::getOptionWidth(const llvm::cl::Option &opt) const {
926 size_t maxWidth = llvm::cl::parser<PassArgData>::getOptionWidth(opt) + 2;
927
928 // Check for any wider pass or pipeline options.
929 for (auto &entry : *passRegistry)
930 maxWidth = std::max(maxWidth, entry.second.getOptionWidth() + 4);
931 for (auto &entry : *passPipelineRegistry)
932 maxWidth = std::max(maxWidth, entry.second.getOptionWidth() + 4);
933 return maxWidth;
934}
935
936bool PassNameParser::parse(llvm::cl::Option &opt, StringRef argName,
937 StringRef arg, PassArgData &value) {
938 if (llvm::cl::parser<PassArgData>::parse(opt, argName, arg, value))
939 return true;
940 value.options = arg;
941 return false;
942}
943
944//===----------------------------------------------------------------------===//
945// PassPipelineCLParser
946//===----------------------------------------------------------------------===//
947
948namespace mlir {
949namespace detail {
951 PassPipelineCLParserImpl(StringRef arg, StringRef description,
952 bool passNamesOnly)
953 : passList(arg, llvm::cl::desc(description)) {
954 passList.getParser().passNamesOnly = passNamesOnly;
955 passList.setValueExpectedFlag(llvm::cl::ValueExpected::ValueOptional);
956 }
957
958 /// Returns true if the given pass registry entry was registered at the
959 /// top-level of the parser, i.e. not within an explicit textual pipeline.
960 bool contains(const PassRegistryEntry *entry) const {
961 return llvm::any_of(passList, [&](const PassArgData &data) {
962 return data.registryEntry == entry;
963 });
964 }
965
966 /// The set of passes and pass pipelines to run.
967 llvm::cl::list<PassArgData, bool, PassNameParser> passList;
968};
969} // namespace detail
970} // namespace mlir
971
972/// Construct a pass pipeline parser with the given command line description.
973PassPipelineCLParser::PassPipelineCLParser(StringRef arg, StringRef description)
974 : impl(std::make_unique<detail::PassPipelineCLParserImpl>(
975 arg, description, /*passNamesOnly=*/false)),
976 passPipeline(
978 llvm::cl::desc("Textual description of the pass pipeline to run")) {}
979
980PassPipelineCLParser::PassPipelineCLParser(StringRef arg, StringRef description,
981 StringRef alias)
982 : PassPipelineCLParser(arg, description) {
983 passPipelineAlias.emplace(alias,
984 llvm::cl::desc("Alias for --" PASS_PIPELINE_ARG),
985 llvm::cl::aliasopt(passPipeline));
986}
987
989
990/// Returns true if this parser contains any valid options to add.
992 return passPipeline.getNumOccurrences() != 0 ||
993 impl->passList.getNumOccurrences() != 0;
994}
995
996/// Returns true if the given pass registry entry was registered at the
997/// top-level of the parser, i.e. not within an explicit textual pipeline.
999 return impl->contains(entry);
1000}
1001
1002/// Adds the passes defined by this parser entry to the given pass manager.
1004 OpPassManager &pm,
1005 function_ref<LogicalResult(const Twine &)> errorHandler) const {
1006 if (passPipeline.getNumOccurrences()) {
1007 if (impl->passList.getNumOccurrences())
1008 return errorHandler(
1010 "' option can't be used with individual pass options");
1011 std::string errMsg;
1012 llvm::raw_string_ostream os(errMsg);
1013 FailureOr<OpPassManager> parsed = parsePassPipeline(passPipeline, os);
1014 if (failed(parsed))
1015 return errorHandler(errMsg);
1016 pm = std::move(*parsed);
1017 return success();
1018 }
1019
1020 for (auto &passIt : impl->passList) {
1021 if (failed(passIt.registryEntry->addToPipeline(pm, passIt.options,
1022 errorHandler)))
1023 return errorHandler("failed to add `" +
1024 passIt.registryEntry->getPassArgument() +
1025 "` with options `" + passIt.options + "`");
1026 }
1027 return success();
1028}
1029
1030//===----------------------------------------------------------------------===//
1031// PassNameCLParser
1032//===----------------------------------------------------------------------===//
1033
1034/// Construct a pass pipeline parser with the given command line description.
1035PassNameCLParser::PassNameCLParser(StringRef arg, StringRef description)
1036 : impl(std::make_unique<detail::PassPipelineCLParserImpl>(
1037 arg, description, /*passNamesOnly=*/true)) {
1038 impl->passList.setMiscFlag(llvm::cl::CommaSeparated);
1039}
1041
1042/// Returns true if this parser contains any valid options to add.
1044 return impl->passList.getNumOccurrences() != 0;
1045}
1046
1047/// Returns true if the given pass registry entry was registered at the
1048/// top-level of the parser, i.e. not within an explicit textual pipeline.
1050 return impl->contains(entry);
1051}
return success()
true
Given two iterators into the same block, return "true" if a is before `b.
LogicalResult initialize(unsigned origNumLoops, ArrayRef< ReassociationIndices > foldedIterationDims)
lhs
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
false
Parses a map_entries map type from a string format back into its numeric value.
static llvm::ManagedStatic< PassManagerOptions > options
static llvm::ManagedStatic< llvm::StringMap< PassPipelineInfo > > passPipelineRegistry
Static mapping of all of the registered pass pipelines.
#define PASS_PIPELINE_ARG
The name for the command line option used for parsing the textual pass pipeline.
static llvm::ManagedStatic< llvm::StringMap< PassInfo > > passRegistry
Static mapping of all of the registered passes.
static PassRegistryFunction buildDefaultRegistryFn(const PassAllocatorFunction &allocator)
Utility to create a default registry function from a pass instance.
static void printOptionHelp(StringRef arg, StringRef desc, size_t indent, size_t descIndent, bool isTopLevel)
Utility to print the help string for a specific option.
static llvm::ManagedStatic< llvm::StringMap< TypeID > > passRegistryTypeIDs
A mapping of the above pass registry entries to the corresponding TypeID of the pass that they genera...
static std::tuple< StringRef, StringRef, StringRef > parseNextArg(StringRef options)
Parse in the next argument from the given options string.
static size_t findChar(StringRef str, size_t index, char c)
Attempt to find the next occurance of character 'c' in the string starting from the index-th position...
static StringRef extractArgAndUpdateOptions(StringRef &options, size_t argSize)
Extract an argument from 'options' and update it to point after the arg.
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
This class represents a pass manager that runs passes on either a specific operation type,...
Definition PassManager.h:46
void printAsTextualPipeline(raw_ostream &os, bool pretty=false) const
Prints out the passes of the pass manager as the textual representation of pipelines.
Definition Pass.cpp:452
std::optional< OperationName > getOpName(MLIRContext &context) const
Return the operation name that this pass manager operates on, or std::nullopt if this is an op-agnost...
Definition Pass.cpp:411
void setNesting(Nesting nesting)
Enable or disable the implicit nesting on this particular PassManager.
Definition Pass.cpp:478
void addPass(std::unique_ptr< Pass > pass)
Add the given pass to this pass manager.
Definition Pass.cpp:392
Nesting getNesting()
Return the current nesting mode.
Definition Pass.cpp:480
Nesting
This enum represents the nesting behavior of the pass manager.
Definition PassManager.h:49
@ Explicit
Explicit nesting behavior.
Definition PassManager.h:56
StringRef getOpAnchorName() const
Return the name used to anchor this pass manager.
Definition Pass.cpp:415
OpPassManager & nest(OperationName nestedName)
Nest a new operation pass manager for the given operation kind under this pass manager.
Definition Pass.cpp:382
A structure to represent the information for a derived pass class.
static const PassInfo * lookup(StringRef passArg)
Returns the pass info for the specified pass class or null if unknown.
PassInfo(StringRef arg, StringRef description, const PassAllocatorFunction &allocator)
PassInfo constructor should not be invoked directly, instead use PassRegistration or registerPass.
bool hasAnyOccurrences() const
Returns true if this parser contains any valid options to add.
PassNameCLParser(StringRef arg, StringRef description)
Construct a parser with the given command line description.
bool contains(const PassRegistryEntry *entry) const
Returns true if the given pass registry entry was registered at the top-level of the parser,...
bool hasAnyOccurrences() const
Returns true if this parser contains any valid options to add.
PassPipelineCLParser(StringRef arg, StringRef description)
Construct a pass pipeline parser with the given command line description.
LogicalResult addToPipeline(OpPassManager &pm, function_ref< LogicalResult(const Twine &)> errorHandler) const
Adds the passes defined by this parser entry to the given pass manager.
bool contains(const PassRegistryEntry *entry) const
Returns true if the given pass registry entry was registered at the top-level of the parser,...
A structure to represent the information of a registered pass pipeline.
PassPipelineInfo(StringRef arg, StringRef description, const PassRegistryFunction &builder, std::function< void(function_ref< void(const detail::PassOptions &)>)> optHandler)
static const PassPipelineInfo * lookup(StringRef pipelineArg)
Returns the pass pipeline info for the specified pass pipeline or null if unknown.
Structure to group information about a passes and pass pipelines (argument to invoke via mlir-opt,...
void printHelpStr(size_t indent, size_t descIndent) const
Print the help information for this pass.
size_t getOptionWidth() const
Return the maximum width required when printing the options of this entry.
PassRegistryEntry(StringRef arg, StringRef description, const PassRegistryFunction &builder, std::function< void(function_ref< void(const detail::PassOptions &)>)> optHandler)
StringRef getPassDescription() const
Returns a description for the pass, this never returns null.
StringRef getPassArgument() const
Returns the command line option that may be passed to 'mlir-opt' that will cause this pass to run or ...
This class provides an efficient unique identifier for a specific C++ type.
Definition TypeID.h:107
This class represents a specific pass option, with a provided data type.
Base container class and manager for all pass options.
Definition PassOptions.h:89
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition CallGraph.h:229
LogicalResult parseCommaSeparatedList(llvm::cl::Option &opt, StringRef argName, StringRef optionStr, function_ref< LogicalResult(StringRef)> elementParseFn)
Parse a string containing a list of comma-delimited elements, invoking the given parser for each sub-...
AttrTypeReplacer.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.
std::function< std::unique_ptr< Pass >()> PassAllocatorFunction
std::function< LogicalResult( OpPassManager &, StringRef options, function_ref< LogicalResult(const Twine &)> errorHandler)> PassRegistryFunction
A registry function that adds passes to the given pass manager.
void printRegisteredPasses()
Prints the passes that were previously registered and stored in passRegistry.
void registerPass(const PassAllocatorFunction &function)
Register a specific dialect pass allocator function with the system, typically used through the PassR...
void registerPassPipeline(StringRef arg, StringRef description, const PassRegistryFunction &function, std::function< void(function_ref< void(const detail::PassOptions &)>)> optHandler)
Register a specific dialect pipeline registry function with the system, typically used through the Pa...
LogicalResult parsePassPipeline(StringRef pipeline, OpPassManager &pm, raw_ostream &errorStream=llvm::errs())
Parse the textual representation of a pass pipeline, adding the result to 'pm' on success.
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
const PassArgData & getValue() const
OptionValue(const PassArgData &value)
void setValue(const PassArgData &value)
llvm::cl::list< PassArgData, bool, PassNameParser > passList
The set of passes and pass pipelines to run.
PassPipelineCLParserImpl(StringRef arg, StringRef description, bool passNamesOnly)
bool contains(const PassRegistryEntry *entry) const
Returns true if the given pass registry entry was registered at the top-level of the parser,...