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