• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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