xref: /llvm-project/mlir/include/mlir/IR/AsmState.h (revision b96ebee1fab2b281c97deb54f3d61c469fe07d01)
1 //===- AsmState.h - Assembly State 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 defines various classes and utilites for interacting with the MLIR
10 // assembly formats.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef MLIR_IR_ASMSTATE_H_
15 #define MLIR_IR_ASMSTATE_H_
16 
17 #include "mlir/Bytecode/BytecodeReaderConfig.h"
18 #include "mlir/IR/OperationSupport.h"
19 #include "mlir/Support/LLVM.h"
20 #include "llvm/ADT/MapVector.h"
21 #include "llvm/ADT/StringMap.h"
22 
23 #include <memory>
24 #include <variant>
25 
26 namespace mlir {
27 class AsmResourcePrinter;
28 class AsmDialectResourceHandle;
29 class Operation;
30 
31 namespace detail {
32 class AsmStateImpl;
33 } // namespace detail
34 
35 //===----------------------------------------------------------------------===//
36 // Resources
37 //===----------------------------------------------------------------------===//
38 
39 /// The following classes enable support for parsing and printing resources
40 /// within MLIR assembly formats. Resources are a mechanism by which dialects,
41 /// and external clients, may attach additional information when parsing or
42 /// printing IR without that information being encoded in the IR itself.
43 /// Resources are not uniqued within the MLIR context, are not attached directly
44 /// to any operation, and are solely intended to live and be processed outside
45 /// of the immediate IR.
46 ///
47 /// Resources are encoded using a key-value pair nested within dictionaries
48 /// anchored either on a dialect, or an externally registered entity.
49 /// Dictionaries anchored on dialects use the dialect namespace directly, and
50 /// dictionaries anchored on external entities use a provided unique identifier.
51 /// The resource key is an identifier used to disambiguate the data. The
52 /// resource value may be stored in various limited forms, but general encodings
53 /// use a string (human readable) or blob format (binary). Within the textual
54 /// format, an example may be of the form:
55 ///
56 /// {-#
57 ///   // The `dialect_resources` section within the file-level metadata
58 ///   // dictionary is used to contain any dialect resource entries.
59 ///   dialect_resources: {
60 ///     // Here is a dictionary anchored on "foo_dialect", which is a dialect
61 ///     // namespace.
62 ///     foo_dialect: {
63 ///       // `some_dialect_resource` is a key to be interpreted by the dialect,
64 ///       // and used to initialize/configure/etc.
65 ///       some_dialect_resource: "Some important resource value"
66 ///     }
67 ///   },
68 ///   // The `external_resources` section within the file-level metadata
69 ///   // dictionary is used to contain any non-dialect resource entries.
70 ///   external_resources: {
71 ///     // Here is a dictionary anchored on "mlir_reproducer", which is an
72 ///     // external entity representing MLIR's crash reproducer functionality.
73 ///     mlir_reproducer: {
74 ///       // `pipeline` is an entry that holds a crash reproducer pipeline
75 ///       // resource.
76 ///       pipeline: "func.func(canonicalize,cse)"
77 ///     }
78 ///   }
79 /// #-}
80 ///
81 
82 //===----------------------------------------------------------------------===//
83 // Resource Entry
84 
85 class HeapAsmResourceBlob;
86 
87 /// This class represents a processed binary blob of data. A resource blob is
88 /// essentially a collection of data, potentially mutable, with an associated
89 /// deleter function (used if the data needs to be destroyed).
90 class AsmResourceBlob {
91 public:
92   /// A deleter function that frees a blob given the data, allocation size, and
93   /// allocation aligment.
94   using DeleterFn =
95       llvm::unique_function<void(void *data, size_t size, size_t align)>;
96 
97   //===--------------------------------------------------------------------===//
98   // Construction
99   //===--------------------------------------------------------------------===//
100 
101   AsmResourceBlob() = default;
102   AsmResourceBlob(ArrayRef<char> data, size_t dataAlignment, DeleterFn deleter,
103                   bool dataIsMutable)
104       : data(data), dataAlignment(dataAlignment), deleter(std::move(deleter)),
105         dataIsMutable(dataIsMutable) {}
106   /// Utility constructor that initializes a blob with a non-char type T.
107   template <typename T, typename DelT>
108   AsmResourceBlob(ArrayRef<T> data, DelT &&deleteFn, bool dataIsMutable)
109       : data((const char *)data.data(), data.size() * sizeof(T)),
110         dataAlignment(alignof(T)),
111         deleter([deleteFn = std::forward<DelT>(deleteFn)](
112                     void *data, size_t size, size_t align) {
113           return deleteFn((T *)data, size, align);
114         }),
115         dataIsMutable(dataIsMutable) {}
116   AsmResourceBlob(AsmResourceBlob &&) = default;
117   AsmResourceBlob &operator=(AsmResourceBlob &&rhs) {
118     // Delete the current blob if necessary.
119     if (deleter)
120       deleter(const_cast<char *>(data.data()), data.size(), dataAlignment);
121 
122     // Take the data entries from rhs.
123     data = rhs.data;
124     dataAlignment = rhs.dataAlignment;
125     deleter = std::move(rhs.deleter);
126     dataIsMutable = rhs.dataIsMutable;
127     return *this;
128   }
129   AsmResourceBlob(const AsmResourceBlob &) = delete;
130   AsmResourceBlob &operator=(const AsmResourceBlob &) = delete;
131   ~AsmResourceBlob() {
132     if (deleter)
133       deleter(const_cast<char *>(data.data()), data.size(), dataAlignment);
134   }
135 
136   //===--------------------------------------------------------------------===//
137   // Data Access
138   //===--------------------------------------------------------------------===//
139 
140   /// Return the alignment of the underlying data.
141   size_t getDataAlignment() const { return dataAlignment; }
142 
143   /// Return the raw underlying data of this blob.
144   ArrayRef<char> getData() const { return data; }
145 
146   /// Return the underlying data as an array of the given type. This is an
147   /// inherrently unsafe operation, and should only be used when the data is
148   /// known to be of the correct type.
149   template <typename T>
150   ArrayRef<T> getDataAs() const {
151     return llvm::ArrayRef<T>((const T *)data.data(), data.size() / sizeof(T));
152   }
153 
154   /// Return a mutable reference to the raw underlying data of this blob.
155   /// Asserts that the blob `isMutable`.
156   MutableArrayRef<char> getMutableData() {
157     assert(isMutable() &&
158            "cannot access mutable reference to non-mutable data");
159     return MutableArrayRef<char>(const_cast<char *>(data.data()), data.size());
160   }
161 
162   /// Return if the data of this blob is mutable.
163   bool isMutable() const { return dataIsMutable; }
164 
165   /// Return the deleter function of this blob.
166   DeleterFn &getDeleter() { return deleter; }
167   const DeleterFn &getDeleter() const { return deleter; }
168 
169 private:
170   /// The raw, properly aligned, blob data.
171   ArrayRef<char> data;
172 
173   /// The alignment of the data.
174   size_t dataAlignment = 0;
175 
176   /// An optional deleter function used to deallocate the underlying data when
177   /// necessary.
178   DeleterFn deleter;
179 
180   /// Whether the data is mutable.
181   bool dataIsMutable;
182 
183   friend class HeapAsmResourceBlob;
184 };
185 
186 /// This class provides a simple utility wrapper for creating heap allocated
187 /// AsmResourceBlobs.
188 class HeapAsmResourceBlob {
189 public:
190   /// Create a new heap allocated blob with the given size and alignment.
191   /// `dataIsMutable` indicates if the allocated data can be mutated. By
192   /// default, we treat heap allocated blobs as mutable.
193   static AsmResourceBlob allocate(size_t size, size_t align,
194                                   bool dataIsMutable = true) {
195     return AsmResourceBlob(
196         ArrayRef<char>((char *)llvm::allocate_buffer(size, align), size), align,
197         llvm::deallocate_buffer, dataIsMutable);
198   }
199   /// Create a new heap allocated blob and copy the provided data into it.
200   static AsmResourceBlob allocateAndCopyWithAlign(ArrayRef<char> data,
201                                                   size_t align,
202                                                   bool dataIsMutable = true) {
203     // This sets the blob to be mutable initially to allow writing
204     // (getMutableData) below.
205     AsmResourceBlob blob = allocate(data.size(), align, /*dataIsMutable=*/true);
206     std::memcpy(blob.getMutableData().data(), data.data(), data.size());
207     blob.dataIsMutable = dataIsMutable;
208     return blob;
209   }
210   template <typename T>
211   static AsmResourceBlob allocateAndCopyInferAlign(ArrayRef<T> data,
212                                                    bool dataIsMutable = true) {
213     return allocateAndCopyWithAlign(
214         ArrayRef<char>((const char *)data.data(), data.size() * sizeof(T)),
215         alignof(T), dataIsMutable);
216   }
217 };
218 /// This class provides a simple utility wrapper for creating "unmanaged"
219 /// AsmResourceBlobs. The lifetime of the data provided to these blobs is
220 /// guaranteed to persist beyond the lifetime of this reference.
221 class UnmanagedAsmResourceBlob {
222 public:
223   /// Create a new unmanaged resource directly referencing the provided data.
224   /// `dataIsMutable` indicates if the allocated data can be mutated. By
225   /// default, we treat unmanaged blobs as immutable.
226   static AsmResourceBlob
227   allocateWithAlign(ArrayRef<char> data, size_t align,
228                     AsmResourceBlob::DeleterFn deleter = {},
229                     bool dataIsMutable = false) {
230     return AsmResourceBlob(data, align, std::move(deleter), dataIsMutable);
231   }
232   template <typename T>
233   static AsmResourceBlob
234   allocateInferAlign(ArrayRef<T> data, AsmResourceBlob::DeleterFn deleter = {},
235                      bool dataIsMutable = false) {
236     return allocateWithAlign(
237         ArrayRef<char>((const char *)data.data(), data.size() * sizeof(T)),
238         alignof(T), std::move(deleter), dataIsMutable);
239   }
240 };
241 
242 /// This class is used to build resource entries for use by the printer. Each
243 /// resource entry is represented using a key/value pair. The provided key must
244 /// be unique within the current context, which allows for a client to provide
245 /// resource entries without worrying about overlap with other clients.
246 class AsmResourceBuilder {
247 public:
248   virtual ~AsmResourceBuilder();
249 
250   /// Build a resource entry represented by the given bool.
251   virtual void buildBool(StringRef key, bool data) = 0;
252 
253   /// Build a resource entry represented by the given human-readable string
254   /// value.
255   virtual void buildString(StringRef key, StringRef data) = 0;
256 
257   /// Build an resource entry represented by the given binary blob data.
258   virtual void buildBlob(StringRef key, ArrayRef<char> data,
259                          uint32_t dataAlignment) = 0;
260   /// Build an resource entry represented by the given binary blob data. This is
261   /// a useful overload if the data type is known. Note that this does not
262   /// support `char` element types to avoid accidentally not providing the
263   /// expected alignment of data in situations that treat blobs generically.
264   template <typename T>
265   std::enable_if_t<!std::is_same<T, char>::value> buildBlob(StringRef key,
266                                                             ArrayRef<T> data) {
267     buildBlob(
268         key, ArrayRef<char>((const char *)data.data(), data.size() * sizeof(T)),
269         alignof(T));
270   }
271   /// Build an resource entry represented by the given resource blob. This is
272   /// a useful overload if a blob already exists in-memory.
273   void buildBlob(StringRef key, const AsmResourceBlob &blob) {
274     buildBlob(key, blob.getData(), blob.getDataAlignment());
275   }
276 };
277 
278 /// This enum represents the different kinds of resource values.
279 enum class AsmResourceEntryKind {
280   /// A blob of data with an accompanying alignment.
281   Blob,
282   /// A boolean value.
283   Bool,
284   /// A string value.
285   String,
286 };
287 StringRef toString(AsmResourceEntryKind kind);
288 
289 /// This class represents a single parsed resource entry.
290 class AsmParsedResourceEntry {
291 public:
292   virtual ~AsmParsedResourceEntry();
293 
294   /// Return the key of the resource entry.
295   virtual StringRef getKey() const = 0;
296 
297   /// Emit an error at the location of this entry.
298   virtual InFlightDiagnostic emitError() const = 0;
299 
300   /// Return the kind of this value.
301   virtual AsmResourceEntryKind getKind() const = 0;
302 
303   /// Parse the resource entry represented by a boolean. Returns failure if the
304   /// entry does not correspond to a bool.
305   virtual FailureOr<bool> parseAsBool() const = 0;
306 
307   /// Parse the resource entry represented by a human-readable string. Returns
308   /// failure if the entry does not correspond to a string.
309   virtual FailureOr<std::string> parseAsString() const = 0;
310 
311   /// An allocator function used to allocate memory for a blob when required.
312   /// The function is provided a size and alignment, and should return an
313   /// aligned allocation buffer.
314   using BlobAllocatorFn =
315       function_ref<AsmResourceBlob(size_t size, size_t align)>;
316 
317   /// Parse the resource entry represented by a binary blob. Returns failure if
318   /// the entry does not correspond to a blob. If the blob needed to be
319   /// allocated, the given allocator function is invoked.
320   virtual FailureOr<AsmResourceBlob>
321   parseAsBlob(BlobAllocatorFn allocator) const = 0;
322   /// Parse the resource entry represented by a binary blob using heap
323   /// allocation.
324   FailureOr<AsmResourceBlob> parseAsBlob() const {
325     return parseAsBlob([](size_t size, size_t align) {
326       return HeapAsmResourceBlob::allocate(size, align);
327     });
328   }
329 };
330 
331 //===----------------------------------------------------------------------===//
332 // Resource Parser/Printer
333 
334 /// This class represents an instance of a resource parser. This class should be
335 /// implemented by non-dialect clients that want to inject additional resources
336 /// into MLIR assembly formats.
337 class AsmResourceParser {
338 public:
339   /// Create a new parser with the given identifying name. This name uniquely
340   /// identifies the entries of this parser, and differentiates them from other
341   /// contexts.
342   AsmResourceParser(StringRef name) : name(name.str()) {}
343   virtual ~AsmResourceParser();
344 
345   /// Return the name of this parser.
346   StringRef getName() const { return name; }
347 
348   /// Parse the given resource entry. Returns failure if the key/data were not
349   /// valid, or could otherwise not be processed correctly. Any necessary errors
350   /// should be emitted with the provided entry.
351   virtual LogicalResult parseResource(AsmParsedResourceEntry &entry) = 0;
352 
353   /// Return a resource parser implemented via the given callable, whose form
354   /// should match that of `parseResource` above.
355   template <typename CallableT>
356   static std::unique_ptr<AsmResourceParser> fromCallable(StringRef name,
357                                                          CallableT &&parseFn) {
358     struct Processor : public AsmResourceParser {
359       Processor(StringRef name, CallableT &&parseFn)
360           : AsmResourceParser(name), parseFn(std::move(parseFn)) {}
361       LogicalResult parseResource(AsmParsedResourceEntry &entry) override {
362         return parseFn(entry);
363       }
364 
365       std::decay_t<CallableT> parseFn;
366     };
367     return std::make_unique<Processor>(name, std::forward<CallableT>(parseFn));
368   }
369 
370 private:
371   std::string name;
372 };
373 
374 /// This class represents an instance of a resource printer. This class should
375 /// be implemented by non-dialect clients that want to inject additional
376 /// resources into MLIR assembly formats.
377 class AsmResourcePrinter {
378 public:
379   /// Create a new printer with the given identifying name. This name uniquely
380   /// identifies the entries of this printer, and differentiates them from
381   /// other contexts.
382   AsmResourcePrinter(StringRef name) : name(name.str()) {}
383   virtual ~AsmResourcePrinter();
384 
385   /// Return the name of this printer.
386   StringRef getName() const { return name; }
387 
388   /// Build any resources to include during printing, utilizing the given
389   /// top-level root operation to help determine what information to include.
390   /// Provided data should be registered in the form of a key/data pair, to the
391   /// given builder.
392   virtual void buildResources(Operation *op,
393                               AsmResourceBuilder &builder) const = 0;
394 
395   /// Return a resource printer implemented via the given callable, whose form
396   /// should match that of `buildResources` above.
397   template <typename CallableT>
398   static std::unique_ptr<AsmResourcePrinter> fromCallable(StringRef name,
399                                                           CallableT &&printFn) {
400     struct Printer : public AsmResourcePrinter {
401       Printer(StringRef name, CallableT &&printFn)
402           : AsmResourcePrinter(name), printFn(std::move(printFn)) {}
403       void buildResources(Operation *op,
404                           AsmResourceBuilder &builder) const override {
405         printFn(op, builder);
406       }
407 
408       std::decay_t<CallableT> printFn;
409     };
410     return std::make_unique<Printer>(name, std::forward<CallableT>(printFn));
411   }
412 
413 private:
414   std::string name;
415 };
416 
417 /// A fallback map containing external resources not explicitly handled by
418 /// another parser/printer.
419 class FallbackAsmResourceMap {
420 public:
421   /// This class represents an opaque resource.
422   struct OpaqueAsmResource {
423     OpaqueAsmResource(StringRef key,
424                       std::variant<AsmResourceBlob, bool, std::string> value)
425         : key(key.str()), value(std::move(value)) {}
426 
427     /// The key identifying the resource.
428     std::string key;
429     /// An opaque value for the resource, whose variant values align 1-1 with
430     /// the kinds defined in AsmResourceEntryKind.
431     std::variant<AsmResourceBlob, bool, std::string> value;
432   };
433 
434   /// Return a parser than can be used for parsing entries for the given
435   /// identifier key.
436   AsmResourceParser &getParserFor(StringRef key);
437 
438   /// Build a set of resource printers to print the resources within this map.
439   std::vector<std::unique_ptr<AsmResourcePrinter>> getPrinters();
440 
441 private:
442   struct ResourceCollection : public AsmResourceParser {
443     ResourceCollection(StringRef name) : AsmResourceParser(name) {}
444 
445     /// Parse a resource into this collection.
446     LogicalResult parseResource(AsmParsedResourceEntry &entry) final;
447 
448     /// Build the resources held by this collection.
449     void buildResources(Operation *op, AsmResourceBuilder &builder) const;
450 
451     /// The set of resources parsed into this collection.
452     SmallVector<OpaqueAsmResource> resources;
453   };
454 
455   /// The set of opaque resources.
456   llvm::MapVector<std::string, std::unique_ptr<ResourceCollection>,
457                   llvm::StringMap<unsigned>>
458       keyToResources;
459 };
460 
461 //===----------------------------------------------------------------------===//
462 // ParserConfig
463 //===----------------------------------------------------------------------===//
464 
465 /// This class represents a configuration for the MLIR assembly parser. It
466 /// contains all of the necessary state to parse a MLIR source file.
467 class ParserConfig {
468 public:
469   /// Construct a parser configuration with the given context.
470   /// `verifyAfterParse` indicates if the IR should be verified after parsing.
471   /// `fallbackResourceMap` is an optional fallback handler that can be used to
472   /// parse external resources not explicitly handled by another parser.
473   ParserConfig(MLIRContext *context, bool verifyAfterParse = true,
474                FallbackAsmResourceMap *fallbackResourceMap = nullptr)
475       : context(context), verifyAfterParse(verifyAfterParse),
476         fallbackResourceMap(fallbackResourceMap) {
477     assert(context && "expected valid MLIR context");
478   }
479 
480   /// Return the MLIRContext to be used when parsing.
481   MLIRContext *getContext() const { return context; }
482 
483   /// Returns if the parser should verify the IR after parsing.
484   bool shouldVerifyAfterParse() const { return verifyAfterParse; }
485 
486   /// Returns the parsing configurations associated to the bytecode read.
487   BytecodeReaderConfig &getBytecodeReaderConfig() const {
488     return const_cast<BytecodeReaderConfig &>(bytecodeReaderConfig);
489   }
490 
491   /// Return the resource parser registered to the given name, or nullptr if no
492   /// parser with `name` is registered.
493   AsmResourceParser *getResourceParser(StringRef name) const {
494     auto it = resourceParsers.find(name);
495     if (it != resourceParsers.end())
496       return it->second.get();
497     if (fallbackResourceMap)
498       return &fallbackResourceMap->getParserFor(name);
499     return nullptr;
500   }
501 
502   /// Attach the given resource parser.
503   void attachResourceParser(std::unique_ptr<AsmResourceParser> parser) {
504     StringRef name = parser->getName();
505     auto it = resourceParsers.try_emplace(name, std::move(parser));
506     (void)it;
507     assert(it.second &&
508            "resource parser already registered with the given name");
509   }
510 
511   /// Attach the given callable resource parser with the given name.
512   template <typename CallableT>
513   std::enable_if_t<std::is_convertible<
514       CallableT, function_ref<LogicalResult(AsmParsedResourceEntry &)>>::value>
515   attachResourceParser(StringRef name, CallableT &&parserFn) {
516     attachResourceParser(AsmResourceParser::fromCallable(
517         name, std::forward<CallableT>(parserFn)));
518   }
519 
520 private:
521   MLIRContext *context;
522   bool verifyAfterParse;
523   DenseMap<StringRef, std::unique_ptr<AsmResourceParser>> resourceParsers;
524   FallbackAsmResourceMap *fallbackResourceMap;
525   BytecodeReaderConfig bytecodeReaderConfig;
526 };
527 
528 //===----------------------------------------------------------------------===//
529 // AsmState
530 //===----------------------------------------------------------------------===//
531 
532 /// This class provides management for the lifetime of the state used when
533 /// printing the IR. It allows for alleviating the cost of recomputing the
534 /// internal state of the asm printer.
535 ///
536 /// The IR should not be mutated in-between invocations using this state, and
537 /// the IR being printed must not be an parent of the IR originally used to
538 /// initialize this state. This means that if a child operation is provided, a
539 /// parent operation cannot reuse this state.
540 class AsmState {
541 public:
542   /// This map represents the raw locations of operations within the output
543   /// stream. This maps the original pointer to the operation, to a pair of line
544   /// and column in the output stream.
545   using LocationMap = DenseMap<Operation *, std::pair<unsigned, unsigned>>;
546 
547   /// Initialize the asm state at the level of the given operation. A location
548   /// map may optionally be provided to be populated when printing. `map` is an
549   /// optional fallback resource map, which when provided will attach resource
550   /// printers for the fallback resources within the map.
551   AsmState(Operation *op,
552            const OpPrintingFlags &printerFlags = OpPrintingFlags(),
553            LocationMap *locationMap = nullptr,
554            FallbackAsmResourceMap *map = nullptr);
555   AsmState(MLIRContext *ctx,
556            const OpPrintingFlags &printerFlags = OpPrintingFlags(),
557            LocationMap *locationMap = nullptr,
558            FallbackAsmResourceMap *map = nullptr);
559   ~AsmState();
560 
561   /// Get the printer flags.
562   const OpPrintingFlags &getPrinterFlags() const;
563 
564   /// Return an instance of the internal implementation. Returns nullptr if the
565   /// state has not been initialized.
566   detail::AsmStateImpl &getImpl() { return *impl; }
567 
568   //===--------------------------------------------------------------------===//
569   // Resources
570   //===--------------------------------------------------------------------===//
571 
572   /// Attach the given resource printer to the AsmState.
573   void attachResourcePrinter(std::unique_ptr<AsmResourcePrinter> printer);
574 
575   /// Attach an resource printer, in the form of a callable, to the AsmState.
576   template <typename CallableT>
577   std::enable_if_t<std::is_convertible<
578       CallableT, function_ref<void(Operation *, AsmResourceBuilder &)>>::value>
579   attachResourcePrinter(StringRef name, CallableT &&printFn) {
580     attachResourcePrinter(AsmResourcePrinter::fromCallable(
581         name, std::forward<CallableT>(printFn)));
582   }
583 
584   /// Attach resource printers to the AsmState for the fallback resources
585   /// in the given map.
586   void attachFallbackResourcePrinter(FallbackAsmResourceMap &map) {
587     for (auto &printer : map.getPrinters())
588       attachResourcePrinter(std::move(printer));
589   }
590 
591   /// Returns a map of dialect resources that were referenced when using this
592   /// state to print IR.
593   DenseMap<Dialect *, SetVector<AsmDialectResourceHandle>> &
594   getDialectResources() const;
595 
596 private:
597   AsmState() = delete;
598 
599   /// A pointer to allocated storage for the impl state.
600   std::unique_ptr<detail::AsmStateImpl> impl;
601 };
602 
603 //===----------------------------------------------------------------------===//
604 // AsmPrinter CommandLine Options
605 //===----------------------------------------------------------------------===//
606 
607 /// Register a set of useful command-line options that can be used to configure
608 /// various flags within the AsmPrinter.
609 void registerAsmPrinterCLOptions();
610 
611 } // namespace mlir
612 
613 #endif // MLIR_IR_ASMSTATE_H_
614