Home | History | Annotate | Download | only in SPIRV
      1 //
      2 // Copyright (C) 2015 LunarG, Inc.
      3 //
      4 // All rights reserved.
      5 //
      6 // Redistribution and use in source and binary forms, with or without
      7 // modification, are permitted provided that the following conditions
      8 // are met:
      9 //
     10 //    Redistributions of source code must retain the above copyright
     11 //    notice, this list of conditions and the following disclaimer.
     12 //
     13 //    Redistributions in binary form must reproduce the above
     14 //    copyright notice, this list of conditions and the following
     15 //    disclaimer in the documentation and/or other materials provided
     16 //    with the distribution.
     17 //
     18 //    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
     19 //    contributors may be used to endorse or promote products derived
     20 //    from this software without specific prior written permission.
     21 //
     22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     23 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     24 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     25 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     26 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     27 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     28 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     29 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     30 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     32 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     33 // POSSIBILITY OF SUCH DAMAGE.
     34 //
     35 
     36 #include "SPVRemapper.h"
     37 #include "doc.h"
     38 
     39 #if !defined (use_cpp11)
     40 // ... not supported before C++11
     41 #else // defined (use_cpp11)
     42 
     43 #include <algorithm>
     44 #include <cassert>
     45 #include "../glslang/Include/Common.h"
     46 
     47 namespace spv {
     48 
     49     // By default, just abort on error.  Can be overridden via RegisterErrorHandler
     50     spirvbin_t::errorfn_t spirvbin_t::errorHandler = [](const std::string&) { exit(5); };
     51     // By default, eat log messages.  Can be overridden via RegisterLogHandler
     52     spirvbin_t::logfn_t   spirvbin_t::logHandler   = [](const std::string&) { };
     53 
     54     // This can be overridden to provide other message behavior if needed
     55     void spirvbin_t::msg(int minVerbosity, int indent, const std::string& txt) const
     56     {
     57         if (verbose >= minVerbosity)
     58             logHandler(std::string(indent, ' ') + txt);
     59     }
     60 
     61     // hash opcode, with special handling for OpExtInst
     62     std::uint32_t spirvbin_t::asOpCodeHash(unsigned word)
     63     {
     64         const spv::Op opCode = asOpCode(word);
     65 
     66         std::uint32_t offset = 0;
     67 
     68         switch (opCode) {
     69         case spv::OpExtInst:
     70             offset += asId(word + 4); break;
     71         default:
     72             break;
     73         }
     74 
     75         return opCode * 19 + offset; // 19 = small prime
     76     }
     77 
     78     spirvbin_t::range_t spirvbin_t::literalRange(spv::Op opCode) const
     79     {
     80         static const int maxCount = 1<<30;
     81 
     82         switch (opCode) {
     83         case spv::OpTypeFloat:        // fall through...
     84         case spv::OpTypePointer:      return range_t(2, 3);
     85         case spv::OpTypeInt:          return range_t(2, 4);
     86         // TODO: case spv::OpTypeImage:
     87         // TODO: case spv::OpTypeSampledImage:
     88         case spv::OpTypeSampler:      return range_t(3, 8);
     89         case spv::OpTypeVector:       // fall through
     90         case spv::OpTypeMatrix:       // ...
     91         case spv::OpTypePipe:         return range_t(3, 4);
     92         case spv::OpConstant:         return range_t(3, maxCount);
     93         default:                      return range_t(0, 0);
     94         }
     95     }
     96 
     97     spirvbin_t::range_t spirvbin_t::typeRange(spv::Op opCode) const
     98     {
     99         static const int maxCount = 1<<30;
    100 
    101         if (isConstOp(opCode))
    102             return range_t(1, 2);
    103 
    104         switch (opCode) {
    105         case spv::OpTypeVector:       // fall through
    106         case spv::OpTypeMatrix:       // ...
    107         case spv::OpTypeSampler:      // ...
    108         case spv::OpTypeArray:        // ...
    109         case spv::OpTypeRuntimeArray: // ...
    110         case spv::OpTypePipe:         return range_t(2, 3);
    111         case spv::OpTypeStruct:       // fall through
    112         case spv::OpTypeFunction:     return range_t(2, maxCount);
    113         case spv::OpTypePointer:      return range_t(3, 4);
    114         default:                      return range_t(0, 0);
    115         }
    116     }
    117 
    118     spirvbin_t::range_t spirvbin_t::constRange(spv::Op opCode) const
    119     {
    120         static const int maxCount = 1<<30;
    121 
    122         switch (opCode) {
    123         case spv::OpTypeArray:         // fall through...
    124         case spv::OpTypeRuntimeArray:  return range_t(3, 4);
    125         case spv::OpConstantComposite: return range_t(3, maxCount);
    126         default:                       return range_t(0, 0);
    127         }
    128     }
    129 
    130     // Return the size of a type in 32-bit words.  This currently only
    131     // handles ints and floats, and is only invoked by queries which must be
    132     // integer types.  If ever needed, it can be generalized.
    133     unsigned spirvbin_t::typeSizeInWords(spv::Id id) const
    134     {
    135         const unsigned typeStart = idPos(id);
    136         const spv::Op  opCode    = asOpCode(typeStart);
    137 
    138         if (errorLatch)
    139             return 0;
    140 
    141         switch (opCode) {
    142         case spv::OpTypeInt:   // fall through...
    143         case spv::OpTypeFloat: return (spv[typeStart+2]+31)/32;
    144         default:
    145             return 0;
    146         }
    147     }
    148 
    149     // Looks up the type of a given const or variable ID, and
    150     // returns its size in 32-bit words.
    151     unsigned spirvbin_t::idTypeSizeInWords(spv::Id id) const
    152     {
    153         const auto tid_it = idTypeSizeMap.find(id);
    154         if (tid_it == idTypeSizeMap.end()) {
    155             error("type size for ID not found");
    156             return 0;
    157         }
    158 
    159         return tid_it->second;
    160     }
    161 
    162     // Is this an opcode we should remove when using --strip?
    163     bool spirvbin_t::isStripOp(spv::Op opCode) const
    164     {
    165         switch (opCode) {
    166         case spv::OpSource:
    167         case spv::OpSourceExtension:
    168         case spv::OpName:
    169         case spv::OpMemberName:
    170         case spv::OpLine:           return true;
    171         default:                    return false;
    172         }
    173     }
    174 
    175     // Return true if this opcode is flow control
    176     bool spirvbin_t::isFlowCtrl(spv::Op opCode) const
    177     {
    178         switch (opCode) {
    179         case spv::OpBranchConditional:
    180         case spv::OpBranch:
    181         case spv::OpSwitch:
    182         case spv::OpLoopMerge:
    183         case spv::OpSelectionMerge:
    184         case spv::OpLabel:
    185         case spv::OpFunction:
    186         case spv::OpFunctionEnd:    return true;
    187         default:                    return false;
    188         }
    189     }
    190 
    191     // Return true if this opcode defines a type
    192     bool spirvbin_t::isTypeOp(spv::Op opCode) const
    193     {
    194         switch (opCode) {
    195         case spv::OpTypeVoid:
    196         case spv::OpTypeBool:
    197         case spv::OpTypeInt:
    198         case spv::OpTypeFloat:
    199         case spv::OpTypeVector:
    200         case spv::OpTypeMatrix:
    201         case spv::OpTypeImage:
    202         case spv::OpTypeSampler:
    203         case spv::OpTypeArray:
    204         case spv::OpTypeRuntimeArray:
    205         case spv::OpTypeStruct:
    206         case spv::OpTypeOpaque:
    207         case spv::OpTypePointer:
    208         case spv::OpTypeFunction:
    209         case spv::OpTypeEvent:
    210         case spv::OpTypeDeviceEvent:
    211         case spv::OpTypeReserveId:
    212         case spv::OpTypeQueue:
    213         case spv::OpTypeSampledImage:
    214         case spv::OpTypePipe:         return true;
    215         default:                      return false;
    216         }
    217     }
    218 
    219     // Return true if this opcode defines a constant
    220     bool spirvbin_t::isConstOp(spv::Op opCode) const
    221     {
    222         switch (opCode) {
    223         case spv::OpConstantSampler:
    224             error("unimplemented constant type");
    225             return true;
    226 
    227         case spv::OpConstantNull:
    228         case spv::OpConstantTrue:
    229         case spv::OpConstantFalse:
    230         case spv::OpConstantComposite:
    231         case spv::OpConstant:
    232             return true;
    233 
    234         default:
    235             return false;
    236         }
    237     }
    238 
    239     const auto inst_fn_nop = [](spv::Op, unsigned) { return false; };
    240     const auto op_fn_nop   = [](spv::Id&)          { };
    241 
    242     // g++ doesn't like these defined in the class proper in an anonymous namespace.
    243     // Dunno why.  Also MSVC doesn't like the constexpr keyword.  Also dunno why.
    244     // Defining them externally seems to please both compilers, so, here they are.
    245     const spv::Id spirvbin_t::unmapped    = spv::Id(-10000);
    246     const spv::Id spirvbin_t::unused      = spv::Id(-10001);
    247     const int     spirvbin_t::header_size = 5;
    248 
    249     spv::Id spirvbin_t::nextUnusedId(spv::Id id)
    250     {
    251         while (isNewIdMapped(id))  // search for an unused ID
    252             ++id;
    253 
    254         return id;
    255     }
    256 
    257     spv::Id spirvbin_t::localId(spv::Id id, spv::Id newId)
    258     {
    259         //assert(id != spv::NoResult && newId != spv::NoResult);
    260 
    261         if (id > bound()) {
    262             error(std::string("ID out of range: ") + std::to_string(id));
    263             return spirvbin_t::unused;
    264         }
    265 
    266         if (id >= idMapL.size())
    267             idMapL.resize(id+1, unused);
    268 
    269         if (newId != unmapped && newId != unused) {
    270             if (isOldIdUnused(id)) {
    271                 error(std::string("ID unused in module: ") + std::to_string(id));
    272                 return spirvbin_t::unused;
    273             }
    274 
    275             if (!isOldIdUnmapped(id)) {
    276                 error(std::string("ID already mapped: ") + std::to_string(id) + " -> "
    277                         + std::to_string(localId(id)));
    278 
    279                 return spirvbin_t::unused;
    280             }
    281 
    282             if (isNewIdMapped(newId)) {
    283                 error(std::string("ID already used in module: ") + std::to_string(newId));
    284                 return spirvbin_t::unused;
    285             }
    286 
    287             msg(4, 4, std::string("map: ") + std::to_string(id) + " -> " + std::to_string(newId));
    288             setMapped(newId);
    289             largestNewId = std::max(largestNewId, newId);
    290         }
    291 
    292         return idMapL[id] = newId;
    293     }
    294 
    295     // Parse a literal string from the SPIR binary and return it as an std::string
    296     // Due to C++11 RValue references, this doesn't copy the result string.
    297     std::string spirvbin_t::literalString(unsigned word) const
    298     {
    299         std::string literal;
    300 
    301         literal.reserve(16);
    302 
    303         const char* bytes = reinterpret_cast<const char*>(spv.data() + word);
    304 
    305         while (bytes && *bytes)
    306             literal += *bytes++;
    307 
    308         return literal;
    309     }
    310 
    311     void spirvbin_t::applyMap()
    312     {
    313         msg(3, 2, std::string("Applying map: "));
    314 
    315         // Map local IDs through the ID map
    316         process(inst_fn_nop, // ignore instructions
    317             [this](spv::Id& id) {
    318                 id = localId(id);
    319 
    320                 if (errorLatch)
    321                     return;
    322 
    323                 assert(id != unused && id != unmapped);
    324             }
    325         );
    326     }
    327 
    328     // Find free IDs for anything we haven't mapped
    329     void spirvbin_t::mapRemainder()
    330     {
    331         msg(3, 2, std::string("Remapping remainder: "));
    332 
    333         spv::Id     unusedId  = 1;  // can't use 0: that's NoResult
    334         spirword_t  maxBound  = 0;
    335 
    336         for (spv::Id id = 0; id < idMapL.size(); ++id) {
    337             if (isOldIdUnused(id))
    338                 continue;
    339 
    340             // Find a new mapping for any used but unmapped IDs
    341             if (isOldIdUnmapped(id)) {
    342                 localId(id, unusedId = nextUnusedId(unusedId));
    343                 if (errorLatch)
    344                     return;
    345             }
    346 
    347             if (isOldIdUnmapped(id)) {
    348                 error(std::string("old ID not mapped: ") + std::to_string(id));
    349                 return;
    350             }
    351 
    352             // Track max bound
    353             maxBound = std::max(maxBound, localId(id) + 1);
    354 
    355             if (errorLatch)
    356                 return;
    357         }
    358 
    359         bound(maxBound); // reset header ID bound to as big as it now needs to be
    360     }
    361 
    362     // Mark debug instructions for stripping
    363     void spirvbin_t::stripDebug()
    364     {
    365         // Strip instructions in the stripOp set: debug info.
    366         process(
    367             [&](spv::Op opCode, unsigned start) {
    368                 // remember opcodes we want to strip later
    369                 if (isStripOp(opCode))
    370                     stripInst(start);
    371                 return true;
    372             },
    373             op_fn_nop);
    374     }
    375 
    376     // Mark instructions that refer to now-removed IDs for stripping
    377     void spirvbin_t::stripDeadRefs()
    378     {
    379         process(
    380             [&](spv::Op opCode, unsigned start) {
    381                 // strip opcodes pointing to removed data
    382                 switch (opCode) {
    383                 case spv::OpName:
    384                 case spv::OpMemberName:
    385                 case spv::OpDecorate:
    386                 case spv::OpMemberDecorate:
    387                     if (idPosR.find(asId(start+1)) == idPosR.end())
    388                         stripInst(start);
    389                     break;
    390                 default:
    391                     break; // leave it alone
    392                 }
    393 
    394                 return true;
    395             },
    396             op_fn_nop);
    397 
    398         strip();
    399     }
    400 
    401     // Update local maps of ID, type, etc positions
    402     void spirvbin_t::buildLocalMaps()
    403     {
    404         msg(2, 2, std::string("build local maps: "));
    405 
    406         mapped.clear();
    407         idMapL.clear();
    408 //      preserve nameMap, so we don't clear that.
    409         fnPos.clear();
    410         fnCalls.clear();
    411         typeConstPos.clear();
    412         idPosR.clear();
    413         entryPoint = spv::NoResult;
    414         largestNewId = 0;
    415 
    416         idMapL.resize(bound(), unused);
    417 
    418         int         fnStart = 0;
    419         spv::Id     fnRes   = spv::NoResult;
    420 
    421         // build local Id and name maps
    422         process(
    423             [&](spv::Op opCode, unsigned start) {
    424                 unsigned word = start+1;
    425                 spv::Id  typeId = spv::NoResult;
    426 
    427                 if (spv::InstructionDesc[opCode].hasType())
    428                     typeId = asId(word++);
    429 
    430                 // If there's a result ID, remember the size of its type
    431                 if (spv::InstructionDesc[opCode].hasResult()) {
    432                     const spv::Id resultId = asId(word++);
    433                     idPosR[resultId] = start;
    434 
    435                     if (typeId != spv::NoResult) {
    436                         const unsigned idTypeSize = typeSizeInWords(typeId);
    437 
    438                         if (errorLatch)
    439                             return false;
    440 
    441                         if (idTypeSize != 0)
    442                             idTypeSizeMap[resultId] = idTypeSize;
    443                     }
    444                 }
    445 
    446                 if (opCode == spv::Op::OpName) {
    447                     const spv::Id    target = asId(start+1);
    448                     const std::string  name = literalString(start+2);
    449                     nameMap[name] = target;
    450 
    451                 } else if (opCode == spv::Op::OpFunctionCall) {
    452                     ++fnCalls[asId(start + 3)];
    453                 } else if (opCode == spv::Op::OpEntryPoint) {
    454                     entryPoint = asId(start + 2);
    455                 } else if (opCode == spv::Op::OpFunction) {
    456                     if (fnStart != 0) {
    457                         error("nested function found");
    458                         return false;
    459                     }
    460 
    461                     fnStart = start;
    462                     fnRes   = asId(start + 2);
    463                 } else if (opCode == spv::Op::OpFunctionEnd) {
    464                     assert(fnRes != spv::NoResult);
    465                     if (fnStart == 0) {
    466                         error("function end without function start");
    467                         return false;
    468                     }
    469 
    470                     fnPos[fnRes] = range_t(fnStart, start + asWordCount(start));
    471                     fnStart = 0;
    472                 } else if (isConstOp(opCode)) {
    473                     if (errorLatch)
    474                         return false;
    475 
    476                     assert(asId(start + 2) != spv::NoResult);
    477                     typeConstPos.insert(start);
    478                 } else if (isTypeOp(opCode)) {
    479                     assert(asId(start + 1) != spv::NoResult);
    480                     typeConstPos.insert(start);
    481                 }
    482 
    483                 return false;
    484             },
    485 
    486             [this](spv::Id& id) { localId(id, unmapped); }
    487         );
    488     }
    489 
    490     // Validate the SPIR header
    491     void spirvbin_t::validate() const
    492     {
    493         msg(2, 2, std::string("validating: "));
    494 
    495         if (spv.size() < header_size) {
    496             error("file too short: ");
    497             return;
    498         }
    499 
    500         if (magic() != spv::MagicNumber) {
    501             error("bad magic number");
    502             return;
    503         }
    504 
    505         // field 1 = version
    506         // field 2 = generator magic
    507         // field 3 = result <id> bound
    508 
    509         if (schemaNum() != 0) {
    510             error("bad schema, must be 0");
    511             return;
    512         }
    513     }
    514 
    515     int spirvbin_t::processInstruction(unsigned word, instfn_t instFn, idfn_t idFn)
    516     {
    517         const auto     instructionStart = word;
    518         const unsigned wordCount = asWordCount(instructionStart);
    519         const int      nextInst  = word++ + wordCount;
    520         spv::Op  opCode    = asOpCode(instructionStart);
    521 
    522         if (nextInst > int(spv.size())) {
    523             error("spir instruction terminated too early");
    524             return -1;
    525         }
    526 
    527         // Base for computing number of operands; will be updated as more is learned
    528         unsigned numOperands = wordCount - 1;
    529 
    530         if (instFn(opCode, instructionStart))
    531             return nextInst;
    532 
    533         // Read type and result ID from instruction desc table
    534         if (spv::InstructionDesc[opCode].hasType()) {
    535             idFn(asId(word++));
    536             --numOperands;
    537         }
    538 
    539         if (spv::InstructionDesc[opCode].hasResult()) {
    540             idFn(asId(word++));
    541             --numOperands;
    542         }
    543 
    544         // Extended instructions: currently, assume everything is an ID.
    545         // TODO: add whatever data we need for exceptions to that
    546         if (opCode == spv::OpExtInst) {
    547             word        += 2; // instruction set, and instruction from set
    548             numOperands -= 2;
    549 
    550             for (unsigned op=0; op < numOperands; ++op)
    551                 idFn(asId(word++)); // ID
    552 
    553             return nextInst;
    554         }
    555 
    556         // Circular buffer so we can look back at previous unmapped values during the mapping pass.
    557         static const unsigned idBufferSize = 4;
    558         spv::Id idBuffer[idBufferSize];
    559         unsigned idBufferPos = 0;
    560 
    561         // Store IDs from instruction in our map
    562         for (int op = 0; numOperands > 0; ++op, --numOperands) {
    563             // SpecConstantOp is special: it includes the operands of another opcode which is
    564             // given as a literal in the 3rd word.  We will switch over to pretending that the
    565             // opcode being processed is the literal opcode value of the SpecConstantOp.  See the
    566             // SPIRV spec for details.  This way we will handle IDs and literals as appropriate for
    567             // the embedded op.
    568             if (opCode == spv::OpSpecConstantOp) {
    569                 if (op == 0) {
    570                     opCode = asOpCode(word++);  // this is the opcode embedded in the SpecConstantOp.
    571                     --numOperands;
    572                 }
    573             }
    574 
    575             switch (spv::InstructionDesc[opCode].operands.getClass(op)) {
    576             case spv::OperandId:
    577             case spv::OperandScope:
    578             case spv::OperandMemorySemantics:
    579                 idBuffer[idBufferPos] = asId(word);
    580                 idBufferPos = (idBufferPos + 1) % idBufferSize;
    581                 idFn(asId(word++));
    582                 break;
    583 
    584             case spv::OperandVariableIds:
    585                 for (unsigned i = 0; i < numOperands; ++i)
    586                     idFn(asId(word++));
    587                 return nextInst;
    588 
    589             case spv::OperandVariableLiterals:
    590                 // for clarity
    591                 // if (opCode == spv::OpDecorate && asDecoration(word - 1) == spv::DecorationBuiltIn) {
    592                 //     ++word;
    593                 //     --numOperands;
    594                 // }
    595                 // word += numOperands;
    596                 return nextInst;
    597 
    598             case spv::OperandVariableLiteralId: {
    599                 if (opCode == OpSwitch) {
    600                     // word-2 is the position of the selector ID.  OpSwitch Literals match its type.
    601                     // In case the IDs are currently being remapped, we get the word[-2] ID from
    602                     // the circular idBuffer.
    603                     const unsigned literalSizePos = (idBufferPos+idBufferSize-2) % idBufferSize;
    604                     const unsigned literalSize = idTypeSizeInWords(idBuffer[literalSizePos]);
    605                     const unsigned numLiteralIdPairs = (nextInst-word) / (1+literalSize);
    606 
    607                     if (errorLatch)
    608                         return -1;
    609 
    610                     for (unsigned arg=0; arg<numLiteralIdPairs; ++arg) {
    611                         word += literalSize;  // literal
    612                         idFn(asId(word++));   // label
    613                     }
    614                 } else {
    615                     assert(0); // currentely, only OpSwitch uses OperandVariableLiteralId
    616                 }
    617 
    618                 return nextInst;
    619             }
    620 
    621             case spv::OperandLiteralString: {
    622                 const int stringWordCount = literalStringWords(literalString(word));
    623                 word += stringWordCount;
    624                 numOperands -= (stringWordCount-1); // -1 because for() header post-decrements
    625                 break;
    626             }
    627 
    628             // Execution mode might have extra literal operands.  Skip them.
    629             case spv::OperandExecutionMode:
    630                 return nextInst;
    631 
    632             // Single word operands we simply ignore, as they hold no IDs
    633             case spv::OperandLiteralNumber:
    634             case spv::OperandSource:
    635             case spv::OperandExecutionModel:
    636             case spv::OperandAddressing:
    637             case spv::OperandMemory:
    638             case spv::OperandStorage:
    639             case spv::OperandDimensionality:
    640             case spv::OperandSamplerAddressingMode:
    641             case spv::OperandSamplerFilterMode:
    642             case spv::OperandSamplerImageFormat:
    643             case spv::OperandImageChannelOrder:
    644             case spv::OperandImageChannelDataType:
    645             case spv::OperandImageOperands:
    646             case spv::OperandFPFastMath:
    647             case spv::OperandFPRoundingMode:
    648             case spv::OperandLinkageType:
    649             case spv::OperandAccessQualifier:
    650             case spv::OperandFuncParamAttr:
    651             case spv::OperandDecoration:
    652             case spv::OperandBuiltIn:
    653             case spv::OperandSelect:
    654             case spv::OperandLoop:
    655             case spv::OperandFunction:
    656             case spv::OperandMemoryAccess:
    657             case spv::OperandGroupOperation:
    658             case spv::OperandKernelEnqueueFlags:
    659             case spv::OperandKernelProfilingInfo:
    660             case spv::OperandCapability:
    661                 ++word;
    662                 break;
    663 
    664             default:
    665                 assert(0 && "Unhandled Operand Class");
    666                 break;
    667             }
    668         }
    669 
    670         return nextInst;
    671     }
    672 
    673     // Make a pass over all the instructions and process them given appropriate functions
    674     spirvbin_t& spirvbin_t::process(instfn_t instFn, idfn_t idFn, unsigned begin, unsigned end)
    675     {
    676         // For efficiency, reserve name map space.  It can grow if needed.
    677         nameMap.reserve(32);
    678 
    679         // If begin or end == 0, use defaults
    680         begin = (begin == 0 ? header_size          : begin);
    681         end   = (end   == 0 ? unsigned(spv.size()) : end);
    682 
    683         // basic parsing and InstructionDesc table borrowed from SpvDisassemble.cpp...
    684         unsigned nextInst = unsigned(spv.size());
    685 
    686         for (unsigned word = begin; word < end; word = nextInst) {
    687             nextInst = processInstruction(word, instFn, idFn);
    688 
    689             if (errorLatch)
    690                 return *this;
    691         }
    692 
    693         return *this;
    694     }
    695 
    696     // Apply global name mapping to a single module
    697     void spirvbin_t::mapNames()
    698     {
    699         static const std::uint32_t softTypeIdLimit = 3011;  // small prime.  TODO: get from options
    700         static const std::uint32_t firstMappedID   = 3019;  // offset into ID space
    701 
    702         for (const auto& name : nameMap) {
    703             std::uint32_t hashval = 1911;
    704             for (const char c : name.first)
    705                 hashval = hashval * 1009 + c;
    706 
    707             if (isOldIdUnmapped(name.second)) {
    708                 localId(name.second, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
    709                 if (errorLatch)
    710                     return;
    711             }
    712         }
    713     }
    714 
    715     // Map fn contents to IDs of similar functions in other modules
    716     void spirvbin_t::mapFnBodies()
    717     {
    718         static const std::uint32_t softTypeIdLimit = 19071;  // small prime.  TODO: get from options
    719         static const std::uint32_t firstMappedID   =  6203;  // offset into ID space
    720 
    721         // Initial approach: go through some high priority opcodes first and assign them
    722         // hash values.
    723 
    724         spv::Id               fnId       = spv::NoResult;
    725         std::vector<unsigned> instPos;
    726         instPos.reserve(unsigned(spv.size()) / 16); // initial estimate; can grow if needed.
    727 
    728         // Build local table of instruction start positions
    729         process(
    730             [&](spv::Op, unsigned start) { instPos.push_back(start); return true; },
    731             op_fn_nop);
    732 
    733         if (errorLatch)
    734             return;
    735 
    736         // Window size for context-sensitive canonicalization values
    737         // Empirical best size from a single data set.  TODO: Would be a good tunable.
    738         // We essentially perform a little convolution around each instruction,
    739         // to capture the flavor of nearby code, to hopefully match to similar
    740         // code in other modules.
    741         static const unsigned windowSize = 2;
    742 
    743         for (unsigned entry = 0; entry < unsigned(instPos.size()); ++entry) {
    744             const unsigned start  = instPos[entry];
    745             const spv::Op  opCode = asOpCode(start);
    746 
    747             if (opCode == spv::OpFunction)
    748                 fnId   = asId(start + 2);
    749 
    750             if (opCode == spv::OpFunctionEnd)
    751                 fnId = spv::NoResult;
    752 
    753             if (fnId != spv::NoResult) { // if inside a function
    754                 if (spv::InstructionDesc[opCode].hasResult()) {
    755                     const unsigned word    = start + (spv::InstructionDesc[opCode].hasType() ? 2 : 1);
    756                     const spv::Id  resId   = asId(word);
    757                     std::uint32_t  hashval = fnId * 17; // small prime
    758 
    759                     for (unsigned i = entry-1; i >= entry-windowSize; --i) {
    760                         if (asOpCode(instPos[i]) == spv::OpFunction)
    761                             break;
    762                         hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime
    763                     }
    764 
    765                     for (unsigned i = entry; i <= entry + windowSize; ++i) {
    766                         if (asOpCode(instPos[i]) == spv::OpFunctionEnd)
    767                             break;
    768                         hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime
    769                     }
    770 
    771                     if (isOldIdUnmapped(resId)) {
    772                         localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
    773                         if (errorLatch)
    774                             return;
    775                     }
    776 
    777                 }
    778             }
    779         }
    780 
    781         spv::Op          thisOpCode(spv::OpNop);
    782         std::unordered_map<int, int> opCounter;
    783         int              idCounter(0);
    784         fnId = spv::NoResult;
    785 
    786         process(
    787             [&](spv::Op opCode, unsigned start) {
    788                 switch (opCode) {
    789                 case spv::OpFunction:
    790                     // Reset counters at each function
    791                     idCounter = 0;
    792                     opCounter.clear();
    793                     fnId = asId(start + 2);
    794                     break;
    795 
    796                 case spv::OpImageSampleImplicitLod:
    797                 case spv::OpImageSampleExplicitLod:
    798                 case spv::OpImageSampleDrefImplicitLod:
    799                 case spv::OpImageSampleDrefExplicitLod:
    800                 case spv::OpImageSampleProjImplicitLod:
    801                 case spv::OpImageSampleProjExplicitLod:
    802                 case spv::OpImageSampleProjDrefImplicitLod:
    803                 case spv::OpImageSampleProjDrefExplicitLod:
    804                 case spv::OpDot:
    805                 case spv::OpCompositeExtract:
    806                 case spv::OpCompositeInsert:
    807                 case spv::OpVectorShuffle:
    808                 case spv::OpLabel:
    809                 case spv::OpVariable:
    810 
    811                 case spv::OpAccessChain:
    812                 case spv::OpLoad:
    813                 case spv::OpStore:
    814                 case spv::OpCompositeConstruct:
    815                 case spv::OpFunctionCall:
    816                     ++opCounter[opCode];
    817                     idCounter = 0;
    818                     thisOpCode = opCode;
    819                     break;
    820                 default:
    821                     thisOpCode = spv::OpNop;
    822                 }
    823 
    824                 return false;
    825             },
    826 
    827             [&](spv::Id& id) {
    828                 if (thisOpCode != spv::OpNop) {
    829                     ++idCounter;
    830                     const std::uint32_t hashval = opCounter[thisOpCode] * thisOpCode * 50047 + idCounter + fnId * 117;
    831 
    832                     if (isOldIdUnmapped(id))
    833                         localId(id, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
    834                 }
    835             });
    836     }
    837 
    838     // EXPERIMENTAL: forward IO and uniform load/stores into operands
    839     // This produces invalid Schema-0 SPIRV
    840     void spirvbin_t::forwardLoadStores()
    841     {
    842         idset_t fnLocalVars; // set of function local vars
    843         idmap_t idMap;       // Map of load result IDs to what they load
    844 
    845         // EXPERIMENTAL: Forward input and access chain loads into consumptions
    846         process(
    847             [&](spv::Op opCode, unsigned start) {
    848                 // Add inputs and uniforms to the map
    849                 if ((opCode == spv::OpVariable && asWordCount(start) == 4) &&
    850                     (spv[start+3] == spv::StorageClassUniform ||
    851                     spv[start+3] == spv::StorageClassUniformConstant ||
    852                     spv[start+3] == spv::StorageClassInput))
    853                     fnLocalVars.insert(asId(start+2));
    854 
    855                 if (opCode == spv::OpAccessChain && fnLocalVars.count(asId(start+3)) > 0)
    856                     fnLocalVars.insert(asId(start+2));
    857 
    858                 if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) {
    859                     idMap[asId(start+2)] = asId(start+3);
    860                     stripInst(start);
    861                 }
    862 
    863                 return false;
    864             },
    865 
    866             [&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; }
    867         );
    868 
    869         if (errorLatch)
    870             return;
    871 
    872         // EXPERIMENTAL: Implicit output stores
    873         fnLocalVars.clear();
    874         idMap.clear();
    875 
    876         process(
    877             [&](spv::Op opCode, unsigned start) {
    878                 // Add inputs and uniforms to the map
    879                 if ((opCode == spv::OpVariable && asWordCount(start) == 4) &&
    880                     (spv[start+3] == spv::StorageClassOutput))
    881                     fnLocalVars.insert(asId(start+2));
    882 
    883                 if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) {
    884                     idMap[asId(start+2)] = asId(start+1);
    885                     stripInst(start);
    886                 }
    887 
    888                 return false;
    889             },
    890             op_fn_nop);
    891 
    892         if (errorLatch)
    893             return;
    894 
    895         process(
    896             inst_fn_nop,
    897             [&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; }
    898         );
    899 
    900         if (errorLatch)
    901             return;
    902 
    903         strip();          // strip out data we decided to eliminate
    904     }
    905 
    906     // optimize loads and stores
    907     void spirvbin_t::optLoadStore()
    908     {
    909         idset_t    fnLocalVars;  // candidates for removal (only locals)
    910         idmap_t    idMap;        // Map of load result IDs to what they load
    911         blockmap_t blockMap;     // Map of IDs to blocks they first appear in
    912         int        blockNum = 0; // block count, to avoid crossing flow control
    913 
    914         // Find all the function local pointers stored at most once, and not via access chains
    915         process(
    916             [&](spv::Op opCode, unsigned start) {
    917                 const int wordCount = asWordCount(start);
    918 
    919                 // Count blocks, so we can avoid crossing flow control
    920                 if (isFlowCtrl(opCode))
    921                     ++blockNum;
    922 
    923                 // Add local variables to the map
    924                 if ((opCode == spv::OpVariable && spv[start+3] == spv::StorageClassFunction && asWordCount(start) == 4)) {
    925                     fnLocalVars.insert(asId(start+2));
    926                     return true;
    927                 }
    928 
    929                 // Ignore process vars referenced via access chain
    930                 if ((opCode == spv::OpAccessChain || opCode == spv::OpInBoundsAccessChain) && fnLocalVars.count(asId(start+3)) > 0) {
    931                     fnLocalVars.erase(asId(start+3));
    932                     idMap.erase(asId(start+3));
    933                     return true;
    934                 }
    935 
    936                 if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) {
    937                     const spv::Id varId = asId(start+3);
    938 
    939                     // Avoid loads before stores
    940                     if (idMap.find(varId) == idMap.end()) {
    941                         fnLocalVars.erase(varId);
    942                         idMap.erase(varId);
    943                     }
    944 
    945                     // don't do for volatile references
    946                     if (wordCount > 4 && (spv[start+4] & spv::MemoryAccessVolatileMask)) {
    947                         fnLocalVars.erase(varId);
    948                         idMap.erase(varId);
    949                     }
    950 
    951                     // Handle flow control
    952                     if (blockMap.find(varId) == blockMap.end()) {
    953                         blockMap[varId] = blockNum;  // track block we found it in.
    954                     } else if (blockMap[varId] != blockNum) {
    955                         fnLocalVars.erase(varId);  // Ignore if crosses flow control
    956                         idMap.erase(varId);
    957                     }
    958 
    959                     return true;
    960                 }
    961 
    962                 if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) {
    963                     const spv::Id varId = asId(start+1);
    964 
    965                     if (idMap.find(varId) == idMap.end()) {
    966                         idMap[varId] = asId(start+2);
    967                     } else {
    968                         // Remove if it has more than one store to the same pointer
    969                         fnLocalVars.erase(varId);
    970                         idMap.erase(varId);
    971                     }
    972 
    973                     // don't do for volatile references
    974                     if (wordCount > 3 && (spv[start+3] & spv::MemoryAccessVolatileMask)) {
    975                         fnLocalVars.erase(asId(start+3));
    976                         idMap.erase(asId(start+3));
    977                     }
    978 
    979                     // Handle flow control
    980                     if (blockMap.find(varId) == blockMap.end()) {
    981                         blockMap[varId] = blockNum;  // track block we found it in.
    982                     } else if (blockMap[varId] != blockNum) {
    983                         fnLocalVars.erase(varId);  // Ignore if crosses flow control
    984                         idMap.erase(varId);
    985                     }
    986 
    987                     return true;
    988                 }
    989 
    990                 return false;
    991             },
    992 
    993             // If local var id used anywhere else, don't eliminate
    994             [&](spv::Id& id) {
    995                 if (fnLocalVars.count(id) > 0) {
    996                     fnLocalVars.erase(id);
    997                     idMap.erase(id);
    998                 }
    999             }
   1000         );
   1001 
   1002         if (errorLatch)
   1003             return;
   1004 
   1005         process(
   1006             [&](spv::Op opCode, unsigned start) {
   1007                 if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0)
   1008                     idMap[asId(start+2)] = idMap[asId(start+3)];
   1009                 return false;
   1010             },
   1011             op_fn_nop);
   1012 
   1013         if (errorLatch)
   1014             return;
   1015 
   1016         // Chase replacements to their origins, in case there is a chain such as:
   1017         //   2 = store 1
   1018         //   3 = load 2
   1019         //   4 = store 3
   1020         //   5 = load 4
   1021         // We want to replace uses of 5 with 1.
   1022         for (const auto& idPair : idMap) {
   1023             spv::Id id = idPair.first;
   1024             while (idMap.find(id) != idMap.end())  // Chase to end of chain
   1025                 id = idMap[id];
   1026 
   1027             idMap[idPair.first] = id;              // replace with final result
   1028         }
   1029 
   1030         // Remove the load/store/variables for the ones we've discovered
   1031         process(
   1032             [&](spv::Op opCode, unsigned start) {
   1033                 if ((opCode == spv::OpLoad  && fnLocalVars.count(asId(start+3)) > 0) ||
   1034                     (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) ||
   1035                     (opCode == spv::OpVariable && fnLocalVars.count(asId(start+2)) > 0)) {
   1036 
   1037                     stripInst(start);
   1038                     return true;
   1039                 }
   1040 
   1041                 return false;
   1042             },
   1043 
   1044             [&](spv::Id& id) {
   1045                 if (idMap.find(id) != idMap.end()) id = idMap[id];
   1046             }
   1047         );
   1048 
   1049         if (errorLatch)
   1050             return;
   1051 
   1052         strip();          // strip out data we decided to eliminate
   1053     }
   1054 
   1055     // remove bodies of uncalled functions
   1056     void spirvbin_t::dceFuncs()
   1057     {
   1058         msg(3, 2, std::string("Removing Dead Functions: "));
   1059 
   1060         // TODO: There are more efficient ways to do this.
   1061         bool changed = true;
   1062 
   1063         while (changed) {
   1064             changed = false;
   1065 
   1066             for (auto fn = fnPos.begin(); fn != fnPos.end(); ) {
   1067                 if (fn->first == entryPoint) { // don't DCE away the entry point!
   1068                     ++fn;
   1069                     continue;
   1070                 }
   1071 
   1072                 const auto call_it = fnCalls.find(fn->first);
   1073 
   1074                 if (call_it == fnCalls.end() || call_it->second == 0) {
   1075                     changed = true;
   1076                     stripRange.push_back(fn->second);
   1077 
   1078                     // decrease counts of called functions
   1079                     process(
   1080                         [&](spv::Op opCode, unsigned start) {
   1081                             if (opCode == spv::Op::OpFunctionCall) {
   1082                                 const auto call_it = fnCalls.find(asId(start + 3));
   1083                                 if (call_it != fnCalls.end()) {
   1084                                     if (--call_it->second <= 0)
   1085                                         fnCalls.erase(call_it);
   1086                                 }
   1087                             }
   1088 
   1089                             return true;
   1090                         },
   1091                         op_fn_nop,
   1092                         fn->second.first,
   1093                         fn->second.second);
   1094 
   1095                     if (errorLatch)
   1096                         return;
   1097 
   1098                     fn = fnPos.erase(fn);
   1099                 } else ++fn;
   1100             }
   1101         }
   1102     }
   1103 
   1104     // remove unused function variables + decorations
   1105     void spirvbin_t::dceVars()
   1106     {
   1107         msg(3, 2, std::string("DCE Vars: "));
   1108 
   1109         std::unordered_map<spv::Id, int> varUseCount;
   1110 
   1111         // Count function variable use
   1112         process(
   1113             [&](spv::Op opCode, unsigned start) {
   1114                 if (opCode == spv::OpVariable) {
   1115                     ++varUseCount[asId(start+2)];
   1116                     return true;
   1117                 } else if (opCode == spv::OpEntryPoint) {
   1118                     const int wordCount = asWordCount(start);
   1119                     for (int i = 4; i < wordCount; i++) {
   1120                         ++varUseCount[asId(start+i)];
   1121                     }
   1122                     return true;
   1123                 } else
   1124                     return false;
   1125             },
   1126 
   1127             [&](spv::Id& id) { if (varUseCount[id]) ++varUseCount[id]; }
   1128         );
   1129 
   1130         if (errorLatch)
   1131             return;
   1132 
   1133         // Remove single-use function variables + associated decorations and names
   1134         process(
   1135             [&](spv::Op opCode, unsigned start) {
   1136                 spv::Id id = spv::NoResult;
   1137                 if (opCode == spv::OpVariable)
   1138                     id = asId(start+2);
   1139                 if (opCode == spv::OpDecorate || opCode == spv::OpName)
   1140                     id = asId(start+1);
   1141 
   1142                 if (id != spv::NoResult && varUseCount[id] == 1)
   1143                     stripInst(start);
   1144 
   1145                 return true;
   1146             },
   1147             op_fn_nop);
   1148     }
   1149 
   1150     // remove unused types
   1151     void spirvbin_t::dceTypes()
   1152     {
   1153         std::vector<bool> isType(bound(), false);
   1154 
   1155         // for speed, make O(1) way to get to type query (map is log(n))
   1156         for (const auto typeStart : typeConstPos)
   1157             isType[asTypeConstId(typeStart)] = true;
   1158 
   1159         std::unordered_map<spv::Id, int> typeUseCount;
   1160 
   1161         // This is not the most efficient algorithm, but this is an offline tool, and
   1162         // it's easy to write this way.  Can be improved opportunistically if needed.
   1163         bool changed = true;
   1164         while (changed) {
   1165             changed = false;
   1166             strip();
   1167             typeUseCount.clear();
   1168 
   1169             // Count total type usage
   1170             process(inst_fn_nop,
   1171                     [&](spv::Id& id) { if (isType[id]) ++typeUseCount[id]; }
   1172                     );
   1173 
   1174             if (errorLatch)
   1175                 return;
   1176 
   1177             // Remove single reference types
   1178             for (const auto typeStart : typeConstPos) {
   1179                 const spv::Id typeId = asTypeConstId(typeStart);
   1180                 if (typeUseCount[typeId] == 1) {
   1181                     changed = true;
   1182                     --typeUseCount[typeId];
   1183                     stripInst(typeStart);
   1184                 }
   1185             }
   1186 
   1187             if (errorLatch)
   1188                 return;
   1189         }
   1190     }
   1191 
   1192 #ifdef NOTDEF
   1193     bool spirvbin_t::matchType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const
   1194     {
   1195         // Find the local type id "lt" and global type id "gt"
   1196         const auto lt_it = typeConstPosR.find(lt);
   1197         if (lt_it == typeConstPosR.end())
   1198             return false;
   1199 
   1200         const auto typeStart = lt_it->second;
   1201 
   1202         // Search for entry in global table
   1203         const auto gtype = globalTypes.find(gt);
   1204         if (gtype == globalTypes.end())
   1205             return false;
   1206 
   1207         const auto& gdata = gtype->second;
   1208 
   1209         // local wordcount and opcode
   1210         const int     wordCount   = asWordCount(typeStart);
   1211         const spv::Op opCode      = asOpCode(typeStart);
   1212 
   1213         // no type match if opcodes don't match, or operand count doesn't match
   1214         if (opCode != opOpCode(gdata[0]) || wordCount != opWordCount(gdata[0]))
   1215             return false;
   1216 
   1217         const unsigned numOperands = wordCount - 2; // all types have a result
   1218 
   1219         const auto cmpIdRange = [&](range_t range) {
   1220             for (int x=range.first; x<std::min(range.second, wordCount); ++x)
   1221                 if (!matchType(globalTypes, asId(typeStart+x), gdata[x]))
   1222                     return false;
   1223             return true;
   1224         };
   1225 
   1226         const auto cmpConst   = [&]() { return cmpIdRange(constRange(opCode)); };
   1227         const auto cmpSubType = [&]() { return cmpIdRange(typeRange(opCode));  };
   1228 
   1229         // Compare literals in range [start,end)
   1230         const auto cmpLiteral = [&]() {
   1231             const auto range = literalRange(opCode);
   1232             return std::equal(spir.begin() + typeStart + range.first,
   1233                 spir.begin() + typeStart + std::min(range.second, wordCount),
   1234                 gdata.begin() + range.first);
   1235         };
   1236 
   1237         assert(isTypeOp(opCode) || isConstOp(opCode));
   1238 
   1239         switch (opCode) {
   1240         case spv::OpTypeOpaque:       // TODO: disable until we compare the literal strings.
   1241         case spv::OpTypeQueue:        return false;
   1242         case spv::OpTypeEvent:        // fall through...
   1243         case spv::OpTypeDeviceEvent:  // ...
   1244         case spv::OpTypeReserveId:    return false;
   1245             // for samplers, we don't handle the optional parameters yet
   1246         case spv::OpTypeSampler:      return cmpLiteral() && cmpConst() && cmpSubType() && wordCount == 8;
   1247         default:                      return cmpLiteral() && cmpConst() && cmpSubType();
   1248         }
   1249     }
   1250 
   1251     // Look for an equivalent type in the globalTypes map
   1252     spv::Id spirvbin_t::findType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt) const
   1253     {
   1254         // Try a recursive type match on each in turn, and return a match if we find one
   1255         for (const auto& gt : globalTypes)
   1256             if (matchType(globalTypes, lt, gt.first))
   1257                 return gt.first;
   1258 
   1259         return spv::NoType;
   1260     }
   1261 #endif // NOTDEF
   1262 
   1263     // Return start position in SPV of given Id.  error if not found.
   1264     unsigned spirvbin_t::idPos(spv::Id id) const
   1265     {
   1266         const auto tid_it = idPosR.find(id);
   1267         if (tid_it == idPosR.end()) {
   1268             error("ID not found");
   1269             return 0;
   1270         }
   1271 
   1272         return tid_it->second;
   1273     }
   1274 
   1275     // Hash types to canonical values.  This can return ID collisions (it's a bit
   1276     // inevitable): it's up to the caller to handle that gracefully.
   1277     std::uint32_t spirvbin_t::hashType(unsigned typeStart) const
   1278     {
   1279         const unsigned wordCount   = asWordCount(typeStart);
   1280         const spv::Op  opCode      = asOpCode(typeStart);
   1281 
   1282         switch (opCode) {
   1283         case spv::OpTypeVoid:         return 0;
   1284         case spv::OpTypeBool:         return 1;
   1285         case spv::OpTypeInt:          return 3 + (spv[typeStart+3]);
   1286         case spv::OpTypeFloat:        return 5;
   1287         case spv::OpTypeVector:
   1288             return 6 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1);
   1289         case spv::OpTypeMatrix:
   1290             return 30 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1);
   1291         case spv::OpTypeImage:
   1292             return 120 + hashType(idPos(spv[typeStart+2])) +
   1293                 spv[typeStart+3] +            // dimensionality
   1294                 spv[typeStart+4] * 8 * 16 +   // depth
   1295                 spv[typeStart+5] * 4 * 16 +   // arrayed
   1296                 spv[typeStart+6] * 2 * 16 +   // multisampled
   1297                 spv[typeStart+7] * 1 * 16;    // format
   1298         case spv::OpTypeSampler:
   1299             return 500;
   1300         case spv::OpTypeSampledImage:
   1301             return 502;
   1302         case spv::OpTypeArray:
   1303             return 501 + hashType(idPos(spv[typeStart+2])) * spv[typeStart+3];
   1304         case spv::OpTypeRuntimeArray:
   1305             return 5000  + hashType(idPos(spv[typeStart+2]));
   1306         case spv::OpTypeStruct:
   1307             {
   1308                 std::uint32_t hash = 10000;
   1309                 for (unsigned w=2; w < wordCount; ++w)
   1310                     hash += w * hashType(idPos(spv[typeStart+w]));
   1311                 return hash;
   1312             }
   1313 
   1314         case spv::OpTypeOpaque:         return 6000 + spv[typeStart+2];
   1315         case spv::OpTypePointer:        return 100000  + hashType(idPos(spv[typeStart+3]));
   1316         case spv::OpTypeFunction:
   1317             {
   1318                 std::uint32_t hash = 200000;
   1319                 for (unsigned w=2; w < wordCount; ++w)
   1320                     hash += w * hashType(idPos(spv[typeStart+w]));
   1321                 return hash;
   1322             }
   1323 
   1324         case spv::OpTypeEvent:           return 300000;
   1325         case spv::OpTypeDeviceEvent:     return 300001;
   1326         case spv::OpTypeReserveId:       return 300002;
   1327         case spv::OpTypeQueue:           return 300003;
   1328         case spv::OpTypePipe:            return 300004;
   1329         case spv::OpConstantTrue:        return 300007;
   1330         case spv::OpConstantFalse:       return 300008;
   1331         case spv::OpConstantComposite:
   1332             {
   1333                 std::uint32_t hash = 300011 + hashType(idPos(spv[typeStart+1]));
   1334                 for (unsigned w=3; w < wordCount; ++w)
   1335                     hash += w * hashType(idPos(spv[typeStart+w]));
   1336                 return hash;
   1337             }
   1338         case spv::OpConstant:
   1339             {
   1340                 std::uint32_t hash = 400011 + hashType(idPos(spv[typeStart+1]));
   1341                 for (unsigned w=3; w < wordCount; ++w)
   1342                     hash += w * spv[typeStart+w];
   1343                 return hash;
   1344             }
   1345         case spv::OpConstantNull:
   1346             {
   1347                 std::uint32_t hash = 500009 + hashType(idPos(spv[typeStart+1]));
   1348                 return hash;
   1349             }
   1350         case spv::OpConstantSampler:
   1351             {
   1352                 std::uint32_t hash = 600011 + hashType(idPos(spv[typeStart+1]));
   1353                 for (unsigned w=3; w < wordCount; ++w)
   1354                     hash += w * spv[typeStart+w];
   1355                 return hash;
   1356             }
   1357 
   1358         default:
   1359             error("unknown type opcode");
   1360             return 0;
   1361         }
   1362     }
   1363 
   1364     void spirvbin_t::mapTypeConst()
   1365     {
   1366         globaltypes_t globalTypeMap;
   1367 
   1368         msg(3, 2, std::string("Remapping Consts & Types: "));
   1369 
   1370         static const std::uint32_t softTypeIdLimit = 3011; // small prime.  TODO: get from options
   1371         static const std::uint32_t firstMappedID   = 8;    // offset into ID space
   1372 
   1373         for (auto& typeStart : typeConstPos) {
   1374             const spv::Id       resId     = asTypeConstId(typeStart);
   1375             const std::uint32_t hashval   = hashType(typeStart);
   1376 
   1377             if (errorLatch)
   1378                 return;
   1379 
   1380             if (isOldIdUnmapped(resId)) {
   1381                 localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
   1382                 if (errorLatch)
   1383                     return;
   1384             }
   1385         }
   1386     }
   1387 
   1388     // Strip a single binary by removing ranges given in stripRange
   1389     void spirvbin_t::strip()
   1390     {
   1391         if (stripRange.empty()) // nothing to do
   1392             return;
   1393 
   1394         // Sort strip ranges in order of traversal
   1395         std::sort(stripRange.begin(), stripRange.end());
   1396 
   1397         // Allocate a new binary big enough to hold old binary
   1398         // We'll step this iterator through the strip ranges as we go through the binary
   1399         auto strip_it = stripRange.begin();
   1400 
   1401         int strippedPos = 0;
   1402         for (unsigned word = 0; word < unsigned(spv.size()); ++word) {
   1403             while (strip_it != stripRange.end() && word >= strip_it->second)
   1404                 ++strip_it;
   1405 
   1406             if (strip_it == stripRange.end() || word < strip_it->first || word >= strip_it->second)
   1407                 spv[strippedPos++] = spv[word];
   1408         }
   1409 
   1410         spv.resize(strippedPos);
   1411         stripRange.clear();
   1412 
   1413         buildLocalMaps();
   1414     }
   1415 
   1416     // Strip a single binary by removing ranges given in stripRange
   1417     void spirvbin_t::remap(std::uint32_t opts)
   1418     {
   1419         options = opts;
   1420 
   1421         // Set up opcode tables from SpvDoc
   1422         spv::Parameterize();
   1423 
   1424         validate();       // validate header
   1425         buildLocalMaps(); // build ID maps
   1426 
   1427         msg(3, 4, std::string("ID bound: ") + std::to_string(bound()));
   1428 
   1429         if (options & STRIP)         stripDebug();
   1430         if (errorLatch) return;
   1431 
   1432         strip();        // strip out data we decided to eliminate
   1433         if (errorLatch) return;
   1434 
   1435         if (options & OPT_LOADSTORE) optLoadStore();
   1436         if (errorLatch) return;
   1437 
   1438         if (options & OPT_FWD_LS)    forwardLoadStores();
   1439         if (errorLatch) return;
   1440 
   1441         if (options & DCE_FUNCS)     dceFuncs();
   1442         if (errorLatch) return;
   1443 
   1444         if (options & DCE_VARS)      dceVars();
   1445         if (errorLatch) return;
   1446 
   1447         if (options & DCE_TYPES)     dceTypes();
   1448         if (errorLatch) return;
   1449 
   1450         strip();         // strip out data we decided to eliminate
   1451         if (errorLatch) return;
   1452 
   1453         stripDeadRefs(); // remove references to things we DCEed
   1454         if (errorLatch) return;
   1455 
   1456         // after the last strip, we must clean any debug info referring to now-deleted data
   1457 
   1458         if (options & MAP_TYPES)     mapTypeConst();
   1459         if (errorLatch) return;
   1460 
   1461         if (options & MAP_NAMES)     mapNames();
   1462         if (errorLatch) return;
   1463 
   1464         if (options & MAP_FUNCS)     mapFnBodies();
   1465         if (errorLatch) return;
   1466 
   1467         if (options & MAP_ALL) {
   1468             mapRemainder(); // map any unmapped IDs
   1469             if (errorLatch) return;
   1470 
   1471             applyMap();     // Now remap each shader to the new IDs we've come up with
   1472             if (errorLatch) return;
   1473         }
   1474     }
   1475 
   1476     // remap from a memory image
   1477     void spirvbin_t::remap(std::vector<std::uint32_t>& in_spv, std::uint32_t opts)
   1478     {
   1479         spv.swap(in_spv);
   1480         remap(opts);
   1481         spv.swap(in_spv);
   1482     }
   1483 
   1484 } // namespace SPV
   1485 
   1486 #endif // defined (use_cpp11)
   1487 
   1488