mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-24 23:46:41 +00:00
Fix some differences with DXC's disassembly
* We also add an option to tweak our disassembly output to be more easily compared with dxc's
This commit is contained in:
@@ -28,7 +28,6 @@
|
||||
#include <string>
|
||||
#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>()];
|
||||
size_t attr = op.get<size_t>();
|
||||
if(attr > 0)
|
||||
inst.paramAttrs = &m_Attributes[attr - 1];
|
||||
|
||||
uint64_t callingFlags = op.get<uint64_t>();
|
||||
|
||||
|
||||
@@ -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<Instruction> args;
|
||||
rdcarray<Instruction> instructions;
|
||||
|
||||
|
||||
@@ -26,9 +26,18 @@
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#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 += ">";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user