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