1 //===- PassOptions.h - Pass Option Utilities --------------------*- C++ -*-===// 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 // This file contains utilities for registering options with compiler passes and 10 // pipelines. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef MLIR_PASS_PASSOPTIONS_H_ 15 #define MLIR_PASS_PASSOPTIONS_H_ 16 17 #include "mlir/Support/LLVM.h" 18 #include "mlir/Support/LogicalResult.h" 19 #include "llvm/ADT/StringRef.h" 20 #include "llvm/Support/CommandLine.h" 21 #include "llvm/Support/Compiler.h" 22 #include <memory> 23 24 namespace mlir { 25 namespace detail { 26 /// Base container class and manager for all pass options. 27 class PassOptions : protected llvm::cl::SubCommand { 28 private: 29 /// This is the type-erased option base class. This provides some additional 30 /// hooks into the options that are not available via llvm::cl::Option. 31 class OptionBase { 32 public: 33 virtual ~OptionBase() = default; 34 35 /// Out of line virtual function to provide home for the class. 36 virtual void anchor(); 37 38 /// Print the name and value of this option to the given stream. 39 virtual void print(raw_ostream &os) = 0; 40 41 /// Return the argument string of this option. getArgStr()42 StringRef getArgStr() const { return getOption()->ArgStr; } 43 44 /// Returns true if this option has any value assigned to it. hasValue()45 bool hasValue() const { return optHasValue; } 46 47 protected: 48 /// Return the main option instance. 49 virtual const llvm::cl::Option *getOption() const = 0; 50 51 /// Copy the value from the given option into this one. 52 virtual void copyValueFrom(const OptionBase &other) = 0; 53 54 /// Flag indicating if this option has a value. 55 bool optHasValue = false; 56 57 /// Allow access to private methods. 58 friend PassOptions; 59 }; 60 61 /// This is the parser that is used by pass options that use literal options. 62 /// This is a thin wrapper around the llvm::cl::parser, that exposes some 63 /// additional methods. 64 template <typename DataType> 65 struct GenericOptionParser : public llvm::cl::parser<DataType> { 66 using llvm::cl::parser<DataType>::parser; 67 68 /// Returns an argument name that maps to the specified value. findArgStrForValueGenericOptionParser69 Optional<StringRef> findArgStrForValue(const DataType &value) { 70 for (auto &it : this->Values) 71 if (it.V.compare(value)) 72 return it.Name; 73 return llvm::None; 74 } 75 }; 76 77 /// Utility methods for printing option values. 78 template <typename DataT> printValue(raw_ostream & os,GenericOptionParser<DataT> & parser,const DataT & value)79 static void printValue(raw_ostream &os, GenericOptionParser<DataT> &parser, 80 const DataT &value) { 81 if (Optional<StringRef> argStr = parser.findArgStrForValue(value)) 82 os << argStr; 83 else 84 llvm_unreachable("unknown data value for option"); 85 } 86 template <typename DataT, typename ParserT> printValue(raw_ostream & os,ParserT & parser,const DataT & value)87 static void printValue(raw_ostream &os, ParserT &parser, const DataT &value) { 88 os << value; 89 } 90 template <typename ParserT> printValue(raw_ostream & os,ParserT & parser,const bool & value)91 static void printValue(raw_ostream &os, ParserT &parser, const bool &value) { 92 os << (value ? StringRef("true") : StringRef("false")); 93 } 94 95 public: 96 /// The specific parser to use depending on llvm::cl parser used. This is only 97 /// necessary because we need to provide additional methods for certain data 98 /// type parsers. 99 /// TODO: We should upstream the methods in GenericOptionParser to avoid the 100 /// need to do this. 101 template <typename DataType> 102 using OptionParser = 103 std::conditional_t<std::is_base_of<llvm::cl::generic_parser_base, 104 llvm::cl::parser<DataType>>::value, 105 GenericOptionParser<DataType>, 106 llvm::cl::parser<DataType>>; 107 108 /// This class represents a specific pass option, with a provided data type. 109 template <typename DataType, typename OptionParser = OptionParser<DataType>> 110 class Option 111 : public llvm::cl::opt<DataType, /*ExternalStorage=*/false, OptionParser>, 112 public OptionBase { 113 public: 114 template <typename... Args> Option(PassOptions & parent,StringRef arg,Args &&...args)115 Option(PassOptions &parent, StringRef arg, Args &&... args) 116 : llvm::cl::opt<DataType, /*ExternalStorage=*/false, OptionParser>( 117 arg, llvm::cl::sub(parent), std::forward<Args>(args)...) { 118 assert(!this->isPositional() && !this->isSink() && 119 "sink and positional options are not supported"); 120 parent.options.push_back(this); 121 122 // Set a callback to track if this option has a value. 123 this->setCallback([this](const auto &) { this->optHasValue = true; }); 124 } 125 ~Option() override = default; 126 using llvm::cl::opt<DataType, /*ExternalStorage=*/false, 127 OptionParser>::operator=; 128 Option &operator=(const Option &other) { 129 *this = other.getValue(); 130 return *this; 131 } 132 133 private: 134 /// Return the main option instance. getOption()135 const llvm::cl::Option *getOption() const final { return this; } 136 137 /// Print the name and value of this option to the given stream. print(raw_ostream & os)138 void print(raw_ostream &os) final { 139 os << this->ArgStr << '='; 140 printValue(os, this->getParser(), this->getValue()); 141 } 142 143 /// Copy the value from the given option into this one. copyValueFrom(const OptionBase & other)144 void copyValueFrom(const OptionBase &other) final { 145 this->setValue(static_cast<const Option<DataType, OptionParser> &>(other) 146 .getValue()); 147 optHasValue = other.optHasValue; 148 } 149 }; 150 151 /// This class represents a specific pass option that contains a list of 152 /// values of the provided data type. 153 template <typename DataType, typename OptionParser = OptionParser<DataType>> 154 class ListOption 155 : public llvm::cl::list<DataType, /*StorageClass=*/bool, OptionParser>, 156 public OptionBase { 157 public: 158 template <typename... Args> ListOption(PassOptions & parent,StringRef arg,Args &&...args)159 ListOption(PassOptions &parent, StringRef arg, Args &&... args) 160 : llvm::cl::list<DataType, /*StorageClass=*/bool, OptionParser>( 161 arg, llvm::cl::sub(parent), std::forward<Args>(args)...) { 162 assert(!this->isPositional() && !this->isSink() && 163 "sink and positional options are not supported"); 164 parent.options.push_back(this); 165 166 // Set a callback to track if this option has a value. 167 this->setCallback([this](const auto &) { this->optHasValue = true; }); 168 } 169 ~ListOption() override = default; 170 ListOption<DataType, OptionParser> & 171 operator=(const ListOption<DataType, OptionParser> &other) { 172 *this = ArrayRef<DataType>(other); 173 this->optHasValue = other.optHasValue; 174 return *this; 175 } 176 177 /// Allow assigning from an ArrayRef. 178 ListOption<DataType, OptionParser> &operator=(ArrayRef<DataType> values) { 179 ((std::vector<DataType> &)*this).assign(values.begin(), values.end()); 180 optHasValue = true; 181 return *this; 182 } 183 184 MutableArrayRef<DataType> operator->() const { return &*this; } 185 186 private: 187 /// Return the main option instance. getOption()188 const llvm::cl::Option *getOption() const final { return this; } 189 190 /// Print the name and value of this option to the given stream. print(raw_ostream & os)191 void print(raw_ostream &os) final { 192 os << this->ArgStr << '='; 193 auto printElementFn = [&](const DataType &value) { 194 printValue(os, this->getParser(), value); 195 }; 196 llvm::interleave(*this, os, printElementFn, ","); 197 } 198 199 /// Copy the value from the given option into this one. copyValueFrom(const OptionBase & other)200 void copyValueFrom(const OptionBase &other) final { 201 *this = static_cast<const ListOption<DataType, OptionParser> &>(other); 202 } 203 }; 204 205 PassOptions() = default; 206 /// Delete the copy constructor to avoid copying the internal options map. 207 PassOptions(const PassOptions &) = delete; 208 PassOptions(PassOptions &&) = delete; 209 210 /// Copy the option values from 'other' into 'this', where 'other' has the 211 /// same options as 'this'. 212 void copyOptionValuesFrom(const PassOptions &other); 213 214 /// Parse options out as key=value pairs that can then be handed off to the 215 /// `llvm::cl` command line passing infrastructure. Everything is space 216 /// separated. 217 LogicalResult parseFromString(StringRef options); 218 219 /// Print the options held by this struct in a form that can be parsed via 220 /// 'parseFromString'. 221 void print(raw_ostream &os); 222 223 /// Print the help string for the options held by this struct. `descIndent` is 224 /// the indent that the descriptions should be aligned. 225 void printHelp(size_t indent, size_t descIndent) const; 226 227 /// Return the maximum width required when printing the help string. 228 size_t getOptionWidth() const; 229 230 private: 231 /// A list of all of the opaque options. 232 std::vector<OptionBase *> options; 233 }; 234 } // end namespace detail 235 236 //===----------------------------------------------------------------------===// 237 // PassPipelineOptions 238 //===----------------------------------------------------------------------===// 239 240 /// Subclasses of PassPipelineOptions provide a set of options that can be used 241 /// to initialize a pass pipeline. See PassPipelineRegistration for usage 242 /// details. 243 /// 244 /// Usage: 245 /// 246 /// struct MyPipelineOptions : PassPipelineOptions<MyPassOptions> { 247 /// ListOption<int> someListFlag{ 248 /// *this, "flag-name", llvm::cl::MiscFlags::CommaSeparated, 249 /// llvm::cl::desc("...")}; 250 /// }; 251 template <typename T> class PassPipelineOptions : public detail::PassOptions { 252 public: 253 /// Factory that parses the provided options and returns a unique_ptr to the 254 /// struct. createFromString(StringRef options)255 static std::unique_ptr<T> createFromString(StringRef options) { 256 auto result = std::make_unique<T>(); 257 if (failed(result->parseFromString(options))) 258 return nullptr; 259 return result; 260 } 261 }; 262 263 /// A default empty option struct to be used for passes that do not need to take 264 /// any options. 265 struct EmptyPipelineOptions : public PassPipelineOptions<EmptyPipelineOptions> { 266 }; 267 268 } // end namespace mlir 269 270 #endif // MLIR_PASS_PASSOPTIONS_H_ 271 272