Home | History | Annotate | Download | only in SPIRV
      1 //
      2 // Copyright (C) 2014-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 // Disassembler for SPIR-V.
     37 //
     38 
     39 #include <cstdlib>
     40 #include <cstring>
     41 #include <cassert>
     42 #include <iomanip>
     43 #include <stack>
     44 #include <sstream>
     45 #include <cstring>
     46 
     47 #include "disassemble.h"
     48 #include "doc.h"
     49 
     50 namespace spv {
     51     extern "C" {
     52         // Include C-based headers that don't have a namespace
     53         #include "GLSL.std.450.h"
     54 #ifdef AMD_EXTENSIONS
     55         #include "GLSL.ext.AMD.h"
     56 #endif
     57 #ifdef NV_EXTENSIONS
     58         #include "GLSL.ext.NV.h"
     59 #endif
     60     }
     61 }
     62 const char* GlslStd450DebugNames[spv::GLSLstd450Count];
     63 
     64 namespace spv {
     65 
     66 #ifdef AMD_EXTENSIONS
     67 static const char* GLSLextAMDGetDebugNames(const char*, unsigned);
     68 #endif
     69 
     70 #ifdef NV_EXTENSIONS
     71 static const char* GLSLextNVGetDebugNames(const char*, unsigned);
     72 #endif
     73 
     74 static void Kill(std::ostream& out, const char* message)
     75 {
     76     out << std::endl << "Disassembly failed: " << message << std::endl;
     77     exit(1);
     78 }
     79 
     80 // used to identify the extended instruction library imported when printing
     81 enum ExtInstSet {
     82     GLSL450Inst,
     83 #ifdef AMD_EXTENSIONS
     84     GLSLextAMDInst,
     85 #endif
     86 #ifdef NV_EXTENSIONS
     87     GLSLextNVInst,
     88 #endif
     89     OpenCLExtInst,
     90 };
     91 
     92 // Container class for a single instance of a SPIR-V stream, with methods for disassembly.
     93 class SpirvStream {
     94 public:
     95     SpirvStream(std::ostream& out, const std::vector<unsigned int>& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { }
     96     virtual ~SpirvStream() { }
     97 
     98     void validate();
     99     void processInstructions();
    100 
    101 protected:
    102     SpirvStream(const SpirvStream&);
    103     SpirvStream& operator=(const SpirvStream&);
    104     Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; }
    105 
    106     // Output methods
    107     void outputIndent();
    108     void formatId(Id id, std::stringstream&);
    109     void outputResultId(Id id);
    110     void outputTypeId(Id id);
    111     void outputId(Id id);
    112     void outputMask(OperandClass operandClass, unsigned mask);
    113     void disassembleImmediates(int numOperands);
    114     void disassembleIds(int numOperands);
    115     int disassembleString();
    116     void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands);
    117 
    118     // Data
    119     std::ostream& out;                       // where to write the disassembly
    120     const std::vector<unsigned int>& stream; // the actual word stream
    121     int size;                                // the size of the word stream
    122     int word;                                // the next word of the stream to read
    123 
    124     // map each <id> to the instruction that created it
    125     Id bound;
    126     std::vector<unsigned int> idInstruction;  // the word offset into the stream where the instruction for result [id] starts; 0 if not yet seen (forward reference or function parameter)
    127 
    128     std::vector<std::string> idDescriptor;    // the best text string known for explaining the <id>
    129 
    130     // schema
    131     unsigned int schema;
    132 
    133     // stack of structured-merge points
    134     std::stack<Id> nestedControl;
    135     Id nextNestedControl;         // need a slight delay for when we are nested
    136 };
    137 
    138 void SpirvStream::validate()
    139 {
    140     size = (int)stream.size();
    141     if (size < 4)
    142         Kill(out, "stream is too short");
    143 
    144     // Magic number
    145     if (stream[word++] != MagicNumber) {
    146         out << "Bad magic number";
    147         return;
    148     }
    149 
    150     // Version
    151     out << "// Module Version " << std::hex << stream[word++] << std::endl;
    152 
    153     // Generator's magic number
    154     out << "// Generated by (magic number): " << std::hex << stream[word++] << std::dec << std::endl;
    155 
    156     // Result <id> bound
    157     bound = stream[word++];
    158     idInstruction.resize(bound);
    159     idDescriptor.resize(bound);
    160     out << "// Id's are bound by " << bound << std::endl;
    161     out << std::endl;
    162 
    163     // Reserved schema, must be 0 for now
    164     schema = stream[word++];
    165     if (schema != 0)
    166         Kill(out, "bad schema, must be 0");
    167 }
    168 
    169 // Loop over all the instructions, in order, processing each.
    170 // Boiler plate for each is handled here directly, the rest is dispatched.
    171 void SpirvStream::processInstructions()
    172 {
    173     // Instructions
    174     while (word < size) {
    175         int instructionStart = word;
    176 
    177         // Instruction wordCount and opcode
    178         unsigned int firstWord = stream[word];
    179         unsigned wordCount = firstWord >> WordCountShift;
    180         Op opCode = (Op)(firstWord & OpCodeMask);
    181         int nextInst = word + wordCount;
    182         ++word;
    183 
    184         // Presence of full instruction
    185         if (nextInst > size)
    186             Kill(out, "stream instruction terminated too early");
    187 
    188         // Base for computing number of operands; will be updated as more is learned
    189         unsigned numOperands = wordCount - 1;
    190 
    191         // Type <id>
    192         Id typeId = 0;
    193         if (InstructionDesc[opCode].hasType()) {
    194             typeId = stream[word++];
    195             --numOperands;
    196         }
    197 
    198         // Result <id>
    199         Id resultId = 0;
    200         if (InstructionDesc[opCode].hasResult()) {
    201             resultId = stream[word++];
    202             --numOperands;
    203 
    204             // save instruction for future reference
    205             idInstruction[resultId] = instructionStart;
    206         }
    207 
    208         outputResultId(resultId);
    209         outputTypeId(typeId);
    210         outputIndent();
    211 
    212         // Hand off the Op and all its operands
    213         disassembleInstruction(resultId, typeId, opCode, numOperands);
    214         if (word != nextInst) {
    215             out << " ERROR, incorrect number of operands consumed.  At " << word << " instead of " << nextInst << " instruction start was " << instructionStart;
    216             word = nextInst;
    217         }
    218         out << std::endl;
    219     }
    220 }
    221 
    222 void SpirvStream::outputIndent()
    223 {
    224     for (int i = 0; i < (int)nestedControl.size(); ++i)
    225         out << "  ";
    226 }
    227 
    228 void SpirvStream::formatId(Id id, std::stringstream& idStream)
    229 {
    230     if (id != 0) {
    231         // On instructions with no IDs, this is called with "0", which does not
    232         // have to be within ID bounds on null shaders.
    233         if (id >= bound)
    234             Kill(out, "Bad <id>");
    235 
    236         idStream << id;
    237         if (idDescriptor[id].size() > 0)
    238             idStream << "(" << idDescriptor[id] << ")";
    239     }
    240 }
    241 
    242 void SpirvStream::outputResultId(Id id)
    243 {
    244     const int width = 16;
    245     std::stringstream idStream;
    246     formatId(id, idStream);
    247     out << std::setw(width) << std::right << idStream.str();
    248     if (id != 0)
    249         out << ":";
    250     else
    251         out << " ";
    252 
    253     if (nestedControl.size() && id == nestedControl.top())
    254         nestedControl.pop();
    255 }
    256 
    257 void SpirvStream::outputTypeId(Id id)
    258 {
    259     const int width = 12;
    260     std::stringstream idStream;
    261     formatId(id, idStream);
    262     out << std::setw(width) << std::right << idStream.str() << " ";
    263 }
    264 
    265 void SpirvStream::outputId(Id id)
    266 {
    267     if (id >= bound)
    268         Kill(out, "Bad <id>");
    269 
    270     out << id;
    271     if (idDescriptor[id].size() > 0)
    272         out << "(" << idDescriptor[id] << ")";
    273 }
    274 
    275 void SpirvStream::outputMask(OperandClass operandClass, unsigned mask)
    276 {
    277     if (mask == 0)
    278         out << "None";
    279     else {
    280         for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) {
    281             if (mask & (1 << m))
    282                 out << OperandClassParams[operandClass].getName(m) << " ";
    283         }
    284     }
    285 }
    286 
    287 void SpirvStream::disassembleImmediates(int numOperands)
    288 {
    289     for (int i = 0; i < numOperands; ++i) {
    290         out << stream[word++];
    291         if (i < numOperands - 1)
    292             out << " ";
    293     }
    294 }
    295 
    296 void SpirvStream::disassembleIds(int numOperands)
    297 {
    298     for (int i = 0; i < numOperands; ++i) {
    299         outputId(stream[word++]);
    300         if (i < numOperands - 1)
    301             out << " ";
    302     }
    303 }
    304 
    305 // return the number of operands consumed by the string
    306 int SpirvStream::disassembleString()
    307 {
    308     int startWord = word;
    309 
    310     out << " \"";
    311 
    312     const char* wordString;
    313     bool done = false;
    314     do {
    315         unsigned int content = stream[word];
    316         wordString = (const char*)&content;
    317         for (int charCount = 0; charCount < 4; ++charCount) {
    318             if (*wordString == 0) {
    319                 done = true;
    320                 break;
    321             }
    322             out << *(wordString++);
    323         }
    324         ++word;
    325     } while (! done);
    326 
    327     out << "\"";
    328 
    329     return word - startWord;
    330 }
    331 
    332 void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands)
    333 {
    334     // Process the opcode
    335 
    336     out << (OpcodeString(opCode) + 2);  // leave out the "Op"
    337 
    338     if (opCode == OpLoopMerge || opCode == OpSelectionMerge)
    339         nextNestedControl = stream[word];
    340     else if (opCode == OpBranchConditional || opCode == OpSwitch) {
    341         if (nextNestedControl) {
    342             nestedControl.push(nextNestedControl);
    343             nextNestedControl = 0;
    344         }
    345     } else if (opCode == OpExtInstImport) {
    346         idDescriptor[resultId] = (const char*)(&stream[word]);
    347     }
    348     else {
    349         if (resultId != 0 && idDescriptor[resultId].size() == 0) {
    350             switch (opCode) {
    351             case OpTypeInt:
    352                 idDescriptor[resultId] = "int";
    353                 break;
    354             case OpTypeFloat:
    355                 idDescriptor[resultId] = "float";
    356                 break;
    357             case OpTypeBool:
    358                 idDescriptor[resultId] = "bool";
    359                 break;
    360             case OpTypeStruct:
    361                 idDescriptor[resultId] = "struct";
    362                 break;
    363             case OpTypePointer:
    364                 idDescriptor[resultId] = "ptr";
    365                 break;
    366             case OpTypeVector:
    367                 if (idDescriptor[stream[word]].size() > 0)
    368                     idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 1);
    369                 idDescriptor[resultId].append("vec");
    370                 switch (stream[word + 1]) {
    371                 case 2:   idDescriptor[resultId].append("2");   break;
    372                 case 3:   idDescriptor[resultId].append("3");   break;
    373                 case 4:   idDescriptor[resultId].append("4");   break;
    374                 case 8:   idDescriptor[resultId].append("8");   break;
    375                 case 16:  idDescriptor[resultId].append("16");  break;
    376                 case 32:  idDescriptor[resultId].append("32");  break;
    377                 default: break;
    378                 }
    379                 break;
    380             default:
    381                 break;
    382             }
    383         }
    384     }
    385 
    386     // Process the operands.  Note, a new context-dependent set could be
    387     // swapped in mid-traversal.
    388 
    389     // Handle images specially, so can put out helpful strings.
    390     if (opCode == OpTypeImage) {
    391         out << " ";
    392         disassembleIds(1);
    393         out << " " << DimensionString((Dim)stream[word++]);
    394         out << (stream[word++] != 0 ? " depth" : "");
    395         out << (stream[word++] != 0 ? " array" : "");
    396         out << (stream[word++] != 0 ? " multi-sampled" : "");
    397         switch (stream[word++]) {
    398         case 0: out << " runtime";    break;
    399         case 1: out << " sampled";    break;
    400         case 2: out << " nonsampled"; break;
    401         }
    402         out << " format:" << ImageFormatString((ImageFormat)stream[word++]);
    403 
    404         if (numOperands == 8) {
    405             out << " " << AccessQualifierString(stream[word++]);
    406         }
    407         return;
    408     }
    409 
    410     // Handle all the parameterized operands
    411     for (int op = 0; op < InstructionDesc[opCode].operands.getNum() && numOperands > 0; ++op) {
    412         out << " ";
    413         OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op);
    414         switch (operandClass) {
    415         case OperandId:
    416         case OperandScope:
    417         case OperandMemorySemantics:
    418             disassembleIds(1);
    419             --numOperands;
    420             // Get names for printing "(XXX)" for readability, *after* this id
    421             if (opCode == OpName)
    422                 idDescriptor[stream[word - 1]] = (const char*)(&stream[word]);
    423             break;
    424         case OperandVariableIds:
    425             disassembleIds(numOperands);
    426             return;
    427         case OperandImageOperands:
    428             outputMask(OperandImageOperands, stream[word++]);
    429             --numOperands;
    430             disassembleIds(numOperands);
    431             return;
    432         case OperandOptionalLiteral:
    433         case OperandVariableLiterals:
    434             if ((opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) ||
    435                 (opCode == OpMemberDecorate && stream[word - 1] == DecorationBuiltIn)) {
    436                 out << BuiltInString(stream[word++]);
    437                 --numOperands;
    438                 ++op;
    439             }
    440             disassembleImmediates(numOperands);
    441             return;
    442         case OperandVariableIdLiteral:
    443             while (numOperands > 0) {
    444                 out << std::endl;
    445                 outputResultId(0);
    446                 outputTypeId(0);
    447                 outputIndent();
    448                 out << "     Type ";
    449                 disassembleIds(1);
    450                 out << ", member ";
    451                 disassembleImmediates(1);
    452                 numOperands -= 2;
    453             }
    454             return;
    455         case OperandVariableLiteralId:
    456             while (numOperands > 0) {
    457                 out << std::endl;
    458                 outputResultId(0);
    459                 outputTypeId(0);
    460                 outputIndent();
    461                 out << "     case ";
    462                 disassembleImmediates(1);
    463                 out << ": ";
    464                 disassembleIds(1);
    465                 numOperands -= 2;
    466             }
    467             return;
    468         case OperandLiteralNumber:
    469             disassembleImmediates(1);
    470             --numOperands;
    471             if (opCode == OpExtInst) {
    472                 ExtInstSet extInstSet = GLSL450Inst;
    473                 const char* name = idDescriptor[stream[word - 2]].c_str();
    474                 if (0 == memcmp("OpenCL", name, 6)) {
    475                     extInstSet = OpenCLExtInst;
    476 #ifdef AMD_EXTENSIONS
    477                 } else if (strcmp(spv::E_SPV_AMD_shader_ballot, name) == 0 ||
    478                            strcmp(spv::E_SPV_AMD_shader_trinary_minmax, name) == 0 ||
    479                            strcmp(spv::E_SPV_AMD_shader_explicit_vertex_parameter, name) == 0 ||
    480                            strcmp(spv::E_SPV_AMD_gcn_shader, name) == 0) {
    481                     extInstSet = GLSLextAMDInst;
    482 #endif
    483 #ifdef NV_EXTENSIONS
    484                 }else if (strcmp(spv::E_SPV_NV_sample_mask_override_coverage, name) == 0 ||
    485                           strcmp(spv::E_SPV_NV_geometry_shader_passthrough, name) == 0 ||
    486                           strcmp(spv::E_SPV_NV_viewport_array2, name) == 0 ||
    487                           strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0) {
    488                     extInstSet = GLSLextNVInst;
    489 #endif
    490                 }
    491                 unsigned entrypoint = stream[word - 1];
    492                 if (extInstSet == GLSL450Inst) {
    493                     if (entrypoint < GLSLstd450Count) {
    494                         out << "(" << GlslStd450DebugNames[entrypoint] << ")";
    495                     }
    496 #ifdef AMD_EXTENSIONS
    497                 } else if (extInstSet == GLSLextAMDInst) {
    498                     out << "(" << GLSLextAMDGetDebugNames(name, entrypoint) << ")";
    499 #endif
    500 #ifdef NV_EXTENSIONS
    501                 }
    502                 else if (extInstSet == GLSLextNVInst) {
    503                     out << "(" << GLSLextNVGetDebugNames(name, entrypoint) << ")";
    504 #endif
    505                 }
    506             }
    507             break;
    508         case OperandOptionalLiteralString:
    509         case OperandLiteralString:
    510             numOperands -= disassembleString();
    511             break;
    512         default:
    513             assert(operandClass >= OperandSource && operandClass < OperandOpcode);
    514 
    515             if (OperandClassParams[operandClass].bitmask)
    516                 outputMask(operandClass, stream[word++]);
    517             else
    518                 out << OperandClassParams[operandClass].getName(stream[word++]);
    519             --numOperands;
    520 
    521             break;
    522         }
    523     }
    524 
    525     return;
    526 }
    527 
    528 static void GLSLstd450GetDebugNames(const char** names)
    529 {
    530     for (int i = 0; i < GLSLstd450Count; ++i)
    531         names[i] = "Unknown";
    532 
    533     names[GLSLstd450Round]                   = "Round";
    534     names[GLSLstd450RoundEven]               = "RoundEven";
    535     names[GLSLstd450Trunc]                   = "Trunc";
    536     names[GLSLstd450FAbs]                    = "FAbs";
    537     names[GLSLstd450SAbs]                    = "SAbs";
    538     names[GLSLstd450FSign]                   = "FSign";
    539     names[GLSLstd450SSign]                   = "SSign";
    540     names[GLSLstd450Floor]                   = "Floor";
    541     names[GLSLstd450Ceil]                    = "Ceil";
    542     names[GLSLstd450Fract]                   = "Fract";
    543     names[GLSLstd450Radians]                 = "Radians";
    544     names[GLSLstd450Degrees]                 = "Degrees";
    545     names[GLSLstd450Sin]                     = "Sin";
    546     names[GLSLstd450Cos]                     = "Cos";
    547     names[GLSLstd450Tan]                     = "Tan";
    548     names[GLSLstd450Asin]                    = "Asin";
    549     names[GLSLstd450Acos]                    = "Acos";
    550     names[GLSLstd450Atan]                    = "Atan";
    551     names[GLSLstd450Sinh]                    = "Sinh";
    552     names[GLSLstd450Cosh]                    = "Cosh";
    553     names[GLSLstd450Tanh]                    = "Tanh";
    554     names[GLSLstd450Asinh]                   = "Asinh";
    555     names[GLSLstd450Acosh]                   = "Acosh";
    556     names[GLSLstd450Atanh]                   = "Atanh";
    557     names[GLSLstd450Atan2]                   = "Atan2";
    558     names[GLSLstd450Pow]                     = "Pow";
    559     names[GLSLstd450Exp]                     = "Exp";
    560     names[GLSLstd450Log]                     = "Log";
    561     names[GLSLstd450Exp2]                    = "Exp2";
    562     names[GLSLstd450Log2]                    = "Log2";
    563     names[GLSLstd450Sqrt]                    = "Sqrt";
    564     names[GLSLstd450InverseSqrt]             = "InverseSqrt";
    565     names[GLSLstd450Determinant]             = "Determinant";
    566     names[GLSLstd450MatrixInverse]           = "MatrixInverse";
    567     names[GLSLstd450Modf]                    = "Modf";
    568     names[GLSLstd450ModfStruct]              = "ModfStruct";
    569     names[GLSLstd450FMin]                    = "FMin";
    570     names[GLSLstd450SMin]                    = "SMin";
    571     names[GLSLstd450UMin]                    = "UMin";
    572     names[GLSLstd450FMax]                    = "FMax";
    573     names[GLSLstd450SMax]                    = "SMax";
    574     names[GLSLstd450UMax]                    = "UMax";
    575     names[GLSLstd450FClamp]                  = "FClamp";
    576     names[GLSLstd450SClamp]                  = "SClamp";
    577     names[GLSLstd450UClamp]                  = "UClamp";
    578     names[GLSLstd450FMix]                    = "FMix";
    579     names[GLSLstd450Step]                    = "Step";
    580     names[GLSLstd450SmoothStep]              = "SmoothStep";
    581     names[GLSLstd450Fma]                     = "Fma";
    582     names[GLSLstd450Frexp]                   = "Frexp";
    583     names[GLSLstd450FrexpStruct]             = "FrexpStruct";
    584     names[GLSLstd450Ldexp]                   = "Ldexp";
    585     names[GLSLstd450PackSnorm4x8]            = "PackSnorm4x8";
    586     names[GLSLstd450PackUnorm4x8]            = "PackUnorm4x8";
    587     names[GLSLstd450PackSnorm2x16]           = "PackSnorm2x16";
    588     names[GLSLstd450PackUnorm2x16]           = "PackUnorm2x16";
    589     names[GLSLstd450PackHalf2x16]            = "PackHalf2x16";
    590     names[GLSLstd450PackDouble2x32]          = "PackDouble2x32";
    591     names[GLSLstd450UnpackSnorm2x16]         = "UnpackSnorm2x16";
    592     names[GLSLstd450UnpackUnorm2x16]         = "UnpackUnorm2x16";
    593     names[GLSLstd450UnpackHalf2x16]          = "UnpackHalf2x16";
    594     names[GLSLstd450UnpackSnorm4x8]          = "UnpackSnorm4x8";
    595     names[GLSLstd450UnpackUnorm4x8]          = "UnpackUnorm4x8";
    596     names[GLSLstd450UnpackDouble2x32]        = "UnpackDouble2x32";
    597     names[GLSLstd450Length]                  = "Length";
    598     names[GLSLstd450Distance]                = "Distance";
    599     names[GLSLstd450Cross]                   = "Cross";
    600     names[GLSLstd450Normalize]               = "Normalize";
    601     names[GLSLstd450FaceForward]             = "FaceForward";
    602     names[GLSLstd450Reflect]                 = "Reflect";
    603     names[GLSLstd450Refract]                 = "Refract";
    604     names[GLSLstd450FindILsb]                = "FindILsb";
    605     names[GLSLstd450FindSMsb]                = "FindSMsb";
    606     names[GLSLstd450FindUMsb]                = "FindUMsb";
    607     names[GLSLstd450InterpolateAtCentroid]   = "InterpolateAtCentroid";
    608     names[GLSLstd450InterpolateAtSample]     = "InterpolateAtSample";
    609     names[GLSLstd450InterpolateAtOffset]     = "InterpolateAtOffset";
    610 }
    611 
    612 #ifdef AMD_EXTENSIONS
    613 static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint)
    614 {
    615     if (strcmp(name, spv::E_SPV_AMD_shader_ballot) == 0) {
    616         switch (entrypoint) {
    617         case SwizzleInvocationsAMD:         return "SwizzleInvocationsAMD";
    618         case SwizzleInvocationsMaskedAMD:   return "SwizzleInvocationsMaskedAMD";
    619         case WriteInvocationAMD:            return "WriteInvocationAMD";
    620         case MbcntAMD:                      return "MbcntAMD";
    621         default:                            return "Bad";
    622         }
    623     } else if (strcmp(name, spv::E_SPV_AMD_shader_trinary_minmax) == 0) {
    624         switch (entrypoint) {
    625         case FMin3AMD:      return "FMin3AMD";
    626         case UMin3AMD:      return "UMin3AMD";
    627         case SMin3AMD:      return "SMin3AMD";
    628         case FMax3AMD:      return "FMax3AMD";
    629         case UMax3AMD:      return "UMax3AMD";
    630         case SMax3AMD:      return "SMax3AMD";
    631         case FMid3AMD:      return "FMid3AMD";
    632         case UMid3AMD:      return "UMid3AMD";
    633         case SMid3AMD:      return "SMid3AMD";
    634         default:            return "Bad";
    635         }
    636     } else if (strcmp(name, spv::E_SPV_AMD_shader_explicit_vertex_parameter) == 0) {
    637         switch (entrypoint) {
    638         case InterpolateAtVertexAMD:    return "InterpolateAtVertexAMD";
    639         default:                        return "Bad";
    640         }
    641     }
    642     else if (strcmp(name, spv::E_SPV_AMD_gcn_shader) == 0) {
    643         switch (entrypoint) {
    644         case CubeFaceIndexAMD:      return "CubeFaceIndexAMD";
    645         case CubeFaceCoordAMD:      return "CubeFaceCoordAMD";
    646         case TimeAMD:               return "TimeAMD";
    647         default:
    648             break;
    649         }
    650     }
    651 
    652     return "Bad";
    653 }
    654 #endif
    655 
    656 
    657 #ifdef NV_EXTENSIONS
    658 static const char* GLSLextNVGetDebugNames(const char* name, unsigned entrypoint)
    659 {
    660     if (strcmp(name, spv::E_SPV_NV_sample_mask_override_coverage) == 0 ||
    661         strcmp(name, spv::E_SPV_NV_geometry_shader_passthrough) == 0 ||
    662         strcmp(name, spv::E_ARB_shader_viewport_layer_array) == 0 ||
    663         strcmp(name, spv::E_SPV_NV_viewport_array2) == 0 ||
    664         strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0) {
    665         switch (entrypoint) {
    666         case DecorationOverrideCoverageNV:          return "OverrideCoverageNV";
    667         case DecorationPassthroughNV:               return "PassthroughNV";
    668         case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV";
    669         case DecorationViewportRelativeNV:          return "ViewportRelativeNV";
    670         case BuiltInViewportMaskNV:                 return "ViewportMaskNV";
    671         case CapabilityShaderViewportMaskNV:        return "ShaderViewportMaskNV";
    672         case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV";
    673         case BuiltInSecondaryPositionNV:            return "SecondaryPositionNV";
    674         case BuiltInSecondaryViewportMaskNV:        return "SecondaryViewportMaskNV";
    675         case CapabilityShaderStereoViewNV:          return "ShaderStereoViewNV";
    676         case BuiltInPositionPerViewNV:              return "PositionPerViewNV";
    677         case BuiltInViewportMaskPerViewNV:          return "ViewportMaskPerViewNV";
    678         case CapabilityPerViewAttributesNV:         return "PerViewAttributesNV";
    679         default:                                    return "Bad";
    680         }
    681     }
    682     return "Bad";
    683 }
    684 #endif
    685 
    686 void Disassemble(std::ostream& out, const std::vector<unsigned int>& stream)
    687 {
    688     SpirvStream SpirvStream(out, stream);
    689     spv::Parameterize();
    690     GLSLstd450GetDebugNames(GlslStd450DebugNames);
    691     SpirvStream.validate();
    692     SpirvStream.processInstructions();
    693 }
    694 
    695 }; // end namespace spv
    696