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