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 
9 #include "mlir/Pass/PassRegistry.h"
10 #include "mlir/Pass/Pass.h"
11 #include "mlir/Pass/PassManager.h"
12 #include "llvm/ADT/DenseMap.h"
13 #include "llvm/Support/Format.h"
14 #include "llvm/Support/ManagedStatic.h"
15 #include "llvm/Support/MemoryBuffer.h"
16 #include "llvm/Support/SourceMgr.h"
17 
18 using namespace mlir;
19 using namespace detail;
20 
21 /// Static mapping of all of the registered passes.
22 static llvm::ManagedStatic<llvm::StringMap<PassInfo>> passRegistry;
23 
24 /// A mapping of the above pass registry entries to the corresponding TypeID
25 /// of the pass that they generate.
26 static llvm::ManagedStatic<llvm::StringMap<TypeID>> passRegistryTypeIDs;
27 
28 /// Static mapping of all of the registered pass pipelines.
29 static llvm::ManagedStatic<llvm::StringMap<PassPipelineInfo>>
30     passPipelineRegistry;
31 
32 /// Utility to create a default registry function from a pass instance.
33 static PassRegistryFunction
34 buildDefaultRegistryFn(const PassAllocatorFunction &allocator) {
35   return [=](OpPassManager &pm, StringRef options,
36              function_ref<LogicalResult(const Twine &)> errorHandler) {
37     std::unique_ptr<Pass> pass = allocator();
38     LogicalResult result = pass->initializeOptions(options);
39     if ((pm.getNesting() == OpPassManager::Nesting::Explicit) &&
40         pass->getOpName() && *pass->getOpName() != pm.getOpName())
41       return errorHandler(llvm::Twine("Can't add pass '") + pass->getName() +
42                           "' restricted to '" + *pass->getOpName() +
43                           "' on a PassManager intended to run on '" +
44                           pm.getOpName() + "', did you intend to nest?");
45     pm.addPass(std::move(pass));
46     return result;
47   };
48 }
49 
50 /// Utility to print the help string for a specific option.
51 static void printOptionHelp(StringRef arg, StringRef desc, size_t indent,
52                             size_t descIndent, bool isTopLevel) {
53   size_t numSpaces = descIndent - indent - 4;
54   llvm::outs().indent(indent)
55       << "--" << llvm::left_justify(arg, numSpaces) << "-   " << desc << '\n';
56 }
57 
58 //===----------------------------------------------------------------------===//
59 // PassRegistry
60 //===----------------------------------------------------------------------===//
61 
62 /// Print the help information for this pass. This includes the argument,
63 /// description, and any pass options. `descIndent` is the indent that the
64 /// descriptions should be aligned.
65 void PassRegistryEntry::printHelpStr(size_t indent, size_t descIndent) const {
66   printOptionHelp(getPassArgument(), getPassDescription(), indent, descIndent,
67                   /*isTopLevel=*/true);
68   // If this entry has options, print the help for those as well.
69   optHandler([=](const PassOptions &options) {
70     options.printHelp(indent, descIndent);
71   });
72 }
73 
74 /// Return the maximum width required when printing the options of this
75 /// entry.
76 size_t PassRegistryEntry::getOptionWidth() const {
77   size_t maxLen = 0;
78   optHandler([&](const PassOptions &options) mutable {
79     maxLen = options.getOptionWidth() + 2;
80   });
81   return maxLen;
82 }
83 
84 //===----------------------------------------------------------------------===//
85 // PassPipelineInfo
86 //===----------------------------------------------------------------------===//
87 
88 void mlir::registerPassPipeline(
89     StringRef arg, StringRef description, const PassRegistryFunction &function,
90     std::function<void(function_ref<void(const PassOptions &)>)> optHandler) {
91   PassPipelineInfo pipelineInfo(arg, description, function, optHandler);
92   bool inserted = passPipelineRegistry->try_emplace(arg, pipelineInfo).second;
93   assert(inserted && "Pass pipeline registered multiple times");
94   (void)inserted;
95 }
96 
97 //===----------------------------------------------------------------------===//
98 // PassInfo
99 //===----------------------------------------------------------------------===//
100 
101 PassInfo::PassInfo(StringRef arg, StringRef description,
102                    const PassAllocatorFunction &allocator)
103     : PassRegistryEntry(
104           arg, description, buildDefaultRegistryFn(allocator),
105           // Use a temporary pass to provide an options instance.
106           [=](function_ref<void(const PassOptions &)> optHandler) {
107             optHandler(allocator()->passOptions);
108           }) {}
109 
110 void mlir::registerPass(const PassAllocatorFunction &function) {
111   std::unique_ptr<Pass> pass = function();
112   StringRef arg = pass->getArgument();
113   if (arg.empty())
114     llvm::report_fatal_error(
115         "Trying to register a pass that does not override `getArgument()`");
116   StringRef description = pass->getDescription();
117   PassInfo passInfo(arg, description, function);
118   passRegistry->try_emplace(arg, passInfo);
119 
120   // Verify that the registered pass has the same ID as any registered to this
121   // arg before it.
122   TypeID entryTypeID = pass->getTypeID();
123   auto it = passRegistryTypeIDs->try_emplace(arg, entryTypeID).first;
124   if (it->second != entryTypeID)
125     llvm::report_fatal_error(
126         "pass allocator creates a different pass than previously "
127         "registered for pass " +
128         arg);
129 }
130 
131 /// Returns the pass info for the specified pass argument or null if unknown.
132 const PassInfo *mlir::Pass::lookupPassInfo(StringRef passArg) {
133   auto it = passRegistry->find(passArg);
134   return it == passRegistry->end() ? nullptr : &it->second;
135 }
136 
137 //===----------------------------------------------------------------------===//
138 // PassOptions
139 //===----------------------------------------------------------------------===//
140 
141 /// Out of line virtual function to provide home for the class.
142 void detail::PassOptions::OptionBase::anchor() {}
143 
144 /// Copy the option values from 'other'.
145 void detail::PassOptions::copyOptionValuesFrom(const PassOptions &other) {
146   assert(options.size() == other.options.size());
147   if (options.empty())
148     return;
149   for (auto optionsIt : llvm::zip(options, other.options))
150     std::get<0>(optionsIt)->copyValueFrom(*std::get<1>(optionsIt));
151 }
152 
153 LogicalResult detail::PassOptions::parseFromString(StringRef options) {
154   // TODO: Handle escaping strings.
155   // NOTE: `options` is modified in place to always refer to the unprocessed
156   // part of the string.
157   while (!options.empty()) {
158     size_t spacePos = options.find(' ');
159     StringRef arg = options;
160     if (spacePos != StringRef::npos) {
161       arg = options.substr(0, spacePos);
162       options = options.substr(spacePos + 1);
163     } else {
164       options = StringRef();
165     }
166     if (arg.empty())
167       continue;
168 
169     // At this point, arg refers to everything that is non-space in options
170     // upto the next space, and options refers to the rest of the string after
171     // that point.
172 
173     // Split the individual option on '=' to form key and value. If there is no
174     // '=', then value is `StringRef()`.
175     size_t equalPos = arg.find('=');
176     StringRef key = arg;
177     StringRef value;
178     if (equalPos != StringRef::npos) {
179       key = arg.substr(0, equalPos);
180       value = arg.substr(equalPos + 1);
181     }
182     auto it = OptionsMap.find(key);
183     if (it == OptionsMap.end()) {
184       llvm::errs() << "<Pass-Options-Parser>: no such option " << key << "\n";
185       return failure();
186     }
187     if (llvm::cl::ProvidePositionalOption(it->second, value, 0))
188       return failure();
189   }
190 
191   return success();
192 }
193 
194 /// Print the options held by this struct in a form that can be parsed via
195 /// 'parseFromString'.
196 void detail::PassOptions::print(raw_ostream &os) {
197   // If there are no options, there is nothing left to do.
198   if (OptionsMap.empty())
199     return;
200 
201   // Sort the options to make the ordering deterministic.
202   SmallVector<OptionBase *, 4> orderedOps(options.begin(), options.end());
203   auto compareOptionArgs = [](OptionBase *const *lhs, OptionBase *const *rhs) {
204     return (*lhs)->getArgStr().compare((*rhs)->getArgStr());
205   };
206   llvm::array_pod_sort(orderedOps.begin(), orderedOps.end(), compareOptionArgs);
207 
208   // Interleave the options with ' '.
209   os << '{';
210   llvm::interleave(
211       orderedOps, os, [&](OptionBase *option) { option->print(os); }, " ");
212   os << '}';
213 }
214 
215 /// Print the help string for the options held by this struct. `descIndent` is
216 /// the indent within the stream that the descriptions should be aligned.
217 void detail::PassOptions::printHelp(size_t indent, size_t descIndent) const {
218   // Sort the options to make the ordering deterministic.
219   SmallVector<OptionBase *, 4> orderedOps(options.begin(), options.end());
220   auto compareOptionArgs = [](OptionBase *const *lhs, OptionBase *const *rhs) {
221     return (*lhs)->getArgStr().compare((*rhs)->getArgStr());
222   };
223   llvm::array_pod_sort(orderedOps.begin(), orderedOps.end(), compareOptionArgs);
224   for (OptionBase *option : orderedOps) {
225     // TODO: printOptionInfo assumes a specific indent and will
226     // print options with values with incorrect indentation. We should add
227     // support to llvm::cl::Option for passing in a base indent to use when
228     // printing.
229     llvm::outs().indent(indent);
230     option->getOption()->printOptionInfo(descIndent - indent);
231   }
232 }
233 
234 /// Return the maximum width required when printing the help string.
235 size_t detail::PassOptions::getOptionWidth() const {
236   size_t max = 0;
237   for (auto *option : options)
238     max = std::max(max, option->getOption()->getOptionWidth());
239   return max;
240 }
241 
242 //===----------------------------------------------------------------------===//
243 // TextualPassPipeline Parser
244 //===----------------------------------------------------------------------===//
245 
246 namespace {
247 /// This class represents a textual description of a pass pipeline.
248 class TextualPipeline {
249 public:
250   /// Try to initialize this pipeline with the given pipeline text.
251   /// `errorStream` is the output stream to emit errors to.
252   LogicalResult initialize(StringRef text, raw_ostream &errorStream);
253 
254   /// Add the internal pipeline elements to the provided pass manager.
255   LogicalResult
256   addToPipeline(OpPassManager &pm,
257                 function_ref<LogicalResult(const Twine &)> errorHandler) const;
258 
259 private:
260   /// A functor used to emit errors found during pipeline handling. The first
261   /// parameter corresponds to the raw location within the pipeline string. This
262   /// should always return failure.
263   using ErrorHandlerT = function_ref<LogicalResult(const char *, Twine)>;
264 
265   /// A struct to capture parsed pass pipeline names.
266   ///
267   /// A pipeline is defined as a series of names, each of which may in itself
268   /// recursively contain a nested pipeline. A name is either the name of a pass
269   /// (e.g. "cse") or the name of an operation type (e.g. "builtin.func"). If
270   /// the name is the name of a pass, the InnerPipeline is empty, since passes
271   /// cannot contain inner pipelines.
272   struct PipelineElement {
273     PipelineElement(StringRef name) : name(name), registryEntry(nullptr) {}
274 
275     StringRef name;
276     StringRef options;
277     const PassRegistryEntry *registryEntry;
278     std::vector<PipelineElement> innerPipeline;
279   };
280 
281   /// Parse the given pipeline text into the internal pipeline vector. This
282   /// function only parses the structure of the pipeline, and does not resolve
283   /// its elements.
284   LogicalResult parsePipelineText(StringRef text, ErrorHandlerT errorHandler);
285 
286   /// Resolve the elements of the pipeline, i.e. connect passes and pipelines to
287   /// the corresponding registry entry.
288   LogicalResult
289   resolvePipelineElements(MutableArrayRef<PipelineElement> elements,
290                           ErrorHandlerT errorHandler);
291 
292   /// Resolve a single element of the pipeline.
293   LogicalResult resolvePipelineElement(PipelineElement &element,
294                                        ErrorHandlerT errorHandler);
295 
296   /// Add the given pipeline elements to the provided pass manager.
297   LogicalResult
298   addToPipeline(ArrayRef<PipelineElement> elements, OpPassManager &pm,
299                 function_ref<LogicalResult(const Twine &)> errorHandler) const;
300 
301   std::vector<PipelineElement> pipeline;
302 };
303 
304 } // end anonymous namespace
305 
306 /// Try to initialize this pipeline with the given pipeline text. An option is
307 /// given to enable accurate error reporting.
308 LogicalResult TextualPipeline::initialize(StringRef text,
309                                           raw_ostream &errorStream) {
310   if (text.empty())
311     return success();
312 
313   // Build a source manager to use for error reporting.
314   llvm::SourceMgr pipelineMgr;
315   pipelineMgr.AddNewSourceBuffer(
316       llvm::MemoryBuffer::getMemBuffer(text, "MLIR Textual PassPipeline Parser",
317                                        /*RequiresNullTerminator=*/false),
318       llvm::SMLoc());
319   auto errorHandler = [&](const char *rawLoc, Twine msg) {
320     pipelineMgr.PrintMessage(errorStream, llvm::SMLoc::getFromPointer(rawLoc),
321                              llvm::SourceMgr::DK_Error, msg);
322     return failure();
323   };
324 
325   // Parse the provided pipeline string.
326   if (failed(parsePipelineText(text, errorHandler)))
327     return failure();
328   return resolvePipelineElements(pipeline, errorHandler);
329 }
330 
331 /// Add the internal pipeline elements to the provided pass manager.
332 LogicalResult TextualPipeline::addToPipeline(
333     OpPassManager &pm,
334     function_ref<LogicalResult(const Twine &)> errorHandler) const {
335   return addToPipeline(pipeline, pm, errorHandler);
336 }
337 
338 /// Parse the given pipeline text into the internal pipeline vector. This
339 /// function only parses the structure of the pipeline, and does not resolve
340 /// its elements.
341 LogicalResult TextualPipeline::parsePipelineText(StringRef text,
342                                                  ErrorHandlerT errorHandler) {
343   SmallVector<std::vector<PipelineElement> *, 4> pipelineStack = {&pipeline};
344   for (;;) {
345     std::vector<PipelineElement> &pipeline = *pipelineStack.back();
346     size_t pos = text.find_first_of(",(){");
347     pipeline.emplace_back(/*name=*/text.substr(0, pos).trim());
348 
349     // If we have a single terminating name, we're done.
350     if (pos == StringRef::npos)
351       break;
352 
353     text = text.substr(pos);
354     char sep = text[0];
355 
356     // Handle pulling ... from 'pass{...}' out as PipelineElement.options.
357     if (sep == '{') {
358       text = text.substr(1);
359 
360       // Skip over everything until the closing '}' and store as options.
361       size_t close = StringRef::npos;
362       for (unsigned i = 0, e = text.size(), braceCount = 1; i < e; ++i) {
363         if (text[i] == '{') {
364           ++braceCount;
365           continue;
366         }
367         if (text[i] == '}' && --braceCount == 0) {
368           close = i;
369           break;
370         }
371       }
372 
373       // Check to see if a closing options brace was found.
374       if (close == StringRef::npos) {
375         return errorHandler(
376             /*rawLoc=*/text.data() - 1,
377             "missing closing '}' while processing pass options");
378       }
379       pipeline.back().options = text.substr(0, close);
380       text = text.substr(close + 1);
381 
382       // Skip checking for '(' because nested pipelines cannot have options.
383     } else if (sep == '(') {
384       text = text.substr(1);
385 
386       // Push the inner pipeline onto the stack to continue processing.
387       pipelineStack.push_back(&pipeline.back().innerPipeline);
388       continue;
389     }
390 
391     // When handling the close parenthesis, we greedily consume them to avoid
392     // empty strings in the pipeline.
393     while (text.consume_front(")")) {
394       // If we try to pop the outer pipeline we have unbalanced parentheses.
395       if (pipelineStack.size() == 1)
396         return errorHandler(/*rawLoc=*/text.data() - 1,
397                             "encountered extra closing ')' creating unbalanced "
398                             "parentheses while parsing pipeline");
399 
400       pipelineStack.pop_back();
401     }
402 
403     // Check if we've finished parsing.
404     if (text.empty())
405       break;
406 
407     // Otherwise, the end of an inner pipeline always has to be followed by
408     // a comma, and then we can continue.
409     if (!text.consume_front(","))
410       return errorHandler(text.data(), "expected ',' after parsing pipeline");
411   }
412 
413   // Check for unbalanced parentheses.
414   if (pipelineStack.size() > 1)
415     return errorHandler(
416         text.data(),
417         "encountered unbalanced parentheses while parsing pipeline");
418 
419   assert(pipelineStack.back() == &pipeline &&
420          "wrong pipeline at the bottom of the stack");
421   return success();
422 }
423 
424 /// Resolve the elements of the pipeline, i.e. connect passes and pipelines to
425 /// the corresponding registry entry.
426 LogicalResult TextualPipeline::resolvePipelineElements(
427     MutableArrayRef<PipelineElement> elements, ErrorHandlerT errorHandler) {
428   for (auto &elt : elements)
429     if (failed(resolvePipelineElement(elt, errorHandler)))
430       return failure();
431   return success();
432 }
433 
434 /// Resolve a single element of the pipeline.
435 LogicalResult
436 TextualPipeline::resolvePipelineElement(PipelineElement &element,
437                                         ErrorHandlerT errorHandler) {
438   // If the inner pipeline of this element is not empty, this is an operation
439   // pipeline.
440   if (!element.innerPipeline.empty())
441     return resolvePipelineElements(element.innerPipeline, errorHandler);
442   // Otherwise, this must be a pass or pass pipeline.
443   // Check to see if a pipeline was registered with this name.
444   auto pipelineRegistryIt = passPipelineRegistry->find(element.name);
445   if (pipelineRegistryIt != passPipelineRegistry->end()) {
446     element.registryEntry = &pipelineRegistryIt->second;
447     return success();
448   }
449 
450   // If not, then this must be a specific pass name.
451   if ((element.registryEntry = Pass::lookupPassInfo(element.name)))
452     return success();
453 
454   // Emit an error for the unknown pass.
455   auto *rawLoc = element.name.data();
456   return errorHandler(rawLoc, "'" + element.name +
457                                   "' does not refer to a "
458                                   "registered pass or pass pipeline");
459 }
460 
461 /// Add the given pipeline elements to the provided pass manager.
462 LogicalResult TextualPipeline::addToPipeline(
463     ArrayRef<PipelineElement> elements, OpPassManager &pm,
464     function_ref<LogicalResult(const Twine &)> errorHandler) const {
465   for (auto &elt : elements) {
466     if (elt.registryEntry) {
467       if (failed(elt.registryEntry->addToPipeline(pm, elt.options,
468                                                   errorHandler))) {
469         return errorHandler("failed to add `" + elt.name + "` with options `" +
470                             elt.options + "`");
471       }
472     } else if (failed(addToPipeline(elt.innerPipeline, pm.nest(elt.name),
473                                     errorHandler))) {
474       return errorHandler("failed to add `" + elt.name + "` with options `" +
475                           elt.options + "` to inner pipeline");
476     }
477   }
478   return success();
479 }
480 
481 /// This function parses the textual representation of a pass pipeline, and adds
482 /// the result to 'pm' on success. This function returns failure if the given
483 /// pipeline was invalid. 'errorStream' is an optional parameter that, if
484 /// non-null, will be used to emit errors found during parsing.
485 LogicalResult mlir::parsePassPipeline(StringRef pipeline, OpPassManager &pm,
486                                       raw_ostream &errorStream) {
487   TextualPipeline pipelineParser;
488   if (failed(pipelineParser.initialize(pipeline, errorStream)))
489     return failure();
490   auto errorHandler = [&](Twine msg) {
491     errorStream << msg << "\n";
492     return failure();
493   };
494   if (failed(pipelineParser.addToPipeline(pm, errorHandler)))
495     return failure();
496   return success();
497 }
498 
499 //===----------------------------------------------------------------------===//
500 // PassNameParser
501 //===----------------------------------------------------------------------===//
502 
503 namespace {
504 /// This struct represents the possible data entries in a parsed pass pipeline
505 /// list.
506 struct PassArgData {
507   PassArgData() : registryEntry(nullptr) {}
508   PassArgData(const PassRegistryEntry *registryEntry)
509       : registryEntry(registryEntry) {}
510 
511   /// This field is used when the parsed option corresponds to a registered pass
512   /// or pass pipeline.
513   const PassRegistryEntry *registryEntry;
514 
515   /// This field is set when instance specific pass options have been provided
516   /// on the command line.
517   StringRef options;
518 
519   /// This field is used when the parsed option corresponds to an explicit
520   /// pipeline.
521   TextualPipeline pipeline;
522 };
523 } // end anonymous namespace
524 
525 namespace llvm {
526 namespace cl {
527 /// Define a valid OptionValue for the command line pass argument.
528 template <>
529 struct OptionValue<PassArgData> final
530     : OptionValueBase<PassArgData, /*isClass=*/true> {
531   OptionValue(const PassArgData &value) { this->setValue(value); }
532   OptionValue() = default;
533   void anchor() override {}
534 
535   bool hasValue() const { return true; }
536   const PassArgData &getValue() const { return value; }
537   void setValue(const PassArgData &value) { this->value = value; }
538 
539   PassArgData value;
540 };
541 } // end namespace cl
542 } // end namespace llvm
543 
544 namespace {
545 
546 /// The name for the command line option used for parsing the textual pass
547 /// pipeline.
548 static constexpr StringLiteral passPipelineArg = "pass-pipeline";
549 
550 /// Adds command line option for each registered pass or pass pipeline, as well
551 /// as textual pass pipelines.
552 struct PassNameParser : public llvm::cl::parser<PassArgData> {
553   PassNameParser(llvm::cl::Option &opt) : llvm::cl::parser<PassArgData>(opt) {}
554 
555   void initialize();
556   void printOptionInfo(const llvm::cl::Option &opt,
557                        size_t globalWidth) const override;
558   size_t getOptionWidth(const llvm::cl::Option &opt) const override;
559   bool parse(llvm::cl::Option &opt, StringRef argName, StringRef arg,
560              PassArgData &value);
561 
562   /// If true, this parser only parses entries that correspond to a concrete
563   /// pass registry entry, and does not add a `pass-pipeline` argument, does not
564   /// include the options for pass entries, and does not include pass pipelines
565   /// entries.
566   bool passNamesOnly = false;
567 };
568 } // namespace
569 
570 void PassNameParser::initialize() {
571   llvm::cl::parser<PassArgData>::initialize();
572 
573   /// Add an entry for the textual pass pipeline option.
574   if (!passNamesOnly) {
575     addLiteralOption(passPipelineArg, PassArgData(),
576                      "A textual description of a pass pipeline to run");
577   }
578 
579   /// Add the pass entries.
580   for (const auto &kv : *passRegistry) {
581     addLiteralOption(kv.second.getPassArgument(), &kv.second,
582                      kv.second.getPassDescription());
583   }
584   /// Add the pass pipeline entries.
585   if (!passNamesOnly) {
586     for (const auto &kv : *passPipelineRegistry) {
587       addLiteralOption(kv.second.getPassArgument(), &kv.second,
588                        kv.second.getPassDescription());
589     }
590   }
591 }
592 
593 void PassNameParser::printOptionInfo(const llvm::cl::Option &opt,
594                                      size_t globalWidth) const {
595   // If this parser is just parsing pass names, print a simplified option
596   // string.
597   if (passNamesOnly) {
598     llvm::outs() << "  --" << opt.ArgStr << "=<pass-arg>";
599     opt.printHelpStr(opt.HelpStr, globalWidth, opt.ArgStr.size() + 18);
600     return;
601   }
602 
603   // Print the information for the top-level option.
604   if (opt.hasArgStr()) {
605     llvm::outs() << "  --" << opt.ArgStr;
606     opt.printHelpStr(opt.HelpStr, globalWidth, opt.ArgStr.size() + 7);
607   } else {
608     llvm::outs() << "  " << opt.HelpStr << '\n';
609   }
610 
611   // Print the top-level pipeline argument.
612   printOptionHelp(passPipelineArg,
613                   "A textual description of a pass pipeline to run",
614                   /*indent=*/4, globalWidth, /*isTopLevel=*/!opt.hasArgStr());
615 
616   // Functor used to print the ordered entries of a registration map.
617   auto printOrderedEntries = [&](StringRef header, auto &map) {
618     llvm::SmallVector<PassRegistryEntry *, 32> orderedEntries;
619     for (auto &kv : map)
620       orderedEntries.push_back(&kv.second);
621     llvm::array_pod_sort(
622         orderedEntries.begin(), orderedEntries.end(),
623         [](PassRegistryEntry *const *lhs, PassRegistryEntry *const *rhs) {
624           return (*lhs)->getPassArgument().compare((*rhs)->getPassArgument());
625         });
626 
627     llvm::outs().indent(4) << header << ":\n";
628     for (PassRegistryEntry *entry : orderedEntries)
629       entry->printHelpStr(/*indent=*/6, globalWidth);
630   };
631 
632   // Print the available passes.
633   printOrderedEntries("Passes", *passRegistry);
634 
635   // Print the available pass pipelines.
636   if (!passPipelineRegistry->empty())
637     printOrderedEntries("Pass Pipelines", *passPipelineRegistry);
638 }
639 
640 size_t PassNameParser::getOptionWidth(const llvm::cl::Option &opt) const {
641   size_t maxWidth = llvm::cl::parser<PassArgData>::getOptionWidth(opt) + 2;
642 
643   // Check for any wider pass or pipeline options.
644   for (auto &entry : *passRegistry)
645     maxWidth = std::max(maxWidth, entry.second.getOptionWidth() + 4);
646   for (auto &entry : *passPipelineRegistry)
647     maxWidth = std::max(maxWidth, entry.second.getOptionWidth() + 4);
648   return maxWidth;
649 }
650 
651 bool PassNameParser::parse(llvm::cl::Option &opt, StringRef argName,
652                            StringRef arg, PassArgData &value) {
653   // Handle the pipeline option explicitly.
654   if (argName == passPipelineArg)
655     return failed(value.pipeline.initialize(arg, llvm::errs()));
656 
657   // Otherwise, default to the base for handling.
658   if (llvm::cl::parser<PassArgData>::parse(opt, argName, arg, value))
659     return true;
660   value.options = arg;
661   return false;
662 }
663 
664 //===----------------------------------------------------------------------===//
665 // PassPipelineCLParser
666 //===----------------------------------------------------------------------===//
667 
668 namespace mlir {
669 namespace detail {
670 struct PassPipelineCLParserImpl {
671   PassPipelineCLParserImpl(StringRef arg, StringRef description,
672                            bool passNamesOnly)
673       : passList(arg, llvm::cl::desc(description)) {
674     passList.getParser().passNamesOnly = passNamesOnly;
675     passList.setValueExpectedFlag(llvm::cl::ValueExpected::ValueOptional);
676   }
677 
678   /// Returns true if the given pass registry entry was registered at the
679   /// top-level of the parser, i.e. not within an explicit textual pipeline.
680   bool contains(const PassRegistryEntry *entry) const {
681     return llvm::any_of(passList, [&](const PassArgData &data) {
682       return data.registryEntry == entry;
683     });
684   }
685 
686   /// The set of passes and pass pipelines to run.
687   llvm::cl::list<PassArgData, bool, PassNameParser> passList;
688 };
689 } // end namespace detail
690 } // end namespace mlir
691 
692 /// Construct a pass pipeline parser with the given command line description.
693 PassPipelineCLParser::PassPipelineCLParser(StringRef arg, StringRef description)
694     : impl(std::make_unique<detail::PassPipelineCLParserImpl>(
695           arg, description, /*passNamesOnly=*/false)) {}
696 PassPipelineCLParser::~PassPipelineCLParser() {}
697 
698 /// Returns true if this parser contains any valid options to add.
699 bool PassPipelineCLParser::hasAnyOccurrences() const {
700   return impl->passList.getNumOccurrences() != 0;
701 }
702 
703 /// Returns true if the given pass registry entry was registered at the
704 /// top-level of the parser, i.e. not within an explicit textual pipeline.
705 bool PassPipelineCLParser::contains(const PassRegistryEntry *entry) const {
706   return impl->contains(entry);
707 }
708 
709 /// Adds the passes defined by this parser entry to the given pass manager.
710 LogicalResult PassPipelineCLParser::addToPipeline(
711     OpPassManager &pm,
712     function_ref<LogicalResult(const Twine &)> errorHandler) const {
713   for (auto &passIt : impl->passList) {
714     if (passIt.registryEntry) {
715       if (failed(passIt.registryEntry->addToPipeline(pm, passIt.options,
716                                                      errorHandler)))
717         return failure();
718     } else {
719       OpPassManager::Nesting nesting = pm.getNesting();
720       pm.setNesting(OpPassManager::Nesting::Explicit);
721       LogicalResult status = passIt.pipeline.addToPipeline(pm, errorHandler);
722       pm.setNesting(nesting);
723       if (failed(status))
724         return failure();
725     }
726   }
727   return success();
728 }
729 
730 //===----------------------------------------------------------------------===//
731 // PassNameCLParser
732 
733 /// Construct a pass pipeline parser with the given command line description.
734 PassNameCLParser::PassNameCLParser(StringRef arg, StringRef description)
735     : impl(std::make_unique<detail::PassPipelineCLParserImpl>(
736           arg, description, /*passNamesOnly=*/true)) {
737   impl->passList.setMiscFlag(llvm::cl::CommaSeparated);
738 }
739 PassNameCLParser::~PassNameCLParser() {}
740 
741 /// Returns true if this parser contains any valid options to add.
742 bool PassNameCLParser::hasAnyOccurrences() const {
743   return impl->passList.getNumOccurrences() != 0;
744 }
745 
746 /// Returns true if the given pass registry entry was registered at the
747 /// top-level of the parser, i.e. not within an explicit textual pipeline.
748 bool PassNameCLParser::contains(const PassRegistryEntry *entry) const {
749   return impl->contains(entry);
750 }
751