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