Home | History | Annotate | Download | only in source
      1 // Copyright (c) 2015-2016 The Khronos Group Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include "source/opcode.h"
     16 
     17 #include <assert.h>
     18 #include <string.h>
     19 
     20 #include <algorithm>
     21 #include <cstdlib>
     22 
     23 #include "source/instruction.h"
     24 #include "source/macro.h"
     25 #include "source/spirv_constant.h"
     26 #include "source/spirv_endian.h"
     27 #include "source/spirv_target_env.h"
     28 #include "spirv-tools/libspirv.h"
     29 
     30 namespace {
     31 struct OpcodeDescPtrLen {
     32   const spv_opcode_desc_t* ptr;
     33   uint32_t len;
     34 };
     35 
     36 #include "core.insts-unified1.inc"
     37 
     38 static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries),
     39                                                 kOpcodeTableEntries};
     40 
     41 // Represents a vendor tool entry in the SPIR-V XML Regsitry.
     42 struct VendorTool {
     43   uint32_t value;
     44   const char* vendor;
     45   const char* tool;         // Might be empty string.
     46   const char* vendor_tool;  // Combiantion of vendor and tool.
     47 };
     48 
     49 const VendorTool vendor_tools[] = {
     50 #include "generators.inc"
     51 };
     52 
     53 }  // anonymous namespace
     54 
     55 // TODO(dneto): Move this to another file.  It doesn't belong with opcode
     56 // processing.
     57 const char* spvGeneratorStr(uint32_t generator) {
     58   auto where = std::find_if(
     59       std::begin(vendor_tools), std::end(vendor_tools),
     60       [generator](const VendorTool& vt) { return generator == vt.value; });
     61   if (where != std::end(vendor_tools)) return where->vendor_tool;
     62   return "Unknown";
     63 }
     64 
     65 uint32_t spvOpcodeMake(uint16_t wordCount, SpvOp opcode) {
     66   return ((uint32_t)opcode) | (((uint32_t)wordCount) << 16);
     67 }
     68 
     69 void spvOpcodeSplit(const uint32_t word, uint16_t* pWordCount,
     70                     uint16_t* pOpcode) {
     71   if (pWordCount) {
     72     *pWordCount = (uint16_t)((0xffff0000 & word) >> 16);
     73   }
     74   if (pOpcode) {
     75     *pOpcode = 0x0000ffff & word;
     76   }
     77 }
     78 
     79 spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable, spv_target_env) {
     80   if (!pInstTable) return SPV_ERROR_INVALID_POINTER;
     81 
     82   // Descriptions of each opcode.  Each entry describes the format of the
     83   // instruction that follows a particular opcode.
     84 
     85   *pInstTable = &kOpcodeTable;
     86   return SPV_SUCCESS;
     87 }
     88 
     89 spv_result_t spvOpcodeTableNameLookup(spv_target_env env,
     90                                       const spv_opcode_table table,
     91                                       const char* name,
     92                                       spv_opcode_desc* pEntry) {
     93   if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER;
     94   if (!table) return SPV_ERROR_INVALID_TABLE;
     95 
     96   // TODO: This lookup of the Opcode table is suboptimal! Binary sort would be
     97   // preferable but the table requires sorting on the Opcode name, but it's
     98   // static const initialized and matches the order of the spec.
     99   const size_t nameLength = strlen(name);
    100   for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) {
    101     const spv_opcode_desc_t& entry = table->entries[opcodeIndex];
    102     // We considers the current opcode as available as long as
    103     // 1. The target environment satisfies the minimal requirement of the
    104     //    opcode; or
    105     // 2. There is at least one extension enabling this opcode.
    106     //
    107     // Note that the second rule assumes the extension enabling this instruction
    108     // is indeed requested in the SPIR-V code; checking that should be
    109     // validator's work.
    110     if ((spvVersionForTargetEnv(env) >= entry.minVersion ||
    111          entry.numExtensions > 0u || entry.numCapabilities > 0u) &&
    112         nameLength == strlen(entry.name) &&
    113         !strncmp(name, entry.name, nameLength)) {
    114       // NOTE: Found out Opcode!
    115       *pEntry = &entry;
    116       return SPV_SUCCESS;
    117     }
    118   }
    119 
    120   return SPV_ERROR_INVALID_LOOKUP;
    121 }
    122 
    123 spv_result_t spvOpcodeTableValueLookup(spv_target_env env,
    124                                        const spv_opcode_table table,
    125                                        const SpvOp opcode,
    126                                        spv_opcode_desc* pEntry) {
    127   if (!table) return SPV_ERROR_INVALID_TABLE;
    128   if (!pEntry) return SPV_ERROR_INVALID_POINTER;
    129 
    130   const auto beg = table->entries;
    131   const auto end = table->entries + table->count;
    132 
    133   spv_opcode_desc_t needle = {"",    opcode, 0, nullptr, 0,  {},
    134                               false, false,  0, nullptr, ~0u};
    135 
    136   auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
    137     return lhs.opcode < rhs.opcode;
    138   };
    139 
    140   // We need to loop here because there can exist multiple symbols for the same
    141   // opcode value, and they can be introduced in different target environments,
    142   // which means they can have different minimal version requirements.
    143   // Assumes the underlying table is already sorted ascendingly according to
    144   // opcode value.
    145   for (auto it = std::lower_bound(beg, end, needle, comp);
    146        it != end && it->opcode == opcode; ++it) {
    147     // We considers the current opcode as available as long as
    148     // 1. The target environment satisfies the minimal requirement of the
    149     //    opcode; or
    150     // 2. There is at least one extension enabling this opcode.
    151     //
    152     // Note that the second rule assumes the extension enabling this instruction
    153     // is indeed requested in the SPIR-V code; checking that should be
    154     // validator's work.
    155     if (spvVersionForTargetEnv(env) >= it->minVersion ||
    156         it->numExtensions > 0u || it->numCapabilities > 0u) {
    157       *pEntry = it;
    158       return SPV_SUCCESS;
    159     }
    160   }
    161 
    162   return SPV_ERROR_INVALID_LOOKUP;
    163 }
    164 
    165 void spvInstructionCopy(const uint32_t* words, const SpvOp opcode,
    166                         const uint16_t wordCount, const spv_endianness_t endian,
    167                         spv_instruction_t* pInst) {
    168   pInst->opcode = opcode;
    169   pInst->words.resize(wordCount);
    170   for (uint16_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) {
    171     pInst->words[wordIndex] = spvFixWord(words[wordIndex], endian);
    172     if (!wordIndex) {
    173       uint16_t thisWordCount;
    174       uint16_t thisOpcode;
    175       spvOpcodeSplit(pInst->words[wordIndex], &thisWordCount, &thisOpcode);
    176       assert(opcode == static_cast<SpvOp>(thisOpcode) &&
    177              wordCount == thisWordCount && "Endianness failed!");
    178     }
    179   }
    180 }
    181 
    182 const char* spvOpcodeString(const SpvOp opcode) {
    183   const auto beg = kOpcodeTableEntries;
    184   const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries);
    185   spv_opcode_desc_t needle = {"",    opcode, 0, nullptr, 0,  {},
    186                               false, false,  0, nullptr, ~0u};
    187   auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
    188     return lhs.opcode < rhs.opcode;
    189   };
    190   auto it = std::lower_bound(beg, end, needle, comp);
    191   if (it != end && it->opcode == opcode) {
    192     return it->name;
    193   }
    194 
    195   assert(0 && "Unreachable!");
    196   return "unknown";
    197 }
    198 
    199 int32_t spvOpcodeIsScalarType(const SpvOp opcode) {
    200   switch (opcode) {
    201     case SpvOpTypeInt:
    202     case SpvOpTypeFloat:
    203     case SpvOpTypeBool:
    204       return true;
    205     default:
    206       return false;
    207   }
    208 }
    209 
    210 int32_t spvOpcodeIsSpecConstant(const SpvOp opcode) {
    211   switch (opcode) {
    212     case SpvOpSpecConstantTrue:
    213     case SpvOpSpecConstantFalse:
    214     case SpvOpSpecConstant:
    215     case SpvOpSpecConstantComposite:
    216     case SpvOpSpecConstantOp:
    217       return true;
    218     default:
    219       return false;
    220   }
    221 }
    222 
    223 int32_t spvOpcodeIsConstant(const SpvOp opcode) {
    224   switch (opcode) {
    225     case SpvOpConstantTrue:
    226     case SpvOpConstantFalse:
    227     case SpvOpConstant:
    228     case SpvOpConstantComposite:
    229     case SpvOpConstantSampler:
    230     case SpvOpConstantNull:
    231     case SpvOpSpecConstantTrue:
    232     case SpvOpSpecConstantFalse:
    233     case SpvOpSpecConstant:
    234     case SpvOpSpecConstantComposite:
    235     case SpvOpSpecConstantOp:
    236       return true;
    237     default:
    238       return false;
    239   }
    240 }
    241 
    242 bool spvOpcodeIsConstantOrUndef(const SpvOp opcode) {
    243   return opcode == SpvOpUndef || spvOpcodeIsConstant(opcode);
    244 }
    245 
    246 bool spvOpcodeIsScalarSpecConstant(const SpvOp opcode) {
    247   switch (opcode) {
    248     case SpvOpSpecConstantTrue:
    249     case SpvOpSpecConstantFalse:
    250     case SpvOpSpecConstant:
    251       return true;
    252     default:
    253       return false;
    254   }
    255 }
    256 
    257 int32_t spvOpcodeIsComposite(const SpvOp opcode) {
    258   switch (opcode) {
    259     case SpvOpTypeVector:
    260     case SpvOpTypeMatrix:
    261     case SpvOpTypeArray:
    262     case SpvOpTypeStruct:
    263       return true;
    264     default:
    265       return false;
    266   }
    267 }
    268 
    269 bool spvOpcodeReturnsLogicalVariablePointer(const SpvOp opcode) {
    270   switch (opcode) {
    271     case SpvOpVariable:
    272     case SpvOpAccessChain:
    273     case SpvOpInBoundsAccessChain:
    274     case SpvOpFunctionParameter:
    275     case SpvOpImageTexelPointer:
    276     case SpvOpCopyObject:
    277     case SpvOpSelect:
    278     case SpvOpPhi:
    279     case SpvOpFunctionCall:
    280     case SpvOpPtrAccessChain:
    281     case SpvOpLoad:
    282     case SpvOpConstantNull:
    283       return true;
    284     default:
    285       return false;
    286   }
    287 }
    288 
    289 int32_t spvOpcodeReturnsLogicalPointer(const SpvOp opcode) {
    290   switch (opcode) {
    291     case SpvOpVariable:
    292     case SpvOpAccessChain:
    293     case SpvOpInBoundsAccessChain:
    294     case SpvOpFunctionParameter:
    295     case SpvOpImageTexelPointer:
    296     case SpvOpCopyObject:
    297       return true;
    298     default:
    299       return false;
    300   }
    301 }
    302 
    303 int32_t spvOpcodeGeneratesType(SpvOp op) {
    304   switch (op) {
    305     case SpvOpTypeVoid:
    306     case SpvOpTypeBool:
    307     case SpvOpTypeInt:
    308     case SpvOpTypeFloat:
    309     case SpvOpTypeVector:
    310     case SpvOpTypeMatrix:
    311     case SpvOpTypeImage:
    312     case SpvOpTypeSampler:
    313     case SpvOpTypeSampledImage:
    314     case SpvOpTypeArray:
    315     case SpvOpTypeRuntimeArray:
    316     case SpvOpTypeStruct:
    317     case SpvOpTypeOpaque:
    318     case SpvOpTypePointer:
    319     case SpvOpTypeFunction:
    320     case SpvOpTypeEvent:
    321     case SpvOpTypeDeviceEvent:
    322     case SpvOpTypeReserveId:
    323     case SpvOpTypeQueue:
    324     case SpvOpTypePipe:
    325     case SpvOpTypePipeStorage:
    326     case SpvOpTypeNamedBarrier:
    327     case SpvOpTypeAccelerationStructureNV:
    328       return true;
    329     default:
    330       // In particular, OpTypeForwardPointer does not generate a type,
    331       // but declares a storage class for a pointer type generated
    332       // by a different instruction.
    333       break;
    334   }
    335   return 0;
    336 }
    337 
    338 bool spvOpcodeIsDecoration(const SpvOp opcode) {
    339   switch (opcode) {
    340     case SpvOpDecorate:
    341     case SpvOpDecorateId:
    342     case SpvOpMemberDecorate:
    343     case SpvOpGroupDecorate:
    344     case SpvOpGroupMemberDecorate:
    345     case SpvOpDecorateStringGOOGLE:
    346     case SpvOpMemberDecorateStringGOOGLE:
    347       return true;
    348     default:
    349       break;
    350   }
    351   return false;
    352 }
    353 
    354 bool spvOpcodeIsLoad(const SpvOp opcode) {
    355   switch (opcode) {
    356     case SpvOpLoad:
    357     case SpvOpImageSampleExplicitLod:
    358     case SpvOpImageSampleImplicitLod:
    359     case SpvOpImageSampleDrefImplicitLod:
    360     case SpvOpImageSampleDrefExplicitLod:
    361     case SpvOpImageSampleProjImplicitLod:
    362     case SpvOpImageSampleProjExplicitLod:
    363     case SpvOpImageSampleProjDrefImplicitLod:
    364     case SpvOpImageSampleProjDrefExplicitLod:
    365     case SpvOpImageFetch:
    366     case SpvOpImageGather:
    367     case SpvOpImageDrefGather:
    368     case SpvOpImageRead:
    369     case SpvOpImageSparseSampleImplicitLod:
    370     case SpvOpImageSparseSampleExplicitLod:
    371     case SpvOpImageSparseSampleDrefExplicitLod:
    372     case SpvOpImageSparseSampleDrefImplicitLod:
    373     case SpvOpImageSparseFetch:
    374     case SpvOpImageSparseGather:
    375     case SpvOpImageSparseDrefGather:
    376     case SpvOpImageSparseRead:
    377       return true;
    378     default:
    379       return false;
    380   }
    381 }
    382 
    383 bool spvOpcodeIsBranch(SpvOp opcode) {
    384   switch (opcode) {
    385     case SpvOpBranch:
    386     case SpvOpBranchConditional:
    387     case SpvOpSwitch:
    388       return true;
    389     default:
    390       return false;
    391   }
    392 }
    393 
    394 bool spvOpcodeIsAtomicWithLoad(const SpvOp opcode) {
    395   switch (opcode) {
    396     case SpvOpAtomicLoad:
    397     case SpvOpAtomicExchange:
    398     case SpvOpAtomicCompareExchange:
    399     case SpvOpAtomicCompareExchangeWeak:
    400     case SpvOpAtomicIIncrement:
    401     case SpvOpAtomicIDecrement:
    402     case SpvOpAtomicIAdd:
    403     case SpvOpAtomicISub:
    404     case SpvOpAtomicSMin:
    405     case SpvOpAtomicUMin:
    406     case SpvOpAtomicSMax:
    407     case SpvOpAtomicUMax:
    408     case SpvOpAtomicAnd:
    409     case SpvOpAtomicOr:
    410     case SpvOpAtomicXor:
    411     case SpvOpAtomicFlagTestAndSet:
    412       return true;
    413     default:
    414       return false;
    415   }
    416 }
    417 
    418 bool spvOpcodeIsAtomicOp(const SpvOp opcode) {
    419   return (spvOpcodeIsAtomicWithLoad(opcode) || opcode == SpvOpAtomicStore ||
    420           opcode == SpvOpAtomicFlagClear);
    421 }
    422 
    423 bool spvOpcodeIsReturn(SpvOp opcode) {
    424   switch (opcode) {
    425     case SpvOpReturn:
    426     case SpvOpReturnValue:
    427       return true;
    428     default:
    429       return false;
    430   }
    431 }
    432 
    433 bool spvOpcodeIsReturnOrAbort(SpvOp opcode) {
    434   return spvOpcodeIsReturn(opcode) || opcode == SpvOpKill ||
    435          opcode == SpvOpUnreachable;
    436 }
    437 
    438 bool spvOpcodeIsBlockTerminator(SpvOp opcode) {
    439   return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode);
    440 }
    441 
    442 bool spvOpcodeIsBaseOpaqueType(SpvOp opcode) {
    443   switch (opcode) {
    444     case SpvOpTypeImage:
    445     case SpvOpTypeSampler:
    446     case SpvOpTypeSampledImage:
    447     case SpvOpTypeOpaque:
    448     case SpvOpTypeEvent:
    449     case SpvOpTypeDeviceEvent:
    450     case SpvOpTypeReserveId:
    451     case SpvOpTypeQueue:
    452     case SpvOpTypePipe:
    453     case SpvOpTypeForwardPointer:
    454     case SpvOpTypePipeStorage:
    455     case SpvOpTypeNamedBarrier:
    456       return true;
    457     default:
    458       return false;
    459   }
    460 }
    461 
    462 bool spvOpcodeIsNonUniformGroupOperation(SpvOp opcode) {
    463   switch (opcode) {
    464     case SpvOpGroupNonUniformElect:
    465     case SpvOpGroupNonUniformAll:
    466     case SpvOpGroupNonUniformAny:
    467     case SpvOpGroupNonUniformAllEqual:
    468     case SpvOpGroupNonUniformBroadcast:
    469     case SpvOpGroupNonUniformBroadcastFirst:
    470     case SpvOpGroupNonUniformBallot:
    471     case SpvOpGroupNonUniformInverseBallot:
    472     case SpvOpGroupNonUniformBallotBitExtract:
    473     case SpvOpGroupNonUniformBallotBitCount:
    474     case SpvOpGroupNonUniformBallotFindLSB:
    475     case SpvOpGroupNonUniformBallotFindMSB:
    476     case SpvOpGroupNonUniformShuffle:
    477     case SpvOpGroupNonUniformShuffleXor:
    478     case SpvOpGroupNonUniformShuffleUp:
    479     case SpvOpGroupNonUniformShuffleDown:
    480     case SpvOpGroupNonUniformIAdd:
    481     case SpvOpGroupNonUniformFAdd:
    482     case SpvOpGroupNonUniformIMul:
    483     case SpvOpGroupNonUniformFMul:
    484     case SpvOpGroupNonUniformSMin:
    485     case SpvOpGroupNonUniformUMin:
    486     case SpvOpGroupNonUniformFMin:
    487     case SpvOpGroupNonUniformSMax:
    488     case SpvOpGroupNonUniformUMax:
    489     case SpvOpGroupNonUniformFMax:
    490     case SpvOpGroupNonUniformBitwiseAnd:
    491     case SpvOpGroupNonUniformBitwiseOr:
    492     case SpvOpGroupNonUniformBitwiseXor:
    493     case SpvOpGroupNonUniformLogicalAnd:
    494     case SpvOpGroupNonUniformLogicalOr:
    495     case SpvOpGroupNonUniformLogicalXor:
    496     case SpvOpGroupNonUniformQuadBroadcast:
    497     case SpvOpGroupNonUniformQuadSwap:
    498       return true;
    499     default:
    500       return false;
    501   }
    502 }
    503 
    504 bool spvOpcodeIsScalarizable(SpvOp opcode) {
    505   switch (opcode) {
    506     case SpvOpPhi:
    507     case SpvOpCopyObject:
    508     case SpvOpConvertFToU:
    509     case SpvOpConvertFToS:
    510     case SpvOpConvertSToF:
    511     case SpvOpConvertUToF:
    512     case SpvOpUConvert:
    513     case SpvOpSConvert:
    514     case SpvOpFConvert:
    515     case SpvOpQuantizeToF16:
    516     case SpvOpVectorInsertDynamic:
    517     case SpvOpSNegate:
    518     case SpvOpFNegate:
    519     case SpvOpIAdd:
    520     case SpvOpFAdd:
    521     case SpvOpISub:
    522     case SpvOpFSub:
    523     case SpvOpIMul:
    524     case SpvOpFMul:
    525     case SpvOpUDiv:
    526     case SpvOpSDiv:
    527     case SpvOpFDiv:
    528     case SpvOpUMod:
    529     case SpvOpSRem:
    530     case SpvOpSMod:
    531     case SpvOpFRem:
    532     case SpvOpFMod:
    533     case SpvOpVectorTimesScalar:
    534     case SpvOpIAddCarry:
    535     case SpvOpISubBorrow:
    536     case SpvOpUMulExtended:
    537     case SpvOpSMulExtended:
    538     case SpvOpShiftRightLogical:
    539     case SpvOpShiftRightArithmetic:
    540     case SpvOpShiftLeftLogical:
    541     case SpvOpBitwiseOr:
    542     case SpvOpBitwiseAnd:
    543     case SpvOpNot:
    544     case SpvOpBitFieldInsert:
    545     case SpvOpBitFieldSExtract:
    546     case SpvOpBitFieldUExtract:
    547     case SpvOpBitReverse:
    548     case SpvOpBitCount:
    549     case SpvOpIsNan:
    550     case SpvOpIsInf:
    551     case SpvOpIsFinite:
    552     case SpvOpIsNormal:
    553     case SpvOpSignBitSet:
    554     case SpvOpLessOrGreater:
    555     case SpvOpOrdered:
    556     case SpvOpUnordered:
    557     case SpvOpLogicalEqual:
    558     case SpvOpLogicalNotEqual:
    559     case SpvOpLogicalOr:
    560     case SpvOpLogicalAnd:
    561     case SpvOpLogicalNot:
    562     case SpvOpSelect:
    563     case SpvOpIEqual:
    564     case SpvOpINotEqual:
    565     case SpvOpUGreaterThan:
    566     case SpvOpSGreaterThan:
    567     case SpvOpUGreaterThanEqual:
    568     case SpvOpSGreaterThanEqual:
    569     case SpvOpULessThan:
    570     case SpvOpSLessThan:
    571     case SpvOpULessThanEqual:
    572     case SpvOpSLessThanEqual:
    573     case SpvOpFOrdEqual:
    574     case SpvOpFUnordEqual:
    575     case SpvOpFOrdNotEqual:
    576     case SpvOpFUnordNotEqual:
    577     case SpvOpFOrdLessThan:
    578     case SpvOpFUnordLessThan:
    579     case SpvOpFOrdGreaterThan:
    580     case SpvOpFUnordGreaterThan:
    581     case SpvOpFOrdLessThanEqual:
    582     case SpvOpFUnordLessThanEqual:
    583     case SpvOpFOrdGreaterThanEqual:
    584     case SpvOpFUnordGreaterThanEqual:
    585       return true;
    586     default:
    587       return false;
    588   }
    589 }
    590 
    591 bool spvOpcodeIsDebug(SpvOp opcode) {
    592   switch (opcode) {
    593     case SpvOpName:
    594     case SpvOpMemberName:
    595     case SpvOpSource:
    596     case SpvOpSourceContinued:
    597     case SpvOpSourceExtension:
    598     case SpvOpString:
    599     case SpvOpLine:
    600     case SpvOpNoLine:
    601       return true;
    602     default:
    603       return false;
    604   }
    605 }
    606