brintos

brintos / llvm-project-archived public Read only

0
0
Text · 21.2 KiB · 5b04a14 Raw
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