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