ppsspp/Common/Vulkan/SPIRVDisasm.cpp
2017-11-25 14:08:49 -08:00

618 lines
16 KiB
C++

#include <sstream>
#include "Common/StringUtils.h"
#include "Common/Vulkan/SPIRVDisasm.h"
#include "ext/vulkan/spirv.hpp"
#define WRITE p+=sprintf
struct SpirvID {
std::string name;
int opcode;
};
struct OpInfo {
const char *name;
const char *extra;
};
static const char *executionModelNames[] = {
"Vertex", // 0,
"TessellationControl", // 1,
"TessellationEvaluation", // 2,
"Geometry", // 3,
"Fragment", // 4,
"GLCompute", // 5,
"Kernel", // 6,
};
static const char *storageClassNames[] = {
"UniformConstant",
"Input",
"Uniform",
"Output",
"Workgroup",
"CrossWorkgroup",
"Private",
"Function",
"Generic",
"PushConstant",
"AtomicCounter",
"Image",
};
static const char *decorationNames[] = {
"RelaxedPrecision", // 0,
"SpecId", // 1,
"Block", // 2,
"BufferBlock", // 3,
"RowMajor", // 4,
"ColMajor", // 5,
"ArrayStride", // 6,
"MatrixStride", // 7,
"GLSLShared", // 8,
"GLSLPacked", // 9,
"CPacked", // 10,
"BuiltIn", // 11,
nullptr, // 12
"NoPerspective", // 13,
"Flat", // 14,
"Patch", // 15,
"Centroid", // 16,
"Sample", // 17,
"Invariant", // 18,
"Restrict", // 19,
"Aliased", // 20,
"Volatile", // 21,
"Constant", // 22,
"Coherent", // 23,
"NonWritable", // 24,
"NonReadable", // 25,
"Uniform", // 26,
nullptr, // 27
"SaturatedConversion", // 28,
"Stream", // 29,
"Location", // 30,
"Component", // 31,
"Index", // 32,
"Binding", // 33,
"DescriptorSet", // 34,
"Offset", // 35,
"XfbBuffer", // 36,
"XfbStride", // 37,
"FuncParamAttr", // 38,
"FPRoundingMode", // 39,
"FPFastMathMode", // 40,
"LinkageAttributes", // 41,
"NoContraction", // 42,
"InputAttachmentIndex", // 43,
"Alignment", // 44,
};
static const OpInfo opInfo[] = {
{"Nop"}, // 0,
{"Undef"}, // 1,
{"SourceContinued"}, // 2,
{"Source"}, // 3,
{"SourceExtension"}, // 4,
{"Name"}, // 5,
{"MemberName"}, // 6,
{"String"}, // 7,
{"Line"}, // 8,
{nullptr}, // 9
{"Extension"}, // 10,
{"ExtInstImport"}, // 11,
{"ExtInst"}, // 12,
{nullptr}, // 13
{"MemoryModel"}, // 14,
{"EntryPoint"}, // 15,
{"ExecutionMode"}, // 16,
{"Capability"}, // 17,
{nullptr}, // 18
{"TypeVoid"}, // 19,
{"TypeBool"}, // 20,
{"TypeInt"}, // 21,
{"TypeFloat"}, // 22,
{"TypeVector"}, // 23,
{"TypeMatrix"}, // 24,
{"TypeImage"}, // 25,
{"TypeSampler"}, // 26,
{"TypeSampledImage"}, // 27,
{"TypeArray"}, // 28,
{"TypeRuntimeArray"}, // 29,
{"TypeStruct"}, // 30,
{"TypeOpaque"}, // 31,
{"TypePointer"}, // 32,
{"TypeFunction"}, // 33,
{"TypeEvent"}, // 34,
{"TypeDeviceEvent"}, // 35,
{"TypeReserveId"}, // 36,
{"TypeQueue"}, // 37,
{"TypePipe"}, // 38,
{"TypeForwardPointer"}, // 39,
{nullptr}, // 40
{"ConstantTrue"}, // 41,
{"ConstantFalse"}, // 42,
{"Constant"}, // 43,
{"ConstantComposite"}, // 44,
{"ConstantSampler"}, // 45,
{"ConstantNull"}, // 46,
{nullptr}, // 47
{"SpecConstantTrue"}, // 48,
{"SpecConstantFalse"}, // 49,
{"SpecConstant"}, // 50,
{"SpecConstantComposite"}, // 51,
{"SpecConstantOp"}, // 52,
{nullptr}, // 53
{"Function"}, // 54,
{"FunctionParameter"}, // 55,
{"FunctionEnd"}, // 56,
{"FunctionCall"}, // 57,
{nullptr}, // 58
{"Variable"}, // 59,
{"ImageTexelPointer"}, // 60,
{"Load"}, // 61,
{"Store"}, // 62,
{"CopyMemory"}, // 63,
{"CopyMemorySized"}, // 64,
{"AccessChain"}, // 65,
{"InBoundsAccessChain"}, // 66,
{"PtrAccessChain"}, // 67,
{"ArrayLength"}, // 68,
{"GenericPtrMemSemantics"}, // 69,
{"InBoundsPtrAccessChain"}, // 70,
{"Decorate"}, // 71,
{"MemberDecorate"}, // 72,
{"DecorationGroup"}, // 73,
{"GroupDecorate"}, // 74,
{"GroupMemberDecorate"}, // 75,
{nullptr}, // 76
{"VectorExtractDynamic"}, // 77,
{"VectorInsertDynamic"}, // 78,
{"VectorShuffle"}, // 79,
{"CompositeConstruct"}, // 80,
{"CompositeExtract"}, // 81,
{"CompositeInsert"}, // 82,
{"CopyObject"}, // 83,
{"Transpose"}, // 84,
{nullptr}, // 85
{"SampledImage"}, // 86,
{"ImageSampleImplicitLod"}, // 87,
{"ImageSampleExplicitLod"}, // 88,
{"ImageSampleDrefImplicitLod"}, // 89,
{"ImageSampleDrefExplicitLod"}, // 90,
{"ImageSampleProjImplicitLod"}, // 91,
{"ImageSampleProjExplicitLod"}, // 92,
{"ImageSampleProjDrefImplicitLod"}, // 93,
{"ImageSampleProjDrefExplicitLod"}, // 94,
{"ImageFetch"}, // 95,
{"ImageGather"}, // 96,
{"ImageDrefGather"}, // 97,
{"ImageRead"}, // 98,
{"ImageWrite"}, // 99,
{"Image"}, // 100,
{"ImageQueryFormat"}, // 101,
{"ImageQueryOrder"}, // 102,
{"ImageQuerySizeLod"}, // 103,
{"ImageQuerySize"}, // 104,
{"ImageQueryLod"}, // 105,
{"ImageQueryLevels"}, // 106,
{"ImageQuerySamples"}, // 107,
{nullptr}, // 108
{"ConvertFToU"}, // 109,
{"ConvertFToS"}, // 110,
{"ConvertSToF"}, // 111,
{"ConvertUToF"}, // 112,
{"UConvert"}, // 113,
{"SConvert"}, // 114,
{"FConvert"}, // 115,
{"QuantizeToF16"}, // 116,
{"ConvertPtrToU"}, // 117,
{"SatConvertSToU"}, // 118,
{"SatConvertUToS"}, // 119,
{"ConvertUToPtr"}, // 120,
{"PtrCastToGeneric"}, // 121,
{"GenericCastToPtr"}, // 122,
{"GenericCastToPtrExplicit"}, // 123,
{"Bitcast"}, // 124,
{nullptr}, // 125
{"SNegate"}, // 126,
{"FNegate"}, // 127,
{"IAdd"}, // 128,
{"FAdd", "+"}, // 129,
{"ISub"}, // 130,
{"FSub", "-"}, // 131,
{"IMul"}, // 132,
{"FMul", "*"}, // 133,
{"UDiv"}, // 134,
{"SDiv"}, // 135,
{"FDiv", "/"}, // 136,
{"UMod"}, // 137,
{"SRem"}, // 138,
{"SMod"}, // 139,
{"FRem"}, // 140,
{"FMod", "%"}, // 141,
{"VectorTimesScalar"}, // 142,
{"MatrixTimesScalar"}, // 143,
{"VectorTimesMatrix"}, // 144,
{"MatrixTimesVector"}, // 145,
{"MatrixTimesMatrix"}, // 146,
{"OuterProduct"}, // 147,
{"Dot", " dot "}, // 148,
{"IAddCarry"}, // 149,
{"ISubBorrow"}, // 150,
{"UMulExtended"}, // 151,
{"SMulExtended"}, // 152,
{nullptr}, // 153
{"Any"}, // 154,
{"All"}, // 155,
{"IsNan"}, // 156,
{"IsInf"}, // 157,
{"IsFinite"}, // 158,
{"IsNormal"}, // 159,
{"SignBitSet"}, // 160,
{"LessOrGreater"}, // 161,
{"Ordered"}, // 162,
{"Unordered"}, // 163,
{"LogicalEqual"}, // 164,
{"LogicalNotEqual"}, // 165,
{"LogicalOr"}, // 166,
{"LogicalAnd"}, // 167,
{"LogicalNot"}, // 168,
{"Select"}, // 169,
{"IEqual"}, // 170,
{"INotEqual"}, // 171,
{"UGreaterThan"}, // 172,
{"SGreaterThan"}, // 173,
{"UGreaterThanEqual"}, // 174,
{"SGreaterThanEqual"}, // 175,
{"ULessThan"}, // 176,
{"SLessThan"}, // 177,
{"ULessThanEqual"}, // 178,
{"SLessThanEqual"}, // 179,
{"FOrdEqual", "=="}, // 180,
{"FUnordEqual", "=="}, // 181,
{"FOrdNotEqual", "!="}, // 182,
{"FUnordNotEqual", "!="}, // 183,
{"FOrdLessThan"}, // 184,
{"FUnordLessThan"}, // 185,
{"FOrdGreaterThan"}, // 186,
{"FUnordGreaterThan"}, // 187,
{"FOrdLessThanEqual"}, // 188,
{"FUnordLessThanEqual"}, // 189,
{"FOrdGreaterThanEqual"}, // 190,
{"FUnordGreaterThanEqual"}, // 191,
{ nullptr }, // 192
{ nullptr }, // 193
{"ShiftRightLogical"}, // 194,
{"ShiftRightArithmetic"}, // 195,
{"ShiftLeftLogical"}, // 196,
{"BitwiseOr", "|"}, // 197,
{"BitwiseXor", "^"}, // 198,
{"BitwiseAnd", "&"}, // 199,
{"Not", "~"}, // 200,
{"BitFieldInsert"}, // 201,
{"BitFieldSExtract"}, // 202,
{"BitFieldUExtract"}, // 203,
{"BitReverse"}, // 204,
{"BitCount"}, // 205,
{nullptr},
{"DPdx"}, // 207,
{"DPdy"}, // 208,
{"Fwidth"}, // 209,
{"DPdxFine"}, // 210,
{"DPdyFine"}, // 211,
{"FwidthFine"}, // 212,
{"DPdxCoarse"}, // 213,
{"DPdyCoarse"}, // 214,
{"FwidthCoarse"}, // 215,
{nullptr}, // 216
{nullptr}, // 217
{"EmitVertex"}, // 218,
{"EndPrimitive"}, // 219,
{"EmitStreamVertex"}, // 220,
{"EndStreamPrimitive"}, // 221,
{nullptr}, // 222
{nullptr}, // 223
{"ControlBarrier"}, // 224,
{"MemoryBarrier"}, // 225,
{nullptr}, // 226
{"AtomicLoad"}, // 227,
{"AtomicStore"}, // 228,
{"AtomicExchange"}, // 229,
{"AtomicCompareExchange"}, // 230,
{"AtomicCompareExchangeWeak"}, // 231,
{"AtomicIIncrement"}, // 232,
{"AtomicIDecrement"}, // 233,
{"AtomicIAdd"}, // 234,
{"AtomicISub"}, // 235,
{"AtomicSMin"}, // 236,
{"AtomicUMin"}, // 237,
{"AtomicSMax"}, // 238,
{"AtomicUMax"}, // 239,
{"AtomicAnd"}, // 240,
{"AtomicOr"}, // 241,
{"AtomicXor"}, // 242,
{ nullptr }, // 243,
{ nullptr }, // 244,
{"Phi"}, // 245,
{"LoopMerge"}, // 246,
{"SelectionMerge"}, // 247,
{"Label"}, // 248,
{"Branch"}, // 249,
{"BranchConditional"}, // 250,
{"Switch"}, // 251,
{"Kill"}, // 252,
{"Return"}, // 253,
{"ReturnValue"}, // 254,
{"Unreachable"}, // 255,
{"LifetimeStart"}, // 256,
{"LifetimeStop"}, // 257,
{ nullptr },
{"GroupAsyncCopy"}, // 259,
{"GroupWaitEvents"}, // 260,
{"GroupAll"}, // 261,
{"GroupAny"}, // 262,
{"GroupBroadcast"}, // 263,
{"GroupIAdd"}, // 264,
{"GroupFAdd"}, // 265,
{"GroupFMin"}, // 266,
{"GroupUMin"}, // 267,
{"GroupSMin"}, // 268,
{"GroupFMax"}, // 269,
{"GroupUMax"}, // 270,
{"GroupSMax"}, // 271,
{nullptr}, // 272
{nullptr}, // 273
{"ReadPipe"}, // 274,
{"WritePipe"}, // 275,
{"ReservedReadPipe"}, // 276,
{"ReservedWritePipe"}, // 277,
{"ReserveReadPipePackets"}, // 278,
{"ReserveWritePipePackets"}, // 279,
{"CommitReadPipe"}, // 280,
{"CommitWritePipe"}, // 281,
{"IsValidReserveId"}, // 282,
{"GetNumPipePackets"}, // 283,
{"GetMaxPipePackets"}, // 284,
{"GroupReserveReadPipePackets"}, // 285,
{"GroupReserveWritePipePackets"}, // 286,
{"GroupCommitReadPipe"}, // 287,
{"GroupCommitWritePipe"}, // 288,
{ nullptr }, // 289
{ nullptr }, // 290
{"EnqueueMarker"}, // 291,
{"EnqueueKernel"}, // 292,
{"GetKernelNDrangeSubGroupCount"}, // 293,
{"GetKernelNDrangeMaxSubGroupSize"}, // 294,
{"GetKernelWorkGroupSize"}, // 295,
{"GetKernelPreferredWorkGroupSizeMultiple"}, // 296,
{"RetainEvent"}, // 297,
{"ReleaseEvent"}, // 298,
{"CreateUserEvent"}, // 299,
{"IsValidEvent"}, // 300,
{"SetUserEventStatus"}, // 301,
{"CaptureEventProfilingInfo"}, // 302,
{"GetDefaultQueue"}, // 303,
{"BuildNDRange"}, // 304,
{"ImageSparseSampleImplicitLod"}, // 305,
{"ImageSparseSampleExplicitLod"}, // 306,
{"ImageSparseSampleDrefImplicitLod"}, // 307,
{"ImageSparseSampleDrefExplicitLod"}, // 308,
{"ImageSparseSampleProjImplicitLod"}, // 309,
{"ImageSparseSampleProjExplicitLod"}, // 310,
{"ImageSparseSampleProjDrefImplicitLod"}, // 311,
{"ImageSparseSampleProjDrefExplicitLod"}, // 312,
{"ImageSparseFetch"}, // 313,
{"ImageSparseGather"}, // 314,
{"ImageSparseDrefGather"}, // 315,
{"ImageSparseTexelsResident"}, // 316,
{"NoLine"}, // 317,
{"AtomicFlagTestAndSet"}, // 318,
{"AtomicFlagClear"}, // 319,
};
static std::string ReadSpirvString(const std::vector<uint32_t> &spirv, int offset, int *outOffset = nullptr) {
bool done = false;
std::string temp;
while (!done) {
uint32_t data = spirv[offset++];
for (int i = 0; i < 4; i++) {
char c = (char)(data & 0xff);
if (!c) {
done = true;
break;
}
temp.push_back(data);
data >>= 8;
}
if (offset == (int)spirv.size())
break;
}
if (outOffset)
*outOffset = offset;
return temp;
}
bool DisassembleSPIRV(std::vector<uint32_t> spirv, std::string *output) {
if (spirv.size() < 10) {
*output = "Too small";
return false;
}
uint32_t magic = spirv[0];
uint32_t version = spirv[1];
uint32_t generator = spirv[2];
int bound = (int)spirv[3]; // Max ID used in file.
// spirv[4] is reserved for schema.
std::vector<SpirvID> ids;
ids.resize(bound);
for (int i = 0; i < bound; i++) {
ids[i].name = StringFromFormat("%%%d", i);
}
if (magic != 0x07230203) {
*output = "Not SPIRV";
return false;
}
int indent = 0;
char *buffer = new char[1024 * 1024];
char *p = buffer;
WRITE(p, "// ======= SPIR-V version %08x =======\n// Max ID: %d\n", version, bound); // GLSL ES
int i = 5;
while (i < (int)spirv.size()) {
uint32_t d = spirv[i];
int wordCount = d >> 16;
int opcode = d & 0xFFFF;
// Force to a valid index in the array.
const OpInfo &op = opInfo[opcode >= (int)ARRAY_SIZE(opInfo) ? 0 : opcode];
int target = (i < (int)spirv.size() - 1) ? spirv[(i + 1)] : 0; // Not always, but used often enough that we get it here.
int source, source2, resType;
std::string name;
switch (opcode) {
case spv::OpTypeVoid:
case spv::OpTypeBool:
case spv::OpTypeInt:
case spv::OpTypeFloat:
case spv::OpTypeMatrix:
case spv::OpTypeImage:
case spv::OpTypeSampler:
case spv::OpTypeSampledImage:
case spv::OpTypeRuntimeArray:
case spv::OpTypeOpaque:
case spv::OpTypePointer:
case spv::OpTypeFunction:
case spv::OpTypeEvent:
case spv::OpTypeDeviceEvent:
case spv::OpTypeReserveId:
case spv::OpTypeQueue:
case spv::OpTypePipe:
case spv::OpTypeForwardPointer:
ids[target].name = op.name + 4; // Remove "Type"
ids[target].opcode = opcode;
break;
case spv::OpTypeVector:
source = spirv[i + 2];
source2 = spirv[i + 3];
ids[target].name = ids[source].name + StringFromFormat("%d", source2);
break;
case spv::OpTypeArray:
source = spirv[i + 2];
source2 = spirv[i + 3];
ids[target].name = ids[source].name + "[" + ids[source2].name + "]";
break;
case spv::OpTypeStruct:
{
ids[target].name = "struct" + ids[target].name;
WRITE(p, "struct {\n");
if (wordCount == 3) {
source = spirv[i + 2];
WRITE(p, " %s\n", ids[source].name.c_str());
} else {
int numMembers = (wordCount - 2) / 2;
for (int m = 0; m < numMembers; m++) {
int id = spirv[i + 2 + m];
int type = spirv[i + 2 + m + numMembers];
WRITE(p, " %s %s;\n", ids[type].name.c_str(), ids[id].name.c_str());
}
}
WRITE(p, "};\n");
}
break;
case spv::OpVariable:
resType = spirv[i + 1];
target = spirv[i + 2];
source = spirv[i + 3];
source2 = spirv[i + 4];
ids[target].name = storageClassNames[source];
break;
case spv::OpDecorate:
source = spirv[i + 2];
ids[target].name += std::string("[") + decorationNames[source] + "]";
break;
case spv::OpName:
ids[target].name = ReadSpirvString(spirv, i + 2);
// WRITE(p, "Name %d: '%s'\n", target, ids[target].name.c_str());
break;
case spv::OpMemberName:
break;
case spv::OpStore:
source = spirv[i + 2];
WRITE(p, "Store(%s, %s)\n", ids[target].name.c_str(), ids[source].name.c_str());
break;
case spv::OpLoad:
resType = spirv[i + 1];
target = spirv[i + 2];
source = spirv[i + 3];
WRITE(p, "%s (%s) := Load(%s)\n", ids[target].name.c_str(), ids[resType].name.c_str(), ids[source].name.c_str());
break;
case spv::OpFAdd:
case spv::OpFMul:
case spv::OpFDiv:
case spv::OpFSub:
resType = spirv[i + 2];
source = spirv[i + 3];
source2 = spirv[i + 4];
WRITE(p, "%s (%s) := %s %s %s\n", ids[target].name.c_str(), ids[resType].name.c_str(), ids[source].name.c_str(), op.extra, ids[source2].name.c_str());
break;
case spv::OpCapability:
case spv::OpMemoryModel:
case spv::OpExtInstImport:
case spv::OpExecutionMode:
case spv::OpGroupAsyncCopy:
case spv::OpSource:
case spv::OpExtension:
// hide these for now
break;
case spv::OpEntryPoint:
source = spirv[i + 1];
source2 = spirv[i + 2];
name = ReadSpirvString(spirv, i + 3);
WRITE(p, "EntryPoint %s: '%s' %s : %s\n", executionModelNames[target], ids[source].name.c_str(), ids[source2].name.c_str(), name.c_str());
break;
case spv::OpFunction:
resType = spirv[i + 1];
target = spirv[i + 2];
source = spirv[i + 3];
ids[target].name = ids[resType].name + "()";
WRITE(p, "Function %s {\n", ids[target].name.c_str());
break;
case spv::OpLabel:
WRITE(p, "label %s:\n", ids[target].name.c_str());
break;
case spv::OpReturnValue:
WRITE(p, "return %s\n", ids[target].name.c_str());
break;
case spv::OpReturn:
WRITE(p, "return\n");
break;
case spv::OpFunctionEnd:
WRITE(p, "}\n");
break;
case spv::OpSourceExtension:
break;
default:
WRITE(p, "%s (%d data words)\n", op.name, wordCount - 1);
break;
}
i += wordCount;
}
*output = buffer;
delete[] buffer;
return true;
}