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, 299 [&](StringRef elided) { return attr.first == elided; })) { 300 continue; 301 } 302 if (failed(processDecoration(op.getLoc(), resultID, attr))) { 303 return failure(); 304 } 305 } 306 return success(); 307 } 308 309 LogicalResult 310 Serializer::processGlobalVariableOp(spirv::GlobalVariableOp varOp) { 311 // Get TypeID. 312 uint32_t resultTypeID = 0; 313 SmallVector<StringRef, 4> elidedAttrs; 314 if (failed(processType(varOp.getLoc(), varOp.type(), resultTypeID))) { 315 return failure(); 316 } 317 318 if (isInterfaceStructPtrType(varOp.type())) { 319 auto structType = varOp.type() 320 .cast<spirv::PointerType>() 321 .getPointeeType() 322 .cast<spirv::StructType>(); 323 if (failed( 324 emitDecoration(getTypeID(structType), spirv::Decoration::Block))) { 325 return varOp.emitError("cannot decorate ") 326 << structType << " with Block decoration"; 327 } 328 } 329 330 elidedAttrs.push_back("type"); 331 SmallVector<uint32_t, 4> operands; 332 operands.push_back(resultTypeID); 333 auto resultID = getNextID(); 334 335 // Encode the name. 336 auto varName = varOp.sym_name(); 337 elidedAttrs.push_back(SymbolTable::getSymbolAttrName()); 338 if (failed(processName(resultID, varName))) { 339 return failure(); 340 } 341 globalVarIDMap[varName] = resultID; 342 operands.push_back(resultID); 343 344 // Encode StorageClass. 345 operands.push_back(static_cast<uint32_t>(varOp.storageClass())); 346 347 // Encode initialization. 348 if (auto initializer = varOp.initializer()) { 349 auto initializerID = getVariableID(initializer.getValue()); 350 if (!initializerID) { 351 return emitError(varOp.getLoc(), 352 "invalid usage of undefined variable as initializer"); 353 } 354 operands.push_back(initializerID); 355 elidedAttrs.push_back("initializer"); 356 } 357 358 (void)emitDebugLine(typesGlobalValues, varOp.getLoc()); 359 if (failed(encodeInstructionInto(typesGlobalValues, spirv::Opcode::OpVariable, 360 operands))) { 361 elidedAttrs.push_back("initializer"); 362 return failure(); 363 } 364 365 // Encode decorations. 366 for (auto attr : varOp->getAttrs()) { 367 if (llvm::any_of(elidedAttrs, 368 [&](StringRef elided) { return attr.first == elided; })) { 369 continue; 370 } 371 if (failed(processDecoration(varOp.getLoc(), resultID, attr))) { 372 return failure(); 373 } 374 } 375 return success(); 376 } 377 378 LogicalResult Serializer::processSelectionOp(spirv::SelectionOp selectionOp) { 379 // Assign <id>s to all blocks so that branches inside the SelectionOp can 380 // resolve properly. 381 auto &body = selectionOp.body(); 382 for (Block &block : body) 383 getOrCreateBlockID(&block); 384 385 auto *headerBlock = selectionOp.getHeaderBlock(); 386 auto *mergeBlock = selectionOp.getMergeBlock(); 387 auto mergeID = getBlockID(mergeBlock); 388 auto loc = selectionOp.getLoc(); 389 390 // Emit the selection header block, which dominates all other blocks, first. 391 // We need to emit an OpSelectionMerge instruction before the selection header 392 // block's terminator. 393 auto emitSelectionMerge = [&]() { 394 (void)emitDebugLine(functionBody, loc); 395 lastProcessedWasMergeInst = true; 396 (void)encodeInstructionInto( 397 functionBody, spirv::Opcode::OpSelectionMerge, 398 {mergeID, static_cast<uint32_t>(selectionOp.selection_control())}); 399 }; 400 // For structured selection, we cannot have blocks in the selection construct 401 // branching to the selection header block. Entering the selection (and 402 // reaching the selection header) must be from the block containing the 403 // spv.mlir.selection op. If there are ops ahead of the spv.mlir.selection op 404 // in the block, we can "merge" them into the selection header. So here we 405 // don't need to emit a separate block; just continue with the existing block. 406 if (failed(processBlock(headerBlock, /*omitLabel=*/true, emitSelectionMerge))) 407 return failure(); 408 409 // Process all blocks with a depth-first visitor starting from the header 410 // block. The selection header block and merge block are skipped by this 411 // visitor. 412 if (failed(visitInPrettyBlockOrder( 413 headerBlock, [&](Block *block) { return processBlock(block); }, 414 /*skipHeader=*/true, /*skipBlocks=*/{mergeBlock}))) 415 return failure(); 416 417 // There is nothing to do for the merge block in the selection, which just 418 // contains a spv.mlir.merge op, itself. But we need to have an OpLabel 419 // instruction to start a new SPIR-V block for ops following this SelectionOp. 420 // The block should use the <id> for the merge block. 421 return encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {mergeID}); 422 } 423 424 LogicalResult Serializer::processLoopOp(spirv::LoopOp loopOp) { 425 // Assign <id>s to all blocks so that branches inside the LoopOp can resolve 426 // properly. We don't need to assign for the entry block, which is just for 427 // satisfying MLIR region's structural requirement. 428 auto &body = loopOp.body(); 429 for (Block &block : 430 llvm::make_range(std::next(body.begin(), 1), body.end())) { 431 getOrCreateBlockID(&block); 432 } 433 auto *headerBlock = loopOp.getHeaderBlock(); 434 auto *continueBlock = loopOp.getContinueBlock(); 435 auto *mergeBlock = loopOp.getMergeBlock(); 436 auto headerID = getBlockID(headerBlock); 437 auto continueID = getBlockID(continueBlock); 438 auto mergeID = getBlockID(mergeBlock); 439 auto loc = loopOp.getLoc(); 440 441 // This LoopOp is in some MLIR block with preceding and following ops. In the 442 // binary format, it should reside in separate SPIR-V blocks from its 443 // preceding and following ops. So we need to emit unconditional branches to 444 // jump to this LoopOp's SPIR-V blocks and jumping back to the normal flow 445 // afterwards. 446 (void)encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, 447 {headerID}); 448 449 // LoopOp's entry block is just there for satisfying MLIR's structural 450 // requirements so we omit it and start serialization from the loop header 451 // block. 452 453 // Emit the loop header block, which dominates all other blocks, first. We 454 // need to emit an OpLoopMerge instruction before the loop header block's 455 // terminator. 456 auto emitLoopMerge = [&]() { 457 (void)emitDebugLine(functionBody, loc); 458 lastProcessedWasMergeInst = true; 459 (void)encodeInstructionInto( 460 functionBody, spirv::Opcode::OpLoopMerge, 461 {mergeID, continueID, static_cast<uint32_t>(loopOp.loop_control())}); 462 }; 463 if (failed(processBlock(headerBlock, /*omitLabel=*/false, emitLoopMerge))) 464 return failure(); 465 466 // Process all blocks with a depth-first visitor starting from the header 467 // block. The loop header block, loop continue block, and loop merge block are 468 // skipped by this visitor and handled later in this function. 469 if (failed(visitInPrettyBlockOrder( 470 headerBlock, [&](Block *block) { return processBlock(block); }, 471 /*skipHeader=*/true, /*skipBlocks=*/{continueBlock, mergeBlock}))) 472 return failure(); 473 474 // We have handled all other blocks. Now get to the loop continue block. 475 if (failed(processBlock(continueBlock))) 476 return failure(); 477 478 // There is nothing to do for the merge block in the loop, which just contains 479 // a spv.mlir.merge op, itself. But we need to have an OpLabel instruction to 480 // start a new SPIR-V block for ops following this LoopOp. The block should 481 // use the <id> for the merge block. 482 return encodeInstructionInto(functionBody, spirv::Opcode::OpLabel, {mergeID}); 483 } 484 485 LogicalResult Serializer::processBranchConditionalOp( 486 spirv::BranchConditionalOp condBranchOp) { 487 auto conditionID = getValueID(condBranchOp.condition()); 488 auto trueLabelID = getOrCreateBlockID(condBranchOp.getTrueBlock()); 489 auto falseLabelID = getOrCreateBlockID(condBranchOp.getFalseBlock()); 490 SmallVector<uint32_t, 5> arguments{conditionID, trueLabelID, falseLabelID}; 491 492 if (auto weights = condBranchOp.branch_weights()) { 493 for (auto val : weights->getValue()) 494 arguments.push_back(val.cast<IntegerAttr>().getInt()); 495 } 496 497 (void)emitDebugLine(functionBody, condBranchOp.getLoc()); 498 return encodeInstructionInto(functionBody, spirv::Opcode::OpBranchConditional, 499 arguments); 500 } 501 502 LogicalResult Serializer::processBranchOp(spirv::BranchOp branchOp) { 503 (void)emitDebugLine(functionBody, branchOp.getLoc()); 504 return encodeInstructionInto(functionBody, spirv::Opcode::OpBranch, 505 {getOrCreateBlockID(branchOp.getTarget())}); 506 } 507 508 LogicalResult Serializer::processAddressOfOp(spirv::AddressOfOp addressOfOp) { 509 auto varName = addressOfOp.variable(); 510 auto variableID = getVariableID(varName); 511 if (!variableID) { 512 return addressOfOp.emitError("unknown result <id> for variable ") 513 << varName; 514 } 515 valueIDMap[addressOfOp.pointer()] = variableID; 516 return success(); 517 } 518 519 LogicalResult 520 Serializer::processReferenceOfOp(spirv::ReferenceOfOp referenceOfOp) { 521 auto constName = referenceOfOp.spec_const(); 522 auto constID = getSpecConstID(constName); 523 if (!constID) { 524 return referenceOfOp.emitError( 525 "unknown result <id> for specialization constant ") 526 << constName; 527 } 528 valueIDMap[referenceOfOp.reference()] = constID; 529 return success(); 530 } 531 532 template <> 533 LogicalResult 534 Serializer::processOp<spirv::EntryPointOp>(spirv::EntryPointOp op) { 535 SmallVector<uint32_t, 4> operands; 536 // Add the ExecutionModel. 537 operands.push_back(static_cast<uint32_t>(op.execution_model())); 538 // Add the function <id>. 539 auto funcID = getFunctionID(op.fn()); 540 if (!funcID) { 541 return op.emitError("missing <id> for function ") 542 << op.fn() 543 << "; function needs to be defined before spv.EntryPoint is " 544 "serialized"; 545 } 546 operands.push_back(funcID); 547 // Add the name of the function. 548 (void)spirv::encodeStringLiteralInto(operands, op.fn()); 549 550 // Add the interface values. 551 if (auto interface = op.interface()) { 552 for (auto var : interface.getValue()) { 553 auto id = getVariableID(var.cast<FlatSymbolRefAttr>().getValue()); 554 if (!id) { 555 return op.emitError("referencing undefined global variable." 556 "spv.EntryPoint is at the end of spv.module. All " 557 "referenced variables should already be defined"); 558 } 559 operands.push_back(id); 560 } 561 } 562 return encodeInstructionInto(entryPoints, spirv::Opcode::OpEntryPoint, 563 operands); 564 } 565 566 template <> 567 LogicalResult 568 Serializer::processOp<spirv::ControlBarrierOp>(spirv::ControlBarrierOp op) { 569 StringRef argNames[] = {"execution_scope", "memory_scope", 570 "memory_semantics"}; 571 SmallVector<uint32_t, 3> operands; 572 573 for (auto argName : argNames) { 574 auto argIntAttr = op->getAttrOfType<IntegerAttr>(argName); 575 auto operand = prepareConstantInt(op.getLoc(), argIntAttr); 576 if (!operand) { 577 return failure(); 578 } 579 operands.push_back(operand); 580 } 581 582 return encodeInstructionInto(functionBody, spirv::Opcode::OpControlBarrier, 583 operands); 584 } 585 586 template <> 587 LogicalResult 588 Serializer::processOp<spirv::ExecutionModeOp>(spirv::ExecutionModeOp op) { 589 SmallVector<uint32_t, 4> operands; 590 // Add the function <id>. 591 auto funcID = getFunctionID(op.fn()); 592 if (!funcID) { 593 return op.emitError("missing <id> for function ") 594 << op.fn() 595 << "; function needs to be serialized before ExecutionModeOp is " 596 "serialized"; 597 } 598 operands.push_back(funcID); 599 // Add the ExecutionMode. 600 operands.push_back(static_cast<uint32_t>(op.execution_mode())); 601 602 // Serialize values if any. 603 auto values = op.values(); 604 if (values) { 605 for (auto &intVal : values.getValue()) { 606 operands.push_back(static_cast<uint32_t>( 607 intVal.cast<IntegerAttr>().getValue().getZExtValue())); 608 } 609 } 610 return encodeInstructionInto(executionModes, spirv::Opcode::OpExecutionMode, 611 operands); 612 } 613 614 template <> 615 LogicalResult 616 Serializer::processOp<spirv::MemoryBarrierOp>(spirv::MemoryBarrierOp op) { 617 StringRef argNames[] = {"memory_scope", "memory_semantics"}; 618 SmallVector<uint32_t, 2> operands; 619 620 for (auto argName : argNames) { 621 auto argIntAttr = op->getAttrOfType<IntegerAttr>(argName); 622 auto operand = prepareConstantInt(op.getLoc(), argIntAttr); 623 if (!operand) { 624 return failure(); 625 } 626 operands.push_back(operand); 627 } 628 629 return encodeInstructionInto(functionBody, spirv::Opcode::OpMemoryBarrier, 630 operands); 631 } 632 633 template <> 634 LogicalResult 635 Serializer::processOp<spirv::FunctionCallOp>(spirv::FunctionCallOp op) { 636 auto funcName = op.callee(); 637 uint32_t resTypeID = 0; 638 639 Type resultTy = op.getNumResults() ? *op.result_type_begin() : getVoidType(); 640 if (failed(processType(op.getLoc(), resultTy, resTypeID))) 641 return failure(); 642 643 auto funcID = getOrCreateFunctionID(funcName); 644 auto funcCallID = getNextID(); 645 SmallVector<uint32_t, 8> operands{resTypeID, funcCallID, funcID}; 646 647 for (auto value : op.arguments()) { 648 auto valueID = getValueID(value); 649 assert(valueID && "cannot find a value for spv.FunctionCall"); 650 operands.push_back(valueID); 651 } 652 653 if (!resultTy.isa<NoneType>()) 654 valueIDMap[op.getResult(0)] = funcCallID; 655 656 return encodeInstructionInto(functionBody, spirv::Opcode::OpFunctionCall, 657 operands); 658 } 659 660 template <> 661 LogicalResult 662 Serializer::processOp<spirv::CopyMemoryOp>(spirv::CopyMemoryOp op) { 663 SmallVector<uint32_t, 4> operands; 664 SmallVector<StringRef, 2> elidedAttrs; 665 666 for (Value operand : op->getOperands()) { 667 auto id = getValueID(operand); 668 assert(id && "use before def!"); 669 operands.push_back(id); 670 } 671 672 if (auto attr = op->getAttr("memory_access")) { 673 operands.push_back(static_cast<uint32_t>( 674 attr.cast<IntegerAttr>().getValue().getZExtValue())); 675 } 676 677 elidedAttrs.push_back("memory_access"); 678 679 if (auto attr = op->getAttr("alignment")) { 680 operands.push_back(static_cast<uint32_t>( 681 attr.cast<IntegerAttr>().getValue().getZExtValue())); 682 } 683 684 elidedAttrs.push_back("alignment"); 685 686 if (auto attr = op->getAttr("source_memory_access")) { 687 operands.push_back(static_cast<uint32_t>( 688 attr.cast<IntegerAttr>().getValue().getZExtValue())); 689 } 690 691 elidedAttrs.push_back("source_memory_access"); 692 693 if (auto attr = op->getAttr("source_alignment")) { 694 operands.push_back(static_cast<uint32_t>( 695 attr.cast<IntegerAttr>().getValue().getZExtValue())); 696 } 697 698 elidedAttrs.push_back("source_alignment"); 699 (void)emitDebugLine(functionBody, op.getLoc()); 700 (void)encodeInstructionInto(functionBody, spirv::Opcode::OpCopyMemory, 701 operands); 702 703 return success(); 704 } 705 706 // Pull in auto-generated Serializer::dispatchToAutogenSerialization() and 707 // various Serializer::processOp<...>() specializations. 708 #define GET_SERIALIZATION_FNS 709 #include "mlir/Dialect/SPIRV/IR/SPIRVSerialization.inc" 710 711 } // namespace spirv 712 } // namespace mlir 713