From bdf2f046d9d6545f8313e3def69278602254cba7 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 18 Oct 2019 20:18:39 +0100 Subject: [PATCH] Add stubbed out DXIL bytecode decoder --- .../driver/shaders/dxbc/dxbc_container.cpp | 106 ++- .../driver/shaders/dxbc/dxbc_container.h | 9 + .../shaders/dxbc/renderdoc_dxbc.vcxproj | 5 + .../driver/shaders/dxil/dxil_bytecode.cpp | 769 ++++++++++++++++++ renderdoc/driver/shaders/dxil/dxil_bytecode.h | 33 + .../driver/shaders/dxil/llvm_bitreader.h | 2 +- .../driver/shaders/dxil/llvm_decoder.cpp | 15 +- 7 files changed, 919 insertions(+), 20 deletions(-) diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp index 16f2811d1..52712393d 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp @@ -28,6 +28,7 @@ #include "api/app/renderdoc_app.h" #include "common/common.h" #include "driver/dx/official/d3dcompiler.h" +#include "driver/shaders/dxil/dxil_bytecode.h" #include "serialise/serialiser.h" #include "strings/string_utils.h" #include "dxbc_bytecode.h" @@ -103,11 +104,15 @@ struct FileHeader // uint32 chunkOffsets[numChunks]; follows }; +struct ILDNHeader +{ + uint16_t Flags; + uint16_t NameLength; + char Name[1]; +}; + struct RDEFHeader { - uint32_t fourcc; // "RDEF" - uint32_t chunkLength; // length of this chunk - ////////////////////////////////////////////////////// // offsets are relative to this position in the file. // NOT the end of this structure. Note this differs @@ -151,9 +156,6 @@ struct RDEFResource struct SIGNHeader { - uint32_t fourcc; // "ISGN", "OSGN, "OSG5", "PCSG" - uint32_t chunkLength; // length of this chunk - ////////////////////////////////////////////////////// // offsets are relative to this position in the file. // NOT the end of this structure. Note this differs @@ -244,6 +246,9 @@ static const uint32_t FOURCC_OSG5 = MAKE_FOURCC('O', 'S', 'G', '5'); static const uint32_t FOURCC_PCSG = MAKE_FOURCC('P', 'C', 'S', 'G'); static const uint32_t FOURCC_Aon9 = MAKE_FOURCC('A', 'o', 'n', '9'); static const uint32_t FOURCC_PRIV = MAKE_FOURCC('P', 'R', 'I', 'V'); +static const uint32_t FOURCC_DXIL = MAKE_FOURCC('D', 'X', 'I', 'L'); +static const uint32_t FOURCC_ILDB = MAKE_FOURCC('I', 'L', 'D', 'B'); +static const uint32_t FOURCC_ILDN = MAKE_FOURCC('I', 'L', 'D', 'N'); int TypeByteSize(VariableType t) { @@ -473,6 +478,8 @@ D3D_PRIMITIVE_TOPOLOGY DXBCContainer::GetOutputTopology() if(m_DXBCByteCode) m_OutputTopology = m_DXBCByteCode->GetOutputTopology(); + else if(m_DXILByteCode) + m_OutputTopology = m_DXILByteCode->GetOutputTopology(); } return m_OutputTopology; @@ -490,6 +497,8 @@ const std::string &DXBCContainer::GetDisassembly() if(m_DXBCByteCode) m_Disassembly += m_DXBCByteCode->GetDisassembly(); + else if(m_DXILByteCode) + m_Disassembly += m_DXILByteCode->GetDisassembly(); } return m_Disassembly; @@ -631,13 +640,13 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength) for(uint32_t chunkIdx = 0; chunkIdx < header->numChunks; chunkIdx++) { uint32_t *fourcc = (uint32_t *)(data + chunkOffsets[chunkIdx]); - uint32_t *chunkSize = (uint32_t *)(data + chunkOffsets[chunkIdx] + sizeof(uint32_t)); + uint32_t *chunkSize = (uint32_t *)(fourcc + 1); - char *chunkContents = (char *)(data + chunkOffsets[chunkIdx] + sizeof(uint32_t) * 2); + char *chunkContents = (char *)(chunkSize + 1); if(*fourcc == FOURCC_RDEF) { - RDEFHeader *h = (RDEFHeader *)fourcc; + RDEFHeader *h = (RDEFHeader *)chunkContents; // for target version 0x500, unknown[0] is FOURCC_RD11. // for 0x501 it's "\x13\x13\D%" @@ -920,6 +929,46 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength) { m_DXBCByteCode = new DXBCBytecode::Program((const byte *)chunkContents, *chunkSize); } + else if(*fourcc == FOURCC_ILDN) + { + ILDNHeader *h = (ILDNHeader *)chunkContents; + + m_DebugFileName = rdcstr(h->Name, h->NameLength); + } + } + + if(m_DXBCByteCode == NULL) + { + // prefer ILDB if present + for(uint32_t chunkIdx = 0; chunkIdx < header->numChunks; chunkIdx++) + { + uint32_t *fourcc = (uint32_t *)(data + chunkOffsets[chunkIdx]); + uint32_t *chunkSize = (uint32_t *)(data + chunkOffsets[chunkIdx] + sizeof(uint32_t)); + + char *chunkContents = (char *)(data + chunkOffsets[chunkIdx] + sizeof(uint32_t) * 2); + + if(*fourcc == FOURCC_ILDB) + { + m_DXILByteCode = new DXIL::Program((const byte *)chunkContents, *chunkSize); + } + } + + // if we didn't find it, look for DXIL + if(m_DXILByteCode == NULL) + { + for(uint32_t chunkIdx = 0; chunkIdx < header->numChunks; chunkIdx++) + { + uint32_t *fourcc = (uint32_t *)(data + chunkOffsets[chunkIdx]); + uint32_t *chunkSize = (uint32_t *)(data + chunkOffsets[chunkIdx] + sizeof(uint32_t)); + + char *chunkContents = (char *)(data + chunkOffsets[chunkIdx] + sizeof(uint32_t) * 2); + + if(*fourcc == FOURCC_DXIL) + { + m_DXILByteCode = new DXIL::Program((const byte *)chunkContents, *chunkSize); + } + } + } } // get type/version that's used regularly and cheap to fetch @@ -929,6 +978,12 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength) m_Version.Major = m_DXBCByteCode->GetMajorVersion(); m_Version.Minor = m_DXBCByteCode->GetMinorVersion(); } + else if(m_DXILByteCode) + { + m_Type = m_DXILByteCode->GetShaderType(); + m_Version.Major = m_DXILByteCode->GetMajorVersion(); + m_Version.Minor = m_DXILByteCode->GetMinorVersion(); + } // if reflection information was stripped, attempt to reverse engineer basic info from // declarations @@ -937,19 +992,21 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength) // need to disassemble now to guess resources if(m_DXBCByteCode) m_Reflection = m_DXBCByteCode->GuessReflection(); + else if(m_DXILByteCode) + m_Reflection = m_DXILByteCode->GetReflection(); } for(uint32_t chunkIdx = 0; chunkIdx < header->numChunks; chunkIdx++) { uint32_t *fourcc = (uint32_t *)(data + chunkOffsets[chunkIdx]); - // uint32_t *chunkSize = (uint32_t *)(data + chunkOffsets[chunkIdx] + sizeof(uint32_t)); + // uint32_t *chunkSize = (uint32_t *)(fourcc + 1); - char *chunkContents = (char *)(data + chunkOffsets[chunkIdx] + sizeof(uint32_t) * 2); + char *chunkContents = (char *)(fourcc + 2); if(*fourcc == FOURCC_ISGN || *fourcc == FOURCC_OSGN || *fourcc == FOURCC_ISG1 || *fourcc == FOURCC_OSG1 || *fourcc == FOURCC_OSG5 || *fourcc == FOURCC_PCSG) { - SIGNHeader *sign = (SIGNHeader *)fourcc; + SIGNHeader *sign = (SIGNHeader *)chunkContents; std::vector *sig = NULL; @@ -1122,6 +1179,8 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength) { if(m_DXBCByteCode) m_DXBCByteCode->FetchComputeProperties(m_Reflection); + else if(m_DXILByteCode) + m_DXILByteCode->FetchComputeProperties(m_Reflection); } // initialise debug chunks last @@ -1345,7 +1404,7 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength) // if we had bytecode in this container, ensure we had reflection. If it's a blob with only an // input signature then we can do without reflection. - if(m_DXBCByteCode) + if(m_DXBCByteCode || m_DXILByteCode) { RDCASSERT(m_Reflection); } @@ -1355,6 +1414,7 @@ DXBCContainer::~DXBCContainer() { SAFE_DELETE(m_DebugInfo); SAFE_DELETE(m_DXBCByteCode); + SAFE_DELETE(m_DXILByteCode); SAFE_DELETE(m_Reflection); } @@ -1478,6 +1538,26 @@ ShaderCompileFlags EncodeFlags(const IDebugInfo *dbg) #include "3rdparty/catch/catch.hpp" +#if 0 + +TEST_CASE("DO NOT COMMIT - convenience test", "[dxbc]") +{ + // this test loads a file from disk and passes it through DXBC::DXBCContainer. Useful for when you + // are iterating on a shader and don't want to have to load a whole capture. + std::vector buf; + FileIO::slurp("/path/to/container_file.dxbc", buf); + + DXBC::DXBCContainer container(buf.data(), buf.size()); + + // the only thing fetched lazily is the disassembly, so grab that here + + std::string disasm = container.GetDisassembly(); + + RDCLOG("%s", disasm.c_str()); +} + +#endif + TEST_CASE("Check DXBC flags are non-overlapping", "[dxbc]") { for(const DXBC::FxcArg &a : DXBC::fxc_flags) diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.h b/renderdoc/driver/shaders/dxbc/dxbc_container.h index 31251a9be..00f3383ef 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.h @@ -39,6 +39,11 @@ namespace DXBCBytecode class Program; }; +namespace DXIL +{ +class Program; +}; + namespace DXBC { class IDebugInfo; @@ -151,6 +156,7 @@ public: const std::string &GetDisassembly(); const DXBCBytecode::Program *GetDXBCByteCode() { return m_DXBCByteCode; } + const DXIL::Program *GetDXILByteCode() { return m_DXILByteCode; } static void GetHash(uint32_t hash[4], const void *ByteCode, size_t BytecodeLength); static bool CheckForDebugInfo(const void *ByteCode, size_t ByteCodeLength); @@ -168,8 +174,11 @@ private: CBufferVariableType ParseRDEFType(RDEFHeader *h, char *chunk, uint32_t offset); std::map m_Variables; + rdcstr m_DebugFileName; + ShaderStatistics m_ShaderStats; DXBCBytecode::Program *m_DXBCByteCode = NULL; + DXIL::Program *m_DXILByteCode = NULL; IDebugInfo *m_DebugInfo = NULL; Reflection *m_Reflection = NULL; }; diff --git a/renderdoc/driver/shaders/dxbc/renderdoc_dxbc.vcxproj b/renderdoc/driver/shaders/dxbc/renderdoc_dxbc.vcxproj index 0a7cbe7aa..dbd497ce6 100644 --- a/renderdoc/driver/shaders/dxbc/renderdoc_dxbc.vcxproj +++ b/renderdoc/driver/shaders/dxbc/renderdoc_dxbc.vcxproj @@ -124,6 +124,11 @@ + + + {8ae46ec2-ea46-441f-bee2-94097101d6a3} + + diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp b/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp index 00680aff0..249cf880d 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp @@ -23,7 +23,776 @@ ******************************************************************************/ #include "dxil_bytecode.h" +#include +#include +#include +#include "common/common.h" +#include "llvm_decoder.h" + +// undef some annoying defines that might come from OS headers +#undef VOID +#undef FLOAT +#undef LABEL +#undef OPAQUE namespace DXIL { +struct ProgramHeader +{ + uint16_t ProgramVersion; + uint16_t ProgramType; + uint32_t SizeInUint32; // Size in uint32_t units including this header. + uint32_t DxilMagic; // 0x4C495844, ASCII "DXIL". + uint32_t DxilVersion; // DXIL version. + uint32_t BitcodeOffset; // Offset to LLVM bitcode (from DxilMagic). + uint32_t BitcodeSize; // Size of LLVM bitcode. +}; + +enum class KnownBlocks : uint32_t +{ + BLOCKINFO = 0, + + // 1-7 reserved, + + MODULE_BLOCK = 8, + PARAMATTR_BLOCK = 9, + PARAMATTR_GROUP_BLOCK = 10, + CONSTANTS_BLOCK = 11, + FUNCTION_BLOCK = 12, + TYPE_SYMTAB_BLOCK = 13, + VALUE_SYMTAB_BLOCK = 14, + METADATA_BLOCK = 15, + METADATA_ATTACHMENT = 16, + TYPE_BLOCK = 17, +}; + +enum class ModuleRecord : uint32_t +{ + VERSION = 1, + TRIPLE = 2, + DATALAYOUT = 3, + FUNCTION = 8, +}; + +enum class ConstantsRecord : uint32_t +{ + SETTYPE = 1, + CONST_NULL = 2, + UNDEF = 3, + INTEGER = 4, + WIDE_INTEGER = 5, + FLOAT = 6, + AGGREGATE = 7, + STRING = 8, + DATA = 22, +}; + +enum class FunctionRecord : uint32_t +{ + DECLAREBLOCKS = 1, + INST_BINOP = 2, + INST_CAST = 3, + INST_GEP_OLD = 4, + INST_SELECT = 5, + INST_EXTRACTELT = 6, + INST_INSERTELT = 7, + INST_SHUFFLEVEC = 8, + INST_CMP = 9, + INST_RET = 10, + INST_BR = 11, + INST_SWITCH = 12, + INST_INVOKE = 13, + INST_UNREACHABLE = 15, + INST_PHI = 16, + INST_ALLOCA = 19, + INST_LOAD = 20, + INST_VAARG = 23, + INST_STORE_OLD = 24, + INST_EXTRACTVAL = 26, + INST_INSERTVAL = 27, + INST_CMP2 = 28, + INST_VSELECT = 29, + INST_INBOUNDS_GEP_OLD = 30, + INST_INDIRECTBR = 31, + DEBUG_LOC_AGAIN = 33, + INST_CALL = 34, + DEBUG_LOC = 35, + INST_FENCE = 36, + INST_CMPXCHG_OLD = 37, + INST_ATOMICRMW = 38, + INST_RESUME = 39, + INST_LANDINGPAD_OLD = 40, + INST_LOADATOMIC = 41, + INST_STOREATOMIC_OLD = 42, + INST_GEP = 43, + INST_STORE = 44, + INST_STOREATOMIC = 45, + INST_CMPXCHG = 46, + INST_LANDINGPAD = 47, + INST_CLEANUPRET = 48, + INST_CATCHRET = 49, + INST_CATCHPAD = 50, + INST_CLEANUPPAD = 51, + INST_CATCHSWITCH = 52, + OPERAND_BUNDLE = 55, + INST_UNOP = 56, + INST_CALLBR = 57, +}; + +enum class ValueSymtabRecord : uint32_t +{ + ENTRY = 1, + BBENTRY = 2, + FNENTRY = 3, + COMBINED_ENTRY = 5, +}; + +enum class MetaDataRecord : uint32_t +{ + STRING_OLD = 1, + VALUE = 2, + NODE = 3, + NAME = 4, + DISTINCT_NODE = 5, + KIND = 6, + LOCATION = 7, + OLD_NODE = 8, + OLD_FN_NODE = 9, + NAMED_NODE = 10, + ATTACHMENT = 11, + GENERIC_DEBUG = 12, + SUBRANGE = 13, + ENUMERATOR = 14, + BASIC_TYPE = 15, + FILE = 16, + DERIVED_TYPE = 17, + COMPOSITE_TYPE = 18, + SUBROUTINE_TYPE = 19, + COMPILE_UNIT = 20, + SUBPROGRAM = 21, + LEXICAL_BLOCK = 22, + LEXICAL_BLOCK_FILE = 23, + NAMESPACE = 24, + TEMPLATE_TYPE = 25, + TEMPLATE_VALUE = 26, + GLOBAL_VAR = 27, + LOCAL_VAR = 28, + EXPRESSION = 29, + OBJC_PROPERTY = 30, + IMPORTED_ENTITY = 31, + MODULE = 32, + MACRO = 33, + MACRO_FILE = 34, + STRINGS = 35, + GLOBAL_DECL_ATTACHMENT = 36, + GLOBAL_VAR_EXPR = 37, + INDEX_OFFSET = 38, + INDEX = 39, + LABEL = 40, + COMMON_BLOCK = 44, +}; + +enum class TypeRecord : uint32_t +{ + NUMENTRY = 1, + VOID = 2, + FLOAT = 3, + DOUBLE = 4, + LABEL = 5, + OPAQUE = 6, + INTEGER = 7, + POINTER = 8, + FUNCTION_OLD = 9, + HALF = 10, + ARRAY = 11, + VECTOR = 12, + METADATA = 16, + STRUCT_ANON = 18, + STRUCT_NAME = 19, + STRUCT_NAMED = 20, + FUNCTION = 21, + TOKEN = 22, +}; + +static std::string getName(uint32_t parentBlock, const LLVMBC::BlockOrRecord &block) +{ + const char *name = NULL; + + if(block.IsBlock()) + { + // GetBlockName in BitcodeAnalyzer.cpp + switch(KnownBlocks(block.id)) + { + case KnownBlocks::BLOCKINFO: name = "BLOCKINFO"; break; + case KnownBlocks::MODULE_BLOCK: name = "MODULE_BLOCK"; break; + case KnownBlocks::PARAMATTR_BLOCK: name = "PARAMATTR_BLOCK"; break; + case KnownBlocks::PARAMATTR_GROUP_BLOCK: name = "PARAMATTR_GROUP_BLOCK"; break; + case KnownBlocks::CONSTANTS_BLOCK: name = "CONSTANTS_BLOCK"; break; + case KnownBlocks::FUNCTION_BLOCK: name = "FUNCTION_BLOCK"; break; + case KnownBlocks::TYPE_SYMTAB_BLOCK: name = "TYPE_SYMTAB_BLOCK"; break; + case KnownBlocks::VALUE_SYMTAB_BLOCK: name = "VALUE_SYMTAB_BLOCK"; break; + case KnownBlocks::METADATA_BLOCK: name = "METADATA_BLOCK"; break; + case KnownBlocks::METADATA_ATTACHMENT: name = "METADATA_ATTACHMENT"; break; + case KnownBlocks::TYPE_BLOCK: name = "TYPE_BLOCK"; break; + default: break; + } + } + else + { +#define STRINGISE_RECORD(a) \ + case decltype(code)::a: name = #a; break; + + // GetCodeName in BitcodeAnalyzer.cpp + switch(KnownBlocks(parentBlock)) + { + case KnownBlocks::MODULE_BLOCK: + { + ModuleRecord code = ModuleRecord(block.id); + switch(code) + { + STRINGISE_RECORD(VERSION); + STRINGISE_RECORD(TRIPLE); + STRINGISE_RECORD(DATALAYOUT); + STRINGISE_RECORD(FUNCTION); + default: break; + } + break; + } + case KnownBlocks::PARAMATTR_BLOCK: + case KnownBlocks::PARAMATTR_GROUP_BLOCK: name = "ENTRY"; break; + case KnownBlocks::CONSTANTS_BLOCK: + { + ConstantsRecord code = ConstantsRecord(block.id); + switch(code) + { + STRINGISE_RECORD(SETTYPE); + STRINGISE_RECORD(UNDEF); + STRINGISE_RECORD(INTEGER); + STRINGISE_RECORD(WIDE_INTEGER); + STRINGISE_RECORD(FLOAT); + STRINGISE_RECORD(AGGREGATE); + STRINGISE_RECORD(STRING); + STRINGISE_RECORD(DATA); + case ConstantsRecord::CONST_NULL: name = "NULL"; break; + default: break; + } + break; + } + case KnownBlocks::FUNCTION_BLOCK: + { + FunctionRecord code = FunctionRecord(block.id); + switch(code) + { + STRINGISE_RECORD(DECLAREBLOCKS); + STRINGISE_RECORD(INST_BINOP); + STRINGISE_RECORD(INST_CAST); + STRINGISE_RECORD(INST_GEP_OLD); + STRINGISE_RECORD(INST_SELECT); + STRINGISE_RECORD(INST_EXTRACTELT); + STRINGISE_RECORD(INST_INSERTELT); + STRINGISE_RECORD(INST_SHUFFLEVEC); + STRINGISE_RECORD(INST_CMP); + STRINGISE_RECORD(INST_RET); + STRINGISE_RECORD(INST_BR); + STRINGISE_RECORD(INST_SWITCH); + STRINGISE_RECORD(INST_INVOKE); + STRINGISE_RECORD(INST_UNREACHABLE); + STRINGISE_RECORD(INST_PHI); + STRINGISE_RECORD(INST_ALLOCA); + STRINGISE_RECORD(INST_LOAD); + STRINGISE_RECORD(INST_VAARG); + STRINGISE_RECORD(INST_STORE_OLD); + STRINGISE_RECORD(INST_EXTRACTVAL); + STRINGISE_RECORD(INST_INSERTVAL); + STRINGISE_RECORD(INST_CMP2); + STRINGISE_RECORD(INST_VSELECT); + STRINGISE_RECORD(INST_INBOUNDS_GEP_OLD); + STRINGISE_RECORD(INST_INDIRECTBR); + STRINGISE_RECORD(DEBUG_LOC_AGAIN); + STRINGISE_RECORD(INST_CALL); + STRINGISE_RECORD(DEBUG_LOC); + STRINGISE_RECORD(INST_FENCE); + STRINGISE_RECORD(INST_CMPXCHG_OLD); + STRINGISE_RECORD(INST_ATOMICRMW); + STRINGISE_RECORD(INST_RESUME); + STRINGISE_RECORD(INST_LANDINGPAD_OLD); + STRINGISE_RECORD(INST_LOADATOMIC); + STRINGISE_RECORD(INST_STOREATOMIC_OLD); + STRINGISE_RECORD(INST_GEP); + STRINGISE_RECORD(INST_STORE); + STRINGISE_RECORD(INST_STOREATOMIC); + STRINGISE_RECORD(INST_CMPXCHG); + STRINGISE_RECORD(INST_LANDINGPAD); + STRINGISE_RECORD(INST_CLEANUPRET); + STRINGISE_RECORD(INST_CATCHRET); + STRINGISE_RECORD(INST_CATCHPAD); + STRINGISE_RECORD(INST_CLEANUPPAD); + STRINGISE_RECORD(INST_CATCHSWITCH); + STRINGISE_RECORD(OPERAND_BUNDLE); + STRINGISE_RECORD(INST_UNOP); + STRINGISE_RECORD(INST_CALLBR); + default: break; + } + break; + } + case KnownBlocks::VALUE_SYMTAB_BLOCK: + { + ValueSymtabRecord code = ValueSymtabRecord(block.id); + switch(code) + { + STRINGISE_RECORD(ENTRY); + STRINGISE_RECORD(BBENTRY); + STRINGISE_RECORD(FNENTRY); + STRINGISE_RECORD(COMBINED_ENTRY); + default: break; + } + break; + } + case KnownBlocks::METADATA_BLOCK: + { + MetaDataRecord code = MetaDataRecord(block.id); + switch(code) + { + STRINGISE_RECORD(STRING_OLD); + STRINGISE_RECORD(VALUE); + STRINGISE_RECORD(NODE); + STRINGISE_RECORD(NAME); + STRINGISE_RECORD(DISTINCT_NODE); + STRINGISE_RECORD(KIND); + STRINGISE_RECORD(LOCATION); + STRINGISE_RECORD(OLD_NODE); + STRINGISE_RECORD(OLD_FN_NODE); + STRINGISE_RECORD(NAMED_NODE); + STRINGISE_RECORD(ATTACHMENT); + STRINGISE_RECORD(GENERIC_DEBUG); + STRINGISE_RECORD(SUBRANGE); + STRINGISE_RECORD(ENUMERATOR); + STRINGISE_RECORD(BASIC_TYPE); + STRINGISE_RECORD(FILE); + STRINGISE_RECORD(DERIVED_TYPE); + STRINGISE_RECORD(COMPOSITE_TYPE); + STRINGISE_RECORD(SUBROUTINE_TYPE); + STRINGISE_RECORD(COMPILE_UNIT); + STRINGISE_RECORD(SUBPROGRAM); + STRINGISE_RECORD(LEXICAL_BLOCK); + STRINGISE_RECORD(LEXICAL_BLOCK_FILE); + STRINGISE_RECORD(NAMESPACE); + STRINGISE_RECORD(TEMPLATE_TYPE); + STRINGISE_RECORD(TEMPLATE_VALUE); + STRINGISE_RECORD(GLOBAL_VAR); + STRINGISE_RECORD(LOCAL_VAR); + STRINGISE_RECORD(EXPRESSION); + STRINGISE_RECORD(OBJC_PROPERTY); + STRINGISE_RECORD(IMPORTED_ENTITY); + STRINGISE_RECORD(MODULE); + STRINGISE_RECORD(MACRO); + STRINGISE_RECORD(MACRO_FILE); + STRINGISE_RECORD(STRINGS); + STRINGISE_RECORD(GLOBAL_DECL_ATTACHMENT); + STRINGISE_RECORD(GLOBAL_VAR_EXPR); + STRINGISE_RECORD(INDEX_OFFSET); + STRINGISE_RECORD(INDEX); + STRINGISE_RECORD(LABEL); + STRINGISE_RECORD(COMMON_BLOCK); + default: break; + } + break; + } + case KnownBlocks::TYPE_BLOCK: + { + TypeRecord code = TypeRecord(block.id); + switch(code) + { + STRINGISE_RECORD(NUMENTRY); + STRINGISE_RECORD(VOID); + STRINGISE_RECORD(FLOAT); + STRINGISE_RECORD(DOUBLE); + STRINGISE_RECORD(LABEL); + STRINGISE_RECORD(OPAQUE); + STRINGISE_RECORD(INTEGER); + STRINGISE_RECORD(POINTER); + STRINGISE_RECORD(FUNCTION_OLD); + STRINGISE_RECORD(HALF); + STRINGISE_RECORD(ARRAY); + STRINGISE_RECORD(VECTOR); + STRINGISE_RECORD(METADATA); + STRINGISE_RECORD(STRUCT_ANON); + STRINGISE_RECORD(STRUCT_NAME); + STRINGISE_RECORD(STRUCT_NAMED); + STRINGISE_RECORD(FUNCTION); + STRINGISE_RECORD(TOKEN); + default: break; + } + break; + } + } + } + + // fallback + if(name) + { + return name; + } + else + { + if(block.IsBlock()) + return StringFormat::Fmt("BLOCK%u", block.id); + else + return StringFormat::Fmt("RECORD%u", block.id); + } +} + +static rdcstr escapeString(rdcstr str) +{ + for(size_t i = 0; i < str.size(); i++) + { + if(str[i] == '\r') + { + str[i] = 'r'; + str.insert(i, "\\", 1); + i++; + } + else if(str[i] == '\n') + { + str[i] = 'n'; + str.insert(i, "\\", 1); + i++; + } + else if(str[i] == '\t') + { + str[i] = 't'; + str.insert(i, "\\", 1); + i++; + } + else if(str[i] == '\'' || str[i] == '\\') + { + str.insert(i, "\\", 1); + i++; + } + else if(!isprint(str[i])) + { + str.insert(i + 1, StringFormat::Fmt("x%02x", str[i])); + str[i] = '\\'; + } + } + + return str; +} + +static void dumpRecord(uint32_t parentBlock, const LLVMBC::BlockOrRecord &record, int indent) +{ + std::string line(indent, ' '); + + line += "<" + getName(parentBlock, record); + + if(KnownBlocks(parentBlock) == KnownBlocks::METADATA_BLOCK && + (MetaDataRecord(record.id) == MetaDataRecord::STRING_OLD || + MetaDataRecord(record.id) == MetaDataRecord::NAME || + MetaDataRecord(record.id) == MetaDataRecord::KIND)) + { + line += " record string = '" + escapeString(record.getString()) + "'"; + } + else + { + for(size_t i = 0; i < record.ops.size(); i++) + line += StringFormat::Fmt(" op%u=%llu", (uint32_t)i, record.ops[i]); + } + + if(record.blob) + line += StringFormat::Fmt(" with blob of %u bytes", (uint32_t)record.blobLength); + + line += "/>"; + + RDCLOG("%s", line.c_str()); +} + +static void dumpBlock(const LLVMBC::BlockOrRecord &block, int indent) +{ + std::string line(indent, ' '); + + if(block.children.empty() || KnownBlocks(block.id) == KnownBlocks::BLOCKINFO) + { + line += StringFormat::Fmt("<%s/>", getName(0, block).c_str()); + RDCLOG("%s", line.c_str()); + return; + } + + line += StringFormat::Fmt("<%s NumWords=%u>", getName(0, block).c_str(), block.blockDwordLength); + RDCLOG("%s", line.c_str()); + + for(const LLVMBC::BlockOrRecord &child : block.children) + { + if(child.IsBlock()) + dumpBlock(child, indent + 2); + else + dumpRecord(block.id, child, indent + 2); + } + + line = std::string(indent, ' '); + line += StringFormat::Fmt("", getName(0, block).c_str()); + RDCLOG("%s", line.c_str()); +} + +Program::Program(const byte *bytes, size_t length) +{ + const byte *ptr = bytes; + const ProgramHeader *header = (const ProgramHeader *)ptr; + RDCASSERT(header->DxilMagic == MAKE_FOURCC('D', 'X', 'I', 'L')); + + const byte *bitcode = ((const byte *)&header->DxilMagic) + header->BitcodeOffset; + RDCASSERT(bitcode + header->BitcodeSize == ptr + length); + + LLVMBC::BitcodeReader reader(bitcode, header->BitcodeSize); + + LLVMBC::BlockOrRecord root = reader.ReadToplevelBlock(); + + // the top-level block should be MODULE_BLOCK + RDCASSERT(KnownBlocks(root.id) == KnownBlocks::MODULE_BLOCK); + + // we should have consumed all bits, only one top-level block + RDCASSERT(reader.AtEndOfStream()); + + m_Type = DXBC::ShaderType(header->ProgramType); + m_Major = (header->ProgramVersion & 0xf0) >> 4; + m_Minor = header->ProgramVersion & 0xf; + + // Input signature and Output signature haven't changed. + // Pipeline Runtime Information we have decoded just not implemented here + + rdcstr datalayout, triple; + +#define IS_KNOWN(val, KnownID) (decltype(KnownID)(val) == KnownID) + + for(const LLVMBC::BlockOrRecord &rootblock : root.children) + { + if(rootblock.IsRecord() && IS_KNOWN(rootblock.id, ModuleRecord::TRIPLE)) + { + m_Triple = rootblock.getString(); + } + else if(rootblock.IsRecord() && IS_KNOWN(rootblock.id, ModuleRecord::DATALAYOUT)) + { + m_Datalayout = rootblock.getString(); + } + else if(rootblock.IsBlock() && IS_KNOWN(rootblock.id, KnownBlocks::VALUE_SYMTAB_BLOCK)) + { + for(const LLVMBC::BlockOrRecord &symtab : rootblock.children) + { + RDCLOG("function %llu is \"%s\"\n", symtab.ops[0], symtab.getString(1).c_str()); + } + } + else if(rootblock.IsBlock() && IS_KNOWN(rootblock.id, KnownBlocks::METADATA_BLOCK)) + { + for(size_t i = 0; i < rootblock.children.size(); i++) + { + const LLVMBC::BlockOrRecord &meta = rootblock.children[i]; + if(IS_KNOWN(meta.id, MetaDataRecord::NAME)) + { + rdcstr metaName = meta.getString(); + i++; + const LLVMBC::BlockOrRecord &namedNode = rootblock.children[i]; + RDCASSERT(IS_KNOWN(namedNode.id, MetaDataRecord::NAMED_NODE)); + + std::string namedMeta = StringFormat::Fmt("!%s = !{", metaName.c_str()); + + bool first = true; + for(uint64_t op : namedNode.ops) + { + if(!first) + namedMeta += ", "; + namedMeta += ToStr(op); + first = false; + } + namedMeta += "}"; + + RDCLOG("%s", namedMeta.c_str()); + } + else + { + if(IS_KNOWN(meta.id, MetaDataRecord::KIND)) + { + RDCLOG("Kind[%llu] = %s\n", meta.ops[0], meta.getString(1).c_str()); + continue; + } + + std::string metastr = StringFormat::Fmt("!%u = ", (uint32_t)i); + + auto getMetaString = [&rootblock](uint64_t id) -> rdcstr { + return id ? rootblock.children[id - 1].getString() : "NULL"; + }; + + if(IS_KNOWN(meta.id, MetaDataRecord::STRING_OLD)) + { + metastr += "\"" + escapeString(meta.getString()) + "\""; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::FILE)) + { + if(meta.ops[0]) + metastr += "distinct "; + + metastr += "!DIFile("; + metastr += StringFormat::Fmt("filename: \"%s\"", + escapeString(getMetaString(meta.ops[1])).c_str()); + metastr += StringFormat::Fmt(", directory: \"%s\"", + escapeString(getMetaString(meta.ops[2])).c_str()); + metastr += ")"; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::NODE) || + IS_KNOWN(meta.id, MetaDataRecord::DISTINCT_NODE)) + { + if(IS_KNOWN(meta.id, MetaDataRecord::DISTINCT_NODE)) + metastr += "distinct "; + + metastr += "!{"; + bool first = true; + for(uint64_t op : meta.ops) + { + if(!first) + metastr += ", "; + metastr += ToStr(op - 1); + first = false; + } + metastr += "}"; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::BASIC_TYPE)) + { + metastr += "!DIBasicType()"; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::DERIVED_TYPE)) + { + metastr += "!DIDerivedType()"; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::COMPOSITE_TYPE)) + { + metastr += "!DICompositeType()"; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::SUBROUTINE_TYPE)) + { + metastr += "!DISubroutineType()"; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::TEMPLATE_TYPE)) + { + metastr += "!DITemplateTypeParameter()"; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::TEMPLATE_VALUE)) + { + metastr += "!DITemplateValueParameter()"; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::SUBPROGRAM)) + { + metastr += "!DISubprogram()"; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::LOCATION)) + { + metastr += "!DILocation()"; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::LOCAL_VAR)) + { + metastr += "!DILocalVariable()"; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::VALUE)) + { + // need to decode CONSTANTS_BLOCK and TYPE_BLOCK for this + metastr += StringFormat::Fmt("!{values[%llu] interpreted as types[%llu]}", meta.ops[1], + meta.ops[0]); + } + else if(IS_KNOWN(meta.id, MetaDataRecord::EXPRESSION)) + { + // don't decode this yet + metastr += "!DIExpression("; + bool first = true; + for(uint64_t op : meta.ops) + { + if(!first) + metastr += ", "; + metastr += ToStr(op); + first = false; + } + metastr += ")"; + } + else if(IS_KNOWN(meta.id, MetaDataRecord::COMPILE_UNIT)) + { + // should be at least 14 parameters + RDCASSERT(meta.ops.size() >= 14); + + // we expect it to be marked as distinct, but we'll always treat it that way + if(meta.ops[0]) + metastr += "distinct "; + else + metastr += "distinct? "; + + metastr += "!DICompileUnit("; + { + metastr += StringFormat::Fmt( + "language: %s", meta.ops[1] == 0x4 ? "DW_LANG_C_plus_plus" : "DW_LANG_unknown"); + metastr += StringFormat::Fmt(", file: !%llu", meta.ops[2] - 1); + metastr += StringFormat::Fmt(", producer: \"%s\"", + escapeString(getMetaString(meta.ops[3])).c_str()); + metastr += StringFormat::Fmt(", isOptimized: %s", meta.ops[4] ? "true" : "false"); + metastr += StringFormat::Fmt(", flags: \"%s\"", + escapeString(getMetaString(meta.ops[5])).c_str()); + metastr += StringFormat::Fmt(", runtimeVersion: %llu", meta.ops[6]); + metastr += StringFormat::Fmt(", splitDebugFilename: \"%s\"", + escapeString(getMetaString(meta.ops[7])).c_str()); + metastr += StringFormat::Fmt(", emissionKind: %llu", meta.ops[8]); + metastr += StringFormat::Fmt(", enums: !%llu", meta.ops[9] - 1); + metastr += StringFormat::Fmt(", retainedTypes: !%llu", meta.ops[10] - 1); + metastr += StringFormat::Fmt(", subprograms: !%llu", meta.ops[11] - 1); + metastr += StringFormat::Fmt(", globals: !%llu", meta.ops[12] - 1); + metastr += StringFormat::Fmt(", imports: !%llu", meta.ops[13] - 1); + if(meta.ops.size() >= 15) + metastr += StringFormat::Fmt(", dwoId: 0x%llu", meta.ops[14]); + } + metastr += ")"; + } + else + { + RDCERR("unhandled metadata type %u", meta.id); + } + + RDCLOG("%s", metastr.c_str()); + } + } + } + } + + dumpBlock(root, 0); +} + +void Program::FetchComputeProperties(DXBC::Reflection *reflection) +{ + RDCERR("Unimplemented DXIL::Program::FetchComputeProperties()"); + reflection->DispatchThreadsDimension[0] = 1; + reflection->DispatchThreadsDimension[1] = 1; + reflection->DispatchThreadsDimension[2] = 1; +} + +DXBC::Reflection *Program::GetReflection() +{ + RDCERR("Unimplemented DXIL::Program::GetReflection()"); + return new DXBC::Reflection; +} + +D3D_PRIMITIVE_TOPOLOGY Program::GetOutputTopology() +{ + RDCERR("Unimplemented DXIL::Program::GetOutputTopology()"); + return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; +} + +void Program::MakeDisassemblyString() +{ + RDCERR("Unimplemented DXIL::Program::MakeDisassemblyString()"); + + const char *shaderName[] = { + "Pixel", "Vertex", "Geometry", "Hull", "Domain", + "Compute", "Library", "RayGeneration", "Intersection", "AnyHit", + "ClosestHit", "Miss", "Callable", "Mesh", "Amplification", + }; + + m_Disassembly = StringFormat::Fmt("; %s Shader, compiled under SM%u.%u\n", + shaderName[int(m_Type)], m_Major, m_Minor); + m_Disassembly += StringFormat::Fmt("target triple = \"%s\"\n", m_Triple.c_str()); + m_Disassembly += StringFormat::Fmt("target datalayout = \"%s\"\n", m_Datalayout.c_str()); + + m_Disassembly += "; No disassembly implemented"; +} }; \ No newline at end of file diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode.h b/renderdoc/driver/shaders/dxil/dxil_bytecode.h index a19bdb4ff..752fb4055 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode.h +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode.h @@ -26,6 +26,39 @@ #include +#include "driver/dx/official/d3dcommon.h" +#include "driver/shaders/dxbc/dxbc_common.h" + namespace DXIL { +class Program +{ +public: + Program(const byte *bytes, size_t length); + + void FetchComputeProperties(DXBC::Reflection *reflection); + DXBC::Reflection *GetReflection(); + + DXBC::ShaderType GetShaderType() { return m_Type; } + uint32_t GetMajorVersion() { return m_Major; } + uint32_t GetMinorVersion() { return m_Minor; } + D3D_PRIMITIVE_TOPOLOGY GetOutputTopology(); + const std::string &GetDisassembly() + { + if(m_Disassembly.empty()) + MakeDisassemblyString(); + return m_Disassembly; + } + +private: + void MakeDisassemblyString(); + + DXBC::ShaderType m_Type; + uint32_t m_Major, m_Minor; + + rdcstr m_Triple, m_Datalayout; + + std::string m_Disassembly; +}; + }; // namespace DXIL diff --git a/renderdoc/driver/shaders/dxil/llvm_bitreader.h b/renderdoc/driver/shaders/dxil/llvm_bitreader.h index f55d13a23..6bc1b76cd 100644 --- a/renderdoc/driver/shaders/dxil/llvm_bitreader.h +++ b/renderdoc/driver/shaders/dxil/llvm_bitreader.h @@ -317,7 +317,7 @@ private: byte data = *m_Bits & mask; // check if we overlap into the next destination byte - if(dstoffs + bitsToRead < 8) + if(dstoffs + bitsToRead <= 8) { *dst |= data << dstoffs; } diff --git a/renderdoc/driver/shaders/dxil/llvm_decoder.cpp b/renderdoc/driver/shaders/dxil/llvm_decoder.cpp index e06eb0537..2b7ab7247 100644 --- a/renderdoc/driver/shaders/dxil/llvm_decoder.cpp +++ b/renderdoc/driver/shaders/dxil/llvm_decoder.cpp @@ -303,18 +303,21 @@ size_t BitcodeReader::abbrevSize() const const AbbrevDesc &BitcodeReader::getAbbrev(uint32_t blockId, uint32_t abbrevID) { - const BlockInfo &info = *blockInfo[blockId]; + const BlockInfo *info = blockInfo[blockId]; // IDs start at the first application specified ID. Rebase to that to get 0-base indices RDCASSERT(abbrevID >= APPLICATION_ABBREV); abbrevID -= APPLICATION_ABBREV; - // IDs are first assigned to those permanently from BLOCKINFO - if(abbrevID < info.abbrevs.size()) - return info.abbrevs[abbrevID]; + if(info) + { + // IDs are first assigned to those permanently from BLOCKINFO + if(abbrevID < info->abbrevs.size()) + return info->abbrevs[abbrevID]; - // block-local IDs start after the BLOCKINFO ones - abbrevID -= (uint32_t)info.abbrevs.size(); + // block-local IDs start after the BLOCKINFO ones + abbrevID -= (uint32_t)info->abbrevs.size(); + } RDCASSERT(!blockStack.empty()); RDCASSERT(abbrevID < blockStack.back()->abbrevs.size());