diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp b/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp index bbde5a8d4..c885eb954 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp @@ -28,7 +28,6 @@ #include #include "common/common.h" #include "common/formatting.h" -#include "maths/half_convert.h" #include "os/os_specific.h" #include "llvm_decoder.h" @@ -277,12 +276,7 @@ void ParseConstant(const LLVMBC::BlockOrRecord &constant, const Type *&curType, { Constant v; v.type = curType; - if(curType->bitWidth == 16) - v.val.f32v[0] = ConvertFromHalf(uint16_t(constant.ops[0] & 0xffff)); - else if(curType->bitWidth == 32) - memcpy(&v.val.f32v[0], &constant.ops[0], sizeof(float)); - else - memcpy(&v.val.f64v[0], &constant.ops[0], sizeof(double)); + memcpy(&v.val.f64v[0], &constant.ops[0], curType->bitWidth / 8); addConstant(v); } else if(IS_KNOWN(constant.id, ConstantsRecord::STRING) || @@ -563,6 +557,39 @@ Program::Program(const byte *bytes, size_t length) if(rootchild.ops[1] & 0x1) g.flags |= GlobalFlags::IsConst; + if(rootchild.ops[2]) + g.initialiser = Symbol(SymbolType::Constant, rootchild.ops[2] - 1); + + switch(rootchild.ops[3]) + { + case 0: g.flags |= GlobalFlags::ExternalLinkage; break; + case 16: g.flags |= GlobalFlags::WeakAnyLinkage; break; + case 2: g.flags |= GlobalFlags::AppendingLinkage; break; + case 3: g.flags |= GlobalFlags::InternalLinkage; break; + case 18: g.flags |= GlobalFlags::LinkOnceAnyLinkage; break; + case 7: g.flags |= GlobalFlags::ExternalWeakLinkage; break; + case 8: g.flags |= GlobalFlags::CommonLinkage; break; + case 9: g.flags |= GlobalFlags::PrivateLinkage; break; + case 17: g.flags |= GlobalFlags::WeakODRLinkage; break; + case 19: g.flags |= GlobalFlags::LinkOnceODRLinkage; break; + case 12: g.flags |= GlobalFlags::AvailableExternallyLinkage; break; + default: break; + } + + g.align = (1ULL << rootchild.ops[4]) >> 1; + + g.section = int32_t(rootchild.ops[5]) - 1; + + if(rootchild.ops.size() > 6) + { + RDCASSERTMSG("global has non-default visibility", rootchild.ops[6] == 0); + } + + if(rootchild.ops.size() > 7) + { + RDCASSERTMSG("global has non-default TLS mode", rootchild.ops[7] == 0); + } + if(rootchild.ops.size() > 8) { if(rootchild.ops[8] == 1) @@ -571,23 +598,22 @@ Program::Program(const byte *bytes, size_t length) g.flags |= GlobalFlags::LocalUnnamedAddr; } - if(rootchild.ops[2]) - g.initialiser = Symbol(SymbolType::Constant, rootchild.ops[2] - 1); - - switch(rootchild.ops[3]) + if(rootchild.ops.size() > 9) { - case 0: - case 5: - case 6: - case 7: - case 15: g.flags |= GlobalFlags::IsExternal; break; - case 2: g.flags |= GlobalFlags::IsAppending; break; - default: break; + if(rootchild.ops[9]) + g.flags |= GlobalFlags::ExternallyInitialised; } - g.align = (1ULL << rootchild.ops[4]) >> 1; + if(rootchild.ops.size() > 10) + { + RDCASSERTMSG("global has non-default DLL storage class", rootchild.ops[10] == 0); + } - g.section = int32_t(rootchild.ops[5]) - 1; + if(rootchild.ops.size() > 11) + { + // assume no comdat + RDCASSERTMSG("global has comdat", rootchild.ops[11] == 0); + } // symbols refer into any of N types in declaration order m_Symbols.push_back({SymbolType::GlobalVar, m_GlobalVars.size()}); @@ -610,16 +636,23 @@ Program::Program(const byte *bytes, size_t length) else if(IS_KNOWN(rootchild.id, ModuleRecord::FUNCTION)) { // [type, callingconv, isproto, linkage, paramattrs, alignment, section, visibility, gc, - // unnamed_addr] + // unnamed_addr, prologuedata, dllstorageclass, comdat, prefixdata] Function f; f.funcType = &m_Types[(size_t)rootchild.ops[0]]; // ignore callingconv + RDCASSERTMSG("Calling convention is non-default", rootchild.ops[1] == 0); f.external = (rootchild.ops[2] != 0); // ignore linkage + RDCASSERTMSG("Linkage is non-default", rootchild.ops[3] == 0); if(rootchild.ops[4] > 0 && rootchild.ops[4] - 1 < m_Attributes.size()) f.attrs = &m_Attributes[(size_t)rootchild.ops[4] - 1]; - // ignore rest of properties + + f.align = rootchild.ops[5]; + + // ignore rest of properties, assert that if present they are 0 + for(size_t p = 6; p < rootchild.ops.size(); p++) + RDCASSERT(rootchild.ops[p] == 0, p, rootchild.ops[p]); // symbols refer into any of N types in declaration order m_Symbols.push_back({SymbolType::Function, m_Functions.size()}); @@ -1394,7 +1427,9 @@ Program::Program(const byte *bytes, size_t length) { Instruction inst; inst.op = Operation::Call; - inst.paramAttrs = &m_Attributes[op.get()]; + size_t attr = op.get(); + if(attr > 0) + inst.paramAttrs = &m_Attributes[attr - 1]; uint64_t callingFlags = op.get(); diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode.h b/renderdoc/driver/shaders/dxil/dxil_bytecode.h index 8e97cafd9..01c7dc38c 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode.h +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode.h @@ -117,11 +117,24 @@ struct Symbol enum class GlobalFlags : uint32_t { NoFlags = 0, - IsConst = 0x1, - IsExternal = 0x2, - LocalUnnamedAddr = 0x4, - GlobalUnnamedAddr = 0x8, - IsAppending = 0x10, + ExternalLinkage = 1, + AvailableExternallyLinkage = 2, + LinkOnceAnyLinkage = 3, + LinkOnceODRLinkage = 4, + WeakAnyLinkage = 5, + WeakODRLinkage = 6, + AppendingLinkage = 7, + InternalLinkage = 8, + PrivateLinkage = 9, + ExternalWeakLinkage = 10, + CommonLinkage = 11, + LinkageMask = 0xf, + IsConst = 0x10, + IsExternal = 0x20, + LocalUnnamedAddr = 0x40, + GlobalUnnamedAddr = 0x80, + IsAppending = 0x100, + ExternallyInitialised = 0x200, }; BITMASK_OPERATORS(GlobalFlags); @@ -483,6 +496,8 @@ struct Function bool external = false; const Attributes *attrs = NULL; + uint64_t align = 0; + rdcarray args; rdcarray instructions; diff --git a/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp b/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp index 4fe4081fc..f6c478b34 100644 --- a/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp @@ -26,9 +26,18 @@ #include #include #include "common/formatting.h" +#include "maths/half_convert.h" #include "dxil_bytecode.h" #include "dxil_common.h" +#define DXC_COMPATIBLE_DISASM OPTION_OFF + +#if ENABLED(DXC_COMPATIBLE_DISASM) && ENABLED(RDOC_RELEASE) + +#error "DXC compatible disassembly should only be enabled in debug builds for testing" + +#endif + namespace DXIL { struct TypeOrderer @@ -441,21 +450,31 @@ void Program::MakeDisassemblyString() const GlobalVar &g = m_GlobalVars[i]; m_Disassembly += StringFormat::Fmt("@%s = ", escapeStringIfNeeded(g.name).c_str()); - if(g.initialiser.type != SymbolType::Constant) + switch(g.flags & GlobalFlags::LinkageMask) { - if(g.flags & GlobalFlags::IsExternal) - m_Disassembly += "external "; + case GlobalFlags::ExternalLinkage: + if(g.initialiser.type != SymbolType::Constant) + m_Disassembly += "external "; + break; + case GlobalFlags::PrivateLinkage: m_Disassembly += "private "; break; + case GlobalFlags::InternalLinkage: m_Disassembly += "internal "; break; + case GlobalFlags::LinkOnceAnyLinkage: m_Disassembly += "linkonce "; break; + case GlobalFlags::LinkOnceODRLinkage: m_Disassembly += "linkonce_odr "; break; + case GlobalFlags::WeakAnyLinkage: m_Disassembly += "weak "; break; + case GlobalFlags::WeakODRLinkage: m_Disassembly += "weak_odr "; break; + case GlobalFlags::CommonLinkage: m_Disassembly += "common "; break; + case GlobalFlags::AppendingLinkage: m_Disassembly += "appending "; break; + case GlobalFlags::ExternalWeakLinkage: m_Disassembly += "extern_weak "; break; + case GlobalFlags::AvailableExternallyLinkage: m_Disassembly += "available_externally "; break; + default: break; } - if(g.flags & GlobalFlags::IsAppending) - m_Disassembly += "appending "; - else if(!(g.flags & GlobalFlags::IsExternal)) - m_Disassembly += "internal "; - if(g.type->addrSpace) - m_Disassembly += StringFormat::Fmt("addrspace(%d) ", g.type->addrSpace); + if(g.flags & GlobalFlags::LocalUnnamedAddr) m_Disassembly += "local_unnamed_addr "; else if(g.flags & GlobalFlags::GlobalUnnamedAddr) m_Disassembly += "unnamed_addr "; + if(g.type->addrSpace != Type::PointerAddrSpace::Default) + m_Disassembly += StringFormat::Fmt("addrspace(%d) ", g.type->addrSpace); if(g.flags & GlobalFlags::IsConst) m_Disassembly += "constant "; else @@ -527,11 +546,16 @@ void Program::MakeDisassemblyString() if(m.isConstant && m.constant && m.constant->symbol) ret += m.constant->toString(withTypes); else if(m.isConstant && m.constant && - (m.constant->type->type == Type::Scalar || m.constant->type->type == Type::Vector || + (m.constant->type->type == Type::Scalar || + m.constant->type->type == Type::Vector || m.constant->undef || m.constant->nullconst || m.constant->type->name.beginsWith("class.matrix."))) + { ret += m.constant->toString(withTypes); + } else + { ret += StringFormat::Fmt("!%u", GetOrAssignMetaID(&m)); + } } else { @@ -638,6 +662,8 @@ void Program::MakeDisassemblyString() } m_Disassembly += ")"; debugCall = inst.funcCall->name.beginsWith("llvm.dbg."); + if(inst.paramAttrs) + m_Disassembly += StringFormat::Fmt(" #%u", inst.paramAttrs - m_Attributes.begin()); break; } case Operation::Trunc: @@ -1365,6 +1391,12 @@ void Program::MakeDisassemblyString() labelName.push_back(' '); labelName += "; preds = "; +#if ENABLED(DXC_COMPATIBLE_DISASM) + // unfortunately due to how llvm/dxc packs its preds, this is not feasible to replicate so + // instead we omit the pred list entirely and dxc's output needs to be regex replaced to + // match + labelName += "..."; +#else bool first = true; for(const Block *pred : func.blocks[curBlock].preds) { @@ -1376,6 +1408,7 @@ void Program::MakeDisassemblyString() else labelName += "%" + escapeStringIfNeeded(pred->name); } +#endif m_Disassembly += labelName; m_Disassembly += "\n"; @@ -1408,9 +1441,15 @@ void Program::MakeDisassemblyString() { if(numIdx < m_NumberedMeta.size() && m_NumberedMeta[numIdx]->id == i) { - m_Disassembly += StringFormat::Fmt("!%u = %s%s\n", i, - m_NumberedMeta[numIdx]->isDistinct ? "distinct " : "", - m_NumberedMeta[numIdx]->valString().c_str()); + rdcstr metaline = StringFormat::Fmt("!%u = %s%s\n", i, + m_NumberedMeta[numIdx]->isDistinct ? "distinct " : "", + m_NumberedMeta[numIdx]->valString().c_str()); +#if ENABLED(DXC_COMPATIBLE_DISASM) + for(size_t c = 0; c < metaline.size(); c += 4096) + m_Disassembly += metaline.substr(c, 4096); +#else + m_Disassembly += metaline; +#endif if(m_NumberedMeta[numIdx]->dwarf) m_NumberedMeta[numIdx]->dwarf->setID(i); numIdx++; @@ -1555,8 +1594,10 @@ rdcstr Metadata::refString() const rdcstr DebugLocation::toString() const { - rdcstr ret = StringFormat::Fmt("!DILocation(line: %llu, column: %llu, scope: %s", line, col, - scope ? scope->refString().c_str() : "null"); + rdcstr ret = StringFormat::Fmt("!DILocation(line: %llu", line); + if(col) + ret += StringFormat::Fmt(", column: %llu", col); + ret += StringFormat::Fmt(", scope: %s", scope ? scope->refString().c_str() : "null"); if(inlinedAt) ret += StringFormat::Fmt(", inlinedAt: %s", inlinedAt->refString().c_str()); ret += ")"; @@ -1577,6 +1618,9 @@ rdcstr Metadata::valString() const { if(type == NULL) { +// don't truncate here for dxc-compatible disassembly, instead we wrap at 4096 columns at a higher +// level +#if DISABLED(DXC_COMPATIBLE_DISASM) // truncate very long strings - most likely these are shader source if(str.length() > 400) { @@ -1585,10 +1629,9 @@ rdcstr Metadata::valString() const trunc.insert(200, "..."); return StringFormat::Fmt("!%s", escapeString(trunc).c_str()); } - else - { - return StringFormat::Fmt("!%s", escapeString(str).c_str()); - } +#endif + + return StringFormat::Fmt("!%s", escapeString(str).c_str()); } else { @@ -1637,6 +1680,43 @@ rdcstr Metadata::valString() const } } +static void floatAppendToString(const Type *t, const ShaderValue &val, uint32_t i, rdcstr &ret) +{ +#if ENABLED(DXC_COMPATIBLE_DISASM) + // dxc/llvm always prints half floats as their 16-bit hex representation. + if(t->bitWidth == 16) + { + ret += StringFormat::Fmt("0xH%04X", val.u64v[i]); + return; + } +#endif + + double d = t->bitWidth == 64 ? val.f64v[i] : val.f32v[i]; + + // NaNs/infs are printed as hex to ensure we don't lose bits + if(RDCISFINITE(d)) + { + // check we can reparse precisely a float-formatted string. Otherwise we print as hex + rdcstr flt = StringFormat::Fmt("%.6le", d); + +#if ENABLED(DXC_COMPATIBLE_DISASM) + // dxc/llvm only prints floats as floats if they roundtrip, but our disassembly doesn't need to + // roundtrip so it's better to display the value in all cases + double reparse = strtod(flt.begin(), NULL); + + if(d == reparse) + { + ret += flt; + return; + } +#else + ret += flt; +#endif + } + + ret += StringFormat::Fmt("0x%llX", d); +} + rdcstr Constant::toString(bool withType) const { if(type == NULL) @@ -1719,25 +1799,7 @@ rdcstr Constant::toString(bool withType) const { if(type->scalarType == Type::Float) { - double orig; - if(type->bitWidth > 32) - orig = val.f64v[0]; - else - orig = val.f32v[0]; - - // NaNs/infs are printed as hex to ensure we don't lose bits - if(RDCISFINITE(orig)) - { - // check we can reparse precisely a float-formatted string. Otherwise we print as hex - rdcstr flt = StringFormat::Fmt("%.6le", orig); - - double reparse = strtod(flt.begin(), NULL); - - if(orig == reparse) - return ret + flt; - } - - ret += StringFormat::Fmt("0x%llX", orig); + floatAppendToString(type, val, 0, ret); } else if(type->scalarType == Type::Int) { @@ -1765,19 +1827,16 @@ rdcstr Constant::toString(bool withType) const ret += type->inner->toString() + " "; if(type->scalarType == Type::Float) { - // TODO need to know how to determine signedness here - if(type->bitWidth > 32) - ret += StringFormat::Fmt("%le", val.f64v[i]); - else - ret += StringFormat::Fmt("%e", val.f32v[i]); + floatAppendToString(type, val, i, ret); } else if(type->scalarType == Type::Int) { - // TODO need to know how to determine signedness here if(type->bitWidth > 32) - ret += StringFormat::Fmt("%llu", val.u64v[i]); + ret += StringFormat::Fmt("%lld", val.s64v[i]); + else if(type->bitWidth == 1) + ret += val.u32v[i] ? "true" : "false"; else - ret += StringFormat::Fmt("%u", val.u32v[i]); + ret += StringFormat::Fmt("%d", val.s32v[i]); } } ret += ">"; diff --git a/renderdoc/maths/formatpacking.cpp b/renderdoc/maths/formatpacking.cpp index f39e6b53a..caad400bb 100644 --- a/renderdoc/maths/formatpacking.cpp +++ b/renderdoc/maths/formatpacking.cpp @@ -1281,11 +1281,6 @@ TEST_CASE("Check format conversion", "[format]") float f2 = ConvertFromHalf(i2); CHECK(!RDCISFINITE(f2)); } - else if(i == 0x8000) - { - // signed 0 - CHECK(i2 == 0); - } else { CHECK(i == i2); diff --git a/renderdoc/maths/half_convert.h b/renderdoc/maths/half_convert.h index 6fa82d84a..246ba88f8 100644 --- a/renderdoc/maths/half_convert.h +++ b/renderdoc/maths/half_convert.h @@ -94,7 +94,7 @@ inline float ConvertFromHalf(uint16_t comp) if(exponent == 0x00) { if(mantissa == 0) - return 0.0f; + return sign ? -0.0f : 0.0f; // subnormal float ret = (float)mantissa;