1//===-- Properties.td - Properties definition file ----------------*- tablegen -*-===// 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 is the base properties defination file. 10// 11//===----------------------------------------------------------------------===// 12 13#ifndef PROPERTIES 14#define PROPERTIES 15 16include "mlir/IR/Constraints.td" 17include "mlir/IR/Utils.td" 18 19// Base class for defining properties. 20class Property<string storageTypeParam = "", string desc = ""> { 21 // User-readable one line summary used in error reporting messages. If empty, 22 // a generic message will be used. 23 string summary = desc; 24 // The full description of this property. 25 string description = ""; 26 code storageType = storageTypeParam; 27 code interfaceType = storageTypeParam; 28 29 // The expression to convert from the storage type to the Interface 30 // type. For example, an enum can be stored as an int but returned as an 31 // enum class. 32 // 33 // Format: 34 // - `$_storage` will contain the property in the storage type. 35 code convertFromStorage = "$_storage"; 36 37 // The call expression to build a property storage from the interface type. 38 // 39 // Format: 40 // - `$_storage` will contain the property in the storage type. 41 // - `$_value` will contain the property in the user interface type. 42 code assignToStorage = "$_storage = $_value"; 43 44 // The call expression to convert from the storage type to an attribute. 45 // The resulting attribute must be non-null in non-error cases. 46 // 47 // Format: 48 // - `$_storage` is the storage type value. 49 // - `$_ctxt` is a `MLIRContext *`. 50 // 51 // The expression must return an `Attribute` and will be used as a function body. 52 code convertToAttribute = [{ 53 return convertToAttribute($_ctxt, $_storage); 54 }]; 55 56 // The call expression to convert from an Attribute to the storage type. 57 // 58 // Format: 59 // - `$_storage` is a reference to a value of the storage type. 60 // - `$_attr` is the attribute. 61 // - `$_diag` is a callback to get a Diagnostic to emit error. 62 // 63 // The expression must return a LogicalResult and will be used as a function body 64 // or in other similar contexts. 65 code convertFromAttribute = [{ 66 return convertFromAttribute($_storage, $_attr, $_diag); 67 }]; 68 69 // The verification predicate for this property. Defaults to the true predicate, 70 // since properties are always their expected type. 71 // Within the predicate, `$_self` is an instance of the **interface** 72 // type of the property. Setting this field to ? will also result in a 73 // true predicate but is not recommended, as it breaks composability. 74 Pred predicate = TruePred; 75 76 // The call expression to hash the property. 77 // 78 // Format: 79 // - `$_storage` is the variable to hash. 80 // 81 // The expression should define a llvm::hash_code. 82 // If unspecified, defaults to `llvm::hash_value($_storage)`. 83 // The default is not specified in tablegen because many combinators, like 84 // ArrayProperty, can fall back to more efficient implementations of 85 // `hashProperty` when their underlying elements have trivial hashing. 86 code hashProperty = ""; 87 88 // The body of the parser for a value of this property. 89 // Format: 90 // - `$_parser` is the OpAsmParser. 91 // - `$_storage` is the location into which the value is to be placed if it is 92 // present. 93 // - `$_ctxt` is a `MLIRContext *` 94 // 95 // This defines the body of a function (typically a lambda) that returns a 96 // ParseResult. There is an implicit `return success()` at the end of the parser 97 // code. 98 // 99 // When this code executes, `$_storage` will be initialized to the property's 100 // default value (if any, accounting for the storage type override). 101 code parser = [{ 102 auto value = ::mlir::FieldParser<}] # storageType # [{>::parse($_parser); 103 if (::mlir::failed(value)) 104 return ::mlir::failure(); 105 $_storage = std::move(*value); 106 }]; 107 108 // The body of the parser for a value of this property as the anchor of an optional 109 // group. This should parse the property if possible and do nothing if a value of 110 // the relevant type is not next in the parse stream. 111 // You are not required to define this parser if it cannot be meaningfully 112 // implemented. 113 // This has the same context and substitutions as `parser` except that it is 114 // required to return an OptionalParseResult. 115 // 116 // If the optional parser doesn't parse anything, it should not set 117 // $_storage, since the parser doesn't know if the default value has been 118 // overwritten. 119 code optionalParser = ""; 120 121 // The printer for a value of this property. 122 // Format: 123 // - `$_storage` is the storage data. 124 // - `$_printer` is the OpAsmPrinter instance. 125 // - `$_ctxt` is a `MLIRContext *` 126 // 127 // This may be called in an expression context, so variable declarations must 128 // be placed within a new scope. 129 // 130 // The printer for a property should always print a non-empty value - default value 131 // printing elision happens outside the context of this printing expression. 132 code printer = "$_printer << $_storage"; 133 134 // The call expression to emit the storage type to bytecode. 135 // 136 // Format: 137 // - `$_storage` is the storage type value. 138 // - `$_writer` is a `DialectBytecodeWriter`. 139 // - `$_ctxt` is a `MLIRContext *`. 140 // 141 // This will become the body af a function returning void. 142 code writeToMlirBytecode = [{ 143 writeToMlirBytecode($_writer, $_storage); 144 }]; 145 146 // The call expression to read the storage type from bytecode. 147 // 148 // Format: 149 // - `$_storage` is the storage type value. 150 // - `$_reader` is a `DialectBytecodeReader`. 151 // - `$_ctxt` is a `MLIRContext *`. 152 // 153 // This will become the body of a function returning LogicalResult. 154 // There is an implicit `return success()` at the end of this function. 155 // 156 // When this code executes, `$_storage` will be initialized to the property's 157 // default value (if any, accounting for the storage type override). 158 code readFromMlirBytecode = [{ 159 if (::mlir::failed(readFromMlirBytecode($_reader, $_storage))) 160 return ::mlir::failure(); 161 }]; 162 163 // Base definition for the property. Used to look through `OptionalProperty` 164 // for some format generation, as with the `baseAttr` field on attributes. 165 Property baseProperty = ?; 166 167 // Default value for the property within its storage. This should be an expression 168 // of type `interfaceType` and should be comparable with other types of that 169 // interface typ with `==`. The empty string means there is no default value. 170 string defaultValue = ""; 171 172 // If set, the default value the storage of the property should be initilized to. 173 // This is only needed when the storage and interface types of the property 174 // are distinct (ex. SmallVector for storage vs. ArrayRef for interfacing), as it 175 // will fall back to `defaultValue` when unspecified. 176 string storageTypeValueOverride = ""; 177} 178 179/// Implementation of the Property class's `readFromMlirBytecode` field using 180/// the default `convertFromAttribute` implementation. 181/// Users not wanting to implement their own `readFromMlirBytecode` and 182/// `writeToMlirBytecode` implementations can opt into using this implementation 183/// by writing: 184/// 185/// let writeToMlirBytecode = writeMlirBytecodeWithConvertToAttribute; 186/// let readFromMlirBytecode = readMlirBytecodeUsingConvertFromAttribute; 187/// 188/// in their property definition. 189/// Serialization and deserialization is performed using the attributes 190/// returned by `convertFromAttribute` and `convertToAttribute`. 191/// 192/// WARNING: This implementation creates a less than optimal encoding. 193/// Users caring about optimal encoding should not use this implementation and 194/// implement `readFromMlirBytecode` and `writeToMlirBytecode` themselves. 195defvar readMlirBytecodeUsingConvertFromAttribute = [{ 196 ::mlir::Attribute attr; 197 if (::mlir::failed($_reader.readAttribute(attr))) 198 return ::mlir::failure(); 199 if (::mlir::failed(convertFromAttribute($_storage, attr, nullptr))) 200 return ::mlir::failure(); 201}]; 202 203/// Implementation of the Property class's `writeToMlirBytecode` field using 204/// the default `convertToAttribute` implementation. 205/// See description of `readMlirBytecodeUsingConvertFromAttribute` above for 206/// details. 207defvar writeMlirBytecodeWithConvertToAttribute = [{ 208 $_writer.writeAttribute(convertToAttribute($_ctxt, $_storage)) 209}]; 210 211//===----------------------------------------------------------------------===// 212// Primitive property kinds 213 214// Any kind of integer stored as properties. 215class IntProp<string storageTypeParam, string desc = ""> : 216 Property<storageTypeParam, desc> { 217 let summary = !if(!empty(desc), storageTypeParam, desc); 218 let optionalParser = [{ 219 return $_parser.parseOptionalInteger($_storage); 220 }]; 221 let writeToMlirBytecode = [{ 222 $_writer.writeVarInt($_storage); 223 }]; 224 let readFromMlirBytecode = [{ 225 uint64_t val; 226 if (failed($_reader.readVarInt(val))) 227 return ::mlir::failure(); 228 $_storage = val; 229 }]; 230} 231 232class IntProperty<string storageTypeParam, string desc = ""> 233 : IntProp<storageTypeParam, desc>, Deprecated<"moved to the shorter name IntProp">; 234 235def I32Prop : IntProp<"int32_t">; 236def I64Prop : IntProp<"int64_t">; 237 238def I32Property : IntProp<"int32_t">, Deprecated<"moved to shorter name I32Prop">; 239def I64Property : IntProp<"int64_t">, Deprecated<"moved to shorter name I64Prop">; 240 241class EnumProp<string storageTypeParam, string desc = "", string default = ""> : 242 Property<storageTypeParam, desc> { 243 // TODO: implement predicate for enum validity. 244 let writeToMlirBytecode = [{ 245 $_writer.writeVarInt(static_cast<uint64_t>($_storage)); 246 }]; 247 let readFromMlirBytecode = [{ 248 uint64_t val; 249 if (failed($_reader.readVarInt(val))) 250 return ::mlir::failure(); 251 $_storage = static_cast<}] # storageTypeParam # [{>(val); 252 }]; 253 let defaultValue = default; 254} 255 256class EnumProperty<string storageTypeParam, string desc = "", string default = ""> : 257 EnumProp<storageTypeParam, desc, default>, 258 Deprecated<"moved to shorter name EnumProp">; 259 260// Note: only a class so we can deprecate the old name 261class _cls_StringProp : Property<"std::string", "string"> { 262 let interfaceType = "::llvm::StringRef"; 263 let convertFromStorage = "::llvm::StringRef{$_storage}"; 264 let assignToStorage = "$_storage = $_value.str()"; 265 let optionalParser = [{ 266 if (::mlir::failed($_parser.parseOptionalString(&$_storage))) 267 return std::nullopt; 268 }]; 269 let printer = "$_printer.printString($_storage)"; 270 let readFromMlirBytecode = [{ 271 StringRef val; 272 if (::mlir::failed($_reader.readString(val))) 273 return ::mlir::failure(); 274 $_storage = val.str(); 275 }]; 276 let writeToMlirBytecode = [{ 277 $_writer.writeOwnedString($_storage); 278 }]; 279} 280def StringProp : _cls_StringProp; 281def StringProperty : _cls_StringProp, Deprecated<"moved to shorter name StringProp">; 282 283// Note: only a class so we can deprecate the old name 284class _cls_BoolProp : IntProp<"bool", "boolean"> { 285 let printer = [{ $_printer << ($_storage ? "true" : "false") }]; 286 let readFromMlirBytecode = [{ 287 return $_reader.readBool($_storage); 288 }]; 289 let writeToMlirBytecode = [{ 290 $_writer.writeOwnedBool($_storage); 291 }]; 292} 293def BoolProp : _cls_BoolProp; 294def BoolProperty : _cls_BoolProp, Deprecated<"moved to shorter name BoolProp">; 295 296// Note: only a class so we can deprecate the old name 297class _cls_UnitProp : Property<"bool", "unit property"> { 298 let summary = "unit property"; 299 let description = [{ 300 A property whose presence or abscence is used as a flag. 301 302 This is stored as a boolean that defaults to false, and is named UnitProperty 303 by analogy with UnitAttr, which has the more comprehensive rationale and 304 explains the less typical syntax. 305 306 Note that this attribute does have a syntax for the false case to allow for its 307 use in contexts where default values shouldn't be elided. 308 }]; 309 let defaultValue = "false"; 310 311 let convertToAttribute = [{ 312 if ($_storage) 313 return ::mlir::UnitAttr::get($_ctxt); 314 else 315 return ::mlir::BoolAttr::get($_ctxt, false); 316 }]; 317 let convertFromAttribute = [{ 318 if (::llvm::isa<::mlir::UnitAttr>($_attr)) { 319 $_storage = true; 320 return ::mlir::success(); 321 } 322 if (auto boolAttr = ::llvm::dyn_cast<::mlir::BoolAttr>($_attr)) { 323 $_storage = boolAttr.getValue(); 324 return ::mlir::success(); 325 } 326 return ::mlir::failure(); 327 }]; 328 329 let parser = [{ 330 ::llvm::StringRef keyword; 331 if (::mlir::failed($_parser.parseOptionalKeyword(&keyword, 332 {"unit", "unit_absent"}))) 333 return $_parser.emitError($_parser.getCurrentLocation(), 334 "expected 'unit' or 'unit_absent'"); 335 $_storage = (keyword == "unit"); 336 }]; 337 338 let optionalParser = [{ 339 ::llvm::StringRef keyword; 340 if (::mlir::failed($_parser.parseOptionalKeyword(&keyword, 341 {"unit", "unit_absent"}))) 342 return std::nullopt; 343 $_storage = (keyword == "unit"); 344 }]; 345 346 let printer = [{ 347 $_printer << ($_storage ? "unit" : "unit_absent") 348 }]; 349 350 let writeToMlirBytecode = [{ 351 $_writer.writeOwnedBool($_storage); 352 }]; 353 let readFromMlirBytecode = [{ 354 if (::mlir::failed($_reader.readBool($_storage))) 355 return ::mlir::failure(); 356 }]; 357} 358def UnitProp : _cls_UnitProp; 359def UnitProperty : _cls_UnitProp, Deprecated<"moved to shorter name UnitProp">; 360 361//===----------------------------------------------------------------------===// 362// Property field overwrites 363 364/// Class for giving a property a default value. 365/// This doesn't change anything about the property other than giving it a default 366/// which can be used by ODS to elide printing. 367class DefaultValuedProp<Property p, string default = "", string storageDefault = ""> : Property<p.storageType, p.summary> { 368 let defaultValue = default; 369 let storageTypeValueOverride = storageDefault; 370 let baseProperty = p; 371 // Keep up to date with `Property` above. 372 let summary = p.summary; 373 let description = p.description; 374 let storageType = p.storageType; 375 let interfaceType = p.interfaceType; 376 let convertFromStorage = p.convertFromStorage; 377 let assignToStorage = p.assignToStorage; 378 let convertToAttribute = p.convertToAttribute; 379 let convertFromAttribute = p.convertFromAttribute; 380 let predicate = p.predicate; 381 let hashProperty = p.hashProperty; 382 let parser = p.parser; 383 let optionalParser = p.optionalParser; 384 let printer = p.printer; 385 let readFromMlirBytecode = p.readFromMlirBytecode; 386 let writeToMlirBytecode = p.writeToMlirBytecode; 387} 388class DefaultValuedProperty<Property p, string default = "", string storageDefault = ""> 389 : DefaultValuedProp<p, default, storageDefault>, Deprecated<"moved to shorter name DefaultValuedProp">; 390 391/// Apply the predicate `pred` to the property `p`, ANDing it with any 392/// predicates it may already have. If `newSummary` is provided, replace the 393/// summary of `p` with `newSummary`. 394class ConfinedProp<Property p, Pred pred, string newSummary = ""> 395 : Property<p.storageType, !if(!empty(newSummary), p.summary, newSummary)> { 396 let predicate = !if(!ne(p.predicate, TruePred), And<[p.predicate, pred]>, pred); 397 let baseProperty = p; 398 // Keep up to date with `Property` above. 399 let description = p.description; 400 let storageType = p.storageType; 401 let interfaceType = p.interfaceType; 402 let convertFromStorage = p.convertFromStorage; 403 let assignToStorage = p.assignToStorage; 404 let convertToAttribute = p.convertToAttribute; 405 let convertFromAttribute = p.convertFromAttribute; 406 let hashProperty = p.hashProperty; 407 let parser = p.parser; 408 let optionalParser = p.optionalParser; 409 let printer = p.printer; 410 let readFromMlirBytecode = p.readFromMlirBytecode; 411 let writeToMlirBytecode = p.writeToMlirBytecode; 412 let defaultValue = p.defaultValue; 413 let storageTypeValueOverride = p.storageTypeValueOverride; 414} 415 416class ConfinedProperty<Property p, Pred pred, string newSummary = ""> 417 : ConfinedProp<p, pred, newSummary>, 418 Deprecated<"moved to shorter name ConfinedProp">; 419 420//===----------------------------------------------------------------------===// 421// Primitive property combinators 422 423/// Create a variable named `name` of `prop`'s storage type that is initialized 424/// to the correct default value, if there is one. 425class _makePropStorage<Property prop, string name> { 426 code ret = prop.storageType # " " # name 427 # !cond(!not(!empty(prop.storageTypeValueOverride)) : " = " # prop.storageTypeValueOverride, 428 !not(!empty(prop.defaultValue)) : " = " # prop.defaultValue, 429 true : "") # ";"; 430} 431 432/// Construct a `Pred`icate `ret` that wraps the predicate of the underlying 433/// property `childProp` with: 434/// 435/// [](childProp.storageType& s) { 436/// return [](childProp.interfaceType i) { 437/// return leafSubst(childProp.predicate, "$_self" to "i"); 438/// }(childProp.convertFromStorage(s)) 439/// } 440/// 441/// and then appends `prefix` and `suffix`. 442class _makeStorageWrapperPred<Property wrappedProp> { 443 Pred ret = 444 Concat< 445 "[](" # "const " # wrappedProp.storageType 446 # "& baseStore) -> bool { return [](" 447 # wrappedProp.interfaceType # " baseIface) -> bool { return (", 448 SubstLeaves<"$_self", "baseIface", wrappedProp.predicate>, 449 "); }(" # !subst("$_storage", "baseStore", wrappedProp.convertFromStorage) 450 # "); }">; 451} 452 453/// The generic class for arrays of some other property, which is stored as a 454/// `SmallVector` of that property. This uses an `ArrayAttr` as its attribute form 455/// though subclasses can override this, as is the case with IntArrayAttr below. 456/// Those wishing to use a non-default number of SmallVector elements should 457/// subclass `ArrayProp`. 458class ArrayProp<Property elem = Property<>, string newSummary = ""> : 459 Property<"::llvm::SmallVector<" # elem.storageType # ">", 460 !if(!empty(newSummary), "array of " # elem.summary, newSummary)> { 461 let interfaceType = "::llvm::ArrayRef<" # elem.storageType # ">"; 462 let convertFromStorage = "::llvm::ArrayRef<" # elem.storageType # ">{$_storage}"; 463 let assignToStorage = "$_storage.assign($_value.begin(), $_value.end())"; 464 465 let convertFromAttribute = [{ 466 auto arrayAttr = ::llvm::dyn_cast_if_present<::mlir::ArrayAttr>($_attr); 467 if (!arrayAttr) 468 return $_diag() << "expected array attribute"; 469 for (::mlir::Attribute elemAttr : arrayAttr) { 470 }] # _makePropStorage<elem, "elemVal">.ret # [{ 471 auto elemRes = [&](Attribute propAttr, }] # elem.storageType # [{& propStorage) -> ::mlir::LogicalResult { 472 }] # !subst("$_attr", "propAttr", 473 !subst("$_storage", "propStorage", elem.convertFromAttribute)) # [{ 474 }(elemAttr, elemVal); 475 if (::mlir::failed(elemRes)) 476 return ::mlir::failure(); 477 $_storage.push_back(std::move(elemVal)); 478 } 479 return ::mlir::success(); 480 }]; 481 482 let convertToAttribute = [{ 483 SmallVector<Attribute> elems; 484 for (const auto& elemVal : $_storage) { 485 auto elemAttr = [&](const }] # elem.storageType #[{& propStorage) -> ::mlir::Attribute { 486 }] # !subst("$_storage", "propStorage", elem.convertToAttribute) # [{ 487 }(elemVal); 488 elems.push_back(elemAttr); 489 } 490 return ::mlir::ArrayAttr::get($_ctxt, elems); 491 }]; 492 493 let predicate = !if(!eq(elem.predicate, TruePred), 494 TruePred, 495 Concat<"::llvm::all_of($_self, ", _makeStorageWrapperPred<elem>.ret, ")">); 496 497 defvar theParserBegin = [{ 498 auto& storage = $_storage; 499 auto parseElemFn = [&]() -> ::mlir::ParseResult { 500 }] # _makePropStorage<elem, "elemVal">.ret # [{ 501 auto elemParse = [&](}] # elem.storageType # [{& propStorage) -> ::mlir::ParseResult { 502 }] # !subst("$_storage", "propStorage", elem.parser) # [{ 503 return ::mlir::success(); 504 }(elemVal); 505 if (::mlir::failed(elemParse)) 506 return ::mlir::failure(); 507 storage.push_back(std::move(elemVal)); 508 return ::mlir::success(); 509 }; 510 }]; 511 let parser = theParserBegin # [{ 512 return $_parser.parseCommaSeparatedList( 513 ::mlir::OpAsmParser::Delimiter::Square, parseElemFn); 514 }]; 515 // Hack around the lack of a peek method 516 let optionalParser = theParserBegin # [{ 517 auto oldLoc = $_parser.getCurrentLocation(); 518 auto parseResult = $_parser.parseCommaSeparatedList( 519 ::mlir::OpAsmParser::Delimiter::OptionalSquare, parseElemFn); 520 if (::mlir::failed(parseResult)) 521 return ::mlir::failure(); 522 auto newLoc = $_parser.getCurrentLocation(); 523 if (oldLoc == newLoc) 524 return std::nullopt; 525 return ::mlir::success(); 526 }]; 527 528 let printer = [{ [&](){ 529 $_printer << "["; 530 auto elemPrinter = [&](const }] # elem.storageType # [{& elemVal) { 531 }] # !subst("$_storage", "elemVal", elem.printer) #[{; 532 }; 533 ::llvm::interleaveComma($_storage, $_printer, elemPrinter); 534 $_printer << "]"; 535 }()}]; 536 537 let readFromMlirBytecode = [{ 538 uint64_t length; 539 if (::mlir::failed($_reader.readVarInt(length))) 540 return ::mlir::failure(); 541 $_storage.reserve(length); 542 for (uint64_t i = 0; i < length; ++i) { 543 }]# _makePropStorage<elem, "elemVal">.ret # [{ 544 auto elemRead = [&](}] # elem.storageType # [{& propStorage) -> ::mlir::LogicalResult { 545 }] # !subst("$_storage", "propStorage", elem.readFromMlirBytecode) # [{; 546 return ::mlir::success(); 547 }(elemVal); 548 if (::mlir::failed(elemRead)) 549 return ::mlir::failure(); 550 $_storage.push_back(std::move(elemVal)); 551 } 552 }]; 553 554 let writeToMlirBytecode = [{ 555 $_writer.writeVarInt($_storage.size()); 556 for (const auto& elemVal : $_storage) { 557 [&]() { 558 }] # !subst("$_storage", "elemVal", elem.writeToMlirBytecode) #[{; 559 }(); 560 } 561 }]; 562 563 // There's no hash_value for SmallVector<T>, so we construct the ArrayRef ourselves. 564 // In the non-trivial case, we define a mapped range to get internal hash 565 // codes. 566 let hashProperty = !if(!empty(elem.hashProperty), 567 [{::llvm::hash_value(::llvm::ArrayRef<}] # elem.storageType # [{>{$_storage})}], 568 [{[&]() -> ::llvm::hash_code { 569 auto getElemHash = [](const auto& propStorage) -> ::llvm::hash_code { 570 return }] # !subst("$_storage", "propStorage", elem.hashProperty) # [{; 571 }; 572 auto mapped = ::llvm::map_range($_storage, getElemHash); 573 return ::llvm::hash_combine_range(mapped.begin(), mapped.end()); 574 }() 575 }]); 576} 577class ArrayProperty<Property elem = Property<>, string newSummary = ""> 578 : ArrayProp<elem, newSummary>, Deprecated<"moved to shorter name ArrayProp">; 579 580class IntArrayProp<Property elem, string newSummary=""> : 581 ArrayProp<elem, newSummary> { 582 // Bring back the trivial conversions we don't get in the general case. 583 let convertFromAttribute = [{ 584 return convertFromAttribute($_storage, $_attr, $_diag); 585 }]; 586 let convertToAttribute = [{ 587 return convertToAttribute($_ctxt, $_storage); 588 }]; 589} 590class IntArrayProperty<Property elem, string newSummary=""> 591 : IntArrayProp<elem, newSummary>, Deprecated<"moved to shorter name IntArrayProp">; 592 593/// An optional property, stored as an std::optional<p.storageType> 594/// interfaced with as an std::optional<p.interfaceType>.. 595/// The syntax is `none` (or empty string if elided) for an absent value or 596/// `some<[underlying property]>` when a value is set. 597/// 598/// As a special exception, if the underlying property has an optional parser and 599/// no default value (ex. an integer property), the printer will skip the `some` 600/// bracketing and delegate to the optional parser. In that case, the syntax is the 601/// syntax of the underlying property, or the keyword `none` in the rare cases that 602/// it is needed. This behavior can be disabled by setting `canDelegateParsing` to 0. 603class OptionalProp<Property p, bit canDelegateParsing = 1> 604 : Property<"std::optional<" # p.storageType # ">", "optional " # p.summary> { 605 606 // In the cases where the underlying attribute is plain old data that's passed by 607 // value, the conversion code is trivial. 608 defvar hasTrivialStorage = !and(!eq(p.convertFromStorage, "$_storage"), 609 !eq(p.assignToStorage, "$_storage = $_value"), 610 !eq(p.storageType, p.interfaceType)); 611 612 defvar delegatesParsing = !and(!empty(p.defaultValue), 613 !not(!empty(p.optionalParser)), canDelegateParsing); 614 615 let interfaceType = "std::optional<" # p.interfaceType # ">"; 616 let defaultValue = "std::nullopt"; 617 618 let convertFromStorage = !if(hasTrivialStorage, 619 p.convertFromStorage, 620 [{($_storage.has_value() ? std::optional<}] # p.interfaceType # ">{" 621 # !subst("$_storage", "(*($_storage))", p.convertFromStorage) 622 # [{} : std::nullopt)}]); 623 let assignToStorage = !if(hasTrivialStorage, 624 p.assignToStorage, 625 [{[&]() { 626 if (!$_value.has_value()) { 627 $_storage = std::nullopt; 628 return; 629 } 630 }] # _makePropStorage<p, "presentVal">.ret # [{ 631 [&](}] # p.storageType # [{& propStorage) { 632 }] # !subst("$_storage", "propStorage", 633 !subst("$_value", "(*($_value))", p.assignToStorage)) # [{; 634 }(presentVal); 635 $_storage = std::move(presentVal); 636 }()}]); 637 638 let convertFromAttribute = [{ 639 auto arrayAttr = ::llvm::dyn_cast<::mlir::ArrayAttr>($_attr); 640 if (!arrayAttr) 641 return $_diag() << "expected optional properties to materialize as arrays"; 642 if (arrayAttr.size() > 1) 643 return $_diag() << "expected optional properties to become 0- or 1-element arrays"; 644 if (arrayAttr.empty()) { 645 $_storage = std::nullopt; 646 return ::mlir::success(); 647 } 648 ::mlir::Attribute presentAttr = arrayAttr[0]; 649 }] # _makePropStorage<p, "presentVal">.ret # [{ 650 auto presentRes = [&](Attribute propAttr, }] # p.storageType # [{& propStorage) -> ::mlir::LogicalResult { 651 }] # !subst("$_storage", "propStorage", 652 !subst("$_attr", "propAttr", p.convertFromAttribute)) # [{ 653 }(presentAttr, presentVal); 654 if (::mlir::failed(presentRes)) 655 return ::mlir::failure(); 656 $_storage = std::move(presentVal); 657 return ::mlir::success(); 658 }]; 659 660 let convertToAttribute = [{ 661 if (!$_storage.has_value()) { 662 return ::mlir::ArrayAttr::get($_ctxt, {}); 663 } 664 auto attr = [&]() -> ::mlir::Attribute { 665 }] # !subst("$_storage", "(*($_storage))", p.convertToAttribute) # [{ 666 }(); 667 return ::mlir::ArrayAttr::get($_ctxt, {attr}); 668 }]; 669 670 let predicate = !if(!ne(p.predicate, TruePred), 671 Or<[CPred<"!$_self.has_value()">, 672 SubstLeaves<"$_self", "(*($_self))", p.predicate>]>, 673 TruePred); 674 675 defvar delegatedParserBegin = [{ 676 if (::mlir::succeeded($_parser.parseOptionalKeyword("none"))) { 677 $_storage = std::nullopt; 678 return ::mlir::success(); 679 } 680 }] #_makePropStorage<p, "presentVal">.ret # [{ 681 auto delegParseResult = [&](}] # p.storageType # [{& propStorage) -> ::mlir::OptionalParseResult { 682 }] # !subst("$_storage", "propStorage", p.optionalParser) # [{ 683 return ::mlir::success(); 684 }(presentVal); 685 if (!delegParseResult.has_value()) { 686 }]; 687 688 defvar delegatedParserEnd = [{ 689 } 690 if (delegParseResult.has_value() && ::mlir::failed(*delegParseResult)) 691 return ::mlir::failure(); 692 $_storage = std::move(presentVal); 693 return ::mlir::success(); 694 }]; 695 // If we're being explicitly called for our parser, we're expecting to have been 696 // printede into a context where the default value isn't elided. Therefore, 697 // not-present from the underlying parser is a failure. 698 defvar delegatedParser = delegatedParserBegin # [{ 699 return ::mlir::failure(); 700 }] # delegatedParserEnd; 701 defvar delegatedOptionalParser = delegatedParserBegin # [{ 702 return std::nullopt; 703 }] # delegatedParserEnd; 704 705 defvar generalParserBegin = [{ 706 ::llvm::StringRef keyword; 707 if (::mlir::failed($_parser.parseOptionalKeyword(&keyword, {"none", "some"}))) { 708 }]; 709 defvar generalParserEnd = [{ 710 } 711 if (keyword == "none") { 712 $_storage = std::nullopt; 713 return ::mlir::success(); 714 } 715 if (::mlir::failed($_parser.parseLess())) 716 return ::mlir::failure(); 717 }] # _makePropStorage<p, "presentVal">.ret # [{ 718 auto presentParse = [&](}] # p.storageType # [{& propStorage) -> ::mlir::ParseResult { 719 }] # !subst("$_storage", "propStorage", p.parser) # [{ 720 return ::mlir::success(); 721 }(presentVal); 722 if (presentParse || $_parser.parseGreater()) 723 return ::mlir::failure(); 724 $_storage = std::move(presentVal); 725 }]; 726 defvar generalParser = generalParserBegin # [{ 727 return $_parser.emitError($_parser.getCurrentLocation(), "expected 'none' or 'some<prop>'"); 728 }] # generalParserEnd; 729 defvar generalOptionalParser = generalParserBegin # [{ 730 return std::nullopt; 731 }] # generalParserEnd; 732 733 let parser = !if(delegatesParsing, delegatedParser, generalParser); 734 let optionalParser = !if(delegatesParsing, 735 delegatedOptionalParser, generalOptionalParser); 736 737 defvar delegatedPrinter = [{ 738 [&]() { 739 if (!$_storage.has_value()) { 740 $_printer << "none"; 741 return; 742 } 743 }] # !subst("$_storage", "(*($_storage))", p.printer) # [{; 744 }()}]; 745 defvar generalPrinter = [{ 746 [&]() { 747 if (!$_storage.has_value()) { 748 $_printer << "none"; 749 return; 750 } 751 $_printer << "some<"; 752 }] # !subst("$_storage", "(*($_storage))", p.printer) # [{; 753 $_printer << ">"; 754 }()}]; 755 let printer = !if(delegatesParsing, delegatedPrinter, generalPrinter); 756 757 let readFromMlirBytecode = [{ 758 bool isPresent = false; 759 if (::mlir::failed($_reader.readBool(isPresent))) 760 return ::mlir::failure(); 761 if (!isPresent) { 762 $_storage = std::nullopt; 763 return ::mlir::success(); 764 } 765 }] # _makePropStorage<p, "presentVal">.ret # [{ 766 auto presentResult = [&](}] # p.storageType # [{& propStorage) -> ::mlir::LogicalResult { 767 }] # !subst("$_storage", "propStorage", p.readFromMlirBytecode) # [{; 768 return ::mlir::success(); 769 }(presentVal); 770 if (::mlir::failed(presentResult)) 771 return ::mlir::failure(); 772 $_storage = std::move(presentVal); 773 }]; 774 let writeToMlirBytecode = [{ 775 $_writer.writeOwnedBool($_storage.has_value()); 776 if (!$_storage.has_value()) 777 return; 778 }] # !subst("$_storage", "(*($_storage))", p.writeToMlirBytecode); 779 780 let hashProperty = !if(!empty(p.hashProperty), p.hashProperty, 781 [{ ::llvm::hash_value($_storage.has_value() ? std::optional<::llvm::hash_code>{}] # 782 !subst("$_storage", "(*($_storage))", p.hashProperty) #[{} : std::nullopt) }]); 783 assert !or(!not(delegatesParsing), !eq(defaultValue, "std::nullopt")), 784 "For delegated parsing to be used, the default value must be nullopt. " # 785 "To use a non-trivial default, set the canDelegateParsing argument to 0"; 786} 787class OptionalProperty<Property p, bit canDelegateParsing = 1> 788 : OptionalProp<p, canDelegateParsing>, 789 Deprecated<"moved to shorter name OptionalProp">; 790#endif // PROPERTIES 791