Improve DXIL bytecode editor

* Add helpers for creating DX op instructions with less boilerplate.
* On encode, strip any unused functions or globals to comply with strict
  DXIL validation requirements.
* Create attribute sets on demand to match functions.
* Add some extra helpers for creating constants, blocks, and patching
  runtime chunk.
This commit is contained in:
baldurk
2023-11-16 18:20:23 +00:00
parent 6ea7710380
commit 788f68a1f7
7 changed files with 644 additions and 428 deletions
+165 -285
View File
@@ -210,7 +210,12 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
ProgramEditor editor(dxbc, editedBlob);
const Type *handleType = editor.CreateNamedStructType("dx.types.Handle", {});
const Type *i32 = editor.GetInt32Type();
const Type *i8 = editor.GetInt8Type();
const Type *i1 = editor.GetBoolType();
const Type *handleType = editor.CreateNamedStructType(
"dx.types.Handle", {editor.CreatePointerType(i8, Type::PointerAddrSpace::Default)});
const Function *createHandle = editor.GetFunctionByName("dx.op.createHandle");
const Function *createHandleFromBinding =
editor.GetFunctionByName("dx.op.createHandleFromBinding");
@@ -225,118 +230,21 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
(isShaderModel6_6OrAbove && !createHandleFromHeap && !createHandleFromBinding))
return false;
const Type *i32 = editor.GetInt32Type();
const Type *i8 = editor.GetInt8Type();
const Type *i1 = editor.GetBoolType();
// Create createHandleFromBinding we'll need to create the feedback UAV
if(!createHandleFromBinding && isShaderModel6_6OrAbove)
{
const Type *resBindType = editor.CreateNamedStructType("dx.types.ResBind", {i32, i32, i32, i8});
const Type *funcType = editor.CreateFunctionType(handleType, {i32, resBindType, i32, i1});
Function createHandleBaseFunction;
createHandleBaseFunction.name = "dx.op.createHandleFromBinding";
createHandleBaseFunction.type = funcType;
createHandleBaseFunction.external = true;
for(const AttributeSet &attrs : editor.GetAttributeSets())
{
if(attrs.functionSlot && attrs.functionSlot->params == Attribute::NoUnwind)
{
createHandleBaseFunction.attrs = &attrs;
break;
}
}
if(!createHandleBaseFunction.attrs)
RDCWARN("Couldn't find existing nounwind attr set");
createHandleFromBinding = editor.DeclareFunction(createHandleBaseFunction);
createHandleFromBinding = editor.DeclareFunction("dx.op.createHandleFromBinding", handleType,
{i32, resBindType, i32, i1},
Attribute::NoUnwind | Attribute::ReadNone);
}
// get the functions we'll need
Function *atomicBinOp = editor.GetFunctionByName("dx.op.atomicBinOp.i32");
if(!atomicBinOp)
{
const Type *funcType = editor.CreateFunctionType(i32, {i32, handleType, i32, i32, i32, i32, i32});
Function atomicFunc;
atomicFunc.name = "dx.op.atomicBinOp.i32";
atomicFunc.type = funcType;
atomicFunc.external = true;
for(const AttributeSet &attrs : editor.GetAttributeSets())
{
if(attrs.functionSlot && attrs.functionSlot->params == Attribute::NoUnwind)
{
atomicFunc.attrs = &attrs;
break;
}
}
if(!atomicFunc.attrs)
RDCWARN("Couldn't find existing nounwind attr set");
// should we add a set here? or assume the attrs don't matter because this is a builtin function
// anyway?
atomicBinOp = editor.DeclareFunction(atomicFunc);
}
const Function *binOp = editor.GetFunctionByName("dx.op.binary.i32");
if(!binOp)
{
const Type *funcType = editor.CreateFunctionType(i32, {i32, i32, i32});
Function binopFunc;
binopFunc.name = "dx.op.binary.i32";
binopFunc.type = funcType;
binopFunc.external = true;
for(const AttributeSet &attrs : editor.GetAttributeSets())
{
if(attrs.functionSlot &&
attrs.functionSlot->params == (Attribute::NoUnwind | Attribute::ReadNone))
{
binopFunc.attrs = &attrs;
break;
}
}
// we haven't implemented adding attribute sets since their encoding is obtuse, so if we can't
// get the 'real' binop attributes try to get the next most conservative one
if(!binopFunc.attrs)
{
for(const AttributeSet &attrs : editor.GetAttributeSets())
{
if(attrs.functionSlot &&
attrs.functionSlot->params == (Attribute::NoUnwind | Attribute::ReadOnly))
{
binopFunc.attrs = &attrs;
break;
}
}
}
if(!binopFunc.attrs)
{
for(const AttributeSet &attrs : editor.GetAttributeSets())
{
if(attrs.functionSlot && attrs.functionSlot->params == Attribute::NoUnwind)
{
binopFunc.attrs = &attrs;
break;
}
}
}
if(!binopFunc.attrs)
RDCWARN("Couldn't find existing nounwind readnone attr set");
// should we add a set here? or assume the attrs don't matter because this is a builtin function
// anyway?
binOp = editor.DeclareFunction(binopFunc);
}
const Function *atomicBinOp = editor.DeclareFunction("dx.op.atomicBinOp.i32", i32,
{i32, handleType, i32, i32, i32, i32, i32},
Attribute::NoUnwind | Attribute::ReadNone);
const Function *binOp = editor.DeclareFunction("dx.op.binary.i32", i32, {i32, i32, i32},
Attribute::NoUnwind | Attribute::ReadNone);
// while we're iterating through the metadata to add our UAV, we'll also note the shader-local
// register IDs of each SRV/UAV with slots, and record the base slot in this array for easy access
@@ -344,11 +252,6 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
// the index dxc provides is register-relative
rdcarray<rdcpair<uint32_t, int32_t>> srvBaseSlots, uavBaseSlots;
// when we add metadata we do it in reverse order since it's unclear if we're supposed to have
// forward references (LLVM seems to handle it, but not emit it, so it's very much a grey area)
// we do this by recreating any nodes that we modify (or their parents recursively up to the named
// metadata)
// declare the resource, this happens purely in metadata but we need to store the slot
uint32_t regSlot = 0;
Metadata *reslist = NULL;
@@ -603,22 +506,19 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
if(createHandle)
{
RDCASSERT(!isShaderModel6_6OrAbove);
handle = editor.CreateInstruction(createHandle);
handle->type = handleType;
handle->args = {
// dx.op.createHandle opcode
editor.CreateConstant(57U),
// kind = UAV
editor.CreateConstant((uint8_t)HandleKind::UAV),
// ID/slot
editor.CreateConstant(regSlot),
// array index
editor.CreateConstant(0U),
// non-uniform
editor.CreateConstant(false),
};
f->instructions.insert(startInst++, handle);
handle = editor.InsertInstruction(
f, startInst++,
editor.CreateInstruction(createHandle, DXOp::createHandle,
{
// kind = UAV
editor.CreateConstant((uint8_t)HandleKind::UAV),
// ID/slot
editor.CreateConstant(regSlot),
// array index
editor.CreateConstant(0U),
// non-uniform
editor.CreateConstant(false),
}));
}
else if(createHandleFromBinding)
{
@@ -636,74 +536,56 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
editor.CreateConstant((uint8_t)HandleKind::UAV),
});
Instruction *handleCreate = editor.CreateInstruction(createHandleFromBinding);
handleCreate->type = handleType;
handleCreate->args = {
// dx.op.createHandleFromBinding opcode
editor.CreateConstant(217U),
// resBind
resBindConstant,
// ID/slot
editor.CreateConstant(0U),
// non-uniform
editor.CreateConstant(false),
};
Instruction *handleCreate = editor.InsertInstruction(
f, startInst++,
editor.CreateInstruction(createHandleFromBinding, DXOp::createHandleFromBinding,
{
// resBind
resBindConstant,
// ID/slot
editor.CreateConstant(0U),
// non-uniform
editor.CreateConstant(false),
}));
f->instructions.insert(startInst++, handleCreate);
// Annotate handle
handle = editor.CreateInstruction(editor.GetFunctionByName("dx.op.annotateHandle"));
handle->type = handleType;
handle->args = {
// dx.op.annotateHandle opcode
editor.CreateConstant(216U),
// Resource handle
handleCreate,
// Resource properties
editor.CreateConstant(
editor.CreateNamedStructType("dx.types.ResourceProperties", {}),
handle = editor.InsertInstruction(
f, startInst++,
editor.CreateInstruction(
annotateHandle, DXOp::annotateHandle,
{
// IsUav : (1 << 12)
editor.CreateConstant(uint32_t((1 << 12) | (uint32_t)ResourceKind::RawBuffer)),
//
editor.CreateConstant(0U),
}),
};
f->instructions.insert(startInst++, handle);
// Resource handle
handleCreate,
// Resource properties
editor.CreateConstant(
editor.CreateNamedStructType("dx.types.ResourceProperties", {}),
{
// IsUav : (1 << 12)
editor.CreateConstant(uint32_t((1 << 12) | (uint32_t)ResourceKind::RawBuffer)),
//
editor.CreateConstant(0U),
}),
}));
}
Constant *undefi32;
{
Constant c;
c.type = i32;
c.setUndef(true);
undefi32 = editor.CreateConstant(c);
}
Constant *undefi32 = editor.CreateUndef(i32);
// insert an OR to offset 0, just to indicate validity
{
Instruction *inst = editor.CreateInstruction(atomicBinOp);
inst->type = i32;
inst->args = {
// dx.op.atomicBinOp.i32 opcode
editor.CreateConstant(78U),
// feedback UAV handle
handle,
// operation OR
editor.CreateConstant(2U),
// offset
editor.CreateConstant(0U),
// offset 2
undefi32,
// offset 3
undefi32,
// value
editor.CreateConstant(magicFeedbackValue),
};
f->instructions.insert(startInst++, inst);
}
editor.InsertInstruction(f, startInst++,
editor.CreateInstruction(atomicBinOp, DXOp::atomicBinOp,
{
// feedback UAV handle
handle,
// operation OR
editor.CreateConstant(2U),
// offset
editor.CreateConstant(0U),
// offset 2
undefi32,
// offset 3
undefi32,
// value
editor.CreateConstant(magicFeedbackValue),
}));
for(size_t i = startInst; i < f->instructions.size(); i++)
{
@@ -800,72 +682,64 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
if(slotInfo.first == 0)
continue;
Instruction op;
op.op = Operation::Sub;
// idx0Based = idx - baseReg
Instruction *idx0Based = editor.CreateInstruction(Operation::Sub);
f->instructions.insert(i++, idx0Based);
idx0Based->type = i32;
idx0Based->args = {
// idx to the createHandle op
idxArg,
// register that this is relative to
editor.CreateConstant((uint32_t)slotInfo.second),
};
Instruction *idx0Based = editor.InsertInstruction(
f, i++,
editor.CreateInstruction(Operation::Sub, i32,
{
// idx to the createHandle op
idxArg,
// register that this is relative to
editor.CreateConstant((uint32_t)slotInfo.second),
}));
// slotPlusBase = idx0Based + slot
Instruction *slotPlusBase = editor.CreateInstruction(Operation::Add);
f->instructions.insert(i++, slotPlusBase);
slotPlusBase->type = i32;
slotPlusBase->args = {
// idx to the createHandle op
idx0Based,
// base slot
editor.CreateConstant(slotInfo.first),
};
Instruction *slotPlusBase = editor.InsertInstruction(
f, i++,
editor.CreateInstruction(Operation::Add, i32,
{
// idx to the createHandle op
idx0Based,
// base slot
editor.CreateConstant(slotInfo.first),
}));
// slotPlusBaseClamped = min(slotPlusBase, maxSlot)
Instruction *slotPlusBaseClamped = editor.CreateInstruction(binOp);
f->instructions.insert(i++, slotPlusBaseClamped);
slotPlusBaseClamped->type = i32;
slotPlusBaseClamped->args = {
// dx.op.binOp.i32 UMin opcode
editor.CreateConstant(40U),
// slotPlusBase
slotPlusBase,
// max slot
editor.CreateConstant(maxSlot),
};
Instruction *slotPlusBaseClamped =
editor.InsertInstruction(f, i++,
editor.CreateInstruction(binOp, DXOp::UMin,
{
// slotPlusBase
slotPlusBase,
// max slot
editor.CreateConstant(maxSlot),
}));
// byteOffset = slotPlusBaseClamped << 2
Instruction *byteOffset = editor.CreateInstruction(Operation::ShiftLeft);
f->instructions.insert(i++, byteOffset);
byteOffset->type = i32;
byteOffset->args = {
slotPlusBaseClamped,
editor.CreateConstant(2U),
};
Instruction *byteOffset =
editor.InsertInstruction(f, i++,
editor.CreateInstruction(Operation::ShiftLeft, i32,
{
slotPlusBaseClamped,
editor.CreateConstant(2U),
}));
Instruction *atomicOr = editor.CreateInstruction(atomicBinOp);
f->instructions.insert(i++, atomicOr);
atomicOr->args = {
// dx.op.atomicBinOp.i32 opcode
editor.CreateConstant(78U),
// feedback UAV handle
handle,
// operation OR
editor.CreateConstant(2U),
// offset
byteOffset,
// offset 2
undefi32,
// offset 3
undefi32,
// value
editor.CreateConstant(magicFeedbackValue),
};
atomicOr->type = i32;
editor.InsertInstruction(f, i++,
editor.CreateInstruction(atomicBinOp, DXOp::atomicBinOp,
{
// feedback UAV handle
handle,
// operation OR
editor.CreateConstant(2U),
// offset
byteOffset,
// offset 2
undefi32,
// offset 3
undefi32,
// value
editor.CreateConstant(magicFeedbackValue),
}));
}
else if(inst.op == Operation::Call && createHandleFromHeap &&
inst.getFuncCall()->name == createHandleFromHeap->name)
@@ -963,45 +837,53 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
Instruction op;
// slotPlusBase = idx0Based + slot
Instruction *slotPlusBase = editor.CreateInstruction(Operation::Add);
f->instructions.insert(i++, slotPlusBase);
slotPlusBase->type = i32;
slotPlusBase->args = {
// idx to the createHandleFromHeap op
idxArg,
// base slot
editor.CreateConstant(it->second.Slot()),
};
Instruction *slotPlusBase = editor.InsertInstruction(
f, i++,
editor.CreateInstruction(Operation::Add, i32,
{
// idx to the createHandleFromHeap op
idxArg,
// base slot
editor.CreateConstant(it->second.Slot()),
}));
// slotPlusBaseClamped = min(slotPlusBase, maxSlot)
Instruction *slotPlusBaseClamped =
editor.InsertInstruction(f, i++,
editor.CreateInstruction(binOp, DXOp::UMin,
{
// slotPlusBase
slotPlusBase,
// max slot
editor.CreateConstant(maxSlot),
}));
// byteOffset = slotPlusBase << 2
Instruction *byteOffset = editor.CreateInstruction(Operation::ShiftLeft);
f->instructions.insert(i++, byteOffset);
byteOffset->type = i32;
byteOffset->args = {
slotPlusBase,
editor.CreateConstant(2U),
};
Instruction *byteOffset =
editor.InsertInstruction(f, i++,
editor.CreateInstruction(Operation::ShiftLeft, i32,
{
slotPlusBaseClamped,
editor.CreateConstant(2U),
}));
uint32_t feedbackValue = magicFeedbackValue | (1 << (uint32_t)handleKind);
Instruction *atomicOr = editor.CreateInstruction(atomicBinOp);
f->instructions.insert(i++, atomicOr);
atomicOr->args = {
// dx.op.atomicBinOp.i32 opcode
editor.CreateConstant(78U),
// feedback UAV handle
handle,
// operation OR
editor.CreateConstant(2U),
// offset
byteOffset,
// offset 2
undefi32,
// offset 3
undefi32,
// value
editor.CreateConstant(feedbackValue),
};
atomicOr->type = i32;
editor.InsertInstruction(f, i++,
editor.CreateInstruction(atomicBinOp, DXOp::atomicBinOp,
{
// feedback UAV handle
handle,
// operation OR
editor.CreateConstant(2U),
// offset
byteOffset,
// offset 2
undefi32,
// offset 3
undefi32,
// value
editor.CreateConstant(feedbackValue),
}));
}
}
@@ -1120,14 +1002,12 @@ static bool AddArraySlots(WrappedID3D12PipelineState::ShaderEntry *shad, uint32_
{
if(AnnotateDXILShader(shad->GetDXBC(), space, slots, numSlots, editedBlob))
{
// strip ILDB because it's valid code (with debug info) and who knows what might use it
DXBC::DXBCContainer::StripChunk(editedBlob, DXBC::FOURCC_ILDB);
if(!D3D12_Debug_FeedbackDumpDirPath().empty())
{
bytebuf orig = shad->GetDXBC()->GetShaderBlob();
DXBC::DXBCContainer::StripChunk(orig, DXBC::FOURCC_ILDB);
DXBC::DXBCContainer::StripChunk(orig, DXBC::FOURCC_STAT);
FileIO::WriteAll(D3D12_Debug_FeedbackDumpDirPath() + "/before_dxil_" +
ToStr(shad->GetDetails().stage).c_str() + ".dxbc",
+64 -54
View File
@@ -490,7 +490,7 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
RDCASSERTMSG("Linkage is non-default and not internal", rootchild.ops[3] == 0,
rootchild.ops[3]);
if(rootchild.ops[4] > 0 && rootchild.ops[4] - 1 < m_AttributeSets.size())
f->attrs = &m_AttributeSets[(size_t)rootchild.ops[4] - 1];
f->attrs = m_AttributeSets[(size_t)rootchild.ops[4] - 1];
f->align = rootchild.ops[5];
@@ -565,10 +565,10 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
continue;
}
AttributeGroup group;
AttributeGroup *group = alloc.alloc<AttributeGroup>();
size_t id = (size_t)attrgroup.ops[0];
group.slotIndex = (uint32_t)attrgroup.ops[1];
group->slotIndex = (uint32_t)attrgroup.ops[1];
for(size_t i = 2; i < attrgroup.ops.size(); i++)
{
@@ -576,7 +576,7 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
{
case 0:
{
group.params |= Attribute(1ULL << (attrgroup.ops[i + 1]));
group->params |= Attribute(1ULL << (attrgroup.ops[i + 1]));
i++;
break;
}
@@ -584,13 +584,13 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
{
uint64_t param = attrgroup.ops[i + 2];
Attribute attr = Attribute(1ULL << attrgroup.ops[i + 1]);
group.params |= attr;
group->params |= attr;
switch(attr)
{
case Attribute::Alignment: group.align = param; break;
case Attribute::StackAlignment: group.stackAlign = param; break;
case Attribute::Dereferenceable: group.derefBytes = param; break;
case Attribute::DereferenceableOrNull: group.derefOrNullBytes = param; break;
case Attribute::Alignment: group->align = param; break;
case Attribute::StackAlignment: group->stackAlign = param; break;
case Attribute::Dereferenceable: group->derefBytes = param; break;
case Attribute::DereferenceableOrNull: group->derefOrNullBytes = param; break;
default: RDCERR("Unexpected attribute %llu with parameter", attr);
}
i += 2;
@@ -614,7 +614,7 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
i += a.size() + 1;
}
group.strs.push_back({a, b});
group->strs.push_back({a, b});
break;
}
}
@@ -640,24 +640,24 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
continue;
}
AttributeSet attrs;
AttributeSet *attrs = alloc.alloc<AttributeSet>();
attrs.orderedGroups = paramattr.ops;
attrs->orderedGroups = paramattr.ops;
for(uint64_t g : paramattr.ops)
{
if(g < m_AttributeGroups.size())
{
const AttributeGroup &group = m_AttributeGroups[(size_t)g];
if(group.slotIndex == AttributeGroup::FunctionSlot)
const AttributeGroup *group = m_AttributeGroups[(size_t)g];
if(group->slotIndex == AttributeGroup::FunctionSlot)
{
RDCASSERT(attrs.functionSlot == NULL);
attrs.functionSlot = &group;
RDCASSERT(attrs->functionSlot == NULL);
attrs->functionSlot = group;
}
else
{
attrs.groupSlots.resize_for_index(group.slotIndex);
attrs.groupSlots[group.slotIndex] = &m_AttributeGroups[(size_t)g];
attrs->groupSlots.resize_for_index(group->slotIndex);
attrs->groupSlots[group->slotIndex] = group;
}
}
else
@@ -1249,7 +1249,7 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
inst->type = funcCall->type->inner;
inst->opFlags() = flags;
if(paramAttrs > 0)
inst->extra(alloc).paramAttrs = &m_AttributeSets[paramAttrs - 1];
inst->extra(alloc).paramAttrs = m_AttributeSets[paramAttrs - 1];
if(funcCallType)
{
@@ -2298,28 +2298,26 @@ Metadata::~Metadata()
SAFE_DELETE(debugLoc);
}
static const uint32_t unvisitedValueId = Value::NoID - 0x1;
static const uint32_t visitedValueId = Value::NoID - 0x2;
static const uint16_t unvisitedTypeId = 0xffff;
void LLVMOrderAccumulator::reset(GlobalVar *g)
{
g->id = unvisitedValueId;
g->id = Value::UnvisitedID;
reset((Constant *)g->initialiser);
}
void LLVMOrderAccumulator::reset(Alias *a)
{
a->id = unvisitedValueId;
a->id = Value::UnvisitedID;
reset(a->val);
}
void LLVMOrderAccumulator::reset(Constant *c)
{
if(!c || c->id == unvisitedValueId)
if(!c || c->id == Value::UnvisitedID)
return;
c->id = unvisitedValueId;
c->id = Value::UnvisitedID;
c->refCount = 0;
if(c->isCast())
{
@@ -2334,14 +2332,14 @@ void LLVMOrderAccumulator::reset(Constant *c)
void LLVMOrderAccumulator::reset(Block *b)
{
b->id = unvisitedValueId;
b->id = Value::UnvisitedID;
}
void LLVMOrderAccumulator::reset(Metadata *m)
{
if(!m || m->id == unvisitedValueId)
if(!m || m->id == Value::UnvisitedID)
return;
m->id = unvisitedValueId;
m->id = Value::UnvisitedID;
reset(m->value);
@@ -2351,10 +2349,10 @@ void LLVMOrderAccumulator::reset(Metadata *m)
void LLVMOrderAccumulator::reset(Instruction *i)
{
if(!i || i->id == unvisitedValueId)
if(!i || i->id == Value::UnvisitedID)
return;
i->id = unvisitedValueId;
i->id = Value::UnvisitedID;
for(Value *a : i->args)
reset(a);
@@ -2365,7 +2363,7 @@ void LLVMOrderAccumulator::reset(Instruction *i)
void LLVMOrderAccumulator::reset(Function *f)
{
f->id = unvisitedValueId;
f->id = Value::UnvisitedID;
for(Instruction *i : f->args)
reset(i);
for(Instruction *i : f->instructions)
@@ -2394,7 +2392,7 @@ void LLVMOrderAccumulator::reset(Value *v)
reset(a);
}
void LLVMOrderAccumulator::processGlobals(Program *prog)
void LLVMOrderAccumulator::processGlobals(Program *prog, bool doLiveChecking)
{
// reset all IDs, so we know if we're encountering a new value/metadata or not when walking
for(Type *t : prog->m_Types)
@@ -2408,6 +2406,8 @@ void LLVMOrderAccumulator::processGlobals(Program *prog)
for(Function *f : prog->m_Functions)
reset(f);
liveChecking = doLiveChecking;
// just for extra fun, the search order for types for printing, and types enumerated while getting
// values is slightly different! yay yay yay!
for(const GlobalVar *g : prog->m_GlobalVars)
@@ -2454,29 +2454,35 @@ void LLVMOrderAccumulator::processGlobals(Program *prog)
accumulateTypePrintOrder(visited, meta);
}
for(const GlobalVar *g : prog->m_GlobalVars)
accumulate(g);
for(const Function *f : prog->m_Functions)
if(!liveChecking)
{
accumulate(f);
assignTypeId(prog->GetPointerType(f->type, Type::PointerAddrSpace::Default));
}
for(const GlobalVar *g : prog->m_GlobalVars)
accumulate(g);
for(const Alias *a : prog->m_Aliases)
accumulate(a);
for(const Function *f : prog->m_Functions)
{
accumulate(f);
assignTypeId(prog->GetPointerType(f->type, Type::PointerAddrSpace::Default));
}
for(const Alias *a : prog->m_Aliases)
accumulate(a);
}
firstConst = values.size();
for(const GlobalVar *g : prog->m_GlobalVars)
if(g->initialiser)
accumulate(g->initialiser);
if(!liveChecking)
{
for(const GlobalVar *g : prog->m_GlobalVars)
if(g->initialiser)
accumulate(g->initialiser);
for(const Alias *a : prog->m_Aliases)
accumulate(a->val);
for(const Alias *a : prog->m_Aliases)
accumulate(a->val);
for(const Value *v : prog->m_ValueSymtabOrder)
accumulate(v);
for(const Value *v : prog->m_ValueSymtabOrder)
accumulate(v);
}
assignTypeId(prog->m_MetaType);
@@ -2513,7 +2519,10 @@ void LLVMOrderAccumulator::processGlobals(Program *prog)
}
numConsts = values.size() - firstConst;
sortConsts = !prog->m_Uselists;
// don't skip constants when doing live checking, because then constants won't be contiguous as
// globals referenced later will be pulled into values later. When skipping globals we only care
// if they are seen at all (and given a value id)
sortConsts = !prog->m_Uselists && !liveChecking;
if(sortConsts)
{
@@ -2557,6 +2566,7 @@ void LLVMOrderAccumulator::processFunction(Function *f)
{
for(size_t a = 0; a < inst->args.size(); a++)
accumulate(cast<Constant>(inst->args[a]));
accumulate(inst->getFuncCall());
}
numFuncConsts = values.size() - firstFuncConst;
@@ -2608,7 +2618,7 @@ void LLVMOrderAccumulator::processFunction(Function *f)
accumulate(inst->getAttachedMeta()[m].second);
for(size_t a = 0; a < inst->args.size(); a++)
if(inst->args[a]->kind() == ValueKind::Constant)
if(inst->args[a]->kind() == ValueKind::Constant || liveChecking)
accumulate(inst->args[a]);
if(inst->type->isVoid())
@@ -2724,7 +2734,7 @@ void LLVMOrderAccumulator::assignTypeId(const Constant *c)
void LLVMOrderAccumulator::accumulate(const Value *v)
{
Value *value = (Value *)v;
if(!v || v->id != unvisitedValueId)
if(!v || v->id != Value::UnvisitedID)
{
Constant *c = cast<Constant>(value);
if(c)
@@ -2736,7 +2746,7 @@ void LLVMOrderAccumulator::accumulate(const Value *v)
assignTypeId(value->type);
value->id = visitedValueId;
value->id = Value::VisitedID;
if(Constant *c = cast<Constant>(value))
{
@@ -2759,11 +2769,11 @@ void LLVMOrderAccumulator::accumulate(const Value *v)
void LLVMOrderAccumulator::accumulate(const Metadata *m)
{
if(!m || m->id != unvisitedValueId)
if(!m || m->id != Value::UnvisitedID)
return;
Metadata *meta = (Metadata *)m;
meta->id = visitedValueId;
meta->id = Value::VisitedID;
for(const Metadata *c : m->children)
if(c)
+29 -3
View File
@@ -307,6 +307,28 @@ enum class Operation : uint8_t
AtomicUMin,
};
// added as needed, since names in docs/LLVM don't match neatly so there's no pre-made list
enum class DXOp : uint32_t
{
UMin = 40,
createHandle = 57,
atomicBinOp = 78,
barrier = 80,
groupId = 94,
threadIdInGroup = 95,
flattenedThreadIdInGroup = 96,
rawBufferLoad = 139,
rawBufferStore = 140,
setMeshOutputCounts = 168,
emitIndices = 169,
getMeshPayload = 170,
storeVertexOutput = 171,
storePrimitiveOutput = 172,
dispatchMesh = 173,
annotateHandle = 216,
createHandleFromBinding = 217,
};
inline Operation DecodeBinOp(const Type *type, uint64_t opcode)
{
bool isFloatOp = (type->scalarType == Type::Float);
@@ -431,6 +453,9 @@ struct Value
// this ID is very close but different to the number displayed in disassembly. This ID is only
// used internally for encoding
static constexpr uint32_t NoID = 0x00ffffff;
// these IDs are used during enumeration to count values which we have or haven't seen before
static constexpr uint32_t UnvisitedID = 0x00fffffe;
static constexpr uint32_t VisitedID = 0x00fffffd;
uint32_t id : 24;
rdcstr toString(bool withType = false) const;
@@ -1167,8 +1192,8 @@ protected:
const Type *m_MetaType = NULL;
const Type *m_LabelType = NULL;
rdcarray<AttributeGroup> m_AttributeGroups;
rdcarray<AttributeSet> m_AttributeSets;
rdcarray<AttributeGroup *> m_AttributeGroups;
rdcarray<AttributeSet *> m_AttributeSets;
rdcarray<NamedMetadata *> m_NamedMeta;
@@ -1203,7 +1228,7 @@ public:
size_t firstConst;
size_t numConsts;
void processGlobals(Program *p);
void processGlobals(Program *p, bool doLiveChecking);
size_t firstFuncConst;
size_t numFuncConsts;
@@ -1214,6 +1239,7 @@ public:
private:
size_t functionWaterMark;
bool sortConsts = true;
bool liveChecking = false;
void reset(GlobalVar *g);
void reset(Alias *a);
@@ -84,7 +84,7 @@ ProgramEditor::ProgramEditor(const DXBC::DXBCContainer *container, bytebuf &outB
// potential cycles that llvm puts in :(
LLVMOrderAccumulator accum;
accum.processGlobals(this);
accum.processGlobals(this, false);
for(size_t idx = accum.firstConst; idx < accum.firstConst + accum.numConsts; idx++)
m_Constants.push_back((Constant *)cast<const Constant>(accum.values[idx]));
@@ -102,9 +102,45 @@ ProgramEditor::ProgramEditor(const DXBC::DXBCContainer *container, bytebuf &outB
ProgramEditor::~ProgramEditor()
{
LLVMOrderAccumulator accum;
accum.processGlobals(this, true);
// delete any functions that aren't referenced by call instructions
rdcarray<const Function *> keep;
for(Function *f : m_Functions)
{
accum.processFunction(f);
accum.exitFunction();
}
RDCCOMPILE_ASSERT(Value::VisitedID < Value::UnvisitedID && Value::UnvisitedID < Value::NoID,
"ID constants should be ordered");
m_Functions.removeIf(
[&keep](Function *f) { return f->instructions.empty() && f->id >= Value::UnvisitedID; });
// delete any globals that aren't referenced
m_GlobalVars.removeIf([&accum](GlobalVar *var) { return var->id >= Value::UnvisitedID; });
m_ValueSymtabOrder.removeIf([this](Value *v) {
if(v->kind() == ValueKind::Function && !m_Functions.contains(cast<Function>(v)))
return true;
if(v->kind() == ValueKind::GlobalVar && !m_GlobalVars.contains(cast<GlobalVar>(v)))
return true;
return false;
});
// replace the DXIL bytecode in the container with
DXBC::DXBCContainer::ReplaceChunk(m_OutBlob, DXBC::FOURCC_DXIL, EncodeProgram());
// strip ILDB because it's valid code (with debug info) and who knows what might use it
DXBC::DXBCContainer::StripChunk(m_OutBlob, DXBC::FOURCC_ILDB);
// also strip STAT because it might have stale reflection info
DXBC::DXBCContainer::StripChunk(m_OutBlob, DXBC::FOURCC_STAT);
#if ENABLED(RDOC_DEVEL) && 1
// on debug builds, run through dxil for "validation" if it's available.
// we need BOTH of htese because dxil.dll's interface is incomplete, it lacks the library
@@ -205,6 +241,36 @@ Type *ProgramEditor::CreateNewType()
return m_Types.back();
}
const AttributeSet *ProgramEditor::GetAttributeSet(Attribute desiredAttrs)
{
for(const AttributeSet *attrs : m_AttributeSets)
if(attrs && attrs->functionSlot && attrs->functionSlot->params == desiredAttrs)
return attrs;
m_AttributeGroups.push_back(alloc.alloc<AttributeGroup>());
m_AttributeGroups.back()->slotIndex = AttributeGroup::FunctionSlot;
m_AttributeGroups.back()->params = desiredAttrs;
m_AttributeSets.push_back(alloc.alloc<AttributeSet>());
m_AttributeSets.back()->functionSlot = m_AttributeGroups.back();
m_AttributeSets.back()->orderedGroups = {m_AttributeGroups.size() - 1};
return m_AttributeSets.back();
}
Type *ProgramEditor::CreateScalarType(Type::ScalarKind scalarType, uint32_t bitWidth)
{
for(size_t i = 0; i < m_Types.size(); i++)
if(m_Types[i]->scalarType == scalarType && m_Types[i]->bitWidth == bitWidth)
return m_Types[i];
Type *t = CreateNewType();
t->type = Type::Scalar;
t->scalarType = scalarType;
t->bitWidth = bitWidth;
return t;
}
Type *ProgramEditor::CreateNamedStructType(const rdcstr &name, rdcarray<const Type *> members)
{
for(size_t i = 0; i < m_Types.size(); i++)
@@ -221,15 +287,15 @@ Type *ProgramEditor::CreateNamedStructType(const rdcstr &name, rdcarray<const Ty
return structType;
}
DXIL::Type *ProgramEditor::CreateFunctionType(const Type *ret, rdcarray<const Type *> params)
DXIL::Type *ProgramEditor::CreateFunctionType(const Type *retType, rdcarray<const Type *> params)
{
for(Type *type : m_Types)
if(type->type == Type::Function && type->inner == ret && type->members == params)
if(type->type == Type::Function && type->inner == retType && type->members == params)
return type;
Type *funcType = CreateNewType();
funcType->type = Type::Function;
funcType->inner = ret;
funcType->inner = retType;
funcType->members = params;
return funcType;
}
@@ -256,6 +322,47 @@ Function *ProgramEditor::GetFunctionByName(const rdcstr &name)
return NULL;
}
Function *ProgramEditor::GetFunctionByPrefix(const rdcstr &name)
{
for(size_t i = 0; i < m_Functions.size(); i++)
if(m_Functions[i]->name.beginsWith(name))
return m_Functions[i];
return NULL;
}
Function *ProgramEditor::DeclareFunction(const rdcstr &name, const Type *retType,
rdcarray<const Type *> params, Attribute desiredAttrs)
{
Function *ret = GetFunctionByName(name);
if(!ret)
{
const Type *funcType = CreateFunctionType(retType, params);
Function functionDef;
functionDef.name = name;
functionDef.type = funcType;
functionDef.external = true;
functionDef.attrs = GetAttributeSet(desiredAttrs);
ret = DeclareFunction(functionDef);
}
return ret;
}
Block *ProgramEditor::CreateBlock()
{
if(m_LabelType == NULL)
{
Type *label = CreateNewType();
label->type = Type::Label;
m_LabelType = label;
}
return new(alloc) Block(m_LabelType);
}
Metadata *ProgramEditor::GetMetadataByName(const rdcstr &name)
{
for(size_t i = 0; i < m_NamedMeta.size(); i++)
@@ -360,6 +467,11 @@ NamedMetadata *ProgramEditor::CreateNamedMetadata(const rdcstr &name)
return m_NamedMeta.back();
}
Literal *ProgramEditor::CreateLiteral(uint64_t val)
{
return new(alloc) Literal(val);
}
Constant *ProgramEditor::CreateConstant(const Constant &c)
{
// for scalars, check for an existing constant
@@ -391,6 +503,32 @@ Constant *ProgramEditor::CreateConstant(const Type *type, const rdcarray<Value *
return ret;
}
Constant *ProgramEditor::CreateConstantGEP(const Type *resultType,
const rdcarray<Value *> &pointerAndIdxs)
{
Constant *ret = new(alloc) Constant;
ret->op = Operation::GetElementPtr;
ret->type = resultType;
ret->setCompound(alloc, pointerAndIdxs);
return ret;
}
Constant *ProgramEditor::CreateUndef(const Type *t)
{
Constant c;
c.type = t;
c.setUndef(true);
return CreateConstant(c);
}
Constant *ProgramEditor::CreateNULL(const Type *t)
{
Constant c;
c.type = t;
c.setNULL(true);
return CreateConstant(c);
}
Instruction *ProgramEditor::CreateInstruction(Operation op)
{
Instruction *ret = new(alloc) Instruction;
@@ -405,7 +543,27 @@ Instruction *ProgramEditor::CreateInstruction(const Function *f)
return ret;
}
#define getAttribID(a) uint64_t(a - m_AttributeSets.begin())
Instruction *ProgramEditor::CreateInstruction(Operation op, const Type *retType,
const rdcarray<Value *> &args)
{
Instruction *ret = new(alloc) Instruction;
ret->op = op;
ret->type = retType;
ret->args = args;
return ret;
}
Instruction *ProgramEditor::CreateInstruction(const Function *f, DXOp op,
const rdcarray<Value *> &args)
{
Instruction *ret = CreateInstruction(f);
ret->type = f->type->inner;
ret->args = args;
ret->args.insert(0, CreateConstant((uint32_t)op));
return ret;
}
#define getAttribID(a) uint64_t(m_AttributeSets.indexOf((AttributeSet *)a))
#define getTypeID(t) uint64_t(t->id)
#define getMetaID(m) uint64_t(m->id)
#define getValueID(v) uint64_t(v->id)
@@ -420,7 +578,7 @@ bytebuf ProgramEditor::EncodeProgram()
LLVMBC::BitcodeWriter::Config cfg = {};
LLVMOrderAccumulator accum;
accum.processGlobals(this);
accum.processGlobals(this, false);
const rdcarray<const Value *> &values = accum.values;
const rdcarray<const Metadata *> &metadata = accum.metadata;
@@ -478,18 +636,18 @@ bytebuf ProgramEditor::EncodeProgram()
for(size_t i = 0; i < m_AttributeGroups.size(); i++)
{
if(m_AttributeGroups[i].slotIndex != AttributeGroup::InvalidSlot)
if(m_AttributeGroups[i] && m_AttributeGroups[i]->slotIndex != AttributeGroup::InvalidSlot)
{
const AttributeGroup &group = m_AttributeGroups[i];
const AttributeGroup *group = m_AttributeGroups[i];
vals.clear();
vals.push_back(i);
vals.push_back(group.slotIndex);
vals.push_back(group->slotIndex);
// decompose params bitfield into bits
if(group.params != Attribute::None)
if(group->params != Attribute::None)
{
uint64_t params = (uint64_t)group.params;
uint64_t params = (uint64_t)group->params;
for(uint64_t p = 0; p < 64; p++)
{
if((params & (1ULL << p)) != 0)
@@ -500,28 +658,28 @@ bytebuf ProgramEditor::EncodeProgram()
{
vals.push_back(1);
vals.push_back(p);
vals.push_back(group.align);
vals.push_back(group->align);
break;
}
case Attribute::StackAlignment:
{
vals.push_back(1);
vals.push_back(p);
vals.push_back(group.stackAlign);
vals.push_back(group->stackAlign);
break;
}
case Attribute::Dereferenceable:
{
vals.push_back(1);
vals.push_back(p);
vals.push_back(group.derefBytes);
vals.push_back(group->derefBytes);
break;
}
case Attribute::DereferenceableOrNull:
{
vals.push_back(1);
vals.push_back(p);
vals.push_back(group.derefOrNullBytes);
vals.push_back(group->derefOrNullBytes);
break;
}
default:
@@ -535,9 +693,9 @@ bytebuf ProgramEditor::EncodeProgram()
}
}
if(!group.strs.empty())
if(!group->strs.empty())
{
for(const rdcpair<rdcstr, rdcstr> &strAttr : group.strs)
for(const rdcpair<rdcstr, rdcstr> &strAttr : group->strs)
{
if(strAttr.second.empty())
vals.push_back(3);
@@ -565,7 +723,7 @@ bytebuf ProgramEditor::EncodeProgram()
writer.BeginBlock(LLVMBC::KnownBlock::PARAMATTR_BLOCK);
for(size_t i = 0; i < m_AttributeSets.size(); i++)
writer.Record(LLVMBC::ParamAttrRecord::ENTRY, m_AttributeSets[i].orderedGroups);
writer.Record(LLVMBC::ParamAttrRecord::ENTRY, m_AttributeSets[i]->orderedGroups);
writer.EndBlock();
}
@@ -1620,21 +1778,114 @@ void ProgramEditor::RegisterUAV(DXILResourceType type, uint32_t space, uint32_t
// patch SFI0 here for non-CS non-PS shaders
if(m_Type != DXBC::ShaderType::Compute && m_Type != DXBC::ShaderType::Pixel)
{
// cheekily cast away const since this returns the blob in-place
DXBC::GlobalShaderFlags *flags =
(DXBC::GlobalShaderFlags *)DXBC::DXBCContainer::FindChunk(m_OutBlob, DXBC::FOURCC_SFI0, sz);
// this *should* always be present, so we can just add our flag
if(flags)
(*flags) |= DXBC::GlobalShaderFlags::UAVsEveryStage;
else
RDCWARN("Feature flags chunk not present");
PatchGlobalShaderFlags(
[](DXBC::GlobalShaderFlags &flags) { flags |= DXBC::GlobalShaderFlags::UAVsEveryStage; });
}
// strip the root signature, we shouldn't need it and it may no longer match and fail validation
DXBC::DXBCContainer::StripChunk(m_OutBlob, DXBC::FOURCC_RTS0);
}
void ProgramEditor::SetNumThreads(uint32_t dim[3])
{
size_t sz = 0;
const byte *psv0 = DXBC::DXBCContainer::FindChunk(m_OutBlob, DXBC::FOURCC_PSV0, sz);
if(psv0)
{
bytebuf psv0blob(psv0, sz);
byte *begin = psv0blob.data();
byte *end = begin + sz;
byte *cur = begin;
uint32_t *headerSize = (uint32_t *)cur;
cur += sizeof(uint32_t);
if(cur >= end)
return;
// from definitions in dxc
const uint32_t headerSizeVer0 = 6 * sizeof(uint32_t);
const uint32_t headerSizeVer1 = sizeof(uint16_t) + 10 * sizeof(uint8_t);
const uint32_t headerSizeVer2 = 3 * sizeof(uint32_t);
if(*headerSize >= headerSizeVer2)
{
cur += headerSizeVer0;
cur += headerSizeVer1;
memcpy(cur, dim, sizeof(uint32_t) * 3);
}
DXBC::DXBCContainer::ReplaceChunk(m_OutBlob, DXBC::FOURCC_PSV0, psv0blob);
}
}
void ProgramEditor::SetASPayloadSize(uint32_t payloadSize)
{
size_t sz = 0;
const byte *psv0 = DXBC::DXBCContainer::FindChunk(m_OutBlob, DXBC::FOURCC_PSV0, sz);
if(psv0)
{
bytebuf psv0blob(psv0, sz);
byte *begin = psv0blob.data();
byte *end = begin + sz;
byte *cur = begin;
cur += sizeof(uint32_t);
if(cur >= end)
return;
// the AS info with the payload size is immediately at the start of the header
memcpy(cur, &payloadSize, sizeof(uint32_t));
DXBC::DXBCContainer::ReplaceChunk(m_OutBlob, DXBC::FOURCC_PSV0, psv0blob);
}
}
void ProgramEditor::SetMSPayloadSize(uint32_t payloadSize)
{
size_t sz = 0;
const byte *psv0 = DXBC::DXBCContainer::FindChunk(m_OutBlob, DXBC::FOURCC_PSV0, sz);
if(psv0)
{
bytebuf psv0blob(psv0, sz);
byte *begin = psv0blob.data();
byte *end = begin + sz;
byte *cur = begin;
cur += sizeof(uint32_t);
if(cur >= end)
return;
// the MS info is immediately at the start of the header
// the first two uint32s are groupshared related, then comes the payload size
memcpy(cur + sizeof(uint32_t) * 2, &payloadSize, sizeof(uint32_t));
DXBC::DXBCContainer::ReplaceChunk(m_OutBlob, DXBC::FOURCC_PSV0, psv0blob);
}
}
void ProgramEditor::PatchGlobalShaderFlags(std::function<void(DXBC::GlobalShaderFlags &)> patcher)
{
size_t sz = 0;
// cheekily cast away const since this returns the blob in-place
DXBC::GlobalShaderFlags *flags =
(DXBC::GlobalShaderFlags *)DXBC::DXBCContainer::FindChunk(m_OutBlob, DXBC::FOURCC_SFI0, sz);
// this *should* always be present, so we can just add our flag
if(flags)
patcher(*flags);
else
RDCWARN("Feature flags chunk not present");
}
void ProgramEditor::EncodeConstants(LLVMBC::BitcodeWriter &writer,
const rdcarray<const Value *> &values, size_t firstIdx,
size_t count) const
@@ -30,6 +30,7 @@
namespace DXBC
{
class DXBCContainer;
enum class GlobalShaderFlags : int64_t;
};
namespace LLVMBC
@@ -74,15 +75,22 @@ public:
using Program::GetInt8Type;
using Program::GetPointerType;
const rdcarray<AttributeSet> &GetAttributeSets() { return m_AttributeSets; }
int32_t GetKind(const rdcstr &kind) { return m_Kinds.indexOf(kind); }
const rdcarray<Type *> &GetTypes() const { return m_Types; }
const AttributeSet *GetAttributeSet(Attribute desiredAttrs);
Type *CreateScalarType(Type::ScalarKind scalarType, uint32_t bitWidth);
Type *CreateNamedStructType(const rdcstr &name, rdcarray<const Type *> members);
Type *CreateFunctionType(const Type *ret, rdcarray<const Type *> params);
Type *CreateFunctionType(const Type *retType, rdcarray<const Type *> params);
Type *CreatePointerType(const Type *inner, Type::PointerAddrSpace addrSpace);
Function *GetFunctionByName(const rdcstr &name);
Function *GetFunctionByPrefix(const rdcstr &name);
Metadata *GetMetadataByName(const rdcstr &name);
Function *DeclareFunction(const rdcstr &name, const Type *retType, rdcarray<const Type *> params,
Attribute desiredAttrs);
Function *DeclareFunction(const Function &f);
Block *CreateBlock();
Metadata *CreateMetadata();
Metadata *CreateConstantMetadata(Constant *val);
Metadata *CreateConstantMetadata(uint32_t val);
@@ -92,20 +100,46 @@ public:
Metadata *CreateConstantMetadata(const rdcstr &str);
NamedMetadata *CreateNamedMetadata(const rdcstr &name);
Literal *CreateLiteral(uint64_t val);
// I think constants have to be unique, so this will return an existing constant (for simple cases
// like integers or NULL) if it exists
Constant *CreateConstant(const Constant &c);
Constant *CreateConstant(const Type *t, const rdcarray<Value *> &members);
Constant *CreateConstantGEP(const Type *resultType, const rdcarray<Value *> &pointerAndIdxs);
Constant *CreateUndef(const Type *t);
Constant *CreateNULL(const Type *t);
Constant *CreateConstant(uint32_t u) { return CreateConstant(Constant(m_Int32Type, u)); }
Constant *CreateConstant(uint8_t u) { return CreateConstant(Constant(m_Int8Type, u)); }
Constant *CreateConstant(bool b) { return CreateConstant(Constant(m_BoolType, b)); }
Instruction *CreateInstruction(Operation op);
Instruction *CreateInstruction(Operation op, const Type *retType, const rdcarray<Value *> &args);
Instruction *CreateInstruction(const Function *f);
Instruction *CreateInstruction(const Function *f, DXOp op, const rdcarray<Value *> &args);
Instruction::ExtraInstructionInfo &GetInstructionExtras(Instruction *inst)
{
return inst->extra(alloc);
}
Instruction *AddInstruction(Function *f, Instruction *i)
{
f->instructions.push_back(i);
return i;
}
Instruction *InsertInstruction(Function *f, size_t idx, Instruction *i)
{
f->instructions.insert(idx, i);
return i;
}
void RegisterUAV(DXILResourceType type, uint32_t space, uint32_t regBase, uint32_t regEnd,
ResourceKind kind);
void SetNumThreads(uint32_t dim[3]);
void SetASPayloadSize(uint32_t payloadSize);
void SetMSPayloadSize(uint32_t payloadSize);
void PatchGlobalShaderFlags(std::function<void(DXBC::GlobalShaderFlags &)> patcher);
private:
bytebuf &m_OutBlob;
@@ -344,7 +344,7 @@ void Program::MakeDisassemblyString()
}
LLVMOrderAccumulator accum;
accum.processGlobals(this);
accum.processGlobals(this, false);
bool printedTypes = false;
@@ -463,13 +463,16 @@ void Program::MakeDisassemblyString()
rdcarray<const AttributeGroup *> funcAttrGroups;
for(size_t i = 0; i < m_AttributeGroups.size(); i++)
{
if(m_AttributeGroups[i].slotIndex != AttributeGroup::FunctionSlot)
if(!m_AttributeGroups[i])
continue;
if(funcAttrGroups.contains(&m_AttributeGroups[i]))
if(m_AttributeGroups[i]->slotIndex != AttributeGroup::FunctionSlot)
continue;
funcAttrGroups.push_back(&m_AttributeGroups[i]);
if(funcAttrGroups.contains(m_AttributeGroups[i]))
continue;
funcAttrGroups.push_back(m_AttributeGroups[i]);
}
for(size_t i = 0; i < m_Functions.size(); i++)
+64 -52
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="rdcinflexiblestr">
<DisplayString>{(char*)pointer,s}</DisplayString>
@@ -66,58 +66,70 @@
</ArrayItems>
</Expand>
</Type>
<Type Name="rdcpair&lt;*&gt;">
<DisplayString>{{ {first}, {second} }}</DisplayString>
<Expand>
<Item Name="first" ExcludeView="simple">first</Item>
<Item Name="second" ExcludeView="simple">second</Item>
</Expand>
</Type>
<Type Name="SDType">
<DisplayString>{name}</DisplayString>
<Expand>
<Item Name="name">name</Item>
<Item Name="basetype">basetype</Item>
<Item Name="byteSize">byteSize</Item>
<Item Name="flags">flags</Item>
</Expand>
</Type>
<Type Name="SDObject">
<DisplayString Condition="type.flags &amp; SDTypeFlags::HasCustomString">{name} = {data.str}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::String">{name} = {data.str}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::UnsignedInteger">{name} = {data.basic.u}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::SignedInteger">{name} = {data.basic.i}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::Float">{name} = {data.basic.d}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::Boolean">{name} = {data.basic.b}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::Character">{name} = {data.basic.c}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::Resource">{name} = {data.basic.id}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::Array">{name} = {type.name}[]</DisplayString>
<DisplayString>SDObject: {type.name} {name}</DisplayString>
<Expand>
<Item Condition="type.basetype == SDBasic::Array" Name="[size]" ExcludeView="simple">data.children.usedCount</Item>
<ArrayItems Condition="type.basetype == SDBasic::Array || type.basetype == SDBasic::Struct">
<Size>data.children.usedCount</Size>
<ValuePointer>data.children.elems</ValuePointer>
</ArrayItems>
<Item Condition="type.basetype != SDBasic::Array &amp;&amp; type.basetype != SDBasic::Struct" Name="type">type</Item>
<Item Condition="type.basetype != SDBasic::Array &amp;&amp; type.basetype != SDBasic::Struct" Name="name">name</Item>
<Item Condition="type.basetype != SDBasic::Array &amp;&amp; type.basetype != SDBasic::Struct" Name="data">data</Item>
</Expand>
</Type>
<Type Name="SDChunk">
<DisplayString>SDChunk: {name} ({metadata.chunkID})</DisplayString>
<Expand>
<Item Name="metadata" ExcludeView="simple">metadata</Item>
<Item Name="[size]" ExcludeView="simple">data.children.usedCount</Item>
<ArrayItems>
<Size>data.children.usedCount</Size>
<ValuePointer>data.children.elems</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="D3D12ResourceLayout">
<Type Name="rdcpair&lt;*&gt;">
<DisplayString>{{ {first}, {second} }}</DisplayString>
<Expand>
<Item Name="first" ExcludeView="simple">first</Item>
<Item Name="second" ExcludeView="simple">second</Item>
</Expand>
</Type>
<Type Name="SDType">
<DisplayString>{name}</DisplayString>
<Expand>
<Item Name="name">name</Item>
<Item Name="basetype">basetype</Item>
<Item Name="byteSize">byteSize</Item>
<Item Name="flags">flags</Item>
</Expand>
</Type>
<Type Name="SDObject">
<DisplayString Condition="type.flags &amp; SDTypeFlags::HasCustomString">{name} = {data.str}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::String">{name} = {data.str}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::UnsignedInteger">{name} = {data.basic.u}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::SignedInteger">{name} = {data.basic.i}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::Float">{name} = {data.basic.d}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::Boolean">{name} = {data.basic.b}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::Character">{name} = {data.basic.c}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::Resource">{name} = {data.basic.id}</DisplayString>
<DisplayString Condition="type.basetype == SDBasic::Array">{name} = {type.name}[]</DisplayString>
<DisplayString>SDObject: {type.name} {name}</DisplayString>
<Expand>
<Item Condition="type.basetype == SDBasic::Array" Name="[size]" ExcludeView="simple">data.children.usedCount</Item>
<ArrayItems Condition="type.basetype == SDBasic::Array || type.basetype == SDBasic::Struct">
<Size>data.children.usedCount</Size>
<ValuePointer>data.children.elems</ValuePointer>
</ArrayItems>
<Item Condition="type.basetype != SDBasic::Array &amp;&amp; type.basetype != SDBasic::Struct" Name="type">type</Item>
<Item Condition="type.basetype != SDBasic::Array &amp;&amp; type.basetype != SDBasic::Struct" Name="name">name</Item>
<Item Condition="type.basetype != SDBasic::Array &amp;&amp; type.basetype != SDBasic::Struct" Name="data">data</Item>
</Expand>
</Type>
<Type Name="SDChunk">
<DisplayString>SDChunk: {name} ({metadata.chunkID})</DisplayString>
<Expand>
<Item Name="metadata" ExcludeView="simple">metadata</Item>
<Item Name="[size]" ExcludeView="simple">data.children.usedCount</Item>
<ArrayItems>
<Size>data.children.usedCount</Size>
<ValuePointer>data.children.elems</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="D3D12ResourceLayout">
<DisplayString Condition="value == D3D12_BARRIER_LAYOUT_UNDEFINED">{D3D12_BARRIER_LAYOUT_UNDEFINED}</DisplayString>
<DisplayString Condition="value &amp; 0x80000000U">{D3D12_BARRIER_LAYOUT(value &amp; 0x7fffffffU)}</DisplayString>
<DisplayString>{D3D12_RESOURCE_STATES(value)}</DisplayString>
</Type>
</Type>
<Type Name="DXIL::Value" Inheritable="false">
<Expand>
<ExpandedItem Condition="valKind==1">*(DXIL::Literal *)this</ExpandedItem>
<ExpandedItem Condition="valKind==2">*(DXIL::Alias *)this</ExpandedItem>
<ExpandedItem Condition="valKind==3">*(DXIL::Constant *)this</ExpandedItem>
<ExpandedItem Condition="valKind==4">*(DXIL::GlobalVar *)this</ExpandedItem>
<ExpandedItem Condition="valKind==5">*(DXIL::Metadata *)this</ExpandedItem>
<ExpandedItem Condition="valKind==6">*(DXIL::Instruction *)this</ExpandedItem>
<ExpandedItem Condition="valKind==7">*(DXIL::Function *)this</ExpandedItem>
<ExpandedItem Condition="valKind==8">*(DXIL::Block *)this</ExpandedItem>
</Expand>
</Type>
</AutoVisualizer>