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/IR/OperationSupport.h" 18 #include "mlir/Support/LLVM.h" 19 20 #include <memory> 21 22 namespace mlir { 23 class AsmResourcePrinter; 24 class Operation; 25 26 namespace detail { 27 class AsmStateImpl; 28 } // namespace detail 29 30 //===----------------------------------------------------------------------===// 31 // Resources 32 //===----------------------------------------------------------------------===// 33 34 /// The following classes enable support for parsing and printing resources 35 /// within MLIR assembly formats. Resources are a mechanism by which dialects, 36 /// and external clients, may attach additional information when parsing or 37 /// printing IR without that information being encoded in the IR itself. 38 /// Resources are not uniqued within the MLIR context, are not attached directly 39 /// to any operation, and are solely intended to live and be processed outside 40 /// of the immediate IR. 41 /// 42 /// Resources are encoded using a key-value pair nested within dictionaries 43 /// anchored either on a dialect, or an externally registered entity. 44 /// Dictionaries anchored on dialects use the dialect namespace directly, and 45 /// dictionaries anchored on external entities use a provided unique identifier. 46 /// The resource key is an identifier used to disambiguate the data. The 47 /// resource value may be stored in various limited forms, but general encodings 48 /// use a string (human readable) or blob format (binary). Within the textual 49 /// format, an example may be of the form: 50 /// 51 /// {-# 52 /// // The `dialect_resources` section within the file-level metadata 53 /// // dictionary is used to contain any dialect resource entries. 54 /// dialect_resources: { 55 /// // Here is a dictionary anchored on "foo_dialect", which is a dialect 56 /// // namespace. 57 /// foo_dialect: { 58 /// // `some_dialect_resource` is a key to be interpreted by the dialect, 59 /// // and used to initialize/configure/etc. 60 /// some_dialect_resource: "Some important resource value" 61 /// } 62 /// }, 63 /// // The `external_resources` section within the file-level metadata 64 /// // dictionary is used to contain any non-dialect resource entries. 65 /// external_resources: { 66 /// // Here is a dictionary anchored on "mlir_reproducer", which is an 67 /// // external entity representing MLIR's crash reproducer functionality. 68 /// mlir_reproducer: { 69 /// // `pipeline` is an entry that holds a crash reproducer pipeline 70 /// // resource. 71 /// pipeline: "func.func(canonicalize,cse)" 72 /// } 73 /// } 74 /// #-} 75 /// 76 77 //===----------------------------------------------------------------------===// 78 // Resource Entry 79 80 /// This class is used to build resource entries for use by the printer. Each 81 /// resource entry is represented using a key/value pair. The provided key must 82 /// be unique within the current context, which allows for a client to provide 83 /// resource entries without worrying about overlap with other clients. 84 class AsmResourceBuilder { 85 public: 86 virtual ~AsmResourceBuilder(); 87 88 /// Build a resource entry represented by the given bool. 89 virtual void buildBool(StringRef key, bool data) = 0; 90 91 /// Build a resource entry represented by the given human-readable string 92 /// value. 93 virtual void buildString(StringRef key, StringRef data) = 0; 94 95 /// Build an resource entry represented by the given binary blob data. 96 virtual void buildBlob(StringRef key, ArrayRef<char> data, 97 uint32_t dataAlignment) = 0; 98 /// Build an resource entry represented by the given binary blob data. This is 99 /// a useful overload if the data type is known. Note that this does not 100 /// support `char` element types to avoid accidentally not providing the 101 /// expected alignment of data in situations that treat blobs generically. 102 template <typename T> buildBlob(StringRef key,ArrayRef<T> data)103 std::enable_if_t<!std::is_same<T, char>::value> buildBlob(StringRef key, 104 ArrayRef<T> data) { 105 buildBlob( 106 key, ArrayRef<char>((const char *)data.data(), data.size() * sizeof(T)), 107 alignof(T)); 108 } 109 }; 110 111 /// This class represents a processed binary blob of data. A resource blob is 112 /// essentially a collection of data, potentially mutable, with an associated 113 /// deleter function (used if the data needs to be destroyed). 114 class AsmResourceBlob { 115 public: 116 /// A deleter function that frees a blob given the data and allocation size. 117 using DeleterFn = llvm::unique_function<void(const void *data, size_t size)>; 118 119 AsmResourceBlob() = default; AsmResourceBlob(ArrayRef<char> data,DeleterFn deleter,bool dataIsMutable)120 AsmResourceBlob(ArrayRef<char> data, DeleterFn deleter, bool dataIsMutable) 121 : data(data), deleter(std::move(deleter)), dataIsMutable(dataIsMutable) {} 122 /// Utility constructor that initializes a blob with a non-char type T. 123 template <typename T, typename DelT> AsmResourceBlob(ArrayRef<T> data,DelT && deleteFn,bool dataIsMutable)124 AsmResourceBlob(ArrayRef<T> data, DelT &&deleteFn, bool dataIsMutable) 125 : data((const char *)data.data(), data.size() * sizeof(T)), 126 deleter([deleteFn = std::forward<DelT>(deleteFn)](const void *data, 127 size_t size) { 128 return deleteFn((const T *)data, size); 129 }), 130 dataIsMutable(dataIsMutable) {} 131 AsmResourceBlob(AsmResourceBlob &&) = default; 132 AsmResourceBlob &operator=(AsmResourceBlob &&) = default; 133 AsmResourceBlob(const AsmResourceBlob &) = delete; 134 AsmResourceBlob &operator=(const AsmResourceBlob &) = delete; ~AsmResourceBlob()135 ~AsmResourceBlob() { 136 if (deleter) 137 deleter(data.data(), data.size()); 138 } 139 140 /// Return the raw underlying data of this blob. getData()141 ArrayRef<char> getData() const { return data; } 142 143 /// Return a mutable reference to the raw underlying data of this blob. 144 /// Asserts that the blob `isMutable`. getMutableData()145 MutableArrayRef<char> getMutableData() { 146 assert(isMutable() && 147 "cannot access mutable reference to non-mutable data"); 148 return MutableArrayRef<char>(const_cast<char *>(data.data()), data.size()); 149 } 150 151 /// Return if the data of this blob is mutable. isMutable()152 bool isMutable() const { return dataIsMutable; } 153 154 /// Return the deleter function of this blob. getDeleter()155 DeleterFn &getDeleter() { return deleter; } getDeleter()156 const DeleterFn &getDeleter() const { return deleter; } 157 158 private: 159 /// The raw, properly aligned, blob data. 160 ArrayRef<char> data; 161 162 /// An optional deleter function used to deallocate the underlying data when 163 /// necessary. 164 DeleterFn deleter; 165 166 /// Whether the data is mutable. 167 bool dataIsMutable; 168 }; 169 170 /// This class represents a single parsed resource entry. 171 class AsmParsedResourceEntry { 172 public: 173 virtual ~AsmParsedResourceEntry(); 174 175 /// Return the key of the resource entry. 176 virtual StringRef getKey() const = 0; 177 178 /// Emit an error at the location of this entry. 179 virtual InFlightDiagnostic emitError() const = 0; 180 181 /// Parse the resource entry represented by a boolean. Returns failure if the 182 /// entry does not correspond to a bool. 183 virtual FailureOr<bool> parseAsBool() const = 0; 184 185 /// Parse the resource entry represented by a human-readable string. Returns 186 /// failure if the entry does not correspond to a string. 187 virtual FailureOr<std::string> parseAsString() const = 0; 188 189 /// The type of an allocator function used to allocate memory for a blob when 190 /// required. The function is provided a size and alignment, and should return 191 /// an aligned allocation buffer. 192 using BlobAllocatorFn = 193 function_ref<AsmResourceBlob(unsigned size, unsigned align)>; 194 195 /// Parse the resource entry represented by a binary blob. Returns failure if 196 /// the entry does not correspond to a blob. If the blob needed to be 197 /// allocated, the given allocator function is invoked. 198 virtual FailureOr<AsmResourceBlob> 199 parseAsBlob(BlobAllocatorFn allocator) const = 0; 200 }; 201 202 //===----------------------------------------------------------------------===// 203 // Resource Parser/Printer 204 205 /// This class represents an instance of a resource parser. This class should be 206 /// implemented by non-dialect clients that want to inject additional resources 207 /// into MLIR assembly formats. 208 class AsmResourceParser { 209 public: 210 /// Create a new parser with the given identifying name. This name uniquely 211 /// identifies the entries of this parser, and differentiates them from other 212 /// contexts. AsmResourceParser(StringRef name)213 AsmResourceParser(StringRef name) : name(name.str()) {} 214 virtual ~AsmResourceParser(); 215 216 /// Return the name of this parser. getName()217 StringRef getName() const { return name; } 218 219 /// Parse the given resource entry. Returns failure if the key/data were not 220 /// valid, or could otherwise not be processed correctly. Any necessary errors 221 /// should be emitted with the provided entry. 222 virtual LogicalResult parseResource(AsmParsedResourceEntry &entry) = 0; 223 224 /// Return a resource parser implemented via the given callable, whose form 225 /// should match that of `parseResource` above. 226 template <typename CallableT> fromCallable(StringRef name,CallableT && parseFn)227 static std::unique_ptr<AsmResourceParser> fromCallable(StringRef name, 228 CallableT &&parseFn) { 229 struct Processor : public AsmResourceParser { 230 Processor(StringRef name, CallableT &&parseFn) 231 : AsmResourceParser(name), parseFn(std::move(parseFn)) {} 232 LogicalResult parseResource(AsmParsedResourceEntry &entry) override { 233 return parseFn(entry); 234 } 235 236 std::decay_t<CallableT> parseFn; 237 }; 238 return std::make_unique<Processor>(name, std::forward<CallableT>(parseFn)); 239 } 240 241 private: 242 std::string name; 243 }; 244 245 /// This class represents an instance of a resource printer. This class should 246 /// be implemented by non-dialect clients that want to inject additional 247 /// resources into MLIR assembly formats. 248 class AsmResourcePrinter { 249 public: 250 /// Create a new printer with the given identifying name. This name uniquely 251 /// identifies the entries of this printer, and differentiates them from 252 /// other contexts. AsmResourcePrinter(StringRef name)253 AsmResourcePrinter(StringRef name) : name(name.str()) {} 254 virtual ~AsmResourcePrinter(); 255 256 /// Return the name of this printer. getName()257 StringRef getName() const { return name; } 258 259 /// Build any resources to include during printing, utilizing the given 260 /// top-level root operation to help determine what information to include. 261 /// Provided data should be registered in the form of a key/data pair, to the 262 /// given builder. 263 virtual void buildResources(Operation *op, 264 AsmResourceBuilder &builder) const = 0; 265 266 /// Return a resource printer implemented via the given callable, whose form 267 /// should match that of `buildResources` above. 268 template <typename CallableT> fromCallable(StringRef name,CallableT && printFn)269 static std::unique_ptr<AsmResourcePrinter> fromCallable(StringRef name, 270 CallableT &&printFn) { 271 struct Printer : public AsmResourcePrinter { 272 Printer(StringRef name, CallableT &&printFn) 273 : AsmResourcePrinter(name), printFn(std::move(printFn)) {} 274 void buildResources(Operation *op, 275 AsmResourceBuilder &builder) const override { 276 printFn(op, builder); 277 } 278 279 std::decay_t<CallableT> printFn; 280 }; 281 return std::make_unique<Printer>(name, std::forward<CallableT>(printFn)); 282 } 283 284 private: 285 std::string name; 286 }; 287 288 //===----------------------------------------------------------------------===// 289 // ParserConfig 290 //===----------------------------------------------------------------------===// 291 292 /// This class represents a configuration for the MLIR assembly parser. It 293 /// contains all of the necessary state to parse a MLIR source file. 294 class ParserConfig { 295 public: ParserConfig(MLIRContext * context)296 ParserConfig(MLIRContext *context) : context(context) { 297 assert(context && "expected valid MLIR context"); 298 } 299 300 /// Return the MLIRContext to be used when parsing. getContext()301 MLIRContext *getContext() const { return context; } 302 303 /// Return the resource parser registered to the given name, or nullptr if no 304 /// parser with `name` is registered. getResourceParser(StringRef name)305 AsmResourceParser *getResourceParser(StringRef name) const { 306 auto it = resourceParsers.find(name); 307 return it == resourceParsers.end() ? nullptr : it->second.get(); 308 } 309 310 /// Attach the given resource parser. attachResourceParser(std::unique_ptr<AsmResourceParser> parser)311 void attachResourceParser(std::unique_ptr<AsmResourceParser> parser) { 312 StringRef name = parser->getName(); 313 auto it = resourceParsers.try_emplace(name, std::move(parser)); 314 (void)it; 315 assert(it.second && 316 "resource parser already registered with the given name"); 317 } 318 319 /// Attach the given callable resource parser with the given name. 320 template <typename CallableT> 321 std::enable_if_t<std::is_convertible< 322 CallableT, function_ref<LogicalResult(AsmParsedResourceEntry &)>>::value> attachResourceParser(StringRef name,CallableT && parserFn)323 attachResourceParser(StringRef name, CallableT &&parserFn) { 324 attachResourceParser(AsmResourceParser::fromCallable( 325 name, std::forward<CallableT>(parserFn))); 326 } 327 328 private: 329 MLIRContext *context; 330 DenseMap<StringRef, std::unique_ptr<AsmResourceParser>> resourceParsers; 331 }; 332 333 //===----------------------------------------------------------------------===// 334 // AsmState 335 //===----------------------------------------------------------------------===// 336 337 /// This class provides management for the lifetime of the state used when 338 /// printing the IR. It allows for alleviating the cost of recomputing the 339 /// internal state of the asm printer. 340 /// 341 /// The IR should not be mutated in-between invocations using this state, and 342 /// the IR being printed must not be an parent of the IR originally used to 343 /// initialize this state. This means that if a child operation is provided, a 344 /// parent operation cannot reuse this state. 345 class AsmState { 346 public: 347 /// This map represents the raw locations of operations within the output 348 /// stream. This maps the original pointer to the operation, to a pair of line 349 /// and column in the output stream. 350 using LocationMap = DenseMap<Operation *, std::pair<unsigned, unsigned>>; 351 352 /// Initialize the asm state at the level of the given operation. A location 353 /// map may optionally be provided to be populated when printing. 354 AsmState(Operation *op, 355 const OpPrintingFlags &printerFlags = OpPrintingFlags(), 356 LocationMap *locationMap = nullptr); 357 ~AsmState(); 358 359 /// Get the printer flags. 360 const OpPrintingFlags &getPrinterFlags() const; 361 362 /// Return an instance of the internal implementation. Returns nullptr if the 363 /// state has not been initialized. getImpl()364 detail::AsmStateImpl &getImpl() { return *impl; } 365 366 //===--------------------------------------------------------------------===// 367 // Resources 368 //===--------------------------------------------------------------------===// 369 370 /// Attach the given resource printer to the AsmState. 371 void attachResourcePrinter(std::unique_ptr<AsmResourcePrinter> printer); 372 373 /// Attach an resource printer, in the form of a callable, to the AsmState. 374 template <typename CallableT> 375 std::enable_if_t<std::is_convertible< 376 CallableT, function_ref<void(Operation *, AsmResourceBuilder &)>>::value> attachResourcePrinter(StringRef name,CallableT && printFn)377 attachResourcePrinter(StringRef name, CallableT &&printFn) { 378 attachResourcePrinter(AsmResourcePrinter::fromCallable( 379 name, std::forward<CallableT>(printFn))); 380 } 381 382 private: 383 AsmState() = delete; 384 385 /// A pointer to allocated storage for the impl state. 386 std::unique_ptr<detail::AsmStateImpl> impl; 387 }; 388 389 //===----------------------------------------------------------------------===// 390 // AsmPrinter CommandLine Options 391 //===----------------------------------------------------------------------===// 392 393 /// Register a set of useful command-line options that can be used to configure 394 /// various flags within the AsmPrinter. 395 void registerAsmPrinterCLOptions(); 396 397 } // namespace mlir 398 399 #endif // MLIR_IR_ASMSTATE_H_ 400