611 lines · cpp
1//===- DeserializeOps.cpp - MLIR SPIR-V Deserialization (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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines the Deserializer methods for SPIR-V binary instructions.10//11//===----------------------------------------------------------------------===//12 13#include "Deserializer.h"14 15#include "mlir/Dialect/SPIRV/IR/SPIRVEnums.h"16#include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"17#include "mlir/IR/Builders.h"18#include "mlir/IR/Location.h"19#include "mlir/Target/SPIRV/SPIRVBinaryUtils.h"20#include "llvm/ADT/STLExtras.h"21#include "llvm/ADT/SmallVector.h"22#include "llvm/Support/Debug.h"23#include <optional>24 25using namespace mlir;26 27#define DEBUG_TYPE "spirv-deserialization"28 29//===----------------------------------------------------------------------===//30// Utility Functions31//===----------------------------------------------------------------------===//32 33/// Extracts the opcode from the given first word of a SPIR-V instruction.34static inline spirv::Opcode extractOpcode(uint32_t word) {35 return static_cast<spirv::Opcode>(word & 0xffff);36}37 38//===----------------------------------------------------------------------===//39// Instruction40//===----------------------------------------------------------------------===//41 42Value spirv::Deserializer::getValue(uint32_t id) {43 if (auto constInfo = getConstant(id)) {44 // Materialize a `spirv.Constant` op at every use site.45 return spirv::ConstantOp::create(opBuilder, unknownLoc, constInfo->second,46 constInfo->first);47 }48 if (std::optional<std::pair<Attribute, Type>> constCompositeReplicateInfo =49 getConstantCompositeReplicate(id)) {50 return spirv::EXTConstantCompositeReplicateOp::create(51 opBuilder, unknownLoc, constCompositeReplicateInfo->second,52 constCompositeReplicateInfo->first);53 }54 if (auto varOp = getGlobalVariable(id)) {55 auto addressOfOp =56 spirv::AddressOfOp::create(opBuilder, unknownLoc, varOp.getType(),57 SymbolRefAttr::get(varOp.getOperation()));58 return addressOfOp.getPointer();59 }60 if (auto constOp = getSpecConstant(id)) {61 auto referenceOfOp = spirv::ReferenceOfOp::create(62 opBuilder, unknownLoc, constOp.getDefaultValue().getType(),63 SymbolRefAttr::get(constOp.getOperation()));64 return referenceOfOp.getReference();65 }66 if (SpecConstantCompositeOp specConstCompositeOp =67 getSpecConstantComposite(id)) {68 auto referenceOfOp = spirv::ReferenceOfOp::create(69 opBuilder, unknownLoc, specConstCompositeOp.getType(),70 SymbolRefAttr::get(specConstCompositeOp.getOperation()));71 return referenceOfOp.getReference();72 }73 if (auto specConstCompositeReplicateOp =74 getSpecConstantCompositeReplicate(id)) {75 auto referenceOfOp = spirv::ReferenceOfOp::create(76 opBuilder, unknownLoc, specConstCompositeReplicateOp.getType(),77 SymbolRefAttr::get(specConstCompositeReplicateOp.getOperation()));78 return referenceOfOp.getReference();79 }80 if (auto specConstOperationInfo = getSpecConstantOperation(id)) {81 return materializeSpecConstantOperation(82 id, specConstOperationInfo->enclodesOpcode,83 specConstOperationInfo->resultTypeID,84 specConstOperationInfo->enclosedOpOperands);85 }86 if (auto undef = getUndefType(id)) {87 return spirv::UndefOp::create(opBuilder, unknownLoc, undef);88 }89 if (std::optional<spirv::GraphConstantARMOpMaterializationInfo>90 graphConstantARMInfo = getGraphConstantARM(id)) {91 IntegerAttr graphConstantID = graphConstantARMInfo->graphConstantID;92 Type resultType = graphConstantARMInfo->resultType;93 return spirv::GraphConstantARMOp::create(opBuilder, unknownLoc, resultType,94 graphConstantID);95 }96 return valueMap.lookup(id);97}98 99LogicalResult spirv::Deserializer::sliceInstruction(100 spirv::Opcode &opcode, ArrayRef<uint32_t> &operands,101 std::optional<spirv::Opcode> expectedOpcode) {102 auto binarySize = binary.size();103 if (curOffset >= binarySize) {104 return emitError(unknownLoc, "expected ")105 << (expectedOpcode ? spirv::stringifyOpcode(*expectedOpcode)106 : "more")107 << " instruction";108 }109 110 // For each instruction, get its word count from the first word to slice it111 // from the stream properly, and then dispatch to the instruction handler.112 113 uint32_t wordCount = binary[curOffset] >> 16;114 115 if (wordCount == 0)116 return emitError(unknownLoc, "word count cannot be zero");117 118 uint32_t nextOffset = curOffset + wordCount;119 if (nextOffset > binarySize)120 return emitError(unknownLoc, "insufficient words for the last instruction");121 122 opcode = extractOpcode(binary[curOffset]);123 operands = binary.slice(curOffset + 1, wordCount - 1);124 curOffset = nextOffset;125 return success();126}127 128LogicalResult spirv::Deserializer::processInstruction(129 spirv::Opcode opcode, ArrayRef<uint32_t> operands, bool deferInstructions) {130 LLVM_DEBUG(logger.startLine() << "[inst] processing instruction "131 << spirv::stringifyOpcode(opcode) << "\n");132 133 // First dispatch all the instructions whose opcode does not correspond to134 // those that have a direct mirror in the SPIR-V dialect135 switch (opcode) {136 case spirv::Opcode::OpCapability:137 return processCapability(operands);138 case spirv::Opcode::OpExtension:139 return processExtension(operands);140 case spirv::Opcode::OpExtInst:141 return processExtInst(operands);142 case spirv::Opcode::OpExtInstImport:143 return processExtInstImport(operands);144 case spirv::Opcode::OpMemberName:145 return processMemberName(operands);146 case spirv::Opcode::OpMemoryModel:147 return processMemoryModel(operands);148 case spirv::Opcode::OpEntryPoint:149 case spirv::Opcode::OpExecutionMode:150 if (deferInstructions) {151 deferredInstructions.emplace_back(opcode, operands);152 return success();153 }154 break;155 case spirv::Opcode::OpVariable:156 if (isa<spirv::ModuleOp>(opBuilder.getBlock()->getParentOp())) {157 return processGlobalVariable(operands);158 }159 break;160 case spirv::Opcode::OpLine:161 return processDebugLine(operands);162 case spirv::Opcode::OpNoLine:163 clearDebugLine();164 return success();165 case spirv::Opcode::OpName:166 return processName(operands);167 case spirv::Opcode::OpString:168 return processDebugString(operands);169 case spirv::Opcode::OpModuleProcessed:170 case spirv::Opcode::OpSource:171 case spirv::Opcode::OpSourceContinued:172 case spirv::Opcode::OpSourceExtension:173 // TODO: This is debug information embedded in the binary which should be174 // translated into the spirv.module.175 return success();176 case spirv::Opcode::OpTypeVoid:177 case spirv::Opcode::OpTypeBool:178 case spirv::Opcode::OpTypeInt:179 case spirv::Opcode::OpTypeFloat:180 case spirv::Opcode::OpTypeVector:181 case spirv::Opcode::OpTypeMatrix:182 case spirv::Opcode::OpTypeArray:183 case spirv::Opcode::OpTypeFunction:184 case spirv::Opcode::OpTypeImage:185 case spirv::Opcode::OpTypeSampledImage:186 case spirv::Opcode::OpTypeRuntimeArray:187 case spirv::Opcode::OpTypeStruct:188 case spirv::Opcode::OpTypePointer:189 case spirv::Opcode::OpTypeTensorARM:190 case spirv::Opcode::OpTypeGraphARM:191 case spirv::Opcode::OpTypeCooperativeMatrixKHR:192 return processType(opcode, operands);193 case spirv::Opcode::OpTypeForwardPointer:194 return processTypeForwardPointer(operands);195 case spirv::Opcode::OpConstant:196 return processConstant(operands, /*isSpec=*/false);197 case spirv::Opcode::OpSpecConstant:198 return processConstant(operands, /*isSpec=*/true);199 case spirv::Opcode::OpConstantComposite:200 return processConstantComposite(operands);201 case spirv::Opcode::OpConstantCompositeReplicateEXT:202 return processConstantCompositeReplicateEXT(operands);203 case spirv::Opcode::OpSpecConstantComposite:204 return processSpecConstantComposite(operands);205 case spirv::Opcode::OpSpecConstantCompositeReplicateEXT:206 return processSpecConstantCompositeReplicateEXT(operands);207 case spirv::Opcode::OpSpecConstantOp:208 return processSpecConstantOperation(operands);209 case spirv::Opcode::OpConstantTrue:210 return processConstantBool(/*isTrue=*/true, operands, /*isSpec=*/false);211 case spirv::Opcode::OpSpecConstantTrue:212 return processConstantBool(/*isTrue=*/true, operands, /*isSpec=*/true);213 case spirv::Opcode::OpConstantFalse:214 return processConstantBool(/*isTrue=*/false, operands, /*isSpec=*/false);215 case spirv::Opcode::OpSpecConstantFalse:216 return processConstantBool(/*isTrue=*/false, operands, /*isSpec=*/true);217 case spirv::Opcode::OpConstantNull:218 return processConstantNull(operands);219 case spirv::Opcode::OpGraphConstantARM:220 return processGraphConstantARM(operands);221 case spirv::Opcode::OpDecorate:222 return processDecoration(operands);223 case spirv::Opcode::OpMemberDecorate:224 return processMemberDecoration(operands);225 case spirv::Opcode::OpFunction:226 return processFunction(operands);227 case spirv::Opcode::OpGraphEntryPointARM:228 if (deferInstructions) {229 deferredInstructions.emplace_back(opcode, operands);230 return success();231 }232 return processGraphEntryPointARM(operands);233 case spirv::Opcode::OpGraphARM:234 return processGraphARM(operands);235 case spirv::Opcode::OpGraphSetOutputARM:236 return processOpGraphSetOutputARM(operands);237 case spirv::Opcode::OpGraphEndARM:238 return processGraphEndARM(operands);239 case spirv::Opcode::OpLabel:240 return processLabel(operands);241 case spirv::Opcode::OpBranch:242 return processBranch(operands);243 case spirv::Opcode::OpBranchConditional:244 return processBranchConditional(operands);245 case spirv::Opcode::OpSelectionMerge:246 return processSelectionMerge(operands);247 case spirv::Opcode::OpLoopMerge:248 return processLoopMerge(operands);249 case spirv::Opcode::OpPhi:250 return processPhi(operands);251 case spirv::Opcode::OpSwitch:252 return processSwitch(operands);253 case spirv::Opcode::OpUndef:254 return processUndef(operands);255 default:256 break;257 }258 return dispatchToAutogenDeserialization(opcode, operands);259}260 261LogicalResult spirv::Deserializer::processOpWithoutGrammarAttr(262 ArrayRef<uint32_t> words, StringRef opName, bool hasResult,263 unsigned numOperands) {264 SmallVector<Type, 1> resultTypes;265 uint32_t valueID = 0;266 267 size_t wordIndex = 0;268 if (hasResult) {269 if (wordIndex >= words.size())270 return emitError(unknownLoc,271 "expected result type <id> while deserializing for ")272 << opName;273 274 // Decode the type <id>275 auto type = getType(words[wordIndex]);276 if (!type)277 return emitError(unknownLoc, "unknown type result <id>: ")278 << words[wordIndex];279 resultTypes.push_back(type);280 ++wordIndex;281 282 // Decode the result <id>283 if (wordIndex >= words.size())284 return emitError(unknownLoc,285 "expected result <id> while deserializing for ")286 << opName;287 valueID = words[wordIndex];288 ++wordIndex;289 }290 291 SmallVector<Value, 4> operands;292 SmallVector<NamedAttribute, 4> attributes;293 294 // Decode operands295 size_t operandIndex = 0;296 for (; operandIndex < numOperands && wordIndex < words.size();297 ++operandIndex, ++wordIndex) {298 auto arg = getValue(words[wordIndex]);299 if (!arg)300 return emitError(unknownLoc, "unknown result <id>: ") << words[wordIndex];301 operands.push_back(arg);302 }303 if (operandIndex != numOperands) {304 return emitError(305 unknownLoc,306 "found less operands than expected when deserializing for ")307 << opName << "; only " << operandIndex << " of " << numOperands308 << " processed";309 }310 if (wordIndex != words.size()) {311 return emitError(312 unknownLoc,313 "found more operands than expected when deserializing for ")314 << opName << "; only " << wordIndex << " of " << words.size()315 << " processed";316 }317 318 // Attach attributes from decorations319 if (decorations.count(valueID)) {320 auto attrs = decorations[valueID].getAttrs();321 attributes.append(attrs.begin(), attrs.end());322 }323 324 // Create the op and update bookkeeping maps325 Location loc = createFileLineColLoc(opBuilder);326 OperationState opState(loc, opName);327 opState.addOperands(operands);328 if (hasResult)329 opState.addTypes(resultTypes);330 opState.addAttributes(attributes);331 Operation *op = opBuilder.create(opState);332 if (hasResult)333 valueMap[valueID] = op->getResult(0);334 335 if (op->hasTrait<OpTrait::IsTerminator>())336 clearDebugLine();337 338 return success();339}340 341LogicalResult spirv::Deserializer::processUndef(ArrayRef<uint32_t> operands) {342 if (operands.size() != 2) {343 return emitError(unknownLoc, "OpUndef instruction must have two operands");344 }345 auto type = getType(operands[0]);346 if (!type) {347 return emitError(unknownLoc, "unknown type <id> with OpUndef instruction");348 }349 undefMap[operands[1]] = type;350 return success();351}352 353LogicalResult spirv::Deserializer::processExtInst(ArrayRef<uint32_t> operands) {354 if (operands.size() < 4) {355 return emitError(unknownLoc,356 "OpExtInst must have at least 4 operands, result type "357 "<id>, result <id>, set <id> and instruction opcode");358 }359 if (!extendedInstSets.count(operands[2])) {360 return emitError(unknownLoc, "undefined set <id> in OpExtInst");361 }362 SmallVector<uint32_t, 4> slicedOperands;363 slicedOperands.append(operands.begin(), std::next(operands.begin(), 2));364 slicedOperands.append(std::next(operands.begin(), 4), operands.end());365 return dispatchToExtensionSetAutogenDeserialization(366 extendedInstSets[operands[2]], operands[3], slicedOperands);367}368 369namespace mlir {370namespace spirv {371 372template <>373LogicalResult374Deserializer::processOp<spirv::EntryPointOp>(ArrayRef<uint32_t> words) {375 unsigned wordIndex = 0;376 if (wordIndex >= words.size()) {377 return emitError(unknownLoc,378 "missing Execution Model specification in OpEntryPoint");379 }380 auto execModel = spirv::ExecutionModelAttr::get(381 context, static_cast<spirv::ExecutionModel>(words[wordIndex++]));382 if (wordIndex >= words.size()) {383 return emitError(unknownLoc, "missing <id> in OpEntryPoint");384 }385 // Get the function <id>386 auto fnID = words[wordIndex++];387 // Get the function name388 auto fnName = decodeStringLiteral(words, wordIndex);389 // Verify that the function <id> matches the fnName390 auto parsedFunc = getFunction(fnID);391 if (!parsedFunc) {392 return emitError(unknownLoc, "no function matching <id> ") << fnID;393 }394 if (parsedFunc.getName() != fnName) {395 // The deserializer uses "spirv_fn_<id>" as the function name if the input396 // SPIR-V blob does not contain a name for it. We should use a more clear397 // indication for such case rather than relying on naming details.398 if (!parsedFunc.getName().starts_with("spirv_fn_"))399 return emitError(unknownLoc,400 "function name mismatch between OpEntryPoint "401 "and OpFunction with <id> ")402 << fnID << ": " << fnName << " vs. " << parsedFunc.getName();403 parsedFunc.setName(fnName);404 }405 SmallVector<Attribute, 4> interface;406 while (wordIndex < words.size()) {407 auto arg = getGlobalVariable(words[wordIndex]);408 if (!arg) {409 return emitError(unknownLoc, "undefined result <id> ")410 << words[wordIndex] << " while decoding OpEntryPoint";411 }412 interface.push_back(SymbolRefAttr::get(arg.getOperation()));413 wordIndex++;414 }415 spirv::EntryPointOp::create(416 opBuilder, unknownLoc, execModel,417 SymbolRefAttr::get(opBuilder.getContext(), fnName),418 opBuilder.getArrayAttr(interface));419 return success();420}421 422template <>423LogicalResult424Deserializer::processOp<spirv::ExecutionModeOp>(ArrayRef<uint32_t> words) {425 unsigned wordIndex = 0;426 if (wordIndex >= words.size()) {427 return emitError(unknownLoc,428 "missing function result <id> in OpExecutionMode");429 }430 // Get the function <id> to get the name of the function431 auto fnID = words[wordIndex++];432 auto fn = getFunction(fnID);433 if (!fn) {434 return emitError(unknownLoc, "no function matching <id> ") << fnID;435 }436 // Get the Execution mode437 if (wordIndex >= words.size()) {438 return emitError(unknownLoc, "missing Execution Mode in OpExecutionMode");439 }440 auto execMode = spirv::ExecutionModeAttr::get(441 context, static_cast<spirv::ExecutionMode>(words[wordIndex++]));442 443 // Get the values444 SmallVector<Attribute, 4> attrListElems;445 while (wordIndex < words.size()) {446 attrListElems.push_back(opBuilder.getI32IntegerAttr(words[wordIndex++]));447 }448 auto values = opBuilder.getArrayAttr(attrListElems);449 spirv::ExecutionModeOp::create(450 opBuilder, unknownLoc,451 SymbolRefAttr::get(opBuilder.getContext(), fn.getName()), execMode,452 values);453 return success();454}455 456template <>457LogicalResult458Deserializer::processOp<spirv::FunctionCallOp>(ArrayRef<uint32_t> operands) {459 if (operands.size() < 3) {460 return emitError(unknownLoc,461 "OpFunctionCall must have at least 3 operands");462 }463 464 Type resultType = getType(operands[0]);465 if (!resultType) {466 return emitError(unknownLoc, "undefined result type from <id> ")467 << operands[0];468 }469 470 // Use null type to mean no result type.471 if (isVoidType(resultType))472 resultType = nullptr;473 474 auto resultID = operands[1];475 auto functionID = operands[2];476 477 auto functionName = getFunctionSymbol(functionID);478 479 SmallVector<Value, 4> arguments;480 for (auto operand : llvm::drop_begin(operands, 3)) {481 auto value = getValue(operand);482 if (!value) {483 return emitError(unknownLoc, "unknown <id> ")484 << operand << " used by OpFunctionCall";485 }486 arguments.push_back(value);487 }488 489 auto opFunctionCall = spirv::FunctionCallOp::create(490 opBuilder, unknownLoc, resultType,491 SymbolRefAttr::get(opBuilder.getContext(), functionName), arguments);492 493 if (resultType)494 valueMap[resultID] = opFunctionCall.getResult(0);495 return success();496}497 498template <>499LogicalResult500Deserializer::processOp<spirv::CopyMemoryOp>(ArrayRef<uint32_t> words) {501 SmallVector<Type, 1> resultTypes;502 size_t wordIndex = 0;503 SmallVector<Value, 4> operands;504 SmallVector<NamedAttribute, 4> attributes;505 506 if (wordIndex < words.size()) {507 auto arg = getValue(words[wordIndex]);508 509 if (!arg) {510 return emitError(unknownLoc, "unknown result <id> : ")511 << words[wordIndex];512 }513 514 operands.push_back(arg);515 wordIndex++;516 }517 518 if (wordIndex < words.size()) {519 auto arg = getValue(words[wordIndex]);520 521 if (!arg) {522 return emitError(unknownLoc, "unknown result <id> : ")523 << words[wordIndex];524 }525 526 operands.push_back(arg);527 wordIndex++;528 }529 530 bool isAlignedAttr = false;531 532 if (wordIndex < words.size()) {533 auto attrValue = words[wordIndex++];534 auto attr = opBuilder.getAttr<spirv::MemoryAccessAttr>(535 static_cast<spirv::MemoryAccess>(attrValue));536 attributes.push_back(537 opBuilder.getNamedAttr(attributeName<MemoryAccess>(), attr));538 isAlignedAttr = (attrValue == 2);539 }540 541 if (isAlignedAttr && wordIndex < words.size()) {542 attributes.push_back(opBuilder.getNamedAttr(543 "alignment", opBuilder.getI32IntegerAttr(words[wordIndex++])));544 }545 546 if (wordIndex < words.size()) {547 auto attrValue = words[wordIndex++];548 auto attr = opBuilder.getAttr<spirv::MemoryAccessAttr>(549 static_cast<spirv::MemoryAccess>(attrValue));550 attributes.push_back(opBuilder.getNamedAttr("source_memory_access", attr));551 }552 553 if (wordIndex < words.size()) {554 attributes.push_back(opBuilder.getNamedAttr(555 "source_alignment", opBuilder.getI32IntegerAttr(words[wordIndex++])));556 }557 558 if (wordIndex != words.size()) {559 return emitError(unknownLoc,560 "found more operands than expected when deserializing "561 "spirv::CopyMemoryOp, only ")562 << wordIndex << " of " << words.size() << " processed";563 }564 565 Location loc = createFileLineColLoc(opBuilder);566 spirv::CopyMemoryOp::create(opBuilder, loc, resultTypes, operands,567 attributes);568 569 return success();570}571 572template <>573LogicalResult Deserializer::processOp<spirv::GenericCastToPtrExplicitOp>(574 ArrayRef<uint32_t> words) {575 if (words.size() != 4) {576 return emitError(unknownLoc,577 "expected 4 words in GenericCastToPtrExplicitOp"578 " but got : ")579 << words.size();580 }581 SmallVector<Type, 1> resultTypes;582 SmallVector<Value, 4> operands;583 uint32_t valueID = 0;584 auto type = getType(words[0]);585 586 if (!type)587 return emitError(unknownLoc, "unknown type result <id> : ") << words[0];588 resultTypes.push_back(type);589 590 valueID = words[1];591 592 auto arg = getValue(words[2]);593 if (!arg)594 return emitError(unknownLoc, "unknown result <id> : ") << words[2];595 operands.push_back(arg);596 597 Location loc = createFileLineColLoc(opBuilder);598 Operation *op = spirv::GenericCastToPtrExplicitOp::create(599 opBuilder, loc, resultTypes, operands);600 valueMap[valueID] = op->getResult(0);601 return success();602}603 604// Pull in auto-generated Deserializer::dispatchToAutogenDeserialization() and605// various Deserializer::processOp<...>() specializations.606#define GET_DESERIALIZATION_FNS607#include "mlir/Dialect/SPIRV/IR/SPIRVSerialization.inc"608 609} // namespace spirv610} // namespace mlir611