Revision control

++ //
++ // 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 <cstdint>
++ #include <cstdlib>
++ #include <cstring>
++ #include <cassert>
++ #include <iomanip>
++ #include <stack>
++ #include <sstream>
++ #include <cstring>
++ #include <utility>
++
++ #include "disassemble.h"
++ #include "doc.h"
++ #include "spvUtil.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"
++ #include "GLSL.ext.ARM.h"
++ #include "NonSemanticShaderDebugInfo.h"
++ #include "GLSL.ext.QCOM.h"
++ }
++ }
++ static const char* GlslStd450DebugNames[spv::GLSLstd450Count];
++
++ namespace spv {
++
++ static const char* GLSLextAMDGetDebugNames(const char*, unsigned);
++ static const char* GLSLextNVGetDebugNames(const char*, unsigned);
++ static const char* NonSemanticShaderDebugInfoGetDebugNames(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,
++ NonSemanticDebugBreakExtInst,
++ NonSemanticShaderDebugInfo
++ };
++
++ // 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) : Op::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);
++ std::pair<int, std::string> decodeString();
++ 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[enumCast(opCode)].hasType()) {
++ typeId = stream[word++];
++ --numOperands;
++ }
++
++ // Result <id>
++ Id resultId = 0;
++ if (InstructionDesc[enumCast(opCode)].hasResult()) {
++ resultId = stream[word++];
++ --numOperands;
++
++ if (resultId >= bound)
++ Kill(out, "Bad <id>");
++
++ // 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 << " ";
++ }
++ }
++
++ // decode string from words at current position (non-consuming)
++ std::pair<int, std::string> SpirvStream::decodeString()
++ {
++ std::string res;
++ int wordPos = word;
++ char c;
++ bool done = false;
++
++ do {
++ unsigned int content = stream[wordPos];
++ for (int charCount = 0; charCount < 4; ++charCount) {
++ c = content & 0xff;
++ content >>= 8;
++ if (c == '\0') {
++ done = true;
++ break;
++ }
++ res += c;
++ }
++ ++wordPos;
++ } while(! done);
++
++ return std::make_pair(wordPos - word, res);
++ }
++
++ // return the number of operands consumed by the string
++ int SpirvStream::disassembleString()
++ {
++ out << " \"";
++
++ std::pair<int, std::string> decoderes = decodeString();
++
++ out << decoderes.second;
++ out << "\"";
++
++ word += decoderes.first;
++
++ return decoderes.first;
++ }
++
++ static uint32_t popcount(uint32_t mask)
++ {
++ uint32_t count = 0;
++ while (mask) {
++ if (mask & 1) {
++ count++;
++ }
++ mask >>= 1;
++ }
++ return count;
++ }
++
++ void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands)
++ {
++ // Process the opcode
++
++ out << (OpcodeString((int)opCode) + 2); // leave out the "Op"
++
++ if (opCode == Op::OpLoopMerge || opCode == Op::OpSelectionMerge)
++ nextNestedControl = stream[word];
++ else if (opCode == Op::OpBranchConditional || opCode == Op::OpSwitch) {
++ if (nextNestedControl) {
++ nestedControl.push(nextNestedControl);
++ nextNestedControl = 0;
++ }
++ } else if (opCode == Op::OpExtInstImport) {
++ idDescriptor[resultId] = decodeString().second;
++ }
++ else {
++ if (resultId != 0 && idDescriptor[resultId].size() == 0) {
++ switch (opCode) {
++ case Op::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 Op::OpTypeFloat:
++ switch (stream[word]) {
++ case 4:
++ case 6:
++ case 8:
++ case 16:
++ if (numOperands > 1) {
++ switch (stream[word+1]) {
++ default:
++ assert(0); [[fallthrough]];
++ case (int)spv::FPEncoding::BFloat16KHR:
++ idDescriptor[resultId] = "bfloat16_t";
++ break;
++ case (int)spv::FPEncoding::Float8E4M3EXT:
++ idDescriptor[resultId] = "floate4m3_t";
++ break;
++ case (int)spv::FPEncoding::Float8E5M2EXT:
++ idDescriptor[resultId] = "floate5m2_t";
++ break;
++ case (int)spv::FPEncoding::Float4E2M1EXT:
++ idDescriptor[resultId] = "floate2m1_t";
++ break;
++ case (int)spv::FPEncoding::Float6E3M2EXT:
++ idDescriptor[resultId] = "floate3m2_t";
++ break;
++ case (int)spv::FPEncoding::Float6E2M3EXT:
++ idDescriptor[resultId] = "floate2m3_t";
++ break;
++ case (int)spv::FPEncoding::Float8UnsignedE8M0EXT:
++ idDescriptor[resultId] = "floatue8m0_t";
++ break;
++ case (int)spv::FPEncoding::MXInt8EXT:
++ idDescriptor[resultId] = "floatmxint8_t";
++ break;
++ }
++ } else {
++ idDescriptor[resultId] = "float16_t";
++ }
++ break;
++ default: assert(0); [[fallthrough]];
++ case 32: idDescriptor[resultId] = "float"; break;
++ case 64: idDescriptor[resultId] = "float64_t"; break;
++ }
++ break;
++ case Op::OpTypeBool:
++ idDescriptor[resultId] = "bool";
++ break;
++ case Op::OpTypeStruct:
++ idDescriptor[resultId] = "struct";
++ break;
++ case Op::OpTypePointer:
++ case Op::OpTypeUntypedPointerKHR:
++ idDescriptor[resultId] = "ptr";
++ break;
++ case Op::OpTypeVector:
++ if (stream[word] >= bound)
++ Kill(out, "Bad <id>");
++ if (idDescriptor[stream[word]].size() > 0) {
++ if (idDescriptor[stream[word]].substr(0,2) == "bf") {
++ idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 2);
++ } else {
++ 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 == Op::OpTypeImage) {
++ out << " ";
++ disassembleIds(1);
++ out << " " << DimensionString((int)(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((int)(ImageFormat)stream[word++]);
++
++ if (numOperands == 8) {
++ out << " " << AccessQualifierString(stream[word++]);
++ }
++ return;
++ }
++
++ // Handle all the parameterized operands
++ for (int op = 0; op < InstructionDesc[enumCast(opCode)].operands.getNum() && numOperands > 0; ++op) {
++ out << " ";
++ OperandClass operandClass = InstructionDesc[enumCast(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 == Op::OpName)
++ idDescriptor[stream[word - 1]] = decodeString().second;
++ break;
++ case OperandVariableIds:
++ disassembleIds(numOperands);
++ return;
++ case OperandImageOperands:
++ outputMask(OperandImageOperands, stream[word++]);
++ --numOperands;
++ disassembleIds(numOperands);
++ return;
++ case OperandOptionalLiteral:
++ case OperandVariableLiterals:
++ if ((opCode == Op::OpDecorate && stream[word - 1] == Decoration::BuiltIn) ||
++ (opCode == Op::OpMemberDecorate && stream[word - 1] == Decoration::BuiltIn)) {
++ 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 == Op::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("NonSemantic.DebugBreak", name) == 0) {
++ extInstSet = NonSemanticDebugBreakExtInst;
++ } else if (strncmp("NonSemantic.Shader.DebugInfo.", name, strlen("NonSemantic.Shader.DebugInfo.")) ==
++ 0) {
++ extInstSet = NonSemanticShaderDebugInfo;
++ } 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)";
++ } else if (extInstSet == NonSemanticDebugBreakExtInst) {
++ out << "(DebugBreak)";
++ } else if (extInstSet == NonSemanticShaderDebugInfo) {
++ out << "(" << NonSemanticShaderDebugInfoGetDebugNames(entrypoint) << ")";
++ }
++ }
++ break;
++ case OperandOptionalLiteralString:
++ case OperandLiteralString:
++ numOperands -= disassembleString();
++ break;
++ case OperandVariableLiteralStrings:
++ while (numOperands > 0)
++ numOperands -= disassembleString();
++ return;
++ case OperandMemoryAccess:
++ {
++ outputMask(OperandMemoryAccess, stream[word++]);
++ --numOperands;
++ // Put a space after "None" if there are any remaining operands
++ if (numOperands && stream[word-1] == 0) {
++ out << " ";
++ }
++ uint32_t mask = stream[word-1];
++ // 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 (mask & (uint32_t)MemoryAccessMask::Aligned) {
++ disassembleImmediates(1);
++ numOperands--;
++ if (numOperands)
++ out << " ";
++ }
++
++ uint32_t bitCount = popcount(mask & (uint32_t)(MemoryAccessMask::MakePointerAvailable | MemoryAccessMask::MakePointerVisible));
++ disassembleIds(bitCount);
++ numOperands -= bitCount;
++ }
++ break;
++ case OperandTensorAddressingOperands:
++ {
++ outputMask(OperandTensorAddressingOperands, stream[word++]);
++ --numOperands;
++ // Put a space after "None" if there are any remaining operands
++ if (numOperands && stream[word-1] == 0) {
++ out << " ";
++ }
++ uint32_t bitCount = popcount(stream[word-1]);
++ disassembleIds(bitCount);
++ numOperands -= bitCount;
++ }
++ break;
++ 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 (unsigned)BuiltIn::ViewportMaskNV: return "ViewportMaskNV";
++ case (unsigned)BuiltIn::SecondaryPositionNV: return "SecondaryPositionNV";
++ case (unsigned)BuiltIn::SecondaryViewportMaskNV: return "SecondaryViewportMaskNV";
++ case (unsigned)BuiltIn::PositionPerViewNV: return "PositionPerViewNV";
++ case (unsigned)BuiltIn::ViewportMaskPerViewNV: return "ViewportMaskPerViewNV";
++ case (unsigned)BuiltIn::BaryCoordNV: return "BaryCoordNV";
++ case (unsigned)BuiltIn::BaryCoordNoPerspNV: return "BaryCoordNoPerspNV";
++ case (unsigned)BuiltIn::TaskCountNV: return "TaskCountNV";
++ case (unsigned)BuiltIn::PrimitiveCountNV: return "PrimitiveCountNV";
++ case (unsigned)BuiltIn::PrimitiveIndicesNV: return "PrimitiveIndicesNV";
++ case (unsigned)BuiltIn::ClipDistancePerViewNV: return "ClipDistancePerViewNV";
++ case (unsigned)BuiltIn::CullDistancePerViewNV: return "CullDistancePerViewNV";
++ case (unsigned)BuiltIn::LayerPerViewNV: return "LayerPerViewNV";
++ case (unsigned)BuiltIn::MeshViewCountNV: return "MeshViewCountNV";
++ case (unsigned)BuiltIn::MeshViewIndicesNV: return "MeshViewIndicesNV";
++
++ // NV Capabilities
++ case (unsigned)Capability::GeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV";
++ case (unsigned)Capability::ShaderViewportMaskNV: return "ShaderViewportMaskNV";
++ case (unsigned)Capability::ShaderStereoViewNV: return "ShaderStereoViewNV";
++ case (unsigned)Capability::PerViewAttributesNV: return "PerViewAttributesNV";
++ case (unsigned)Capability::FragmentBarycentricNV: return "FragmentBarycentricNV";
++ case (unsigned)Capability::MeshShadingNV: return "MeshShadingNV";
++ case (unsigned)Capability::ImageFootprintNV: return "ImageFootprintNV";
++ case (unsigned)Capability::SampleMaskOverrideCoverageNV:return "SampleMaskOverrideCoverageNV";
++
++ // NV Decorations
++ case (unsigned)Decoration::OverrideCoverageNV: return "OverrideCoverageNV";
++ case (unsigned)Decoration::PassthroughNV: return "PassthroughNV";
++ case (unsigned)Decoration::ViewportRelativeNV: return "ViewportRelativeNV";
++ case (unsigned)Decoration::SecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV";
++ case (unsigned)Decoration::PerVertexNV: return "PerVertexNV";
++ case (unsigned)Decoration::PerPrimitiveNV: return "PerPrimitiveNV";
++ case (unsigned)Decoration::PerViewNV: return "PerViewNV";
++ case (unsigned)Decoration::PerTaskNV: return "PerTaskNV";
++
++ default: return "Bad";
++ }
++ }
++ return "Bad";
++ }
++
++ static const char* NonSemanticShaderDebugInfoGetDebugNames(unsigned entrypoint)
++ {
++ switch (entrypoint) {
++ case NonSemanticShaderDebugInfoDebugInfoNone: return "DebugInfoNone";
++ case NonSemanticShaderDebugInfoDebugCompilationUnit: return "DebugCompilationUnit";
++ case NonSemanticShaderDebugInfoDebugTypeBasic: return "DebugTypeBasic";
++ case NonSemanticShaderDebugInfoDebugTypePointer: return "DebugTypePointer";
++ case NonSemanticShaderDebugInfoDebugTypeQualifier: return "DebugTypeQualifier";
++ case NonSemanticShaderDebugInfoDebugTypeArray: return "DebugTypeArray";
++ case NonSemanticShaderDebugInfoDebugTypeVector: return "DebugTypeVector";
++ case NonSemanticShaderDebugInfoDebugTypedef: return "DebugTypedef";
++ case NonSemanticShaderDebugInfoDebugTypeFunction: return "DebugTypeFunction";
++ case NonSemanticShaderDebugInfoDebugTypeEnum: return "DebugTypeEnum";
++ case NonSemanticShaderDebugInfoDebugTypeComposite: return "DebugTypeComposite";
++ case NonSemanticShaderDebugInfoDebugTypeMember: return "DebugTypeMember";
++ case NonSemanticShaderDebugInfoDebugTypeInheritance: return "DebugTypeInheritance";
++ case NonSemanticShaderDebugInfoDebugTypePtrToMember: return "DebugTypePtrToMember";
++ case NonSemanticShaderDebugInfoDebugTypeTemplate: return "DebugTypeTemplate";
++ case NonSemanticShaderDebugInfoDebugTypeTemplateParameter: return "DebugTypeTemplateParameter";
++ case NonSemanticShaderDebugInfoDebugTypeTemplateTemplateParameter: return "DebugTypeTemplateTemplateParameter";
++ case NonSemanticShaderDebugInfoDebugTypeTemplateParameterPack: return "DebugTypeTemplateParameterPack";
++ case NonSemanticShaderDebugInfoDebugGlobalVariable: return "DebugGlobalVariable";
++ case NonSemanticShaderDebugInfoDebugFunctionDeclaration: return "DebugFunctionDeclaration";
++ case NonSemanticShaderDebugInfoDebugFunction: return "DebugFunction";
++ case NonSemanticShaderDebugInfoDebugLexicalBlock: return "DebugLexicalBlock";
++ case NonSemanticShaderDebugInfoDebugLexicalBlockDiscriminator: return "DebugLexicalBlockDiscriminator";
++ case NonSemanticShaderDebugInfoDebugScope: return "DebugScope";
++ case NonSemanticShaderDebugInfoDebugNoScope: return "DebugNoScope";
++ case NonSemanticShaderDebugInfoDebugInlinedAt: return "DebugInlinedAt";
++ case NonSemanticShaderDebugInfoDebugLocalVariable: return "DebugLocalVariable";
++ case NonSemanticShaderDebugInfoDebugInlinedVariable: return "DebugInlinedVariable";
++ case NonSemanticShaderDebugInfoDebugDeclare: return "DebugDeclare";
++ case NonSemanticShaderDebugInfoDebugValue: return "DebugValue";
++ case NonSemanticShaderDebugInfoDebugOperation: return "DebugOperation";
++ case NonSemanticShaderDebugInfoDebugExpression: return "DebugExpression";
++ case NonSemanticShaderDebugInfoDebugMacroDef: return "DebugMacroDef";
++ case NonSemanticShaderDebugInfoDebugMacroUndef: return "DebugMacroUndef";
++ case NonSemanticShaderDebugInfoDebugImportedEntity: return "DebugImportedEntity";
++ case NonSemanticShaderDebugInfoDebugSource: return "DebugSource";
++ case NonSemanticShaderDebugInfoDebugFunctionDefinition: return "DebugFunctionDefinition";
++ case NonSemanticShaderDebugInfoDebugSourceContinued: return "DebugSourceContinued";
++ case NonSemanticShaderDebugInfoDebugLine: return "DebugLine";
++ case NonSemanticShaderDebugInfoDebugNoLine: return "DebugNoLine";
++ case NonSemanticShaderDebugInfoDebugBuildIdentifier: return "DebugBuildIdentifier";
++ case NonSemanticShaderDebugInfoDebugStoragePath: return "DebugStoragePath";
++ case NonSemanticShaderDebugInfoDebugEntryPoint: return "DebugEntryPoint";
++ case NonSemanticShaderDebugInfoDebugTypeMatrix: return "DebugTypeMatrix";
++ case NonSemanticShaderDebugInfoDebugTypeVectorIdEXT: return "DebugTypeVectorIdEXT";
++ case NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR: return "DebugTypeCooperativeMatrixKHR";
++ 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