 f4f1d8a352
			
		
	
	
		f4f1d8a352
		
			
		
	
	
	
	
		
			
			disassemble.cpp appears not to be using anything from SpvTools.h, but the inclusion of it prevents standalone building of the SPIR-V portion (for instance, when needed purely for generation and disassembly) without SPIRV-Tools dependency.
		
			
				
	
	
		
			747 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			747 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //
 | |
| // Copyright (C) 2014-2015 LunarG, Inc.
 | |
| //
 | |
| // All rights reserved.
 | |
| //
 | |
| // Redistribution and use in source and binary forms, with or without
 | |
| // modification, are permitted provided that the following conditions
 | |
| // are met:
 | |
| //
 | |
| //    Redistributions of source code must retain the above copyright
 | |
| //    notice, this list of conditions and the following disclaimer.
 | |
| //
 | |
| //    Redistributions in binary form must reproduce the above
 | |
| //    copyright notice, this list of conditions and the following
 | |
| //    disclaimer in the documentation and/or other materials provided
 | |
| //    with the distribution.
 | |
| //
 | |
| //    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
 | |
| //    contributors may be used to endorse or promote products derived
 | |
| //    from this software without specific prior written permission.
 | |
| //
 | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | |
| // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 | |
| // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 | |
| // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 | |
| // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | |
| // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | |
| // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 | |
| // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | |
| // POSSIBILITY OF SUCH DAMAGE.
 | |
| 
 | |
| //
 | |
| // Disassembler for SPIR-V.
 | |
| //
 | |
| 
 | |
| #include <cstdlib>
 | |
| #include <cstring>
 | |
| #include <cassert>
 | |
| #include <iomanip>
 | |
| #include <stack>
 | |
| #include <sstream>
 | |
| #include <cstring>
 | |
| 
 | |
| #include "disassemble.h"
 | |
| #include "doc.h"
 | |
| 
 | |
| namespace spv {
 | |
|     extern "C" {
 | |
|         // Include C-based headers that don't have a namespace
 | |
|         #include "GLSL.std.450.h"
 | |
|         #include "GLSL.ext.AMD.h"
 | |
|         #include "GLSL.ext.NV.h"
 | |
|     }
 | |
| }
 | |
| const char* GlslStd450DebugNames[spv::GLSLstd450Count];
 | |
| 
 | |
| namespace spv {
 | |
| 
 | |
| static const char* GLSLextAMDGetDebugNames(const char*, unsigned);
 | |
| static const char* GLSLextNVGetDebugNames(const char*, unsigned);
 | |
| 
 | |
| static void Kill(std::ostream& out, const char* message)
 | |
| {
 | |
|     out << std::endl << "Disassembly failed: " << message << std::endl;
 | |
|     exit(1);
 | |
| }
 | |
| 
 | |
| // used to identify the extended instruction library imported when printing
 | |
| enum ExtInstSet {
 | |
|     GLSL450Inst,
 | |
|     GLSLextAMDInst,
 | |
|     GLSLextNVInst,
 | |
|     OpenCLExtInst,
 | |
|     NonSemanticDebugPrintfExtInst,
 | |
| };
 | |
| 
 | |
| // Container class for a single instance of a SPIR-V stream, with methods for disassembly.
 | |
| class SpirvStream {
 | |
| public:
 | |
|     SpirvStream(std::ostream& out, const std::vector<unsigned int>& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { }
 | |
|     virtual ~SpirvStream() { }
 | |
| 
 | |
|     void validate();
 | |
|     void processInstructions();
 | |
| 
 | |
| protected:
 | |
|     SpirvStream(const SpirvStream&);
 | |
|     SpirvStream& operator=(const SpirvStream&);
 | |
|     Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; }
 | |
| 
 | |
|     // Output methods
 | |
|     void outputIndent();
 | |
|     void formatId(Id id, std::stringstream&);
 | |
|     void outputResultId(Id id);
 | |
|     void outputTypeId(Id id);
 | |
|     void outputId(Id id);
 | |
|     void outputMask(OperandClass operandClass, unsigned mask);
 | |
|     void disassembleImmediates(int numOperands);
 | |
|     void disassembleIds(int numOperands);
 | |
|     int disassembleString();
 | |
|     void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands);
 | |
| 
 | |
|     // Data
 | |
|     std::ostream& out;                       // where to write the disassembly
 | |
|     const std::vector<unsigned int>& stream; // the actual word stream
 | |
|     int size;                                // the size of the word stream
 | |
|     int word;                                // the next word of the stream to read
 | |
| 
 | |
|     // map each <id> to the instruction that created it
 | |
|     Id bound;
 | |
|     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)
 | |
| 
 | |
|     std::vector<std::string> idDescriptor;    // the best text string known for explaining the <id>
 | |
| 
 | |
|     // schema
 | |
|     unsigned int schema;
 | |
| 
 | |
|     // stack of structured-merge points
 | |
|     std::stack<Id> nestedControl;
 | |
|     Id nextNestedControl;         // need a slight delay for when we are nested
 | |
| };
 | |
| 
 | |
| void SpirvStream::validate()
 | |
| {
 | |
|     size = (int)stream.size();
 | |
|     if (size < 4)
 | |
|         Kill(out, "stream is too short");
 | |
| 
 | |
|     // Magic number
 | |
|     if (stream[word++] != MagicNumber) {
 | |
|         out << "Bad magic number";
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     // Version
 | |
|     out << "// Module Version " << std::hex << stream[word++] << std::endl;
 | |
| 
 | |
|     // Generator's magic number
 | |
|     out << "// Generated by (magic number): " << std::hex << stream[word++] << std::dec << std::endl;
 | |
| 
 | |
|     // Result <id> bound
 | |
|     bound = stream[word++];
 | |
|     idInstruction.resize(bound);
 | |
|     idDescriptor.resize(bound);
 | |
|     out << "// Id's are bound by " << bound << std::endl;
 | |
|     out << std::endl;
 | |
| 
 | |
|     // Reserved schema, must be 0 for now
 | |
|     schema = stream[word++];
 | |
|     if (schema != 0)
 | |
|         Kill(out, "bad schema, must be 0");
 | |
| }
 | |
| 
 | |
| // Loop over all the instructions, in order, processing each.
 | |
| // Boiler plate for each is handled here directly, the rest is dispatched.
 | |
| void SpirvStream::processInstructions()
 | |
| {
 | |
|     // Instructions
 | |
|     while (word < size) {
 | |
|         int instructionStart = word;
 | |
| 
 | |
|         // Instruction wordCount and opcode
 | |
|         unsigned int firstWord = stream[word];
 | |
|         unsigned wordCount = firstWord >> WordCountShift;
 | |
|         Op opCode = (Op)(firstWord & OpCodeMask);
 | |
|         int nextInst = word + wordCount;
 | |
|         ++word;
 | |
| 
 | |
|         // Presence of full instruction
 | |
|         if (nextInst > size)
 | |
|             Kill(out, "stream instruction terminated too early");
 | |
| 
 | |
|         // Base for computing number of operands; will be updated as more is learned
 | |
|         unsigned numOperands = wordCount - 1;
 | |
| 
 | |
|         // Type <id>
 | |
|         Id typeId = 0;
 | |
|         if (InstructionDesc[opCode].hasType()) {
 | |
|             typeId = stream[word++];
 | |
|             --numOperands;
 | |
|         }
 | |
| 
 | |
|         // Result <id>
 | |
|         Id resultId = 0;
 | |
|         if (InstructionDesc[opCode].hasResult()) {
 | |
|             resultId = stream[word++];
 | |
|             --numOperands;
 | |
| 
 | |
|             // save instruction for future reference
 | |
|             idInstruction[resultId] = instructionStart;
 | |
|         }
 | |
| 
 | |
|         outputResultId(resultId);
 | |
|         outputTypeId(typeId);
 | |
|         outputIndent();
 | |
| 
 | |
|         // Hand off the Op and all its operands
 | |
|         disassembleInstruction(resultId, typeId, opCode, numOperands);
 | |
|         if (word != nextInst) {
 | |
|             out << " ERROR, incorrect number of operands consumed.  At " << word << " instead of " << nextInst << " instruction start was " << instructionStart;
 | |
|             word = nextInst;
 | |
|         }
 | |
|         out << std::endl;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void SpirvStream::outputIndent()
 | |
| {
 | |
|     for (int i = 0; i < (int)nestedControl.size(); ++i)
 | |
|         out << "  ";
 | |
| }
 | |
| 
 | |
| void SpirvStream::formatId(Id id, std::stringstream& idStream)
 | |
| {
 | |
|     if (id != 0) {
 | |
|         // On instructions with no IDs, this is called with "0", which does not
 | |
|         // have to be within ID bounds on null shaders.
 | |
|         if (id >= bound)
 | |
|             Kill(out, "Bad <id>");
 | |
| 
 | |
|         idStream << id;
 | |
|         if (idDescriptor[id].size() > 0)
 | |
|             idStream << "(" << idDescriptor[id] << ")";
 | |
|     }
 | |
| }
 | |
| 
 | |
| void SpirvStream::outputResultId(Id id)
 | |
| {
 | |
|     const int width = 16;
 | |
|     std::stringstream idStream;
 | |
|     formatId(id, idStream);
 | |
|     out << std::setw(width) << std::right << idStream.str();
 | |
|     if (id != 0)
 | |
|         out << ":";
 | |
|     else
 | |
|         out << " ";
 | |
| 
 | |
|     if (nestedControl.size() && id == nestedControl.top())
 | |
|         nestedControl.pop();
 | |
| }
 | |
| 
 | |
| void SpirvStream::outputTypeId(Id id)
 | |
| {
 | |
|     const int width = 12;
 | |
|     std::stringstream idStream;
 | |
|     formatId(id, idStream);
 | |
|     out << std::setw(width) << std::right << idStream.str() << " ";
 | |
| }
 | |
| 
 | |
| void SpirvStream::outputId(Id id)
 | |
| {
 | |
|     if (id >= bound)
 | |
|         Kill(out, "Bad <id>");
 | |
| 
 | |
|     out << id;
 | |
|     if (idDescriptor[id].size() > 0)
 | |
|         out << "(" << idDescriptor[id] << ")";
 | |
| }
 | |
| 
 | |
| void SpirvStream::outputMask(OperandClass operandClass, unsigned mask)
 | |
| {
 | |
|     if (mask == 0)
 | |
|         out << "None";
 | |
|     else {
 | |
|         for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) {
 | |
|             if (mask & (1 << m))
 | |
|                 out << OperandClassParams[operandClass].getName(m) << " ";
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void SpirvStream::disassembleImmediates(int numOperands)
 | |
| {
 | |
|     for (int i = 0; i < numOperands; ++i) {
 | |
|         out << stream[word++];
 | |
|         if (i < numOperands - 1)
 | |
|             out << " ";
 | |
|     }
 | |
| }
 | |
| 
 | |
| void SpirvStream::disassembleIds(int numOperands)
 | |
| {
 | |
|     for (int i = 0; i < numOperands; ++i) {
 | |
|         outputId(stream[word++]);
 | |
|         if (i < numOperands - 1)
 | |
|             out << " ";
 | |
|     }
 | |
| }
 | |
| 
 | |
| // return the number of operands consumed by the string
 | |
| int SpirvStream::disassembleString()
 | |
| {
 | |
|     int startWord = word;
 | |
| 
 | |
|     out << " \"";
 | |
| 
 | |
|     const char* wordString;
 | |
|     bool done = false;
 | |
|     do {
 | |
|         unsigned int content = stream[word];
 | |
|         wordString = (const char*)&content;
 | |
|         for (int charCount = 0; charCount < 4; ++charCount) {
 | |
|             if (*wordString == 0) {
 | |
|                 done = true;
 | |
|                 break;
 | |
|             }
 | |
|             out << *(wordString++);
 | |
|         }
 | |
|         ++word;
 | |
|     } while (! done);
 | |
| 
 | |
|     out << "\"";
 | |
| 
 | |
|     return word - startWord;
 | |
| }
 | |
| 
 | |
| void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands)
 | |
| {
 | |
|     // Process the opcode
 | |
| 
 | |
|     out << (OpcodeString(opCode) + 2);  // leave out the "Op"
 | |
| 
 | |
|     if (opCode == OpLoopMerge || opCode == OpSelectionMerge)
 | |
|         nextNestedControl = stream[word];
 | |
|     else if (opCode == OpBranchConditional || opCode == OpSwitch) {
 | |
|         if (nextNestedControl) {
 | |
|             nestedControl.push(nextNestedControl);
 | |
|             nextNestedControl = 0;
 | |
|         }
 | |
|     } else if (opCode == OpExtInstImport) {
 | |
|         idDescriptor[resultId] = (const char*)(&stream[word]);
 | |
|     }
 | |
|     else {
 | |
|         if (resultId != 0 && idDescriptor[resultId].size() == 0) {
 | |
|             switch (opCode) {
 | |
|             case OpTypeInt:
 | |
|                 switch (stream[word]) {
 | |
|                 case 8:  idDescriptor[resultId] = "int8_t"; break;
 | |
|                 case 16: idDescriptor[resultId] = "int16_t"; break;
 | |
|                 default: assert(0); // fallthrough
 | |
|                 case 32: idDescriptor[resultId] = "int"; break;
 | |
|                 case 64: idDescriptor[resultId] = "int64_t"; break;
 | |
|                 }
 | |
|                 break;
 | |
|             case OpTypeFloat:
 | |
|                 switch (stream[word]) {
 | |
|                 case 16: idDescriptor[resultId] = "float16_t"; break;
 | |
|                 default: assert(0); // fallthrough
 | |
|                 case 32: idDescriptor[resultId] = "float"; break;
 | |
|                 case 64: idDescriptor[resultId] = "float64_t"; break;
 | |
|                 }
 | |
|                 break;
 | |
|             case OpTypeBool:
 | |
|                 idDescriptor[resultId] = "bool";
 | |
|                 break;
 | |
|             case OpTypeStruct:
 | |
|                 idDescriptor[resultId] = "struct";
 | |
|                 break;
 | |
|             case OpTypePointer:
 | |
|                 idDescriptor[resultId] = "ptr";
 | |
|                 break;
 | |
|             case OpTypeVector:
 | |
|                 if (idDescriptor[stream[word]].size() > 0) {
 | |
|                     idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 1);
 | |
|                     if (strstr(idDescriptor[stream[word]].c_str(), "8")) {
 | |
|                         idDescriptor[resultId].append("8");
 | |
|                     }
 | |
|                     if (strstr(idDescriptor[stream[word]].c_str(), "16")) {
 | |
|                         idDescriptor[resultId].append("16");
 | |
|                     }
 | |
|                     if (strstr(idDescriptor[stream[word]].c_str(), "64")) {
 | |
|                         idDescriptor[resultId].append("64");
 | |
|                     }
 | |
|                 }
 | |
|                 idDescriptor[resultId].append("vec");
 | |
|                 switch (stream[word + 1]) {
 | |
|                 case 2:   idDescriptor[resultId].append("2");   break;
 | |
|                 case 3:   idDescriptor[resultId].append("3");   break;
 | |
|                 case 4:   idDescriptor[resultId].append("4");   break;
 | |
|                 case 8:   idDescriptor[resultId].append("8");   break;
 | |
|                 case 16:  idDescriptor[resultId].append("16");  break;
 | |
|                 case 32:  idDescriptor[resultId].append("32");  break;
 | |
|                 default: break;
 | |
|                 }
 | |
|                 break;
 | |
|             default:
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Process the operands.  Note, a new context-dependent set could be
 | |
|     // swapped in mid-traversal.
 | |
| 
 | |
|     // Handle images specially, so can put out helpful strings.
 | |
|     if (opCode == OpTypeImage) {
 | |
|         out << " ";
 | |
|         disassembleIds(1);
 | |
|         out << " " << DimensionString((Dim)stream[word++]);
 | |
|         out << (stream[word++] != 0 ? " depth" : "");
 | |
|         out << (stream[word++] != 0 ? " array" : "");
 | |
|         out << (stream[word++] != 0 ? " multi-sampled" : "");
 | |
|         switch (stream[word++]) {
 | |
|         case 0: out << " runtime";    break;
 | |
|         case 1: out << " sampled";    break;
 | |
|         case 2: out << " nonsampled"; break;
 | |
|         }
 | |
|         out << " format:" << ImageFormatString((ImageFormat)stream[word++]);
 | |
| 
 | |
|         if (numOperands == 8) {
 | |
|             out << " " << AccessQualifierString(stream[word++]);
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     // Handle all the parameterized operands
 | |
|     for (int op = 0; op < InstructionDesc[opCode].operands.getNum() && numOperands > 0; ++op) {
 | |
|         out << " ";
 | |
|         OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op);
 | |
|         switch (operandClass) {
 | |
|         case OperandId:
 | |
|         case OperandScope:
 | |
|         case OperandMemorySemantics:
 | |
|             disassembleIds(1);
 | |
|             --numOperands;
 | |
|             // Get names for printing "(XXX)" for readability, *after* this id
 | |
|             if (opCode == OpName)
 | |
|                 idDescriptor[stream[word - 1]] = (const char*)(&stream[word]);
 | |
|             break;
 | |
|         case OperandVariableIds:
 | |
|             disassembleIds(numOperands);
 | |
|             return;
 | |
|         case OperandImageOperands:
 | |
|             outputMask(OperandImageOperands, stream[word++]);
 | |
|             --numOperands;
 | |
|             disassembleIds(numOperands);
 | |
|             return;
 | |
|         case OperandOptionalLiteral:
 | |
|         case OperandVariableLiterals:
 | |
|             if ((opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) ||
 | |
|                 (opCode == OpMemberDecorate && stream[word - 1] == DecorationBuiltIn)) {
 | |
|                 out << BuiltInString(stream[word++]);
 | |
|                 --numOperands;
 | |
|                 ++op;
 | |
|             }
 | |
|             disassembleImmediates(numOperands);
 | |
|             return;
 | |
|         case OperandVariableIdLiteral:
 | |
|             while (numOperands > 0) {
 | |
|                 out << std::endl;
 | |
|                 outputResultId(0);
 | |
|                 outputTypeId(0);
 | |
|                 outputIndent();
 | |
|                 out << "     Type ";
 | |
|                 disassembleIds(1);
 | |
|                 out << ", member ";
 | |
|                 disassembleImmediates(1);
 | |
|                 numOperands -= 2;
 | |
|             }
 | |
|             return;
 | |
|         case OperandVariableLiteralId:
 | |
|             while (numOperands > 0) {
 | |
|                 out << std::endl;
 | |
|                 outputResultId(0);
 | |
|                 outputTypeId(0);
 | |
|                 outputIndent();
 | |
|                 out << "     case ";
 | |
|                 disassembleImmediates(1);
 | |
|                 out << ": ";
 | |
|                 disassembleIds(1);
 | |
|                 numOperands -= 2;
 | |
|             }
 | |
|             return;
 | |
|         case OperandLiteralNumber:
 | |
|             disassembleImmediates(1);
 | |
|             --numOperands;
 | |
|             if (opCode == OpExtInst) {
 | |
|                 ExtInstSet extInstSet = GLSL450Inst;
 | |
|                 const char* name = idDescriptor[stream[word - 2]].c_str();
 | |
|                 if (strcmp("OpenCL.std", name) == 0) {
 | |
|                     extInstSet = OpenCLExtInst;
 | |
|                 } else if (strcmp("OpenCL.DebugInfo.100", name) == 0) {
 | |
|                     extInstSet = OpenCLExtInst;
 | |
|                 } else if (strcmp("NonSemantic.DebugPrintf", name) == 0) {
 | |
|                     extInstSet = NonSemanticDebugPrintfExtInst;
 | |
|                 } else if (strcmp(spv::E_SPV_AMD_shader_ballot, name) == 0 ||
 | |
|                            strcmp(spv::E_SPV_AMD_shader_trinary_minmax, name) == 0 ||
 | |
|                            strcmp(spv::E_SPV_AMD_shader_explicit_vertex_parameter, name) == 0 ||
 | |
|                            strcmp(spv::E_SPV_AMD_gcn_shader, name) == 0) {
 | |
|                     extInstSet = GLSLextAMDInst;
 | |
|                 } else if (strcmp(spv::E_SPV_NV_sample_mask_override_coverage, name) == 0 ||
 | |
|                           strcmp(spv::E_SPV_NV_geometry_shader_passthrough, name) == 0 ||
 | |
|                           strcmp(spv::E_SPV_NV_viewport_array2, name) == 0 ||
 | |
|                           strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0 || 
 | |
|                           strcmp(spv::E_SPV_NV_fragment_shader_barycentric, name) == 0 ||
 | |
|                           strcmp(spv::E_SPV_NV_mesh_shader, name) == 0) {
 | |
|                     extInstSet = GLSLextNVInst;
 | |
|                 }
 | |
|                 unsigned entrypoint = stream[word - 1];
 | |
|                 if (extInstSet == GLSL450Inst) {
 | |
|                     if (entrypoint < GLSLstd450Count) {
 | |
|                         out << "(" << GlslStd450DebugNames[entrypoint] << ")";
 | |
|                     }
 | |
|                 } else if (extInstSet == GLSLextAMDInst) {
 | |
|                     out << "(" << GLSLextAMDGetDebugNames(name, entrypoint) << ")";
 | |
|                 }
 | |
|                 else if (extInstSet == GLSLextNVInst) {
 | |
|                     out << "(" << GLSLextNVGetDebugNames(name, entrypoint) << ")";
 | |
|                 } else if (extInstSet == NonSemanticDebugPrintfExtInst) {
 | |
|                     out << "(DebugPrintf)";
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         case OperandOptionalLiteralString:
 | |
|         case OperandLiteralString:
 | |
|             numOperands -= disassembleString();
 | |
|             break;
 | |
|         case OperandVariableLiteralStrings:
 | |
|             while (numOperands > 0)
 | |
|                 numOperands -= disassembleString();
 | |
|             return;
 | |
|         case OperandMemoryAccess:
 | |
|             outputMask(OperandMemoryAccess, stream[word++]);
 | |
|             --numOperands;
 | |
|             // Aligned is the only memory access operand that uses an immediate
 | |
|             // value, and it is also the first operand that uses a value at all.
 | |
|             if (stream[word-1] & MemoryAccessAlignedMask) {
 | |
|                 disassembleImmediates(1);
 | |
|                 numOperands--;
 | |
|                 if (numOperands)
 | |
|                     out << " ";
 | |
|             }
 | |
|             disassembleIds(numOperands);
 | |
|             return;
 | |
|         default:
 | |
|             assert(operandClass >= OperandSource && operandClass < OperandOpcode);
 | |
| 
 | |
|             if (OperandClassParams[operandClass].bitmask)
 | |
|                 outputMask(operandClass, stream[word++]);
 | |
|             else
 | |
|                 out << OperandClassParams[operandClass].getName(stream[word++]);
 | |
|             --numOperands;
 | |
| 
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return;
 | |
| }
 | |
| 
 | |
| static void GLSLstd450GetDebugNames(const char** names)
 | |
| {
 | |
|     for (int i = 0; i < GLSLstd450Count; ++i)
 | |
|         names[i] = "Unknown";
 | |
| 
 | |
|     names[GLSLstd450Round]                   = "Round";
 | |
|     names[GLSLstd450RoundEven]               = "RoundEven";
 | |
|     names[GLSLstd450Trunc]                   = "Trunc";
 | |
|     names[GLSLstd450FAbs]                    = "FAbs";
 | |
|     names[GLSLstd450SAbs]                    = "SAbs";
 | |
|     names[GLSLstd450FSign]                   = "FSign";
 | |
|     names[GLSLstd450SSign]                   = "SSign";
 | |
|     names[GLSLstd450Floor]                   = "Floor";
 | |
|     names[GLSLstd450Ceil]                    = "Ceil";
 | |
|     names[GLSLstd450Fract]                   = "Fract";
 | |
|     names[GLSLstd450Radians]                 = "Radians";
 | |
|     names[GLSLstd450Degrees]                 = "Degrees";
 | |
|     names[GLSLstd450Sin]                     = "Sin";
 | |
|     names[GLSLstd450Cos]                     = "Cos";
 | |
|     names[GLSLstd450Tan]                     = "Tan";
 | |
|     names[GLSLstd450Asin]                    = "Asin";
 | |
|     names[GLSLstd450Acos]                    = "Acos";
 | |
|     names[GLSLstd450Atan]                    = "Atan";
 | |
|     names[GLSLstd450Sinh]                    = "Sinh";
 | |
|     names[GLSLstd450Cosh]                    = "Cosh";
 | |
|     names[GLSLstd450Tanh]                    = "Tanh";
 | |
|     names[GLSLstd450Asinh]                   = "Asinh";
 | |
|     names[GLSLstd450Acosh]                   = "Acosh";
 | |
|     names[GLSLstd450Atanh]                   = "Atanh";
 | |
|     names[GLSLstd450Atan2]                   = "Atan2";
 | |
|     names[GLSLstd450Pow]                     = "Pow";
 | |
|     names[GLSLstd450Exp]                     = "Exp";
 | |
|     names[GLSLstd450Log]                     = "Log";
 | |
|     names[GLSLstd450Exp2]                    = "Exp2";
 | |
|     names[GLSLstd450Log2]                    = "Log2";
 | |
|     names[GLSLstd450Sqrt]                    = "Sqrt";
 | |
|     names[GLSLstd450InverseSqrt]             = "InverseSqrt";
 | |
|     names[GLSLstd450Determinant]             = "Determinant";
 | |
|     names[GLSLstd450MatrixInverse]           = "MatrixInverse";
 | |
|     names[GLSLstd450Modf]                    = "Modf";
 | |
|     names[GLSLstd450ModfStruct]              = "ModfStruct";
 | |
|     names[GLSLstd450FMin]                    = "FMin";
 | |
|     names[GLSLstd450SMin]                    = "SMin";
 | |
|     names[GLSLstd450UMin]                    = "UMin";
 | |
|     names[GLSLstd450FMax]                    = "FMax";
 | |
|     names[GLSLstd450SMax]                    = "SMax";
 | |
|     names[GLSLstd450UMax]                    = "UMax";
 | |
|     names[GLSLstd450FClamp]                  = "FClamp";
 | |
|     names[GLSLstd450SClamp]                  = "SClamp";
 | |
|     names[GLSLstd450UClamp]                  = "UClamp";
 | |
|     names[GLSLstd450FMix]                    = "FMix";
 | |
|     names[GLSLstd450Step]                    = "Step";
 | |
|     names[GLSLstd450SmoothStep]              = "SmoothStep";
 | |
|     names[GLSLstd450Fma]                     = "Fma";
 | |
|     names[GLSLstd450Frexp]                   = "Frexp";
 | |
|     names[GLSLstd450FrexpStruct]             = "FrexpStruct";
 | |
|     names[GLSLstd450Ldexp]                   = "Ldexp";
 | |
|     names[GLSLstd450PackSnorm4x8]            = "PackSnorm4x8";
 | |
|     names[GLSLstd450PackUnorm4x8]            = "PackUnorm4x8";
 | |
|     names[GLSLstd450PackSnorm2x16]           = "PackSnorm2x16";
 | |
|     names[GLSLstd450PackUnorm2x16]           = "PackUnorm2x16";
 | |
|     names[GLSLstd450PackHalf2x16]            = "PackHalf2x16";
 | |
|     names[GLSLstd450PackDouble2x32]          = "PackDouble2x32";
 | |
|     names[GLSLstd450UnpackSnorm2x16]         = "UnpackSnorm2x16";
 | |
|     names[GLSLstd450UnpackUnorm2x16]         = "UnpackUnorm2x16";
 | |
|     names[GLSLstd450UnpackHalf2x16]          = "UnpackHalf2x16";
 | |
|     names[GLSLstd450UnpackSnorm4x8]          = "UnpackSnorm4x8";
 | |
|     names[GLSLstd450UnpackUnorm4x8]          = "UnpackUnorm4x8";
 | |
|     names[GLSLstd450UnpackDouble2x32]        = "UnpackDouble2x32";
 | |
|     names[GLSLstd450Length]                  = "Length";
 | |
|     names[GLSLstd450Distance]                = "Distance";
 | |
|     names[GLSLstd450Cross]                   = "Cross";
 | |
|     names[GLSLstd450Normalize]               = "Normalize";
 | |
|     names[GLSLstd450FaceForward]             = "FaceForward";
 | |
|     names[GLSLstd450Reflect]                 = "Reflect";
 | |
|     names[GLSLstd450Refract]                 = "Refract";
 | |
|     names[GLSLstd450FindILsb]                = "FindILsb";
 | |
|     names[GLSLstd450FindSMsb]                = "FindSMsb";
 | |
|     names[GLSLstd450FindUMsb]                = "FindUMsb";
 | |
|     names[GLSLstd450InterpolateAtCentroid]   = "InterpolateAtCentroid";
 | |
|     names[GLSLstd450InterpolateAtSample]     = "InterpolateAtSample";
 | |
|     names[GLSLstd450InterpolateAtOffset]     = "InterpolateAtOffset";
 | |
|     names[GLSLstd450NMin]                    = "NMin";
 | |
|     names[GLSLstd450NMax]                    = "NMax";
 | |
|     names[GLSLstd450NClamp]                  = "NClamp";
 | |
| }
 | |
| 
 | |
| static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint)
 | |
| {
 | |
|     if (strcmp(name, spv::E_SPV_AMD_shader_ballot) == 0) {
 | |
|         switch (entrypoint) {
 | |
|         case SwizzleInvocationsAMD:         return "SwizzleInvocationsAMD";
 | |
|         case SwizzleInvocationsMaskedAMD:   return "SwizzleInvocationsMaskedAMD";
 | |
|         case WriteInvocationAMD:            return "WriteInvocationAMD";
 | |
|         case MbcntAMD:                      return "MbcntAMD";
 | |
|         default:                            return "Bad";
 | |
|         }
 | |
|     } else if (strcmp(name, spv::E_SPV_AMD_shader_trinary_minmax) == 0) {
 | |
|         switch (entrypoint) {
 | |
|         case FMin3AMD:      return "FMin3AMD";
 | |
|         case UMin3AMD:      return "UMin3AMD";
 | |
|         case SMin3AMD:      return "SMin3AMD";
 | |
|         case FMax3AMD:      return "FMax3AMD";
 | |
|         case UMax3AMD:      return "UMax3AMD";
 | |
|         case SMax3AMD:      return "SMax3AMD";
 | |
|         case FMid3AMD:      return "FMid3AMD";
 | |
|         case UMid3AMD:      return "UMid3AMD";
 | |
|         case SMid3AMD:      return "SMid3AMD";
 | |
|         default:            return "Bad";
 | |
|         }
 | |
|     } else if (strcmp(name, spv::E_SPV_AMD_shader_explicit_vertex_parameter) == 0) {
 | |
|         switch (entrypoint) {
 | |
|         case InterpolateAtVertexAMD:    return "InterpolateAtVertexAMD";
 | |
|         default:                        return "Bad";
 | |
|         }
 | |
|     }
 | |
|     else if (strcmp(name, spv::E_SPV_AMD_gcn_shader) == 0) {
 | |
|         switch (entrypoint) {
 | |
|         case CubeFaceIndexAMD:      return "CubeFaceIndexAMD";
 | |
|         case CubeFaceCoordAMD:      return "CubeFaceCoordAMD";
 | |
|         case TimeAMD:               return "TimeAMD";
 | |
|         default:
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return "Bad";
 | |
| }
 | |
| 
 | |
| static const char* GLSLextNVGetDebugNames(const char* name, unsigned entrypoint)
 | |
| {
 | |
|     if (strcmp(name, spv::E_SPV_NV_sample_mask_override_coverage) == 0 ||
 | |
|         strcmp(name, spv::E_SPV_NV_geometry_shader_passthrough) == 0 ||
 | |
|         strcmp(name, spv::E_ARB_shader_viewport_layer_array) == 0 ||
 | |
|         strcmp(name, spv::E_SPV_NV_viewport_array2) == 0 ||
 | |
|         strcmp(name, spv::E_SPV_NVX_multiview_per_view_attributes) == 0 ||
 | |
|         strcmp(name, spv::E_SPV_NV_fragment_shader_barycentric) == 0 ||
 | |
|         strcmp(name, spv::E_SPV_NV_mesh_shader) == 0 ||
 | |
|         strcmp(name, spv::E_SPV_NV_shader_image_footprint) == 0) {
 | |
|         switch (entrypoint) {
 | |
|         // NV builtins
 | |
|         case BuiltInViewportMaskNV:                 return "ViewportMaskNV";
 | |
|         case BuiltInSecondaryPositionNV:            return "SecondaryPositionNV";
 | |
|         case BuiltInSecondaryViewportMaskNV:        return "SecondaryViewportMaskNV";
 | |
|         case BuiltInPositionPerViewNV:              return "PositionPerViewNV";
 | |
|         case BuiltInViewportMaskPerViewNV:          return "ViewportMaskPerViewNV";
 | |
|         case BuiltInBaryCoordNV:                    return "BaryCoordNV";
 | |
|         case BuiltInBaryCoordNoPerspNV:             return "BaryCoordNoPerspNV";
 | |
|         case BuiltInTaskCountNV:                    return "TaskCountNV";
 | |
|         case BuiltInPrimitiveCountNV:               return "PrimitiveCountNV";
 | |
|         case BuiltInPrimitiveIndicesNV:             return "PrimitiveIndicesNV";
 | |
|         case BuiltInClipDistancePerViewNV:          return "ClipDistancePerViewNV";
 | |
|         case BuiltInCullDistancePerViewNV:          return "CullDistancePerViewNV";
 | |
|         case BuiltInLayerPerViewNV:                 return "LayerPerViewNV";
 | |
|         case BuiltInMeshViewCountNV:                return "MeshViewCountNV";
 | |
|         case BuiltInMeshViewIndicesNV:              return "MeshViewIndicesNV";
 | |
| 
 | |
|         // NV Capabilities
 | |
|         case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV";
 | |
|         case CapabilityShaderViewportMaskNV:        return "ShaderViewportMaskNV";
 | |
|         case CapabilityShaderStereoViewNV:          return "ShaderStereoViewNV";
 | |
|         case CapabilityPerViewAttributesNV:         return "PerViewAttributesNV";
 | |
|         case CapabilityFragmentBarycentricNV:       return "FragmentBarycentricNV";
 | |
|         case CapabilityMeshShadingNV:               return "MeshShadingNV";
 | |
|         case CapabilityImageFootprintNV:            return "ImageFootprintNV";
 | |
|         case CapabilitySampleMaskOverrideCoverageNV:return "SampleMaskOverrideCoverageNV";
 | |
| 
 | |
|         // NV Decorations
 | |
|         case DecorationOverrideCoverageNV:          return "OverrideCoverageNV";
 | |
|         case DecorationPassthroughNV:               return "PassthroughNV";
 | |
|         case DecorationViewportRelativeNV:          return "ViewportRelativeNV";
 | |
|         case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV";
 | |
|         case DecorationPerVertexNV:                 return "PerVertexNV";
 | |
|         case DecorationPerPrimitiveNV:              return "PerPrimitiveNV";
 | |
|         case DecorationPerViewNV:                   return "PerViewNV";
 | |
|         case DecorationPerTaskNV:                   return "PerTaskNV";
 | |
| 
 | |
|         default:                                    return "Bad";
 | |
|         }
 | |
|     }
 | |
|     return "Bad";
 | |
| }
 | |
| 
 | |
| void Disassemble(std::ostream& out, const std::vector<unsigned int>& stream)
 | |
| {
 | |
|     SpirvStream SpirvStream(out, stream);
 | |
|     spv::Parameterize();
 | |
|     GLSLstd450GetDebugNames(GlslStd450DebugNames);
 | |
|     SpirvStream.validate();
 | |
|     SpirvStream.processInstructions();
 | |
| }
 | |
| 
 | |
| }; // end namespace spv
 |