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