github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/mlir/lib/Dialect/SPIRV/Serialization/Serializer.cpp (about) 1 //===- Serializer.cpp - MLIR SPIR-V Serialization -------------------------===// 2 // 3 // Copyright 2019 The MLIR Authors. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 // ============================================================================= 17 // 18 // This file defines the MLIR SPIR-V module to SPIR-V binary seralization. 19 // 20 //===----------------------------------------------------------------------===// 21 22 #include "mlir/Dialect/SPIRV/Serialization.h" 23 24 #include "mlir/Dialect/SPIRV/SPIRVBinaryUtils.h" 25 #include "mlir/Dialect/SPIRV/SPIRVDialect.h" 26 #include "mlir/Dialect/SPIRV/SPIRVOps.h" 27 #include "mlir/Dialect/SPIRV/SPIRVTypes.h" 28 #include "mlir/IR/Builders.h" 29 #include "mlir/Support/LogicalResult.h" 30 #include "mlir/Support/StringExtras.h" 31 #include "llvm/ADT/Sequence.h" 32 #include "llvm/ADT/SmallVector.h" 33 #include "llvm/ADT/bit.h" 34 #include "llvm/Support/raw_ostream.h" 35 36 using namespace mlir; 37 38 /// Returns the word-count-prefixed opcode for an SPIR-V instruction. 39 static inline uint32_t getPrefixedOpcode(uint32_t wordCount, 40 spirv::Opcode opcode) { 41 assert(((wordCount >> 16) == 0) && "word count out of range!"); 42 return (wordCount << 16) | static_cast<uint32_t>(opcode); 43 } 44 45 /// Encodes an SPIR-V instruction with the given `opcode` and `operands` into 46 /// the given `binary` vector. 47 static LogicalResult encodeInstructionInto(SmallVectorImpl<uint32_t> &binary, 48 spirv::Opcode op, 49 ArrayRef<uint32_t> operands) { 50 uint32_t wordCount = 1 + operands.size(); 51 binary.push_back(getPrefixedOpcode(wordCount, op)); 52 if (!operands.empty()) { 53 binary.append(operands.begin(), operands.end()); 54 } 55 return success(); 56 } 57 58 /// Encodes an SPIR-V `literal` string into the given `binary` vector. 59 static LogicalResult encodeStringLiteralInto(SmallVectorImpl<uint32_t> &binary, 60 StringRef literal) { 61 // We need to encode the literal and the null termination. 62 auto encodingSize = literal.size() / 4 + 1; 63 auto bufferStartSize = binary.size(); 64 binary.resize(bufferStartSize + encodingSize, 0); 65 std::memcpy(binary.data() + bufferStartSize, literal.data(), literal.size()); 66 return success(); 67 } 68 69 namespace { 70 71 /// A SPIR-V module serializer. 72 /// 73 /// A SPIR-V binary module is a single linear stream of instructions; each 74 /// instruction is composed of 32-bit words with the layout: 75 /// 76 /// | <word-count>|<opcode> | <operand> | <operand> | ... | 77 /// | <------ word -------> | <-- word --> | <-- word --> | ... | 78 /// 79 /// For the first word, the 16 high-order bits are the word count of the 80 /// instruction, the 16 low-order bits are the opcode enumerant. The 81 /// instructions then belong to different sections, which must be laid out in 82 /// the particular order as specified in "2.4 Logical Layout of a Module" of 83 /// the SPIR-V spec. 84 class Serializer { 85 public: 86 /// Creates a serializer for the given SPIR-V `module`. 87 explicit Serializer(spirv::ModuleOp module); 88 89 /// Serializes the remembered SPIR-V module. 90 LogicalResult serialize(); 91 92 /// Collects the final SPIR-V `binary`. 93 void collect(SmallVectorImpl<uint32_t> &binary); 94 95 private: 96 // Note that there are two main categories of methods in this class: 97 // * process*() methods are meant to fully serialize a SPIR-V module entity 98 // (header, type, op, etc.). They update internal vectors containing 99 // different binary sections. They are not meant to be called except the 100 // top-level serialization loop. 101 // * prepare*() methods are meant to be helpers that prepare for serializing 102 // certain entity. They may or may not update internal vectors containing 103 // different binary sections. They are meant to be called among themselves 104 // or by other process*() methods for subtasks. 105 106 //===--------------------------------------------------------------------===// 107 // <id> 108 //===--------------------------------------------------------------------===// 109 110 // Note that it is illegal to use id <0> in SPIR-V binary module. Various 111 // methods in this class, if using SPIR-V word (uint32_t) as interface, 112 // check or return id <0> to indicate error in processing. 113 114 /// Consumes the next unused <id>. This method will never return 0. 115 uint32_t getNextID() { return nextID++; } 116 117 //===--------------------------------------------------------------------===// 118 // Module structure 119 //===--------------------------------------------------------------------===// 120 121 uint32_t findSpecConstID(StringRef constName) const { 122 return specConstIDMap.lookup(constName); 123 } 124 125 uint32_t findVariableID(StringRef varName) const { 126 return globalVarIDMap.lookup(varName); 127 } 128 129 uint32_t findFunctionID(StringRef fnName) const { 130 return funcIDMap.lookup(fnName); 131 } 132 133 void processCapability(); 134 135 void processExtension(); 136 137 void processMemoryModel(); 138 139 LogicalResult processConstantOp(spirv::ConstantOp op); 140 141 LogicalResult processSpecConstantOp(spirv::SpecConstantOp op); 142 143 /// Emit OpName for the given `resultID`. 144 LogicalResult processName(uint32_t resultID, StringRef name); 145 146 /// Processes a SPIR-V function op. 147 LogicalResult processFuncOp(FuncOp op); 148 149 /// Process a SPIR-V GlobalVariableOp 150 LogicalResult processGlobalVariableOp(spirv::GlobalVariableOp varOp); 151 152 /// Process attributes that translate to decorations on the result <id> 153 LogicalResult processDecoration(Location loc, uint32_t resultID, 154 NamedAttribute attr); 155 156 template <typename DType> 157 LogicalResult processTypeDecoration(Location loc, DType type, 158 uint32_t resultId) { 159 return emitError(loc, "unhandled decoraion for type:") << type; 160 } 161 162 /// Process member decoration 163 LogicalResult processMemberDecoration(uint32_t structID, uint32_t memberNum, 164 spirv::Decoration decorationType, 165 uint32_t value); 166 167 //===--------------------------------------------------------------------===// 168 // Types 169 //===--------------------------------------------------------------------===// 170 171 uint32_t findTypeID(Type type) const { return typeIDMap.lookup(type); } 172 173 Type getVoidType() { return mlirBuilder.getNoneType(); } 174 175 bool isVoidType(Type type) const { return type.isa<NoneType>(); } 176 177 /// Returns true if the given type is a pointer type to a struct in Uniform or 178 /// StorageBuffer storage class. 179 bool isInterfaceStructPtrType(Type type) const; 180 181 /// Main dispatch method for serializing a type. The result <id> of the 182 /// serialized type will be returned as `typeID`. 183 LogicalResult processType(Location loc, Type type, uint32_t &typeID); 184 185 /// Method for preparing basic SPIR-V type serialization. Returns the type's 186 /// opcode and operands for the instruction via `typeEnum` and `operands`. 187 LogicalResult prepareBasicType(Location loc, Type type, uint32_t resultID, 188 spirv::Opcode &typeEnum, 189 SmallVectorImpl<uint32_t> &operands); 190 191 LogicalResult prepareFunctionType(Location loc, FunctionType type, 192 spirv::Opcode &typeEnum, 193 SmallVectorImpl<uint32_t> &operands); 194 195 //===--------------------------------------------------------------------===// 196 // Constant 197 //===--------------------------------------------------------------------===// 198 199 uint32_t findConstantID(Attribute value) const { 200 return constIDMap.lookup(value); 201 } 202 203 /// Main dispatch method for processing a constant with the given `constType` 204 /// and `valueAttr`. `constType` is needed here because we can interpret the 205 /// `valueAttr` as a different type than the type of `valueAttr` itself; for 206 /// example, ArrayAttr, whose type is NoneType, is used for spirv::ArrayType 207 /// constants. 208 uint32_t prepareConstant(Location loc, Type constType, Attribute valueAttr); 209 210 /// Prepares bool ElementsAttr serialization. This method updates `opcode` 211 /// with a proper OpConstant* instruction and pushes literal values for the 212 /// constant to `operands`. 213 LogicalResult prepareBoolVectorConstant(Location loc, 214 DenseIntElementsAttr elementsAttr, 215 spirv::Opcode &opcode, 216 SmallVectorImpl<uint32_t> &operands); 217 218 /// Prepares int ElementsAttr serialization. This method updates `opcode` with 219 /// a proper OpConstant* instruction and pushes literal values for the 220 /// constant to `operands`. 221 LogicalResult prepareIntVectorConstant(Location loc, 222 DenseIntElementsAttr elementsAttr, 223 spirv::Opcode &opcode, 224 SmallVectorImpl<uint32_t> &operands); 225 226 /// Prepares float ElementsAttr serialization. This method updates `opcode` 227 /// with a proper OpConstant* instruction and pushes literal values for the 228 /// constant to `operands`. 229 LogicalResult prepareFloatVectorConstant(Location loc, 230 DenseFPElementsAttr elementsAttr, 231 spirv::Opcode &opcode, 232 SmallVectorImpl<uint32_t> &operands); 233 234 /// Prepares scalar attribute serialization. This method emits corresponding 235 /// OpConstant* and returns the result <id> associated with it. Returns 0 if 236 /// the attribute is not for a scalar bool/integer/float value. If `isSpec` is 237 /// true, then the constant will be serialized as a specialization constant. 238 uint32_t prepareConstantScalar(Location loc, Attribute valueAttr, 239 bool isSpec = false); 240 241 uint32_t prepareConstantBool(Location loc, BoolAttr boolAttr, 242 bool isSpec = false); 243 244 uint32_t prepareConstantInt(Location loc, IntegerAttr intAttr, 245 bool isSpec = false); 246 247 uint32_t prepareConstantFp(Location loc, FloatAttr floatAttr, 248 bool isSpec = false); 249 250 //===--------------------------------------------------------------------===// 251 // Operations 252 //===--------------------------------------------------------------------===// 253 254 uint32_t findValueID(Value *val) const { return valueIDMap.lookup(val); } 255 256 LogicalResult processAddressOfOp(spirv::AddressOfOp addressOfOp); 257 258 LogicalResult processReferenceOfOp(spirv::ReferenceOfOp referenceOfOp); 259 260 /// Main dispatch method for serializing an operation. 261 LogicalResult processOperation(Operation *op); 262 263 /// Method to dispatch to the serialization function for an operation in 264 /// SPIR-V dialect that is a mirror of an instruction in the SPIR-V spec. 265 /// This is auto-generated from ODS. Dispatch is handled for all operations 266 /// in SPIR-V dialect that have hasOpcode == 1. 267 LogicalResult dispatchToAutogenSerialization(Operation *op); 268 269 /// Method to serialize an operation in the SPIR-V dialect that is a mirror of 270 /// an instruction in the SPIR-V spec. This is auto generated if hasOpcode == 271 /// 1 and autogenSerialization == 1 in ODS. 272 template <typename OpTy> LogicalResult processOp(OpTy op) { 273 return op.emitError("unsupported op serialization"); 274 } 275 276 private: 277 /// The SPIR-V module to be serialized. 278 spirv::ModuleOp module; 279 280 /// An MLIR builder for getting MLIR constructs. 281 mlir::Builder mlirBuilder; 282 283 /// The next available result <id>. 284 uint32_t nextID = 1; 285 286 // The following are for different SPIR-V instruction sections. They follow 287 // the logical layout of a SPIR-V module. 288 289 SmallVector<uint32_t, 4> capabilities; 290 SmallVector<uint32_t, 0> extensions; 291 SmallVector<uint32_t, 0> extendedSets; 292 SmallVector<uint32_t, 3> memoryModel; 293 SmallVector<uint32_t, 0> entryPoints; 294 SmallVector<uint32_t, 4> executionModes; 295 // TODO(antiagainst): debug instructions 296 SmallVector<uint32_t, 0> names; 297 SmallVector<uint32_t, 0> decorations; 298 SmallVector<uint32_t, 0> typesGlobalValues; 299 SmallVector<uint32_t, 0> functions; 300 301 /// Map from type used in SPIR-V module to their <id>s. 302 DenseMap<Type, uint32_t> typeIDMap; 303 304 /// Map from constant values to their <id>s. 305 DenseMap<Attribute, uint32_t> constIDMap; 306 307 /// Map from specialization constant names to their <id>s. 308 llvm::StringMap<uint32_t> specConstIDMap; 309 310 /// Map from GlobalVariableOps name to <id>s. 311 llvm::StringMap<uint32_t> globalVarIDMap; 312 313 /// Map from FuncOps name to <id>s. 314 llvm::StringMap<uint32_t> funcIDMap; 315 316 /// Map from results of normal operations to their <id>s. 317 DenseMap<Value *, uint32_t> valueIDMap; 318 }; 319 } // namespace 320 321 Serializer::Serializer(spirv::ModuleOp module) 322 : module(module), mlirBuilder(module.getContext()) {} 323 324 LogicalResult Serializer::serialize() { 325 if (failed(module.verify())) 326 return failure(); 327 328 // TODO(antiagainst): handle the other sections 329 processCapability(); 330 processExtension(); 331 processMemoryModel(); 332 333 // Iterate over the module body to serialze it. Assumptions are that there is 334 // only one basic block in the moduleOp 335 for (auto &op : module.getBlock()) { 336 if (failed(processOperation(&op))) { 337 return failure(); 338 } 339 } 340 return success(); 341 } 342 343 void Serializer::collect(SmallVectorImpl<uint32_t> &binary) { 344 auto moduleSize = spirv::kHeaderWordCount + capabilities.size() + 345 extensions.size() + extendedSets.size() + 346 memoryModel.size() + entryPoints.size() + 347 executionModes.size() + decorations.size() + 348 typesGlobalValues.size() + functions.size(); 349 350 binary.clear(); 351 binary.reserve(moduleSize); 352 353 spirv::appendModuleHeader(binary, nextID); 354 binary.append(capabilities.begin(), capabilities.end()); 355 binary.append(extensions.begin(), extensions.end()); 356 binary.append(extendedSets.begin(), extendedSets.end()); 357 binary.append(memoryModel.begin(), memoryModel.end()); 358 binary.append(entryPoints.begin(), entryPoints.end()); 359 binary.append(executionModes.begin(), executionModes.end()); 360 binary.append(names.begin(), names.end()); 361 binary.append(decorations.begin(), decorations.end()); 362 binary.append(typesGlobalValues.begin(), typesGlobalValues.end()); 363 binary.append(functions.begin(), functions.end()); 364 } 365 //===----------------------------------------------------------------------===// 366 // Module structure 367 //===----------------------------------------------------------------------===// 368 369 void Serializer::processCapability() { 370 auto caps = module.getAttrOfType<ArrayAttr>("capabilities"); 371 if (!caps) 372 return; 373 374 for (auto cap : caps.getValue()) { 375 auto capStr = cap.cast<StringAttr>().getValue(); 376 auto capVal = spirv::symbolizeCapability(capStr); 377 encodeInstructionInto(capabilities, spirv::Opcode::OpCapability, 378 {static_cast<uint32_t>(*capVal)}); 379 } 380 } 381 382 void Serializer::processExtension() { 383 auto exts = module.getAttrOfType<ArrayAttr>("extensions"); 384 if (!exts) 385 return; 386 387 SmallVector<uint32_t, 16> extName; 388 for (auto ext : exts.getValue()) { 389 auto extStr = ext.cast<StringAttr>().getValue(); 390 extName.clear(); 391 encodeStringLiteralInto(extName, extStr); 392 encodeInstructionInto(extensions, spirv::Opcode::OpExtension, extName); 393 } 394 } 395 396 void Serializer::processMemoryModel() { 397 uint32_t mm = module.getAttrOfType<IntegerAttr>("memory_model").getInt(); 398 uint32_t am = module.getAttrOfType<IntegerAttr>("addressing_model").getInt(); 399 400 encodeInstructionInto(memoryModel, spirv::Opcode::OpMemoryModel, {am, mm}); 401 } 402 403 LogicalResult Serializer::processConstantOp(spirv::ConstantOp op) { 404 if (auto resultID = prepareConstant(op.getLoc(), op.getType(), op.value())) { 405 valueIDMap[op.getResult()] = resultID; 406 return success(); 407 } 408 return failure(); 409 } 410 411 LogicalResult Serializer::processSpecConstantOp(spirv::SpecConstantOp op) { 412 if (auto resultID = prepareConstantScalar(op.getLoc(), op.default_value(), 413 /*isSpec=*/true)) { 414 specConstIDMap[op.sym_name()] = resultID; 415 return processName(resultID, op.sym_name()); 416 } 417 return failure(); 418 } 419 420 LogicalResult Serializer::processDecoration(Location loc, uint32_t resultID, 421 NamedAttribute attr) { 422 auto attrName = attr.first.strref(); 423 auto decorationName = mlir::convertToCamelCase(attrName, true); 424 auto decoration = spirv::symbolizeDecoration(decorationName); 425 if (!decoration) { 426 return emitError( 427 loc, "non-argument attributes expected to have snake-case-ified " 428 "decoration name, unhandled attribute with name : ") 429 << attrName; 430 } 431 SmallVector<uint32_t, 1> args; 432 args.push_back(resultID); 433 args.push_back(static_cast<uint32_t>(decoration.getValue())); 434 switch (decoration.getValue()) { 435 case spirv::Decoration::DescriptorSet: 436 case spirv::Decoration::Binding: 437 if (auto intAttr = attr.second.dyn_cast<IntegerAttr>()) { 438 args.push_back(intAttr.getValue().getZExtValue()); 439 break; 440 } 441 return emitError(loc, "expected integer attribute for ") << attrName; 442 case spirv::Decoration::BuiltIn: 443 if (auto strAttr = attr.second.dyn_cast<StringAttr>()) { 444 auto enumVal = spirv::symbolizeBuiltIn(strAttr.getValue()); 445 if (enumVal) { 446 args.push_back(static_cast<uint32_t>(enumVal.getValue())); 447 break; 448 } 449 return emitError(loc, "invalid ") 450 << attrName << " attribute " << strAttr.getValue(); 451 } 452 return emitError(loc, "expected string attribute for ") << attrName; 453 default: 454 return emitError(loc, "unhandled decoration ") << decorationName; 455 } 456 return encodeInstructionInto(decorations, spirv::Opcode::OpDecorate, args); 457 } 458 459 LogicalResult Serializer::processName(uint32_t resultID, StringRef name) { 460 assert(!name.empty() && "unexpected empty string for OpName"); 461 462 SmallVector<uint32_t, 4> nameOperands; 463 nameOperands.push_back(resultID); 464 if (failed(encodeStringLiteralInto(nameOperands, name))) { 465 return failure(); 466 } 467 return encodeInstructionInto(names, spirv::Opcode::OpName, nameOperands); 468 } 469 470 namespace { 471 template <> 472 LogicalResult Serializer::processTypeDecoration<spirv::ArrayType>( 473 Location loc, spirv::ArrayType type, uint32_t resultID) { 474 if (type.hasLayout()) { 475 // OpDecorate %arrayTypeSSA ArrayStride strideLiteral 476 SmallVector<uint32_t, 3> args; 477 args.push_back(resultID); 478 args.push_back(static_cast<uint32_t>(spirv::Decoration::ArrayStride)); 479 args.push_back(type.getArrayStride()); 480 return encodeInstructionInto(decorations, spirv::Opcode::OpDecorate, args); 481 } 482 return success(); 483 } 484 485 LogicalResult 486 Serializer::processMemberDecoration(uint32_t structID, uint32_t memberIndex, 487 spirv::Decoration decorationType, 488 uint32_t value) { 489 SmallVector<uint32_t, 4> args( 490 {structID, memberIndex, static_cast<uint32_t>(decorationType), value}); 491 return encodeInstructionInto(decorations, spirv::Opcode::OpMemberDecorate, 492 args); 493 } 494 } // namespace 495 496 LogicalResult Serializer::processFuncOp(FuncOp op) { 497 uint32_t fnTypeID = 0; 498 // Generate type of the function. 499 processType(op.getLoc(), op.getType(), fnTypeID); 500 501 // Add the function definition. 502 SmallVector<uint32_t, 4> operands; 503 uint32_t resTypeID = 0; 504 auto resultTypes = op.getType().getResults(); 505 if (resultTypes.size() > 1) { 506 return emitError(op.getLoc(), 507 "cannot serialize function with multiple return types"); 508 } 509 if (failed(processType(op.getLoc(), 510 (resultTypes.empty() ? getVoidType() : resultTypes[0]), 511 resTypeID))) { 512 return failure(); 513 } 514 operands.push_back(resTypeID); 515 auto funcID = getNextID(); 516 funcIDMap[op.getName()] = funcID; 517 operands.push_back(funcID); 518 // TODO : Support other function control options. 519 operands.push_back(static_cast<uint32_t>(spirv::FunctionControl::None)); 520 operands.push_back(fnTypeID); 521 encodeInstructionInto(functions, spirv::Opcode::OpFunction, operands); 522 523 // Add function name. 524 if (failed(processName(funcID, op.getName()))) { 525 return failure(); 526 } 527 528 // Declare the parameters. 529 for (auto arg : op.getArguments()) { 530 uint32_t argTypeID = 0; 531 if (failed(processType(op.getLoc(), arg->getType(), argTypeID))) { 532 return failure(); 533 } 534 auto argValueID = getNextID(); 535 valueIDMap[arg] = argValueID; 536 encodeInstructionInto(functions, spirv::Opcode::OpFunctionParameter, 537 {argTypeID, argValueID}); 538 } 539 540 // Process the body. 541 if (op.isExternal()) { 542 return emitError(op.getLoc(), "external function is unhandled"); 543 } 544 545 for (auto &b : op) { 546 // TODO(antiagainst): support basic blocks and control flow properly. 547 encodeInstructionInto(functions, spirv::Opcode::OpLabel, {getNextID()}); 548 for (auto &op : b) { 549 if (failed(processOperation(&op))) { 550 return failure(); 551 } 552 } 553 } 554 555 // Insert Function End. 556 return encodeInstructionInto(functions, spirv::Opcode::OpFunctionEnd, {}); 557 } 558 559 LogicalResult 560 Serializer::processGlobalVariableOp(spirv::GlobalVariableOp varOp) { 561 // Get TypeID. 562 uint32_t resultTypeID = 0; 563 SmallVector<StringRef, 4> elidedAttrs; 564 if (failed(processType(varOp.getLoc(), varOp.type(), resultTypeID))) { 565 return failure(); 566 } 567 568 if (isInterfaceStructPtrType(varOp.type())) { 569 auto structType = varOp.type() 570 .cast<spirv::PointerType>() 571 .getPointeeType() 572 .cast<spirv::StructType>(); 573 SmallVector<uint32_t, 2> args{ 574 findTypeID(structType), 575 static_cast<uint32_t>(spirv::Decoration::Block)}; 576 if (failed(encodeInstructionInto(decorations, spirv::Opcode::OpDecorate, 577 args))) { 578 return varOp.emitError("cannot decorate ") 579 << structType << " with Block decoration"; 580 } 581 } 582 583 elidedAttrs.push_back("type"); 584 SmallVector<uint32_t, 4> operands; 585 operands.push_back(resultTypeID); 586 auto resultID = getNextID(); 587 588 // Encode the name. 589 auto varName = varOp.sym_name(); 590 elidedAttrs.push_back(SymbolTable::getSymbolAttrName()); 591 if (failed(processName(resultID, varName))) { 592 return failure(); 593 } 594 globalVarIDMap[varName] = resultID; 595 operands.push_back(resultID); 596 597 // Encode StorageClass. 598 operands.push_back(static_cast<uint32_t>(varOp.storageClass())); 599 600 // Encode initialization. 601 if (auto initializer = varOp.initializer()) { 602 auto initializerID = findVariableID(initializer.getValue()); 603 if (!initializerID) { 604 return emitError(varOp.getLoc(), 605 "invalid usage of undefined variable as initializer"); 606 } 607 operands.push_back(initializerID); 608 elidedAttrs.push_back("initializer"); 609 } 610 611 if (failed(encodeInstructionInto(functions, spirv::Opcode::OpVariable, 612 operands))) { 613 elidedAttrs.push_back("initializer"); 614 return failure(); 615 } 616 617 // Encode decorations. 618 for (auto attr : varOp.getAttrs()) { 619 if (llvm::any_of(elidedAttrs, 620 [&](StringRef elided) { return attr.first.is(elided); })) { 621 continue; 622 } 623 if (failed(processDecoration(varOp.getLoc(), resultID, attr))) { 624 return failure(); 625 } 626 } 627 return success(); 628 } 629 630 //===----------------------------------------------------------------------===// 631 // Type 632 //===----------------------------------------------------------------------===// 633 634 bool Serializer::isInterfaceStructPtrType(Type type) const { 635 if (auto ptrType = type.dyn_cast<spirv::PointerType>()) { 636 auto storageClass = ptrType.getStorageClass(); 637 if (storageClass == spirv::StorageClass::Uniform || 638 storageClass == spirv::StorageClass::StorageBuffer) { 639 return ptrType.getPointeeType().isa<spirv::StructType>(); 640 } 641 } 642 return false; 643 } 644 645 LogicalResult Serializer::processType(Location loc, Type type, 646 uint32_t &typeID) { 647 typeID = findTypeID(type); 648 if (typeID) { 649 return success(); 650 } 651 typeID = getNextID(); 652 SmallVector<uint32_t, 4> operands; 653 operands.push_back(typeID); 654 auto typeEnum = spirv::Opcode::OpTypeVoid; 655 if ((type.isa<FunctionType>() && 656 succeeded(prepareFunctionType(loc, type.cast<FunctionType>(), typeEnum, 657 operands))) || 658 succeeded(prepareBasicType(loc, type, typeID, typeEnum, operands))) { 659 typeIDMap[type] = typeID; 660 return encodeInstructionInto(typesGlobalValues, typeEnum, operands); 661 } 662 return failure(); 663 } 664 665 LogicalResult 666 Serializer::prepareBasicType(Location loc, Type type, uint32_t resultID, 667 spirv::Opcode &typeEnum, 668 SmallVectorImpl<uint32_t> &operands) { 669 if (isVoidType(type)) { 670 typeEnum = spirv::Opcode::OpTypeVoid; 671 return success(); 672 } 673 674 if (auto intType = type.dyn_cast<IntegerType>()) { 675 if (intType.getWidth() == 1) { 676 typeEnum = spirv::Opcode::OpTypeBool; 677 return success(); 678 } 679 680 typeEnum = spirv::Opcode::OpTypeInt; 681 operands.push_back(intType.getWidth()); 682 // TODO(antiagainst): support unsigned integers 683 operands.push_back(1); 684 return success(); 685 } 686 687 if (auto floatType = type.dyn_cast<FloatType>()) { 688 typeEnum = spirv::Opcode::OpTypeFloat; 689 operands.push_back(floatType.getWidth()); 690 return success(); 691 } 692 693 if (auto vectorType = type.dyn_cast<VectorType>()) { 694 uint32_t elementTypeID = 0; 695 if (failed(processType(loc, vectorType.getElementType(), elementTypeID))) { 696 return failure(); 697 } 698 typeEnum = spirv::Opcode::OpTypeVector; 699 operands.push_back(elementTypeID); 700 operands.push_back(vectorType.getNumElements()); 701 return success(); 702 } 703 704 if (auto arrayType = type.dyn_cast<spirv::ArrayType>()) { 705 typeEnum = spirv::Opcode::OpTypeArray; 706 uint32_t elementTypeID = 0; 707 if (failed(processType(loc, arrayType.getElementType(), elementTypeID))) { 708 return failure(); 709 } 710 operands.push_back(elementTypeID); 711 if (auto elementCountID = prepareConstantInt( 712 loc, mlirBuilder.getI32IntegerAttr(arrayType.getNumElements()))) { 713 operands.push_back(elementCountID); 714 } 715 return processTypeDecoration(loc, arrayType, resultID); 716 } 717 718 if (auto ptrType = type.dyn_cast<spirv::PointerType>()) { 719 uint32_t pointeeTypeID = 0; 720 if (failed(processType(loc, ptrType.getPointeeType(), pointeeTypeID))) { 721 return failure(); 722 } 723 typeEnum = spirv::Opcode::OpTypePointer; 724 operands.push_back(static_cast<uint32_t>(ptrType.getStorageClass())); 725 operands.push_back(pointeeTypeID); 726 return success(); 727 } 728 729 if (auto structType = type.dyn_cast<spirv::StructType>()) { 730 bool hasLayout = structType.hasLayout(); 731 for (auto elementIndex : 732 llvm::seq<uint32_t>(0, structType.getNumElements())) { 733 uint32_t elementTypeID = 0; 734 if (failed(processType(loc, structType.getElementType(elementIndex), 735 elementTypeID))) { 736 return failure(); 737 } 738 operands.push_back(elementTypeID); 739 if (hasLayout) { 740 // Decorate each struct member with an offset 741 if (failed(processMemberDecoration( 742 resultID, elementIndex, spirv::Decoration::Offset, 743 static_cast<uint32_t>(structType.getOffset(elementIndex))))) { 744 return emitError(loc, "cannot decorate ") 745 << elementIndex << "-th member of : " << structType 746 << "with its offset"; 747 } 748 } 749 } 750 typeEnum = spirv::Opcode::OpTypeStruct; 751 return success(); 752 } 753 754 // TODO(ravishankarm) : Handle other types. 755 return emitError(loc, "unhandled type in serialization: ") << type; 756 } 757 758 LogicalResult 759 Serializer::prepareFunctionType(Location loc, FunctionType type, 760 spirv::Opcode &typeEnum, 761 SmallVectorImpl<uint32_t> &operands) { 762 typeEnum = spirv::Opcode::OpTypeFunction; 763 assert(type.getNumResults() <= 1 && 764 "Serialization supports only a single return value"); 765 uint32_t resultID = 0; 766 if (failed(processType( 767 loc, type.getNumResults() == 1 ? type.getResult(0) : getVoidType(), 768 resultID))) { 769 return failure(); 770 } 771 operands.push_back(resultID); 772 for (auto &res : type.getInputs()) { 773 uint32_t argTypeID = 0; 774 if (failed(processType(loc, res, argTypeID))) { 775 return failure(); 776 } 777 operands.push_back(argTypeID); 778 } 779 return success(); 780 } 781 782 //===----------------------------------------------------------------------===// 783 // Constant 784 //===----------------------------------------------------------------------===// 785 786 uint32_t Serializer::prepareConstant(Location loc, Type constType, 787 Attribute valueAttr) { 788 if (auto id = prepareConstantScalar(loc, valueAttr)) { 789 return id; 790 } 791 // This is a composite literal. We need to handle each component separately 792 // and then emit an OpConstantComposite for the whole. 793 794 if (auto id = findConstantID(valueAttr)) { 795 return id; 796 } 797 798 uint32_t typeID = 0; 799 if (failed(processType(loc, constType, typeID))) { 800 return 0; 801 } 802 auto resultID = getNextID(); 803 804 spirv::Opcode opcode = spirv::Opcode::OpNop; 805 SmallVector<uint32_t, 4> operands; 806 operands.push_back(typeID); 807 operands.push_back(resultID); 808 809 if (auto vectorAttr = valueAttr.dyn_cast<DenseIntElementsAttr>()) { 810 if (vectorAttr.getType().getElementType().isInteger(1)) { 811 if (failed(prepareBoolVectorConstant(loc, vectorAttr, opcode, operands))) 812 return 0; 813 } else if (failed( 814 prepareIntVectorConstant(loc, vectorAttr, opcode, operands))) 815 return 0; 816 } else if (auto vectorAttr = valueAttr.dyn_cast<DenseFPElementsAttr>()) { 817 if (failed(prepareFloatVectorConstant(loc, vectorAttr, opcode, operands))) 818 return 0; 819 } else if (auto arrayAttr = valueAttr.dyn_cast<ArrayAttr>()) { 820 opcode = spirv::Opcode::OpConstantComposite; 821 operands.reserve(arrayAttr.size() + 2); 822 823 auto elementType = constType.cast<spirv::ArrayType>().getElementType(); 824 for (Attribute elementAttr : arrayAttr) 825 if (auto elementID = prepareConstant(loc, elementType, elementAttr)) { 826 operands.push_back(elementID); 827 } else { 828 return 0; 829 } 830 } else { 831 emitError(loc, "cannot serialize attribute: ") << valueAttr; 832 return 0; 833 } 834 835 encodeInstructionInto(typesGlobalValues, opcode, operands); 836 constIDMap[valueAttr] = resultID; 837 return resultID; 838 } 839 840 LogicalResult Serializer::prepareBoolVectorConstant( 841 Location loc, DenseIntElementsAttr elementsAttr, spirv::Opcode &opcode, 842 SmallVectorImpl<uint32_t> &operands) { 843 auto type = elementsAttr.getType(); 844 assert(type.hasRank() && type.getRank() == 1 && 845 "spv.constant should have verified only vector literal uses " 846 "ElementsAttr"); 847 assert(type.getElementType().isInteger(1) && "must be bool ElementsAttr"); 848 auto count = type.getNumElements(); 849 850 // Operands for constructing the SPIR-V OpConstant* instruction 851 operands.reserve(count + 2); 852 853 // For splat cases, we don't need to loop over all elements, especially when 854 // the splat value is zero. 855 if (elementsAttr.isSplat()) { 856 // We can use OpConstantNull if this bool ElementsAttr is splatting false. 857 if (!elementsAttr.getSplatValue<bool>()) { 858 opcode = spirv::Opcode::OpConstantNull; 859 return success(); 860 } 861 862 if (auto id = 863 prepareConstantBool(loc, elementsAttr.getSplatValue<BoolAttr>())) { 864 opcode = spirv::Opcode::OpConstantComposite; 865 operands.append(count, id); 866 return success(); 867 } 868 869 return failure(); 870 } 871 872 // Otherwise, we need to process each element and compose them with 873 // OpConstantComposite. 874 opcode = spirv::Opcode::OpConstantComposite; 875 for (auto boolAttr : elementsAttr.getValues<BoolAttr>()) { 876 // We are constructing an BoolAttr for each value here. But given that 877 // we only use ElementsAttr for vectors with no more than 4 elements, it 878 // should be fine here. 879 if (auto elementID = prepareConstantBool(loc, boolAttr)) { 880 operands.push_back(elementID); 881 } else { 882 return failure(); 883 } 884 } 885 return success(); 886 } 887 888 LogicalResult Serializer::prepareIntVectorConstant( 889 Location loc, DenseIntElementsAttr elementsAttr, spirv::Opcode &opcode, 890 SmallVectorImpl<uint32_t> &operands) { 891 auto type = elementsAttr.getType(); 892 assert(type.hasRank() && type.getRank() == 1 && 893 "spv.constant should have verified only vector literal uses " 894 "ElementsAttr"); 895 assert(!type.getElementType().isInteger(1) && 896 "must be non-bool ElementsAttr"); 897 auto count = type.getNumElements(); 898 899 // Operands for constructing the SPIR-V OpConstant* instruction 900 operands.reserve(count + 2); 901 902 // For splat cases, we don't need to loop over all elements, especially when 903 // the splat value is zero. 904 if (elementsAttr.isSplat()) { 905 auto splatAttr = elementsAttr.getSplatValue<IntegerAttr>(); 906 907 // We can use OpConstantNull if this int ElementsAttr is splatting 0. 908 if (splatAttr.getValue().isNullValue()) { 909 opcode = spirv::Opcode::OpConstantNull; 910 return success(); 911 } 912 913 if (auto id = prepareConstantInt(loc, splatAttr)) { 914 opcode = spirv::Opcode::OpConstantComposite; 915 operands.append(count, id); 916 return success(); 917 } 918 return failure(); 919 } 920 921 // Otherwise, we need to process each element and compose them with 922 // OpConstantComposite. 923 opcode = spirv::Opcode::OpConstantComposite; 924 for (auto intAttr : elementsAttr.getValues<IntegerAttr>()) { 925 // We are constructing an IntegerAttr for each value here. But given that 926 // we only use ElementsAttr for vectors with no more than 4 elements, it 927 // should be fine here. 928 // TODO(antiagainst): revisit this if special extensions enabling large 929 // vectors are supported. 930 if (auto elementID = prepareConstantInt(loc, intAttr)) { 931 operands.push_back(elementID); 932 } else { 933 return failure(); 934 } 935 } 936 return success(); 937 } 938 939 LogicalResult Serializer::prepareFloatVectorConstant( 940 Location loc, DenseFPElementsAttr elementsAttr, spirv::Opcode &opcode, 941 SmallVectorImpl<uint32_t> &operands) { 942 auto type = elementsAttr.getType(); 943 assert(type.hasRank() && type.getRank() == 1 && 944 "spv.constant should have verified only vector literal uses " 945 "ElementsAttr"); 946 auto count = type.getNumElements(); 947 948 operands.reserve(count + 2); 949 950 if (elementsAttr.isSplat()) { 951 FloatAttr splatAttr = elementsAttr.getSplatValue<FloatAttr>(); 952 if (splatAttr.getValue().isZero()) { 953 opcode = spirv::Opcode::OpConstantNull; 954 return success(); 955 } 956 957 if (auto id = prepareConstantFp(loc, splatAttr)) { 958 opcode = spirv::Opcode::OpConstantComposite; 959 operands.append(count, id); 960 return success(); 961 } 962 963 return failure(); 964 } 965 966 opcode = spirv::Opcode::OpConstantComposite; 967 for (auto fpAttr : elementsAttr.getValues<FloatAttr>()) { 968 if (auto elementID = prepareConstantFp(loc, fpAttr)) { 969 operands.push_back(elementID); 970 } else { 971 return failure(); 972 } 973 } 974 return success(); 975 } 976 977 uint32_t Serializer::prepareConstantScalar(Location loc, Attribute valueAttr, 978 bool isSpec) { 979 if (auto floatAttr = valueAttr.dyn_cast<FloatAttr>()) { 980 return prepareConstantFp(loc, floatAttr, isSpec); 981 } 982 if (auto intAttr = valueAttr.dyn_cast<IntegerAttr>()) { 983 return prepareConstantInt(loc, intAttr, isSpec); 984 } 985 if (auto boolAttr = valueAttr.dyn_cast<BoolAttr>()) { 986 return prepareConstantBool(loc, boolAttr, isSpec); 987 } 988 989 return 0; 990 } 991 992 uint32_t Serializer::prepareConstantBool(Location loc, BoolAttr boolAttr, 993 bool isSpec) { 994 if (!isSpec) { 995 // We can de-duplicate nomral contants, but not specialization constants. 996 if (auto id = findConstantID(boolAttr)) { 997 return id; 998 } 999 } 1000 1001 // Process the type for this bool literal 1002 uint32_t typeID = 0; 1003 if (failed(processType(loc, boolAttr.getType(), typeID))) { 1004 return 0; 1005 } 1006 1007 auto resultID = getNextID(); 1008 auto opcode = boolAttr.getValue() 1009 ? (isSpec ? spirv::Opcode::OpSpecConstantTrue 1010 : spirv::Opcode::OpConstantTrue) 1011 : (isSpec ? spirv::Opcode::OpSpecConstantFalse 1012 : spirv::Opcode::OpConstantFalse); 1013 encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID}); 1014 1015 if (!isSpec) { 1016 constIDMap[boolAttr] = resultID; 1017 } 1018 return resultID; 1019 } 1020 1021 uint32_t Serializer::prepareConstantInt(Location loc, IntegerAttr intAttr, 1022 bool isSpec) { 1023 if (!isSpec) { 1024 // We can de-duplicate nomral contants, but not specialization constants. 1025 if (auto id = findConstantID(intAttr)) { 1026 return id; 1027 } 1028 } 1029 1030 // Process the type for this integer literal 1031 uint32_t typeID = 0; 1032 if (failed(processType(loc, intAttr.getType(), typeID))) { 1033 return 0; 1034 } 1035 1036 auto resultID = getNextID(); 1037 APInt value = intAttr.getValue(); 1038 unsigned bitwidth = value.getBitWidth(); 1039 bool isSigned = value.isSignedIntN(bitwidth); 1040 1041 auto opcode = 1042 isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant; 1043 1044 // According to SPIR-V spec, "When the type's bit width is less than 32-bits, 1045 // the literal's value appears in the low-order bits of the word, and the 1046 // high-order bits must be 0 for a floating-point type, or 0 for an integer 1047 // type with Signedness of 0, or sign extended when Signedness is 1." 1048 if (bitwidth == 32 || bitwidth == 16) { 1049 uint32_t word = 0; 1050 if (isSigned) { 1051 word = static_cast<int32_t>(value.getSExtValue()); 1052 } else { 1053 word = static_cast<uint32_t>(value.getZExtValue()); 1054 } 1055 encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word}); 1056 } 1057 // According to SPIR-V spec: "When the type's bit width is larger than one 1058 // word, the literal’s low-order words appear first." 1059 else if (bitwidth == 64) { 1060 struct DoubleWord { 1061 uint32_t word1; 1062 uint32_t word2; 1063 } words; 1064 if (isSigned) { 1065 words = llvm::bit_cast<DoubleWord>(value.getSExtValue()); 1066 } else { 1067 words = llvm::bit_cast<DoubleWord>(value.getZExtValue()); 1068 } 1069 encodeInstructionInto(typesGlobalValues, opcode, 1070 {typeID, resultID, words.word1, words.word2}); 1071 } else { 1072 std::string valueStr; 1073 llvm::raw_string_ostream rss(valueStr); 1074 value.print(rss, /*isSigned=*/false); 1075 1076 emitError(loc, "cannot serialize ") 1077 << bitwidth << "-bit integer literal: " << rss.str(); 1078 return 0; 1079 } 1080 1081 if (!isSpec) { 1082 constIDMap[intAttr] = resultID; 1083 } 1084 return resultID; 1085 } 1086 1087 uint32_t Serializer::prepareConstantFp(Location loc, FloatAttr floatAttr, 1088 bool isSpec) { 1089 if (!isSpec) { 1090 // We can de-duplicate nomral contants, but not specialization constants. 1091 if (auto id = findConstantID(floatAttr)) { 1092 return id; 1093 } 1094 } 1095 1096 // Process the type for this float literal 1097 uint32_t typeID = 0; 1098 if (failed(processType(loc, floatAttr.getType(), typeID))) { 1099 return 0; 1100 } 1101 1102 auto resultID = getNextID(); 1103 APFloat value = floatAttr.getValue(); 1104 APInt intValue = value.bitcastToAPInt(); 1105 1106 auto opcode = 1107 isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant; 1108 1109 if (&value.getSemantics() == &APFloat::IEEEsingle()) { 1110 uint32_t word = llvm::bit_cast<uint32_t>(value.convertToFloat()); 1111 encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word}); 1112 } else if (&value.getSemantics() == &APFloat::IEEEdouble()) { 1113 struct DoubleWord { 1114 uint32_t word1; 1115 uint32_t word2; 1116 } words = llvm::bit_cast<DoubleWord>(value.convertToDouble()); 1117 encodeInstructionInto(typesGlobalValues, opcode, 1118 {typeID, resultID, words.word1, words.word2}); 1119 } else if (&value.getSemantics() == &APFloat::IEEEhalf()) { 1120 uint32_t word = 1121 static_cast<uint32_t>(value.bitcastToAPInt().getZExtValue()); 1122 encodeInstructionInto(typesGlobalValues, opcode, {typeID, resultID, word}); 1123 } else { 1124 std::string valueStr; 1125 llvm::raw_string_ostream rss(valueStr); 1126 value.print(rss); 1127 1128 emitError(loc, "cannot serialize ") 1129 << floatAttr.getType() << "-typed float literal: " << rss.str(); 1130 return 0; 1131 } 1132 1133 if (!isSpec) { 1134 constIDMap[floatAttr] = resultID; 1135 } 1136 return resultID; 1137 } 1138 1139 //===----------------------------------------------------------------------===// 1140 // Operation 1141 //===----------------------------------------------------------------------===// 1142 1143 LogicalResult Serializer::processAddressOfOp(spirv::AddressOfOp addressOfOp) { 1144 auto varName = addressOfOp.variable(); 1145 auto variableID = findVariableID(varName); 1146 if (!variableID) { 1147 return addressOfOp.emitError("unknown result <id> for variable ") 1148 << varName; 1149 } 1150 valueIDMap[addressOfOp.pointer()] = variableID; 1151 return success(); 1152 } 1153 1154 LogicalResult 1155 Serializer::processReferenceOfOp(spirv::ReferenceOfOp referenceOfOp) { 1156 auto constName = referenceOfOp.spec_const(); 1157 auto constID = findSpecConstID(constName); 1158 if (!constID) { 1159 return referenceOfOp.emitError( 1160 "unknown result <id> for specialization constant ") 1161 << constName; 1162 } 1163 valueIDMap[referenceOfOp.reference()] = constID; 1164 return success(); 1165 } 1166 1167 LogicalResult Serializer::processOperation(Operation *op) { 1168 // First dispatch the methods that do not directly mirror an operation from 1169 // the SPIR-V spec 1170 if (auto constOp = dyn_cast<spirv::ConstantOp>(op)) { 1171 return processConstantOp(constOp); 1172 } 1173 if (auto specConstOp = dyn_cast<spirv::SpecConstantOp>(op)) { 1174 return processSpecConstantOp(specConstOp); 1175 } 1176 if (auto refOpOp = dyn_cast<spirv::ReferenceOfOp>(op)) { 1177 return processReferenceOfOp(refOpOp); 1178 } 1179 if (auto fnOp = dyn_cast<FuncOp>(op)) { 1180 return processFuncOp(fnOp); 1181 } 1182 if (isa<spirv::ModuleEndOp>(op)) { 1183 return success(); 1184 } 1185 if (auto varOp = dyn_cast<spirv::GlobalVariableOp>(op)) { 1186 return processGlobalVariableOp(varOp); 1187 } 1188 if (auto addressOfOp = dyn_cast<spirv::AddressOfOp>(op)) { 1189 return processAddressOfOp(addressOfOp); 1190 } 1191 return dispatchToAutogenSerialization(op); 1192 } 1193 1194 namespace { 1195 template <> 1196 LogicalResult 1197 Serializer::processOp<spirv::EntryPointOp>(spirv::EntryPointOp op) { 1198 SmallVector<uint32_t, 4> operands; 1199 // Add the ExectionModel. 1200 operands.push_back(static_cast<uint32_t>(op.execution_model())); 1201 // Add the function <id>. 1202 auto funcID = findFunctionID(op.fn()); 1203 if (!funcID) { 1204 return op.emitError("missing <id> for function ") 1205 << op.fn() 1206 << "; function needs to be defined before spv.EntryPoint is " 1207 "serialized"; 1208 } 1209 operands.push_back(funcID); 1210 // Add the name of the function. 1211 encodeStringLiteralInto(operands, op.fn()); 1212 1213 // Add the interface values. 1214 if (auto interface = op.interface()) { 1215 for (auto var : interface.getValue()) { 1216 auto id = findVariableID(var.cast<SymbolRefAttr>().getValue()); 1217 if (!id) { 1218 return op.emitError("referencing undefined global variable." 1219 "spv.EntryPoint is at the end of spv.module. All " 1220 "referenced variables should already be defined"); 1221 } 1222 operands.push_back(id); 1223 } 1224 } 1225 return encodeInstructionInto(entryPoints, spirv::Opcode::OpEntryPoint, 1226 operands); 1227 } 1228 1229 template <> 1230 LogicalResult 1231 Serializer::processOp<spirv::ExecutionModeOp>(spirv::ExecutionModeOp op) { 1232 SmallVector<uint32_t, 4> operands; 1233 // Add the function <id>. 1234 auto funcID = findFunctionID(op.fn()); 1235 if (!funcID) { 1236 return op.emitError("missing <id> for function ") 1237 << op.fn() 1238 << "; function needs to be serialized before ExecutionModeOp is " 1239 "serialized"; 1240 } 1241 operands.push_back(funcID); 1242 // Add the ExecutionMode. 1243 operands.push_back(static_cast<uint32_t>(op.execution_mode())); 1244 1245 // Serialize values if any. 1246 auto values = op.values(); 1247 if (values) { 1248 for (auto &intVal : values.getValue()) { 1249 operands.push_back(static_cast<uint32_t>( 1250 intVal.cast<IntegerAttr>().getValue().getZExtValue())); 1251 } 1252 } 1253 return encodeInstructionInto(executionModes, spirv::Opcode::OpExecutionMode, 1254 operands); 1255 } 1256 1257 // Pull in auto-generated Serializer::dispatchToAutogenSerialization() and 1258 // various Serializer::processOp<...>() specializations. 1259 #define GET_SERIALIZATION_FNS 1260 #include "mlir/Dialect/SPIRV/SPIRVSerialization.inc" 1261 } // namespace 1262 1263 LogicalResult spirv::serialize(spirv::ModuleOp module, 1264 SmallVectorImpl<uint32_t> &binary) { 1265 Serializer serializer(module); 1266 1267 if (failed(serializer.serialize())) 1268 return failure(); 1269 1270 serializer.collect(binary); 1271 return success(); 1272 }