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/FunctionExtras.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/Support/CommandLine.h"
22#include "llvm/Support/Compiler.h"
23#include <memory>
24
25namespace mlir {
26class OpPassManager;
27
28namespace detail {
29namespace pass_options {
30/// Parse a string containing a list of comma-delimited elements, invoking the
31/// given parser for each sub-element and passing them to the provided
32/// element-append functor.
33LogicalResult
34parseCommaSeparatedList(llvm::cl::Option &opt, StringRef argName,
35 StringRef optionStr,
36 function_ref<LogicalResult(StringRef)> elementParseFn);
37template <typename ElementParser, typename ElementAppendFn>
38LogicalResult parseCommaSeparatedList(llvm::cl::Option &opt, StringRef argName,
39 StringRef optionStr,
40 ElementParser &elementParser,
41 ElementAppendFn &&appendFn) {
42 return parseCommaSeparatedList(
43 opt, argName, optionStr, [&](StringRef valueStr) {
44 typename ElementParser::parser_data_type value = {};
45 if (elementParser.parse(opt, argName, valueStr, value))
46 return failure();
47 appendFn(value);
48 return success();
49 });
50}
51
52/// Trait used to detect if a type has a operator<< method.
53template <typename T>
54using has_stream_operator_trait =
55 decltype(std::declval<raw_ostream &>() << std::declval<T>());
56template <typename T>
57using has_stream_operator = llvm::is_detected<has_stream_operator_trait, T>;
58
59/// Utility methods for printing option values.
60template <typename ParserT>
61static void printOptionValue(raw_ostream &os, const bool &value) {
62 os << (value ? StringRef("true") : StringRef("false"));
63}
64template <typename ParserT, typename DataT>
65static std::enable_if_t<has_stream_operator<DataT>::value>
66printOptionValue(raw_ostream &os, const DataT &value) {
67 os << value;
68}
69template <typename ParserT, typename DataT>
70static std::enable_if_t<!has_stream_operator<DataT>::value>
71printOptionValue(raw_ostream &os, const DataT &value) {
72 // If the value can't be streamed, fallback to checking for a print in the
73 // parser.
74 ParserT::print(os, value);
75}
76} // namespace pass_options
77
78/// Base container class and manager for all pass options.
79class PassOptions : protected llvm::cl::SubCommand {
80private:
81 /// This is the type-erased option base class. This provides some additional
82 /// hooks into the options that are not available via llvm::cl::Option.
83 class OptionBase {
84 public:
85 virtual ~OptionBase() = default;
86
87 /// Out of line virtual function to provide home for the class.
88 virtual void anchor();
89
90 /// Print the name and value of this option to the given stream.
91 virtual void print(raw_ostream &os) = 0;
92
93 /// Return the argument string of this option.
94 StringRef getArgStr() const { return getOption()->ArgStr; }
95
96 /// Returns true if this option has any value assigned to it.
97 bool hasValue() const { return optHasValue; }
98
99 protected:
100 /// Return the main option instance.
101 virtual const llvm::cl::Option *getOption() const = 0;
102
103 /// Copy the value from the given option into this one.
104 virtual void copyValueFrom(const OptionBase &other) = 0;
105
106 /// Flag indicating if this option has a value.
107 bool optHasValue = false;
108
109 /// Allow access to private methods.
110 friend PassOptions;
111 };
112
113 /// This is the parser that is used by pass options that use literal options.
114 /// This is a thin wrapper around the llvm::cl::parser, that exposes some
115 /// additional methods.
116 template <typename DataType>
117 struct GenericOptionParser : public llvm::cl::parser<DataType> {
118 using llvm::cl::parser<DataType>::parser;
119
120 /// Returns an argument name that maps to the specified value.
121 std::optional<StringRef> findArgStrForValue(const DataType &value) {
122 for (auto &it : this->Values)
123 if (it.V.compare(value))
124 return it.Name;
125 return std::nullopt;
126 }
127 };
128
129 /// Utility methods for printing option values.
130 template <typename DataT>
131 static void printValue(raw_ostream &os, GenericOptionParser<DataT> &parser,
132 const DataT &value) {
133 if (std::optional<StringRef> argStr = parser.findArgStrForValue(value))
134 os << *argStr;
135 else
136 llvm_unreachable("unknown data value for option");
137 }
138 template <typename DataT, typename ParserT>
139 static void printValue(raw_ostream &os, ParserT &parser, const DataT &value) {
140 detail::pass_options::printOptionValue<ParserT>(os, value);
141 }
142
143public:
144 /// The specific parser to use depending on llvm::cl parser used. This is only
145 /// necessary because we need to provide additional methods for certain data
146 /// type parsers.
147 /// TODO: We should upstream the methods in GenericOptionParser to avoid the
148 /// need to do this.
149 template <typename DataType>
150 using OptionParser =
151 std::conditional_t<std::is_base_of<llvm::cl::generic_parser_base,
152 llvm::cl::parser<DataType>>::value,
153 GenericOptionParser<DataType>,
154 llvm::cl::parser<DataType>>;
155
156 /// This class represents a specific pass option, with a provided data type.
157 template <typename DataType, typename OptionParser = OptionParser<DataType>>
158 class Option
159 : public llvm::cl::opt<DataType, /*ExternalStorage=*/false, OptionParser>,
160 public OptionBase {
161 public:
162 template <typename... Args>
163 Option(PassOptions &parent, StringRef arg, Args &&...args)
164 : llvm::cl::opt<DataType, /*ExternalStorage=*/false, OptionParser>(
165 arg, llvm::cl::sub(parent), std::forward<Args>(args)...) {
166 assert(!this->isPositional() && !this->isSink() &&
167 "sink and positional options are not supported");
168 parent.options.push_back(this);
169
170 // Set a callback to track if this option has a value.
171 this->setCallback([this](const auto &) { this->optHasValue = true; });
172 }
173 ~Option() override = default;
174 using llvm::cl::opt<DataType, /*ExternalStorage=*/false,
175 OptionParser>::operator=;
176 Option &operator=(const Option &other) {
177 *this = other.getValue();
178 return *this;
179 }
180
181 private:
182 /// Return the main option instance.
183 const llvm::cl::Option *getOption() const final { return this; }
184
185 /// Print the name and value of this option to the given stream.
186 void print(raw_ostream &os) final {
187 os << this->ArgStr << '=';
188 printValue(os, this->getParser(), this->getValue());
189 }
190
191 /// Copy the value from the given option into this one.
192 void copyValueFrom(const OptionBase &other) final {
193 this->setValue(static_cast<const Option<DataType, OptionParser> &>(other)
194 .getValue());
195 optHasValue = other.optHasValue;
196 }
197 };
198
199 /// This class represents a specific pass option that contains a list of
200 /// values of the provided data type. The elements within the textual form of
201 /// this option are parsed assuming they are comma-separated. Delimited
202 /// sub-ranges within individual elements of the list may contain commas that
203 /// are not treated as separators for the top-level list.
204 template <typename DataType, typename OptionParser = OptionParser<DataType>>
205 class ListOption
206 : public llvm::cl::list<DataType, /*StorageClass=*/bool, OptionParser>,
207 public OptionBase {
208 public:
209 template <typename... Args>
210 ListOption(PassOptions &parent, StringRef arg, Args &&...args)
211 : llvm::cl::list<DataType, /*StorageClass=*/bool, OptionParser>(
212 arg, llvm::cl::sub(parent), std::forward<Args>(args)...),
213 elementParser(*this) {
214 assert(!this->isPositional() && !this->isSink() &&
215 "sink and positional options are not supported");
216 assert(!(this->getMiscFlags() & llvm::cl::MiscFlags::CommaSeparated) &&
217 "ListOption is implicitly comma separated, specifying "
218 "CommaSeparated is extraneous");
219 parent.options.push_back(this);
220 elementParser.initialize();
221 }
222 ~ListOption() override = default;
223 ListOption<DataType, OptionParser> &
224 operator=(const ListOption<DataType, OptionParser> &other) {
225 *this = ArrayRef<DataType>(other);
226 this->optHasValue = other.optHasValue;
227 return *this;
228 }
229
230 bool handleOccurrence(unsigned pos, StringRef argName,
231 StringRef arg) override {
232 if (this->isDefaultAssigned()) {
233 this->clear();
234 this->overwriteDefault();
235 }
236 this->optHasValue = true;
237 return failed(detail::pass_options::parseCommaSeparatedList(
238 *this, argName, arg, elementParser,
239 [&](const DataType &value) { this->addValue(value); }));
240 }
241
242 /// Allow assigning from an ArrayRef.
243 ListOption<DataType, OptionParser> &operator=(ArrayRef<DataType> values) {
244 ((std::vector<DataType> &)*this).assign(values.begin(), values.end());
245 optHasValue = true;
246 return *this;
247 }
248
249 /// Allow accessing the data held by this option.
250 MutableArrayRef<DataType> operator*() {
251 return static_cast<std::vector<DataType> &>(*this);
252 }
253 ArrayRef<DataType> operator*() const {
254 return static_cast<const std::vector<DataType> &>(*this);
255 }
256
257 private:
258 /// Return the main option instance.
259 const llvm::cl::Option *getOption() const final { return this; }
260
261 /// Print the name and value of this option to the given stream.
262 void print(raw_ostream &os) final {
263 // Don't print the list if empty. An empty option value can be treated as
264 // an element of the list in certain cases (e.g. ListOption<std::string>).
265 if ((**this).empty())
266 return;
267
268 os << this->ArgStr << '=';
269 auto printElementFn = [&](const DataType &value) {
270 printValue(os, this->getParser(), value);
271 };
272 llvm::interleave(*this, os, printElementFn, ",");
273 }
274
275 /// Copy the value from the given option into this one.
276 void copyValueFrom(const OptionBase &other) final {
277 *this = static_cast<const ListOption<DataType, OptionParser> &>(other);
278 }
279
280 /// The parser to use for parsing the list elements.
281 OptionParser elementParser;
282 };
283
284 PassOptions() = default;
285 /// Delete the copy constructor to avoid copying the internal options map.
286 PassOptions(const PassOptions &) = delete;
287 PassOptions(PassOptions &&) = delete;
288
289 /// Copy the option values from 'other' into 'this', where 'other' has the
290 /// same options as 'this'.
291 void copyOptionValuesFrom(const PassOptions &other);
292
293 /// Parse options out as key=value pairs that can then be handed off to the
294 /// `llvm::cl` command line passing infrastructure. Everything is space
295 /// separated.
296 LogicalResult parseFromString(StringRef options,
297 raw_ostream &errorStream = llvm::errs());
298
299 /// Print the options held by this struct in a form that can be parsed via
300 /// 'parseFromString'.
301 void print(raw_ostream &os);
302
303 /// Print the help string for the options held by this struct. `descIndent` is
304 /// the indent that the descriptions should be aligned.
305 void printHelp(size_t indent, size_t descIndent) const;
306
307 /// Return the maximum width required when printing the help string.
308 size_t getOptionWidth() const;
309
310private:
311 /// A list of all of the opaque options.
312 std::vector<OptionBase *> options;
313};
314} // namespace detail
315
316//===----------------------------------------------------------------------===//
317// PassPipelineOptions
318//===----------------------------------------------------------------------===//
319
320/// Subclasses of PassPipelineOptions provide a set of options that can be used
321/// to initialize a pass pipeline. See PassPipelineRegistration for usage
322/// details.
323///
324/// Usage:
325///
326/// struct MyPipelineOptions : PassPipelineOptions<MyPassOptions> {
327/// ListOption<int> someListFlag{*this, "flag-name", llvm::cl::desc("...")};
328/// };
329template <typename T>
330class PassPipelineOptions : public detail::PassOptions {
331public:
332 /// Factory that parses the provided options and returns a unique_ptr to the
333 /// struct.
334 static std::unique_ptr<T> createFromString(StringRef options) {
335 auto result = std::make_unique<T>();
336 if (failed(result->parseFromString(options)))
337 return nullptr;
338 return result;
339 }
340};
341
342/// A default empty option struct to be used for passes that do not need to take
343/// any options.
344struct EmptyPipelineOptions : public PassPipelineOptions<EmptyPipelineOptions> {
345};
346} // namespace mlir
347
348//===----------------------------------------------------------------------===//
349// MLIR Options
350//===----------------------------------------------------------------------===//
351
352namespace llvm {
353namespace cl {
354//===----------------------------------------------------------------------===//
355// std::vector+SmallVector
356
357namespace detail {
358template <typename VectorT, typename ElementT>
359class VectorParserBase : public basic_parser_impl {
360public:
361 VectorParserBase(Option &opt) : basic_parser_impl(opt), elementParser(opt) {}
362
363 using parser_data_type = VectorT;
364
365 bool parse(Option &opt, StringRef argName, StringRef arg,
366 parser_data_type &vector) {
367 if (!arg.consume_front(Prefix: "[") || !arg.consume_back(Suffix: "]")) {
368 return opt.error(Message: "expected vector option to be wrapped with '[]'",
369 ArgName: argName);
370 }
371
372 return failed(mlir::detail::pass_options::parseCommaSeparatedList(
373 opt, argName, arg, elementParser,
374 [&](const ElementT &value) { vector.push_back(value); }));
375 }
376
377 static void print(raw_ostream &os, const VectorT &vector) {
378 llvm::interleave(
379 vector, os,
380 [&](const ElementT &value) {
381 mlir::detail::pass_options::printOptionValue<
382 llvm::cl::parser<ElementT>>(os, value);
383 },
384 ",");
385 }
386
387 void printOptionInfo(const Option &opt, size_t globalWidth) const {
388 // Add the `vector<>` qualifier to the option info.
389 outs() << " --" << opt.ArgStr;
390 outs() << "=<vector<" << elementParser.getValueName() << ">>";
391 Option::printHelpStr(HelpStr: opt.HelpStr, Indent: globalWidth, FirstLineIndentedBy: getOptionWidth(opt));
392 }
393
394 size_t getOptionWidth(const Option &opt) const {
395 // Add the `vector<>` qualifier to the option width.
396 StringRef vectorExt("vector<>");
397 return elementParser.getOptionWidth(opt) + vectorExt.size();
398 }
399
400private:
401 llvm::cl::parser<ElementT> elementParser;
402};
403} // namespace detail
404
405template <typename T>
406class parser<std::vector<T>>
407 : public detail::VectorParserBase<std::vector<T>, T> {
408public:
409 parser(Option &opt) : detail::VectorParserBase<std::vector<T>, T>(opt) {}
410};
411template <typename T, unsigned N>
412class parser<SmallVector<T, N>>
413 : public detail::VectorParserBase<SmallVector<T, N>, T> {
414public:
415 parser(Option &opt) : detail::VectorParserBase<SmallVector<T, N>, T>(opt) {}
416};
417
418//===----------------------------------------------------------------------===//
419// OpPassManager: OptionValue
420
421template <>
422struct OptionValue<mlir::OpPassManager> final : GenericOptionValue {
423 using WrapperType = mlir::OpPassManager;
424
425 OptionValue();
426 OptionValue(const OptionValue<mlir::OpPassManager> &rhs);
427 OptionValue(const mlir::OpPassManager &value);
428 OptionValue<mlir::OpPassManager> &operator=(const mlir::OpPassManager &rhs);
429 ~OptionValue();
430
431 /// Returns if the current option has a value.
432 bool hasValue() const { return value.get(); }
433
434 /// Returns the current value of the option.
435 mlir::OpPassManager &getValue() const {
436 assert(hasValue() && "invalid option value");
437 return *value;
438 }
439
440 /// Set the value of the option.
441 void setValue(const mlir::OpPassManager &newValue);
442 void setValue(StringRef pipelineStr);
443
444 /// Compare the option with the provided value.
445 bool compare(const mlir::OpPassManager &rhs) const;
446 bool compare(const GenericOptionValue &rhs) const override {
447 const auto &rhsOV =
448 static_cast<const OptionValue<mlir::OpPassManager> &>(rhs);
449 if (!rhsOV.hasValue())
450 return false;
451 return compare(rhs: rhsOV.getValue());
452 }
453
454private:
455 void anchor() override;
456
457 /// The underlying pass manager. We use a unique_ptr to avoid the need for the
458 /// full type definition.
459 std::unique_ptr<mlir::OpPassManager> value;
460};
461
462//===----------------------------------------------------------------------===//
463// OpPassManager: Parser
464
465extern template class basic_parser<mlir::OpPassManager>;
466
467template <>
468class parser<mlir::OpPassManager> : public basic_parser<mlir::OpPassManager> {
469public:
470 /// A utility struct used when parsing a pass manager that prevents the need
471 /// for a default constructor on OpPassManager.
472 struct ParsedPassManager {
473 ParsedPassManager();
474 ParsedPassManager(ParsedPassManager &&);
475 ~ParsedPassManager();
476 operator const mlir::OpPassManager &() const {
477 assert(value && "parsed value was invalid");
478 return *value;
479 }
480
481 std::unique_ptr<mlir::OpPassManager> value;
482 };
483 using parser_data_type = ParsedPassManager;
484 using OptVal = OptionValue<mlir::OpPassManager>;
485
486 parser(Option &opt) : basic_parser(opt) {}
487
488 bool parse(Option &, StringRef, StringRef arg, ParsedPassManager &value);
489
490 /// Print an instance of the underling option value to the given stream.
491 static void print(raw_ostream &os, const mlir::OpPassManager &value);
492
493 // Overload in subclass to provide a better default value.
494 StringRef getValueName() const override { return "pass-manager"; }
495
496 void printOptionDiff(const Option &opt, mlir::OpPassManager &pm,
497 const OptVal &defaultValue, size_t globalWidth) const;
498
499 // An out-of-line virtual method to provide a 'home' for this class.
500 void anchor() override;
501};
502
503} // namespace cl
504} // namespace llvm
505
506#endif // MLIR_PASS_PASSOPTIONS_H_
507

source code of mlir/include/mlir/Pass/PassOptions.h