1052 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "mlir/Pass/PassRegistry.h"10 11#include "mlir/Pass/Pass.h"12#include "mlir/Pass/PassManager.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 TypeID30/// 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>>35 passPipelineRegistry;36 37/// Utility to create a default registry function from a pass instance.38static PassRegistryFunction39buildDefaultRegistryFn(const PassAllocatorFunction &allocator) {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// PassRegistry69//===----------------------------------------------------------------------===//70 71/// Prints the passes that were previously registered and stored in passRegistry72void mlir::printRegisteredPasses() {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) {79 llvm::SmallVector<PassRegistryEntry *, 32> orderedEntries;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 the99/// 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 this110/// entry.111size_t PassRegistryEntry::getOptionWidth() const {112 size_t maxLen = 0;113 optHandler([&](const PassOptions &options) mutable {114 maxLen = options.getOptionWidth() + 2;115 });116 return maxLen;117}118 119//===----------------------------------------------------------------------===//120// PassPipelineInfo121//===----------------------------------------------------------------------===//122 123void mlir::registerPassPipeline(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 NDEBUG130 if (!inserted)131 report_fatal_error("Pass pipeline " + arg + " registered multiple times");132#endif133 (void)inserted;134}135 136//===----------------------------------------------------------------------===//137// PassInfo138//===----------------------------------------------------------------------===//139 140PassInfo::PassInfo(StringRef arg, StringRef description,141 const PassAllocatorFunction &allocator)142 : PassRegistryEntry(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 149void mlir::registerPass(const PassAllocatorFunction &function) {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 this161 // 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 or178/// null if unknown.179const PassPipelineInfo *mlir::PassPipelineInfo::lookup(StringRef pipelineArg) {180 auto it = passPipelineRegistry->find(pipelineArg);181 return it == passPipelineRegistry->end() ? nullptr : &it->second;182}183 184//===----------------------------------------------------------------------===//185// PassOptions186//===----------------------------------------------------------------------===//187 188/// Attempt to find the next occurance of character 'c' in the string starting189/// from the `index`-th position , omitting any occurances that appear within190/// 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 that234 // denote literals, we respect scoping here. The outer `{...}` should not235 // be stripped in cases such as "arg={...},{...}", which can be used to denote236 // 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 246LogicalResult detail::pass_options::parseCommaSeparatedList(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 tuple280/// containing [the key of the option, the value of the option, updated281/// `options` string pointing after the parsed option].282static std::tuple<StringRef, StringRef, StringRef>283parseNextArg(StringRef options) {284 // Try to process the given punctuation, properly escaping any contained285 // characters.286 auto tryProcessPunct = [&](size_t ¤tPos, 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 so325 // 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 unprocessed347 // 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 via367/// '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` is388/// 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 will398 // print options with values with incorrect indentation. We should add399 // support to llvm::cl::Option for passing in a base indent to use when400 // 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 Options416//===----------------------------------------------------------------------===//417 418//===----------------------------------------------------------------------===//419// OpPassManager: OptionValue420//===----------------------------------------------------------------------===//421 422llvm::cl::OptionValue<OpPassManager>::OptionValue() = default;423llvm::cl::OptionValue<OpPassManager>::OptionValue(424 const mlir::OpPassManager &value) {425 setValue(value);426}427llvm::cl::OptionValue<OpPassManager>::OptionValue(428 const llvm::cl::OptionValue<mlir::OpPassManager> &rhs) {429 if (rhs.hasValue())430 setValue(rhs.getValue());431}432llvm::cl::OptionValue<OpPassManager> &433llvm::cl::OptionValue<OpPassManager>::operator=(434 const mlir::OpPassManager &rhs) {435 setValue(rhs);436 return *this;437}438 439llvm::cl::OptionValue<OpPassManager>::~OptionValue<OpPassManager>() = default;440 441void llvm::cl::OptionValue<OpPassManager>::setValue(442 const OpPassManager &newValue) {443 if (hasValue())444 *value = newValue;445 else446 value = std::make_unique<mlir::OpPassManager>(newValue);447}448void llvm::cl::OptionValue<OpPassManager>::setValue(StringRef pipelineStr) {449 FailureOr<OpPassManager> pipeline = parsePassPipeline(pipelineStr);450 assert(succeeded(pipeline) && "invalid pass pipeline");451 setValue(*pipeline);452}453 454bool llvm::cl::OptionValue<OpPassManager>::compare(455 const mlir::OpPassManager &rhs) const {456 std::string lhsStr, rhsStr;457 {458 raw_string_ostream lhsStream(lhsStr);459 value->printAsTextualPipeline(lhsStream);460 461 raw_string_ostream rhsStream(rhsStr);462 rhs.printAsTextualPipeline(rhsStream);463 }464 465 // Use the textual format for pipeline comparisons.466 return lhsStr == rhsStr;467}468 469void llvm::cl::OptionValue<OpPassManager>::anchor() {}470 471//===----------------------------------------------------------------------===//472// OpPassManager: Parser473//===----------------------------------------------------------------------===//474 475namespace llvm {476namespace cl {477template class basic_parser<OpPassManager>;478} // namespace cl479} // namespace llvm480 481bool llvm::cl::parser<OpPassManager>::parse(Option &, StringRef, StringRef arg,482 ParsedPassManager &value) {483 FailureOr<OpPassManager> pipeline = parsePassPipeline(arg);484 if (failed(pipeline))485 return true;486 value.value = std::make_unique<OpPassManager>(std::move(*pipeline));487 return false;488}489 490void llvm::cl::parser<OpPassManager>::print(raw_ostream &os,491 const OpPassManager &value) {492 value.printAsTextualPipeline(os);493}494 495void llvm::cl::parser<OpPassManager>::printOptionDiff(496 const Option &opt, OpPassManager &pm, const OptVal &defaultValue,497 size_t globalWidth) const {498 printOptionName(opt, globalWidth);499 outs() << "= ";500 pm.printAsTextualPipeline(outs());501 502 if (defaultValue.hasValue()) {503 outs().indent(2) << " (default: ";504 defaultValue.getValue().printAsTextualPipeline(outs());505 outs() << ")";506 }507 outs() << "\n";508}509 510void llvm::cl::parser<OpPassManager>::anchor() {}511 512llvm::cl::parser<OpPassManager>::ParsedPassManager::ParsedPassManager() =513 default;514llvm::cl::parser<OpPassManager>::ParsedPassManager::ParsedPassManager(515 ParsedPassManager &&) = default;516llvm::cl::parser<OpPassManager>::ParsedPassManager::~ParsedPassManager() =517 default;518 519//===----------------------------------------------------------------------===//520// TextualPassPipeline Parser521//===----------------------------------------------------------------------===//522 523namespace {524/// This class represents a textual description of a pass pipeline.525class TextualPipeline {526public:527 /// Try to initialize this pipeline with the given pipeline text.528 /// `errorStream` is the output stream to emit errors to.529 LogicalResult initialize(StringRef text, raw_ostream &errorStream);530 531 /// Add the internal pipeline elements to the provided pass manager.532 LogicalResult533 addToPipeline(OpPassManager &pm,534 function_ref<LogicalResult(const Twine &)> errorHandler) const;535 536private:537 /// A functor used to emit errors found during pipeline handling. The first538 /// parameter corresponds to the raw location within the pipeline string. This539 /// should always return failure.540 using ErrorHandlerT = function_ref<LogicalResult(const char *, Twine)>;541 542 /// A struct to capture parsed pass pipeline names.543 ///544 /// A pipeline is defined as a series of names, each of which may in itself545 /// recursively contain a nested pipeline. A name is either the name of a pass546 /// (e.g. "cse") or the name of an operation type (e.g. "buitin.module"). If547 /// the name is the name of a pass, the InnerPipeline is empty, since passes548 /// cannot contain inner pipelines.549 struct PipelineElement {550 PipelineElement(StringRef name) : name(name) {}551 552 StringRef name;553 StringRef options;554 const PassRegistryEntry *registryEntry = nullptr;555 std::vector<PipelineElement> innerPipeline;556 };557 558 /// Parse the given pipeline text into the internal pipeline vector. This559 /// function only parses the structure of the pipeline, and does not resolve560 /// its elements.561 LogicalResult parsePipelineText(StringRef text, ErrorHandlerT errorHandler);562 563 /// Resolve the elements of the pipeline, i.e. connect passes and pipelines to564 /// the corresponding registry entry.565 LogicalResult566 resolvePipelineElements(MutableArrayRef<PipelineElement> elements,567 ErrorHandlerT errorHandler);568 569 /// Resolve a single element of the pipeline.570 LogicalResult resolvePipelineElement(PipelineElement &element,571 ErrorHandlerT errorHandler);572 573 /// Add the given pipeline elements to the provided pass manager.574 LogicalResult575 addToPipeline(ArrayRef<PipelineElement> elements, OpPassManager &pm,576 function_ref<LogicalResult(const Twine &)> errorHandler) const;577 578 std::vector<PipelineElement> pipeline;579};580 581} // namespace582 583/// Try to initialize this pipeline with the given pipeline text. An option is584/// given to enable accurate error reporting.585LogicalResult TextualPipeline::initialize(StringRef text,586 raw_ostream &errorStream) {587 if (text.empty())588 return success();589 590 // Build a source manager to use for error reporting.591 llvm::SourceMgr pipelineMgr;592 pipelineMgr.AddNewSourceBuffer(593 llvm::MemoryBuffer::getMemBuffer(text, "MLIR Textual PassPipeline Parser",594 /*RequiresNullTerminator=*/false),595 SMLoc());596 auto errorHandler = [&](const char *rawLoc, Twine msg) {597 pipelineMgr.PrintMessage(errorStream, SMLoc::getFromPointer(rawLoc),598 llvm::SourceMgr::DK_Error, msg);599 return failure();600 };601 602 // Parse the provided pipeline string.603 if (failed(parsePipelineText(text, errorHandler)))604 return failure();605 return resolvePipelineElements(pipeline, errorHandler);606}607 608/// Add the internal pipeline elements to the provided pass manager.609LogicalResult TextualPipeline::addToPipeline(610 OpPassManager &pm,611 function_ref<LogicalResult(const Twine &)> errorHandler) const {612 // Temporarily disable implicit nesting while we append to the pipeline. We613 // want the created pipeline to exactly match the parsed text pipeline, so614 // it's preferrable to just error out if implicit nesting would be required.615 OpPassManager::Nesting nesting = pm.getNesting();616 pm.setNesting(OpPassManager::Nesting::Explicit);617 auto restore = llvm::make_scope_exit([&]() { pm.setNesting(nesting); });618 619 return addToPipeline(pipeline, pm, errorHandler);620}621 622/// Parse the given pipeline text into the internal pipeline vector. This623/// function only parses the structure of the pipeline, and does not resolve624/// its elements.625LogicalResult TextualPipeline::parsePipelineText(StringRef text,626 ErrorHandlerT errorHandler) {627 SmallVector<std::vector<PipelineElement> *, 4> pipelineStack = {&pipeline};628 for (;;) {629 std::vector<PipelineElement> &pipeline = *pipelineStack.back();630 size_t pos = text.find_first_of(",(){");631 pipeline.emplace_back(/*name=*/text.substr(0, pos).trim());632 633 // If we have a single terminating name, we're done.634 if (pos == StringRef::npos)635 break;636 637 text = text.substr(pos);638 char sep = text[0];639 640 // Handle pulling ... from 'pass{...}' out as PipelineElement.options.641 if (sep == '{') {642 text = text.substr(1);643 644 // Skip over everything until the closing '}' and store as options.645 size_t close = StringRef::npos;646 for (unsigned i = 0, e = text.size(), braceCount = 1; i < e; ++i) {647 if (text[i] == '{') {648 ++braceCount;649 continue;650 }651 if (text[i] == '}' && --braceCount == 0) {652 close = i;653 break;654 }655 }656 657 // Check to see if a closing options brace was found.658 if (close == StringRef::npos) {659 return errorHandler(660 /*rawLoc=*/text.data() - 1,661 "missing closing '}' while processing pass options");662 }663 pipeline.back().options = text.substr(0, close);664 text = text.substr(close + 1);665 666 // Consume space characters that an user might add for readability.667 text = text.ltrim();668 669 // Skip checking for '(' because nested pipelines cannot have options.670 } else if (sep == '(') {671 text = text.substr(1);672 673 // Push the inner pipeline onto the stack to continue processing.674 pipelineStack.push_back(&pipeline.back().innerPipeline);675 continue;676 }677 678 // When handling the close parenthesis, we greedily consume them to avoid679 // empty strings in the pipeline.680 while (text.consume_front(")")) {681 // If we try to pop the outer pipeline we have unbalanced parentheses.682 if (pipelineStack.size() == 1)683 return errorHandler(/*rawLoc=*/text.data() - 1,684 "encountered extra closing ')' creating unbalanced "685 "parentheses while parsing pipeline");686 687 pipelineStack.pop_back();688 // Consume space characters that an user might add for readability.689 text = text.ltrim();690 }691 692 // Check if we've finished parsing.693 if (text.empty())694 break;695 696 // Otherwise, the end of an inner pipeline always has to be followed by697 // a comma, and then we can continue.698 if (!text.consume_front(","))699 return errorHandler(text.data(), "expected ',' after parsing pipeline");700 }701 702 // Check for unbalanced parentheses.703 if (pipelineStack.size() > 1)704 return errorHandler(705 text.data(),706 "encountered unbalanced parentheses while parsing pipeline");707 708 assert(pipelineStack.back() == &pipeline &&709 "wrong pipeline at the bottom of the stack");710 return success();711}712 713/// Resolve the elements of the pipeline, i.e. connect passes and pipelines to714/// the corresponding registry entry.715LogicalResult TextualPipeline::resolvePipelineElements(716 MutableArrayRef<PipelineElement> elements, ErrorHandlerT errorHandler) {717 for (auto &elt : elements)718 if (failed(resolvePipelineElement(elt, errorHandler)))719 return failure();720 return success();721}722 723/// Resolve a single element of the pipeline.724LogicalResult725TextualPipeline::resolvePipelineElement(PipelineElement &element,726 ErrorHandlerT errorHandler) {727 // If the inner pipeline of this element is not empty, this is an operation728 // pipeline.729 if (!element.innerPipeline.empty())730 return resolvePipelineElements(element.innerPipeline, errorHandler);731 732 // Otherwise, this must be a pass or pass pipeline.733 // Check to see if a pipeline was registered with this name.734 if ((element.registryEntry = PassPipelineInfo::lookup(element.name)))735 return success();736 737 // If not, then this must be a specific pass name.738 if ((element.registryEntry = PassInfo::lookup(element.name)))739 return success();740 741 // Emit an error for the unknown pass.742 auto *rawLoc = element.name.data();743 return errorHandler(rawLoc, "'" + element.name +744 "' does not refer to a "745 "registered pass or pass pipeline");746}747 748/// Add the given pipeline elements to the provided pass manager.749LogicalResult TextualPipeline::addToPipeline(750 ArrayRef<PipelineElement> elements, OpPassManager &pm,751 function_ref<LogicalResult(const Twine &)> errorHandler) const {752 for (auto &elt : elements) {753 if (elt.registryEntry) {754 if (failed(elt.registryEntry->addToPipeline(pm, elt.options,755 errorHandler))) {756 return errorHandler("failed to add `" + elt.name + "` with options `" +757 elt.options + "`");758 }759 } else if (failed(addToPipeline(elt.innerPipeline, pm.nest(elt.name),760 errorHandler))) {761 return errorHandler("failed to add `" + elt.name + "` with options `" +762 elt.options + "` to inner pipeline");763 }764 }765 return success();766}767 768LogicalResult mlir::parsePassPipeline(StringRef pipeline, OpPassManager &pm,769 raw_ostream &errorStream) {770 TextualPipeline pipelineParser;771 if (failed(pipelineParser.initialize(pipeline, errorStream)))772 return failure();773 auto errorHandler = [&](Twine msg) {774 errorStream << msg << "\n";775 return failure();776 };777 if (failed(pipelineParser.addToPipeline(pm, errorHandler)))778 return failure();779 return success();780}781 782FailureOr<OpPassManager> mlir::parsePassPipeline(StringRef pipeline,783 raw_ostream &errorStream) {784 pipeline = pipeline.trim();785 // Pipelines are expected to be of the form `<op-name>(<pipeline>)`.786 size_t pipelineStart = pipeline.find_first_of('(');787 if (pipelineStart == 0 || pipelineStart == StringRef::npos ||788 !pipeline.consume_back(")")) {789 errorStream << "expected pass pipeline to be wrapped with the anchor "790 "operation type, e.g. 'builtin.module(...)'";791 return failure();792 }793 794 StringRef opName = pipeline.take_front(pipelineStart).rtrim();795 OpPassManager pm(opName);796 if (failed(parsePassPipeline(pipeline.drop_front(1 + pipelineStart), pm,797 errorStream)))798 return failure();799 return pm;800}801 802//===----------------------------------------------------------------------===//803// PassNameParser804//===----------------------------------------------------------------------===//805 806namespace {807/// This struct represents the possible data entries in a parsed pass pipeline808/// list.809struct PassArgData {810 PassArgData() = default;811 PassArgData(const PassRegistryEntry *registryEntry)812 : registryEntry(registryEntry) {}813 814 /// This field is used when the parsed option corresponds to a registered pass815 /// or pass pipeline.816 const PassRegistryEntry *registryEntry{nullptr};817 818 /// This field is set when instance specific pass options have been provided819 /// on the command line.820 StringRef options;821};822} // namespace823 824namespace llvm {825namespace cl {826/// Define a valid OptionValue for the command line pass argument.827template <>828struct OptionValue<PassArgData> final829 : OptionValueBase<PassArgData, /*isClass=*/true> {830 OptionValue(const PassArgData &value) { this->setValue(value); }831 OptionValue() = default;832 void anchor() override {}833 834 bool hasValue() const { return true; }835 const PassArgData &getValue() const { return value; }836 void setValue(const PassArgData &value) { this->value = value; }837 838 PassArgData value;839};840} // namespace cl841} // namespace llvm842 843namespace {844 845/// The name for the command line option used for parsing the textual pass846/// pipeline.847#define PASS_PIPELINE_ARG "pass-pipeline"848 849/// Adds command line option for each registered pass or pass pipeline, as well850/// as textual pass pipelines.851struct PassNameParser : public llvm::cl::parser<PassArgData> {852 PassNameParser(llvm::cl::Option &opt) : llvm::cl::parser<PassArgData>(opt) {}853 854 void initialize();855 void printOptionInfo(const llvm::cl::Option &opt,856 size_t globalWidth) const override;857 size_t getOptionWidth(const llvm::cl::Option &opt) const override;858 bool parse(llvm::cl::Option &opt, StringRef argName, StringRef arg,859 PassArgData &value);860 861 /// If true, this parser only parses entries that correspond to a concrete862 /// pass registry entry, and does not include pipeline entries or the options863 /// for pass entries.864 bool passNamesOnly = false;865};866} // namespace867 868void PassNameParser::initialize() {869 llvm::cl::parser<PassArgData>::initialize();870 871 /// Add the pass entries.872 for (const auto &kv : *passRegistry) {873 addLiteralOption(kv.second.getPassArgument(), &kv.second,874 kv.second.getPassDescription());875 }876 /// Add the pass pipeline entries.877 if (!passNamesOnly) {878 for (const auto &kv : *passPipelineRegistry) {879 addLiteralOption(kv.second.getPassArgument(), &kv.second,880 kv.second.getPassDescription());881 }882 }883}884 885void PassNameParser::printOptionInfo(const llvm::cl::Option &opt,886 size_t globalWidth) const {887 // If this parser is just parsing pass names, print a simplified option888 // string.889 if (passNamesOnly) {890 llvm::outs() << " --" << opt.ArgStr << "=<pass-arg>";891 opt.printHelpStr(opt.HelpStr, globalWidth, opt.ArgStr.size() + 18);892 return;893 }894 895 // Print the information for the top-level option.896 if (opt.hasArgStr()) {897 llvm::outs() << " --" << opt.ArgStr;898 opt.printHelpStr(opt.HelpStr, globalWidth, opt.ArgStr.size() + 7);899 } else {900 llvm::outs() << " " << opt.HelpStr << '\n';901 }902 903 // Functor used to print the ordered entries of a registration map.904 auto printOrderedEntries = [&](StringRef header, auto &map) {905 llvm::SmallVector<PassRegistryEntry *, 32> orderedEntries;906 for (auto &kv : map)907 orderedEntries.push_back(&kv.second);908 llvm::array_pod_sort(909 orderedEntries.begin(), orderedEntries.end(),910 [](PassRegistryEntry *const *lhs, PassRegistryEntry *const *rhs) {911 return (*lhs)->getPassArgument().compare((*rhs)->getPassArgument());912 });913 914 llvm::outs().indent(4) << header << ":\n";915 for (PassRegistryEntry *entry : orderedEntries)916 entry->printHelpStr(/*indent=*/6, globalWidth);917 };918 919 // Print the available passes.920 printOrderedEntries("Passes", *passRegistry);921 922 // Print the available pass pipelines.923 if (!passPipelineRegistry->empty())924 printOrderedEntries("Pass Pipelines", *passPipelineRegistry);925}926 927size_t PassNameParser::getOptionWidth(const llvm::cl::Option &opt) const {928 size_t maxWidth = llvm::cl::parser<PassArgData>::getOptionWidth(opt) + 2;929 930 // Check for any wider pass or pipeline options.931 for (auto &entry : *passRegistry)932 maxWidth = std::max(maxWidth, entry.second.getOptionWidth() + 4);933 for (auto &entry : *passPipelineRegistry)934 maxWidth = std::max(maxWidth, entry.second.getOptionWidth() + 4);935 return maxWidth;936}937 938bool PassNameParser::parse(llvm::cl::Option &opt, StringRef argName,939 StringRef arg, PassArgData &value) {940 if (llvm::cl::parser<PassArgData>::parse(opt, argName, arg, value))941 return true;942 value.options = arg;943 return false;944}945 946//===----------------------------------------------------------------------===//947// PassPipelineCLParser948//===----------------------------------------------------------------------===//949 950namespace mlir {951namespace detail {952struct PassPipelineCLParserImpl {953 PassPipelineCLParserImpl(StringRef arg, StringRef description,954 bool passNamesOnly)955 : passList(arg, llvm::cl::desc(description)) {956 passList.getParser().passNamesOnly = passNamesOnly;957 passList.setValueExpectedFlag(llvm::cl::ValueExpected::ValueOptional);958 }959 960 /// Returns true if the given pass registry entry was registered at the961 /// top-level of the parser, i.e. not within an explicit textual pipeline.962 bool contains(const PassRegistryEntry *entry) const {963 return llvm::any_of(passList, [&](const PassArgData &data) {964 return data.registryEntry == entry;965 });966 }967 968 /// The set of passes and pass pipelines to run.969 llvm::cl::list<PassArgData, bool, PassNameParser> passList;970};971} // namespace detail972} // namespace mlir973 974/// Construct a pass pipeline parser with the given command line description.975PassPipelineCLParser::PassPipelineCLParser(StringRef arg, StringRef description)976 : impl(std::make_unique<detail::PassPipelineCLParserImpl>(977 arg, description, /*passNamesOnly=*/false)),978 passPipeline(979 PASS_PIPELINE_ARG,980 llvm::cl::desc("Textual description of the pass pipeline to run")) {}981 982PassPipelineCLParser::PassPipelineCLParser(StringRef arg, StringRef description,983 StringRef alias)984 : PassPipelineCLParser(arg, description) {985 passPipelineAlias.emplace(alias,986 llvm::cl::desc("Alias for --" PASS_PIPELINE_ARG),987 llvm::cl::aliasopt(passPipeline));988}989 990PassPipelineCLParser::~PassPipelineCLParser() = default;991 992/// Returns true if this parser contains any valid options to add.993bool PassPipelineCLParser::hasAnyOccurrences() const {994 return passPipeline.getNumOccurrences() != 0 ||995 impl->passList.getNumOccurrences() != 0;996}997 998/// Returns true if the given pass registry entry was registered at the999/// top-level of the parser, i.e. not within an explicit textual pipeline.1000bool PassPipelineCLParser::contains(const PassRegistryEntry *entry) const {1001 return impl->contains(entry);1002}1003 1004/// Adds the passes defined by this parser entry to the given pass manager.1005LogicalResult PassPipelineCLParser::addToPipeline(1006 OpPassManager &pm,1007 function_ref<LogicalResult(const Twine &)> errorHandler) const {1008 if (passPipeline.getNumOccurrences()) {1009 if (impl->passList.getNumOccurrences())1010 return errorHandler(1011 "'-" PASS_PIPELINE_ARG1012 "' option can't be used with individual pass options");1013 std::string errMsg;1014 llvm::raw_string_ostream os(errMsg);1015 FailureOr<OpPassManager> parsed = parsePassPipeline(passPipeline, os);1016 if (failed(parsed))1017 return errorHandler(errMsg);1018 pm = std::move(*parsed);1019 return success();1020 }1021 1022 for (auto &passIt : impl->passList) {1023 if (failed(passIt.registryEntry->addToPipeline(pm, passIt.options,1024 errorHandler)))1025 return failure();1026 }1027 return success();1028}1029 1030//===----------------------------------------------------------------------===//1031// PassNameCLParser1032//===----------------------------------------------------------------------===//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}1040PassNameCLParser::~PassNameCLParser() = default;1041 1042/// Returns true if this parser contains any valid options to add.1043bool PassNameCLParser::hasAnyOccurrences() const {1044 return impl->passList.getNumOccurrences() != 0;1045}1046 1047/// Returns true if the given pass registry entry was registered at the1048/// top-level of the parser, i.e. not within an explicit textual pipeline.1049bool PassNameCLParser::contains(const PassRegistryEntry *entry) const {1050 return impl->contains(entry);1051}1052