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