1 //===- SerializeOps.cpp - MLIR SPIR-V Serialization (Ops) -----------------===// 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 serialization methods for MLIR SPIR-V module ops. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "Serializer.h" 14 15 #include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h" 16 #include "mlir/IR/RegionGraphTraits.h" 17 #include "mlir/Support/LogicalResult.h" 18 #include "mlir/Target/SPIRV/SPIRVBinaryUtils.h" 19 #include "llvm/ADT/DepthFirstIterator.h" 20 #include "llvm/Support/Debug.h" 21 22 #define DEBUG_TYPE "spirv-serialization" 23 24 using namespace mlir; 25 26 /// A pre-order depth-first visitor function for processing basic blocks. 27 /// 28 /// Visits the basic blocks starting from the given `headerBlock` in pre-order 29 /// depth-first manner and calls `blockHandler` on each block. Skips handling 30 /// blocks in the `skipBlocks` list. If `skipHeader` is true, `blockHandler` 31 /// will not be invoked in `headerBlock` but still handles all `headerBlock`'s 32 /// successors. 33 /// 34 /// SPIR-V spec "2.16.1. Universal Validation Rules" requires that "the order 35 /// of blocks in a function must satisfy the rule that blocks appear before 36 /// all blocks they dominate." This can be achieved by a pre-order CFG 37 /// traversal algorithm. To make the serialization output more logical and 38 /// readable to human, we perform depth-first CFG traversal and delay the 39 /// serialization of the merge block and the continue block, if exists, until 40 /// after all other blocks have been processed. 41 static LogicalResult 42 visitInPrettyBlockOrder(Block *headerBlock, 43 function_ref<LogicalResult(Block *)> blockHandler, 44 bool skipHeader = false, BlockRange skipBlocks = {}) { 45 llvm::df_iterator_default_set<Block *, 4> doneBlocks; 46 doneBlocks.insert(skipBlocks.begin(), skipBlocks.end()); 47 48 for (Block *block : llvm::depth_first_ext(headerBlock, doneBlocks)) { 49 if (skipHeader && block == headerBlock) 50 continue; 51 if (failed(blockHandler(block))) 52 return failure(); 53 } 54 return success(); 55 } 56 57 namespace mlir { 58 namespace spirv { 59 LogicalResult Serializer::processConstantOp(spirv::ConstantOp op) { 60 if (auto resultID = prepareConstant(op.getLoc(), op.getType(), op.value())) { 61 valueIDMap[op.getResult()] = resultID; 62 return success(); 63 } 64 return failure(); 65 } 66 67 LogicalResult Serializer::processSpecConstantOp(spirv::SpecConstantOp op) { 68 if (auto resultID = prepareConstantScalar(op.getLoc(), op.default_value(), 69 /*isSpec=*/true)) { 70 // Emit the OpDecorate instruction for SpecId. 71 if (auto specID = op->getAttrOfType<IntegerAttr>("spec_id")) { 72 auto val = static_cast<uint32_t>(specID.getInt()); 73 (void)emitDecoration(resultID, spirv::Decoration::SpecId, {val}); 74 } 75 76 specConstIDMap[op.sym_name()] = resultID; 77 return processName(resultID, op.sym_name()); 78 } 79 return failure(); 80 } 81 82 LogicalResult 83 Serializer::processSpecConstantCompositeOp(spirv::SpecConstantCompositeOp op) { 84 uint32_t typeID = 0; 85 if (failed(processType(op.getLoc(), op.type(), typeID))) { 86 return failure(); 87 } 88 89 auto resultID = getNextID(); 90 91 SmallVector<uint32_t, 8> operands; 92 operands.push_back(typeID); 93 operands.push_back(resultID); 94 95 auto constituents = op.constituents(); 96 97 for (auto index : llvm::seq<uint32_t>(0, constituents.size())) { 98 auto constituent = constituents[index].dyn_cast<FlatSymbolRefAttr>(); 99 100 auto constituentName = constituent.getValue(); 101 auto constituentID = getSpecConstID(constituentName); 102 103 if (!constituentID) { 104 return op.emitError("unknown result <id> for specialization constant ") 105 << constituentName; 106 } 107 108 operands.push_back(constituentID); 109 } 110 111 (void)encodeInstructionInto(typesGlobalValues, 112 spirv::Opcode::OpSpecConstantComposite, operands); 113 specConstIDMap[op.sym_name()] = resultID; 114 115 return processName(resultID, op.sym_name()); 116 } 117 118 LogicalResult 119 Serializer::processSpecConstantOperationOp(spirv::SpecConstantOperationOp op) { 120 uint32_t typeID = 0; 121 if (failed(processType(op.getLoc(), op.getType(), typeID))) { 122 return failure(); 123 } 124 125 auto resultID = getNextID(); 126 127 SmallVector<uint32_t, 8> operands; 128 operands.push_back(typeID); 129 operands.push_back(resultID); 130 131 Block &block = op.getRegion().getBlocks().front(); 132 Operation &enclosedOp = block.getOperations().front(); 133 134 std::string enclosedOpName; 135 llvm::raw_string_ostream rss(enclosedOpName); 136 rss << "Op" << enclosedOp.getName().stripDialect(); 137 auto enclosedOpcode = spirv::symbolizeOpcode(rss.str()); 138 139 if (!enclosedOpcode) { 140 op.emitError("Couldn't find op code for op ") 141 << enclosedOp.getName().getStringRef(); 142 return failure(); 143 } 144 145 operands.push_back(static_cast<uint32_t>(enclosedOpcode.getValue())); 146 147 // Append operands to the enclosed op to the list of operands. 148 for (Value operand : enclosedOp.getOperands()) { 149 uint32_t id = getValueID(operand); 150 assert(id && "use before def!"); 151 operands.push_back(id); 152 } 153 154 (void)encodeInstructionInto(typesGlobalValues, 155 spirv::Opcode::OpSpecConstantOp, operands); 156 valueIDMap[op.getResult()] = resultID; 157 158 return success(); 159 } 160 161 LogicalResult Serializer::processUndefOp(spirv::UndefOp op) { 162 auto undefType = op.getType(); 163 auto &id = undefValIDMap[undefType]; 164 if (!id) { 165 id = getNextID(); 166 uint32_t typeID = 0; 167 if (failed(processType(op.getLoc(), undefType, typeID)) || 168 failed(encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpUndef, 169 {typeID, id}))) { 170 return failure(); 171 } 172 } 173 valueIDMap[op.getResult()] = id; 174 return success(); 175 } 176 177 LogicalResult Serializer::processFuncOp(spirv::FuncOp op) { 178 LLVM_DEBUG(llvm::dbgs() << "-- start function '" << op.getName() << "' --\n"); 179 assert(functionHeader.empty() && functionBody.empty()); 180 181 uint32_t fnTypeID = 0; 182 // Generate type of the function. 183 (void)processType(op.getLoc(), op.getType(), fnTypeID); 184 185 // Add the function definition. 186 SmallVector<uint32_t, 4> operands; 187 uint32_t resTypeID = 0; 188 auto resultTypes = op.getType().getResults(); 189 if (resultTypes.size() > 1) { 190 return op.emitError("cannot serialize function with multiple return types"); 191 } 192 if (failed(processType(op.getLoc(), 193 (resultTypes.empty() ? getVoidType() : resultTypes[0]), 194 resTypeID))) { 195 return failure(); 196 } 197 operands.push_back(resTypeID); 198 auto funcID = getOrCreateFunctionID(op.getName()); 199 operands.push_back(funcID); 200 operands.push_back(static_cast<uint32_t>(op.function_control())); 201 operands.push_back(fnTypeID); 202 (void)encodeInstructionInto(functionHeader, spirv::Opcode::OpFunction, 203 operands); 204 205 // Add function name. 206 if (failed(processName(funcID, op.getName()))) { 207 return failure(); 208 } 209 210 // Declare the parameters. 211 for (auto arg : op.getArguments()) { 212 uint32_t argTypeID = 0; 213 if (failed(processType(op.getLoc(), arg.getType(), argTypeID))) { 214 return failure(); 215 } 216 auto argValueID = getNextID(); 217 valueIDMap[arg] = argValueID; 218 (void)encodeInstructionInto(functionHeader, 219 spirv::Opcode::OpFunctionParameter, 220 {argTypeID, argValueID}); 221 } 222 223 // Process the body. 224 if (op.isExternal()) { 225 return op.emitError("external function is unhandled"); 226 } 227 228 // Some instructions (e.g., OpVariable) in a function must be in the first 229 // block in the function. These instructions will be put in functionHeader. 230 // Thus, we put the label in functionHeader first, and omit it from the first 231 // block. 232 (void)encodeInstructionInto(functionHeader, spirv::Opcode::OpLabel, 233 {getOrCreateBlockID(&op.front())}); 234 (void)processBlock(&op.front(), /*omitLabel=*/true); 235 if (failed(visitInPrettyBlockOrder( 236 &op.front(), [&](Block *block) { return processBlock(block); }, 237 /*skipHeader=*/true))) { 238 return failure(); 239 } 240 241 // There might be OpPhi instructions who have value references needing to fix. 242 for (auto deferredValue : deferredPhiValues) { 243 Value value = deferredValue.first; 244 uint32_t id = getValueID(value); 245 LLVM_DEBUG(llvm::dbgs() << "[phi] fix reference of value " << value 246 << " to id = " << id << '\n'); 247 assert(id && "OpPhi references undefined value!"); 248 for (size_t offset : deferredValue.second) 249 functionBody[offset] = id; 250 } 251 deferredPhiValues.clear(); 252 253 LLVM_DEBUG(llvm::dbgs() << "-- completed function '" << op.getName() 254 << "' --\n"); 255 // Insert OpFunctionEnd. 256 if (failed(encodeInstructionInto(functionBody, spirv::Opcode::OpFunctionEnd, 257 {}))) { 258 return failure(); 259 } 260 261 functions.append(functionHeader.begin(), functionHeader.end()); 262 functions.append(functionBody.begin(), functionBody.end()); 263 functionHeader.clear(); 264 functionBody.clear(); 265 266 return success(); 267 } 268 269 LogicalResult Serializer::processVariableOp(spirv::VariableOp op) { 270 SmallVector<uint32_t, 4> operands; 271 SmallVector<StringRef, 2> elidedAttrs; 272 uint32_t resultID = 0; 273 uint32_t resultTypeID = 0; 274 if (failed(processType(op.getLoc(), op.getType(), resultTypeID))) { 275 return failure(); 276 } 277 operands.push_back(resultTypeID); 278 resultID = getNextID(); 279 valueIDMap[op.getResult()] = resultID; 280 operands.push_back(resultID); 281 auto attr = op->getAttr(spirv::attributeName<spirv::StorageClass>()); 282 if (attr) { 283 operands.push_back(static_cast<uint32_t>( 284 attr.cast<IntegerAttr>().getValue().getZExtValue())); 285 } 286 elidedAttrs.push_back(spirv::attributeName<spirv::StorageClass>()); 287 for (auto arg : op.getODSOperands(0)) { 288 auto argID = getValueID(arg); 289 if (!argID) { 290 return emitError(op.getLoc(), "operand 0 has a use before def"); 291 } 292 operands.push_back(argID); 293 } 294 (void)emitDebugLine(functionHeader, op.getLoc()); 295 (void)encodeInstructionInto(functionHeader, spirv::Opcode::OpVariable, 296 operands); 297 for (auto attr : op->getAttrs()) { 298 if (llvm::any_of(elidedAttrs, [&](StringRef elided) { 299 return attr.getName() == elided; 300 })) { 301 continue; 302 } 303 if (failed(processDecoration(op.getLoc(), resultID, attr))) { 304 return failure(); 305 } 306 } 307 return success(); 308 } 309 310 LogicalResult 311 Serializer::processGlobalVariableOp(spirv::GlobalVariableOp varOp) { 312 // Get TypeID. 313 uint32_t resultTypeID = 0; 314 SmallVector<StringRef, 4> elidedAttrs; 315 if (failed(processType(varOp.getLoc(), varOp.type(), resultTypeID))) { 316 return failure(); 317 } 318 319 if (isInterfaceStructPtrType(varOp.type())) { 320 auto structType = varOp.type() 321 .cast<spirv::PointerType>() 322 .getPointeeType() 323 .cast<spirv::StructType>(); 324 if (failed( 325 emitDecoration(getTypeID(structType), spirv::Decoration::Block))) { 326 return varOp.emitError("cannot decorate ") 327 << structType << " with Block decoration"; 328 } 329 } 330 331 elidedAttrs.push_back("type"); 332 SmallVector<uint32_t, 4> operands; 333 operands.push_back(resultTypeID); 334 auto resultID = getNextID(); 335 336 // Encode the name. 337 auto varName = varOp.sym_name(); 338 elidedAttrs.push_back(SymbolTable::getSymbolAttrName()); 339 if (failed(processName(resultID, varName))) { 340 return failure(); 341 } 342 globalVarIDMap[varName] = resultID; 343 operands.push_back(resultID); 344 345 // Encode StorageClass. 346 operands.push_back(static_cast<uint32_t>(varOp.storageClass())); 347 348 // Encode initialization. 349 if (auto initializer = varOp.initializer()) { 350 auto initializerID = getVariableID(initializer.getValue()); 351 if (!initializerID) { 352 return emitError(varOp.getLoc(), 353 "invalid usage of undefined variable as initializer"); 354 } 355 operands.push_back(initializerID); 356 elidedAttrs.push_back("initializer"); 357 } 358 359 (void)emitDebugLine(typesGlobalValues, varOp.getLoc()); 360 if (failed(encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpVariable, 361 operands))) { 362 elidedAttrs.push_back("initializer"); 363 return failure(); 364 } 365 366 // Encode decorations. 367 for (auto attr : varOp->getAttrs()) { 368 if (llvm::any_of(elidedAttrs, [&](StringRef elided) { 369 return attr.getName() == elided; 370 })) { 371 continue; 372 } 373 if (failed(processDecoration(varOp.getLoc(), resultID, attr))) { 374 return failure(); 375 } 376 } 377 return success(); 378 } 379 380 LogicalResult Serializer::processSelectionOp(spirv::SelectionOp selectionOp) { 381 // Assign <id>s to all blocks so that branches inside the SelectionOp can 382 // resolve properly. 383 auto &body = selectionOp.body(); 384 for (Block &block : body) 385 getOrCreateBlockID(&block); 386 387 auto *headerBlock = selectionOp.getHeaderBlock(); 388 auto *mergeBlock = selectionOp.getMergeBlock(); 389 auto mergeID = getBlockID(mergeBlock); 390 auto loc = selectionOp.getLoc(); 391 392 // Emit the selection header block, which dominates all other blocks, first. 393 // We need to emit an OpSelectionMerge instruction before the selection header 394 // block's terminator. 395 auto emitSelectionMerge = [&]() { 396 (void)emitDebugLine(functionBody, loc); 397 lastProcessedWasMergeInst = true; 398 (void)encodeInstructionInto( 399 functionBody, spirv::Opcode::OpSelectionMerge, 400 {mergeID, static_cast<uint32_t>(selectionOp.selection_control())}); 401 }; 402 // For structured selection, we cannot have blocks in the selection construct 403 // branching to the selection header block. Entering the selection (and 404 // reaching the selection header) must be from the block containing the 405 // spv.mlir.selection op. If there are ops ahead of the spv.mlir.selection op 406 // in the block, we can "merge" them into the selection header. So here we 407 // don't need to emit a separate block; just continue with the existing block. 408 if (failed(processBlock(headerBlock, /*omitLabel=*/true, emitSelectionMerge))) 409 return failure(); 410 411 // Process all blocks with a depth-first visitor starting from the header 412 // block. The selection header block and merge block are skipped by this 413 // visitor. 414 if (failed(visitInPrettyBlockOrder( 415 headerBlock, [&](Block *block) { return processBlock(block); }, 416 /*skipHeader=*/true, /*skipBlocks=*/{mergeBlock}))) 417 return failure(); 418 419 // There is nothing to do for the merge block in the selection, which just 420 // contains a spv.mlir.merge op, itself. But we need to have an OpLabel 421 // instruction to start a new SPIR-V block for ops following this SelectionOp. 422 // The block should use the <id> for the merge block. 423 return encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {mergeID}); 424 } 425 426 LogicalResult Serializer::processLoopOp(spirv::LoopOp loopOp) { 427 // Assign <id>s to all blocks so that branches inside the LoopOp can resolve 428 // properly. We don't need to assign for the entry block, which is just for 429 // satisfying MLIR region's structural requirement. 430 auto &body = loopOp.body(); 431 for (Block &block : 432 llvm::make_range(std::next(body.begin(), 1), body.end())) { 433 getOrCreateBlockID(&block); 434 } 435 auto *headerBlock = loopOp.getHeaderBlock(); 436 auto *continueBlock = loopOp.getContinueBlock(); 437 auto *mergeBlock = loopOp.getMergeBlock(); 438 auto headerID = getBlockID(headerBlock); 439 auto continueID = getBlockID(continueBlock); 440 auto mergeID = getBlockID(mergeBlock); 441 auto loc = loopOp.getLoc(); 442 443 // This LoopOp is in some MLIR block with preceding and following ops. In the 444 // binary format, it should reside in separate SPIR-V blocks from its 445 // preceding and following ops. So we need to emit unconditional branches to 446 // jump to this LoopOp's SPIR-V blocks and jumping back to the normal flow 447 // afterwards. 448 (void)encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, 449 {headerID}); 450 451 // LoopOp's entry block is just there for satisfying MLIR's structural 452 // requirements so we omit it and start serialization from the loop header 453 // block. 454 455 // Emit the loop header block, which dominates all other blocks, first. We 456 // need to emit an OpLoopMerge instruction before the loop header block's 457 // terminator. 458 auto emitLoopMerge = [&]() { 459 (void)emitDebugLine(functionBody, loc); 460 lastProcessedWasMergeInst = true; 461 (void)encodeInstructionInto( 462 functionBody, spirv::Opcode::OpLoopMerge, 463 {mergeID, continueID, static_cast<uint32_t>(loopOp.loop_control())}); 464 }; 465 if (failed(processBlock(headerBlock, /*omitLabel=*/false, emitLoopMerge))) 466 return failure(); 467 468 // Process all blocks with a depth-first visitor starting from the header 469 // block. The loop header block, loop continue block, and loop merge block are 470 // skipped by this visitor and handled later in this function. 471 if (failed(visitInPrettyBlockOrder( 472 headerBlock, [&](Block *block) { return processBlock(block); }, 473 /*skipHeader=*/true, /*skipBlocks=*/{continueBlock, mergeBlock}))) 474 return failure(); 475 476 // We have handled all other blocks. Now get to the loop continue block. 477 if (failed(processBlock(continueBlock))) 478 return failure(); 479 480 // There is nothing to do for the merge block in the loop, which just contains 481 // a spv.mlir.merge op, itself. But we need to have an OpLabel instruction to 482 // start a new SPIR-V block for ops following this LoopOp. The block should 483 // use the <id> for the merge block. 484 return encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {mergeID}); 485 } 486 487 LogicalResult Serializer::processBranchConditionalOp( 488 spirv::BranchConditionalOp condBranchOp) { 489 auto conditionID = getValueID(condBranchOp.condition()); 490 auto trueLabelID = getOrCreateBlockID(condBranchOp.getTrueBlock()); 491 auto falseLabelID = getOrCreateBlockID(condBranchOp.getFalseBlock()); 492 SmallVector<uint32_t, 5> arguments{conditionID, trueLabelID, falseLabelID}; 493 494 if (auto weights = condBranchOp.branch_weights()) { 495 for (auto val : weights->getValue()) 496 arguments.push_back(val.cast<IntegerAttr>().getInt()); 497 } 498 499 (void)emitDebugLine(functionBody, condBranchOp.getLoc()); 500 return encodeInstructionInto(functionBody, spirv::Opcode::OpBranchConditional, 501 arguments); 502 } 503 504 LogicalResult Serializer::processBranchOp(spirv::BranchOp branchOp) { 505 (void)emitDebugLine(functionBody, branchOp.getLoc()); 506 return encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, 507 {getOrCreateBlockID(branchOp.getTarget())}); 508 } 509 510 LogicalResult Serializer::processAddressOfOp(spirv::AddressOfOp addressOfOp) { 511 auto varName = addressOfOp.variable(); 512 auto variableID = getVariableID(varName); 513 if (!variableID) { 514 return addressOfOp.emitError("unknown result <id> for variable ") 515 << varName; 516 } 517 valueIDMap[addressOfOp.pointer()] = variableID; 518 return success(); 519 } 520 521 LogicalResult 522 Serializer::processReferenceOfOp(spirv::ReferenceOfOp referenceOfOp) { 523 auto constName = referenceOfOp.spec_const(); 524 auto constID = getSpecConstID(constName); 525 if (!constID) { 526 return referenceOfOp.emitError( 527 "unknown result <id> for specialization constant ") 528 << constName; 529 } 530 valueIDMap[referenceOfOp.reference()] = constID; 531 return success(); 532 } 533 534 template <> 535 LogicalResult 536 Serializer::processOp<spirv::EntryPointOp>(spirv::EntryPointOp op) { 537 SmallVector<uint32_t, 4> operands; 538 // Add the ExecutionModel. 539 operands.push_back(static_cast<uint32_t>(op.execution_model())); 540 // Add the function <id>. 541 auto funcID = getFunctionID(op.fn()); 542 if (!funcID) { 543 return op.emitError("missing <id> for function ") 544 << op.fn() 545 << "; function needs to be defined before spv.EntryPoint is " 546 "serialized"; 547 } 548 operands.push_back(funcID); 549 // Add the name of the function. 550 (void)spirv::encodeStringLiteralInto(operands, op.fn()); 551 552 // Add the interface values. 553 if (auto interface = op.interface()) { 554 for (auto var : interface.getValue()) { 555 auto id = getVariableID(var.cast<FlatSymbolRefAttr>().getValue()); 556 if (!id) { 557 return op.emitError("referencing undefined global variable." 558 "spv.EntryPoint is at the end of spv.module. All " 559 "referenced variables should already be defined"); 560 } 561 operands.push_back(id); 562 } 563 } 564 return encodeInstructionInto(entryPoints, spirv::Opcode::OpEntryPoint, 565 operands); 566 } 567 568 template <> 569 LogicalResult 570 Serializer::processOp<spirv::ControlBarrierOp>(spirv::ControlBarrierOp op) { 571 StringRef argNames[] = {"execution_scope", "memory_scope", 572 "memory_semantics"}; 573 SmallVector<uint32_t, 3> operands; 574 575 for (auto argName : argNames) { 576 auto argIntAttr = op->getAttrOfType<IntegerAttr>(argName); 577 auto operand = prepareConstantInt(op.getLoc(), argIntAttr); 578 if (!operand) { 579 return failure(); 580 } 581 operands.push_back(operand); 582 } 583 584 return encodeInstructionInto(functionBody, spirv::Opcode::OpControlBarrier, 585 operands); 586 } 587 588 template <> 589 LogicalResult 590 Serializer::processOp<spirv::ExecutionModeOp>(spirv::ExecutionModeOp op) { 591 SmallVector<uint32_t, 4> operands; 592 // Add the function <id>. 593 auto funcID = getFunctionID(op.fn()); 594 if (!funcID) { 595 return op.emitError("missing <id> for function ") 596 << op.fn() 597 << "; function needs to be serialized before ExecutionModeOp is " 598 "serialized"; 599 } 600 operands.push_back(funcID); 601 // Add the ExecutionMode. 602 operands.push_back(static_cast<uint32_t>(op.execution_mode())); 603 604 // Serialize values if any. 605 auto values = op.values(); 606 if (values) { 607 for (auto &intVal : values.getValue()) { 608 operands.push_back(static_cast<uint32_t>( 609 intVal.cast<IntegerAttr>().getValue().getZExtValue())); 610 } 611 } 612 return encodeInstructionInto(executionModes, spirv::Opcode::OpExecutionMode, 613 operands); 614 } 615 616 template <> 617 LogicalResult 618 Serializer::processOp<spirv::MemoryBarrierOp>(spirv::MemoryBarrierOp op) { 619 StringRef argNames[] = {"memory_scope", "memory_semantics"}; 620 SmallVector<uint32_t, 2> operands; 621 622 for (auto argName : argNames) { 623 auto argIntAttr = op->getAttrOfType<IntegerAttr>(argName); 624 auto operand = prepareConstantInt(op.getLoc(), argIntAttr); 625 if (!operand) { 626 return failure(); 627 } 628 operands.push_back(operand); 629 } 630 631 return encodeInstructionInto(functionBody, spirv::Opcode::OpMemoryBarrier, 632 operands); 633 } 634 635 template <> 636 LogicalResult 637 Serializer::processOp<spirv::FunctionCallOp>(spirv::FunctionCallOp op) { 638 auto funcName = op.callee(); 639 uint32_t resTypeID = 0; 640 641 Type resultTy = op.getNumResults() ? *op.result_type_begin() : getVoidType(); 642 if (failed(processType(op.getLoc(), resultTy, resTypeID))) 643 return failure(); 644 645 auto funcID = getOrCreateFunctionID(funcName); 646 auto funcCallID = getNextID(); 647 SmallVector<uint32_t, 8> operands{resTypeID, funcCallID, funcID}; 648 649 for (auto value : op.arguments()) { 650 auto valueID = getValueID(value); 651 assert(valueID && "cannot find a value for spv.FunctionCall"); 652 operands.push_back(valueID); 653 } 654 655 if (!resultTy.isa<NoneType>()) 656 valueIDMap[op.getResult(0)] = funcCallID; 657 658 return encodeInstructionInto(functionBody, spirv::Opcode::OpFunctionCall, 659 operands); 660 } 661 662 template <> 663 LogicalResult 664 Serializer::processOp<spirv::CopyMemoryOp>(spirv::CopyMemoryOp op) { 665 SmallVector<uint32_t, 4> operands; 666 SmallVector<StringRef, 2> elidedAttrs; 667 668 for (Value operand : op->getOperands()) { 669 auto id = getValueID(operand); 670 assert(id && "use before def!"); 671 operands.push_back(id); 672 } 673 674 if (auto attr = op->getAttr("memory_access")) { 675 operands.push_back(static_cast<uint32_t>( 676 attr.cast<IntegerAttr>().getValue().getZExtValue())); 677 } 678 679 elidedAttrs.push_back("memory_access"); 680 681 if (auto attr = op->getAttr("alignment")) { 682 operands.push_back(static_cast<uint32_t>( 683 attr.cast<IntegerAttr>().getValue().getZExtValue())); 684 } 685 686 elidedAttrs.push_back("alignment"); 687 688 if (auto attr = op->getAttr("source_memory_access")) { 689 operands.push_back(static_cast<uint32_t>( 690 attr.cast<IntegerAttr>().getValue().getZExtValue())); 691 } 692 693 elidedAttrs.push_back("source_memory_access"); 694 695 if (auto attr = op->getAttr("source_alignment")) { 696 operands.push_back(static_cast<uint32_t>( 697 attr.cast<IntegerAttr>().getValue().getZExtValue())); 698 } 699 700 elidedAttrs.push_back("source_alignment"); 701 (void)emitDebugLine(functionBody, op.getLoc()); 702 (void)encodeInstructionInto(functionBody, spirv::Opcode::OpCopyMemory, 703 operands); 704 705 return success(); 706 } 707 708 // Pull in auto-generated Serializer::dispatchToAutogenSerialization() and 709 // various Serializer::processOp<...>() specializations. 710 #define GET_SERIALIZATION_FNS 711 #include "mlir/Dialect/SPIRV/IR/SPIRVSerialization.inc" 712 713 } // namespace spirv 714 } // namespace mlir 715