1 //===- SPIRVOps.cpp - MLIR SPIR-V operations ------------------------------===// 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 the operations in the SPIR-V dialect. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "mlir/Dialect/SPIRV/IR/SPIRVOps.h" 14 15 #include "mlir/Dialect/SPIRV/IR/ParserUtils.h" 16 #include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h" 17 #include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h" 18 #include "mlir/Dialect/SPIRV/IR/SPIRVOpTraits.h" 19 #include "mlir/Dialect/SPIRV/IR/SPIRVTypes.h" 20 #include "mlir/Dialect/SPIRV/IR/TargetAndABI.h" 21 #include "mlir/IR/Builders.h" 22 #include "mlir/IR/BuiltinOps.h" 23 #include "mlir/IR/BuiltinTypes.h" 24 #include "mlir/IR/FunctionImplementation.h" 25 #include "mlir/IR/OpDefinition.h" 26 #include "mlir/IR/OpImplementation.h" 27 #include "mlir/IR/TypeUtilities.h" 28 #include "mlir/Interfaces/CallInterfaces.h" 29 #include "llvm/ADT/APFloat.h" 30 #include "llvm/ADT/APInt.h" 31 #include "llvm/ADT/StringExtras.h" 32 #include "llvm/ADT/bit.h" 33 34 using namespace mlir; 35 36 // TODO: generate these strings using ODS. 37 static constexpr const char kMemoryAccessAttrName[] = "memory_access"; 38 static constexpr const char kSourceMemoryAccessAttrName[] = 39 "source_memory_access"; 40 static constexpr const char kAlignmentAttrName[] = "alignment"; 41 static constexpr const char kSourceAlignmentAttrName[] = "source_alignment"; 42 static constexpr const char kBranchWeightAttrName[] = "branch_weights"; 43 static constexpr const char kCallee[] = "callee"; 44 static constexpr const char kClusterSize[] = "cluster_size"; 45 static constexpr const char kControl[] = "control"; 46 static constexpr const char kDefaultValueAttrName[] = "default_value"; 47 static constexpr const char kExecutionScopeAttrName[] = "execution_scope"; 48 static constexpr const char kEqualSemanticsAttrName[] = "equal_semantics"; 49 static constexpr const char kFnNameAttrName[] = "fn"; 50 static constexpr const char kGroupOperationAttrName[] = "group_operation"; 51 static constexpr const char kIndicesAttrName[] = "indices"; 52 static constexpr const char kInitializerAttrName[] = "initializer"; 53 static constexpr const char kInterfaceAttrName[] = "interface"; 54 static constexpr const char kMemoryScopeAttrName[] = "memory_scope"; 55 static constexpr const char kSemanticsAttrName[] = "semantics"; 56 static constexpr const char kSpecIdAttrName[] = "spec_id"; 57 static constexpr const char kTypeAttrName[] = "type"; 58 static constexpr const char kUnequalSemanticsAttrName[] = "unequal_semantics"; 59 static constexpr const char kValueAttrName[] = "value"; 60 static constexpr const char kValuesAttrName[] = "values"; 61 static constexpr const char kCompositeSpecConstituentsName[] = "constituents"; 62 63 //===----------------------------------------------------------------------===// 64 // Common utility functions 65 //===----------------------------------------------------------------------===// 66 67 /// Returns true if the given op is a function-like op or nested in a 68 /// function-like op without a module-like op in the middle. 69 static bool isNestedInFunctionLikeOp(Operation *op) { 70 if (!op) 71 return false; 72 if (op->hasTrait<OpTrait::SymbolTable>()) 73 return false; 74 if (op->hasTrait<OpTrait::FunctionLike>()) 75 return true; 76 return isNestedInFunctionLikeOp(op->getParentOp()); 77 } 78 79 /// Returns true if the given op is an module-like op that maintains a symbol 80 /// table. 81 static bool isDirectInModuleLikeOp(Operation *op) { 82 return op && op->hasTrait<OpTrait::SymbolTable>(); 83 } 84 85 static LogicalResult extractValueFromConstOp(Operation *op, int32_t &value) { 86 auto constOp = dyn_cast_or_null<spirv::ConstantOp>(op); 87 if (!constOp) { 88 return failure(); 89 } 90 auto valueAttr = constOp.value(); 91 auto integerValueAttr = valueAttr.dyn_cast<IntegerAttr>(); 92 if (!integerValueAttr) { 93 return failure(); 94 } 95 value = integerValueAttr.getInt(); 96 return success(); 97 } 98 99 template <typename Ty> 100 static ArrayAttr 101 getStrArrayAttrForEnumList(Builder &builder, ArrayRef<Ty> enumValues, 102 function_ref<StringRef(Ty)> stringifyFn) { 103 if (enumValues.empty()) { 104 return nullptr; 105 } 106 SmallVector<StringRef, 1> enumValStrs; 107 enumValStrs.reserve(enumValues.size()); 108 for (auto val : enumValues) { 109 enumValStrs.emplace_back(stringifyFn(val)); 110 } 111 return builder.getStrArrayAttr(enumValStrs); 112 } 113 114 /// Parses the next string attribute in `parser` as an enumerant of the given 115 /// `EnumClass`. 116 template <typename EnumClass> 117 static ParseResult 118 parseEnumStrAttr(EnumClass &value, OpAsmParser &parser, 119 StringRef attrName = spirv::attributeName<EnumClass>()) { 120 Attribute attrVal; 121 NamedAttrList attr; 122 auto loc = parser.getCurrentLocation(); 123 if (parser.parseAttribute(attrVal, parser.getBuilder().getNoneType(), 124 attrName, attr)) { 125 return failure(); 126 } 127 if (!attrVal.isa<StringAttr>()) { 128 return parser.emitError(loc, "expected ") 129 << attrName << " attribute specified as string"; 130 } 131 auto attrOptional = 132 spirv::symbolizeEnum<EnumClass>(attrVal.cast<StringAttr>().getValue()); 133 if (!attrOptional) { 134 return parser.emitError(loc, "invalid ") 135 << attrName << " attribute specification: " << attrVal; 136 } 137 value = attrOptional.getValue(); 138 return success(); 139 } 140 141 /// Parses the next string attribute in `parser` as an enumerant of the given 142 /// `EnumClass` and inserts the enumerant into `state` as an 32-bit integer 143 /// attribute with the enum class's name as attribute name. 144 template <typename EnumClass> 145 static ParseResult 146 parseEnumStrAttr(EnumClass &value, OpAsmParser &parser, OperationState &state, 147 StringRef attrName = spirv::attributeName<EnumClass>()) { 148 if (parseEnumStrAttr(value, parser)) { 149 return failure(); 150 } 151 state.addAttribute(attrName, parser.getBuilder().getI32IntegerAttr( 152 llvm::bit_cast<int32_t>(value))); 153 return success(); 154 } 155 156 /// Parses the next keyword in `parser` as an enumerant of the given `EnumClass` 157 /// and inserts the enumerant into `state` as an 32-bit integer attribute with 158 /// the enum class's name as attribute name. 159 template <typename EnumClass> 160 static ParseResult 161 parseEnumKeywordAttr(EnumClass &value, OpAsmParser &parser, 162 OperationState &state, 163 StringRef attrName = spirv::attributeName<EnumClass>()) { 164 if (parseEnumKeywordAttr(value, parser)) { 165 return failure(); 166 } 167 state.addAttribute(attrName, parser.getBuilder().getI32IntegerAttr( 168 llvm::bit_cast<int32_t>(value))); 169 return success(); 170 } 171 172 /// Parses Function, Selection and Loop control attributes. If no control is 173 /// specified, "None" is used as a default. 174 template <typename EnumClass> 175 static ParseResult 176 parseControlAttribute(OpAsmParser &parser, OperationState &state, 177 StringRef attrName = spirv::attributeName<EnumClass>()) { 178 if (succeeded(parser.parseOptionalKeyword(kControl))) { 179 EnumClass control; 180 if (parser.parseLParen() || parseEnumKeywordAttr(control, parser, state) || 181 parser.parseRParen()) 182 return failure(); 183 return success(); 184 } 185 // Set control to "None" otherwise. 186 Builder builder = parser.getBuilder(); 187 state.addAttribute(attrName, builder.getI32IntegerAttr(0)); 188 return success(); 189 } 190 191 /// Parses optional memory access attributes attached to a memory access 192 /// operand/pointer. Specifically, parses the following syntax: 193 /// (`[` memory-access `]`)? 194 /// where: 195 /// memory-access ::= `"None"` | `"Volatile"` | `"Aligned", ` 196 /// integer-literal | `"NonTemporal"` 197 static ParseResult parseMemoryAccessAttributes(OpAsmParser &parser, 198 OperationState &state) { 199 // Parse an optional list of attributes staring with '[' 200 if (parser.parseOptionalLSquare()) { 201 // Nothing to do 202 return success(); 203 } 204 205 spirv::MemoryAccess memoryAccessAttr; 206 if (parseEnumStrAttr(memoryAccessAttr, parser, state, 207 kMemoryAccessAttrName)) { 208 return failure(); 209 } 210 211 if (spirv::bitEnumContains(memoryAccessAttr, spirv::MemoryAccess::Aligned)) { 212 // Parse integer attribute for alignment. 213 Attribute alignmentAttr; 214 Type i32Type = parser.getBuilder().getIntegerType(32); 215 if (parser.parseComma() || 216 parser.parseAttribute(alignmentAttr, i32Type, kAlignmentAttrName, 217 state.attributes)) { 218 return failure(); 219 } 220 } 221 return parser.parseRSquare(); 222 } 223 224 // TODO Make sure to merge this and the previous function into one template 225 // parameterized by memory access attribute name and alignment. Doing so now 226 // results in VS2017 in producing an internal error (at the call site) that's 227 // not detailed enough to understand what is happening. 228 static ParseResult parseSourceMemoryAccessAttributes(OpAsmParser &parser, 229 OperationState &state) { 230 // Parse an optional list of attributes staring with '[' 231 if (parser.parseOptionalLSquare()) { 232 // Nothing to do 233 return success(); 234 } 235 236 spirv::MemoryAccess memoryAccessAttr; 237 if (parseEnumStrAttr(memoryAccessAttr, parser, state, 238 kSourceMemoryAccessAttrName)) { 239 return failure(); 240 } 241 242 if (spirv::bitEnumContains(memoryAccessAttr, spirv::MemoryAccess::Aligned)) { 243 // Parse integer attribute for alignment. 244 Attribute alignmentAttr; 245 Type i32Type = parser.getBuilder().getIntegerType(32); 246 if (parser.parseComma() || 247 parser.parseAttribute(alignmentAttr, i32Type, kSourceAlignmentAttrName, 248 state.attributes)) { 249 return failure(); 250 } 251 } 252 return parser.parseRSquare(); 253 } 254 255 template <typename MemoryOpTy> 256 static void printMemoryAccessAttribute( 257 MemoryOpTy memoryOp, OpAsmPrinter &printer, 258 SmallVectorImpl<StringRef> &elidedAttrs, 259 Optional<spirv::MemoryAccess> memoryAccessAtrrValue = None, 260 Optional<uint32_t> alignmentAttrValue = None) { 261 // Print optional memory access attribute. 262 if (auto memAccess = (memoryAccessAtrrValue ? memoryAccessAtrrValue 263 : memoryOp.memory_access())) { 264 elidedAttrs.push_back(kMemoryAccessAttrName); 265 266 printer << " [\"" << stringifyMemoryAccess(*memAccess) << "\""; 267 268 if (spirv::bitEnumContains(*memAccess, spirv::MemoryAccess::Aligned)) { 269 // Print integer alignment attribute. 270 if (auto alignment = (alignmentAttrValue ? alignmentAttrValue 271 : memoryOp.alignment())) { 272 elidedAttrs.push_back(kAlignmentAttrName); 273 printer << ", " << alignment; 274 } 275 } 276 printer << "]"; 277 } 278 elidedAttrs.push_back(spirv::attributeName<spirv::StorageClass>()); 279 } 280 281 // TODO Make sure to merge this and the previous function into one template 282 // parameterized by memory access attribute name and alignment. Doing so now 283 // results in VS2017 in producing an internal error (at the call site) that's 284 // not detailed enough to understand what is happening. 285 template <typename MemoryOpTy> 286 static void printSourceMemoryAccessAttribute( 287 MemoryOpTy memoryOp, OpAsmPrinter &printer, 288 SmallVectorImpl<StringRef> &elidedAttrs, 289 Optional<spirv::MemoryAccess> memoryAccessAtrrValue = None, 290 Optional<uint32_t> alignmentAttrValue = None) { 291 292 printer << ", "; 293 294 // Print optional memory access attribute. 295 if (auto memAccess = (memoryAccessAtrrValue ? memoryAccessAtrrValue 296 : memoryOp.memory_access())) { 297 elidedAttrs.push_back(kSourceMemoryAccessAttrName); 298 299 printer << " [\"" << stringifyMemoryAccess(*memAccess) << "\""; 300 301 if (spirv::bitEnumContains(*memAccess, spirv::MemoryAccess::Aligned)) { 302 // Print integer alignment attribute. 303 if (auto alignment = (alignmentAttrValue ? alignmentAttrValue 304 : memoryOp.alignment())) { 305 elidedAttrs.push_back(kSourceAlignmentAttrName); 306 printer << ", " << alignment; 307 } 308 } 309 printer << "]"; 310 } 311 elidedAttrs.push_back(spirv::attributeName<spirv::StorageClass>()); 312 } 313 314 static ParseResult parseImageOperands(OpAsmParser &parser, 315 spirv::ImageOperandsAttr &attr) { 316 // Expect image operands 317 if (parser.parseOptionalLSquare()) 318 return success(); 319 320 spirv::ImageOperands imageOperands; 321 if (parseEnumStrAttr(imageOperands, parser)) 322 return failure(); 323 324 attr = spirv::ImageOperandsAttr::get(parser.getBuilder().getContext(), 325 imageOperands); 326 327 return parser.parseRSquare(); 328 } 329 330 static void printImageOperands(OpAsmPrinter &printer, Operation *imageOp, 331 spirv::ImageOperandsAttr attr) { 332 if (attr) { 333 auto strImageOperands = stringifyImageOperands(attr.getValue()); 334 printer << "[\"" << strImageOperands << "\"]"; 335 } 336 } 337 338 template <typename Op> 339 static LogicalResult verifyImageOperands(Op imageOp, 340 spirv::ImageOperandsAttr attr, 341 Operation::operand_range operands) { 342 if (!attr) { 343 if (operands.empty()) 344 return success(); 345 346 return imageOp.emitError("the Image Operands should encode what operands " 347 "follow, as per Image Operands"); 348 } 349 350 // TODO: Add the validation rules for the following Image Operands. 351 spirv::ImageOperands noSupportOperands = 352 spirv::ImageOperands::Bias | spirv::ImageOperands::Lod | 353 spirv::ImageOperands::Grad | spirv::ImageOperands::ConstOffset | 354 spirv::ImageOperands::Offset | spirv::ImageOperands::ConstOffsets | 355 spirv::ImageOperands::Sample | spirv::ImageOperands::MinLod | 356 spirv::ImageOperands::MakeTexelAvailable | 357 spirv::ImageOperands::MakeTexelVisible | 358 spirv::ImageOperands::SignExtend | spirv::ImageOperands::ZeroExtend; 359 360 if (spirv::bitEnumContains(attr.getValue(), noSupportOperands)) 361 llvm_unreachable("unimplemented operands of Image Operands"); 362 363 return success(); 364 } 365 366 static LogicalResult verifyCastOp(Operation *op, 367 bool requireSameBitWidth = true, 368 bool skipBitWidthCheck = false) { 369 // Some CastOps have no limit on bit widths for result and operand type. 370 if (skipBitWidthCheck) 371 return success(); 372 373 Type operandType = op->getOperand(0).getType(); 374 Type resultType = op->getResult(0).getType(); 375 376 // ODS checks that result type and operand type have the same shape. 377 if (auto vectorType = operandType.dyn_cast<VectorType>()) { 378 operandType = vectorType.getElementType(); 379 resultType = resultType.cast<VectorType>().getElementType(); 380 } 381 382 if (auto coopMatrixType = 383 operandType.dyn_cast<spirv::CooperativeMatrixNVType>()) { 384 operandType = coopMatrixType.getElementType(); 385 resultType = 386 resultType.cast<spirv::CooperativeMatrixNVType>().getElementType(); 387 } 388 389 auto operandTypeBitWidth = operandType.getIntOrFloatBitWidth(); 390 auto resultTypeBitWidth = resultType.getIntOrFloatBitWidth(); 391 auto isSameBitWidth = operandTypeBitWidth == resultTypeBitWidth; 392 393 if (requireSameBitWidth) { 394 if (!isSameBitWidth) { 395 return op->emitOpError( 396 "expected the same bit widths for operand type and result " 397 "type, but provided ") 398 << operandType << " and " << resultType; 399 } 400 return success(); 401 } 402 403 if (isSameBitWidth) { 404 return op->emitOpError( 405 "expected the different bit widths for operand type and result " 406 "type, but provided ") 407 << operandType << " and " << resultType; 408 } 409 return success(); 410 } 411 412 template <typename MemoryOpTy> 413 static LogicalResult verifyMemoryAccessAttribute(MemoryOpTy memoryOp) { 414 // ODS checks for attributes values. Just need to verify that if the 415 // memory-access attribute is Aligned, then the alignment attribute must be 416 // present. 417 auto *op = memoryOp.getOperation(); 418 auto memAccessAttr = op->getAttr(kMemoryAccessAttrName); 419 if (!memAccessAttr) { 420 // Alignment attribute shouldn't be present if memory access attribute is 421 // not present. 422 if (op->getAttr(kAlignmentAttrName)) { 423 return memoryOp.emitOpError( 424 "invalid alignment specification without aligned memory access " 425 "specification"); 426 } 427 return success(); 428 } 429 430 auto memAccessVal = memAccessAttr.template cast<IntegerAttr>(); 431 auto memAccess = spirv::symbolizeMemoryAccess(memAccessVal.getInt()); 432 433 if (!memAccess) { 434 return memoryOp.emitOpError("invalid memory access specifier: ") 435 << memAccessVal; 436 } 437 438 if (spirv::bitEnumContains(*memAccess, spirv::MemoryAccess::Aligned)) { 439 if (!op->getAttr(kAlignmentAttrName)) { 440 return memoryOp.emitOpError("missing alignment value"); 441 } 442 } else { 443 if (op->getAttr(kAlignmentAttrName)) { 444 return memoryOp.emitOpError( 445 "invalid alignment specification with non-aligned memory access " 446 "specification"); 447 } 448 } 449 return success(); 450 } 451 452 // TODO Make sure to merge this and the previous function into one template 453 // parameterized by memory access attribute name and alignment. Doing so now 454 // results in VS2017 in producing an internal error (at the call site) that's 455 // not detailed enough to understand what is happening. 456 template <typename MemoryOpTy> 457 static LogicalResult verifySourceMemoryAccessAttribute(MemoryOpTy memoryOp) { 458 // ODS checks for attributes values. Just need to verify that if the 459 // memory-access attribute is Aligned, then the alignment attribute must be 460 // present. 461 auto *op = memoryOp.getOperation(); 462 auto memAccessAttr = op->getAttr(kSourceMemoryAccessAttrName); 463 if (!memAccessAttr) { 464 // Alignment attribute shouldn't be present if memory access attribute is 465 // not present. 466 if (op->getAttr(kSourceAlignmentAttrName)) { 467 return memoryOp.emitOpError( 468 "invalid alignment specification without aligned memory access " 469 "specification"); 470 } 471 return success(); 472 } 473 474 auto memAccessVal = memAccessAttr.template cast<IntegerAttr>(); 475 auto memAccess = spirv::symbolizeMemoryAccess(memAccessVal.getInt()); 476 477 if (!memAccess) { 478 return memoryOp.emitOpError("invalid memory access specifier: ") 479 << memAccessVal; 480 } 481 482 if (spirv::bitEnumContains(*memAccess, spirv::MemoryAccess::Aligned)) { 483 if (!op->getAttr(kSourceAlignmentAttrName)) { 484 return memoryOp.emitOpError("missing alignment value"); 485 } 486 } else { 487 if (op->getAttr(kSourceAlignmentAttrName)) { 488 return memoryOp.emitOpError( 489 "invalid alignment specification with non-aligned memory access " 490 "specification"); 491 } 492 } 493 return success(); 494 } 495 496 template <typename BarrierOp> 497 static LogicalResult verifyMemorySemantics(BarrierOp op) { 498 // According to the SPIR-V specification: 499 // "Despite being a mask and allowing multiple bits to be combined, it is 500 // invalid for more than one of these four bits to be set: Acquire, Release, 501 // AcquireRelease, or SequentiallyConsistent. Requesting both Acquire and 502 // Release semantics is done by setting the AcquireRelease bit, not by setting 503 // two bits." 504 auto memorySemantics = op.memory_semantics(); 505 auto atMostOneInSet = spirv::MemorySemantics::Acquire | 506 spirv::MemorySemantics::Release | 507 spirv::MemorySemantics::AcquireRelease | 508 spirv::MemorySemantics::SequentiallyConsistent; 509 510 auto bitCount = llvm::countPopulation( 511 static_cast<uint32_t>(memorySemantics & atMostOneInSet)); 512 if (bitCount > 1) { 513 return op.emitError("expected at most one of these four memory constraints " 514 "to be set: `Acquire`, `Release`," 515 "`AcquireRelease` or `SequentiallyConsistent`"); 516 } 517 return success(); 518 } 519 520 template <typename LoadStoreOpTy> 521 static LogicalResult verifyLoadStorePtrAndValTypes(LoadStoreOpTy op, Value ptr, 522 Value val) { 523 // ODS already checks ptr is spirv::PointerType. Just check that the pointee 524 // type of the pointer and the type of the value are the same 525 // 526 // TODO: Check that the value type satisfies restrictions of 527 // SPIR-V OpLoad/OpStore operations 528 if (val.getType() != 529 ptr.getType().cast<spirv::PointerType>().getPointeeType()) { 530 return op.emitOpError("mismatch in result type and pointer type"); 531 } 532 return success(); 533 } 534 535 template <typename BlockReadWriteOpTy> 536 static LogicalResult verifyBlockReadWritePtrAndValTypes(BlockReadWriteOpTy op, 537 Value ptr, Value val) { 538 auto valType = val.getType(); 539 if (auto valVecTy = valType.dyn_cast<VectorType>()) 540 valType = valVecTy.getElementType(); 541 542 if (valType != ptr.getType().cast<spirv::PointerType>().getPointeeType()) { 543 return op.emitOpError("mismatch in result type and pointer type"); 544 } 545 return success(); 546 } 547 548 static ParseResult parseVariableDecorations(OpAsmParser &parser, 549 OperationState &state) { 550 auto builtInName = llvm::convertToSnakeFromCamelCase( 551 stringifyDecoration(spirv::Decoration::BuiltIn)); 552 if (succeeded(parser.parseOptionalKeyword("bind"))) { 553 Attribute set, binding; 554 // Parse optional descriptor binding 555 auto descriptorSetName = llvm::convertToSnakeFromCamelCase( 556 stringifyDecoration(spirv::Decoration::DescriptorSet)); 557 auto bindingName = llvm::convertToSnakeFromCamelCase( 558 stringifyDecoration(spirv::Decoration::Binding)); 559 Type i32Type = parser.getBuilder().getIntegerType(32); 560 if (parser.parseLParen() || 561 parser.parseAttribute(set, i32Type, descriptorSetName, 562 state.attributes) || 563 parser.parseComma() || 564 parser.parseAttribute(binding, i32Type, bindingName, 565 state.attributes) || 566 parser.parseRParen()) { 567 return failure(); 568 } 569 } else if (succeeded(parser.parseOptionalKeyword(builtInName))) { 570 StringAttr builtIn; 571 if (parser.parseLParen() || 572 parser.parseAttribute(builtIn, builtInName, state.attributes) || 573 parser.parseRParen()) { 574 return failure(); 575 } 576 } 577 578 // Parse other attributes 579 if (parser.parseOptionalAttrDict(state.attributes)) 580 return failure(); 581 582 return success(); 583 } 584 585 static void printVariableDecorations(Operation *op, OpAsmPrinter &printer, 586 SmallVectorImpl<StringRef> &elidedAttrs) { 587 // Print optional descriptor binding 588 auto descriptorSetName = llvm::convertToSnakeFromCamelCase( 589 stringifyDecoration(spirv::Decoration::DescriptorSet)); 590 auto bindingName = llvm::convertToSnakeFromCamelCase( 591 stringifyDecoration(spirv::Decoration::Binding)); 592 auto descriptorSet = op->getAttrOfType<IntegerAttr>(descriptorSetName); 593 auto binding = op->getAttrOfType<IntegerAttr>(bindingName); 594 if (descriptorSet && binding) { 595 elidedAttrs.push_back(descriptorSetName); 596 elidedAttrs.push_back(bindingName); 597 printer << " bind(" << descriptorSet.getInt() << ", " << binding.getInt() 598 << ")"; 599 } 600 601 // Print BuiltIn attribute if present 602 auto builtInName = llvm::convertToSnakeFromCamelCase( 603 stringifyDecoration(spirv::Decoration::BuiltIn)); 604 if (auto builtin = op->getAttrOfType<StringAttr>(builtInName)) { 605 printer << " " << builtInName << "(\"" << builtin.getValue() << "\")"; 606 elidedAttrs.push_back(builtInName); 607 } 608 609 printer.printOptionalAttrDict(op->getAttrs(), elidedAttrs); 610 } 611 612 // Get bit width of types. 613 static unsigned getBitWidth(Type type) { 614 if (type.isa<spirv::PointerType>()) { 615 // Just return 64 bits for pointer types for now. 616 // TODO: Make sure not caller relies on the actual pointer width value. 617 return 64; 618 } 619 620 if (type.isIntOrFloat()) 621 return type.getIntOrFloatBitWidth(); 622 623 if (auto vectorType = type.dyn_cast<VectorType>()) { 624 assert(vectorType.getElementType().isIntOrFloat()); 625 return vectorType.getNumElements() * 626 vectorType.getElementType().getIntOrFloatBitWidth(); 627 } 628 llvm_unreachable("unhandled bit width computation for type"); 629 } 630 631 /// Walks the given type hierarchy with the given indices, potentially down 632 /// to component granularity, to select an element type. Returns null type and 633 /// emits errors with the given loc on failure. 634 static Type 635 getElementType(Type type, ArrayRef<int32_t> indices, 636 function_ref<InFlightDiagnostic(StringRef)> emitErrorFn) { 637 if (indices.empty()) { 638 emitErrorFn("expected at least one index for spv.CompositeExtract"); 639 return nullptr; 640 } 641 642 for (auto index : indices) { 643 if (auto cType = type.dyn_cast<spirv::CompositeType>()) { 644 if (cType.hasCompileTimeKnownNumElements() && 645 (index < 0 || 646 static_cast<uint64_t>(index) >= cType.getNumElements())) { 647 emitErrorFn("index ") << index << " out of bounds for " << type; 648 return nullptr; 649 } 650 type = cType.getElementType(index); 651 } else { 652 emitErrorFn("cannot extract from non-composite type ") 653 << type << " with index " << index; 654 return nullptr; 655 } 656 } 657 return type; 658 } 659 660 static Type 661 getElementType(Type type, Attribute indices, 662 function_ref<InFlightDiagnostic(StringRef)> emitErrorFn) { 663 auto indicesArrayAttr = indices.dyn_cast<ArrayAttr>(); 664 if (!indicesArrayAttr) { 665 emitErrorFn("expected a 32-bit integer array attribute for 'indices'"); 666 return nullptr; 667 } 668 if (!indicesArrayAttr.size()) { 669 emitErrorFn("expected at least one index for spv.CompositeExtract"); 670 return nullptr; 671 } 672 673 SmallVector<int32_t, 2> indexVals; 674 for (auto indexAttr : indicesArrayAttr) { 675 auto indexIntAttr = indexAttr.dyn_cast<IntegerAttr>(); 676 if (!indexIntAttr) { 677 emitErrorFn("expected an 32-bit integer for index, but found '") 678 << indexAttr << "'"; 679 return nullptr; 680 } 681 indexVals.push_back(indexIntAttr.getInt()); 682 } 683 return getElementType(type, indexVals, emitErrorFn); 684 } 685 686 static Type getElementType(Type type, Attribute indices, Location loc) { 687 auto errorFn = [&](StringRef err) -> InFlightDiagnostic { 688 return ::mlir::emitError(loc, err); 689 }; 690 return getElementType(type, indices, errorFn); 691 } 692 693 static Type getElementType(Type type, Attribute indices, OpAsmParser &parser, 694 llvm::SMLoc loc) { 695 auto errorFn = [&](StringRef err) -> InFlightDiagnostic { 696 return parser.emitError(loc, err); 697 }; 698 return getElementType(type, indices, errorFn); 699 } 700 701 /// Returns true if the given `block` only contains one `spv.mlir.merge` op. 702 static inline bool isMergeBlock(Block &block) { 703 return !block.empty() && std::next(block.begin()) == block.end() && 704 isa<spirv::MergeOp>(block.front()); 705 } 706 707 //===----------------------------------------------------------------------===// 708 // Common parsers and printers 709 //===----------------------------------------------------------------------===// 710 711 // Parses an atomic update op. If the update op does not take a value (like 712 // AtomicIIncrement) `hasValue` must be false. 713 static ParseResult parseAtomicUpdateOp(OpAsmParser &parser, 714 OperationState &state, bool hasValue) { 715 spirv::Scope scope; 716 spirv::MemorySemantics memoryScope; 717 SmallVector<OpAsmParser::OperandType, 2> operandInfo; 718 OpAsmParser::OperandType ptrInfo, valueInfo; 719 Type type; 720 llvm::SMLoc loc; 721 if (parseEnumStrAttr(scope, parser, state, kMemoryScopeAttrName) || 722 parseEnumStrAttr(memoryScope, parser, state, kSemanticsAttrName) || 723 parser.parseOperandList(operandInfo, (hasValue ? 2 : 1)) || 724 parser.getCurrentLocation(&loc) || parser.parseColonType(type)) 725 return failure(); 726 727 auto ptrType = type.dyn_cast<spirv::PointerType>(); 728 if (!ptrType) 729 return parser.emitError(loc, "expected pointer type"); 730 731 SmallVector<Type, 2> operandTypes; 732 operandTypes.push_back(ptrType); 733 if (hasValue) 734 operandTypes.push_back(ptrType.getPointeeType()); 735 if (parser.resolveOperands(operandInfo, operandTypes, parser.getNameLoc(), 736 state.operands)) 737 return failure(); 738 return parser.addTypeToList(ptrType.getPointeeType(), state.types); 739 } 740 741 // Prints an atomic update op. 742 static void printAtomicUpdateOp(Operation *op, OpAsmPrinter &printer) { 743 printer << " \""; 744 auto scopeAttr = op->getAttrOfType<IntegerAttr>(kMemoryScopeAttrName); 745 printer << spirv::stringifyScope( 746 static_cast<spirv::Scope>(scopeAttr.getInt())) 747 << "\" \""; 748 auto memorySemanticsAttr = op->getAttrOfType<IntegerAttr>(kSemanticsAttrName); 749 printer << spirv::stringifyMemorySemantics( 750 static_cast<spirv::MemorySemantics>( 751 memorySemanticsAttr.getInt())) 752 << "\" " << op->getOperands() << " : " << op->getOperand(0).getType(); 753 } 754 755 // Verifies an atomic update op. 756 static LogicalResult verifyAtomicUpdateOp(Operation *op) { 757 auto ptrType = op->getOperand(0).getType().cast<spirv::PointerType>(); 758 auto elementType = ptrType.getPointeeType(); 759 if (!elementType.isa<IntegerType>()) 760 return op->emitOpError( 761 "pointer operand must point to an integer value, found ") 762 << elementType; 763 764 if (op->getNumOperands() > 1) { 765 auto valueType = op->getOperand(1).getType(); 766 if (valueType != elementType) 767 return op->emitOpError("expected value to have the same type as the " 768 "pointer operand's pointee type ") 769 << elementType << ", but found " << valueType; 770 } 771 return success(); 772 } 773 774 static ParseResult parseGroupNonUniformArithmeticOp(OpAsmParser &parser, 775 OperationState &state) { 776 spirv::Scope executionScope; 777 spirv::GroupOperation groupOperation; 778 OpAsmParser::OperandType valueInfo; 779 if (parseEnumStrAttr(executionScope, parser, state, 780 kExecutionScopeAttrName) || 781 parseEnumStrAttr(groupOperation, parser, state, 782 kGroupOperationAttrName) || 783 parser.parseOperand(valueInfo)) 784 return failure(); 785 786 Optional<OpAsmParser::OperandType> clusterSizeInfo; 787 if (succeeded(parser.parseOptionalKeyword(kClusterSize))) { 788 clusterSizeInfo = OpAsmParser::OperandType(); 789 if (parser.parseLParen() || parser.parseOperand(*clusterSizeInfo) || 790 parser.parseRParen()) 791 return failure(); 792 } 793 794 Type resultType; 795 if (parser.parseColonType(resultType)) 796 return failure(); 797 798 if (parser.resolveOperand(valueInfo, resultType, state.operands)) 799 return failure(); 800 801 if (clusterSizeInfo.hasValue()) { 802 Type i32Type = parser.getBuilder().getIntegerType(32); 803 if (parser.resolveOperand(*clusterSizeInfo, i32Type, state.operands)) 804 return failure(); 805 } 806 807 return parser.addTypeToList(resultType, state.types); 808 } 809 810 static void printGroupNonUniformArithmeticOp(Operation *groupOp, 811 OpAsmPrinter &printer) { 812 printer << " \"" 813 << stringifyScope(static_cast<spirv::Scope>( 814 groupOp->getAttrOfType<IntegerAttr>(kExecutionScopeAttrName) 815 .getInt())) 816 << "\" \"" 817 << stringifyGroupOperation(static_cast<spirv::GroupOperation>( 818 groupOp->getAttrOfType<IntegerAttr>(kGroupOperationAttrName) 819 .getInt())) 820 << "\" " << groupOp->getOperand(0); 821 822 if (groupOp->getNumOperands() > 1) 823 printer << " " << kClusterSize << '(' << groupOp->getOperand(1) << ')'; 824 printer << " : " << groupOp->getResult(0).getType(); 825 } 826 827 static LogicalResult verifyGroupNonUniformArithmeticOp(Operation *groupOp) { 828 spirv::Scope scope = static_cast<spirv::Scope>( 829 groupOp->getAttrOfType<IntegerAttr>(kExecutionScopeAttrName).getInt()); 830 if (scope != spirv::Scope::Workgroup && scope != spirv::Scope::Subgroup) 831 return groupOp->emitOpError( 832 "execution scope must be 'Workgroup' or 'Subgroup'"); 833 834 spirv::GroupOperation operation = static_cast<spirv::GroupOperation>( 835 groupOp->getAttrOfType<IntegerAttr>(kGroupOperationAttrName).getInt()); 836 if (operation == spirv::GroupOperation::ClusteredReduce && 837 groupOp->getNumOperands() == 1) 838 return groupOp->emitOpError("cluster size operand must be provided for " 839 "'ClusteredReduce' group operation"); 840 if (groupOp->getNumOperands() > 1) { 841 Operation *sizeOp = groupOp->getOperand(1).getDefiningOp(); 842 int32_t clusterSize = 0; 843 844 // TODO: support specialization constant here. 845 if (failed(extractValueFromConstOp(sizeOp, clusterSize))) 846 return groupOp->emitOpError( 847 "cluster size operand must come from a constant op"); 848 849 if (!llvm::isPowerOf2_32(clusterSize)) 850 return groupOp->emitOpError( 851 "cluster size operand must be a power of two"); 852 } 853 return success(); 854 } 855 856 static ParseResult parseUnaryOp(OpAsmParser &parser, OperationState &state) { 857 OpAsmParser::OperandType operandInfo; 858 Type type; 859 if (parser.parseOperand(operandInfo) || parser.parseColonType(type) || 860 parser.resolveOperands(operandInfo, type, state.operands)) { 861 return failure(); 862 } 863 state.addTypes(type); 864 return success(); 865 } 866 867 static void printUnaryOp(Operation *unaryOp, OpAsmPrinter &printer) { 868 printer << ' ' << unaryOp->getOperand(0) << " : " 869 << unaryOp->getOperand(0).getType(); 870 } 871 872 /// Result of a logical op must be a scalar or vector of boolean type. 873 static Type getUnaryOpResultType(Builder &builder, Type operandType) { 874 Type resultType = builder.getIntegerType(1); 875 if (auto vecType = operandType.dyn_cast<VectorType>()) { 876 return VectorType::get(vecType.getNumElements(), resultType); 877 } 878 return resultType; 879 } 880 881 static ParseResult parseLogicalUnaryOp(OpAsmParser &parser, 882 OperationState &state) { 883 OpAsmParser::OperandType operandInfo; 884 Type type; 885 if (parser.parseOperand(operandInfo) || parser.parseColonType(type) || 886 parser.resolveOperand(operandInfo, type, state.operands)) { 887 return failure(); 888 } 889 state.addTypes(getUnaryOpResultType(parser.getBuilder(), type)); 890 return success(); 891 } 892 893 static ParseResult parseLogicalBinaryOp(OpAsmParser &parser, 894 OperationState &result) { 895 SmallVector<OpAsmParser::OperandType, 2> ops; 896 Type type; 897 if (parser.parseOperandList(ops, 2) || parser.parseColonType(type) || 898 parser.resolveOperands(ops, type, result.operands)) { 899 return failure(); 900 } 901 result.addTypes(getUnaryOpResultType(parser.getBuilder(), type)); 902 return success(); 903 } 904 905 static void printLogicalOp(Operation *logicalOp, OpAsmPrinter &printer) { 906 printer << ' ' << logicalOp->getOperands() << " : " 907 << logicalOp->getOperand(0).getType(); 908 } 909 910 static ParseResult parseShiftOp(OpAsmParser &parser, OperationState &state) { 911 SmallVector<OpAsmParser::OperandType, 2> operandInfo; 912 Type baseType; 913 Type shiftType; 914 auto loc = parser.getCurrentLocation(); 915 916 if (parser.parseOperandList(operandInfo, 2) || parser.parseColon() || 917 parser.parseType(baseType) || parser.parseComma() || 918 parser.parseType(shiftType) || 919 parser.resolveOperands(operandInfo, {baseType, shiftType}, loc, 920 state.operands)) { 921 return failure(); 922 } 923 state.addTypes(baseType); 924 return success(); 925 } 926 927 static void printShiftOp(Operation *op, OpAsmPrinter &printer) { 928 Value base = op->getOperand(0); 929 Value shift = op->getOperand(1); 930 printer << ' ' << base << ", " << shift << " : " << base.getType() << ", " 931 << shift.getType(); 932 } 933 934 static LogicalResult verifyShiftOp(Operation *op) { 935 if (op->getOperand(0).getType() != op->getResult(0).getType()) { 936 return op->emitError("expected the same type for the first operand and " 937 "result, but provided ") 938 << op->getOperand(0).getType() << " and " 939 << op->getResult(0).getType(); 940 } 941 return success(); 942 } 943 944 static void buildLogicalBinaryOp(OpBuilder &builder, OperationState &state, 945 Value lhs, Value rhs) { 946 assert(lhs.getType() == rhs.getType()); 947 948 Type boolType = builder.getI1Type(); 949 if (auto vecType = lhs.getType().dyn_cast<VectorType>()) 950 boolType = VectorType::get(vecType.getShape(), boolType); 951 state.addTypes(boolType); 952 953 state.addOperands({lhs, rhs}); 954 } 955 956 static void buildLogicalUnaryOp(OpBuilder &builder, OperationState &state, 957 Value value) { 958 Type boolType = builder.getI1Type(); 959 if (auto vecType = value.getType().dyn_cast<VectorType>()) 960 boolType = VectorType::get(vecType.getShape(), boolType); 961 state.addTypes(boolType); 962 963 state.addOperands(value); 964 } 965 966 //===----------------------------------------------------------------------===// 967 // spv.AccessChainOp 968 //===----------------------------------------------------------------------===// 969 970 static Type getElementPtrType(Type type, ValueRange indices, Location baseLoc) { 971 auto ptrType = type.dyn_cast<spirv::PointerType>(); 972 if (!ptrType) { 973 emitError(baseLoc, "'spv.AccessChain' op expected a pointer " 974 "to composite type, but provided ") 975 << type; 976 return nullptr; 977 } 978 979 auto resultType = ptrType.getPointeeType(); 980 auto resultStorageClass = ptrType.getStorageClass(); 981 int32_t index = 0; 982 983 for (auto indexSSA : indices) { 984 auto cType = resultType.dyn_cast<spirv::CompositeType>(); 985 if (!cType) { 986 emitError(baseLoc, 987 "'spv.AccessChain' op cannot extract from non-composite type ") 988 << resultType << " with index " << index; 989 return nullptr; 990 } 991 index = 0; 992 if (resultType.isa<spirv::StructType>()) { 993 Operation *op = indexSSA.getDefiningOp(); 994 if (!op) { 995 emitError(baseLoc, "'spv.AccessChain' op index must be an " 996 "integer spv.Constant to access " 997 "element of spv.struct"); 998 return nullptr; 999 } 1000 1001 // TODO: this should be relaxed to allow 1002 // integer literals of other bitwidths. 1003 if (failed(extractValueFromConstOp(op, index))) { 1004 emitError(baseLoc, 1005 "'spv.AccessChain' index must be an integer spv.Constant to " 1006 "access element of spv.struct, but provided ") 1007 << op->getName(); 1008 return nullptr; 1009 } 1010 if (index < 0 || static_cast<uint64_t>(index) >= cType.getNumElements()) { 1011 emitError(baseLoc, "'spv.AccessChain' op index ") 1012 << index << " out of bounds for " << resultType; 1013 return nullptr; 1014 } 1015 } 1016 resultType = cType.getElementType(index); 1017 } 1018 return spirv::PointerType::get(resultType, resultStorageClass); 1019 } 1020 1021 void spirv::AccessChainOp::build(OpBuilder &builder, OperationState &state, 1022 Value basePtr, ValueRange indices) { 1023 auto type = getElementPtrType(basePtr.getType(), indices, state.location); 1024 assert(type && "Unable to deduce return type based on basePtr and indices"); 1025 build(builder, state, type, basePtr, indices); 1026 } 1027 1028 static ParseResult parseAccessChainOp(OpAsmParser &parser, 1029 OperationState &state) { 1030 OpAsmParser::OperandType ptrInfo; 1031 SmallVector<OpAsmParser::OperandType, 4> indicesInfo; 1032 Type type; 1033 auto loc = parser.getCurrentLocation(); 1034 SmallVector<Type, 4> indicesTypes; 1035 1036 if (parser.parseOperand(ptrInfo) || 1037 parser.parseOperandList(indicesInfo, OpAsmParser::Delimiter::Square) || 1038 parser.parseColonType(type) || 1039 parser.resolveOperand(ptrInfo, type, state.operands)) { 1040 return failure(); 1041 } 1042 1043 // Check that the provided indices list is not empty before parsing their 1044 // type list. 1045 if (indicesInfo.empty()) { 1046 return emitError(state.location, "'spv.AccessChain' op expected at " 1047 "least one index "); 1048 } 1049 1050 if (parser.parseComma() || parser.parseTypeList(indicesTypes)) 1051 return failure(); 1052 1053 // Check that the indices types list is not empty and that it has a one-to-one 1054 // mapping to the provided indices. 1055 if (indicesTypes.size() != indicesInfo.size()) { 1056 return emitError(state.location, "'spv.AccessChain' op indices " 1057 "types' count must be equal to indices " 1058 "info count"); 1059 } 1060 1061 if (parser.resolveOperands(indicesInfo, indicesTypes, loc, state.operands)) 1062 return failure(); 1063 1064 auto resultType = getElementPtrType( 1065 type, llvm::makeArrayRef(state.operands).drop_front(), state.location); 1066 if (!resultType) { 1067 return failure(); 1068 } 1069 1070 state.addTypes(resultType); 1071 return success(); 1072 } 1073 1074 template <typename Op> 1075 static void printAccessChain(Op op, ValueRange indices, OpAsmPrinter &printer) { 1076 printer << ' ' << op.base_ptr() << '[' << indices 1077 << "] : " << op.base_ptr().getType() << ", " << indices.getTypes(); 1078 } 1079 1080 static void print(spirv::AccessChainOp op, OpAsmPrinter &printer) { 1081 printAccessChain(op, op.indices(), printer); 1082 } 1083 1084 template <typename Op> 1085 static LogicalResult verifyAccessChain(Op accessChainOp, ValueRange indices) { 1086 auto resultType = getElementPtrType(accessChainOp.base_ptr().getType(), 1087 indices, accessChainOp.getLoc()); 1088 if (!resultType) 1089 return failure(); 1090 1091 auto providedResultType = 1092 accessChainOp.getType().template dyn_cast<spirv::PointerType>(); 1093 if (!providedResultType) 1094 return accessChainOp.emitOpError( 1095 "result type must be a pointer, but provided") 1096 << providedResultType; 1097 1098 if (resultType != providedResultType) 1099 return accessChainOp.emitOpError("invalid result type: expected ") 1100 << resultType << ", but provided " << providedResultType; 1101 1102 return success(); 1103 } 1104 1105 static LogicalResult verify(spirv::AccessChainOp accessChainOp) { 1106 return verifyAccessChain(accessChainOp, accessChainOp.indices()); 1107 } 1108 1109 //===----------------------------------------------------------------------===// 1110 // spv.mlir.addressof 1111 //===----------------------------------------------------------------------===// 1112 1113 void spirv::AddressOfOp::build(OpBuilder &builder, OperationState &state, 1114 spirv::GlobalVariableOp var) { 1115 build(builder, state, var.type(), SymbolRefAttr::get(var)); 1116 } 1117 1118 static LogicalResult verify(spirv::AddressOfOp addressOfOp) { 1119 auto varOp = dyn_cast_or_null<spirv::GlobalVariableOp>( 1120 SymbolTable::lookupNearestSymbolFrom(addressOfOp->getParentOp(), 1121 addressOfOp.variableAttr())); 1122 if (!varOp) { 1123 return addressOfOp.emitOpError("expected spv.GlobalVariable symbol"); 1124 } 1125 if (addressOfOp.pointer().getType() != varOp.type()) { 1126 return addressOfOp.emitOpError( 1127 "result type mismatch with the referenced global variable's type"); 1128 } 1129 return success(); 1130 } 1131 1132 //===----------------------------------------------------------------------===// 1133 // spv.AtomicCompareExchangeWeak 1134 //===----------------------------------------------------------------------===// 1135 1136 static ParseResult parseAtomicCompareExchangeWeakOp(OpAsmParser &parser, 1137 OperationState &state) { 1138 spirv::Scope memoryScope; 1139 spirv::MemorySemantics equalSemantics, unequalSemantics; 1140 SmallVector<OpAsmParser::OperandType, 3> operandInfo; 1141 Type type; 1142 if (parseEnumStrAttr(memoryScope, parser, state, kMemoryScopeAttrName) || 1143 parseEnumStrAttr(equalSemantics, parser, state, 1144 kEqualSemanticsAttrName) || 1145 parseEnumStrAttr(unequalSemantics, parser, state, 1146 kUnequalSemanticsAttrName) || 1147 parser.parseOperandList(operandInfo, 3)) 1148 return failure(); 1149 1150 auto loc = parser.getCurrentLocation(); 1151 if (parser.parseColonType(type)) 1152 return failure(); 1153 1154 auto ptrType = type.dyn_cast<spirv::PointerType>(); 1155 if (!ptrType) 1156 return parser.emitError(loc, "expected pointer type"); 1157 1158 if (parser.resolveOperands( 1159 operandInfo, 1160 {ptrType, ptrType.getPointeeType(), ptrType.getPointeeType()}, 1161 parser.getNameLoc(), state.operands)) 1162 return failure(); 1163 1164 return parser.addTypeToList(ptrType.getPointeeType(), state.types); 1165 } 1166 1167 static void print(spirv::AtomicCompareExchangeWeakOp atomOp, 1168 OpAsmPrinter &printer) { 1169 printer << " \"" << stringifyScope(atomOp.memory_scope()) << "\" \"" 1170 << stringifyMemorySemantics(atomOp.equal_semantics()) << "\" \"" 1171 << stringifyMemorySemantics(atomOp.unequal_semantics()) << "\" " 1172 << atomOp.getOperands() << " : " << atomOp.pointer().getType(); 1173 } 1174 1175 static LogicalResult verify(spirv::AtomicCompareExchangeWeakOp atomOp) { 1176 // According to the spec: 1177 // "The type of Value must be the same as Result Type. The type of the value 1178 // pointed to by Pointer must be the same as Result Type. This type must also 1179 // match the type of Comparator." 1180 if (atomOp.getType() != atomOp.value().getType()) 1181 return atomOp.emitOpError("value operand must have the same type as the op " 1182 "result, but found ") 1183 << atomOp.value().getType() << " vs " << atomOp.getType(); 1184 1185 if (atomOp.getType() != atomOp.comparator().getType()) 1186 return atomOp.emitOpError( 1187 "comparator operand must have the same type as the op " 1188 "result, but found ") 1189 << atomOp.comparator().getType() << " vs " << atomOp.getType(); 1190 1191 Type pointeeType = 1192 atomOp.pointer().getType().cast<spirv::PointerType>().getPointeeType(); 1193 if (atomOp.getType() != pointeeType) 1194 return atomOp.emitOpError( 1195 "pointer operand's pointee type must have the same " 1196 "as the op result type, but found ") 1197 << pointeeType << " vs " << atomOp.getType(); 1198 1199 // TODO: Unequal cannot be set to Release or Acquire and Release. 1200 // In addition, Unequal cannot be set to a stronger memory-order then Equal. 1201 1202 return success(); 1203 } 1204 1205 //===----------------------------------------------------------------------===// 1206 // spv.BitcastOp 1207 //===----------------------------------------------------------------------===// 1208 1209 static LogicalResult verify(spirv::BitcastOp bitcastOp) { 1210 // TODO: The SPIR-V spec validation rules are different for different 1211 // versions. 1212 auto operandType = bitcastOp.operand().getType(); 1213 auto resultType = bitcastOp.result().getType(); 1214 if (operandType == resultType) { 1215 return bitcastOp.emitError( 1216 "result type must be different from operand type"); 1217 } 1218 if (operandType.isa<spirv::PointerType>() && 1219 !resultType.isa<spirv::PointerType>()) { 1220 return bitcastOp.emitError( 1221 "unhandled bit cast conversion from pointer type to non-pointer type"); 1222 } 1223 if (!operandType.isa<spirv::PointerType>() && 1224 resultType.isa<spirv::PointerType>()) { 1225 return bitcastOp.emitError( 1226 "unhandled bit cast conversion from non-pointer type to pointer type"); 1227 } 1228 auto operandBitWidth = getBitWidth(operandType); 1229 auto resultBitWidth = getBitWidth(resultType); 1230 if (operandBitWidth != resultBitWidth) { 1231 return bitcastOp.emitOpError("mismatch in result type bitwidth ") 1232 << resultBitWidth << " and operand type bitwidth " 1233 << operandBitWidth; 1234 } 1235 return success(); 1236 } 1237 1238 //===----------------------------------------------------------------------===// 1239 // spv.BranchOp 1240 //===----------------------------------------------------------------------===// 1241 1242 Optional<MutableOperandRange> 1243 spirv::BranchOp::getMutableSuccessorOperands(unsigned index) { 1244 assert(index == 0 && "invalid successor index"); 1245 return targetOperandsMutable(); 1246 } 1247 1248 //===----------------------------------------------------------------------===// 1249 // spv.BranchConditionalOp 1250 //===----------------------------------------------------------------------===// 1251 1252 Optional<MutableOperandRange> 1253 spirv::BranchConditionalOp::getMutableSuccessorOperands(unsigned index) { 1254 assert(index < 2 && "invalid successor index"); 1255 return index == kTrueIndex ? trueTargetOperandsMutable() 1256 : falseTargetOperandsMutable(); 1257 } 1258 1259 static ParseResult parseBranchConditionalOp(OpAsmParser &parser, 1260 OperationState &state) { 1261 auto &builder = parser.getBuilder(); 1262 OpAsmParser::OperandType condInfo; 1263 Block *dest; 1264 1265 // Parse the condition. 1266 Type boolTy = builder.getI1Type(); 1267 if (parser.parseOperand(condInfo) || 1268 parser.resolveOperand(condInfo, boolTy, state.operands)) 1269 return failure(); 1270 1271 // Parse the optional branch weights. 1272 if (succeeded(parser.parseOptionalLSquare())) { 1273 IntegerAttr trueWeight, falseWeight; 1274 NamedAttrList weights; 1275 1276 auto i32Type = builder.getIntegerType(32); 1277 if (parser.parseAttribute(trueWeight, i32Type, "weight", weights) || 1278 parser.parseComma() || 1279 parser.parseAttribute(falseWeight, i32Type, "weight", weights) || 1280 parser.parseRSquare()) 1281 return failure(); 1282 1283 state.addAttribute(kBranchWeightAttrName, 1284 builder.getArrayAttr({trueWeight, falseWeight})); 1285 } 1286 1287 // Parse the true branch. 1288 SmallVector<Value, 4> trueOperands; 1289 if (parser.parseComma() || 1290 parser.parseSuccessorAndUseList(dest, trueOperands)) 1291 return failure(); 1292 state.addSuccessors(dest); 1293 state.addOperands(trueOperands); 1294 1295 // Parse the false branch. 1296 SmallVector<Value, 4> falseOperands; 1297 if (parser.parseComma() || 1298 parser.parseSuccessorAndUseList(dest, falseOperands)) 1299 return failure(); 1300 state.addSuccessors(dest); 1301 state.addOperands(falseOperands); 1302 state.addAttribute( 1303 spirv::BranchConditionalOp::getOperandSegmentSizeAttr(), 1304 builder.getI32VectorAttr({1, static_cast<int32_t>(trueOperands.size()), 1305 static_cast<int32_t>(falseOperands.size())})); 1306 1307 return success(); 1308 } 1309 1310 static void print(spirv::BranchConditionalOp branchOp, OpAsmPrinter &printer) { 1311 printer << ' ' << branchOp.condition(); 1312 1313 if (auto weights = branchOp.branch_weights()) { 1314 printer << " ["; 1315 llvm::interleaveComma(weights->getValue(), printer, [&](Attribute a) { 1316 printer << a.cast<IntegerAttr>().getInt(); 1317 }); 1318 printer << "]"; 1319 } 1320 1321 printer << ", "; 1322 printer.printSuccessorAndUseList(branchOp.getTrueBlock(), 1323 branchOp.getTrueBlockArguments()); 1324 printer << ", "; 1325 printer.printSuccessorAndUseList(branchOp.getFalseBlock(), 1326 branchOp.getFalseBlockArguments()); 1327 } 1328 1329 static LogicalResult verify(spirv::BranchConditionalOp branchOp) { 1330 if (auto weights = branchOp.branch_weights()) { 1331 if (weights->getValue().size() != 2) { 1332 return branchOp.emitOpError("must have exactly two branch weights"); 1333 } 1334 if (llvm::all_of(*weights, [](Attribute attr) { 1335 return attr.cast<IntegerAttr>().getValue().isNullValue(); 1336 })) 1337 return branchOp.emitOpError("branch weights cannot both be zero"); 1338 } 1339 1340 return success(); 1341 } 1342 1343 //===----------------------------------------------------------------------===// 1344 // spv.CompositeConstruct 1345 //===----------------------------------------------------------------------===// 1346 1347 static ParseResult parseCompositeConstructOp(OpAsmParser &parser, 1348 OperationState &state) { 1349 SmallVector<OpAsmParser::OperandType, 4> operands; 1350 Type type; 1351 auto loc = parser.getCurrentLocation(); 1352 1353 if (parser.parseOperandList(operands) || parser.parseColonType(type)) { 1354 return failure(); 1355 } 1356 auto cType = type.dyn_cast<spirv::CompositeType>(); 1357 if (!cType) { 1358 return parser.emitError( 1359 loc, "result type must be a composite type, but provided ") 1360 << type; 1361 } 1362 1363 if (cType.hasCompileTimeKnownNumElements() && 1364 operands.size() != cType.getNumElements()) { 1365 return parser.emitError(loc, "has incorrect number of operands: expected ") 1366 << cType.getNumElements() << ", but provided " << operands.size(); 1367 } 1368 // TODO: Add support for constructing a vector type from the vector operands. 1369 // According to the spec: "for constructing a vector, the operands may 1370 // also be vectors with the same component type as the Result Type component 1371 // type". 1372 SmallVector<Type, 4> elementTypes; 1373 elementTypes.reserve(operands.size()); 1374 for (auto index : llvm::seq<uint32_t>(0, operands.size())) { 1375 elementTypes.push_back(cType.getElementType(index)); 1376 } 1377 state.addTypes(type); 1378 return parser.resolveOperands(operands, elementTypes, loc, state.operands); 1379 } 1380 1381 static void print(spirv::CompositeConstructOp compositeConstructOp, 1382 OpAsmPrinter &printer) { 1383 printer << " " << compositeConstructOp.constituents() << " : " 1384 << compositeConstructOp.getResult().getType(); 1385 } 1386 1387 static LogicalResult verify(spirv::CompositeConstructOp compositeConstructOp) { 1388 auto cType = compositeConstructOp.getType().cast<spirv::CompositeType>(); 1389 SmallVector<Value, 4> constituents(compositeConstructOp.constituents()); 1390 1391 if (cType.isa<spirv::CooperativeMatrixNVType>()) { 1392 if (constituents.size() != 1) 1393 return compositeConstructOp.emitError( 1394 "has incorrect number of operands: expected ") 1395 << "1, but provided " << constituents.size(); 1396 } else if (constituents.size() != cType.getNumElements()) { 1397 return compositeConstructOp.emitError( 1398 "has incorrect number of operands: expected ") 1399 << cType.getNumElements() << ", but provided " 1400 << constituents.size(); 1401 } 1402 1403 for (auto index : llvm::seq<uint32_t>(0, constituents.size())) { 1404 if (constituents[index].getType() != cType.getElementType(index)) { 1405 return compositeConstructOp.emitError( 1406 "operand type mismatch: expected operand type ") 1407 << cType.getElementType(index) << ", but provided " 1408 << constituents[index].getType(); 1409 } 1410 } 1411 1412 return success(); 1413 } 1414 1415 //===----------------------------------------------------------------------===// 1416 // spv.CompositeExtractOp 1417 //===----------------------------------------------------------------------===// 1418 1419 void spirv::CompositeExtractOp::build(OpBuilder &builder, OperationState &state, 1420 Value composite, 1421 ArrayRef<int32_t> indices) { 1422 auto indexAttr = builder.getI32ArrayAttr(indices); 1423 auto elementType = 1424 getElementType(composite.getType(), indexAttr, state.location); 1425 if (!elementType) { 1426 return; 1427 } 1428 build(builder, state, elementType, composite, indexAttr); 1429 } 1430 1431 static ParseResult parseCompositeExtractOp(OpAsmParser &parser, 1432 OperationState &state) { 1433 OpAsmParser::OperandType compositeInfo; 1434 Attribute indicesAttr; 1435 Type compositeType; 1436 llvm::SMLoc attrLocation; 1437 1438 if (parser.parseOperand(compositeInfo) || 1439 parser.getCurrentLocation(&attrLocation) || 1440 parser.parseAttribute(indicesAttr, kIndicesAttrName, state.attributes) || 1441 parser.parseColonType(compositeType) || 1442 parser.resolveOperand(compositeInfo, compositeType, state.operands)) { 1443 return failure(); 1444 } 1445 1446 Type resultType = 1447 getElementType(compositeType, indicesAttr, parser, attrLocation); 1448 if (!resultType) { 1449 return failure(); 1450 } 1451 state.addTypes(resultType); 1452 return success(); 1453 } 1454 1455 static void print(spirv::CompositeExtractOp compositeExtractOp, 1456 OpAsmPrinter &printer) { 1457 printer << ' ' << compositeExtractOp.composite() 1458 << compositeExtractOp.indices() << " : " 1459 << compositeExtractOp.composite().getType(); 1460 } 1461 1462 static LogicalResult verify(spirv::CompositeExtractOp compExOp) { 1463 auto indicesArrayAttr = compExOp.indices().dyn_cast<ArrayAttr>(); 1464 auto resultType = getElementType(compExOp.composite().getType(), 1465 indicesArrayAttr, compExOp.getLoc()); 1466 if (!resultType) 1467 return failure(); 1468 1469 if (resultType != compExOp.getType()) { 1470 return compExOp.emitOpError("invalid result type: expected ") 1471 << resultType << " but provided " << compExOp.getType(); 1472 } 1473 1474 return success(); 1475 } 1476 1477 //===----------------------------------------------------------------------===// 1478 // spv.CompositeInsert 1479 //===----------------------------------------------------------------------===// 1480 1481 void spirv::CompositeInsertOp::build(OpBuilder &builder, OperationState &state, 1482 Value object, Value composite, 1483 ArrayRef<int32_t> indices) { 1484 auto indexAttr = builder.getI32ArrayAttr(indices); 1485 build(builder, state, composite.getType(), object, composite, indexAttr); 1486 } 1487 1488 static ParseResult parseCompositeInsertOp(OpAsmParser &parser, 1489 OperationState &state) { 1490 SmallVector<OpAsmParser::OperandType, 2> operands; 1491 Type objectType, compositeType; 1492 Attribute indicesAttr; 1493 auto loc = parser.getCurrentLocation(); 1494 1495 return failure( 1496 parser.parseOperandList(operands, 2) || 1497 parser.parseAttribute(indicesAttr, kIndicesAttrName, state.attributes) || 1498 parser.parseColonType(objectType) || 1499 parser.parseKeywordType("into", compositeType) || 1500 parser.resolveOperands(operands, {objectType, compositeType}, loc, 1501 state.operands) || 1502 parser.addTypesToList(compositeType, state.types)); 1503 } 1504 1505 static LogicalResult verify(spirv::CompositeInsertOp compositeInsertOp) { 1506 auto indicesArrayAttr = compositeInsertOp.indices().dyn_cast<ArrayAttr>(); 1507 auto objectType = 1508 getElementType(compositeInsertOp.composite().getType(), indicesArrayAttr, 1509 compositeInsertOp.getLoc()); 1510 if (!objectType) 1511 return failure(); 1512 1513 if (objectType != compositeInsertOp.object().getType()) { 1514 return compositeInsertOp.emitOpError("object operand type should be ") 1515 << objectType << ", but found " 1516 << compositeInsertOp.object().getType(); 1517 } 1518 1519 if (compositeInsertOp.composite().getType() != compositeInsertOp.getType()) { 1520 return compositeInsertOp.emitOpError("result type should be the same as " 1521 "the composite type, but found ") 1522 << compositeInsertOp.composite().getType() << " vs " 1523 << compositeInsertOp.getType(); 1524 } 1525 1526 return success(); 1527 } 1528 1529 static void print(spirv::CompositeInsertOp compositeInsertOp, 1530 OpAsmPrinter &printer) { 1531 printer << " " << compositeInsertOp.object() << ", " 1532 << compositeInsertOp.composite() << compositeInsertOp.indices() 1533 << " : " << compositeInsertOp.object().getType() << " into " 1534 << compositeInsertOp.composite().getType(); 1535 } 1536 1537 //===----------------------------------------------------------------------===// 1538 // spv.Constant 1539 //===----------------------------------------------------------------------===// 1540 1541 static ParseResult parseConstantOp(OpAsmParser &parser, OperationState &state) { 1542 Attribute value; 1543 if (parser.parseAttribute(value, kValueAttrName, state.attributes)) 1544 return failure(); 1545 1546 Type type = value.getType(); 1547 if (type.isa<NoneType, TensorType>()) { 1548 if (parser.parseColonType(type)) 1549 return failure(); 1550 } 1551 1552 return parser.addTypeToList(type, state.types); 1553 } 1554 1555 static void print(spirv::ConstantOp constOp, OpAsmPrinter &printer) { 1556 printer << ' ' << constOp.value(); 1557 if (constOp.getType().isa<spirv::ArrayType>()) 1558 printer << " : " << constOp.getType(); 1559 } 1560 1561 static LogicalResult verify(spirv::ConstantOp constOp) { 1562 auto opType = constOp.getType(); 1563 auto value = constOp.value(); 1564 auto valueType = value.getType(); 1565 1566 // ODS already generates checks to make sure the result type is valid. We just 1567 // need to additionally check that the value's attribute type is consistent 1568 // with the result type. 1569 if (value.isa<IntegerAttr, FloatAttr>()) { 1570 if (valueType != opType) 1571 return constOp.emitOpError("result type (") 1572 << opType << ") does not match value type (" << valueType << ")"; 1573 return success(); 1574 } 1575 if (value.isa<DenseIntOrFPElementsAttr, SparseElementsAttr>()) { 1576 if (valueType == opType) 1577 return success(); 1578 auto arrayType = opType.dyn_cast<spirv::ArrayType>(); 1579 auto shapedType = valueType.dyn_cast<ShapedType>(); 1580 if (!arrayType) { 1581 return constOp.emitOpError( 1582 "must have spv.array result type for array value"); 1583 } 1584 1585 int numElements = arrayType.getNumElements(); 1586 auto opElemType = arrayType.getElementType(); 1587 while (auto t = opElemType.dyn_cast<spirv::ArrayType>()) { 1588 numElements *= t.getNumElements(); 1589 opElemType = t.getElementType(); 1590 } 1591 if (!opElemType.isIntOrFloat()) 1592 return constOp.emitOpError("only support nested array result type"); 1593 1594 auto valueElemType = shapedType.getElementType(); 1595 if (valueElemType != opElemType) { 1596 return constOp.emitOpError("result element type (") 1597 << opElemType << ") does not match value element type (" 1598 << valueElemType << ")"; 1599 } 1600 1601 if (numElements != shapedType.getNumElements()) { 1602 return constOp.emitOpError("result number of elements (") 1603 << numElements << ") does not match value number of elements (" 1604 << shapedType.getNumElements() << ")"; 1605 } 1606 return success(); 1607 } 1608 if (auto attayAttr = value.dyn_cast<ArrayAttr>()) { 1609 auto arrayType = opType.dyn_cast<spirv::ArrayType>(); 1610 if (!arrayType) 1611 return constOp.emitOpError( 1612 "must have spv.array result type for array value"); 1613 Type elemType = arrayType.getElementType(); 1614 for (Attribute element : attayAttr.getValue()) { 1615 if (element.getType() != elemType) 1616 return constOp.emitOpError("has array element whose type (") 1617 << element.getType() 1618 << ") does not match the result element type (" << elemType 1619 << ')'; 1620 } 1621 return success(); 1622 } 1623 return constOp.emitOpError("cannot have value of type ") << valueType; 1624 } 1625 1626 bool spirv::ConstantOp::isBuildableWith(Type type) { 1627 // Must be valid SPIR-V type first. 1628 if (!type.isa<spirv::SPIRVType>()) 1629 return false; 1630 1631 if (isa<SPIRVDialect>(type.getDialect())) { 1632 // TODO: support constant struct 1633 return type.isa<spirv::ArrayType>(); 1634 } 1635 1636 return true; 1637 } 1638 1639 spirv::ConstantOp spirv::ConstantOp::getZero(Type type, Location loc, 1640 OpBuilder &builder) { 1641 if (auto intType = type.dyn_cast<IntegerType>()) { 1642 unsigned width = intType.getWidth(); 1643 if (width == 1) 1644 return builder.create<spirv::ConstantOp>(loc, type, 1645 builder.getBoolAttr(false)); 1646 return builder.create<spirv::ConstantOp>( 1647 loc, type, builder.getIntegerAttr(type, APInt(width, 0))); 1648 } 1649 if (auto floatType = type.dyn_cast<FloatType>()) { 1650 return builder.create<spirv::ConstantOp>( 1651 loc, type, builder.getFloatAttr(floatType, 0.0)); 1652 } 1653 if (auto vectorType = type.dyn_cast<VectorType>()) { 1654 Type elemType = vectorType.getElementType(); 1655 if (elemType.isa<IntegerType>()) { 1656 return builder.create<spirv::ConstantOp>( 1657 loc, type, 1658 DenseElementsAttr::get(vectorType, 1659 IntegerAttr::get(elemType, 0.0).getValue())); 1660 } 1661 if (elemType.isa<FloatType>()) { 1662 return builder.create<spirv::ConstantOp>( 1663 loc, type, 1664 DenseFPElementsAttr::get(vectorType, 1665 FloatAttr::get(elemType, 0.0).getValue())); 1666 } 1667 } 1668 1669 llvm_unreachable("unimplemented types for ConstantOp::getZero()"); 1670 } 1671 1672 spirv::ConstantOp spirv::ConstantOp::getOne(Type type, Location loc, 1673 OpBuilder &builder) { 1674 if (auto intType = type.dyn_cast<IntegerType>()) { 1675 unsigned width = intType.getWidth(); 1676 if (width == 1) 1677 return builder.create<spirv::ConstantOp>(loc, type, 1678 builder.getBoolAttr(true)); 1679 return builder.create<spirv::ConstantOp>( 1680 loc, type, builder.getIntegerAttr(type, APInt(width, 1))); 1681 } 1682 if (auto floatType = type.dyn_cast<FloatType>()) { 1683 return builder.create<spirv::ConstantOp>( 1684 loc, type, builder.getFloatAttr(floatType, 1.0)); 1685 } 1686 if (auto vectorType = type.dyn_cast<VectorType>()) { 1687 Type elemType = vectorType.getElementType(); 1688 if (elemType.isa<IntegerType>()) { 1689 return builder.create<spirv::ConstantOp>( 1690 loc, type, 1691 DenseElementsAttr::get(vectorType, 1692 IntegerAttr::get(elemType, 1.0).getValue())); 1693 } 1694 if (elemType.isa<FloatType>()) { 1695 return builder.create<spirv::ConstantOp>( 1696 loc, type, 1697 DenseFPElementsAttr::get(vectorType, 1698 FloatAttr::get(elemType, 1.0).getValue())); 1699 } 1700 } 1701 1702 llvm_unreachable("unimplemented types for ConstantOp::getOne()"); 1703 } 1704 1705 void mlir::spirv::ConstantOp::getAsmResultNames( 1706 llvm::function_ref<void(mlir::Value, llvm::StringRef)> setNameFn) { 1707 Type type = getType(); 1708 1709 SmallString<32> specialNameBuffer; 1710 llvm::raw_svector_ostream specialName(specialNameBuffer); 1711 specialName << "cst"; 1712 1713 IntegerType intTy = type.dyn_cast<IntegerType>(); 1714 1715 if (IntegerAttr intCst = value().dyn_cast<IntegerAttr>()) { 1716 if (intTy && intTy.getWidth() == 1) { 1717 return setNameFn(getResult(), (intCst.getInt() ? "true" : "false")); 1718 } 1719 1720 if (intTy.isSignless()) { 1721 specialName << intCst.getInt(); 1722 } else { 1723 specialName << intCst.getSInt(); 1724 } 1725 } 1726 1727 if (intTy || type.isa<FloatType>()) { 1728 specialName << '_' << type; 1729 } 1730 1731 if (auto vecType = type.dyn_cast<VectorType>()) { 1732 specialName << "_vec_"; 1733 specialName << vecType.getDimSize(0); 1734 1735 Type elementType = vecType.getElementType(); 1736 1737 if (elementType.isa<IntegerType>() || elementType.isa<FloatType>()) { 1738 specialName << "x" << elementType; 1739 } 1740 } 1741 1742 setNameFn(getResult(), specialName.str()); 1743 } 1744 1745 void mlir::spirv::AddressOfOp::getAsmResultNames( 1746 llvm::function_ref<void(mlir::Value, llvm::StringRef)> setNameFn) { 1747 SmallString<32> specialNameBuffer; 1748 llvm::raw_svector_ostream specialName(specialNameBuffer); 1749 specialName << variable() << "_addr"; 1750 setNameFn(getResult(), specialName.str()); 1751 } 1752 1753 //===----------------------------------------------------------------------===// 1754 // spv.EntryPoint 1755 //===----------------------------------------------------------------------===// 1756 1757 void spirv::EntryPointOp::build(OpBuilder &builder, OperationState &state, 1758 spirv::ExecutionModel executionModel, 1759 spirv::FuncOp function, 1760 ArrayRef<Attribute> interfaceVars) { 1761 build(builder, state, 1762 spirv::ExecutionModelAttr::get(builder.getContext(), executionModel), 1763 SymbolRefAttr::get(function), builder.getArrayAttr(interfaceVars)); 1764 } 1765 1766 static ParseResult parseEntryPointOp(OpAsmParser &parser, 1767 OperationState &state) { 1768 spirv::ExecutionModel execModel; 1769 SmallVector<OpAsmParser::OperandType, 0> identifiers; 1770 SmallVector<Type, 0> idTypes; 1771 SmallVector<Attribute, 4> interfaceVars; 1772 1773 FlatSymbolRefAttr fn; 1774 if (parseEnumStrAttr(execModel, parser, state) || 1775 parser.parseAttribute(fn, Type(), kFnNameAttrName, state.attributes)) { 1776 return failure(); 1777 } 1778 1779 if (!parser.parseOptionalComma()) { 1780 // Parse the interface variables 1781 do { 1782 // The name of the interface variable attribute isnt important 1783 auto attrName = "var_symbol"; 1784 FlatSymbolRefAttr var; 1785 NamedAttrList attrs; 1786 if (parser.parseAttribute(var, Type(), attrName, attrs)) { 1787 return failure(); 1788 } 1789 interfaceVars.push_back(var); 1790 } while (!parser.parseOptionalComma()); 1791 } 1792 state.addAttribute(kInterfaceAttrName, 1793 parser.getBuilder().getArrayAttr(interfaceVars)); 1794 return success(); 1795 } 1796 1797 static void print(spirv::EntryPointOp entryPointOp, OpAsmPrinter &printer) { 1798 printer << " \"" << stringifyExecutionModel(entryPointOp.execution_model()) 1799 << "\" "; 1800 printer.printSymbolName(entryPointOp.fn()); 1801 auto interfaceVars = entryPointOp.interface().getValue(); 1802 if (!interfaceVars.empty()) { 1803 printer << ", "; 1804 llvm::interleaveComma(interfaceVars, printer); 1805 } 1806 } 1807 1808 static LogicalResult verify(spirv::EntryPointOp entryPointOp) { 1809 // Checks for fn and interface symbol reference are done in spirv::ModuleOp 1810 // verification. 1811 return success(); 1812 } 1813 1814 //===----------------------------------------------------------------------===// 1815 // spv.ExecutionMode 1816 //===----------------------------------------------------------------------===// 1817 1818 void spirv::ExecutionModeOp::build(OpBuilder &builder, OperationState &state, 1819 spirv::FuncOp function, 1820 spirv::ExecutionMode executionMode, 1821 ArrayRef<int32_t> params) { 1822 build(builder, state, SymbolRefAttr::get(function), 1823 spirv::ExecutionModeAttr::get(builder.getContext(), executionMode), 1824 builder.getI32ArrayAttr(params)); 1825 } 1826 1827 static ParseResult parseExecutionModeOp(OpAsmParser &parser, 1828 OperationState &state) { 1829 spirv::ExecutionMode execMode; 1830 Attribute fn; 1831 if (parser.parseAttribute(fn, kFnNameAttrName, state.attributes) || 1832 parseEnumStrAttr(execMode, parser, state)) { 1833 return failure(); 1834 } 1835 1836 SmallVector<int32_t, 4> values; 1837 Type i32Type = parser.getBuilder().getIntegerType(32); 1838 while (!parser.parseOptionalComma()) { 1839 NamedAttrList attr; 1840 Attribute value; 1841 if (parser.parseAttribute(value, i32Type, "value", attr)) { 1842 return failure(); 1843 } 1844 values.push_back(value.cast<IntegerAttr>().getInt()); 1845 } 1846 state.addAttribute(kValuesAttrName, 1847 parser.getBuilder().getI32ArrayAttr(values)); 1848 return success(); 1849 } 1850 1851 static void print(spirv::ExecutionModeOp execModeOp, OpAsmPrinter &printer) { 1852 printer << " "; 1853 printer.printSymbolName(execModeOp.fn()); 1854 printer << " \"" << stringifyExecutionMode(execModeOp.execution_mode()) 1855 << "\""; 1856 auto values = execModeOp.values(); 1857 if (!values.size()) 1858 return; 1859 printer << ", "; 1860 llvm::interleaveComma(values, printer, [&](Attribute a) { 1861 printer << a.cast<IntegerAttr>().getInt(); 1862 }); 1863 } 1864 1865 //===----------------------------------------------------------------------===// 1866 // spv.func 1867 //===----------------------------------------------------------------------===// 1868 1869 static ParseResult parseFuncOp(OpAsmParser &parser, OperationState &state) { 1870 SmallVector<OpAsmParser::OperandType, 4> entryArgs; 1871 SmallVector<NamedAttrList, 4> argAttrs; 1872 SmallVector<NamedAttrList, 4> resultAttrs; 1873 SmallVector<Type, 4> argTypes; 1874 SmallVector<Type, 4> resultTypes; 1875 auto &builder = parser.getBuilder(); 1876 1877 // Parse the name as a symbol. 1878 StringAttr nameAttr; 1879 if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(), 1880 state.attributes)) 1881 return failure(); 1882 1883 // Parse the function signature. 1884 bool isVariadic = false; 1885 if (function_like_impl::parseFunctionSignature( 1886 parser, /*allowVariadic=*/false, entryArgs, argTypes, argAttrs, 1887 isVariadic, resultTypes, resultAttrs)) 1888 return failure(); 1889 1890 auto fnType = builder.getFunctionType(argTypes, resultTypes); 1891 state.addAttribute(function_like_impl::getTypeAttrName(), 1892 TypeAttr::get(fnType)); 1893 1894 // Parse the optional function control keyword. 1895 spirv::FunctionControl fnControl; 1896 if (parseEnumStrAttr(fnControl, parser, state)) 1897 return failure(); 1898 1899 // If additional attributes are present, parse them. 1900 if (parser.parseOptionalAttrDictWithKeyword(state.attributes)) 1901 return failure(); 1902 1903 // Add the attributes to the function arguments. 1904 assert(argAttrs.size() == argTypes.size()); 1905 assert(resultAttrs.size() == resultTypes.size()); 1906 function_like_impl::addArgAndResultAttrs(builder, state, argAttrs, 1907 resultAttrs); 1908 1909 // Parse the optional function body. 1910 auto *body = state.addRegion(); 1911 OptionalParseResult result = parser.parseOptionalRegion( 1912 *body, entryArgs, entryArgs.empty() ? ArrayRef<Type>() : argTypes); 1913 return failure(result.hasValue() && failed(*result)); 1914 } 1915 1916 static void print(spirv::FuncOp fnOp, OpAsmPrinter &printer) { 1917 // Print function name, signature, and control. 1918 printer << " "; 1919 printer.printSymbolName(fnOp.sym_name()); 1920 auto fnType = fnOp.getType(); 1921 function_like_impl::printFunctionSignature(printer, fnOp, fnType.getInputs(), 1922 /*isVariadic=*/false, 1923 fnType.getResults()); 1924 printer << " \"" << spirv::stringifyFunctionControl(fnOp.function_control()) 1925 << "\""; 1926 function_like_impl::printFunctionAttributes( 1927 printer, fnOp, fnType.getNumInputs(), fnType.getNumResults(), 1928 {spirv::attributeName<spirv::FunctionControl>()}); 1929 1930 // Print the body if this is not an external function. 1931 Region &body = fnOp.body(); 1932 if (!body.empty()) 1933 printer.printRegion(body, /*printEntryBlockArgs=*/false, 1934 /*printBlockTerminators=*/true); 1935 } 1936 1937 LogicalResult spirv::FuncOp::verifyType() { 1938 auto type = getTypeAttr().getValue(); 1939 if (!type.isa<FunctionType>()) 1940 return emitOpError("requires '" + getTypeAttrName() + 1941 "' attribute of function type"); 1942 if (getType().getNumResults() > 1) 1943 return emitOpError("cannot have more than one result"); 1944 return success(); 1945 } 1946 1947 LogicalResult spirv::FuncOp::verifyBody() { 1948 FunctionType fnType = getType(); 1949 1950 auto walkResult = walk([fnType](Operation *op) -> WalkResult { 1951 if (auto retOp = dyn_cast<spirv::ReturnOp>(op)) { 1952 if (fnType.getNumResults() != 0) 1953 return retOp.emitOpError("cannot be used in functions returning value"); 1954 } else if (auto retOp = dyn_cast<spirv::ReturnValueOp>(op)) { 1955 if (fnType.getNumResults() != 1) 1956 return retOp.emitOpError( 1957 "returns 1 value but enclosing function requires ") 1958 << fnType.getNumResults() << " results"; 1959 1960 auto retOperandType = retOp.value().getType(); 1961 auto fnResultType = fnType.getResult(0); 1962 if (retOperandType != fnResultType) 1963 return retOp.emitOpError(" return value's type (") 1964 << retOperandType << ") mismatch with function's result type (" 1965 << fnResultType << ")"; 1966 } 1967 return WalkResult::advance(); 1968 }); 1969 1970 // TODO: verify other bits like linkage type. 1971 1972 return failure(walkResult.wasInterrupted()); 1973 } 1974 1975 void spirv::FuncOp::build(OpBuilder &builder, OperationState &state, 1976 StringRef name, FunctionType type, 1977 spirv::FunctionControl control, 1978 ArrayRef<NamedAttribute> attrs) { 1979 state.addAttribute(SymbolTable::getSymbolAttrName(), 1980 builder.getStringAttr(name)); 1981 state.addAttribute(getTypeAttrName(), TypeAttr::get(type)); 1982 state.addAttribute(spirv::attributeName<spirv::FunctionControl>(), 1983 builder.getI32IntegerAttr(static_cast<uint32_t>(control))); 1984 state.attributes.append(attrs.begin(), attrs.end()); 1985 state.addRegion(); 1986 } 1987 1988 // CallableOpInterface 1989 Region *spirv::FuncOp::getCallableRegion() { 1990 return isExternal() ? nullptr : &body(); 1991 } 1992 1993 // CallableOpInterface 1994 ArrayRef<Type> spirv::FuncOp::getCallableResults() { 1995 return getType().getResults(); 1996 } 1997 1998 //===----------------------------------------------------------------------===// 1999 // spv.FunctionCall 2000 //===----------------------------------------------------------------------===// 2001 2002 static LogicalResult verify(spirv::FunctionCallOp functionCallOp) { 2003 auto fnName = functionCallOp.calleeAttr(); 2004 2005 auto funcOp = 2006 dyn_cast_or_null<spirv::FuncOp>(SymbolTable::lookupNearestSymbolFrom( 2007 functionCallOp->getParentOp(), fnName)); 2008 if (!funcOp) { 2009 return functionCallOp.emitOpError("callee function '") 2010 << fnName.getValue() << "' not found in nearest symbol table"; 2011 } 2012 2013 auto functionType = funcOp.getType(); 2014 2015 if (functionCallOp.getNumResults() > 1) { 2016 return functionCallOp.emitOpError( 2017 "expected callee function to have 0 or 1 result, but provided ") 2018 << functionCallOp.getNumResults(); 2019 } 2020 2021 if (functionType.getNumInputs() != functionCallOp.getNumOperands()) { 2022 return functionCallOp.emitOpError( 2023 "has incorrect number of operands for callee: expected ") 2024 << functionType.getNumInputs() << ", but provided " 2025 << functionCallOp.getNumOperands(); 2026 } 2027 2028 for (uint32_t i = 0, e = functionType.getNumInputs(); i != e; ++i) { 2029 if (functionCallOp.getOperand(i).getType() != functionType.getInput(i)) { 2030 return functionCallOp.emitOpError( 2031 "operand type mismatch: expected operand type ") 2032 << functionType.getInput(i) << ", but provided " 2033 << functionCallOp.getOperand(i).getType() << " for operand number " 2034 << i; 2035 } 2036 } 2037 2038 if (functionType.getNumResults() != functionCallOp.getNumResults()) { 2039 return functionCallOp.emitOpError( 2040 "has incorrect number of results has for callee: expected ") 2041 << functionType.getNumResults() << ", but provided " 2042 << functionCallOp.getNumResults(); 2043 } 2044 2045 if (functionCallOp.getNumResults() && 2046 (functionCallOp.getResult(0).getType() != functionType.getResult(0))) { 2047 return functionCallOp.emitOpError("result type mismatch: expected ") 2048 << functionType.getResult(0) << ", but provided " 2049 << functionCallOp.getResult(0).getType(); 2050 } 2051 2052 return success(); 2053 } 2054 2055 CallInterfaceCallable spirv::FunctionCallOp::getCallableForCallee() { 2056 return (*this)->getAttrOfType<SymbolRefAttr>(kCallee); 2057 } 2058 2059 Operation::operand_range spirv::FunctionCallOp::getArgOperands() { 2060 return arguments(); 2061 } 2062 2063 //===----------------------------------------------------------------------===// 2064 // spv.GlobalVariable 2065 //===----------------------------------------------------------------------===// 2066 2067 void spirv::GlobalVariableOp::build(OpBuilder &builder, OperationState &state, 2068 Type type, StringRef name, 2069 unsigned descriptorSet, unsigned binding) { 2070 build(builder, state, TypeAttr::get(type), builder.getStringAttr(name), 2071 nullptr); 2072 state.addAttribute( 2073 spirv::SPIRVDialect::getAttributeName(spirv::Decoration::DescriptorSet), 2074 builder.getI32IntegerAttr(descriptorSet)); 2075 state.addAttribute( 2076 spirv::SPIRVDialect::getAttributeName(spirv::Decoration::Binding), 2077 builder.getI32IntegerAttr(binding)); 2078 } 2079 2080 void spirv::GlobalVariableOp::build(OpBuilder &builder, OperationState &state, 2081 Type type, StringRef name, 2082 spirv::BuiltIn builtin) { 2083 build(builder, state, TypeAttr::get(type), builder.getStringAttr(name), 2084 nullptr); 2085 state.addAttribute( 2086 spirv::SPIRVDialect::getAttributeName(spirv::Decoration::BuiltIn), 2087 builder.getStringAttr(spirv::stringifyBuiltIn(builtin))); 2088 } 2089 2090 static ParseResult parseGlobalVariableOp(OpAsmParser &parser, 2091 OperationState &state) { 2092 // Parse variable name. 2093 StringAttr nameAttr; 2094 if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(), 2095 state.attributes)) { 2096 return failure(); 2097 } 2098 2099 // Parse optional initializer 2100 if (succeeded(parser.parseOptionalKeyword(kInitializerAttrName))) { 2101 FlatSymbolRefAttr initSymbol; 2102 if (parser.parseLParen() || 2103 parser.parseAttribute(initSymbol, Type(), kInitializerAttrName, 2104 state.attributes) || 2105 parser.parseRParen()) 2106 return failure(); 2107 } 2108 2109 if (parseVariableDecorations(parser, state)) { 2110 return failure(); 2111 } 2112 2113 Type type; 2114 auto loc = parser.getCurrentLocation(); 2115 if (parser.parseColonType(type)) { 2116 return failure(); 2117 } 2118 if (!type.isa<spirv::PointerType>()) { 2119 return parser.emitError(loc, "expected spv.ptr type"); 2120 } 2121 state.addAttribute(kTypeAttrName, TypeAttr::get(type)); 2122 2123 return success(); 2124 } 2125 2126 static void print(spirv::GlobalVariableOp varOp, OpAsmPrinter &printer) { 2127 auto *op = varOp.getOperation(); 2128 SmallVector<StringRef, 4> elidedAttrs{ 2129 spirv::attributeName<spirv::StorageClass>()}; 2130 2131 // Print variable name. 2132 printer << ' '; 2133 printer.printSymbolName(varOp.sym_name()); 2134 elidedAttrs.push_back(SymbolTable::getSymbolAttrName()); 2135 2136 // Print optional initializer 2137 if (auto initializer = varOp.initializer()) { 2138 printer << " " << kInitializerAttrName << '('; 2139 printer.printSymbolName(initializer.getValue()); 2140 printer << ')'; 2141 elidedAttrs.push_back(kInitializerAttrName); 2142 } 2143 2144 elidedAttrs.push_back(kTypeAttrName); 2145 printVariableDecorations(op, printer, elidedAttrs); 2146 printer << " : " << varOp.type(); 2147 } 2148 2149 static LogicalResult verify(spirv::GlobalVariableOp varOp) { 2150 // SPIR-V spec: "Storage Class is the Storage Class of the memory holding the 2151 // object. It cannot be Generic. It must be the same as the Storage Class 2152 // operand of the Result Type." 2153 // Also, Function storage class is reserved by spv.Variable. 2154 auto storageClass = varOp.storageClass(); 2155 if (storageClass == spirv::StorageClass::Generic || 2156 storageClass == spirv::StorageClass::Function) { 2157 return varOp.emitOpError("storage class cannot be '") 2158 << stringifyStorageClass(storageClass) << "'"; 2159 } 2160 2161 if (auto init = 2162 varOp->getAttrOfType<FlatSymbolRefAttr>(kInitializerAttrName)) { 2163 Operation *initOp = SymbolTable::lookupNearestSymbolFrom( 2164 varOp->getParentOp(), init.getAttr()); 2165 // TODO: Currently only variable initialization with specialization 2166 // constants and other variables is supported. They could be normal 2167 // constants in the module scope as well. 2168 if (!initOp || 2169 !isa<spirv::GlobalVariableOp, spirv::SpecConstantOp>(initOp)) { 2170 return varOp.emitOpError("initializer must be result of a " 2171 "spv.SpecConstant or spv.GlobalVariable op"); 2172 } 2173 } 2174 2175 return success(); 2176 } 2177 2178 //===----------------------------------------------------------------------===// 2179 // spv.GroupBroadcast 2180 //===----------------------------------------------------------------------===// 2181 2182 static LogicalResult verify(spirv::GroupBroadcastOp broadcastOp) { 2183 spirv::Scope scope = broadcastOp.execution_scope(); 2184 if (scope != spirv::Scope::Workgroup && scope != spirv::Scope::Subgroup) 2185 return broadcastOp.emitOpError( 2186 "execution scope must be 'Workgroup' or 'Subgroup'"); 2187 2188 if (auto localIdTy = broadcastOp.localid().getType().dyn_cast<VectorType>()) 2189 if (!(localIdTy.getNumElements() == 2 || localIdTy.getNumElements() == 3)) 2190 return broadcastOp.emitOpError("localid is a vector and can be with only " 2191 " 2 or 3 components, actual number is ") 2192 << localIdTy.getNumElements(); 2193 2194 return success(); 2195 } 2196 2197 //===----------------------------------------------------------------------===// 2198 // spv.GroupNonUniformBallotOp 2199 //===----------------------------------------------------------------------===// 2200 2201 static LogicalResult verify(spirv::GroupNonUniformBallotOp ballotOp) { 2202 spirv::Scope scope = ballotOp.execution_scope(); 2203 if (scope != spirv::Scope::Workgroup && scope != spirv::Scope::Subgroup) 2204 return ballotOp.emitOpError( 2205 "execution scope must be 'Workgroup' or 'Subgroup'"); 2206 2207 return success(); 2208 } 2209 2210 //===----------------------------------------------------------------------===// 2211 // spv.GroupNonUniformBroadcast 2212 //===----------------------------------------------------------------------===// 2213 2214 static LogicalResult verify(spirv::GroupNonUniformBroadcastOp broadcastOp) { 2215 spirv::Scope scope = broadcastOp.execution_scope(); 2216 if (scope != spirv::Scope::Workgroup && scope != spirv::Scope::Subgroup) 2217 return broadcastOp.emitOpError( 2218 "execution scope must be 'Workgroup' or 'Subgroup'"); 2219 2220 // SPIR-V spec: "Before version 1.5, Id must come from a 2221 // constant instruction. 2222 auto targetEnv = spirv::getDefaultTargetEnv(broadcastOp.getContext()); 2223 if (auto spirvModule = broadcastOp->getParentOfType<spirv::ModuleOp>()) 2224 targetEnv = spirv::lookupTargetEnvOrDefault(spirvModule); 2225 2226 if (targetEnv.getVersion() < spirv::Version::V_1_5) { 2227 auto *idOp = broadcastOp.id().getDefiningOp(); 2228 if (!idOp || !isa<spirv::ConstantOp, // for normal constant 2229 spirv::ReferenceOfOp>(idOp)) // for spec constant 2230 return broadcastOp.emitOpError("id must be the result of a constant op"); 2231 } 2232 2233 return success(); 2234 } 2235 2236 //===----------------------------------------------------------------------===// 2237 // spv.SubgroupBlockReadINTEL 2238 //===----------------------------------------------------------------------===// 2239 2240 static ParseResult parseSubgroupBlockReadINTELOp(OpAsmParser &parser, 2241 OperationState &state) { 2242 // Parse the storage class specification 2243 spirv::StorageClass storageClass; 2244 OpAsmParser::OperandType ptrInfo; 2245 Type elementType; 2246 if (parseEnumStrAttr(storageClass, parser) || parser.parseOperand(ptrInfo) || 2247 parser.parseColon() || parser.parseType(elementType)) { 2248 return failure(); 2249 } 2250 2251 auto ptrType = spirv::PointerType::get(elementType, storageClass); 2252 if (auto valVecTy = elementType.dyn_cast<VectorType>()) 2253 ptrType = spirv::PointerType::get(valVecTy.getElementType(), storageClass); 2254 2255 if (parser.resolveOperand(ptrInfo, ptrType, state.operands)) { 2256 return failure(); 2257 } 2258 2259 state.addTypes(elementType); 2260 return success(); 2261 } 2262 2263 static void print(spirv::SubgroupBlockReadINTELOp blockReadOp, 2264 OpAsmPrinter &printer) { 2265 SmallVector<StringRef, 4> elidedAttrs; 2266 printer << " " << blockReadOp.ptr(); 2267 printer << " : " << blockReadOp.getType(); 2268 } 2269 2270 static LogicalResult verify(spirv::SubgroupBlockReadINTELOp blockReadOp) { 2271 if (failed(verifyBlockReadWritePtrAndValTypes(blockReadOp, blockReadOp.ptr(), 2272 blockReadOp.value()))) 2273 return failure(); 2274 2275 return success(); 2276 } 2277 2278 //===----------------------------------------------------------------------===// 2279 // spv.SubgroupBlockWriteINTEL 2280 //===----------------------------------------------------------------------===// 2281 2282 static ParseResult parseSubgroupBlockWriteINTELOp(OpAsmParser &parser, 2283 OperationState &state) { 2284 // Parse the storage class specification 2285 spirv::StorageClass storageClass; 2286 SmallVector<OpAsmParser::OperandType, 2> operandInfo; 2287 auto loc = parser.getCurrentLocation(); 2288 Type elementType; 2289 if (parseEnumStrAttr(storageClass, parser) || 2290 parser.parseOperandList(operandInfo, 2) || parser.parseColon() || 2291 parser.parseType(elementType)) { 2292 return failure(); 2293 } 2294 2295 auto ptrType = spirv::PointerType::get(elementType, storageClass); 2296 if (auto valVecTy = elementType.dyn_cast<VectorType>()) 2297 ptrType = spirv::PointerType::get(valVecTy.getElementType(), storageClass); 2298 2299 if (parser.resolveOperands(operandInfo, {ptrType, elementType}, loc, 2300 state.operands)) { 2301 return failure(); 2302 } 2303 return success(); 2304 } 2305 2306 static void print(spirv::SubgroupBlockWriteINTELOp blockWriteOp, 2307 OpAsmPrinter &printer) { 2308 SmallVector<StringRef, 4> elidedAttrs; 2309 printer << " " << blockWriteOp.ptr() << ", " << blockWriteOp.value(); 2310 printer << " : " << blockWriteOp.value().getType(); 2311 } 2312 2313 static LogicalResult verify(spirv::SubgroupBlockWriteINTELOp blockWriteOp) { 2314 if (failed(verifyBlockReadWritePtrAndValTypes( 2315 blockWriteOp, blockWriteOp.ptr(), blockWriteOp.value()))) 2316 return failure(); 2317 2318 return success(); 2319 } 2320 2321 //===----------------------------------------------------------------------===// 2322 // spv.GroupNonUniformElectOp 2323 //===----------------------------------------------------------------------===// 2324 2325 void spirv::GroupNonUniformElectOp::build(OpBuilder &builder, 2326 OperationState &state, 2327 spirv::Scope scope) { 2328 build(builder, state, builder.getI1Type(), scope); 2329 } 2330 2331 static LogicalResult verify(spirv::GroupNonUniformElectOp groupOp) { 2332 spirv::Scope scope = groupOp.execution_scope(); 2333 if (scope != spirv::Scope::Workgroup && scope != spirv::Scope::Subgroup) 2334 return groupOp.emitOpError( 2335 "execution scope must be 'Workgroup' or 'Subgroup'"); 2336 2337 return success(); 2338 } 2339 2340 //===----------------------------------------------------------------------===// 2341 // spv.LoadOp 2342 //===----------------------------------------------------------------------===// 2343 2344 void spirv::LoadOp::build(OpBuilder &builder, OperationState &state, 2345 Value basePtr, MemoryAccessAttr memoryAccess, 2346 IntegerAttr alignment) { 2347 auto ptrType = basePtr.getType().cast<spirv::PointerType>(); 2348 build(builder, state, ptrType.getPointeeType(), basePtr, memoryAccess, 2349 alignment); 2350 } 2351 2352 static ParseResult parseLoadOp(OpAsmParser &parser, OperationState &state) { 2353 // Parse the storage class specification 2354 spirv::StorageClass storageClass; 2355 OpAsmParser::OperandType ptrInfo; 2356 Type elementType; 2357 if (parseEnumStrAttr(storageClass, parser) || parser.parseOperand(ptrInfo) || 2358 parseMemoryAccessAttributes(parser, state) || 2359 parser.parseOptionalAttrDict(state.attributes) || parser.parseColon() || 2360 parser.parseType(elementType)) { 2361 return failure(); 2362 } 2363 2364 auto ptrType = spirv::PointerType::get(elementType, storageClass); 2365 if (parser.resolveOperand(ptrInfo, ptrType, state.operands)) { 2366 return failure(); 2367 } 2368 2369 state.addTypes(elementType); 2370 return success(); 2371 } 2372 2373 static void print(spirv::LoadOp loadOp, OpAsmPrinter &printer) { 2374 auto *op = loadOp.getOperation(); 2375 SmallVector<StringRef, 4> elidedAttrs; 2376 StringRef sc = stringifyStorageClass( 2377 loadOp.ptr().getType().cast<spirv::PointerType>().getStorageClass()); 2378 printer << " \"" << sc << "\" " << loadOp.ptr(); 2379 2380 printMemoryAccessAttribute(loadOp, printer, elidedAttrs); 2381 2382 printer.printOptionalAttrDict(op->getAttrs(), elidedAttrs); 2383 printer << " : " << loadOp.getType(); 2384 } 2385 2386 static LogicalResult verify(spirv::LoadOp loadOp) { 2387 // SPIR-V spec : "Result Type is the type of the loaded object. It must be a 2388 // type with fixed size; i.e., it cannot be, nor include, any 2389 // OpTypeRuntimeArray types." 2390 if (failed(verifyLoadStorePtrAndValTypes(loadOp, loadOp.ptr(), 2391 loadOp.value()))) { 2392 return failure(); 2393 } 2394 return verifyMemoryAccessAttribute(loadOp); 2395 } 2396 2397 //===----------------------------------------------------------------------===// 2398 // spv.mlir.loop 2399 //===----------------------------------------------------------------------===// 2400 2401 void spirv::LoopOp::build(OpBuilder &builder, OperationState &state) { 2402 state.addAttribute("loop_control", 2403 builder.getI32IntegerAttr( 2404 static_cast<uint32_t>(spirv::LoopControl::None))); 2405 state.addRegion(); 2406 } 2407 2408 static ParseResult parseLoopOp(OpAsmParser &parser, OperationState &state) { 2409 if (parseControlAttribute<spirv::LoopControl>(parser, state)) 2410 return failure(); 2411 return parser.parseRegion(*state.addRegion(), /*arguments=*/{}, 2412 /*argTypes=*/{}); 2413 } 2414 2415 static void print(spirv::LoopOp loopOp, OpAsmPrinter &printer) { 2416 auto *op = loopOp.getOperation(); 2417 2418 auto control = loopOp.loop_control(); 2419 if (control != spirv::LoopControl::None) 2420 printer << " control(" << spirv::stringifyLoopControl(control) << ")"; 2421 printer.printRegion(op->getRegion(0), /*printEntryBlockArgs=*/false, 2422 /*printBlockTerminators=*/true); 2423 } 2424 2425 /// Returns true if the given `srcBlock` contains only one `spv.Branch` to the 2426 /// given `dstBlock`. 2427 static inline bool hasOneBranchOpTo(Block &srcBlock, Block &dstBlock) { 2428 // Check that there is only one op in the `srcBlock`. 2429 if (!llvm::hasSingleElement(srcBlock)) 2430 return false; 2431 2432 auto branchOp = dyn_cast<spirv::BranchOp>(srcBlock.back()); 2433 return branchOp && branchOp.getSuccessor() == &dstBlock; 2434 } 2435 2436 static LogicalResult verify(spirv::LoopOp loopOp) { 2437 auto *op = loopOp.getOperation(); 2438 2439 // We need to verify that the blocks follow the following layout: 2440 // 2441 // +-------------+ 2442 // | entry block | 2443 // +-------------+ 2444 // | 2445 // v 2446 // +-------------+ 2447 // | loop header | <-----+ 2448 // +-------------+ | 2449 // | 2450 // ... | 2451 // \ | / | 2452 // v | 2453 // +---------------+ | 2454 // | loop continue | -----+ 2455 // +---------------+ 2456 // 2457 // ... 2458 // \ | / 2459 // v 2460 // +-------------+ 2461 // | merge block | 2462 // +-------------+ 2463 2464 auto ®ion = op->getRegion(0); 2465 // Allow empty region as a degenerated case, which can come from 2466 // optimizations. 2467 if (region.empty()) 2468 return success(); 2469 2470 // The last block is the merge block. 2471 Block &merge = region.back(); 2472 if (!isMergeBlock(merge)) 2473 return loopOp.emitOpError( 2474 "last block must be the merge block with only one 'spv.mlir.merge' op"); 2475 2476 if (std::next(region.begin()) == region.end()) 2477 return loopOp.emitOpError( 2478 "must have an entry block branching to the loop header block"); 2479 // The first block is the entry block. 2480 Block &entry = region.front(); 2481 2482 if (std::next(region.begin(), 2) == region.end()) 2483 return loopOp.emitOpError( 2484 "must have a loop header block branched from the entry block"); 2485 // The second block is the loop header block. 2486 Block &header = *std::next(region.begin(), 1); 2487 2488 if (!hasOneBranchOpTo(entry, header)) 2489 return loopOp.emitOpError( 2490 "entry block must only have one 'spv.Branch' op to the second block"); 2491 2492 if (std::next(region.begin(), 3) == region.end()) 2493 return loopOp.emitOpError( 2494 "requires a loop continue block branching to the loop header block"); 2495 // The second to last block is the loop continue block. 2496 Block &cont = *std::prev(region.end(), 2); 2497 2498 // Make sure that we have a branch from the loop continue block to the loop 2499 // header block. 2500 if (llvm::none_of( 2501 llvm::seq<unsigned>(0, cont.getNumSuccessors()), 2502 [&](unsigned index) { return cont.getSuccessor(index) == &header; })) 2503 return loopOp.emitOpError("second to last block must be the loop continue " 2504 "block that branches to the loop header block"); 2505 2506 // Make sure that no other blocks (except the entry and loop continue block) 2507 // branches to the loop header block. 2508 for (auto &block : llvm::make_range(std::next(region.begin(), 2), 2509 std::prev(region.end(), 2))) { 2510 for (auto i : llvm::seq<unsigned>(0, block.getNumSuccessors())) { 2511 if (block.getSuccessor(i) == &header) { 2512 return loopOp.emitOpError("can only have the entry and loop continue " 2513 "block branching to the loop header block"); 2514 } 2515 } 2516 } 2517 2518 return success(); 2519 } 2520 2521 Block *spirv::LoopOp::getEntryBlock() { 2522 assert(!body().empty() && "op region should not be empty!"); 2523 return &body().front(); 2524 } 2525 2526 Block *spirv::LoopOp::getHeaderBlock() { 2527 assert(!body().empty() && "op region should not be empty!"); 2528 // The second block is the loop header block. 2529 return &*std::next(body().begin()); 2530 } 2531 2532 Block *spirv::LoopOp::getContinueBlock() { 2533 assert(!body().empty() && "op region should not be empty!"); 2534 // The second to last block is the loop continue block. 2535 return &*std::prev(body().end(), 2); 2536 } 2537 2538 Block *spirv::LoopOp::getMergeBlock() { 2539 assert(!body().empty() && "op region should not be empty!"); 2540 // The last block is the loop merge block. 2541 return &body().back(); 2542 } 2543 2544 void spirv::LoopOp::addEntryAndMergeBlock() { 2545 assert(body().empty() && "entry and merge block already exist"); 2546 body().push_back(new Block()); 2547 auto *mergeBlock = new Block(); 2548 body().push_back(mergeBlock); 2549 OpBuilder builder = OpBuilder::atBlockEnd(mergeBlock); 2550 2551 // Add a spv.mlir.merge op into the merge block. 2552 builder.create<spirv::MergeOp>(getLoc()); 2553 } 2554 2555 //===----------------------------------------------------------------------===// 2556 // spv.mlir.merge 2557 //===----------------------------------------------------------------------===// 2558 2559 static LogicalResult verify(spirv::MergeOp mergeOp) { 2560 auto *parentOp = mergeOp->getParentOp(); 2561 if (!parentOp || !isa<spirv::SelectionOp, spirv::LoopOp>(parentOp)) 2562 return mergeOp.emitOpError( 2563 "expected parent op to be 'spv.mlir.selection' or 'spv.mlir.loop'"); 2564 2565 Block &parentLastBlock = mergeOp->getParentRegion()->back(); 2566 if (mergeOp.getOperation() != parentLastBlock.getTerminator()) 2567 return mergeOp.emitOpError("can only be used in the last block of " 2568 "'spv.mlir.selection' or 'spv.mlir.loop'"); 2569 return success(); 2570 } 2571 2572 //===----------------------------------------------------------------------===// 2573 // spv.module 2574 //===----------------------------------------------------------------------===// 2575 2576 void spirv::ModuleOp::build(OpBuilder &builder, OperationState &state, 2577 Optional<StringRef> name) { 2578 OpBuilder::InsertionGuard guard(builder); 2579 builder.createBlock(state.addRegion()); 2580 if (name) { 2581 state.attributes.append(mlir::SymbolTable::getSymbolAttrName(), 2582 builder.getStringAttr(*name)); 2583 } 2584 } 2585 2586 void spirv::ModuleOp::build(OpBuilder &builder, OperationState &state, 2587 spirv::AddressingModel addressingModel, 2588 spirv::MemoryModel memoryModel, 2589 Optional<VerCapExtAttr> vceTriple, 2590 Optional<StringRef> name) { 2591 state.addAttribute( 2592 "addressing_model", 2593 builder.getI32IntegerAttr(static_cast<int32_t>(addressingModel))); 2594 state.addAttribute("memory_model", builder.getI32IntegerAttr( 2595 static_cast<int32_t>(memoryModel))); 2596 OpBuilder::InsertionGuard guard(builder); 2597 builder.createBlock(state.addRegion()); 2598 if (vceTriple) 2599 state.addAttribute(getVCETripleAttrName(), *vceTriple); 2600 if (name) 2601 state.addAttribute(mlir::SymbolTable::getSymbolAttrName(), 2602 builder.getStringAttr(*name)); 2603 } 2604 2605 static ParseResult parseModuleOp(OpAsmParser &parser, OperationState &state) { 2606 Region *body = state.addRegion(); 2607 2608 // If the name is present, parse it. 2609 StringAttr nameAttr; 2610 parser.parseOptionalSymbolName(nameAttr, SymbolTable::getSymbolAttrName(), 2611 state.attributes); 2612 2613 // Parse attributes 2614 spirv::AddressingModel addrModel; 2615 spirv::MemoryModel memoryModel; 2616 if (parseEnumKeywordAttr(addrModel, parser, state) || 2617 parseEnumKeywordAttr(memoryModel, parser, state)) 2618 return failure(); 2619 2620 if (succeeded(parser.parseOptionalKeyword("requires"))) { 2621 spirv::VerCapExtAttr vceTriple; 2622 if (parser.parseAttribute(vceTriple, 2623 spirv::ModuleOp::getVCETripleAttrName(), 2624 state.attributes)) 2625 return failure(); 2626 } 2627 2628 if (parser.parseOptionalAttrDictWithKeyword(state.attributes)) 2629 return failure(); 2630 2631 if (parser.parseRegion(*body, /*arguments=*/{}, /*argTypes=*/{})) 2632 return failure(); 2633 2634 // Make sure we have at least one block. 2635 if (body->empty()) 2636 body->push_back(new Block()); 2637 2638 return success(); 2639 } 2640 2641 static void print(spirv::ModuleOp moduleOp, OpAsmPrinter &printer) { 2642 if (Optional<StringRef> name = moduleOp.getName()) { 2643 printer << ' '; 2644 printer.printSymbolName(*name); 2645 } 2646 2647 SmallVector<StringRef, 2> elidedAttrs; 2648 2649 printer << " " << spirv::stringifyAddressingModel(moduleOp.addressing_model()) 2650 << " " << spirv::stringifyMemoryModel(moduleOp.memory_model()); 2651 auto addressingModelAttrName = spirv::attributeName<spirv::AddressingModel>(); 2652 auto memoryModelAttrName = spirv::attributeName<spirv::MemoryModel>(); 2653 elidedAttrs.assign({addressingModelAttrName, memoryModelAttrName, 2654 SymbolTable::getSymbolAttrName()}); 2655 2656 if (Optional<spirv::VerCapExtAttr> triple = moduleOp.vce_triple()) { 2657 printer << " requires " << *triple; 2658 elidedAttrs.push_back(spirv::ModuleOp::getVCETripleAttrName()); 2659 } 2660 2661 printer.printOptionalAttrDictWithKeyword(moduleOp->getAttrs(), elidedAttrs); 2662 printer.printRegion(moduleOp.getRegion()); 2663 } 2664 2665 static LogicalResult verify(spirv::ModuleOp moduleOp) { 2666 auto &op = *moduleOp.getOperation(); 2667 auto *dialect = op.getDialect(); 2668 DenseMap<std::pair<spirv::FuncOp, spirv::ExecutionModel>, spirv::EntryPointOp> 2669 entryPoints; 2670 SymbolTable table(moduleOp); 2671 2672 for (auto &op : *moduleOp.getBody()) { 2673 if (op.getDialect() != dialect) 2674 return op.emitError("'spv.module' can only contain spv.* ops"); 2675 2676 // For EntryPoint op, check that the function and execution model is not 2677 // duplicated in EntryPointOps. Also verify that the interface specified 2678 // comes from globalVariables here to make this check cheaper. 2679 if (auto entryPointOp = dyn_cast<spirv::EntryPointOp>(op)) { 2680 auto funcOp = table.lookup<spirv::FuncOp>(entryPointOp.fn()); 2681 if (!funcOp) { 2682 return entryPointOp.emitError("function '") 2683 << entryPointOp.fn() << "' not found in 'spv.module'"; 2684 } 2685 if (auto interface = entryPointOp.interface()) { 2686 for (Attribute varRef : interface) { 2687 auto varSymRef = varRef.dyn_cast<FlatSymbolRefAttr>(); 2688 if (!varSymRef) { 2689 return entryPointOp.emitError( 2690 "expected symbol reference for interface " 2691 "specification instead of '") 2692 << varRef; 2693 } 2694 auto variableOp = 2695 table.lookup<spirv::GlobalVariableOp>(varSymRef.getValue()); 2696 if (!variableOp) { 2697 return entryPointOp.emitError("expected spv.GlobalVariable " 2698 "symbol reference instead of'") 2699 << varSymRef << "'"; 2700 } 2701 } 2702 } 2703 2704 auto key = std::pair<spirv::FuncOp, spirv::ExecutionModel>( 2705 funcOp, entryPointOp.execution_model()); 2706 auto entryPtIt = entryPoints.find(key); 2707 if (entryPtIt != entryPoints.end()) { 2708 return entryPointOp.emitError("duplicate of a previous EntryPointOp"); 2709 } 2710 entryPoints[key] = entryPointOp; 2711 } else if (auto funcOp = dyn_cast<spirv::FuncOp>(op)) { 2712 if (funcOp.isExternal()) 2713 return op.emitError("'spv.module' cannot contain external functions"); 2714 2715 // TODO: move this check to spv.func. 2716 for (auto &block : funcOp) 2717 for (auto &op : block) { 2718 if (op.getDialect() != dialect) 2719 return op.emitError( 2720 "functions in 'spv.module' can only contain spv.* ops"); 2721 } 2722 } 2723 } 2724 2725 return success(); 2726 } 2727 2728 //===----------------------------------------------------------------------===// 2729 // spv.mlir.referenceof 2730 //===----------------------------------------------------------------------===// 2731 2732 static LogicalResult verify(spirv::ReferenceOfOp referenceOfOp) { 2733 auto *specConstSym = SymbolTable::lookupNearestSymbolFrom( 2734 referenceOfOp->getParentOp(), referenceOfOp.spec_constAttr()); 2735 Type constType; 2736 2737 auto specConstOp = dyn_cast_or_null<spirv::SpecConstantOp>(specConstSym); 2738 if (specConstOp) 2739 constType = specConstOp.default_value().getType(); 2740 2741 auto specConstCompositeOp = 2742 dyn_cast_or_null<spirv::SpecConstantCompositeOp>(specConstSym); 2743 if (specConstCompositeOp) 2744 constType = specConstCompositeOp.type(); 2745 2746 if (!specConstOp && !specConstCompositeOp) 2747 return referenceOfOp.emitOpError( 2748 "expected spv.SpecConstant or spv.SpecConstantComposite symbol"); 2749 2750 if (referenceOfOp.reference().getType() != constType) 2751 return referenceOfOp.emitOpError("result type mismatch with the referenced " 2752 "specialization constant's type"); 2753 2754 return success(); 2755 } 2756 2757 //===----------------------------------------------------------------------===// 2758 // spv.Return 2759 //===----------------------------------------------------------------------===// 2760 2761 static LogicalResult verify(spirv::ReturnOp returnOp) { 2762 // Verification is performed in spv.func op. 2763 return success(); 2764 } 2765 2766 //===----------------------------------------------------------------------===// 2767 // spv.ReturnValue 2768 //===----------------------------------------------------------------------===// 2769 2770 static LogicalResult verify(spirv::ReturnValueOp retValOp) { 2771 // Verification is performed in spv.func op. 2772 return success(); 2773 } 2774 2775 //===----------------------------------------------------------------------===// 2776 // spv.Select 2777 //===----------------------------------------------------------------------===// 2778 2779 void spirv::SelectOp::build(OpBuilder &builder, OperationState &state, 2780 Value cond, Value trueValue, Value falseValue) { 2781 build(builder, state, trueValue.getType(), cond, trueValue, falseValue); 2782 } 2783 2784 static LogicalResult verify(spirv::SelectOp op) { 2785 if (auto conditionTy = op.condition().getType().dyn_cast<VectorType>()) { 2786 auto resultVectorTy = op.result().getType().dyn_cast<VectorType>(); 2787 if (!resultVectorTy) { 2788 return op.emitOpError("result expected to be of vector type when " 2789 "condition is of vector type"); 2790 } 2791 if (resultVectorTy.getNumElements() != conditionTy.getNumElements()) { 2792 return op.emitOpError("result should have the same number of elements as " 2793 "the condition when condition is of vector type"); 2794 } 2795 } 2796 return success(); 2797 } 2798 2799 //===----------------------------------------------------------------------===// 2800 // spv.mlir.selection 2801 //===----------------------------------------------------------------------===// 2802 2803 static ParseResult parseSelectionOp(OpAsmParser &parser, 2804 OperationState &state) { 2805 if (parseControlAttribute<spirv::SelectionControl>(parser, state)) 2806 return failure(); 2807 return parser.parseRegion(*state.addRegion(), /*arguments=*/{}, 2808 /*argTypes=*/{}); 2809 } 2810 2811 static void print(spirv::SelectionOp selectionOp, OpAsmPrinter &printer) { 2812 auto *op = selectionOp.getOperation(); 2813 auto control = selectionOp.selection_control(); 2814 if (control != spirv::SelectionControl::None) 2815 printer << " control(" << spirv::stringifySelectionControl(control) << ")"; 2816 printer.printRegion(op->getRegion(0), /*printEntryBlockArgs=*/false, 2817 /*printBlockTerminators=*/true); 2818 } 2819 2820 static LogicalResult verify(spirv::SelectionOp selectionOp) { 2821 auto *op = selectionOp.getOperation(); 2822 2823 // We need to verify that the blocks follow the following layout: 2824 // 2825 // +--------------+ 2826 // | header block | 2827 // +--------------+ 2828 // / | \ 2829 // ... 2830 // 2831 // 2832 // +---------+ +---------+ +---------+ 2833 // | case #0 | | case #1 | | case #2 | ... 2834 // +---------+ +---------+ +---------+ 2835 // 2836 // 2837 // ... 2838 // \ | / 2839 // v 2840 // +-------------+ 2841 // | merge block | 2842 // +-------------+ 2843 2844 auto ®ion = op->getRegion(0); 2845 // Allow empty region as a degenerated case, which can come from 2846 // optimizations. 2847 if (region.empty()) 2848 return success(); 2849 2850 // The last block is the merge block. 2851 if (!isMergeBlock(region.back())) 2852 return selectionOp.emitOpError( 2853 "last block must be the merge block with only one 'spv.mlir.merge' op"); 2854 2855 if (std::next(region.begin()) == region.end()) 2856 return selectionOp.emitOpError("must have a selection header block"); 2857 2858 return success(); 2859 } 2860 2861 Block *spirv::SelectionOp::getHeaderBlock() { 2862 assert(!body().empty() && "op region should not be empty!"); 2863 // The first block is the loop header block. 2864 return &body().front(); 2865 } 2866 2867 Block *spirv::SelectionOp::getMergeBlock() { 2868 assert(!body().empty() && "op region should not be empty!"); 2869 // The last block is the loop merge block. 2870 return &body().back(); 2871 } 2872 2873 void spirv::SelectionOp::addMergeBlock() { 2874 assert(body().empty() && "entry and merge block already exist"); 2875 auto *mergeBlock = new Block(); 2876 body().push_back(mergeBlock); 2877 OpBuilder builder = OpBuilder::atBlockEnd(mergeBlock); 2878 2879 // Add a spv.mlir.merge op into the merge block. 2880 builder.create<spirv::MergeOp>(getLoc()); 2881 } 2882 2883 spirv::SelectionOp spirv::SelectionOp::createIfThen( 2884 Location loc, Value condition, 2885 function_ref<void(OpBuilder &builder)> thenBody, OpBuilder &builder) { 2886 auto selectionOp = 2887 builder.create<spirv::SelectionOp>(loc, spirv::SelectionControl::None); 2888 2889 selectionOp.addMergeBlock(); 2890 Block *mergeBlock = selectionOp.getMergeBlock(); 2891 Block *thenBlock = nullptr; 2892 2893 // Build the "then" block. 2894 { 2895 OpBuilder::InsertionGuard guard(builder); 2896 thenBlock = builder.createBlock(mergeBlock); 2897 thenBody(builder); 2898 builder.create<spirv::BranchOp>(loc, mergeBlock); 2899 } 2900 2901 // Build the header block. 2902 { 2903 OpBuilder::InsertionGuard guard(builder); 2904 builder.createBlock(thenBlock); 2905 builder.create<spirv::BranchConditionalOp>( 2906 loc, condition, thenBlock, 2907 /*trueArguments=*/ArrayRef<Value>(), mergeBlock, 2908 /*falseArguments=*/ArrayRef<Value>()); 2909 } 2910 2911 return selectionOp; 2912 } 2913 2914 //===----------------------------------------------------------------------===// 2915 // spv.SpecConstant 2916 //===----------------------------------------------------------------------===// 2917 2918 static ParseResult parseSpecConstantOp(OpAsmParser &parser, 2919 OperationState &state) { 2920 StringAttr nameAttr; 2921 Attribute valueAttr; 2922 2923 if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(), 2924 state.attributes)) 2925 return failure(); 2926 2927 // Parse optional spec_id. 2928 if (succeeded(parser.parseOptionalKeyword(kSpecIdAttrName))) { 2929 IntegerAttr specIdAttr; 2930 if (parser.parseLParen() || 2931 parser.parseAttribute(specIdAttr, kSpecIdAttrName, state.attributes) || 2932 parser.parseRParen()) 2933 return failure(); 2934 } 2935 2936 if (parser.parseEqual() || 2937 parser.parseAttribute(valueAttr, kDefaultValueAttrName, state.attributes)) 2938 return failure(); 2939 2940 return success(); 2941 } 2942 2943 static void print(spirv::SpecConstantOp constOp, OpAsmPrinter &printer) { 2944 printer << ' '; 2945 printer.printSymbolName(constOp.sym_name()); 2946 if (auto specID = constOp->getAttrOfType<IntegerAttr>(kSpecIdAttrName)) 2947 printer << ' ' << kSpecIdAttrName << '(' << specID.getInt() << ')'; 2948 printer << " = " << constOp.default_value(); 2949 } 2950 2951 static LogicalResult verify(spirv::SpecConstantOp constOp) { 2952 if (auto specID = constOp->getAttrOfType<IntegerAttr>(kSpecIdAttrName)) 2953 if (specID.getValue().isNegative()) 2954 return constOp.emitOpError("SpecId cannot be negative"); 2955 2956 auto value = constOp.default_value(); 2957 if (value.isa<IntegerAttr, FloatAttr>()) { 2958 // Make sure bitwidth is allowed. 2959 if (!value.getType().isa<spirv::SPIRVType>()) 2960 return constOp.emitOpError("default value bitwidth disallowed"); 2961 return success(); 2962 } 2963 return constOp.emitOpError( 2964 "default value can only be a bool, integer, or float scalar"); 2965 } 2966 2967 //===----------------------------------------------------------------------===// 2968 // spv.StoreOp 2969 //===----------------------------------------------------------------------===// 2970 2971 static ParseResult parseStoreOp(OpAsmParser &parser, OperationState &state) { 2972 // Parse the storage class specification 2973 spirv::StorageClass storageClass; 2974 SmallVector<OpAsmParser::OperandType, 2> operandInfo; 2975 auto loc = parser.getCurrentLocation(); 2976 Type elementType; 2977 if (parseEnumStrAttr(storageClass, parser) || 2978 parser.parseOperandList(operandInfo, 2) || 2979 parseMemoryAccessAttributes(parser, state) || parser.parseColon() || 2980 parser.parseType(elementType)) { 2981 return failure(); 2982 } 2983 2984 auto ptrType = spirv::PointerType::get(elementType, storageClass); 2985 if (parser.resolveOperands(operandInfo, {ptrType, elementType}, loc, 2986 state.operands)) { 2987 return failure(); 2988 } 2989 return success(); 2990 } 2991 2992 static void print(spirv::StoreOp storeOp, OpAsmPrinter &printer) { 2993 auto *op = storeOp.getOperation(); 2994 SmallVector<StringRef, 4> elidedAttrs; 2995 StringRef sc = stringifyStorageClass( 2996 storeOp.ptr().getType().cast<spirv::PointerType>().getStorageClass()); 2997 printer << " \"" << sc << "\" " << storeOp.ptr() << ", " << storeOp.value(); 2998 2999 printMemoryAccessAttribute(storeOp, printer, elidedAttrs); 3000 3001 printer << " : " << storeOp.value().getType(); 3002 printer.printOptionalAttrDict(op->getAttrs(), elidedAttrs); 3003 } 3004 3005 static LogicalResult verify(spirv::StoreOp storeOp) { 3006 // SPIR-V spec : "Pointer is the pointer to store through. Its type must be an 3007 // OpTypePointer whose Type operand is the same as the type of Object." 3008 if (failed(verifyLoadStorePtrAndValTypes(storeOp, storeOp.ptr(), 3009 storeOp.value()))) { 3010 return failure(); 3011 } 3012 return verifyMemoryAccessAttribute(storeOp); 3013 } 3014 3015 //===----------------------------------------------------------------------===// 3016 // spv.Unreachable 3017 //===----------------------------------------------------------------------===// 3018 3019 static LogicalResult verify(spirv::UnreachableOp unreachableOp) { 3020 auto *op = unreachableOp.getOperation(); 3021 auto *block = op->getBlock(); 3022 // Fast track: if this is in entry block, its invalid. Otherwise, if no 3023 // predecessors, it's valid. 3024 if (block->isEntryBlock()) 3025 return unreachableOp.emitOpError("cannot be used in reachable block"); 3026 if (block->hasNoPredecessors()) 3027 return success(); 3028 3029 // TODO: further verification needs to analyze reachability from 3030 // the entry block. 3031 3032 return success(); 3033 } 3034 3035 //===----------------------------------------------------------------------===// 3036 // spv.Variable 3037 //===----------------------------------------------------------------------===// 3038 3039 static ParseResult parseVariableOp(OpAsmParser &parser, OperationState &state) { 3040 // Parse optional initializer 3041 Optional<OpAsmParser::OperandType> initInfo; 3042 if (succeeded(parser.parseOptionalKeyword("init"))) { 3043 initInfo = OpAsmParser::OperandType(); 3044 if (parser.parseLParen() || parser.parseOperand(*initInfo) || 3045 parser.parseRParen()) 3046 return failure(); 3047 } 3048 3049 if (parseVariableDecorations(parser, state)) { 3050 return failure(); 3051 } 3052 3053 // Parse result pointer type 3054 Type type; 3055 if (parser.parseColon()) 3056 return failure(); 3057 auto loc = parser.getCurrentLocation(); 3058 if (parser.parseType(type)) 3059 return failure(); 3060 3061 auto ptrType = type.dyn_cast<spirv::PointerType>(); 3062 if (!ptrType) 3063 return parser.emitError(loc, "expected spv.ptr type"); 3064 state.addTypes(ptrType); 3065 3066 // Resolve the initializer operand 3067 if (initInfo) { 3068 if (parser.resolveOperand(*initInfo, ptrType.getPointeeType(), 3069 state.operands)) 3070 return failure(); 3071 } 3072 3073 auto attr = parser.getBuilder().getI32IntegerAttr( 3074 llvm::bit_cast<int32_t>(ptrType.getStorageClass())); 3075 state.addAttribute(spirv::attributeName<spirv::StorageClass>(), attr); 3076 3077 return success(); 3078 } 3079 3080 static void print(spirv::VariableOp varOp, OpAsmPrinter &printer) { 3081 SmallVector<StringRef, 4> elidedAttrs{ 3082 spirv::attributeName<spirv::StorageClass>()}; 3083 // Print optional initializer 3084 if (varOp.getNumOperands() != 0) 3085 printer << " init(" << varOp.initializer() << ")"; 3086 3087 printVariableDecorations(varOp, printer, elidedAttrs); 3088 printer << " : " << varOp.getType(); 3089 } 3090 3091 static LogicalResult verify(spirv::VariableOp varOp) { 3092 // SPIR-V spec: "Storage Class is the Storage Class of the memory holding the 3093 // object. It cannot be Generic. It must be the same as the Storage Class 3094 // operand of the Result Type." 3095 if (varOp.storage_class() != spirv::StorageClass::Function) { 3096 return varOp.emitOpError( 3097 "can only be used to model function-level variables. Use " 3098 "spv.GlobalVariable for module-level variables."); 3099 } 3100 3101 auto pointerType = varOp.pointer().getType().cast<spirv::PointerType>(); 3102 if (varOp.storage_class() != pointerType.getStorageClass()) 3103 return varOp.emitOpError( 3104 "storage class must match result pointer's storage class"); 3105 3106 if (varOp.getNumOperands() != 0) { 3107 // SPIR-V spec: "Initializer must be an <id> from a constant instruction or 3108 // a global (module scope) OpVariable instruction". 3109 auto *initOp = varOp.getOperand(0).getDefiningOp(); 3110 if (!initOp || !isa<spirv::ConstantOp, // for normal constant 3111 spirv::ReferenceOfOp, // for spec constant 3112 spirv::AddressOfOp>(initOp)) 3113 return varOp.emitOpError("initializer must be the result of a " 3114 "constant or spv.GlobalVariable op"); 3115 } 3116 3117 // TODO: generate these strings using ODS. 3118 auto *op = varOp.getOperation(); 3119 auto descriptorSetName = llvm::convertToSnakeFromCamelCase( 3120 stringifyDecoration(spirv::Decoration::DescriptorSet)); 3121 auto bindingName = llvm::convertToSnakeFromCamelCase( 3122 stringifyDecoration(spirv::Decoration::Binding)); 3123 auto builtInName = llvm::convertToSnakeFromCamelCase( 3124 stringifyDecoration(spirv::Decoration::BuiltIn)); 3125 3126 for (const auto &attr : {descriptorSetName, bindingName, builtInName}) { 3127 if (op->getAttr(attr)) 3128 return varOp.emitOpError("cannot have '") 3129 << attr << "' attribute (only allowed in spv.GlobalVariable)"; 3130 } 3131 3132 return success(); 3133 } 3134 3135 //===----------------------------------------------------------------------===// 3136 // spv.VectorShuffle 3137 //===----------------------------------------------------------------------===// 3138 3139 static LogicalResult verify(spirv::VectorShuffleOp shuffleOp) { 3140 VectorType resultType = shuffleOp.getType().cast<VectorType>(); 3141 3142 size_t numResultElements = resultType.getNumElements(); 3143 if (numResultElements != shuffleOp.components().size()) 3144 return shuffleOp.emitOpError("result type element count (") 3145 << numResultElements 3146 << ") mismatch with the number of component selectors (" 3147 << shuffleOp.components().size() << ")"; 3148 3149 size_t totalSrcElements = 3150 shuffleOp.vector1().getType().cast<VectorType>().getNumElements() + 3151 shuffleOp.vector2().getType().cast<VectorType>().getNumElements(); 3152 3153 for (const auto &selector : 3154 shuffleOp.components().getAsValueRange<IntegerAttr>()) { 3155 uint32_t index = selector.getZExtValue(); 3156 if (index >= totalSrcElements && 3157 index != std::numeric_limits<uint32_t>().max()) 3158 return shuffleOp.emitOpError("component selector ") 3159 << index << " out of range: expected to be in [0, " 3160 << totalSrcElements << ") or 0xffffffff"; 3161 } 3162 return success(); 3163 } 3164 3165 //===----------------------------------------------------------------------===// 3166 // spv.CooperativeMatrixLoadNV 3167 //===----------------------------------------------------------------------===// 3168 3169 static ParseResult parseCooperativeMatrixLoadNVOp(OpAsmParser &parser, 3170 OperationState &state) { 3171 SmallVector<OpAsmParser::OperandType, 3> operandInfo; 3172 Type strideType = parser.getBuilder().getIntegerType(32); 3173 Type columnMajorType = parser.getBuilder().getIntegerType(1); 3174 Type ptrType; 3175 Type elementType; 3176 if (parser.parseOperandList(operandInfo, 3) || 3177 parseMemoryAccessAttributes(parser, state) || parser.parseColon() || 3178 parser.parseType(ptrType) || parser.parseKeywordType("as", elementType)) { 3179 return failure(); 3180 } 3181 if (parser.resolveOperands(operandInfo, 3182 {ptrType, strideType, columnMajorType}, 3183 parser.getNameLoc(), state.operands)) { 3184 return failure(); 3185 } 3186 3187 state.addTypes(elementType); 3188 return success(); 3189 } 3190 3191 static void print(spirv::CooperativeMatrixLoadNVOp M, OpAsmPrinter &printer) { 3192 printer << " " << M.pointer() << ", " << M.stride() << ", " 3193 << M.columnmajor(); 3194 // Print optional memory access attribute. 3195 if (auto memAccess = M.memory_access()) 3196 printer << " [\"" << stringifyMemoryAccess(*memAccess) << "\"]"; 3197 printer << " : " << M.pointer().getType() << " as " << M.getType(); 3198 } 3199 3200 static LogicalResult verifyPointerAndCoopMatrixType(Operation *op, Type pointer, 3201 Type coopMatrix) { 3202 Type pointeeType = pointer.cast<spirv::PointerType>().getPointeeType(); 3203 if (!pointeeType.isa<spirv::ScalarType>() && !pointeeType.isa<VectorType>()) 3204 return op->emitError( 3205 "Pointer must point to a scalar or vector type but provided ") 3206 << pointeeType; 3207 spirv::StorageClass storage = 3208 pointer.cast<spirv::PointerType>().getStorageClass(); 3209 if (storage != spirv::StorageClass::Workgroup && 3210 storage != spirv::StorageClass::StorageBuffer && 3211 storage != spirv::StorageClass::PhysicalStorageBuffer) 3212 return op->emitError( 3213 "Pointer storage class must be Workgroup, StorageBuffer or " 3214 "PhysicalStorageBufferEXT but provided ") 3215 << stringifyStorageClass(storage); 3216 return success(); 3217 } 3218 3219 //===----------------------------------------------------------------------===// 3220 // spv.CooperativeMatrixStoreNV 3221 //===----------------------------------------------------------------------===// 3222 3223 static ParseResult parseCooperativeMatrixStoreNVOp(OpAsmParser &parser, 3224 OperationState &state) { 3225 SmallVector<OpAsmParser::OperandType, 4> operandInfo; 3226 Type strideType = parser.getBuilder().getIntegerType(32); 3227 Type columnMajorType = parser.getBuilder().getIntegerType(1); 3228 Type ptrType; 3229 Type elementType; 3230 if (parser.parseOperandList(operandInfo, 4) || 3231 parseMemoryAccessAttributes(parser, state) || parser.parseColon() || 3232 parser.parseType(ptrType) || parser.parseComma() || 3233 parser.parseType(elementType)) { 3234 return failure(); 3235 } 3236 if (parser.resolveOperands( 3237 operandInfo, {ptrType, elementType, strideType, columnMajorType}, 3238 parser.getNameLoc(), state.operands)) { 3239 return failure(); 3240 } 3241 3242 return success(); 3243 } 3244 3245 static void print(spirv::CooperativeMatrixStoreNVOp coopMatrix, 3246 OpAsmPrinter &printer) { 3247 printer << " " << coopMatrix.pointer() << ", " << coopMatrix.object() << ", " 3248 << coopMatrix.stride() << ", " << coopMatrix.columnmajor(); 3249 // Print optional memory access attribute. 3250 if (auto memAccess = coopMatrix.memory_access()) 3251 printer << " [\"" << stringifyMemoryAccess(*memAccess) << "\"]"; 3252 printer << " : " << coopMatrix.pointer().getType() << ", " 3253 << coopMatrix.getOperand(1).getType(); 3254 } 3255 3256 //===----------------------------------------------------------------------===// 3257 // spv.CooperativeMatrixMulAddNV 3258 //===----------------------------------------------------------------------===// 3259 3260 static LogicalResult 3261 verifyCoopMatrixMulAdd(spirv::CooperativeMatrixMulAddNVOp op) { 3262 if (op.c().getType() != op.result().getType()) 3263 return op.emitOpError("result and third operand must have the same type"); 3264 auto typeA = op.a().getType().cast<spirv::CooperativeMatrixNVType>(); 3265 auto typeB = op.b().getType().cast<spirv::CooperativeMatrixNVType>(); 3266 auto typeC = op.c().getType().cast<spirv::CooperativeMatrixNVType>(); 3267 auto typeR = op.result().getType().cast<spirv::CooperativeMatrixNVType>(); 3268 if (typeA.getRows() != typeR.getRows() || 3269 typeA.getColumns() != typeB.getRows() || 3270 typeB.getColumns() != typeR.getColumns()) 3271 return op.emitOpError("matrix size must match"); 3272 if (typeR.getScope() != typeA.getScope() || 3273 typeR.getScope() != typeB.getScope() || 3274 typeR.getScope() != typeC.getScope()) 3275 return op.emitOpError("matrix scope must match"); 3276 if (typeA.getElementType() != typeB.getElementType() || 3277 typeR.getElementType() != typeC.getElementType()) 3278 return op.emitOpError("matrix element type must match"); 3279 return success(); 3280 } 3281 3282 //===----------------------------------------------------------------------===// 3283 // spv.MatrixTimesScalar 3284 //===----------------------------------------------------------------------===// 3285 3286 static LogicalResult verifyMatrixTimesScalar(spirv::MatrixTimesScalarOp op) { 3287 // We already checked that result and matrix are both of matrix type in the 3288 // auto-generated verify method. 3289 3290 auto inputMatrix = op.matrix().getType().cast<spirv::MatrixType>(); 3291 auto resultMatrix = op.result().getType().cast<spirv::MatrixType>(); 3292 3293 // Check that the scalar type is the same as the matrix element type. 3294 if (op.scalar().getType() != inputMatrix.getElementType()) 3295 return op.emitError("input matrix components' type and scaling value must " 3296 "have the same type"); 3297 3298 // Note that the next three checks could be done using the AllTypesMatch 3299 // trait in the Op definition file but it generates a vague error message. 3300 3301 // Check that the input and result matrices have the same columns' count 3302 if (inputMatrix.getNumColumns() != resultMatrix.getNumColumns()) 3303 return op.emitError("input and result matrices must have the same " 3304 "number of columns"); 3305 3306 // Check that the input and result matrices' have the same rows count 3307 if (inputMatrix.getNumRows() != resultMatrix.getNumRows()) 3308 return op.emitError("input and result matrices' columns must have " 3309 "the same size"); 3310 3311 // Check that the input and result matrices' have the same component type 3312 if (inputMatrix.getElementType() != resultMatrix.getElementType()) 3313 return op.emitError("input and result matrices' columns must have " 3314 "the same component type"); 3315 3316 return success(); 3317 } 3318 3319 //===----------------------------------------------------------------------===// 3320 // spv.CopyMemory 3321 //===----------------------------------------------------------------------===// 3322 3323 static void print(spirv::CopyMemoryOp copyMemory, OpAsmPrinter &printer) { 3324 auto *op = copyMemory.getOperation(); 3325 printer << ' '; 3326 3327 StringRef targetStorageClass = 3328 stringifyStorageClass(copyMemory.target() 3329 .getType() 3330 .cast<spirv::PointerType>() 3331 .getStorageClass()); 3332 printer << " \"" << targetStorageClass << "\" " << copyMemory.target() 3333 << ", "; 3334 3335 StringRef sourceStorageClass = 3336 stringifyStorageClass(copyMemory.source() 3337 .getType() 3338 .cast<spirv::PointerType>() 3339 .getStorageClass()); 3340 printer << " \"" << sourceStorageClass << "\" " << copyMemory.source(); 3341 3342 SmallVector<StringRef, 4> elidedAttrs; 3343 printMemoryAccessAttribute(copyMemory, printer, elidedAttrs); 3344 printSourceMemoryAccessAttribute(copyMemory, printer, elidedAttrs, 3345 copyMemory.source_memory_access(), 3346 copyMemory.source_alignment()); 3347 3348 printer.printOptionalAttrDict(op->getAttrs(), elidedAttrs); 3349 3350 Type pointeeType = 3351 copyMemory.target().getType().cast<spirv::PointerType>().getPointeeType(); 3352 printer << " : " << pointeeType; 3353 } 3354 3355 static ParseResult parseCopyMemoryOp(OpAsmParser &parser, 3356 OperationState &state) { 3357 spirv::StorageClass targetStorageClass; 3358 OpAsmParser::OperandType targetPtrInfo; 3359 3360 spirv::StorageClass sourceStorageClass; 3361 OpAsmParser::OperandType sourcePtrInfo; 3362 3363 Type elementType; 3364 3365 if (parseEnumStrAttr(targetStorageClass, parser) || 3366 parser.parseOperand(targetPtrInfo) || parser.parseComma() || 3367 parseEnumStrAttr(sourceStorageClass, parser) || 3368 parser.parseOperand(sourcePtrInfo) || 3369 parseMemoryAccessAttributes(parser, state)) { 3370 return failure(); 3371 } 3372 3373 if (!parser.parseOptionalComma()) { 3374 // Parse 2nd memory access attributes. 3375 if (parseSourceMemoryAccessAttributes(parser, state)) { 3376 return failure(); 3377 } 3378 } 3379 3380 if (parser.parseColon() || parser.parseType(elementType)) 3381 return failure(); 3382 3383 if (parser.parseOptionalAttrDict(state.attributes)) 3384 return failure(); 3385 3386 auto targetPtrType = spirv::PointerType::get(elementType, targetStorageClass); 3387 auto sourcePtrType = spirv::PointerType::get(elementType, sourceStorageClass); 3388 3389 if (parser.resolveOperand(targetPtrInfo, targetPtrType, state.operands) || 3390 parser.resolveOperand(sourcePtrInfo, sourcePtrType, state.operands)) { 3391 return failure(); 3392 } 3393 3394 return success(); 3395 } 3396 3397 static LogicalResult verifyCopyMemory(spirv::CopyMemoryOp copyMemory) { 3398 Type targetType = 3399 copyMemory.target().getType().cast<spirv::PointerType>().getPointeeType(); 3400 3401 Type sourceType = 3402 copyMemory.source().getType().cast<spirv::PointerType>().getPointeeType(); 3403 3404 if (targetType != sourceType) { 3405 return copyMemory.emitOpError( 3406 "both operands must be pointers to the same type"); 3407 } 3408 3409 if (failed(verifyMemoryAccessAttribute(copyMemory))) { 3410 return failure(); 3411 } 3412 3413 // TODO - According to the spec: 3414 // 3415 // If two masks are present, the first applies to Target and cannot include 3416 // MakePointerVisible, and the second applies to Source and cannot include 3417 // MakePointerAvailable. 3418 // 3419 // Add such verification here. 3420 3421 return verifySourceMemoryAccessAttribute(copyMemory); 3422 } 3423 3424 //===----------------------------------------------------------------------===// 3425 // spv.Transpose 3426 //===----------------------------------------------------------------------===// 3427 3428 static LogicalResult verifyTranspose(spirv::TransposeOp op) { 3429 auto inputMatrix = op.matrix().getType().cast<spirv::MatrixType>(); 3430 auto resultMatrix = op.result().getType().cast<spirv::MatrixType>(); 3431 3432 // Verify that the input and output matrices have correct shapes. 3433 if (inputMatrix.getNumRows() != resultMatrix.getNumColumns()) 3434 return op.emitError("input matrix rows count must be equal to " 3435 "output matrix columns count"); 3436 3437 if (inputMatrix.getNumColumns() != resultMatrix.getNumRows()) 3438 return op.emitError("input matrix columns count must be equal to " 3439 "output matrix rows count"); 3440 3441 // Verify that the input and output matrices have the same component type 3442 if (inputMatrix.getElementType() != resultMatrix.getElementType()) 3443 return op.emitError("input and output matrices must have the same " 3444 "component type"); 3445 3446 return success(); 3447 } 3448 3449 //===----------------------------------------------------------------------===// 3450 // spv.MatrixTimesMatrix 3451 //===----------------------------------------------------------------------===// 3452 3453 static LogicalResult verifyMatrixTimesMatrix(spirv::MatrixTimesMatrixOp op) { 3454 auto leftMatrix = op.leftmatrix().getType().cast<spirv::MatrixType>(); 3455 auto rightMatrix = op.rightmatrix().getType().cast<spirv::MatrixType>(); 3456 auto resultMatrix = op.result().getType().cast<spirv::MatrixType>(); 3457 3458 // left matrix columns' count and right matrix rows' count must be equal 3459 if (leftMatrix.getNumColumns() != rightMatrix.getNumRows()) 3460 return op.emitError("left matrix columns' count must be equal to " 3461 "the right matrix rows' count"); 3462 3463 // right and result matrices columns' count must be the same 3464 if (rightMatrix.getNumColumns() != resultMatrix.getNumColumns()) 3465 return op.emitError( 3466 "right and result matrices must have equal columns' count"); 3467 3468 // right and result matrices component type must be the same 3469 if (rightMatrix.getElementType() != resultMatrix.getElementType()) 3470 return op.emitError("right and result matrices' component type must" 3471 " be the same"); 3472 3473 // left and result matrices component type must be the same 3474 if (leftMatrix.getElementType() != resultMatrix.getElementType()) 3475 return op.emitError("left and result matrices' component type" 3476 " must be the same"); 3477 3478 // left and result matrices rows count must be the same 3479 if (leftMatrix.getNumRows() != resultMatrix.getNumRows()) 3480 return op.emitError("left and result matrices must have equal rows'" 3481 " count"); 3482 3483 return success(); 3484 } 3485 3486 //===----------------------------------------------------------------------===// 3487 // spv.SpecConstantComposite 3488 //===----------------------------------------------------------------------===// 3489 3490 static ParseResult parseSpecConstantCompositeOp(OpAsmParser &parser, 3491 OperationState &state) { 3492 3493 StringAttr compositeName; 3494 if (parser.parseSymbolName(compositeName, SymbolTable::getSymbolAttrName(), 3495 state.attributes)) 3496 return failure(); 3497 3498 if (parser.parseLParen()) 3499 return failure(); 3500 3501 SmallVector<Attribute, 4> constituents; 3502 3503 do { 3504 // The name of the constituent attribute isn't important 3505 const char *attrName = "spec_const"; 3506 FlatSymbolRefAttr specConstRef; 3507 NamedAttrList attrs; 3508 3509 if (parser.parseAttribute(specConstRef, Type(), attrName, attrs)) 3510 return failure(); 3511 3512 constituents.push_back(specConstRef); 3513 } while (!parser.parseOptionalComma()); 3514 3515 if (parser.parseRParen()) 3516 return failure(); 3517 3518 state.addAttribute(kCompositeSpecConstituentsName, 3519 parser.getBuilder().getArrayAttr(constituents)); 3520 3521 Type type; 3522 if (parser.parseColonType(type)) 3523 return failure(); 3524 3525 state.addAttribute(kTypeAttrName, TypeAttr::get(type)); 3526 3527 return success(); 3528 } 3529 3530 static void print(spirv::SpecConstantCompositeOp op, OpAsmPrinter &printer) { 3531 printer << " "; 3532 printer.printSymbolName(op.sym_name()); 3533 printer << " ("; 3534 auto constituents = op.constituents().getValue(); 3535 3536 if (!constituents.empty()) 3537 llvm::interleaveComma(constituents, printer); 3538 3539 printer << ") : " << op.type(); 3540 } 3541 3542 static LogicalResult verify(spirv::SpecConstantCompositeOp constOp) { 3543 auto cType = constOp.type().dyn_cast<spirv::CompositeType>(); 3544 auto constituents = constOp.constituents().getValue(); 3545 3546 if (!cType) 3547 return constOp.emitError( 3548 "result type must be a composite type, but provided ") 3549 << constOp.type(); 3550 3551 if (cType.isa<spirv::CooperativeMatrixNVType>()) 3552 return constOp.emitError("unsupported composite type ") << cType; 3553 if (constituents.size() != cType.getNumElements()) 3554 return constOp.emitError("has incorrect number of operands: expected ") 3555 << cType.getNumElements() << ", but provided " 3556 << constituents.size(); 3557 3558 for (auto index : llvm::seq<uint32_t>(0, constituents.size())) { 3559 auto constituent = constituents[index].cast<FlatSymbolRefAttr>(); 3560 3561 auto constituentSpecConstOp = 3562 dyn_cast<spirv::SpecConstantOp>(SymbolTable::lookupNearestSymbolFrom( 3563 constOp->getParentOp(), constituent.getAttr())); 3564 3565 if (constituentSpecConstOp.default_value().getType() != 3566 cType.getElementType(index)) 3567 return constOp.emitError("has incorrect types of operands: expected ") 3568 << cType.getElementType(index) << ", but provided " 3569 << constituentSpecConstOp.default_value().getType(); 3570 } 3571 3572 return success(); 3573 } 3574 3575 //===----------------------------------------------------------------------===// 3576 // spv.SpecConstantOperation 3577 //===----------------------------------------------------------------------===// 3578 3579 static ParseResult parseSpecConstantOperationOp(OpAsmParser &parser, 3580 OperationState &state) { 3581 Region *body = state.addRegion(); 3582 3583 if (parser.parseKeyword("wraps")) 3584 return failure(); 3585 3586 body->push_back(new Block); 3587 Block &block = body->back(); 3588 Operation *wrappedOp = parser.parseGenericOperation(&block, block.begin()); 3589 3590 if (!wrappedOp) 3591 return failure(); 3592 3593 OpBuilder builder(parser.getBuilder().getContext()); 3594 builder.setInsertionPointToEnd(&block); 3595 builder.create<spirv::YieldOp>(wrappedOp->getLoc(), wrappedOp->getResult(0)); 3596 state.location = wrappedOp->getLoc(); 3597 3598 state.addTypes(wrappedOp->getResult(0).getType()); 3599 3600 if (parser.parseOptionalAttrDict(state.attributes)) 3601 return failure(); 3602 3603 return success(); 3604 } 3605 3606 static void print(spirv::SpecConstantOperationOp op, OpAsmPrinter &printer) { 3607 printer << " wraps "; 3608 printer.printGenericOp(&op.body().front().front()); 3609 } 3610 3611 static LogicalResult verify(spirv::SpecConstantOperationOp constOp) { 3612 Block &block = constOp.getRegion().getBlocks().front(); 3613 3614 if (block.getOperations().size() != 2) 3615 return constOp.emitOpError("expected exactly 2 nested ops"); 3616 3617 Operation &enclosedOp = block.getOperations().front(); 3618 3619 if (!enclosedOp.hasTrait<OpTrait::spirv::UsableInSpecConstantOp>()) 3620 return constOp.emitOpError("invalid enclosed op"); 3621 3622 for (auto operand : enclosedOp.getOperands()) 3623 if (!isa<spirv::ConstantOp, spirv::ReferenceOfOp, 3624 spirv::SpecConstantOperationOp>(operand.getDefiningOp())) 3625 return constOp.emitOpError( 3626 "invalid operand, must be defined by a constant operation"); 3627 3628 return success(); 3629 } 3630 3631 //===----------------------------------------------------------------------===// 3632 // spv.GLSL.FrexpStruct 3633 //===----------------------------------------------------------------------===// 3634 static LogicalResult 3635 verifyGLSLFrexpStructOp(spirv::GLSLFrexpStructOp frexpStructOp) { 3636 spirv::StructType structTy = 3637 frexpStructOp.result().getType().dyn_cast<spirv::StructType>(); 3638 3639 if (structTy.getNumElements() != 2) 3640 return frexpStructOp.emitError("result type must be a struct type " 3641 "with two memebers"); 3642 3643 Type significandTy = structTy.getElementType(0); 3644 Type exponentTy = structTy.getElementType(1); 3645 VectorType exponentVecTy = exponentTy.dyn_cast<VectorType>(); 3646 IntegerType exponentIntTy = exponentTy.dyn_cast<IntegerType>(); 3647 3648 Type operandTy = frexpStructOp.operand().getType(); 3649 VectorType operandVecTy = operandTy.dyn_cast<VectorType>(); 3650 FloatType operandFTy = operandTy.dyn_cast<FloatType>(); 3651 3652 if (significandTy != operandTy) 3653 return frexpStructOp.emitError("member zero of the resulting struct type " 3654 "must be the same type as the operand"); 3655 3656 if (exponentVecTy) { 3657 IntegerType componentIntTy = 3658 exponentVecTy.getElementType().dyn_cast<IntegerType>(); 3659 if (!(componentIntTy && componentIntTy.getWidth() == 32)) 3660 return frexpStructOp.emitError( 3661 "member one of the resulting struct type must" 3662 "be a scalar or vector of 32 bit integer type"); 3663 } else if (!(exponentIntTy && exponentIntTy.getWidth() == 32)) { 3664 return frexpStructOp.emitError( 3665 "member one of the resulting struct type " 3666 "must be a scalar or vector of 32 bit integer type"); 3667 } 3668 3669 // Check that the two member types have the same number of components 3670 if (operandVecTy && exponentVecTy && 3671 (exponentVecTy.getNumElements() == operandVecTy.getNumElements())) 3672 return success(); 3673 3674 if (operandFTy && exponentIntTy) 3675 return success(); 3676 3677 return frexpStructOp.emitError( 3678 "member one of the resulting struct type " 3679 "must have the same number of components as the operand type"); 3680 } 3681 3682 //===----------------------------------------------------------------------===// 3683 // spv.GLSL.Ldexp 3684 //===----------------------------------------------------------------------===// 3685 3686 static LogicalResult verify(spirv::GLSLLdexpOp ldexpOp) { 3687 Type significandType = ldexpOp.x().getType(); 3688 Type exponentType = ldexpOp.exp().getType(); 3689 3690 if (significandType.isa<FloatType>() != exponentType.isa<IntegerType>()) 3691 return ldexpOp.emitOpError("operands must both be scalars or vectors"); 3692 3693 auto getNumElements = [](Type type) -> unsigned { 3694 if (auto vectorType = type.dyn_cast<VectorType>()) 3695 return vectorType.getNumElements(); 3696 return 1; 3697 }; 3698 3699 if (getNumElements(significandType) != getNumElements(exponentType)) 3700 return ldexpOp.emitOpError( 3701 "operands must have the same number of elements"); 3702 3703 return success(); 3704 } 3705 3706 //===----------------------------------------------------------------------===// 3707 // spv.ImageDrefGather 3708 //===----------------------------------------------------------------------===// 3709 3710 static LogicalResult verify(spirv::ImageDrefGatherOp imageDrefGatherOp) { 3711 VectorType resultType = 3712 imageDrefGatherOp.result().getType().cast<VectorType>(); 3713 auto sampledImageType = imageDrefGatherOp.sampledimage() 3714 .getType() 3715 .cast<spirv::SampledImageType>(); 3716 auto imageType = sampledImageType.getImageType().cast<spirv::ImageType>(); 3717 3718 if (resultType.getNumElements() != 4) 3719 return imageDrefGatherOp.emitOpError( 3720 "result type must be a vector of four components"); 3721 3722 Type elementType = resultType.getElementType(); 3723 Type sampledElementType = imageType.getElementType(); 3724 if (!sampledElementType.isa<NoneType>() && elementType != sampledElementType) 3725 return imageDrefGatherOp.emitOpError( 3726 "the component type of result must be the same as sampled type of the " 3727 "underlying image type"); 3728 3729 spirv::Dim imageDim = imageType.getDim(); 3730 spirv::ImageSamplingInfo imageMS = imageType.getSamplingInfo(); 3731 3732 if (imageDim != spirv::Dim::Dim2D && imageDim != spirv::Dim::Cube && 3733 imageDim != spirv::Dim::Rect) 3734 return imageDrefGatherOp.emitOpError( 3735 "the Dim operand of the underlying image type must be 2D, Cube, or " 3736 "Rect"); 3737 3738 if (imageMS != spirv::ImageSamplingInfo::SingleSampled) 3739 return imageDrefGatherOp.emitOpError( 3740 "the MS operand of the underlying image type must be 0"); 3741 3742 spirv::ImageOperandsAttr attr = imageDrefGatherOp.imageoperandsAttr(); 3743 auto operandArguments = imageDrefGatherOp.operand_arguments(); 3744 3745 return verifyImageOperands(imageDrefGatherOp, attr, operandArguments); 3746 } 3747 3748 //===----------------------------------------------------------------------===// 3749 // spv.ImageQuerySize 3750 //===----------------------------------------------------------------------===// 3751 3752 static LogicalResult verify(spirv::ImageQuerySizeOp imageQuerySizeOp) { 3753 spirv::ImageType imageType = 3754 imageQuerySizeOp.image().getType().cast<spirv::ImageType>(); 3755 Type resultType = imageQuerySizeOp.result().getType(); 3756 3757 spirv::Dim dim = imageType.getDim(); 3758 spirv::ImageSamplingInfo samplingInfo = imageType.getSamplingInfo(); 3759 spirv::ImageSamplerUseInfo samplerInfo = imageType.getSamplerUseInfo(); 3760 switch (dim) { 3761 case spirv::Dim::Dim1D: 3762 case spirv::Dim::Dim2D: 3763 case spirv::Dim::Dim3D: 3764 case spirv::Dim::Cube: 3765 if (!(samplingInfo == spirv::ImageSamplingInfo::MultiSampled || 3766 samplerInfo == spirv::ImageSamplerUseInfo::SamplerUnknown || 3767 samplerInfo == spirv::ImageSamplerUseInfo::NoSampler)) 3768 return imageQuerySizeOp.emitError( 3769 "if Dim is 1D, 2D, 3D, or Cube, " 3770 "it must also have either an MS of 1 or a Sampled of 0 or 2"); 3771 break; 3772 case spirv::Dim::Buffer: 3773 case spirv::Dim::Rect: 3774 break; 3775 default: 3776 return imageQuerySizeOp.emitError("the Dim operand of the image type must " 3777 "be 1D, 2D, 3D, Buffer, Cube, or Rect"); 3778 } 3779 3780 unsigned componentNumber = 0; 3781 switch (dim) { 3782 case spirv::Dim::Dim1D: 3783 case spirv::Dim::Buffer: 3784 componentNumber = 1; 3785 break; 3786 case spirv::Dim::Dim2D: 3787 case spirv::Dim::Cube: 3788 case spirv::Dim::Rect: 3789 componentNumber = 2; 3790 break; 3791 case spirv::Dim::Dim3D: 3792 componentNumber = 3; 3793 break; 3794 default: 3795 break; 3796 } 3797 3798 if (imageType.getArrayedInfo() == spirv::ImageArrayedInfo::Arrayed) 3799 componentNumber += 1; 3800 3801 unsigned resultComponentNumber = 1; 3802 if (auto resultVectorType = resultType.dyn_cast<VectorType>()) 3803 resultComponentNumber = resultVectorType.getNumElements(); 3804 3805 if (componentNumber != resultComponentNumber) 3806 return imageQuerySizeOp.emitError("expected the result to have ") 3807 << componentNumber << " component(s), but found " 3808 << resultComponentNumber << " component(s)"; 3809 3810 return success(); 3811 } 3812 3813 static ParseResult parsePtrAccessChainOpImpl(StringRef opName, 3814 OpAsmParser &parser, 3815 OperationState &state) { 3816 OpAsmParser::OperandType ptrInfo; 3817 SmallVector<OpAsmParser::OperandType, 4> indicesInfo; 3818 Type type; 3819 auto loc = parser.getCurrentLocation(); 3820 SmallVector<Type, 4> indicesTypes; 3821 3822 if (parser.parseOperand(ptrInfo) || 3823 parser.parseOperandList(indicesInfo, OpAsmParser::Delimiter::Square) || 3824 parser.parseColonType(type) || 3825 parser.resolveOperand(ptrInfo, type, state.operands)) 3826 return failure(); 3827 3828 // Check that the provided indices list is not empty before parsing their 3829 // type list. 3830 if (indicesInfo.empty()) 3831 return emitError(state.location) << opName << " expected element"; 3832 3833 if (parser.parseComma() || parser.parseTypeList(indicesTypes)) 3834 return failure(); 3835 3836 // Check that the indices types list is not empty and that it has a one-to-one 3837 // mapping to the provided indices. 3838 if (indicesTypes.size() != indicesInfo.size()) 3839 return emitError(state.location) 3840 << opName 3841 << " indices types' count must be equal to indices info count"; 3842 3843 if (parser.resolveOperands(indicesInfo, indicesTypes, loc, state.operands)) 3844 return failure(); 3845 3846 auto resultType = getElementPtrType( 3847 type, llvm::makeArrayRef(state.operands).drop_front(2), state.location); 3848 if (!resultType) 3849 return failure(); 3850 3851 state.addTypes(resultType); 3852 return success(); 3853 } 3854 3855 template <typename Op> 3856 static auto concatElemAndIndices(Op op) { 3857 SmallVector<Value> ret(op.indices().size() + 1); 3858 ret[0] = op.element(); 3859 llvm::copy(op.indices(), ret.begin() + 1); 3860 return ret; 3861 } 3862 3863 //===----------------------------------------------------------------------===// 3864 // spv.InBoundsPtrAccessChainOp 3865 //===----------------------------------------------------------------------===// 3866 3867 void spirv::InBoundsPtrAccessChainOp::build(OpBuilder &builder, 3868 OperationState &state, 3869 Value basePtr, Value element, 3870 ValueRange indices) { 3871 auto type = getElementPtrType(basePtr.getType(), indices, state.location); 3872 assert(type && "Unable to deduce return type based on basePtr and indices"); 3873 build(builder, state, type, basePtr, element, indices); 3874 } 3875 3876 static ParseResult parseInBoundsPtrAccessChainOp(OpAsmParser &parser, 3877 OperationState &state) { 3878 return parsePtrAccessChainOpImpl( 3879 spirv::InBoundsPtrAccessChainOp::getOperationName(), parser, state); 3880 } 3881 3882 static void print(spirv::InBoundsPtrAccessChainOp op, OpAsmPrinter &printer) { 3883 printAccessChain(op, concatElemAndIndices(op), printer); 3884 } 3885 3886 static LogicalResult verify(spirv::InBoundsPtrAccessChainOp accessChainOp) { 3887 return verifyAccessChain(accessChainOp, accessChainOp.indices()); 3888 } 3889 3890 //===----------------------------------------------------------------------===// 3891 // spv.PtrAccessChainOp 3892 //===----------------------------------------------------------------------===// 3893 3894 void spirv::PtrAccessChainOp::build(OpBuilder &builder, OperationState &state, 3895 Value basePtr, Value element, 3896 ValueRange indices) { 3897 auto type = getElementPtrType(basePtr.getType(), indices, state.location); 3898 assert(type && "Unable to deduce return type based on basePtr and indices"); 3899 build(builder, state, type, basePtr, element, indices); 3900 } 3901 3902 static ParseResult parsePtrAccessChainOp(OpAsmParser &parser, 3903 OperationState &state) { 3904 return parsePtrAccessChainOpImpl(spirv::PtrAccessChainOp::getOperationName(), 3905 parser, state); 3906 } 3907 3908 static void print(spirv::PtrAccessChainOp op, OpAsmPrinter &printer) { 3909 printAccessChain(op, concatElemAndIndices(op), printer); 3910 } 3911 3912 static LogicalResult verify(spirv::PtrAccessChainOp accessChainOp) { 3913 return verifyAccessChain(accessChainOp, accessChainOp.indices()); 3914 } 3915 3916 namespace mlir { 3917 namespace spirv { 3918 3919 // TableGen'erated operation interfaces for querying versions, extensions, and 3920 // capabilities. 3921 #include "mlir/Dialect/SPIRV/IR/SPIRVAvailability.cpp.inc" 3922 } // namespace spirv 3923 } // namespace mlir 3924 3925 // TablenGen'erated operation definitions. 3926 #define GET_OP_CLASSES 3927 #include "mlir/Dialect/SPIRV/IR/SPIRVOps.cpp.inc" 3928 3929 namespace mlir { 3930 namespace spirv { 3931 // TableGen'erated operation availability interface implementations. 3932 #include "mlir/Dialect/SPIRV/IR/SPIRVOpAvailabilityImpl.inc" 3933 3934 } // namespace spirv 3935 } // namespace mlir 3936