From 788f68a1f75685789c6b6b1007db2145afe494fc Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 9 Nov 2023 13:36:41 +0000 Subject: [PATCH] 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. --- .../driver/d3d12/d3d12_shader_feedback.cpp | 450 +++++++----------- .../driver/shaders/dxil/dxil_bytecode.cpp | 118 ++--- renderdoc/driver/shaders/dxil/dxil_bytecode.h | 32 +- .../shaders/dxil/dxil_bytecode_editor.cpp | 305 ++++++++++-- .../shaders/dxil/dxil_bytecode_editor.h | 40 +- .../driver/shaders/dxil/dxil_disassemble.cpp | 11 +- renderdoc/renderdoc.natvis | 116 +++-- 7 files changed, 644 insertions(+), 428 deletions(-) diff --git a/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp b/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp index c4dcb896e..d8c51f3dc 100644 --- a/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp +++ b/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp @@ -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> 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", diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp b/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp index 15e997faa..0776d6f34 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp @@ -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(); 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(); - 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(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(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(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) diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode.h b/renderdoc/driver/shaders/dxil/dxil_bytecode.h index a4403ea45..62fd1566d 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode.h +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode.h @@ -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 m_AttributeGroups; - rdcarray m_AttributeSets; + rdcarray m_AttributeGroups; + rdcarray m_AttributeSets; rdcarray 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); diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp b/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp index cef567a0c..921cf4ac4 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp @@ -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(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 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(v))) + return true; + + if(v->kind() == ValueKind::GlobalVar && !m_GlobalVars.contains(cast(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()); + m_AttributeGroups.back()->slotIndex = AttributeGroup::FunctionSlot; + m_AttributeGroups.back()->params = desiredAttrs; + + m_AttributeSets.push_back(alloc.alloc()); + 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 members) { for(size_t i = 0; i < m_Types.size(); i++) @@ -221,15 +287,15 @@ Type *ProgramEditor::CreateNamedStructType(const rdcstr &name, rdcarray params) +DXIL::Type *ProgramEditor::CreateFunctionType(const Type *retType, rdcarray 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 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 &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 &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 &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 &values = accum.values; const rdcarray &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 &strAttr : group.strs) + for(const rdcpair &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 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 &values, size_t firstIdx, size_t count) const diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.h b/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.h index 2121f72e6..a80a156e1 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.h +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.h @@ -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 &GetAttributeSets() { return m_AttributeSets; } + int32_t GetKind(const rdcstr &kind) { return m_Kinds.indexOf(kind); } + const rdcarray &GetTypes() const { return m_Types; } + const AttributeSet *GetAttributeSet(Attribute desiredAttrs); + Type *CreateScalarType(Type::ScalarKind scalarType, uint32_t bitWidth); Type *CreateNamedStructType(const rdcstr &name, rdcarray members); - Type *CreateFunctionType(const Type *ret, rdcarray params); + Type *CreateFunctionType(const Type *retType, rdcarray 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 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 &members); + Constant *CreateConstantGEP(const Type *resultType, const rdcarray &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 &args); Instruction *CreateInstruction(const Function *f); + Instruction *CreateInstruction(const Function *f, DXOp op, const rdcarray &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 patcher); private: bytebuf &m_OutBlob; diff --git a/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp b/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp index f9ad861fb..c98c599dd 100644 --- a/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp @@ -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 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++) diff --git a/renderdoc/renderdoc.natvis b/renderdoc/renderdoc.natvis index 2514c424d..330eff407 100644 --- a/renderdoc/renderdoc.natvis +++ b/renderdoc/renderdoc.natvis @@ -1,4 +1,4 @@ - + {(char*)pointer,s} @@ -66,58 +66,70 @@ - - {{ {first}, {second} }} - - first - second - - - - {name} - - name - basetype - byteSize - flags - - - - {name} = {data.str} - {name} = {data.str} - {name} = {data.basic.u} - {name} = {data.basic.i} - {name} = {data.basic.d} - {name} = {data.basic.b} - {name} = {data.basic.c} - {name} = {data.basic.id} - {name} = {type.name}[] - SDObject: {type.name} {name} - - data.children.usedCount - - data.children.usedCount - data.children.elems - - type - name - data - - - - SDChunk: {name} ({metadata.chunkID}) - - metadata - data.children.usedCount - - data.children.usedCount - data.children.elems - - - - + + {{ {first}, {second} }} + + first + second + + + + {name} + + name + basetype + byteSize + flags + + + + {name} = {data.str} + {name} = {data.str} + {name} = {data.basic.u} + {name} = {data.basic.i} + {name} = {data.basic.d} + {name} = {data.basic.b} + {name} = {data.basic.c} + {name} = {data.basic.id} + {name} = {type.name}[] + SDObject: {type.name} {name} + + data.children.usedCount + + data.children.usedCount + data.children.elems + + type + name + data + + + + SDChunk: {name} ({metadata.chunkID}) + + metadata + data.children.usedCount + + data.children.usedCount + data.children.elems + + + + {D3D12_BARRIER_LAYOUT_UNDEFINED} {D3D12_BARRIER_LAYOUT(value & 0x7fffffffU)} {D3D12_RESOURCE_STATES(value)} - + + + + *(DXIL::Literal *)this + *(DXIL::Alias *)this + *(DXIL::Constant *)this + *(DXIL::GlobalVar *)this + *(DXIL::Metadata *)this + *(DXIL::Instruction *)this + *(DXIL::Function *)this + *(DXIL::Block *)this + + \ No newline at end of file