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