From b42a1f9d1ddcdec9383782212ef466a690d5ccc4 Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 14 Feb 2023 16:31:37 +0000 Subject: [PATCH] Refactor DXIL parsing and handling to more closely mimic LLVM * This file format is so obtuse that it enforces a code structure almost by design. This makes sense since it was never intended to be used anywhere outside of LLVM internals, so it makes sense that it maps precisely to LLVM's code structure and is hard to handle otherwise. --- renderdoc/driver/d3d12/d3d12_overlay.cpp | 174 +- .../driver/d3d12/d3d12_shader_feedback.cpp | 782 +++--- .../driver/shaders/dxil/dxil_bytecode.cpp | 2302 ++++++++++------- renderdoc/driver/shaders/dxil/dxil_bytecode.h | 768 ++++-- .../shaders/dxil/dxil_bytecode_editor.cpp | 1449 ++++------- .../shaders/dxil/dxil_bytecode_editor.h | 94 +- .../driver/shaders/dxil/dxil_debuginfo.cpp | 141 +- .../driver/shaders/dxil/dxil_disassemble.cpp | 574 ++-- .../driver/shaders/dxil/dxil_reflect.cpp | 48 +- 9 files changed, 3161 insertions(+), 3171 deletions(-) diff --git a/renderdoc/driver/d3d12/d3d12_overlay.cpp b/renderdoc/driver/d3d12/d3d12_overlay.cpp index 000d183b4..938ba0fe3 100644 --- a/renderdoc/driver/d3d12/d3d12_overlay.cpp +++ b/renderdoc/driver/d3d12/d3d12_overlay.cpp @@ -386,6 +386,8 @@ void D3D12Replay::PatchQuadWritePS(D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC &pi if(dxil) { + using namespace DXIL; + rdcstr stringTable; stringTable.push_back('\0'); @@ -394,26 +396,26 @@ void D3D12Replay::PatchQuadWritePS(D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC &pi rdcarray stringTableOffsets; rdcarray semanticIndexTableOffsets; + // !!!NOTE!!! + // + // In the DXIL editing below we directly reference the raster feed DXIL metadata from the edited + // metadata. This is fine as long as the metadata 'externally' passed into the editor has + // lifetime longer than the editor { // use a local bytebuf so that if we error out, patchedPs above won't be modified - DXIL::ProgramEditor editor( - &quadOverdrawDXBC, rastFeedingDXBC.GetDXILByteCode()->GetMetadataCount() * 2, patchedDXBC); - - const DXIL::Type *i32 = editor.GetInt32Type(); - const DXIL::Type *i8 = editor.GetInt8Type(); + ProgramEditor editor(&quadOverdrawDXBC, patchedDXBC); // We need to make two changes: copy the raster-feeding shader's output signature wholesale - // into - // the pixel shader. It only needs position, which *must* have been written by definition, the - // coverage input comes from an intrinsic. None of the properties should need to change, so - // it's - // a pure deep copy of metadata and properties to ensure a compatible signature. + // into the pixel shader. It only needs position, which *must* have been written by + // definition, the coverage input comes from an intrinsic. None of the properties should need + // to change, so it's a pure deep copy of metadata and properties to ensure a compatible + // signature. // // After that, we need to find the input load ops in the original shader, and patch the row it // refers to (it would have been 0 previously). Since position is a full float4 we shouldn't // have to change anything else - const DXIL::Metadata *rastEntryPoints = + const Metadata *rastEntryPoints = rastFeedingDXBC.GetDXILByteCode()->GetMetadataByName("dx.entryPoints"); if(!rastEntryPoints) @@ -424,105 +426,68 @@ void D3D12Replay::PatchQuadWritePS(D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC &pi // TODO select the entry point for multiple entry points? RT only for now RDCASSERT(rastEntryPoints->children.size() > 0 && rastEntryPoints->children[0]); - const DXIL::Metadata *rastEntry = rastEntryPoints->children[0]; + const Metadata *rastEntry = rastEntryPoints->children[0]; RDCASSERT(rastEntry->children.size() > 2 && rastEntry->children[2]); - const DXIL::Metadata *rastSigs = rastEntry->children[2]; + const Metadata *rastSigs = rastEntry->children[2]; RDCASSERT(rastSigs->children.size() > 1 && rastSigs->children[1]); - const DXIL::Metadata *rastOutSig = rastSigs->children[1]; + const Metadata *rastOutSig = rastSigs->children[1]; - DXIL::Metadata *entryPoints = editor.GetMetadataByName("dx.entryPoints"); + Metadata *quadPSentryPoints = editor.GetMetadataByName("dx.entryPoints"); - if(!entryPoints) + if(!quadPSentryPoints) { RDCERR("Couldn't find entry point list"); return; } // TODO select the entry point for multiple entry points? RT only for now - RDCASSERT(entryPoints->children.size() > 0 && entryPoints->children[0]); - DXIL::Metadata *entry = entryPoints->children[0]; + RDCASSERT(quadPSentryPoints->children.size() > 0 && quadPSentryPoints->children[0]); + Metadata *quadPSentry = quadPSentryPoints->children[0]; - rdcstr entryName = entry->children[1]->str; + rdcstr entryName = quadPSentry->children[1]->str; - RDCASSERT(entry->children.size() > 2 && entry->children[2]); - DXIL::Metadata *sigs = entry->children[2]; + RDCASSERT(quadPSentry->children.size() > 2 && quadPSentry->children[2]); + Metadata *quadPSsigs = quadPSentry->children[2]; - RDCASSERT(sigs->children.size() > 0); + RDCASSERT(quadPSsigs->children.size() > 0); - DXIL::Metadata inputSig; + // just repoint input signature list to rast out sig + quadPSsigs->children[0] = (Metadata *)rastOutSig; uint32_t posID = ~0U; -#define DUPLICATE_META_CONSTANT(newConst, type_, oldConst) \ - { \ - DXIL::Metadata m; \ - m.isConstant = true; \ - m.type = type_; \ - m.value = DXIL::Value( \ - editor.GetOrAddConstant(DXIL::Constant(type_, oldConst->value.constant->val.u32v[0]))); \ - newConst = editor.AddMetadata(m); \ - } - + // process signature to get string table & index table for semantics for(size_t i = 0; i < rastOutSig->children.size(); i++) { - const DXIL::Metadata *oldSigEl = rastOutSig->children[i]; - DXIL::Metadata newSigEl; + const Metadata *sigEl = rastOutSig->children[i]; - newSigEl.children.resize(oldSigEl->children.size()); - - // element ID - DUPLICATE_META_CONSTANT(newSigEl.children[0], i32, oldSigEl->children[0]); - - // semantic name + // only append non-system values to the string table + uint32_t systemValue = cast(sigEl->children[3]->value)->getU32(); + if(systemValue == 0) { - DXIL::Metadata m; - m.isString = true; - m.str = oldSigEl->children[1]->str; - newSigEl.children[1] = editor.AddMetadata(m); - - // only append non-system values to the string table - if(oldSigEl->children[3]->value.constant->val.u32v[0] == 0) - { - stringTableOffsets.push_back((uint32_t)stringTable.size()); - stringTable.append(m.str); - stringTable.push_back('\0'); - } - else - { - stringTableOffsets.push_back(0); - } + stringTableOffsets.push_back((uint32_t)stringTable.size()); + stringTable.append(sigEl->children[1]->str); + stringTable.push_back('\0'); } + else + { + stringTableOffsets.push_back(0); - // component type - DUPLICATE_META_CONSTANT(newSigEl.children[2], i8, oldSigEl->children[2]); - - // semantic kind - DUPLICATE_META_CONSTANT(newSigEl.children[3], i8, oldSigEl->children[3]); - - // SV_Position is 3 - if(oldSigEl->children[3]->value.constant->val.u32v[0] == 3) - posID = oldSigEl->children[0]->value.constant->val.u32v[0]; + // SV_Position is 3 + if(systemValue == 3) + posID = cast(sigEl->children[0]->value)->getU32(); + } rdcarray semIndexValues; // semantic indices - const DXIL::Metadata *oldSemIdxs = oldSigEl->children[4]; - if(oldSemIdxs) + if(const Metadata *semIdxs = sigEl->children[4]) { - DXIL::Metadata semanticIndices; - // the semantic index node is a list of constants - semanticIndices.children.resize(oldSemIdxs->children.size()); - for(size_t sidx = 0; sidx < oldSemIdxs->children.size(); sidx++) - { - DUPLICATE_META_CONSTANT(semanticIndices.children[sidx], i32, oldSemIdxs->children[sidx]); - semIndexValues.push_back(oldSemIdxs->children[sidx]->value.constant->val.u32v[0]); - } - - // copy the list - newSigEl.children[4] = editor.AddMetadata(semanticIndices); + for(size_t sidx = 0; sidx < semIdxs->children.size(); sidx++) + semIndexValues.push_back(cast(semIdxs->children[sidx]->value)->getU32()); } size_t tableOffset = ~0U; @@ -555,40 +520,6 @@ void D3D12Replay::PatchQuadWritePS(D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC &pi } semanticIndexTableOffsets.push_back((uint32_t)tableOffset); - - // interpolation mode - DUPLICATE_META_CONSTANT(newSigEl.children[5], i8, oldSigEl->children[5]); - - // number of rows - DUPLICATE_META_CONSTANT(newSigEl.children[6], i32, oldSigEl->children[6]); - - // number of columns - DUPLICATE_META_CONSTANT(newSigEl.children[7], i8, oldSigEl->children[7]); - - // start row - DUPLICATE_META_CONSTANT(newSigEl.children[8], i32, oldSigEl->children[8]); - - // start column - DUPLICATE_META_CONSTANT(newSigEl.children[9], i8, oldSigEl->children[9]); - - // the extra tag/thing list is also a series of ints - const DXIL::Metadata *oldTagList = oldSigEl->children[10]; - if(oldTagList) - { - DXIL::Metadata tagList; - - // the semantic index node is a list of constants - tagList.children.resize(oldTagList->children.size()); - for(size_t sidx = 0; sidx < oldTagList->children.size(); sidx++) - { - DUPLICATE_META_CONSTANT(tagList.children[sidx], i32, oldTagList->children[sidx]); - } - - // copy the list - newSigEl.children[10] = editor.AddMetadata(tagList); - } - - inputSig.children.push_back(editor.AddMetadata(newSigEl)); } if(posID == ~0U) @@ -597,16 +528,7 @@ void D3D12Replay::PatchQuadWritePS(D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC &pi return; } - // recreate input signature list, for backwards references - sigs->children[0] = editor.AddMetadata(inputSig); - - // recreate backwards upwards - sigs = editor.AddMetadata(*sigs); - entry->children[2] = sigs; - entry = editor.AddMetadata(*entry); - entryPoints->children[0] = entry; - - DXIL::Function *f = editor.GetFunctionByName(entryName); + Function *f = editor.GetFunctionByName(entryName); if(!f) { @@ -614,15 +536,15 @@ void D3D12Replay::PatchQuadWritePS(D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC &pi return; } - DXIL::Value inputIDValue(editor.GetOrAddConstant(f, DXIL::Constant(i32, posID))); + Value *inputIDValue = editor.CreateConstant(posID); // now locate the loadInputs and patch the row they refer to. We can unconditionally patch // them all as there was only one input previously for(size_t i = 0; i < f->instructions.size(); i++) { - DXIL::Instruction &inst = f->instructions[i]; + Instruction &inst = *f->instructions[i]; - if(inst.op == DXIL::Operation::Call && inst.funcCall->name == "dx.op.loadInput.f32") + if(inst.op == Operation::Call && inst.getFuncCall()->name == "dx.op.loadInput.f32") { if(inst.args.size() != 5) { diff --git a/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp b/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp index 53a9d0c00..9bd8c38d4 100644 --- a/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp +++ b/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp @@ -206,15 +206,16 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, const std::map &slots, uint32_t maxSlot, bytebuf &editedBlob) { - DXIL::ProgramEditor editor(dxbc, slots.size() + 64, editedBlob); + using namespace DXIL; - const DXIL::Type *handleType = editor.GetTypeByName("dx.types.Handle"); - const DXIL::Function *createHandle = editor.GetFunctionByName("dx.op.createHandle"); - const DXIL::Function *createHandleFromBinding = + ProgramEditor editor(dxbc, editedBlob); + + const Type *handleType = editor.CreateNamedStructType("dx.types.Handle", {}); + const Function *createHandle = editor.GetFunctionByName("dx.op.createHandle"); + const Function *createHandleFromBinding = editor.GetFunctionByName("dx.op.createHandleFromBinding"); - const DXIL::Function *createHandleFromHeap = - editor.GetFunctionByName("dx.op.createHandleFromHeap"); - const DXIL::Function *annotateHandle = editor.GetFunctionByName("dx.op.annotateHandle"); + const Function *createHandleFromHeap = editor.GetFunctionByName("dx.op.createHandleFromHeap"); + const Function *annotateHandle = editor.GetFunctionByName("dx.op.annotateHandle"); bool isShaderModel6_6OrAbove = dxbc->m_Version.Major > 6 || (dxbc->m_Version.Major == 6 && dxbc->m_Version.Minor >= 6); @@ -224,53 +225,24 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, (isShaderModel6_6OrAbove && !createHandleFromHeap && !createHandleFromBinding)) return false; - const DXIL::Type *i32 = editor.GetInt32Type(); - const DXIL::Type *i8 = editor.GetInt8Type(); - const DXIL::Type *i1 = editor.GetBoolType(); + 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 DXIL::Type *resBindType = editor.GetTypeByName("dx.types.ResBind"); - if(!resBindType) - { - DXIL::Type resBindTypeTmp; - resBindTypeTmp.type = DXIL::Type::Struct; - resBindTypeTmp.scalarType = DXIL::Type::Void; - resBindTypeTmp.name = "dx.types.ResBind"; - resBindTypeTmp.members = {i32, i32, i32, i8}; - resBindType = editor.AddType(resBindTypeTmp); - } + const Type *resBindType = editor.CreateNamedStructType("dx.types.ResBind", {i32, i32, i32, i8}); + const Type *funcType = editor.CreateFunctionType(handleType, {i32, resBindType, i32, i1}); - const DXIL::Type *funcType = NULL; - for(const DXIL::Type &type : editor.GetTypes()) - { - if(type.type == DXIL::Type::Function && type.inner == handleType && - type.members.size() == 4 && type.members[0] == i32 && type.members[1] == resBindType && - type.members[2] == i32 && type.members[3] == i1) - { - funcType = &type; - break; - } - } - - if(!funcType) - { - DXIL::Type funcTypeTmp; - funcTypeTmp.type = DXIL::Type::Function; - funcTypeTmp.inner = handleType; - funcTypeTmp.members = {i32, resBindType, i32, i1}; - funcType = editor.AddType(funcTypeTmp); - } - - DXIL::Function createHandleBaseFunction; + Function createHandleBaseFunction; createHandleBaseFunction.name = "dx.op.createHandleFromBinding"; - createHandleBaseFunction.funcType = funcType; + createHandleBaseFunction.type = funcType; createHandleBaseFunction.external = true; - for(const DXIL::AttributeSet &attrs : editor.GetAttributeSets()) + for(const AttributeSet &attrs : editor.GetAttributeSets()) { - if(attrs.functionSlot && attrs.functionSlot->params == DXIL::Attribute::NoUnwind) + if(attrs.functionSlot && attrs.functionSlot->params == Attribute::NoUnwind) { createHandleBaseFunction.attrs = &attrs; break; @@ -283,25 +255,19 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, } // get the functions we'll need - const DXIL::Function *atomicBinOp = editor.GetFunctionByName("dx.op.atomicBinOp.i32"); + Function *atomicBinOp = editor.GetFunctionByName("dx.op.atomicBinOp.i32"); if(!atomicBinOp) { - DXIL::Type atomicType; - atomicType.type = DXIL::Type::Function; - // return type - atomicType.inner = i32; - atomicType.members = {i32, handleType, i32, i32, i32, i32, i32}; + const Type *funcType = editor.CreateFunctionType(i32, {i32, handleType, i32, i32, i32, i32, i32}); - const DXIL::Type *funcType = editor.AddType(atomicType); - - DXIL::Function atomicFunc; + Function atomicFunc; atomicFunc.name = "dx.op.atomicBinOp.i32"; - atomicFunc.funcType = funcType; + atomicFunc.type = funcType; atomicFunc.external = true; - for(const DXIL::AttributeSet &attrs : editor.GetAttributeSets()) + for(const AttributeSet &attrs : editor.GetAttributeSets()) { - if(attrs.functionSlot && attrs.functionSlot->params == DXIL::Attribute::NoUnwind) + if(attrs.functionSlot && attrs.functionSlot->params == Attribute::NoUnwind) { atomicFunc.attrs = &attrs; break; @@ -316,26 +282,20 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, atomicBinOp = editor.DeclareFunction(atomicFunc); } - const DXIL::Function *binOp = editor.GetFunctionByName("dx.op.binOp.i32"); + const Function *binOp = editor.GetFunctionByName("dx.op.binary.i32"); if(!binOp) { - DXIL::Type binopType; - binopType.type = DXIL::Type::Function; - // return type - binopType.inner = i32; - binopType.members = {i32, i32, i32}; + const Type *funcType = editor.CreateFunctionType(i32, {i32, i32, i32}); - const DXIL::Type *funcType = editor.AddType(binopType); - - DXIL::Function binopFunc; - binopFunc.name = "dx.op.binOp.i32"; - binopFunc.funcType = funcType; + Function binopFunc; + binopFunc.name = "dx.op.binary.i32"; + binopFunc.type = funcType; binopFunc.external = true; - for(const DXIL::AttributeSet &attrs : editor.GetAttributeSets()) + for(const AttributeSet &attrs : editor.GetAttributeSets()) { if(attrs.functionSlot && - attrs.functionSlot->params == (DXIL::Attribute::NoUnwind | DXIL::Attribute::ReadNone)) + attrs.functionSlot->params == (Attribute::NoUnwind | Attribute::ReadNone)) { binopFunc.attrs = &attrs; break; @@ -346,10 +306,10 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, // get the 'real' binop attributes try to get the next most conservative one if(!binopFunc.attrs) { - for(const DXIL::AttributeSet &attrs : editor.GetAttributeSets()) + for(const AttributeSet &attrs : editor.GetAttributeSets()) { if(attrs.functionSlot && - attrs.functionSlot->params == (DXIL::Attribute::NoUnwind | DXIL::Attribute::ReadOnly)) + attrs.functionSlot->params == (Attribute::NoUnwind | Attribute::ReadOnly)) { binopFunc.attrs = &attrs; break; @@ -359,9 +319,9 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, if(!binopFunc.attrs) { - for(const DXIL::AttributeSet &attrs : editor.GetAttributeSets()) + for(const AttributeSet &attrs : editor.GetAttributeSets()) { - if(attrs.functionSlot && attrs.functionSlot->params == DXIL::Attribute::NoUnwind) + if(attrs.functionSlot && attrs.functionSlot->params == Attribute::NoUnwind) { binopFunc.attrs = &attrs; break; @@ -391,51 +351,25 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, // declare the resource, this happens purely in metadata but we need to store the slot uint32_t regSlot = 0; - DXIL::Metadata *reslist = NULL; + Metadata *reslist = NULL; { - const DXIL::Type *rw = editor.GetTypeByName("struct.RWByteAddressBuffer"); + const Type *rw = editor.CreateNamedStructType("struct.RWByteAddressBuffer", {i32}); + const Type *rwptr = editor.CreatePointerType(rw, Type::PointerAddrSpace::Default); - // declare the type if not present - if(!rw) - { - DXIL::Type rwType; - rwType.name = "struct.RWByteAddressBuffer"; - rwType.type = DXIL::Type::Struct; - rwType.members = {i32}; - - rw = editor.AddType(rwType); - } - - const DXIL::Type *rwptr = editor.GetPointerType(rw, DXIL::Type::PointerAddrSpace::Default); - - if(!rwptr) - { - DXIL::Type rwPtrType; - rwPtrType.type = DXIL::Type::Pointer; - rwPtrType.addrSpace = DXIL::Type::PointerAddrSpace::Default; - rwPtrType.inner = rw; - - rwptr = editor.AddType(rwPtrType); - } - - DXIL::Metadata *resources = editor.GetMetadataByName("dx.resources"); - if(!resources) - { - DXIL::Metadata tmpResList; - tmpResList.children.resize(4); - DXIL::NamedMetadata tmpResources; - tmpResources.name = "dx.resources"; - tmpResources.children.push_back(editor.AddMetadata(tmpResList)); - resources = editor.AddNamedMetadata(tmpResources); - } - // if there are no resources declared we can't have any dynamic indexing - if(!resources) - return false; + Metadata *resources = editor.CreateNamedMetadata("dx.resources"); + if(resources->children.empty()) + resources->children.push_back(editor.CreateMetadata()); reslist = resources->children[0]; - DXIL::Metadata *srvs = reslist->children[0]; - DXIL::Metadata *uavs = reslist->children[1]; + if(reslist->children.empty()) + reslist->children.resize(4); + + Metadata *srvs = reslist->children[0]; + Metadata *uavs = reslist->children[1]; + // if there isn't a UAV list, create an empty one so we can add our own + if(!uavs) + uavs = reslist->children[1] = editor.CreateMetadata(); D3D12FeedbackKey key; @@ -444,32 +378,32 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, for(size_t i = 0; srvs && i < srvs->children.size(); i++) { // each SRV child should have a fixed format - const DXIL::Metadata *srv = srvs->children[i]; - const DXIL::Metadata *slot = srv->children[(size_t)DXIL::ResField::ID]; - const DXIL::Metadata *srvSpace = srv->children[(size_t)DXIL::ResField::Space]; - const DXIL::Metadata *reg = srv->children[(size_t)DXIL::ResField::RegBase]; + const Metadata *srv = srvs->children[i]; + const Constant *slot = cast(srv->children[(size_t)ResField::ID]->value); + const Constant *srvSpace = cast(srv->children[(size_t)ResField::Space]->value); + const Constant *reg = cast(srv->children[(size_t)ResField::RegBase]->value); - if(slot->value.type != DXIL::ValueType::Constant) + if(!slot) { RDCWARN("Unexpected non-constant slot ID in SRV"); continue; } - if(srvSpace->value.type != DXIL::ValueType::Constant) + if(!srvSpace) { RDCWARN("Unexpected non-constant register space in SRV"); continue; } - if(reg->value.type != DXIL::ValueType::Constant) + if(!reg) { RDCWARN("Unexpected non-constant register base in SRV"); continue; } - uint32_t id = slot->value.constant->val.u32v[0]; - key.bind.bindset = srvSpace->value.constant->val.u32v[0]; - key.bind.bind = reg->value.constant->val.u32v[0]; + uint32_t id = slot->getU32(); + key.bind.bindset = srvSpace->getU32(); + key.bind.bind = reg->getU32(); // ensure every valid ID has an index, even if it's 0 srvBaseSlots.resize_for_index(id); @@ -495,43 +429,43 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, key.type = DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW; - for(size_t i = 0; uavs && i < uavs->children.size(); i++) + for(size_t i = 0; i < uavs->children.size(); i++) { // each UAV child should have a fixed format, [0] is the reg ID and I think this should always // be == the index - const DXIL::Metadata *uav = uavs->children[i]; - const DXIL::Metadata *slot = uav->children[(size_t)DXIL::ResField::ID]; - const DXIL::Metadata *uavSpace = uav->children[(size_t)DXIL::ResField::Space]; - const DXIL::Metadata *reg = uav->children[(size_t)DXIL::ResField::RegBase]; + const Metadata *uav = uavs->children[i]; + const Constant *slot = cast(uav->children[(size_t)ResField::ID]->value); + const Constant *uavSpace = cast(uav->children[(size_t)ResField::Space]->value); + const Constant *reg = cast(uav->children[(size_t)ResField::RegBase]->value); - if(slot->value.type != DXIL::ValueType::Constant) + if(!slot) { RDCWARN("Unexpected non-constant slot ID in UAV"); continue; } - if(uavSpace->value.type != DXIL::ValueType::Constant) + if(!uavSpace) { RDCWARN("Unexpected non-constant register space in UAV"); continue; } - if(reg->value.type != DXIL::ValueType::Constant) + if(!reg) { RDCWARN("Unexpected non-constant register base in UAV"); continue; } - RDCASSERT(slot->value.constant->val.u32v[0] == i); + RDCASSERT(slot->getU32() == i); - uint32_t id = slot->value.constant->val.u32v[0]; + uint32_t id = slot->getU32(); regSlot = RDCMAX(id + 1, regSlot); // ensure every valid ID has an index, even if it's 0 uavBaseSlots.resize_for_index(id); - key.bind.bindset = uavSpace->value.constant->val.u32v[0]; - key.bind.bind = reg->value.constant->val.u32v[0]; + key.bind.bindset = uavSpace->getU32(); + key.bind.bind = reg->getU32(); auto it = slots.find(key); @@ -552,80 +486,33 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, uavBaseSlots[id] = {feedbackSlot, key.bind.bind}; } - DXIL::Metadata *uavRecord[11] = { - editor.AddMetadata(DXIL::Metadata()), - editor.AddMetadata(DXIL::Metadata()), - editor.AddMetadata(DXIL::Metadata()), - editor.AddMetadata(DXIL::Metadata()), - editor.AddMetadata(DXIL::Metadata()), - editor.AddMetadata(DXIL::Metadata()), - editor.AddMetadata(DXIL::Metadata()), - editor.AddMetadata(DXIL::Metadata()), - editor.AddMetadata(DXIL::Metadata()), - editor.AddMetadata(DXIL::Metadata()), - NULL, - }; - -#define SET_CONST_META(m, t, v) \ - m->isConstant = true; \ - m->type = t; \ - m->value = DXIL::Value(editor.GetOrAddConstant(DXIL::Constant(t, v))); - - // linear reg slot/ID - SET_CONST_META(uavRecord[0], i32, regSlot); - // variable - { - DXIL::Constant c; - c.type = rwptr; - c.undef = true; - uavRecord[1]->isConstant = true; - uavRecord[1]->type = rwptr; - uavRecord[1]->value = DXIL::Value(editor.GetOrAddConstant(c)); - } - // name, empty - uavRecord[2]->isString = true; - // reg space - SET_CONST_META(uavRecord[3], i32, space); - // reg base - SET_CONST_META(uavRecord[4], i32, 0U); - // reg count - SET_CONST_META(uavRecord[5], i32, 1U); - // shape - SET_CONST_META(uavRecord[6], i32, uint32_t(DXIL::ResourceKind::RawBuffer)); - // globally coherent - SET_CONST_META(uavRecord[7], i1, 0U); - // hidden counter - SET_CONST_META(uavRecord[8], i1, 0U); - // raster order - SET_CONST_META(uavRecord[9], i1, 0U); - // UAV tags - uavRecord[10] = NULL; + Constant rwundef; + rwundef.type = rwptr; + rwundef.setUndef(true); // create the new UAV record - DXIL::Metadata *feedbackuav = editor.AddMetadata(DXIL::Metadata()); - feedbackuav->children.assign(uavRecord, ARRAY_COUNT(uavRecord)); - - // now push our record onto a new UAV list (either copied from the existing, or created fresh if - // there isn't an existing one). This ensures it has all backwards references - if(uavs) - uavs = editor.AddMetadata(*uavs); - else - uavs = editor.AddMetadata(DXIL::Metadata()); + Metadata *feedbackuav = editor.CreateMetadata(); + feedbackuav->children = { + editor.CreateConstantMetadata(regSlot), + editor.CreateConstantMetadata(editor.CreateConstant(rwundef)), + editor.CreateConstantMetadata(""), + editor.CreateConstantMetadata(space), + editor.CreateConstantMetadata(0U), // reg base + editor.CreateConstantMetadata(1U), // reg count + editor.CreateConstantMetadata(uint32_t(ResourceKind::RawBuffer)), // shape + editor.CreateConstantMetadata(false), // globally coherent + editor.CreateConstantMetadata(false), // hidden counter + editor.CreateConstantMetadata(false), // raster order + NULL, // UAV tags + }; uavs->children.push_back(feedbackuav); - - // now recreate the reslist, and repoint the uavs - reslist = editor.AddMetadata(*reslist); - reslist->children[1] = uavs; - - // finally repoint the named metadata - resources->children[0] = reslist; } rdcstr entryName; // add the entry point tags { - DXIL::Metadata *entryPoints = editor.GetMetadataByName("dx.entryPoints"); + Metadata *entryPoints = editor.GetMetadataByName("dx.entryPoints"); if(!entryPoints) { @@ -634,21 +521,23 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, } // TODO select the entry point for multiple entry points? RT only for now - DXIL::Metadata *entry = entryPoints->children[0]; + Metadata *entry = entryPoints->children[0]; entryName = entry->children[1]->str; - DXIL::Metadata *taglist = entry->children[4]; + Metadata *taglist = entry->children[4]; + if(!taglist) + taglist = entry->children[4] = editor.CreateMetadata(); // find existing shader flags tag, if there is one - DXIL::Metadata *shaderFlagsTag = NULL; - DXIL::Metadata *shaderFlagsData = NULL; + Metadata *shaderFlagsTag = NULL; + Metadata *shaderFlagsData = NULL; size_t existingTag = 0; for(size_t t = 0; taglist && t < taglist->children.size(); t += 2) { RDCASSERT(taglist->children[t]->isConstant); - if(taglist->children[t]->value.constant->val.u32v[0] == - (uint32_t)DXIL::ShaderEntryTag::ShaderFlags) + if(cast(taglist->children[t]->value)->getU32() == + (uint32_t)ShaderEntryTag::ShaderFlags) { shaderFlagsTag = taglist->children[t]; shaderFlagsData = taglist->children[t + 1]; @@ -657,7 +546,8 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, } } - uint32_t shaderFlagsValue = shaderFlagsData ? shaderFlagsData->value.constant->val.u32v[0] : 0U; + uint32_t shaderFlagsValue = + shaderFlagsData ? cast(shaderFlagsData->value)->getU32() : 0U; // raw and structured buffers shaderFlagsValue |= 0x10; @@ -670,22 +560,12 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, } // (re-)create shader flags tag - shaderFlagsData = editor.AddMetadata(DXIL::Metadata()); - SET_CONST_META(shaderFlagsData, i32, shaderFlagsValue); + shaderFlagsData = editor.CreateConstantMetadata(shaderFlagsValue); // if we didn't have a shader tags entry at all, create the metadata node for the shader flags // tag if(!shaderFlagsTag) - { - shaderFlagsTag = editor.AddMetadata(DXIL::Metadata()); - SET_CONST_META(shaderFlagsTag, i32, (uint32_t)DXIL::ShaderEntryTag::ShaderFlags); - } - - // (re-)create tag list - if(taglist) - taglist = editor.AddMetadata(*taglist); - else - taglist = editor.AddMetadata(DXIL::Metadata()); + shaderFlagsTag = editor.CreateConstantMetadata((uint32_t)ShaderEntryTag::ShaderFlags); // if we had a tag already, we can just re-use that tag node and replace the data node. // Otherwise we need to add both, and we insert them first @@ -699,22 +579,15 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, taglist->children.insert(1, shaderFlagsData); } - // recreate the entry - entry = editor.AddMetadata(*entry); - - // repoint taglist and reslist + // set reslist and taglist in case they were null before entry->children[3] = reslist; entry->children[4] = taglist; - - // finally repoint the named metadata - entryPoints->children[0] = entry; } // get the editor to patch PSV0 with our extra UAV - editor.RegisterUAV(DXIL::DXILResourceType::ByteAddressUAV, space, 0, 0, - DXIL::ResourceKind::RawBuffer); + editor.RegisterUAV(DXILResourceType::ByteAddressUAV, space, 0, 0, ResourceKind::RawBuffer); - DXIL::Function *f = editor.GetFunctionByName(entryName); + Function *f = editor.GetFunctionByName(entryName); if(!f) { @@ -726,132 +599,123 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, RDCASSERTNOTEQUAL(createHandle == NULL, createHandleFromBinding == NULL); int startInst = 0; // create our handle first thing - DXIL::Instruction *handle = NULL; + Instruction *handle = NULL; if(createHandle) { RDCASSERT(!isShaderModel6_6OrAbove); - DXIL::Instruction inst; - inst.op = DXIL::Operation::Call; - inst.type = handleType; - inst.funcCall = createHandle; - inst.args = { + handle = editor.CreateInstruction(createHandle); + handle->type = handleType; + handle->args = { // dx.op.createHandle opcode - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 57U))), + editor.CreateConstant(57U), // kind = UAV - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i8, (uint32_t)DXIL::HandleKind::UAV))), + editor.CreateConstant((uint8_t)HandleKind::UAV), // ID/slot - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, regSlot))), + editor.CreateConstant(regSlot), // array index - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 0U))), + editor.CreateConstant(0U), // non-uniform - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i1, 0U))), + editor.CreateConstant(false), }; - handle = editor.AddInstruction(f, startInst++, inst); + f->instructions.insert(startInst++, handle); } else if(createHandleFromBinding) { RDCASSERT(isShaderModel6_6OrAbove); - const DXIL::Type *resBindType = editor.GetTypeByName("dx.types.ResBind"); - DXIL::Constant resBindConstant(resBindType, 0U); - resBindConstant.members = { - // Lower id bound - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 0))), - // Upper id bound - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 0))), - // Space ID - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, space))), - // kind = UAV - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i8, (uint32_t)DXIL::HandleKind::UAV))), - }; + const Type *resBindType = editor.CreateNamedStructType("dx.types.ResBind", {}); + Constant *resBindConstant = + editor.CreateConstant(resBindType, { + // Lower id bound + editor.CreateConstant(0U), + // Upper id bound + editor.CreateConstant(0U), + // Space ID + editor.CreateConstant(space), + // kind = UAV + editor.CreateConstant((uint8_t)HandleKind::UAV), + }); - DXIL::Instruction inst; - inst.op = DXIL::Operation::Call; - inst.type = handleType; - inst.funcCall = createHandleFromBinding; - inst.args = { + Instruction *handleCreate = editor.CreateInstruction(createHandleFromBinding); + handleCreate->type = handleType; + handleCreate->args = { // dx.op.createHandleFromBinding opcode - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 217U))), + editor.CreateConstant(217U), // resBind - DXIL::Value(editor.GetOrAddConstant(f, resBindConstant)), + resBindConstant, // ID/slot - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 0U))), + editor.CreateConstant(0U), // non-uniform - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i1, 0U))), + editor.CreateConstant(false), }; - handle = editor.AddInstruction(f, startInst++, inst); - - const DXIL::Type *resPropsType = editor.GetTypeByName("dx.types.ResourceProperties"); - DXIL::Constant resPropConstant(resPropsType, 0U); - resPropConstant.members = { - // IsUav : (1 << 12) - DXIL::Value(editor.GetOrAddConstant( - f, DXIL::Constant(i32, (1 << 12) | (uint32_t)DXIL::ResourceKind::RawBuffer))), - // - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 0))), - }; + f->instructions.insert(startInst++, handleCreate); // Annotate handle - DXIL::Instruction inst2; - inst2.op = DXIL::Operation::Call; - inst2.type = handleType; - inst2.funcCall = editor.GetFunctionByName("dx.op.annotateHandle"); - inst2.args = {// dx.op.annotateHandle opcode - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 216U))), - // Resource handle - DXIL::Value(handle), - // Resource properties - DXIL::Value(editor.GetOrAddConstant(f, resPropConstant))}; - - handle = editor.AddInstruction(f, startInst++, inst2); - } - - const DXIL::Constant *undefi32; - { - DXIL::Constant c; - c.type = i32; - c.undef = true; - undefi32 = editor.GetOrAddConstant(f, c); - } - - // insert an or to offset 0, just to indicate validity - { - DXIL::Instruction inst; - inst.op = DXIL::Operation::Call; - inst.type = i32; - inst.funcCall = atomicBinOp; - inst.args = { - // dx.op.atomicBinOp.i32 opcode - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 78U))), - // feedback UAV handle - DXIL::Value(handle), - // operation OR - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 2U))), - // offset - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 0U))), - // offset 2 - DXIL::Value(undefi32), - // offset 3 - DXIL::Value(undefi32), - // value - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, magicFeedbackValue))), + 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", {}), + { + // IsUav : (1 << 12) + editor.CreateConstant(uint32_t((1 << 12) | (uint32_t)ResourceKind::RawBuffer)), + // + editor.CreateConstant(0U), + }), }; - editor.AddInstruction(f, startInst++, inst); + f->instructions.insert(startInst++, handle); + } + + Constant *undefi32; + { + Constant c; + c.type = i32; + c.setUndef(true); + undefi32 = editor.CreateConstant(c); + } + + // 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); } for(size_t i = startInst; i < f->instructions.size(); i++) { - const DXIL::Instruction &inst = f->instructions[i]; + const Instruction &inst = *f->instructions[i]; // we want to annotate any calls to createHandle - if(inst.op == DXIL::Operation::Call && - ((createHandle && inst.funcCall->name == createHandle->name) || - (createHandleFromBinding && inst.funcCall->name == createHandleFromBinding->name))) + if(inst.op == Operation::Call && + ((createHandle && inst.getFuncCall()->name == createHandle->name) || + (createHandleFromBinding && inst.getFuncCall()->name == createHandleFromBinding->name))) { - DXIL::Value idxArg; + Value *idxArg; rdcpair slotInfo = {0, 0}; - if((createHandle && inst.funcCall->name == createHandle->name)) + if((createHandle && inst.getFuncCall()->name == createHandle->name)) { RDCASSERT(!isShaderModel6_6OrAbove); if(inst.args.size() != 5) @@ -860,20 +724,20 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, continue; } - DXIL::Value kindArg = inst.args[1]; - DXIL::Value idArg = inst.args[2]; + Constant *kindArg = cast(inst.args[1]); + Constant *idArg = cast(inst.args[2]); idxArg = inst.args[3]; - if(kindArg.type != DXIL::ValueType::Constant || idArg.type != DXIL::ValueType::Constant) + if(!kindArg || !idArg) { RDCERR("Unexpected non-constant argument to createHandle"); continue; } - DXIL::HandleKind kind = (DXIL::HandleKind)kindArg.constant->val.u32v[0]; - uint32_t id = idArg.constant->val.u32v[0]; - if(kind == DXIL::HandleKind::SRV && id < srvBaseSlots.size()) + HandleKind kind = (HandleKind)kindArg->getU32(); + uint32_t id = idArg->getU32(); + if(kind == HandleKind::SRV && id < srvBaseSlots.size()) slotInfo = srvBaseSlots[id]; - else if(kind == DXIL::HandleKind::UAV && id < uavBaseSlots.size()) + else if(kind == HandleKind::UAV && id < uavBaseSlots.size()) slotInfo = uavBaseSlots[id]; } else @@ -884,21 +748,21 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, RDCERR("Unexpected number of arguments to createHandleFromBinding"); continue; } - DXIL::Value resBindArg = inst.args[1]; + Constant *resBindArg = cast(inst.args[1]); idxArg = inst.args[2]; - if(resBindArg.type != DXIL::ValueType::Constant) + if(!resBindArg) { RDCERR("Unexpected non-constant argument to createHandleFromBinding"); continue; } - if(resBindArg.constant->members.size() != 4 && !resBindArg.constant->nullconst) + if(resBindArg->getMembers().size() != 4 && !resBindArg->isNULL()) { RDCERR("Unexpected number of members to resBind"); continue; } D3D12FeedbackKey key; - if(resBindArg.constant->nullconst) + if(resBindArg->isNULL()) { key.type = DXBCBytecode::TYPE_RESOURCE; key.bind.bindset = 0; @@ -906,23 +770,22 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, } else { - DXIL::Value regArg = resBindArg.constant->members[0]; - DXIL::Value spaceArg = resBindArg.constant->members[2]; - DXIL::Value kindArg = resBindArg.constant->members[3]; - if(regArg.type != DXIL::ValueType::Constant || - spaceArg.type != DXIL::ValueType::Constant || kindArg.type != DXIL::ValueType::Constant) + Constant *regArg = cast(resBindArg->getMembers()[0]); + Constant *spaceArg = cast(resBindArg->getMembers()[2]); + Constant *kindArg = cast(resBindArg->getMembers()[3]); + if(!regArg || !spaceArg || !kindArg) { RDCERR("Unexpected non-constant argument to createHandleFromBinding"); continue; } - DXIL::HandleKind kind = (DXIL::HandleKind)kindArg.constant->val.u32v[0]; - if(kind != DXIL::HandleKind::SRV && kind != DXIL::HandleKind::UAV) + HandleKind kind = (HandleKind)kindArg->getU32(); + if(kind != HandleKind::SRV && kind != HandleKind::UAV) continue; - key.type = kind == DXIL::HandleKind::UAV ? DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW - : DXBCBytecode::TYPE_RESOURCE; - key.bind.bindset = spaceArg.constant->val.u32v[0]; - key.bind.bind = regArg.constant->val.u32v[0]; + key.type = kind == HandleKind::UAV ? DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW + : DXBCBytecode::TYPE_RESOURCE; + key.bind.bindset = spaceArg->getU32(); + key.bind.bind = regArg->getU32(); } auto it = slots.find(key); @@ -937,86 +800,74 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, if(slotInfo.first == 0) continue; - DXIL::Instruction op; - op.op = DXIL::Operation::Sub; - op.type = i32; - op.args = { + 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 - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, (uint32_t)slotInfo.second))), - }; - - // idx0Based = idx - baseReg - DXIL::Instruction *idx0Based = editor.AddInstruction(f, i, op); - i++; - - op.op = DXIL::Operation::Add; - op.type = i32; - op.args = { - // idx to the createHandle op - DXIL::Value(idx0Based), - // base slot - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, slotInfo.first))), + editor.CreateConstant((uint32_t)slotInfo.second), }; // slotPlusBase = idx0Based + slot - DXIL::Instruction *slotPlusBase = editor.AddInstruction(f, i, op); - i++; - - op.op = DXIL::Operation::Call; - op.type = i32; - op.funcCall = binOp; - op.args = { - // dx.op.binOp.i32 UMin opcode - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 40U))), - // slotPlusBase - DXIL::Value(slotPlusBase), - // max slot - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, maxSlot))), + 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), }; // slotPlusBaseClamped = min(slotPlusBase, maxSlot) - DXIL::Instruction *slotPlusBaseClamped = editor.AddInstruction(f, i, op); - i++; - - op.op = DXIL::Operation::ShiftLeft; - op.type = i32; - op.args = { - DXIL::Value(slotPlusBaseClamped), - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 2U))), + 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), }; // byteOffset = slotPlusBaseClamped << 2 - DXIL::Instruction *byteOffset = editor.AddInstruction(f, i, op); - i++; - - op.op = DXIL::Operation::Call; - op.type = i32; - op.funcCall = atomicBinOp; - op.args = { - // dx.op.atomicBinOp.i32 opcode - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 78U))), - // feedback UAV handle - DXIL::Value(handle), - // operation OR - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 2U))), - // offset - DXIL::Value(byteOffset), - // offset 2 - DXIL::Value(undefi32), - // offset 3 - DXIL::Value(undefi32), - // value - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, magicFeedbackValue))), + Instruction *byteOffset = editor.CreateInstruction(Operation::ShiftLeft); + f->instructions.insert(i++, byteOffset); + byteOffset->type = i32; + byteOffset->args = { + slotPlusBaseClamped, editor.CreateConstant(2U), }; - // don't care about the return value from this - editor.AddInstruction(f, i, op); - i++; + 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; } - else if(inst.op == DXIL::Operation::Call && createHandleFromHeap && - inst.funcCall->name == createHandleFromHeap->name) + else if(inst.op == Operation::Call && createHandleFromHeap && + inst.getFuncCall()->name == createHandleFromHeap->name) { RDCASSERT(isShaderModel6_6OrAbove); if(inst.args.size() != 4) @@ -1024,13 +875,13 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, RDCERR("Unexpected number of arguments to createHandleFromHeap"); continue; } - DXIL::Value isSamplerArg = inst.args[2]; - if(isSamplerArg.type != DXIL::ValueType::Constant) + Constant *isSamplerArg = cast(inst.args[2]); + if(!isSamplerArg) { RDCERR("Unexpected non-constant argument to createHandleFromHeap"); continue; } - bool isSampler = isSamplerArg.constant->val.u32v[0] != 0; + bool isSampler = isSamplerArg->getU32() != 0; D3D12FeedbackKey key = GetDirectHeapAccessKey(); auto it = slots.find(key); @@ -1038,21 +889,20 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, continue; // Look for annotation for the type of this view ( SRV/UAV/CBV/Sampler ) - const DXIL::Instruction *annotateInst = NULL; + const Instruction *annotateInst = NULL; for(size_t nextInstIndex = i + 1; nextInstIndex < f->instructions.size(); nextInstIndex++) { - const DXIL::Instruction &nextInst = f->instructions[nextInstIndex]; - if(nextInst.op == DXIL::Operation::Call && annotateHandle && - nextInst.funcCall->name == annotateHandle->name) + const Instruction &nextInst = *f->instructions[nextInstIndex]; + if(nextInst.op == Operation::Call && annotateHandle && + nextInst.getFuncCall()->name == annotateHandle->name) { if(nextInst.args.size() != 3) { RDCERR("Unexpected number of arguments to annotateHandle"); continue; } - DXIL::Value idxArg = nextInst.args[1]; - DXIL::Value instValue(&inst); - if(idxArg.instruction->resultID == instValue.instruction->resultID) + Value *idxArg = nextInst.args[1]; + if(idxArg->id == inst.id) { annotateInst = &nextInst; break; @@ -1065,97 +915,91 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, continue; } - DXIL::Value resPropArg = annotateInst->args[2]; - if(resPropArg.type != DXIL::ValueType::Constant) + Constant *resPropArg = cast(annotateInst->args[2]); + if(!resPropArg) { RDCERR("Unexpected non-constant argument for dx.types.ResourceProperties"); continue; } - if(resPropArg.constant->members.size() != 2) + if(resPropArg->getMembers().size() != 2) { RDCERR("Unexpected number of arguments to dx.types.ResourceProperties"); continue; } - DXIL::Value resKindArg = resPropArg.constant->members[0]; - if(resKindArg.type != DXIL::ValueType::Constant) + Constant *resKindArg = cast(resPropArg->getMembers()[0]); + if(!resKindArg) { RDCERR("Unexpected non-constant argument for dx.types.ResourceProperties's resource kind"); continue; } - DXIL::HandleKind handleKind = DXIL::HandleKind::SRV; - DXIL::ResourceKind resKind = (DXIL::ResourceKind)(resKindArg.constant->val.u32v[0] & 0xFF); - bool isUav = (resKindArg.constant->val.u32v[0] & (1 << 12)) != 0; - if(resKind == DXIL::ResourceKind::Sampler || resKind == DXIL::ResourceKind::SamplerComparison) + HandleKind handleKind = HandleKind::SRV; + ResourceKind resKind = (ResourceKind)(resKindArg->getU32() & 0xFF); + bool isUav = (resKindArg->getU32() & (1 << 12)) != 0; + if(resKind == ResourceKind::Sampler || resKind == ResourceKind::SamplerComparison) { - handleKind = DXIL::HandleKind::Sampler; + handleKind = HandleKind::Sampler; RDCASSERT(isSampler); RDCASSERT(!isUav); } - else if(resKind == DXIL::ResourceKind::CBuffer) + else if(resKind == ResourceKind::CBuffer) { - handleKind = DXIL::HandleKind::CBuffer; + handleKind = HandleKind::CBuffer; RDCASSERT(!isSampler); RDCASSERT(!isUav); } else if(isUav) { - handleKind = DXIL::HandleKind::UAV; + handleKind = HandleKind::UAV; RDCASSERT(!isSampler); } else { - handleKind = DXIL::HandleKind::SRV; + handleKind = HandleKind::SRV; RDCASSERT(!isSampler); } - DXIL::Value idxArg = inst.args[1]; - DXIL::Instruction op; + Value *idxArg = inst.args[1]; + Instruction op; - op.op = DXIL::Operation::Add; - op.type = i32; - op.args = { - // idx to the createHandleFromHeap op - DXIL::Value(idxArg), - // base slot - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, it->second.Slot()))), - }; // slotPlusBase = idx0Based + slot - DXIL::Instruction *slotPlusBase = editor.AddInstruction(f, i, op); - i++; - - op.op = DXIL::Operation::ShiftLeft; - op.type = i32; - op.args = { - DXIL::Value(slotPlusBase), DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 2U))), + 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()), }; + // byteOffset = slotPlusBase << 2 - DXIL::Instruction *byteOffset = editor.AddInstruction(f, i, op); - i++; + Instruction *byteOffset = editor.CreateInstruction(Operation::ShiftLeft); + f->instructions.insert(i++, byteOffset); + byteOffset->type = i32; + byteOffset->args = { + slotPlusBase, editor.CreateConstant(2U), + }; uint32_t feedbackValue = magicFeedbackValue | (1 << (uint32_t)handleKind); - op.op = DXIL::Operation::Call; - op.type = i32; - op.funcCall = atomicBinOp; - op.args = { + Instruction *atomicOr = editor.CreateInstruction(atomicBinOp); + f->instructions.insert(i++, atomicOr); + atomicOr->args = { // dx.op.atomicBinOp.i32 opcode - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 78U))), + editor.CreateConstant(78U), // feedback UAV handle - DXIL::Value(handle), + handle, // operation OR - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 2U))), + editor.CreateConstant(2U), // offset - DXIL::Value(byteOffset), + byteOffset, // offset 2 - DXIL::Value(undefi32), + undefi32, // offset 3 - DXIL::Value(undefi32), + undefi32, // value - DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, feedbackValue))), + editor.CreateConstant(feedbackValue), }; - - // don't care about the return value from this - editor.AddInstruction(f, i, op); - i++; + atomicOr->type = i32; } } diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp b/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp index d99bb71dc..2f592eded 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp @@ -33,257 +33,123 @@ #define IS_KNOWN(val, KnownID) (decltype(KnownID)(val) == KnownID) +#define BUMP_ALLOC_DEBUG OPTION_OFF + namespace DXIL { using namespace LLVMBC; -void ParseConstant(const LLVMBC::BlockOrRecord &constant, const Type *&curType, - std::function getType, - std::function getPtrType, - std::function getValue, - std::function addConstant) +BumpAllocator::BumpAllocator(size_t totalSize) { - if(IS_KNOWN(constant.id, ConstantsRecord::SETTYPE)) + m_BlockSize = totalSize; +#if DISABLED(BUMP_ALLOC_DEBUG) + cur = base = AllocAlignedBuffer(m_BlockSize); + storage.push_back(base); +#endif +} + +static constexpr uint32_t pattern[4] = {0x10101010U, 0xDADADADAU, 0x46464646U, 0x12345678U}; + +BumpAllocator::~BumpAllocator() +{ + for(byte *b : storage) { - curType = getType(constant.ops[0]); +#if DISABLED(BUMP_ALLOC_DEBUG) + memset(b, 0xfe, m_BlockSize); +#endif + FreeAlignedBuffer(b); } - else if(IS_KNOWN(constant.id, ConstantsRecord::CONST_NULL) || - IS_KNOWN(constant.id, ConstantsRecord::UNDEF)) +} + +void *BumpAllocator::alloc(size_t sz) +{ +#if ENABLED(BUMP_ALLOC_DEBUG) + for(byte *b : storage) { - Constant c; - c.type = curType; - c.nullconst = IS_KNOWN(constant.id, ConstantsRecord::CONST_NULL); - c.undef = IS_KNOWN(constant.id, ConstantsRecord::UNDEF); - addConstant(c); + uint32_t size = *(uint32_t *)b; + // check preceeding pattern + RDCASSERT(memcmp(pattern, b + sizeof(pattern), sizeof(pattern)) == 0); + // check trailing pattern + RDCASSERT(memcmp(pattern, b + sizeof(pattern) * 2 + size, sizeof(pattern)) == 0); } - else if(IS_KNOWN(constant.id, ConstantsRecord::INTEGER)) + + byte *ret = AllocAlignedBuffer(sz + 3 * sizeof(pattern)); + storage.push_back(ret); + + // tight allocation size + uint32_t size = (uint32_t)sz; + memcpy(ret, &size, sizeof(uint32_t)); + // preceeding pattern + memcpy(ret + sizeof(pattern), pattern, sizeof(pattern)); + // trailing pattern + memcpy(ret + sizeof(pattern) * 2 + sz, pattern, sizeof(pattern)); + + return ret + sizeof(pattern) * 2; +#else + // if the current storage can't satisfy this, retire it and make a new one + if(cur + sz > base + m_BlockSize) { - Constant c; - c.type = curType; - c.val.s64v[0] = LLVMBC::BitReader::svbr(constant.ops[0]); - addConstant(c); + cur = base = AllocAlignedBuffer(m_BlockSize); + storage.push_back(base); } - else if(IS_KNOWN(constant.id, ConstantsRecord::FLOAT)) - { - Constant c; - c.type = curType; - memcpy(&c.val.u64v[0], &constant.ops[0], curType->bitWidth / 8); - addConstant(c); - } - else if(IS_KNOWN(constant.id, ConstantsRecord::STRING) || - IS_KNOWN(constant.id, ConstantsRecord::CSTRING)) - { - Constant c; - c.type = curType; - c.str = constant.getString(0); - addConstant(c); - } - else if(IS_KNOWN(constant.id, ConstantsRecord::EVAL_CAST)) - { - Constant c; - c.op = DecodeCast(constant.ops[0]); - c.type = curType; - const Value *v = getValue(constant.ops[2]); - if(v->type == ValueType::Unknown) - { - c.inner = Value(Value::ForwardRef, v); - } - else - { - RDCASSERT(v->GetType() == getType(constant.ops[1])); - c.inner = *v; - } - addConstant(c); - } - else if(IS_KNOWN(constant.id, ConstantsRecord::EVAL_GEP)) - { - Constant c; - c.op = Operation::GetElementPtr; - - size_t idx = 0; - if(constant.ops.size() & 1) - c.type = getType(constant.ops[idx++]); - - for(; idx < constant.ops.size(); idx += 2) - { - const Type *t = getType(constant.ops[idx]); - const Value *v = getValue(constant.ops[idx + 1]); - RDCASSERT(v->type != ValueType::Unknown && v->GetType() == t); - - c.members.push_back(*v); - } - - c.type = c.members[0].GetType()->inner; - - // walk the type list to get the return type - for(idx = 2; idx < c.members.size(); idx++) - { - if(c.type->type == Type::Vector || c.type->type == Type::Array) - { - c.type = c.type->inner; - } - else if(c.type->type == Type::Struct) - { - c.type = c.type->members[c.members[idx].constant->val.u32v[0]]; - } - else - { - RDCERR("Unexpected type %d encountered in GEP", c.type->type); - } - } - - // the result is a pointer to the return type - c.type = getPtrType(c.type, curType->addrSpace); - - addConstant(c); - } - else if(IS_KNOWN(constant.id, ConstantsRecord::AGGREGATE)) - { - Constant c; - c.type = curType; - if(c.type->type == Type::Vector) - { - // only handle 4-wide vectors right now - RDCASSERT(constant.ops.size() <= 4); - - // inline vectors - for(size_t m = 0; m < constant.ops.size(); m++) - { - const Value *member = getValue(constant.ops[m]); - RDCASSERT(member->type != ValueType::Unknown); - - c.members.push_back(*member); - - if(member->type != ValueType::Unknown) - { - if(c.type->bitWidth <= 32) - c.val.u32v[m] = member->constant->val.u32v[m]; - else - c.val.u64v[m] = member->constant->val.u64v[m]; - } - else - { - RDCERR("Forward reference unexpected for vector"); - } - } - } - else - { - for(uint64_t m : constant.ops) - c.members.push_back(Value(Value::ForwardRef, getValue(m))); - } - addConstant(c); - } - else if(IS_KNOWN(constant.id, ConstantsRecord::DATA)) - { - Constant c; - c.type = curType; - c.data = true; - if(c.type->type == Type::Vector) - { - for(size_t m = 0; m < constant.ops.size(); m++) - { - if(c.type->bitWidth <= 32) - c.val.u32v[m] = constant.ops[m] & ((1ULL << c.type->bitWidth) - 1); - else - c.val.u64v[m] = constant.ops[m]; - } - } - else - { - for(size_t m = 0; m < constant.ops.size(); m++) - { - uint64_t val = 0; - if(c.type->inner->bitWidth <= 32) - val = constant.ops[m] & ((1ULL << c.type->inner->bitWidth) - 1); - else - val = constant.ops[m]; - c.members.push_back(Value(val)); - } - } - addConstant(c); - } - else - { - RDCERR("Unknown record ID %u encountered in constants block", constant.id); - } + cur = AlignUpPtr(cur, 16U); + byte *ret = cur; +#if defined(RDOC_RELEASE) + memset(ret, 0, sz); +#else + memset(ret, 0xcc, sz); +#endif + cur += sz; + return ret; +#endif } // helper struct for reading ops struct OpReader { - OpReader(Program *prog, const LLVMBC::BlockOrRecord &op) - : prog(prog), type((FunctionRecord)op.id), values(op.ops), idx(0) + OpReader(Program *prog, ValueList &values, const LLVMBC::BlockOrRecord &op) + : prog(prog), values(values), type((FunctionRecord)op.id), ops(op.ops), idx(0) { } FunctionRecord type; - size_t remaining() { return values.size() - idx; } - Value getSymbol(uint64_t val) { return prog->m_Values[prog->m_Values.size() - (size_t)val]; } - Value getSymbol(bool withType = true) + size_t remaining() { return ops.size() - idx; } + Value *getSymbol(uint64_t val) { return values[values.getRelativeBackwards(val)]; } + Value *getSymbol(bool withType = true) { // get the value uint64_t val = get(); - // if it's not a forward reference, resolve the relative-ness and return - if(val <= prog->m_Values.size()) - { + // non forward reference? return directly + if(val <= values.curValueIndex()) return getSymbol(val); - } - else - { - // sometimes forward references have types, which we store here in case we need the type - // later. - if(withType) - m_LastType = getType(); - // return the forward reference symbol - return Value(Value::ForwardRef, &prog->m_Values.back() + 1 - (int32_t)val); - } + // sometimes forward references have types + Value *v = values.createPlaceholderValue(values.getRelativeForwards(-(int32_t)val)); + if(withType) + v->type = getType(); + + return v; } // some symbols are referenced absolute, not relative - Value getSymbolAbsolute() { return prog->m_Values[get()]; } - const Type *getType() { return &prog->m_Types[get()]; } - const Type *getType(const Function &f, Value v) - { - if(v.type == ValueType::Unknown) - return m_LastType; - return v.GetType(); - } - + Value *getSymbolAbsolute() { return values[get()]; } + const Type *getType() { return prog->m_Types[get()]; } template T get() { - return (T)values[idx++]; + return (T)ops[idx++]; } private: - const rdcarray &values; + const rdcarray &ops; size_t idx; Program *prog; - const Type *m_LastType = NULL; + ValueList &values; }; -const Type *Value::GetType() const -{ - switch(type) - { - case ValueType::Constant: return constant->type; - case ValueType::Instruction: return instruction->type; - case ValueType::GlobalVar: return global->type; - case ValueType::Function: return function->funcType; - case ValueType::Metadata: return meta->type; - case ValueType::Unknown: - case ValueType::Alias: - case ValueType::BasicBlock: - case ValueType::Literal: RDCERR("Unexpected symbol to get type for %d", type); break; - } - return NULL; -} - bool Program::Valid(const byte *bytes, size_t length) { if(length < sizeof(ProgramHeader)) @@ -306,22 +172,158 @@ bool Program::Valid(const byte *bytes, size_t length) const Metadata *Program::GetMetadataByName(const rdcstr &name) const { for(size_t i = 0; i < m_NamedMeta.size(); i++) - if(m_NamedMeta[i].name == name) - return &m_NamedMeta[i]; + if(m_NamedMeta[i]->name == name) + return m_NamedMeta[i]; return NULL; } -void ResolveForwardReference(Value &v) +void Program::ParseConstant(ValueList &values, const LLVMBC::BlockOrRecord &constant) { - if(!v.empty() && v.type == ValueType::Unknown) + if(IS_KNOWN(constant.id, ConstantsRecord::SETTYPE)) { - v = *v.value; - RDCASSERT(v.type == ValueType::Constant || v.type == ValueType::Literal); + m_CurParseType = m_Types[constant.ops[0]]; + } + else if(IS_KNOWN(constant.id, ConstantsRecord::CONST_NULL) || + IS_KNOWN(constant.id, ConstantsRecord::UNDEF)) + { + Constant *c = values.nextValue(); + c->type = m_CurParseType; + c->setNULL(IS_KNOWN(constant.id, ConstantsRecord::CONST_NULL)); + c->setUndef(IS_KNOWN(constant.id, ConstantsRecord::UNDEF)); + values.addValue(); + } + else if(IS_KNOWN(constant.id, ConstantsRecord::INTEGER)) + { + Constant *c = values.nextValue(); + c->type = m_CurParseType; + c->setValue(LLVMBC::BitReader::svbr(constant.ops[0])); + values.addValue(); + } + else if(IS_KNOWN(constant.id, ConstantsRecord::FLOAT)) + { + Constant *c = values.nextValue(); + c->type = m_CurParseType; + uint64_t uval = 0; + memcpy(&uval, &constant.ops[0], m_CurParseType->bitWidth / 8); + c->setValue(uval); + values.addValue(); + } + else if(IS_KNOWN(constant.id, ConstantsRecord::STRING) || + IS_KNOWN(constant.id, ConstantsRecord::CSTRING)) + { + Constant *c = values.nextValue(); + c->type = m_CurParseType; + c->str = constant.getString(0); + values.addValue(); + } + else if(IS_KNOWN(constant.id, ConstantsRecord::EVAL_CAST)) + { + Constant *c = values.nextValue(); + c->op = DecodeCast(constant.ops[0]); + c->type = m_CurParseType; + c->setInner(values.getOrCreatePlaceholder(constant.ops[2])); + values.addValue(); + } + else if(IS_KNOWN(constant.id, ConstantsRecord::EVAL_GEP)) + { + Constant *c = values.nextValue(); + + c->op = Operation::GetElementPtr; + + size_t idx = 0; + if(constant.ops.size() & 1) + c->type = m_Types[constant.ops[idx++]]; + + rdcarray members; + + for(; idx < constant.ops.size(); idx += 2) + { + const Type *t = m_Types[constant.ops[idx]]; + Value *v = values[constant.ops[idx + 1]]; + RDCASSERT(v->type == t); + + members.push_back(v); + } + + c->type = members[0]->type->inner; + + // walk the type list to get the return type + for(idx = 2; idx < members.size(); idx++) + { + if(c->type->type == Type::Vector || c->type->type == Type::Array) + { + c->type = c->type->inner; + } + else if(c->type->type == Type::Struct) + { + c->type = c->type->members[cast(members[idx])->getU32()]; + } + else + { + RDCERR("Unexpected type %d encountered in GEP", c->type->type); + } + } + + c->setCompound(alloc, std::move(members)); + + // the result is a pointer to the return type + c->type = GetPointerType(c->type, m_CurParseType->addrSpace); + + values.addValue(); + } + else if(IS_KNOWN(constant.id, ConstantsRecord::AGGREGATE)) + { + Constant *c = values.nextValue(); + c->type = m_CurParseType; + rdcarray members; + for(uint64_t m : constant.ops) + members.push_back(values.getOrCreatePlaceholder(m)); + c->setCompound(alloc, std::move(members)); + values.addValue(); + } + else if(IS_KNOWN(constant.id, ConstantsRecord::DATA)) + { + Constant *c = values.nextValue(); + c->type = m_CurParseType; + c->setData(true); + + if(c->type->type == Type::Vector) + { + ShaderValue val; + for(size_t m = 0; m < constant.ops.size(); m++) + { + if(c->type->bitWidth <= 32) + val.u32v[m] = constant.ops[m] & ((1ULL << c->type->bitWidth) - 1); + else + val.u64v[m] = constant.ops[m]; + } + c->setValue(alloc, val); + } + else + { + rdcarray members; + for(size_t m = 0; m < constant.ops.size(); m++) + { + uint64_t val = 0; + if(c->type->inner->bitWidth <= 32) + val = constant.ops[m] & ((1ULL << c->type->inner->bitWidth) - 1); + else + val = constant.ops[m]; + members.push_back(new(alloc) Literal(val)); + } + c->setCompound(alloc, std::move(members)); + } + + values.addValue(); + } + else + { + RDCERR("Unknown record ID %u encountered in constants block", constant.id); } } -Program::Program(const byte *bytes, size_t length) +Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024) { const byte *ptr = bytes; const ProgramHeader *header = (const ProgramHeader *)ptr; @@ -347,6 +349,9 @@ Program::Program(const byte *bytes, size_t length) m_Minor = header->ProgramVersion & 0xf; m_DXILVersion = header->DxilVersion; + ValueList values(alloc); + MetadataList metadata(alloc); + // Input signature and Output signature haven't changed. // Pipeline Runtime Information we have decoded just not implemented here @@ -384,38 +389,38 @@ Program::Program(const byte *bytes, size_t length) { // [pointer type, isconst, initid, linkage, alignment, section, visibility, threadlocal, // unnamed_addr, externally_initialized, dllstorageclass, comdat] - GlobalVar g; + GlobalVar *g = values.nextValue(); - g.type = &m_Types[(size_t)rootchild.ops[0]]; + g->type = m_Types[(size_t)rootchild.ops[0]]; if(rootchild.ops[1] & 0x1) - g.flags |= GlobalFlags::IsConst; + g->flags |= GlobalFlags::IsConst; - Type::PointerAddrSpace addrSpace = g.type->addrSpace; + Type::PointerAddrSpace addrSpace = g->type->addrSpace; if(rootchild.ops[1] & 0x2) addrSpace = Type::PointerAddrSpace(rootchild.ops[1] >> 2); if(rootchild.ops[2]) - g.initialiser += rootchild.ops[2]; + g->initialiser += rootchild.ops[2]; switch(rootchild.ops[3]) { - case 0: g.flags |= GlobalFlags::ExternalLinkage; break; - case 16: g.flags |= GlobalFlags::WeakAnyLinkage; break; - case 2: g.flags |= GlobalFlags::AppendingLinkage; break; - case 3: g.flags |= GlobalFlags::InternalLinkage; break; - case 18: g.flags |= GlobalFlags::LinkOnceAnyLinkage; break; - case 7: g.flags |= GlobalFlags::ExternalWeakLinkage; break; - case 8: g.flags |= GlobalFlags::CommonLinkage; break; - case 9: g.flags |= GlobalFlags::PrivateLinkage; break; - case 17: g.flags |= GlobalFlags::WeakODRLinkage; break; - case 19: g.flags |= GlobalFlags::LinkOnceODRLinkage; break; - case 12: g.flags |= GlobalFlags::AvailableExternallyLinkage; break; + case 0: g->flags |= GlobalFlags::ExternalLinkage; break; + case 16: g->flags |= GlobalFlags::WeakAnyLinkage; break; + case 2: g->flags |= GlobalFlags::AppendingLinkage; break; + case 3: g->flags |= GlobalFlags::InternalLinkage; break; + case 18: g->flags |= GlobalFlags::LinkOnceAnyLinkage; break; + case 7: g->flags |= GlobalFlags::ExternalWeakLinkage; break; + case 8: g->flags |= GlobalFlags::CommonLinkage; break; + case 9: g->flags |= GlobalFlags::PrivateLinkage; break; + case 17: g->flags |= GlobalFlags::WeakODRLinkage; break; + case 19: g->flags |= GlobalFlags::LinkOnceODRLinkage; break; + case 12: g->flags |= GlobalFlags::AvailableExternallyLinkage; break; default: break; } - g.align = (1ULL << rootchild.ops[4]) >> 1; + g->align = (1ULL << rootchild.ops[4]) >> 1; - g.section = int32_t(rootchild.ops[5]) - 1; + g->section = int32_t(rootchild.ops[5]) - 1; if(rootchild.ops.size() > 6) { @@ -430,15 +435,15 @@ Program::Program(const byte *bytes, size_t length) if(rootchild.ops.size() > 8) { if(rootchild.ops[8] == 1) - g.flags |= GlobalFlags::GlobalUnnamedAddr; + g->flags |= GlobalFlags::GlobalUnnamedAddr; else if(rootchild.ops[8] == 2) - g.flags |= GlobalFlags::LocalUnnamedAddr; + g->flags |= GlobalFlags::LocalUnnamedAddr; } if(rootchild.ops.size() > 9) { if(rootchild.ops[9]) - g.flags |= GlobalFlags::ExternallyInitialised; + g->flags |= GlobalFlags::ExternallyInitialised; } if(rootchild.ops.size() > 10) @@ -452,57 +457,57 @@ Program::Program(const byte *bytes, size_t length) RDCASSERTMSG("global has comdat", rootchild.ops[11] == 0); } - const Type *ptrType = GetPointerType(g.type, addrSpace); + const Type *ptrType = GetPointerType(g->type, addrSpace); - if(ptrType == g.type) + if(ptrType == g->type) RDCERR("Expected to find pointer type for global variable"); - g.type = ptrType; + g->type = ptrType; m_GlobalVars.push_back(g); - m_Values.push_back(Value(&m_GlobalVars.back())); + values.addValue(); } else if(IS_KNOWN(rootchild.id, ModuleRecord::FUNCTION)) { // [type, callingconv, isproto, linkage, paramattrs, alignment, section, visibility, gc, // unnamed_addr, prologuedata, dllstorageclass, comdat, prefixdata] - Function f; + Function *f = new(alloc) Function; - f.funcType = &m_Types[(size_t)rootchild.ops[0]]; + f->type = m_Types[(size_t)rootchild.ops[0]]; // ignore callingconv RDCASSERTMSG("Calling convention is non-default", rootchild.ops[1] == 0); - f.external = (rootchild.ops[2] != 0); + f->external = (rootchild.ops[2] != 0); // ignore linkage RDCASSERTMSG("Linkage is non-default", rootchild.ops[3] == 0); if(rootchild.ops[4] > 0 && rootchild.ops[4] - 1 < m_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]; + f->align = rootchild.ops[5]; // ignore rest of properties, assert that if present they are 0 for(size_t p = 6; p < rootchild.ops.size(); p++) RDCASSERT(rootchild.ops[p] == 0, p, rootchild.ops[p]); - if(!f.external) + if(!f->external) functionDecls.push_back(m_Functions.size()); m_Functions.push_back(f); - m_Values.push_back(Value(&m_Functions.back())); + values.addValue(f); } else if(IS_KNOWN(rootchild.id, ModuleRecord::ALIAS)) { // [alias type, aliasee val#, linkage, visibility] - Alias a; + Alias *a = values.nextValue(); - a.type = &m_Types[(size_t)rootchild.ops[0]]; - a.val = m_Values[(size_t)rootchild.ops[1]]; + a->type = m_Types[(size_t)rootchild.ops[0]]; + a->val = values[(size_t)rootchild.ops[1]]; // ignore rest of properties, assert that if present they are 0 for(size_t p = 2; p < rootchild.ops.size(); p++) RDCASSERT(rootchild.ops[p] == 0, p, rootchild.ops[p]); m_Aliases.push_back(a); - m_Values.push_back(Value(&m_Aliases.back())); + values.addValue(); } else if(IS_KNOWN(rootchild.id, ModuleRecord::SECTIONNAME)) { @@ -646,10 +651,9 @@ Program::Program(const byte *bytes, size_t length) if(!rootchild.children.empty() && !IS_KNOWN(rootchild.children[0].id, TypeRecord::NUMENTRY)) { RDCWARN("No NUMENTRY record, resizing conservatively to number of records"); - m_Types.resize(rootchild.children.size()); + m_Types.reserve(rootchild.children.size()); } - size_t typeIndex = 0; for(const LLVMBC::BlockOrRecord &typ : rootchild.children) { if(typ.IsBlock()) @@ -661,94 +665,114 @@ Program::Program(const byte *bytes, size_t length) if(IS_KNOWN(typ.id, TypeRecord::NUMENTRY)) { RDCASSERT(m_Types.size() < (size_t)typ.ops[0], m_Types.size(), typ.ops[0]); - m_Types.resize((size_t)typ.ops[0]); + m_Types.reserve((size_t)typ.ops[0]); } else if(IS_KNOWN(typ.id, TypeRecord::VOID)) { - m_Types[typeIndex].type = Type::Scalar; - m_Types[typeIndex].scalarType = Type::Void; + Type *newType = new(alloc) Type; + newType->type = Type::Scalar; + newType->scalarType = Type::Void; - typeIndex++; + m_Types.push_back(newType); + m_VoidType = newType; } else if(IS_KNOWN(typ.id, TypeRecord::LABEL)) { - m_Types[typeIndex].type = Type::Label; + Type *newType = new(alloc) Type; + newType->type = Type::Label; - typeIndex++; + m_Types.push_back(newType); + m_LabelType = newType; } else if(IS_KNOWN(typ.id, TypeRecord::METADATA)) { - m_Types[typeIndex].type = Type::Metadata; + Type *newType = new(alloc) Type; + newType->type = Type::Metadata; - typeIndex++; + m_Types.push_back(newType); + m_MetaType = newType; } else if(IS_KNOWN(typ.id, TypeRecord::HALF)) { - m_Types[typeIndex].type = Type::Scalar; - m_Types[typeIndex].scalarType = Type::Float; - m_Types[typeIndex].bitWidth = 16; + Type *newType = new(alloc) Type; + newType->type = Type::Scalar; + newType->scalarType = Type::Float; + newType->bitWidth = 16; - typeIndex++; + m_Types.push_back(newType); } else if(IS_KNOWN(typ.id, TypeRecord::FLOAT)) { - m_Types[typeIndex].type = Type::Scalar; - m_Types[typeIndex].scalarType = Type::Float; - m_Types[typeIndex].bitWidth = 32; + Type *newType = new(alloc) Type; + newType->type = Type::Scalar; + newType->scalarType = Type::Float; + newType->bitWidth = 32; - typeIndex++; + m_Types.push_back(newType); } else if(IS_KNOWN(typ.id, TypeRecord::DOUBLE)) { - m_Types[typeIndex].type = Type::Scalar; - m_Types[typeIndex].scalarType = Type::Float; - m_Types[typeIndex].bitWidth = 64; + Type *newType = new(alloc) Type; + newType->type = Type::Scalar; + newType->scalarType = Type::Float; + newType->bitWidth = 64; - typeIndex++; + m_Types.push_back(newType); } else if(IS_KNOWN(typ.id, TypeRecord::INTEGER)) { - m_Types[typeIndex].type = Type::Scalar; - m_Types[typeIndex].scalarType = Type::Int; - m_Types[typeIndex].bitWidth = typ.ops[0] & 0xffffffff; + Type *newType = new(alloc) Type; + newType->type = Type::Scalar; + newType->scalarType = Type::Int; + newType->bitWidth = typ.ops[0] & 0xffffffff; - typeIndex++; + m_Types.push_back(newType); + if(newType->bitWidth == 1) + m_BoolType = newType; + else if(newType->bitWidth == 8) + m_Int8Type = newType; + else if(newType->bitWidth == 32) + m_Int32Type = newType; } else if(IS_KNOWN(typ.id, TypeRecord::VECTOR)) { - m_Types[typeIndex].type = Type::Vector; - m_Types[typeIndex].elemCount = typ.ops[0] & 0xffffffff; - m_Types[typeIndex].inner = &m_Types[(size_t)typ.ops[1]]; + Type *newType = new(alloc) Type; + newType->type = Type::Vector; + newType->elemCount = typ.ops[0] & 0xffffffff; + newType->inner = m_Types[(size_t)typ.ops[1]]; // copy properties out of the inner for convenience - m_Types[typeIndex].scalarType = m_Types[typeIndex].inner->scalarType; - m_Types[typeIndex].bitWidth = m_Types[typeIndex].inner->bitWidth; + newType->scalarType = newType->inner->scalarType; + newType->bitWidth = newType->inner->bitWidth; - typeIndex++; + m_Types.push_back(newType); } else if(IS_KNOWN(typ.id, TypeRecord::ARRAY)) { - m_Types[typeIndex].type = Type::Array; - m_Types[typeIndex].elemCount = typ.ops[0] & 0xffffffff; - m_Types[typeIndex].inner = &m_Types[(size_t)typ.ops[1]]; + Type *newType = new(alloc) Type; + newType->type = Type::Array; + newType->elemCount = typ.ops[0] & 0xffffffff; + newType->inner = m_Types[(size_t)typ.ops[1]]; - typeIndex++; + m_Types.push_back(newType); } else if(IS_KNOWN(typ.id, TypeRecord::POINTER)) { - m_Types[typeIndex].type = Type::Pointer; - m_Types[typeIndex].inner = &m_Types[(size_t)typ.ops[0]]; - m_Types[typeIndex].addrSpace = Type::PointerAddrSpace(typ.ops[1]); + Type *newType = new(alloc) Type; + newType->type = Type::Pointer; + newType->inner = m_Types[(size_t)typ.ops[0]]; + newType->addrSpace = Type::PointerAddrSpace(typ.ops[1]); - typeIndex++; + m_Types.push_back(newType); } else if(IS_KNOWN(typ.id, TypeRecord::OPAQUE)) { + Type *newType = new(alloc) Type; // pretend opaque types are empty structs - m_Types[typeIndex].type = Type::Struct; - m_Types[typeIndex].opaque = true; + newType->type = Type::Struct; + newType->opaque = true; - typeIndex++; + m_Types.push_back(newType); } else if(IS_KNOWN(typ.id, TypeRecord::STRUCT_NAME)) { @@ -757,28 +781,30 @@ Program::Program(const byte *bytes, size_t length) else if(IS_KNOWN(typ.id, TypeRecord::STRUCT_ANON) || IS_KNOWN(typ.id, TypeRecord::STRUCT_NAMED)) { - m_Types[typeIndex].type = Type::Struct; - m_Types[typeIndex].packedStruct = (typ.ops[0] != 0); + Type *newType = new(alloc) Type; + newType->type = Type::Struct; + newType->packedStruct = (typ.ops[0] != 0); for(size_t o = 1; o < typ.ops.size(); o++) - m_Types[typeIndex].members.push_back(&m_Types[(size_t)typ.ops[o]]); + newType->members.push_back(m_Types[(size_t)typ.ops[o]]); if(IS_KNOWN(typ.id, TypeRecord::STRUCT_NAMED)) { // may we want a reverse map name -> type? probably not, this is only relevant for // disassembly or linking and disassembly we can do just by iterating all types - m_Types[typeIndex].name = structname; + newType->name = structname; structname.clear(); } - typeIndex++; + m_Types.push_back(newType); } else if(IS_KNOWN(typ.id, TypeRecord::FUNCTION_OLD) || IS_KNOWN(typ.id, TypeRecord::FUNCTION)) { - m_Types[typeIndex].type = Type::Function; + Type *newType = new(alloc) Type; + newType->type = Type::Function; - m_Types[typeIndex].vararg = (typ.ops[0] != 0); + newType->vararg = (typ.ops[0] != 0); size_t o = 1; @@ -787,13 +813,13 @@ Program::Program(const byte *bytes, size_t length) o++; // return type - m_Types[typeIndex].inner = &m_Types[(size_t)typ.ops[o]]; + newType->inner = m_Types[(size_t)typ.ops[o]]; o++; for(; o < typ.ops.size(); o++) - m_Types[typeIndex].members.push_back(&m_Types[(size_t)typ.ops[o]]); + newType->members.push_back(m_Types[(size_t)typ.ops[o]]); - typeIndex++; + m_Types.push_back(newType); } else { @@ -803,16 +829,9 @@ Program::Program(const byte *bytes, size_t length) } else if(IS_KNOWN(rootchild.id, KnownBlock::CONSTANTS_BLOCK)) { - const Type *t = NULL; + m_CurParseType = NULL; - // resize then clear to ensure the constants array memory that we reserve is cleared to 0 - m_Constants.resize(rootchild.children.size()); - m_Constants.clear(); - - // ensure forward references stay valid until we resolve them after the loop - size_t sz = m_Values.size(); - m_Values.resize(sz + rootchild.children.size()); - m_Values.resize(sz); + values.hintExpansion(rootchild.children.size()); for(const LLVMBC::BlockOrRecord &constant : rootchild.children) { @@ -822,22 +841,7 @@ Program::Program(const byte *bytes, size_t length) continue; } - ParseConstant(constant, t, [this](uint64_t op) { return &m_Types[(size_t)op]; }, - [this](const Type *t, Type::PointerAddrSpace addrSpace) { - return GetPointerType(t, addrSpace); - }, - [this](uint64_t v) { return &m_Values[(size_t)v]; }, - [this](const Constant &v) { - m_Constants.push_back(v); - m_Values.push_back(Value(&m_Constants.back())); - }); - } - - for(size_t i = 0; i < m_Constants.size(); i++) - { - for(Value &v : m_Constants[i].members) - ResolveForwardReference(v); - ResolveForwardReference(m_Constants[i].inner); + ParseConstant(values, constant); } } else if(IS_KNOWN(rootchild.id, KnownBlock::VALUE_SYMTAB_BLOCK)) @@ -857,12 +861,12 @@ Program::Program(const byte *bytes, size_t length) } size_t vidx = (size_t)symtab.ops[0]; - if(vidx < m_Values.size()) + if(vidx < values.curValueIndex()) { - const Value &v = m_Values[vidx]; + Value *v = values[vidx]; rdcstr str = symtab.getString(1); - GetValueSymtabString(v) = str; + SetValueSymtabString(v, str); if(!m_ValueSymtabOrder.empty()) m_SortedSymtab &= GetValueSymtabString(m_ValueSymtabOrder.back()) < str; @@ -877,7 +881,7 @@ Program::Program(const byte *bytes, size_t length) } else if(IS_KNOWN(rootchild.id, KnownBlock::METADATA_BLOCK)) { - m_Metadata.reserve(rootchild.children.size()); + metadata.hintExpansion(rootchild.children.size()); for(size_t i = 0; i < rootchild.children.size(); i++) { const LLVMBC::BlockOrRecord &metaRecord = rootchild.children[i]; @@ -890,15 +894,15 @@ Program::Program(const byte *bytes, size_t length) if(IS_KNOWN(metaRecord.id, MetaDataRecord::NAME)) { - NamedMetadata meta; + NamedMetadata *meta = new(alloc) NamedMetadata; - meta.name = metaRecord.getString(); + meta->name = metaRecord.getString(); i++; const LLVMBC::BlockOrRecord &namedNode = rootchild.children[i]; RDCASSERT(IS_KNOWN(namedNode.id, MetaDataRecord::NAMED_NODE)); for(uint64_t op : namedNode.ops) - meta.children.push_back(&m_Metadata[(size_t)op]); + meta->children.push_back(metadata[(size_t)op]); m_NamedMeta.push_back(meta); } @@ -911,40 +915,32 @@ Program::Program(const byte *bytes, size_t length) } else { - m_Metadata.resize_for_index(i); - Metadata &meta = m_Metadata[i]; - - auto getMetaOrNull = [this](uint64_t id) { - return id ? &m_Metadata[size_t(id - 1)] : NULL; - }; - auto getMetaStringOrNull = [this](uint64_t id) { - return id ? &m_Metadata[size_t(id - 1)].str : NULL; - }; + Metadata *meta = metadata[i]; if(IS_KNOWN(metaRecord.id, MetaDataRecord::STRING_OLD)) { - meta.isConstant = true; - meta.isString = true; - meta.str = metaRecord.getString(); + meta->isConstant = true; + meta->isString = true; + meta->str = metaRecord.getString(); } else if(IS_KNOWN(metaRecord.id, MetaDataRecord::VALUE)) { - meta.isConstant = true; - meta.value = m_Values[(size_t)metaRecord.ops[1]]; - meta.type = &m_Types[(size_t)metaRecord.ops[0]]; + meta->value = values[(size_t)metaRecord.ops[1]]; + meta->type = m_Types[(size_t)metaRecord.ops[0]]; + meta->isConstant = true; } else if(IS_KNOWN(metaRecord.id, MetaDataRecord::NODE) || IS_KNOWN(metaRecord.id, MetaDataRecord::DISTINCT_NODE)) { if(IS_KNOWN(metaRecord.id, MetaDataRecord::DISTINCT_NODE)) - meta.isDistinct = true; + meta->isDistinct = true; for(uint64_t op : metaRecord.ops) - meta.children.push_back(getMetaOrNull(op)); + meta->children.push_back(metadata.getOrNULL(op)); } else { - bool parsed = ParseDebugMetaRecord(metaRecord, meta); + bool parsed = ParseDebugMetaRecord(metadata, metaRecord, *meta); if(!parsed) { RDCERR("unhandled metadata type %u", metaRecord.id); @@ -955,45 +951,29 @@ Program::Program(const byte *bytes, size_t length) } else if(IS_KNOWN(rootchild.id, KnownBlock::FUNCTION_BLOCK)) { - Function &f = m_Functions[functionDecls[0]]; + Function *f = m_Functions[functionDecls[0]]; functionDecls.erase(0); // conservative resize here so we can take pointers and have them stay valid - f.instructions.reserve(rootchild.children.size()); + f->instructions.reserve(rootchild.children.size()); - auto getMeta = [this, &f](uint64_t v) { - size_t idx = (size_t)v; - return idx - 1 < m_Metadata.size() ? &m_Metadata[idx] : &f.metadata[idx]; - }; - auto getMetaOrNull = [this, &f](uint64_t v) { - size_t idx = (size_t)v; - return idx == 0 ? NULL : (idx - 1 < m_Metadata.size() ? &m_Metadata[idx - 1] - : &f.metadata[idx - 1]); - }; + values.beginFunction(); + metadata.beginFunction(); - size_t prevNumSymbols = m_Values.size(); - size_t instrSymbolStart = 0; - - f.args.reserve(f.funcType->members.size()); - for(size_t i = 0; i < f.funcType->members.size(); i++) + f->args.reserve(f->type->members.size()); + for(size_t i = 0; i < f->type->members.size(); i++) { - Instruction arg; - arg.type = f.funcType->members[i]; - arg.name = StringFormat::Fmt("arg%zu", i); - f.args.push_back(arg); - m_Values.push_back(Value(&f.args.back())); + Instruction *arg = values.nextValue(); + arg->type = f->type->members[i]; + arg->extra(alloc).name = StringFormat::Fmt("arg%zu", i); + f->args.push_back(arg); + values.addValue(); } size_t curBlock = 0; int32_t debugLocIndex = -1; - // reserve enough values for the instructions (conservatively) - { - size_t sz = m_Values.size(); - m_Values.resize(sz + rootchild.children.size()); - m_Values.resize(sz); - } - const Value *valueStorage = m_Values.data(); + values.hintExpansion(rootchild.children.size()); for(const LLVMBC::BlockOrRecord &funcChild : rootchild.children) { @@ -1001,21 +981,9 @@ Program::Program(const byte *bytes, size_t length) { if(IS_KNOWN(funcChild.id, KnownBlock::CONSTANTS_BLOCK)) { - // resize then clear to ensure the constants array memory that we reserve is cleared - // to 0 - f.constants.resize(funcChild.children.size()); - f.constants.clear(); + values.hintExpansion(funcChild.children.size()); - // reserve enough values for constants and instructions. We should encounter this - // before anything that can forward reference values - { - size_t sz = m_Values.size(); - m_Values.resize(sz + rootchild.children.size() + funcChild.children.size()); - m_Values.resize(sz); - } - valueStorage = m_Values.data(); - - const Type *t = NULL; + m_CurParseType = NULL; for(const LLVMBC::BlockOrRecord &constant : funcChild.children) { if(constant.IsBlock()) @@ -1024,31 +992,14 @@ Program::Program(const byte *bytes, size_t length) continue; } - ParseConstant(constant, t, [this](uint64_t op) { return &m_Types[(size_t)op]; }, - [this](const Type *t, Type::PointerAddrSpace addrSpace) { - return GetPointerType(t, addrSpace); - }, - [this](uint64_t v) { return &m_Values[(size_t)v]; }, - [this, &f](const Constant &v) { - f.constants.push_back(v); - m_Values.push_back(Value(&f.constants.back())); - }); + ParseConstant(values, constant); } - - for(size_t i = 0; i < f.constants.size(); i++) - { - for(Value &v : f.constants[i].members) - ResolveForwardReference(v); - ResolveForwardReference(f.constants[i].inner); - } - - instrSymbolStart = m_Values.size(); } else if(IS_KNOWN(funcChild.id, KnownBlock::METADATA_BLOCK)) { - f.metadata.resize(funcChild.children.size()); + metadata.hintExpansion(funcChild.children.size()); - size_t m = 0; + size_t m = metadata.size(); for(const LLVMBC::BlockOrRecord &metaRecord : funcChild.children) { @@ -1058,22 +1009,13 @@ Program::Program(const byte *bytes, size_t length) continue; } - Metadata &meta = f.metadata[m]; + Metadata *meta = metadata[m]; if(IS_KNOWN(metaRecord.id, MetaDataRecord::VALUE)) { - meta.isConstant = true; - size_t idx = (size_t)metaRecord.ops[1]; - if(idx < m_Values.size()) - { - meta.value = m_Values[idx]; - } - else - { - // forward reference - meta.value = Value(Value::ForwardRef, &m_Values[idx]); - } - meta.type = &m_Types[(size_t)metaRecord.ops[0]]; + meta->isConstant = true; + meta->value = values.getOrCreatePlaceholder((size_t)metaRecord.ops[1]); + meta->type = m_Types[(size_t)metaRecord.ops[0]]; } else { @@ -1097,34 +1039,34 @@ Program::Program(const byte *bytes, size_t length) { size_t idx = (size_t)symtab.ops[0]; - if(idx >= m_Values.size()) + if(idx >= values.curValueIndex()) { RDCERR("Out of bounds symbol index %zu (%s) in function symbol table", idx, symtab.getString(1).c_str()); continue; } - const Value &v = m_Values[idx]; + Value *v = values[idx]; rdcstr str = symtab.getString(1); - GetValueSymtabString(v) = str; + SetValueSymtabString(v, str); - if(!f.valueSymtabOrder.empty()) - f.sortedSymtab &= GetValueSymtabString(f.valueSymtabOrder.back()) < str; + if(!f->valueSymtabOrder.empty()) + f->sortedSymtab &= GetValueSymtabString(f->valueSymtabOrder.back()) < str; - f.valueSymtabOrder.push_back(v); + f->valueSymtabOrder.push_back(v); } else if(IS_KNOWN(symtab.id, ValueSymtabRecord::BBENTRY)) { - Value v(&f.blocks[(size_t)symtab.ops[0]]); + Value *v = f->blocks[(size_t)symtab.ops[0]]; rdcstr str = symtab.getString(1); - GetValueSymtabString(v) = str; + SetValueSymtabString(v, str); - if(!f.valueSymtabOrder.empty()) - f.sortedSymtab &= GetValueSymtabString(f.valueSymtabOrder.back()) < str; + if(!f->valueSymtabOrder.empty()) + f->sortedSymtab &= GetValueSymtabString(f->valueSymtabOrder.back()) < str; - f.valueSymtabOrder.push_back(v); + f->valueSymtabOrder.push_back(v); } else { @@ -1151,22 +1093,23 @@ Program::Program(const byte *bytes, size_t length) size_t idx = 0; - rdcarray> attach; + AttachedMetadata attach; if(meta.ops.size() % 2 != 0) idx++; for(; idx < meta.ops.size(); idx += 2) - attach.push_back(make_rdcpair(meta.ops[idx], getMeta(meta.ops[idx + 1]))); + attach.push_back(make_rdcpair(meta.ops[idx], metadata.getDirect(meta.ops[idx + 1]))); if(meta.ops.size() % 2 == 0) - f.attachedMeta.swap(attach); + f->attachedMeta.swap(attach); else - f.instructions[(size_t)meta.ops[0]].attachedMeta.swap(attach); + f->instructions[(size_t)meta.ops[0]]->extra(alloc).attachedMeta.swap(attach); } } else if(IS_KNOWN(funcChild.id, KnownBlock::USELIST_BLOCK)) { + m_Uselists = true; for(const LLVMBC::BlockOrRecord &uselist : funcChild.children) { if(uselist.IsBlock()) @@ -1181,9 +1124,9 @@ Program::Program(const byte *bytes, size_t length) UselistEntry u; u.block = bb; u.shuffle = uselist.ops; - u.value = m_Values[(size_t)u.shuffle.back()]; + u.value = values[(size_t)u.shuffle.back()]; u.shuffle.pop_back(); - f.uselist.push_back(u); + f->uselist.push_back(u); } else { @@ -1200,11 +1143,13 @@ Program::Program(const byte *bytes, size_t length) } else { - OpReader op(this, funcChild); + OpReader op(this, values, funcChild); if(op.type == FunctionRecord::DECLAREBLOCKS) { - f.blocks.resize(op.get()); + f->blocks.resize(op.get()); + for(size_t b = 0; b < f->blocks.size(); b++) + f->blocks[b] = new(alloc) Block(m_LabelType); curBlock = 0; } @@ -1213,8 +1158,8 @@ Program::Program(const byte *bytes, size_t length) DebugLocation debugLoc; debugLoc.line = op.get(); debugLoc.col = op.get(); - debugLoc.scope = getMetaOrNull(op.get()); - debugLoc.inlinedAt = getMetaOrNull(op.get()); + debugLoc.scope = metadata.getOrNULL(op.get()); + debugLoc.inlinedAt = metadata.getOrNULL(op.get()); debugLocIndex = m_DebugLocations.indexOf(debugLoc); @@ -1224,26 +1169,24 @@ Program::Program(const byte *bytes, size_t length) debugLocIndex = int32_t(m_DebugLocations.size() - 1); } - f.instructions.back().debugLoc = (uint32_t)debugLocIndex; + f->instructions.back()->debugLoc = (uint32_t)debugLocIndex; } else if(op.type == FunctionRecord::DEBUG_LOC_AGAIN) { - f.instructions.back().debugLoc = (uint32_t)debugLocIndex; + f->instructions.back()->debugLoc = (uint32_t)debugLocIndex; } else if(op.type == FunctionRecord::INST_CALL) { - Instruction inst; - inst.op = Operation::Call; - size_t attr = op.get(); - if(attr > 0) - inst.paramAttrs = &m_AttributeSets[attr - 1]; + size_t paramAttrs = op.get(); uint64_t callingFlags = op.get(); + InstructionFlags flags = InstructionFlags::NoFlags; + if(callingFlags & (1ULL << 17)) { - inst.opFlags = op.get(); - RDCASSERT(inst.opFlags != InstructionFlags::NoFlags); + flags = op.get(); + RDCASSERT(flags != InstructionFlags::NoFlags); callingFlags &= ~(1ULL << 17); } @@ -1260,136 +1203,147 @@ Program::Program(const byte *bytes, size_t length) RDCASSERTMSG("Calling flags should only have at most two known bits set", callingFlags == 0, callingFlags); - Value v = op.getSymbol(); + Function *funcCall = cast(op.getSymbol()); - if(v.type != ValueType::Function) + if(!funcCall) { - RDCERR("Unexpected symbol type %d called in INST_CALL", v.type); + RDCERR("Unexpected symbol type called in INST_CALL"); continue; } - inst.funcCall = v.function; - inst.type = inst.funcCall->funcType->inner; + Instruction *inst = NULL; + + bool voidCall = funcCall->type->inner->isVoid(); + + if(!voidCall) + inst = values.nextValue(); + else + inst = new(alloc) Instruction(); + + inst->op = Operation::Call; + inst->extra(alloc).funcCall = funcCall; + inst->type = funcCall->type->inner; + inst->opFlags() = flags; + if(paramAttrs > 0) + inst->extra(alloc).paramAttrs = &m_AttributeSets[paramAttrs - 1]; if(funcCallType) { - RDCASSERT(funcCallType == inst.funcCall->funcType); + RDCASSERT(funcCallType == funcCall->type); } for(size_t i = 0; op.remaining() > 0; i++) { - if(inst.funcCall->funcType->members[i]->type == Type::Metadata) + Value *arg = NULL; + if(funcCall->type->members[i]->type == Type::Metadata) { int32_t offs = (int32_t)op.get(); - size_t idx = m_Values.size() - offs; - if(idx < m_Metadata.size()) - v = Value(&m_Metadata[idx]); - else - v = Value(&f.metadata[idx - m_Metadata.size()]); + size_t idx = values.curValueIndex() - offs; + arg = metadata[idx]; } else { - v = op.getSymbol(false); + arg = op.getSymbol(false); } - inst.args.push_back(v); + inst->args.push_back(arg); } - RDCASSERTEQUAL(inst.args.size(), inst.funcCall->funcType->members.size()); + RDCASSERTEQUAL(inst->args.size(), funcCall->type->members.size()); - f.instructions.push_back(inst); + f->instructions.push_back(inst); - if(!inst.type->isVoid()) - m_Values.push_back(Value(&f.instructions.back())); - if(inst.funcCall->name == "dx.op.createHandleFromHeap") + if(!voidCall) + values.addValue(); + if(funcCall->name == "dx.op.createHandleFromHeap") m_directHeapAccessCount++; } else if(op.type == FunctionRecord::INST_CAST) { - Instruction inst; + Instruction *inst = values.nextValue(); - inst.args.push_back(op.getSymbol()); - inst.type = op.getType(); + inst->args.push_back(op.getSymbol()); + inst->type = op.getType(); uint64_t opcode = op.get(); - inst.op = DecodeCast(opcode); + inst->op = DecodeCast(opcode); - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_EXTRACTVAL) { - Instruction inst; + Instruction *inst = values.nextValue(); - inst.op = Operation::ExtractVal; + inst->op = Operation::ExtractVal; - inst.args.push_back(op.getSymbol()); - inst.type = op.getType(f, inst.args.back()); + inst->args.push_back(op.getSymbol()); + inst->type = inst->args.back()->type; while(op.remaining() > 0) { uint64_t val = op.get(); - if(inst.type->type == Type::Array) - inst.type = inst.type->inner; + if(inst->type->type == Type::Array) + inst->type = inst->type->inner; else - inst.type = inst.type->members[(size_t)val]; - inst.args.push_back(Value(val)); + inst->type = inst->type->members[(size_t)val]; + inst->args.push_back(new(alloc) Literal(val)); } - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_RET) { - Instruction inst; - - inst.op = Operation::Ret; + Instruction *inst = NULL; if(op.remaining() == 0) { - inst.type = GetVoidType(); + inst = new(alloc) Instruction; + inst->type = GetVoidType(); - RDCASSERT(inst.type); + RDCASSERT(inst->type); } else { - inst.args.push_back(op.getSymbol()); - inst.type = op.getType(f, inst.args.back()); + inst = values.nextValue(); + inst->args.push_back(op.getSymbol()); + inst->type = inst->args.back()->type; + values.addValue(); } + inst->op = Operation::Ret; + curBlock++; - f.instructions.push_back(inst); - - if(!inst.args.empty()) - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); } else if(op.type == FunctionRecord::INST_BINOP) { - Instruction inst; + Instruction *inst = values.nextValue(); - inst.args.push_back(op.getSymbol()); - inst.type = op.getType(f, inst.args.back()); - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol()); + inst->type = inst->args.back()->type; + inst->args.push_back(op.getSymbol(false)); - bool isFloatOp = (inst.type->scalarType == Type::Float); + bool isFloatOp = (inst->type->scalarType == Type::Float); uint64_t opcode = op.get(); switch(opcode) { - case 0: inst.op = isFloatOp ? Operation::FAdd : Operation::Add; break; - case 1: inst.op = isFloatOp ? Operation::FSub : Operation::Sub; break; - case 2: inst.op = isFloatOp ? Operation::FMul : Operation::Mul; break; - case 3: inst.op = Operation::UDiv; break; - case 4: inst.op = isFloatOp ? Operation::FDiv : Operation::SDiv; break; - case 5: inst.op = Operation::URem; break; - case 6: inst.op = isFloatOp ? Operation::FRem : Operation::SRem; break; - case 7: inst.op = Operation::ShiftLeft; break; - case 8: inst.op = Operation::LogicalShiftRight; break; - case 9: inst.op = Operation::ArithShiftRight; break; - case 10: inst.op = Operation::And; break; - case 11: inst.op = Operation::Or; break; - case 12: inst.op = Operation::Xor; break; + case 0: inst->op = isFloatOp ? Operation::FAdd : Operation::Add; break; + case 1: inst->op = isFloatOp ? Operation::FSub : Operation::Sub; break; + case 2: inst->op = isFloatOp ? Operation::FMul : Operation::Mul; break; + case 3: inst->op = Operation::UDiv; break; + case 4: inst->op = isFloatOp ? Operation::FDiv : Operation::SDiv; break; + case 5: inst->op = Operation::URem; break; + case 6: inst->op = isFloatOp ? Operation::FRem : Operation::SRem; break; + case 7: inst->op = Operation::ShiftLeft; break; + case 8: inst->op = Operation::LogicalShiftRight; break; + case 9: inst->op = Operation::ArithShiftRight; break; + case 10: inst->op = Operation::And; break; + case 11: inst->op = Operation::Or; break; + case 12: inst->op = Operation::Xor; break; default: - inst.op = Operation::And; + inst->op = Operation::And; RDCERR("Unhandled binop type %llu", opcode); break; } @@ -1397,231 +1351,232 @@ Program::Program(const byte *bytes, size_t length) if(op.remaining() > 0) { uint64_t flags = op.get(); - if(inst.op == Operation::Add || inst.op == Operation::Sub || - inst.op == Operation::Mul || inst.op == Operation::ShiftLeft) + if(inst->op == Operation::Add || inst->op == Operation::Sub || + inst->op == Operation::Mul || inst->op == Operation::ShiftLeft) { if(flags & 0x2) - inst.opFlags |= InstructionFlags::NoSignedWrap; + inst->opFlags() |= InstructionFlags::NoSignedWrap; if(flags & 0x1) - inst.opFlags |= InstructionFlags::NoUnsignedWrap; + inst->opFlags() |= InstructionFlags::NoUnsignedWrap; } - else if(inst.op == Operation::SDiv || inst.op == Operation::UDiv || - inst.op == Operation::LogicalShiftRight || - inst.op == Operation::ArithShiftRight) + else if(inst->op == Operation::SDiv || inst->op == Operation::UDiv || + inst->op == Operation::LogicalShiftRight || + inst->op == Operation::ArithShiftRight) { if(flags & 0x1) - inst.opFlags |= InstructionFlags::Exact; + inst->opFlags() |= InstructionFlags::Exact; } else if(isFloatOp) { // fast math flags overlap - inst.opFlags = InstructionFlags(flags); + inst->opFlags() = InstructionFlags(flags); } - RDCASSERT(inst.opFlags != InstructionFlags::NoFlags); + RDCASSERT(inst->opFlags() != InstructionFlags::NoFlags); } - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_UNREACHABLE) { - Instruction inst; + Instruction *inst = new(alloc) Instruction; - inst.op = Operation::Unreachable; + inst->op = Operation::Unreachable; - inst.type = GetVoidType(); + inst->type = GetVoidType(); curBlock++; - f.instructions.push_back(inst); + f->instructions.push_back(inst); } else if(op.type == FunctionRecord::INST_ALLOCA) { - Instruction inst; + Instruction *inst = values.nextValue(); - inst.op = Operation::Alloca; + inst->op = Operation::Alloca; - inst.type = op.getType(); + inst->type = op.getType(); // we now have the inner type, but this instruction returns a pointer to that type so // adjust - inst.type = GetPointerType(inst.type, Type::PointerAddrSpace::Default); + inst->type = GetPointerType(inst->type, Type::PointerAddrSpace::Default); - RDCASSERT(inst.type->type == Type::Pointer); + RDCASSERT(inst->type->type == Type::Pointer); // type of the size - ignored const Type *sizeType = op.getType(); // size - inst.args.push_back(op.getSymbolAbsolute()); + inst->args.push_back(op.getSymbolAbsolute()); - RDCASSERT(sizeType == inst.args.back().GetType()); + RDCASSERT(sizeType == inst->args.back()->type); uint64_t align = op.get(); if(align & 0x20) { // argument alloca - inst.opFlags |= InstructionFlags::ArgumentAlloca; + inst->opFlags() |= InstructionFlags::ArgumentAlloca; } if((align & 0x40) == 0) { - RDCASSERT(inst.type->type == Type::Pointer); - inst.type = inst.type->inner; + RDCASSERT(inst->type->type == Type::Pointer); + inst->type = inst->type->inner; } align &= ~0xE0; - inst.align = (1U << align) >> 1; + RDCASSERT(align < 0x100); + inst->align = align & 0xff; - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_INBOUNDS_GEP_OLD || op.type == FunctionRecord::INST_GEP_OLD || op.type == FunctionRecord::INST_GEP) { - Instruction inst; + Instruction *inst = values.nextValue(); - inst.op = Operation::GetElementPtr; + inst->op = Operation::GetElementPtr; if(op.type == FunctionRecord::INST_INBOUNDS_GEP_OLD) - inst.opFlags |= InstructionFlags::InBounds; + inst->opFlags() |= InstructionFlags::InBounds; if(op.type == FunctionRecord::INST_GEP) { if(op.get()) - inst.opFlags |= InstructionFlags::InBounds; - inst.type = op.getType(); + inst->opFlags() |= InstructionFlags::InBounds; + inst->type = op.getType(); } while(op.remaining() > 0) { - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); - if(inst.type == NULL && inst.args.size() == 1) - inst.type = op.getType(f, inst.args.back()); + if(inst->type == NULL && inst->args.size() == 1) + inst->type = inst->args.back()->type; } // walk the type list to get the return type - for(size_t idx = 2; idx < inst.args.size(); idx++) + for(size_t idx = 2; idx < inst->args.size(); idx++) { - if(inst.type->type == Type::Vector || inst.type->type == Type::Array) + if(inst->type->type == Type::Vector || inst->type->type == Type::Array) { - inst.type = inst.type->inner; + inst->type = inst->type->inner; } - else if(inst.type->type == Type::Struct) + else if(inst->type->type == Type::Struct) { - Value v = inst.args[idx]; // if it's a struct the index must be constant - RDCASSERT(v.type == ValueType::Constant); - inst.type = inst.type->members[v.constant->val.u32v[0]]; + Constant *c = cast(inst->args[idx]); + RDCASSERT(c); + inst->type = inst->type->members[c->getU32()]; } else { - RDCERR("Unexpected type %d encountered in GEP", inst.type->type); + RDCERR("Unexpected type %d encountered in GEP", inst->type->type); } } // get the pointer type - inst.type = GetPointerType(inst.type, op.getType(f, inst.args[0])->addrSpace); + inst->type = GetPointerType(inst->type, inst->args[0]->type->addrSpace); - RDCASSERT(inst.type->type == Type::Pointer); + RDCASSERT(inst->type->type == Type::Pointer); - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_LOAD) { - Instruction inst; + Instruction *inst = values.nextValue(); - inst.op = Operation::Load; + inst->op = Operation::Load; - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); if(op.remaining() == 3) { - inst.type = op.getType(); + inst->type = op.getType(); } else { - inst.type = op.getType(f, inst.args.back()); - RDCASSERT(inst.type->type == Type::Pointer); - inst.type = inst.type->inner; + inst->type = inst->args.back()->type; + RDCASSERT(inst->type->type == Type::Pointer); + inst->type = inst->type->inner; } - inst.align = (1U << op.get()) >> 1; - inst.opFlags |= (op.get() != 0) ? InstructionFlags::Volatile - : InstructionFlags::NoFlags; + inst->align = op.get(); + inst->opFlags() |= (op.get() != 0) ? InstructionFlags::Volatile + : InstructionFlags::NoFlags; - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_STORE_OLD || op.type == FunctionRecord::INST_STORE) { - Instruction inst; + Instruction *inst = new(alloc) Instruction; - inst.op = Operation::Store; + inst->op = Operation::Store; - inst.type = GetVoidType(); + inst->type = GetVoidType(); - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); if(op.type == FunctionRecord::INST_STORE_OLD) - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); else - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); - inst.align = (1U << op.get()) >> 1; - inst.opFlags |= (op.get() != 0) ? InstructionFlags::Volatile - : InstructionFlags::NoFlags; + inst->align = op.get(); + inst->opFlags() |= (op.get() != 0) ? InstructionFlags::Volatile + : InstructionFlags::NoFlags; - f.instructions.push_back(inst); + f->instructions.push_back(inst); } else if(op.type == FunctionRecord::INST_CMP || IS_KNOWN(op.type, FunctionRecord::INST_CMP2)) { - Instruction inst; + Instruction *inst = values.nextValue(); // a - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); - const Type *argType = op.getType(f, inst.args.back()); + const Type *argType = inst->args.back()->type; // b - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); uint64_t opcode = op.get(); switch(opcode) { - case 0: inst.op = Operation::FOrdFalse; break; - case 1: inst.op = Operation::FOrdEqual; break; - case 2: inst.op = Operation::FOrdGreater; break; - case 3: inst.op = Operation::FOrdGreaterEqual; break; - case 4: inst.op = Operation::FOrdLess; break; - case 5: inst.op = Operation::FOrdLessEqual; break; - case 6: inst.op = Operation::FOrdNotEqual; break; - case 7: inst.op = Operation::FOrd; break; - case 8: inst.op = Operation::FUnord; break; - case 9: inst.op = Operation::FUnordEqual; break; - case 10: inst.op = Operation::FUnordGreater; break; - case 11: inst.op = Operation::FUnordGreaterEqual; break; - case 12: inst.op = Operation::FUnordLess; break; - case 13: inst.op = Operation::FUnordLessEqual; break; - case 14: inst.op = Operation::FUnordNotEqual; break; - case 15: inst.op = Operation::FOrdTrue; break; + case 0: inst->op = Operation::FOrdFalse; break; + case 1: inst->op = Operation::FOrdEqual; break; + case 2: inst->op = Operation::FOrdGreater; break; + case 3: inst->op = Operation::FOrdGreaterEqual; break; + case 4: inst->op = Operation::FOrdLess; break; + case 5: inst->op = Operation::FOrdLessEqual; break; + case 6: inst->op = Operation::FOrdNotEqual; break; + case 7: inst->op = Operation::FOrd; break; + case 8: inst->op = Operation::FUnord; break; + case 9: inst->op = Operation::FUnordEqual; break; + case 10: inst->op = Operation::FUnordGreater; break; + case 11: inst->op = Operation::FUnordGreaterEqual; break; + case 12: inst->op = Operation::FUnordLess; break; + case 13: inst->op = Operation::FUnordLessEqual; break; + case 14: inst->op = Operation::FUnordNotEqual; break; + case 15: inst->op = Operation::FOrdTrue; break; - case 32: inst.op = Operation::IEqual; break; - case 33: inst.op = Operation::INotEqual; break; - case 34: inst.op = Operation::UGreater; break; - case 35: inst.op = Operation::UGreaterEqual; break; - case 36: inst.op = Operation::ULess; break; - case 37: inst.op = Operation::ULessEqual; break; - case 38: inst.op = Operation::SGreater; break; - case 39: inst.op = Operation::SGreaterEqual; break; - case 40: inst.op = Operation::SLess; break; - case 41: inst.op = Operation::SLessEqual; break; + case 32: inst->op = Operation::IEqual; break; + case 33: inst->op = Operation::INotEqual; break; + case 34: inst->op = Operation::UGreater; break; + case 35: inst->op = Operation::UGreaterEqual; break; + case 36: inst->op = Operation::ULess; break; + case 37: inst->op = Operation::ULessEqual; break; + case 38: inst->op = Operation::SGreater; break; + case 39: inst->op = Operation::SGreaterEqual; break; + case 40: inst->op = Operation::SLess; break; + case 41: inst->op = Operation::SLessEqual; break; default: - inst.op = Operation::FOrdFalse; + inst->op = Operation::FOrdFalse; RDCERR("Unexpected comparison %llu", opcode); break; } @@ -1629,90 +1584,90 @@ Program::Program(const byte *bytes, size_t length) // fast math flags if(op.remaining() > 0) { - inst.opFlags = op.get(); + inst->opFlags() = op.get(); - RDCASSERTNOTEQUAL((uint64_t)inst.opFlags, 0); + RDCASSERTNOTEQUAL((uint64_t)inst->opFlags(), 0); } - inst.type = GetBoolType(); + inst->type = GetBoolType(); // if we're comparing vectors, the return type is an equal sized bool vector if(argType->type == Type::Vector) { - for(const Type &t : m_Types) + for(const Type *t : m_Types) { - if(t.type == Type::Vector && t.inner == inst.type && - t.elemCount == argType->elemCount) + if(t->type == Type::Vector && t->inner == inst->type && + t->elemCount == argType->elemCount) { - inst.type = &t; + inst->type = t; break; } } } - RDCASSERT(inst.type->type == argType->type && - inst.type->elemCount == argType->elemCount); + RDCASSERT(inst->type->type == argType->type && + inst->type->elemCount == argType->elemCount); - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_SELECT || op.type == FunctionRecord::INST_VSELECT) { - Instruction inst; + Instruction *inst = values.nextValue(); - inst.op = Operation::Select; + inst->op = Operation::Select; // if true - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); - inst.type = op.getType(f, inst.args.back()); + inst->type = inst->args.back()->type; // if false - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); // selector if(op.type == FunctionRecord::INST_SELECT) - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); else - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_BR) { - Instruction inst; + Instruction *inst = new(alloc) Instruction; - inst.op = Operation::Branch; + inst->op = Operation::Branch; - inst.type = GetVoidType(); + inst->type = GetVoidType(); // true destination uint64_t trueDest = op.get(); - inst.args.push_back(Value(&f.blocks[(size_t)trueDest])); - f.blocks[(size_t)trueDest].preds.insert(0, &f.blocks[curBlock]); + inst->args.push_back(f->blocks[(size_t)trueDest]); + f->blocks[(size_t)trueDest]->preds.insert(0, f->blocks[curBlock]); if(op.remaining() > 0) { // false destination uint64_t falseDest = op.get(); - inst.args.push_back(Value(&f.blocks[(size_t)falseDest])); - f.blocks[(size_t)falseDest].preds.insert(0, &f.blocks[curBlock]); + inst->args.push_back(f->blocks[(size_t)falseDest]); + f->blocks[(size_t)falseDest]->preds.insert(0, f->blocks[curBlock]); // predicate - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); } curBlock++; - f.instructions.push_back(inst); + f->instructions.push_back(inst); } else if(op.type == FunctionRecord::INST_SWITCH) { - Instruction inst; + Instruction *inst = new(alloc) Instruction; - inst.op = Operation::Switch; + inst->op = Operation::Switch; - inst.type = GetVoidType(); + inst->type = GetVoidType(); uint64_t typeIdx = op.get(); @@ -1725,50 +1680,50 @@ Program::Program(const byte *bytes, size_t length) RDCASSERT(condType->bitWidth <= 64); // condition - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); // default block size_t defaultDest = op.get(); - inst.args.push_back(Value(&f.blocks[defaultDest])); - f.blocks[defaultDest].preds.insert(0, &f.blocks[curBlock]); + inst->args.push_back(f->blocks[defaultDest]); + f->blocks[defaultDest]->preds.insert(0, f->blocks[curBlock]); RDCERR("Unsupported switch instruction version"); } else { // condition - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); // default block size_t defaultDest = op.get(); - inst.args.push_back(Value(&f.blocks[defaultDest])); - f.blocks[defaultDest].preds.insert(0, &f.blocks[curBlock]); + inst->args.push_back(f->blocks[defaultDest]); + f->blocks[defaultDest]->preds.insert(0, f->blocks[curBlock]); uint64_t numCases = op.remaining() / 2; for(uint64_t c = 0; c < numCases; c++) { // case value, absolute not relative - inst.args.push_back(op.getSymbolAbsolute()); + inst->args.push_back(op.getSymbolAbsolute()); // case block size_t caseDest = op.get(); - inst.args.push_back(Value(&f.blocks[caseDest])); - f.blocks[caseDest].preds.insert(0, &f.blocks[curBlock]); + inst->args.push_back(f->blocks[caseDest]); + f->blocks[caseDest]->preds.insert(0, f->blocks[curBlock]); } } curBlock++; - f.instructions.push_back(inst); + f->instructions.push_back(inst); } else if(op.type == FunctionRecord::INST_PHI) { - Instruction inst; + Instruction *inst = values.nextValue(); - inst.op = Operation::Phi; + inst->op = Operation::Phi; - inst.type = op.getType(); + inst->type = op.getType(); while(op.remaining() > 0) { @@ -1777,56 +1732,56 @@ Program::Program(const byte *bytes, size_t length) if(valSrc <= 0) { - inst.args.push_back(Value(Value::ForwardRef, - &m_Values[size_t((int64_t)m_Values.size() - valSrc)])); + inst->args.push_back( + values.createPlaceholderValue(values.getRelativeForwards(-valSrc))); } else { - inst.args.push_back(op.getSymbol((uint64_t)valSrc)); + inst->args.push_back(op.getSymbol((uint64_t)valSrc)); } - inst.args.push_back(Value(&f.blocks[(size_t)blockSrc])); + inst->args.push_back(f->blocks[(size_t)blockSrc]); } - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_LOADATOMIC) { - Instruction inst; + Instruction *inst = values.nextValue(); - inst.op = Operation::LoadAtomic; + inst->op = Operation::LoadAtomic; - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); if(op.remaining() == 5) { - inst.type = op.getType(); + inst->type = op.getType(); } else { - inst.type = op.getType(f, inst.args.back()); - RDCASSERT(inst.type->type == Type::Pointer); - inst.type = inst.type->inner; + inst->type = inst->args.back()->type; + RDCASSERT(inst->type->type == Type::Pointer); + inst->type = inst->type->inner; } - inst.align = (1U << op.get()) >> 1; - inst.opFlags |= (op.get() != 0) ? InstructionFlags::Volatile - : InstructionFlags::NoFlags; + inst->align = op.get(); + inst->opFlags() |= (op.get() != 0) ? InstructionFlags::Volatile + : InstructionFlags::NoFlags; // success ordering uint64_t opcode = op.get(); switch(opcode) { case 0: break; - case 1: inst.opFlags |= InstructionFlags::SuccessUnordered; break; - case 2: inst.opFlags |= InstructionFlags::SuccessMonotonic; break; - case 3: inst.opFlags |= InstructionFlags::SuccessAcquire; break; - case 4: inst.opFlags |= InstructionFlags::SuccessRelease; break; - case 5: inst.opFlags |= InstructionFlags::SuccessAcquireRelease; break; - case 6: inst.opFlags |= InstructionFlags::SuccessSequentiallyConsistent; break; + case 1: inst->opFlags() |= InstructionFlags::SuccessUnordered; break; + case 2: inst->opFlags() |= InstructionFlags::SuccessMonotonic; break; + case 3: inst->opFlags() |= InstructionFlags::SuccessAcquire; break; + case 4: inst->opFlags() |= InstructionFlags::SuccessRelease; break; + case 5: inst->opFlags() |= InstructionFlags::SuccessAcquireRelease; break; + case 6: inst->opFlags() |= InstructionFlags::SuccessSequentiallyConsistent; break; default: RDCERR("Unexpected success ordering %llu", opcode); - inst.opFlags |= InstructionFlags::SuccessSequentiallyConsistent; + inst->opFlags() |= InstructionFlags::SuccessSequentiallyConsistent; break; } @@ -1834,47 +1789,47 @@ Program::Program(const byte *bytes, size_t length) opcode = op.get(); switch(opcode) { - case 0: inst.opFlags |= InstructionFlags::SingleThread; break; + case 0: inst->opFlags() |= InstructionFlags::SingleThread; break; case 1: break; default: RDCERR("Unexpected synchronisation scope %llu", opcode); break; } - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_STOREATOMIC_OLD || op.type == FunctionRecord::INST_STOREATOMIC) { - Instruction inst; + Instruction *inst = new(alloc) Instruction; - inst.op = Operation::StoreAtomic; + inst->op = Operation::StoreAtomic; - inst.type = GetVoidType(); + inst->type = GetVoidType(); - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); if(op.type == FunctionRecord::INST_STOREATOMIC_OLD) - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); else - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); - inst.align = (1U << op.get()) >> 1; - inst.opFlags |= (op.get() != 0) ? InstructionFlags::Volatile - : InstructionFlags::NoFlags; + inst->align = op.get(); + inst->opFlags() |= (op.get() != 0) ? InstructionFlags::Volatile + : InstructionFlags::NoFlags; // success ordering uint64_t opcode = op.get(); switch(opcode) { case 0: break; - case 1: inst.opFlags |= InstructionFlags::SuccessUnordered; break; - case 2: inst.opFlags |= InstructionFlags::SuccessMonotonic; break; - case 3: inst.opFlags |= InstructionFlags::SuccessAcquire; break; - case 4: inst.opFlags |= InstructionFlags::SuccessRelease; break; - case 5: inst.opFlags |= InstructionFlags::SuccessAcquireRelease; break; - case 6: inst.opFlags |= InstructionFlags::SuccessSequentiallyConsistent; break; + case 1: inst->opFlags() |= InstructionFlags::SuccessUnordered; break; + case 2: inst->opFlags() |= InstructionFlags::SuccessMonotonic; break; + case 3: inst->opFlags() |= InstructionFlags::SuccessAcquire; break; + case 4: inst->opFlags() |= InstructionFlags::SuccessRelease; break; + case 5: inst->opFlags() |= InstructionFlags::SuccessAcquireRelease; break; + case 6: inst->opFlags() |= InstructionFlags::SuccessSequentiallyConsistent; break; default: RDCERR("Unexpected success ordering %llu", opcode); - inst.opFlags |= InstructionFlags::SuccessSequentiallyConsistent; + inst->opFlags() |= InstructionFlags::SuccessSequentiallyConsistent; break; } @@ -1882,65 +1837,65 @@ Program::Program(const byte *bytes, size_t length) opcode = op.get(); switch(opcode) { - case 0: inst.opFlags |= InstructionFlags::SingleThread; break; + case 0: inst->opFlags() |= InstructionFlags::SingleThread; break; case 1: break; default: RDCERR("Unexpected synchronisation scope %llu", opcode); break; } - f.instructions.push_back(inst); + f->instructions.push_back(inst); } else if(op.type == FunctionRecord::INST_ATOMICRMW) { - Instruction inst; + Instruction *inst = values.nextValue(); // pointer to atomically modify - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); // type is the pointee of the first argument - inst.type = op.getType(f, inst.args.back()); - RDCASSERT(inst.type->type == Type::Pointer); - inst.type = inst.type->inner; + inst->type = inst->args.back()->type; + RDCASSERT(inst->type->type == Type::Pointer); + inst->type = inst->type->inner; // parameter value - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); uint64_t opcode = op.get(); switch(opcode) { - case 0: inst.op = Operation::AtomicExchange; break; - case 1: inst.op = Operation::AtomicAdd; break; - case 2: inst.op = Operation::AtomicSub; break; - case 3: inst.op = Operation::AtomicAnd; break; - case 4: inst.op = Operation::AtomicNand; break; - case 5: inst.op = Operation::AtomicOr; break; - case 6: inst.op = Operation::AtomicXor; break; - case 7: inst.op = Operation::AtomicMax; break; - case 8: inst.op = Operation::AtomicMin; break; - case 9: inst.op = Operation::AtomicUMax; break; - case 10: inst.op = Operation::AtomicUMin; break; + case 0: inst->op = Operation::AtomicExchange; break; + case 1: inst->op = Operation::AtomicAdd; break; + case 2: inst->op = Operation::AtomicSub; break; + case 3: inst->op = Operation::AtomicAnd; break; + case 4: inst->op = Operation::AtomicNand; break; + case 5: inst->op = Operation::AtomicOr; break; + case 6: inst->op = Operation::AtomicXor; break; + case 7: inst->op = Operation::AtomicMax; break; + case 8: inst->op = Operation::AtomicMin; break; + case 9: inst->op = Operation::AtomicUMax; break; + case 10: inst->op = Operation::AtomicUMin; break; default: RDCERR("Unhandled atomicrmw op %llu", opcode); - inst.op = Operation::AtomicExchange; + inst->op = Operation::AtomicExchange; break; } if(op.get()) - inst.opFlags |= InstructionFlags::Volatile; + inst->opFlags() |= InstructionFlags::Volatile; // success ordering opcode = op.get(); switch(opcode) { case 0: break; - case 1: inst.opFlags |= InstructionFlags::SuccessUnordered; break; - case 2: inst.opFlags |= InstructionFlags::SuccessMonotonic; break; - case 3: inst.opFlags |= InstructionFlags::SuccessAcquire; break; - case 4: inst.opFlags |= InstructionFlags::SuccessRelease; break; - case 5: inst.opFlags |= InstructionFlags::SuccessAcquireRelease; break; - case 6: inst.opFlags |= InstructionFlags::SuccessSequentiallyConsistent; break; + case 1: inst->opFlags() |= InstructionFlags::SuccessUnordered; break; + case 2: inst->opFlags() |= InstructionFlags::SuccessMonotonic; break; + case 3: inst->opFlags() |= InstructionFlags::SuccessAcquire; break; + case 4: inst->opFlags() |= InstructionFlags::SuccessRelease; break; + case 5: inst->opFlags() |= InstructionFlags::SuccessAcquireRelease; break; + case 6: inst->opFlags() |= InstructionFlags::SuccessSequentiallyConsistent; break; default: RDCERR("Unexpected success ordering %llu", opcode); - inst.opFlags |= InstructionFlags::SuccessSequentiallyConsistent; + inst->opFlags() |= InstructionFlags::SuccessSequentiallyConsistent; break; } @@ -1948,73 +1903,73 @@ Program::Program(const byte *bytes, size_t length) opcode = op.get(); switch(opcode) { - case 0: inst.opFlags |= InstructionFlags::SingleThread; break; + case 0: inst->opFlags() |= InstructionFlags::SingleThread; break; case 1: break; default: RDCERR("Unexpected synchronisation scope %llu", opcode); break; } - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_CMPXCHG || op.type == FunctionRecord::INST_CMPXCHG_OLD) { - Instruction inst; + Instruction *inst = values.nextValue(); - inst.op = Operation::CompareExchange; + inst->op = Operation::CompareExchange; // pointer to atomically modify - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); // type is the pointee of the first argument - inst.type = op.getType(f, inst.args.back()); - RDCASSERT(inst.type->type == Type::Pointer); - inst.type = inst.type->inner; + inst->type = inst->args.back()->type; + RDCASSERT(inst->type->type == Type::Pointer); + inst->type = inst->type->inner; // combined with a bool, search for a struct like that const Type *boolType = GetBoolType(); - for(const Type &t : m_Types) + for(const Type *t : m_Types) { - if(t.type == Type::Struct && t.members.size() == 2 && t.members[0] == inst.type && - t.members[1] == boolType) + if(t->type == Type::Struct && t->members.size() == 2 && + t->members[0] == inst->type && t->members[1] == boolType) { - inst.type = &t; + inst->type = t; break; } } - RDCASSERT(inst.type->type == Type::Struct); + RDCASSERT(inst->type->type == Type::Struct); // expect modern encoding with weak parameters. RDCASSERT(funcChild.ops.size() >= 8); // compare value if(op.type == FunctionRecord::INST_CMPXCHG_OLD) - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); else - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); // new replacement value - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); if(op.get()) - inst.opFlags |= InstructionFlags::Volatile; + inst->opFlags() |= InstructionFlags::Volatile; // success ordering uint64_t opcode = op.get(); switch(opcode) { case 0: break; - case 1: inst.opFlags |= InstructionFlags::SuccessUnordered; break; - case 2: inst.opFlags |= InstructionFlags::SuccessMonotonic; break; - case 3: inst.opFlags |= InstructionFlags::SuccessAcquire; break; - case 4: inst.opFlags |= InstructionFlags::SuccessRelease; break; - case 5: inst.opFlags |= InstructionFlags::SuccessAcquireRelease; break; - case 6: inst.opFlags |= InstructionFlags::SuccessSequentiallyConsistent; break; + case 1: inst->opFlags() |= InstructionFlags::SuccessUnordered; break; + case 2: inst->opFlags() |= InstructionFlags::SuccessMonotonic; break; + case 3: inst->opFlags() |= InstructionFlags::SuccessAcquire; break; + case 4: inst->opFlags() |= InstructionFlags::SuccessRelease; break; + case 5: inst->opFlags() |= InstructionFlags::SuccessAcquireRelease; break; + case 6: inst->opFlags() |= InstructionFlags::SuccessSequentiallyConsistent; break; default: RDCERR("Unexpected success ordering %llu", opcode); - inst.opFlags |= InstructionFlags::SuccessSequentiallyConsistent; + inst->opFlags() |= InstructionFlags::SuccessSequentiallyConsistent; break; } @@ -2022,7 +1977,7 @@ Program::Program(const byte *bytes, size_t length) opcode = op.get(); switch(opcode) { - case 0: inst.opFlags |= InstructionFlags::SingleThread; break; + case 0: inst->opFlags() |= InstructionFlags::SingleThread; break; case 1: break; default: RDCERR("Unexpected synchronisation scope %llu", opcode); break; } @@ -2032,46 +1987,46 @@ Program::Program(const byte *bytes, size_t length) switch(opcode) { case 0: break; - case 1: inst.opFlags |= InstructionFlags::FailureUnordered; break; - case 2: inst.opFlags |= InstructionFlags::FailureMonotonic; break; - case 3: inst.opFlags |= InstructionFlags::FailureAcquire; break; - case 4: inst.opFlags |= InstructionFlags::FailureRelease; break; - case 5: inst.opFlags |= InstructionFlags::FailureAcquireRelease; break; - case 6: inst.opFlags |= InstructionFlags::FailureSequentiallyConsistent; break; + case 1: inst->opFlags() |= InstructionFlags::FailureUnordered; break; + case 2: inst->opFlags() |= InstructionFlags::FailureMonotonic; break; + case 3: inst->opFlags() |= InstructionFlags::FailureAcquire; break; + case 4: inst->opFlags() |= InstructionFlags::FailureRelease; break; + case 5: inst->opFlags() |= InstructionFlags::FailureAcquireRelease; break; + case 6: inst->opFlags() |= InstructionFlags::FailureSequentiallyConsistent; break; default: RDCERR("Unexpected failure ordering %llu", opcode); - inst.opFlags |= InstructionFlags::FailureSequentiallyConsistent; + inst->opFlags() |= InstructionFlags::FailureSequentiallyConsistent; break; } if(op.get()) - inst.opFlags |= InstructionFlags::Weak; + inst->opFlags() |= InstructionFlags::Weak; - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_FENCE) { - Instruction inst; + Instruction *inst = new(alloc) Instruction; - inst.op = Operation::Fence; + inst->op = Operation::Fence; - inst.type = GetVoidType(); + inst->type = GetVoidType(); // success ordering uint64_t opcode = op.get(); switch(opcode) { case 0: break; - case 1: inst.opFlags |= InstructionFlags::SuccessUnordered; break; - case 2: inst.opFlags |= InstructionFlags::SuccessMonotonic; break; - case 3: inst.opFlags |= InstructionFlags::SuccessAcquire; break; - case 4: inst.opFlags |= InstructionFlags::SuccessRelease; break; - case 5: inst.opFlags |= InstructionFlags::SuccessAcquireRelease; break; - case 6: inst.opFlags |= InstructionFlags::SuccessSequentiallyConsistent; break; + case 1: inst->opFlags() |= InstructionFlags::SuccessUnordered; break; + case 2: inst->opFlags() |= InstructionFlags::SuccessMonotonic; break; + case 3: inst->opFlags() |= InstructionFlags::SuccessAcquire; break; + case 4: inst->opFlags() |= InstructionFlags::SuccessRelease; break; + case 5: inst->opFlags() |= InstructionFlags::SuccessAcquireRelease; break; + case 6: inst->opFlags() |= InstructionFlags::SuccessSequentiallyConsistent; break; default: RDCERR("Unexpected success ordering %llu", opcode); - inst.opFlags |= InstructionFlags::SuccessSequentiallyConsistent; + inst->opFlags() |= InstructionFlags::SuccessSequentiallyConsistent; break; } @@ -2079,116 +2034,116 @@ Program::Program(const byte *bytes, size_t length) opcode = op.get(); switch(opcode) { - case 0: inst.opFlags |= InstructionFlags::SingleThread; break; + case 0: inst->opFlags() |= InstructionFlags::SingleThread; break; case 1: break; default: RDCERR("Unexpected synchronisation scope %llu", opcode); break; } - f.instructions.push_back(inst); + f->instructions.push_back(inst); } else if(op.type == FunctionRecord::INST_EXTRACTELT) { // DXIL claims to be scalarised but lol that's a lie - Instruction inst; + Instruction *inst = values.nextValue(); - inst.op = Operation::ExtractElement; + inst->op = Operation::ExtractElement; // vector - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); // result is the scalar type within the vector - inst.type = op.getType(f, inst.args.back())->inner; + inst->type = inst->args.back()->type->inner; // index - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_INSERTELT) { // DXIL claims to be scalarised but lol that's a lie - Instruction inst; + Instruction *inst = values.nextValue(); - inst.op = Operation::InsertElement; + inst->op = Operation::InsertElement; // vector - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); // result is the vector type - inst.type = op.getType(f, inst.args.back()); + inst->type = inst->args.back()->type; // replacement element - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); // index - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_SHUFFLEVEC) { // DXIL claims to be scalarised so should this appear? RDCWARN("Unexpected vector instruction shufflevector in DXIL"); - Instruction inst; + Instruction *inst = values.nextValue(); - inst.op = Operation::ShuffleVector; + inst->op = Operation::ShuffleVector; // vector 1 - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); - const Type *vecType = op.getType(f, inst.args.back()); + const Type *vecType = inst->args.back()->type; // vector 2 - inst.args.push_back(op.getSymbol(false)); + inst->args.push_back(op.getSymbol(false)); // indexes - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); // result is a vector with the inner type of the first two vectors and the element // count of the last vector - const Type *maskType = op.getType(f, inst.args.back()); + const Type *maskType = inst->args.back()->type; - for(const Type &t : m_Types) + for(const Type *t : m_Types) { - if(t.type == Type::Vector && t.inner == vecType->inner && - t.elemCount == maskType->elemCount) + if(t->type == Type::Vector && t->inner == vecType->inner && + t->elemCount == maskType->elemCount) { - inst.type = &t; + inst->type = t; break; } } - RDCASSERT(inst.type); + RDCASSERT(inst->type); - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_INSERTVAL) { // DXIL claims to be scalarised so should this appear? RDCWARN("Unexpected aggregate instruction insertvalue in DXIL"); - Instruction inst; + Instruction *inst = values.nextValue(); - inst.op = Operation::InsertValue; + inst->op = Operation::InsertValue; // aggregate - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); // result is the aggregate type - inst.type = op.getType(f, inst.args.back()); + inst->type = inst->args.back()->type; // replacement element - inst.args.push_back(op.getSymbol()); + inst->args.push_back(op.getSymbol()); // indices as literals while(op.remaining() > 0) - inst.args.push_back(Value(op.get())); + inst->args.push_back(new(alloc) Literal(op.get())); - f.instructions.push_back(inst); - m_Values.push_back(Value(&f.instructions.back())); + f->instructions.push_back(inst); + values.addValue(); } else if(op.type == FunctionRecord::INST_VAARG) { @@ -2210,64 +2165,32 @@ Program::Program(const byte *bytes, size_t length) } } - RDCASSERT(valueStorage == m_Values.data()); - - RDCASSERT(curBlock == f.blocks.size()); - - size_t resultID = 0; - - if(f.blocks[0].name.empty()) - f.blocks[0].resultID = (uint32_t)resultID++; - - for(size_t i = 0; i < f.metadata.size(); i++) - { - Value &v = f.metadata[i].value; - if(!v.empty() && v.type == ValueType::Unknown) - { - v = *v.value; - RDCASSERT(v.type == ValueType::Instruction); - } - } + RDCASSERT(curBlock == f->blocks.size()); curBlock = 0; - for(size_t i = 0; i < f.instructions.size(); i++) + for(size_t i = 0; i < f->instructions.size(); i++) { - // fix up forward references here, we couldn't write them up front because we didn't know - // how many actual symbols (non-void instructions) existed after the given instruction - for(Value &s : f.instructions[i].args) - { - if(s.type == ValueType::Unknown) - { - s = *s.value; - RDCASSERT(s.type == ValueType::Instruction); - } - } - - if(f.instructions[i].op == Operation::Branch || - f.instructions[i].op == Operation::Unreachable || - f.instructions[i].op == Operation::Switch || f.instructions[i].op == Operation::Ret) + Instruction &inst = *f->instructions[i]; + if(inst.op == Operation::Branch || inst.op == Operation::Unreachable || + inst.op == Operation::Switch || inst.op == Operation::Ret) { curBlock++; - if(i == f.instructions.size() - 1) + if(i == f->instructions.size() - 1) break; - if(f.blocks[curBlock].name.empty()) - f.blocks[curBlock].resultID = (uint32_t)resultID++; continue; } - if(f.instructions[i].type->isVoid()) + if(inst.type->isVoid()) continue; - if(!f.instructions[i].name.empty()) + if(!inst.getName().empty()) continue; - - f.instructions[i].resultID = (uint32_t)resultID++; } - f.values.assign(m_Values.data() + prevNumSymbols, m_Values.size() - prevNumSymbols); - m_Values.resize(prevNumSymbols); + values.endFunction(); + metadata.endFunction(); } else { @@ -2278,46 +2201,60 @@ Program::Program(const byte *bytes, size_t length) // pointer fixups. This is only needed for global variabls as it has forward references to // constants before we can even reserve the constants. - for(GlobalVar &g : m_GlobalVars) + for(GlobalVar *g : m_GlobalVars) { - if(g.initialiser) + if(g->initialiser) { - size_t idx = g.initialiser - (Constant *)NULL; - Value v = m_Values[idx - 1]; - RDCASSERT(v.type == ValueType::Constant); - g.initialiser = v.constant; + size_t idx = g->initialiser - (Constant *)NULL; + g->initialiser = cast(values[idx - 1]); } } RDCASSERT(functionDecls.empty()); } -rdcstr &Program::GetValueSymtabString(const Value &v) +rdcstr Program::GetValueSymtabString(Value *v) { - static rdcstr err; - switch(v.type) - { - case ValueType::Constant: return (rdcstr &)v.constant->str; - case ValueType::Instruction: return (rdcstr &)v.instruction->name; - case ValueType::BasicBlock: return (rdcstr &)v.block->name; - case ValueType::GlobalVar: return (rdcstr &)v.global->name; - case ValueType::Function: return (rdcstr &)v.function->name; - case ValueType::Alias: return (rdcstr &)v.alias->name; - case ValueType::Unknown: - case ValueType::Metadata: - case ValueType::Literal: RDCERR("Unexpected value symtab entry referring to %d", v.type); break; - } + if(Constant *c = cast(v)) + return c->str; + else if(Instruction *i = cast(v)) + return i->extra(alloc).name; + else if(Block *b = cast(v)) + return b->name; + else if(GlobalVar *g = cast(v)) + return g->name; + else if(Function *f = cast(v)) + return f->name; + else if(Alias *a = cast(v)) + return a->name; - return err; + return ""; } -uint32_t Program::GetOrAssignMetaID(Metadata *m) +void Program::SetValueSymtabString(Value *v, const rdcstr &s) { - if(m->id != ~0U) - return m->id; + if(Constant *c = cast(v)) + c->str = s; + else if(Instruction *i = cast(v)) + i->extra(alloc).name = s; + else if(Block *b = cast(v)) + b->name = s; + else if(GlobalVar *g = cast(v)) + g->name = s; + else if(Function *f = cast(v)) + f->name = s; + else if(Alias *a = cast(v)) + a->name = s; +} - m->id = m_NextMetaID++; - m_NumberedMeta.push_back(m); +uint32_t Program::GetOrAssignMetaSlot(rdcarray &metaSlots, uint32_t &nextMetaSlot, + Metadata *m) +{ + if(m->slot != ~0U) + return m->slot; + + m->slot = nextMetaSlot++; + metaSlots.push_back(m); // assign meta IDs to the children now for(Metadata *c : m->children) @@ -2325,119 +2262,33 @@ uint32_t Program::GetOrAssignMetaID(Metadata *m) if(!c || c->isConstant) continue; - GetOrAssignMetaID(c); + GetOrAssignMetaSlot(metaSlots, nextMetaSlot, c); } - return m->id; + return m->slot; } -uint32_t Program::GetOrAssignMetaID(DebugLocation &l) +uint32_t Program::GetOrAssignMetaSlot(rdcarray &metaSlots, uint32_t &nextMetaSlot, + DebugLocation &l) { - if(l.id != ~0U) - return l.id; + if(l.slot != ~0U) + return l.slot; - l.id = m_NextMetaID++; + l.slot = nextMetaSlot++; if(l.scope) - GetOrAssignMetaID(l.scope); + GetOrAssignMetaSlot(metaSlots, nextMetaSlot, l.scope); if(l.inlinedAt) - GetOrAssignMetaID(l.inlinedAt); + GetOrAssignMetaSlot(metaSlots, nextMetaSlot, l.inlinedAt); - return l.id; -} - -const DXIL::Type *Program::GetVoidType(bool precache) -{ - if(m_VoidType) - return m_VoidType; - - for(size_t i = 0; i < m_Types.size(); i++) - { - if(m_Types[i].isVoid()) - { - m_VoidType = &m_Types[i]; - break; - } - } - - if(!m_VoidType && !precache) - RDCERR("Couldn't find void type"); - - return m_VoidType; -} - -const DXIL::Type *Program::GetBoolType(bool precache) -{ - if(m_BoolType) - return m_BoolType; - - for(size_t i = 0; i < m_Types.size(); i++) - { - if(m_Types[i].type == Type::Scalar && m_Types[i].scalarType == Type::Int && - m_Types[i].bitWidth == 1) - { - m_BoolType = &m_Types[i]; - break; - } - } - - if(!m_BoolType && !precache) - RDCERR("Couldn't find bool type"); - - return m_BoolType; -} - -const Type *Program::GetInt32Type(bool precache) -{ - if(m_Int32Type) - return m_Int32Type; - - for(size_t i = 0; i < m_Types.size(); i++) - { - if(m_Types[i].type == Type::Scalar && m_Types[i].scalarType == Type::Int && - m_Types[i].bitWidth == 32) - { - m_Int32Type = &m_Types[i]; - break; - } - } - - if(!m_Int32Type && !precache) - RDCERR("Couldn't find int32 type"); - - return m_Int32Type; -} - -const Type *Program::GetInt8Type() -{ - if(m_Int8Type) - return m_Int8Type; - - for(size_t i = 0; i < m_Types.size(); i++) - { - if(m_Types[i].type == Type::Scalar && m_Types[i].scalarType == Type::Int && - m_Types[i].bitWidth == 8) - { - m_Int8Type = &m_Types[i]; - break; - } - } - - if(!m_Int8Type) - RDCERR("Couldn't find int8 type"); - - return m_Int8Type; + return l.slot; } const Type *Program::GetPointerType(const Type *type, Type::PointerAddrSpace addrSpace) const { - for(const Type &t : m_Types) - { - if(t.type == Type::Pointer && t.inner == type && t.addrSpace == addrSpace) - { - return &t; - } - } + for(const Type *t : m_Types) + if(t->type == Type::Pointer && t->inner == type && t->addrSpace == addrSpace) + return t; return NULL; } @@ -2447,4 +2298,465 @@ Metadata::~Metadata() SAFE_DELETE(dwarf); 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; + reset((Constant *)g->initialiser); +} + +void LLVMOrderAccumulator::reset(Alias *a) +{ + a->id = unvisitedValueId; + reset(a->val); +} + +void LLVMOrderAccumulator::reset(Constant *c) +{ + if(!c || c->id == unvisitedValueId) + return; + + c->id = unvisitedValueId; + c->refCount = 0; + if(c->isCast()) + { + reset(c->getInner()); + } + else if(c->isCompound()) + { + for(Value *v : c->getMembers()) + reset((Value *)v); + } +} + +void LLVMOrderAccumulator::reset(Block *b) +{ + b->id = unvisitedValueId; +} + +void LLVMOrderAccumulator::reset(Metadata *m) +{ + if(!m || m->id == unvisitedValueId) + return; + m->id = unvisitedValueId; + + reset(m->value); + + for(Metadata *c : m->children) + reset(c); +} + +void LLVMOrderAccumulator::reset(Instruction *i) +{ + if(!i || i->id == unvisitedValueId) + return; + + i->id = unvisitedValueId; + + for(Value *a : i->args) + reset(a); + + for(const rdcpair &m : i->getAttachedMeta()) + reset(m.second); +} + +void LLVMOrderAccumulator::reset(Function *f) +{ + f->id = unvisitedValueId; + for(Instruction *i : f->args) + reset(i); + for(Instruction *i : f->instructions) + reset(i); + for(Block *b : f->blocks) + reset(b); + for(rdcpair &m : f->attachedMeta) + reset(m.second); +} + +void LLVMOrderAccumulator::reset(Value *v) +{ + if(Constant *c = cast(v)) + reset(c); + else if(Instruction *i = cast(v)) + reset(i); + else if(Block *b = cast(v)) + reset(b); + else if(GlobalVar *g = cast(v)) + reset(g); + else if(Metadata *m = cast(v)) + reset(m); + else if(Function *f = cast(v)) + reset(f); + else if(Alias *a = cast(v)) + reset(a); +} + +void LLVMOrderAccumulator::processGlobals(Program *prog) +{ + // reset all IDs, so we know if we're encountering a new value/metadata or not when walking + for(Type *t : prog->m_Types) + t->id = unvisitedTypeId; + for(GlobalVar *v : prog->m_GlobalVars) + reset(v); + for(Alias *a : prog->m_Aliases) + reset(a); + for(Metadata *m : prog->m_NamedMeta) + reset(m); + for(Function *f : prog->m_Functions) + reset(f); + + // 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) + { + accumulateTypePrintOrder(g->type); + if(g->initialiser) + accumulateTypePrintOrder(g->initialiser->type); + } + + for(const Alias *a : prog->m_Aliases) + { + accumulateTypePrintOrder(a->type); + accumulateTypePrintOrder(a->val->type); + } + + // use same array to avoid resizes, but we clear it each time instead of keeping a mega-list to + // reduce the cost of lookups + rdcarray visited; + visited.reserve(128); + + for(const Function *func : prog->m_Functions) + { + accumulateTypePrintOrder(func->type); + for(const Instruction *arg : func->args) + accumulateTypePrintOrder(arg->type); + for(const Instruction *inst : func->instructions) + { + accumulateTypePrintOrder(inst->type); + for(size_t a = 0; a < inst->args.size(); a++) + if(inst->args[a]->kind() != ValueKind::Instruction) + accumulateTypePrintOrder(inst->args[a]->type); + + for(size_t m = 0; m < inst->getAttachedMeta().size(); m++) + { + visited.clear(); + accumulateTypePrintOrder(visited, inst->getAttachedMeta()[m].second); + } + } + } + + for(Metadata *meta : prog->m_NamedMeta) + { + visited.clear(); + accumulateTypePrintOrder(visited, meta); + } + + for(const GlobalVar *g : prog->m_GlobalVars) + accumulate(g); + + 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); + + for(const Alias *a : prog->m_Aliases) + accumulate(a->val); + + for(const Value *v : prog->m_ValueSymtabOrder) + accumulate(v); + + assignTypeId(prog->m_MetaType); + + for(size_t i = 0; i < prog->m_NamedMeta.size(); i++) + { + // named meta node itself doesn't go into meta list, so manually iterate children here + for(const Metadata *child : prog->m_NamedMeta[i]->children) + accumulate(child); + + // reset its id though so we don't permanently mark named meta as unvisited and be unable to + // reset all meta again + prog->m_NamedMeta[i]->id = Value::NoID; + } + + // accumulate metadata in functions, and constants referenced from there + for(const Function *func : prog->m_Functions) + { + for(const Instruction *arg : func->args) + assignTypeId(arg->type); + for(size_t m = 0; m < func->attachedMeta.size(); m++) + accumulate(func->attachedMeta[m].second); + for(const Instruction *inst : func->instructions) + { + for(size_t a = 0; a < inst->args.size(); a++) + { + assignTypeId(inst->args[a]->type); + accumulate(cast(inst->args[a])); + } + assignTypeId(inst->type); + for(size_t m = 0; m < inst->getAttachedMeta().size(); m++) + accumulate(inst->getAttachedMeta()[m].second); + } + } + + numConsts = values.size() - firstConst; + sortConsts = !prog->m_Uselists; + + if(sortConsts) + { + // mimic LLVM's sorting, by type ID then refcount + std::stable_sort(values.begin() + firstConst, values.end(), [](const Value *a, const Value *b) { + const Constant *ca = cast(a); + const Constant *cb = cast(b); + if(ca->type->id != cb->type->id) + return ca->type->id < cb->type->id; + + return ca->refCount > cb->refCount; + }); + + // int or int vectors before everything else + std::partition(values.begin() + firstConst, values.end(), + [](const Value *a) { return a->type->scalarType == Type::Int; }); + + // reassign value IDs after sort + for(size_t i = firstConst; i < firstConst + numConsts; i++) + { + Value *value = (Value *)values[i]; + RDCASSERT(value->id >= firstConst && value->id < firstConst + numConsts, value->id, + firstConst, numConsts); + value->id = i; + } + } +} + +void LLVMOrderAccumulator::processFunction(Function *f) +{ + const Function &func = *f; + + functionWaterMark = values.size(); + + for(size_t j = 0; j < func.args.size(); j++) + accumulate(func.args[j]); + + firstFuncConst = values.size(); + + for(const Instruction *inst : func.instructions) + { + for(size_t a = 0; a < inst->args.size(); a++) + accumulate(cast(inst->args[a])); + } + + numFuncConsts = values.size() - firstFuncConst; + + if(sortConsts) + { + // mimic LLVM's sorting, by type ID then refcount + std::stable_sort(values.begin() + firstFuncConst, values.end(), + [](const Value *a, const Value *b) { + const Constant *ca = cast(a); + const Constant *cb = cast(b); + if(ca->type->id != cb->type->id) + return ca->type->id < cb->type->id; + + return ca->refCount > cb->refCount; + }); + + std::partition(values.begin() + firstFuncConst, values.end(), + [](const Value *a) { return a->type->scalarType == Type::Int; }); + + // reassign value IDs after sort + for(size_t i = firstFuncConst; i < firstFuncConst + numFuncConsts; i++) + { + Value *value = (Value *)values[i]; + RDCASSERT(value->id >= firstFuncConst && value->id < firstFuncConst + numFuncConsts, + value->id, firstFuncConst, numFuncConsts); + value->id = i; + } + } + + for(size_t j = 0; j < func.blocks.size(); j++) + func.blocks[j]->id = j; + + uint32_t slot = 0; + uint32_t curBlock = 0; + + if(!func.blocks.empty() && func.blocks[0]->name.empty()) + func.blocks[0]->slot = slot++; + + for(Instruction *inst : func.instructions) + { + RDCASSERT(curBlock < func.blocks.size()); + + for(size_t m = 0; m < inst->getAttachedMeta().size(); m++) + accumulate(inst->getAttachedMeta()[m].second); + + for(size_t a = 0; a < inst->args.size(); a++) + if(inst->args[a]->kind() == ValueKind::Constant) + accumulate(inst->args[a]); + + if(inst->type->isVoid()) + { + inst->id = Value::NoID; + } + else + { + accumulate(inst); + + if(inst->getName().isEmpty()) + inst->slot = slot++; + } + + if(inst->op == Operation::Branch || inst->op == Operation::Unreachable || + inst->op == Operation::Switch || inst->op == Operation::Ret) + { + curBlock++; + + if(curBlock < func.blocks.size() && func.blocks[curBlock]->name.empty()) + func.blocks[curBlock]->slot = slot++; + } + } +} + +void LLVMOrderAccumulator::exitFunction() +{ + values.resize(functionWaterMark); +} + +void LLVMOrderAccumulator::accumulateTypePrintOrder(rdcarray &visited, + const Metadata *m) +{ + // metadata can be self-referential (why???) so need to check if we have visited this one to avoid + // infinite recursion. We don't set the ID as a flag since then we'd need a type-only reset pass. + // Blech + if(visited.contains(m)) + return; + + visited.push_back(m); + + accumulateTypePrintOrder(m->type); + + if(m->value) + accumulateTypePrintOrder(m->value->type); + + for(const Metadata *c : m->children) + if(c) + accumulateTypePrintOrder(visited, c); +} + +void LLVMOrderAccumulator::accumulateTypePrintOrder(const Type *t) +{ + if(!t || printOrderTypes.contains(t)) + return; + + Type *type = (Type *)t; + + // LLVM doesn't do quite a depth-first search for ordering its types for *printing*, so we + // replicate its search order to ensure types are printed in the same order. + rdcarray workingSet; + workingSet.push_back(type); + do + { + const Type *cur = workingSet.back(); + workingSet.pop_back(); + + printOrderTypes.push_back(cur); + + for(size_t i = 0; i < cur->members.size(); i++) + { + const Type *member = cur->members[cur->members.size() - 1 - i]; + if(!printOrderTypes.contains(member) && !workingSet.contains(member)) + { + workingSet.push_back((Type *)member); + } + } + + if(cur->inner && !printOrderTypes.contains(cur->inner) && !workingSet.contains(cur->inner)) + { + workingSet.push_back((Type *)cur->inner); + } + } while(!workingSet.empty()); +} + +void LLVMOrderAccumulator::assignTypeId(const Type *t) +{ + if(!t || t->id != unvisitedTypeId) + return; + + assignTypeId((Type *)t->inner); + for(size_t i = 0; i < t->members.size(); i++) + assignTypeId((Type *)t->members[i]); + + Type *type = (Type *)t; + type->id = types.size() & 0xffff; + types.push_back(t); +} + +void LLVMOrderAccumulator::accumulate(const Value *v) +{ + Value *value = (Value *)v; + if(!v || v->id != unvisitedValueId) + { + Constant *c = cast(value); + if(c) + c->refCount++; + return; + } + + RDCASSERT(v->kind() != ValueKind::Metadata); + + assignTypeId(value->type); + + value->id = visitedValueId; + + if(Constant *c = cast(value)) + { + if(c->isCast()) + { + accumulate(c->getInner()); + } + else if(c->isCompound()) + { + for(Value *m : c->getMembers()) + accumulate(m); + } + + c->refCount = 1; + } + + value->id = values.size(); + values.push_back(v); +} + +void LLVMOrderAccumulator::accumulate(const Metadata *m) +{ + if(!m || m->id != unvisitedValueId) + return; + + Metadata *meta = (Metadata *)m; + meta->id = visitedValueId; + + for(const Metadata *c : m->children) + if(c) + accumulate(c); + + if(const Constant *c = cast(m->value)) + accumulate(c); + + meta->id = metadata.size(); + metadata.push_back(meta); +} + }; // namespace DXIL diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode.h b/renderdoc/driver/shaders/dxil/dxil_bytecode.h index 34a0d457c..b45a60b70 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode.h +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode.h @@ -28,6 +28,7 @@ #include "api/replay/apidefs.h" #include "api/replay/rdcstr.h" +#include "common/common.h" #include "driver/dx/official/d3dcommon.h" #include "driver/shaders/dxbc/dxbc_common.h" @@ -40,6 +41,25 @@ struct BlockOrRecord; namespace DXIL { +struct BumpAllocator +{ + BumpAllocator(size_t totalSize); + ~BumpAllocator(); + void *alloc(size_t sz); + + template + T *alloc() + { + void *mem = alloc(sizeof(T)); + return new(mem) T(); + } + +private: + byte *base, *cur; + rdcarray storage; + size_t m_BlockSize; +}; + struct ProgramHeader { uint16_t ProgramVersion; @@ -88,9 +108,11 @@ struct Type ImmediateCBuffer = 5, }; + static void *operator new(size_t count, BumpAllocator &b) { return b.alloc(count); } + static void operator delete(void *ptr, BumpAllocator &b) {} bool isVoid() const { return type == Scalar && scalarType == Void; } rdcstr toString() const; - rdcstr declFunction(rdcstr funcName, const rdcarray &args, + rdcstr declFunction(rdcstr funcName, const rdcarray &args, const AttributeSet *attrs) const; // for scalars, arrays, vectors, pointers @@ -111,109 +133,6 @@ struct Type rdcarray members; // the members for a struct, the parameters for functions }; -enum class ValueType -{ - Unknown, - Function, - GlobalVar, - Alias, - Constant, - Instruction, - Metadata, - Literal, - BasicBlock, -}; - -struct Function; -struct GlobalVar; -struct Alias; -struct Constant; -struct Instruction; -struct Metadata; -struct Block; -struct Type; - -struct Value -{ - enum ForwardRefTag - { - ForwardRef - }; - Value() : type(ValueType::Unknown), literal(0) {} - explicit Value(ForwardRefTag, const Value *value) : type(ValueType::Unknown), value(value) {} - explicit Value(const Function *function) : type(ValueType::Function), function(function) {} - explicit Value(const GlobalVar *global) : type(ValueType::GlobalVar), global(global) {} - explicit Value(const Alias *alias) : type(ValueType::Alias), alias(alias) {} - explicit Value(const Constant *constant) : type(ValueType::Constant), constant(constant) {} - explicit Value(const Instruction *instruction) - : type(ValueType::Instruction), instruction(instruction) - { - } - explicit Value(const Metadata *meta) : type(ValueType::Metadata), meta(meta) {} - explicit Value(const Block *block) : type(ValueType::BasicBlock), block(block) {} - explicit Value(uint64_t literal) : type(ValueType::Literal), literal(literal) {} - bool empty() const { return type == ValueType::Unknown && literal == 0; } - const Type *GetType() const; - rdcstr toString(bool withType = false) const; - - bool operator==(const Value &o) { return type == o.type && literal == o.literal; } - ValueType type; - union - { - const Value *value; - const Function *function; - const GlobalVar *global; - const Alias *alias; - const Constant *constant; - const Instruction *instruction; - const Metadata *meta; - const Block *block; - uint64_t literal; - }; -}; - -enum class GlobalFlags : uint32_t -{ - NoFlags = 0, - ExternalLinkage = 1, - AvailableExternallyLinkage = 2, - LinkOnceAnyLinkage = 3, - LinkOnceODRLinkage = 4, - WeakAnyLinkage = 5, - WeakODRLinkage = 6, - AppendingLinkage = 7, - InternalLinkage = 8, - PrivateLinkage = 9, - ExternalWeakLinkage = 10, - CommonLinkage = 11, - LinkageMask = 0xf, - IsConst = 0x10, - IsExternal = 0x20, - LocalUnnamedAddr = 0x40, - GlobalUnnamedAddr = 0x80, - IsAppending = 0x100, - ExternallyInitialised = 0x200, -}; - -BITMASK_OPERATORS(GlobalFlags); - -struct GlobalVar -{ - rdcstr name; - const Type *type = NULL; - uint64_t align = 0; - int32_t section = -1; - GlobalFlags flags = GlobalFlags::NoFlags; - const Constant *initialiser = NULL; -}; - -struct Alias -{ - rdcstr name; - const Type *type = NULL; - Value val; -}; - // this enum is ordered to match the serialised order of these attributes enum class Attribute : uint64_t { @@ -430,22 +349,392 @@ inline uint64_t EncodeCast(Operation op) } } -struct Constant +enum class ValueKind : uint32_t { - Constant() = default; - Constant(const Type *t, uint32_t v) : type(t) { val.u32v[0] = v; } + ForwardReferencePlaceholder, + Literal, + Alias, + Constant, + GlobalVar, + Metadata, + Instruction, + Function, + BasicBlock, +}; + +struct Type; + +struct PlaceholderValue; + +struct Value +{ const Type *type = NULL; - ShaderValue val = {}; - rdcarray members; - Value inner; - rdcstr str; - bool undef = false, nullconst = false, data = false; - Operation op = Operation::NoOp; + + // the ID for this value (two namespaces: values, and metadata) + // note, not all derived classes from Value have an id - e.g. void instructions, blocks, these + // don't have IDs because they don't go in the values array + // + // 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; + uint32_t id : 24; - // steal the last unused part of ShaderValue, used for identifying constants under patching - uint32_t getID() { return val.u32v[15]; } - void setID(uint32_t id) { val.u32v[15] = id; } rdcstr toString(bool withType = false) const; + + static void *operator new(size_t count, BumpAllocator &b) { return b.alloc(count); } + static void operator delete(void *ptr, BumpAllocator &b) {} + static void *operator new(size_t count, PlaceholderValue *v) { return v; } + static void operator delete(void *ptr, PlaceholderValue *v) {} + static void operator delete(void *ptr) {} + ValueKind kind() const { return valKind; } +protected: + Value(ValueKind k) : valKind(k), id(NoID) {} + ValueKind valKind : 8; + uint32_t flags = 0; +}; + +struct PlaceholderValue : public Value +{ +private: + friend struct ValueList; + + PlaceholderValue() : Value(ValueKind::ForwardReferencePlaceholder) {} + byte storage[64 - sizeof(Value)]; +}; + +// helper class to check at compile time that values will be able to be forward referenced as +// we conservatively allocate room for them. +template +struct ForwardReferencableValue : public Value +{ + static constexpr bool IsForwardReferenceable = true; + +protected: + ForwardReferencableValue(ValueKind k) : Value(k) + { + RDCCOMPILE_ASSERT(sizeof(T) <= sizeof(PlaceholderValue), + "Type is too large to be forward referenceable in-place"); + } +}; + +// loose wrapper around an array for value pointers. This doesn't use the underlying array size for +// anything but instead tracks the current latest value, so relative indices can be resolved against +// it while we can still future-allocate in the array for forward reference placeholders. +struct ValueList : private rdcarray +{ + ValueList(BumpAllocator &alloc) : rdcarray(), alloc(alloc) {} + Value *&operator[](size_t i) + { + resize_for_index(i); + RDCASSERT(at(i)); + return at(i); + } + void hintExpansion(size_t newValues) { reserve(lastValue + newValues); } + void beginFunction() { functionWatermark = lastValue; } + void endFunction() + { + resize(functionWatermark); + lastValue = functionWatermark; + } + size_t curValueIndex() const { return lastValue; } + Value *createPlaceholderValue(size_t i) + { + resize_for_index(i); + Value *ret = at(i); + if(ret) + { + RDCASSERT(ret->kind() == ValueKind::ForwardReferencePlaceholder); + return ret; + } + at(i) = ret = new(alloc) PlaceholderValue(); + return ret; + } + Value *getOrCreatePlaceholder(size_t i) + { + resize_for_index(i); + Value *ret = at(i); + if(ret) + return ret; + return createPlaceholderValue(i); + } + // alloc a value in-place to resolve forward references. Should always be paired with a call to + // addAllocedValue() once you're done creating the value + // if you're creating a 'Value' that isn't actually a value (e.g. instruction with no return + // value) then you can new it directly + template + T *nextValue() + { + RDCASSERT(!pendingValue); + RDCCOMPILE_ASSERT(typename T::IsForwardReferenceable, + "alloc'ing next value for non-forward-referenceable type"); + + pendingValue = true; + + // if this value was already allocated as a placeholder, re-use the memory to keep any pointers + // to it valid + if(lastValue < size() && at(lastValue)) + { + Value *v = at(lastValue); + RDCASSERT(v->kind() == ValueKind::ForwardReferencePlaceholder); + PlaceholderValue *memory = (PlaceholderValue *)v; + return new(memory) T(); + } + + // no placeholder, put it in place now and addValue() will be called to bump the count below + resize_for_index(lastValue); + T *ret = new(alloc) T(); + at(lastValue) = ret; + return ret; + } + void addValue(Value *v = NULL) + { + // either pendingValue should be true, or v should be true, but they shouldn't both be + if(pendingValue) + { + RDCASSERT(v == NULL); + pendingValue = false; + lastValue++; + } + else + { + RDCASSERT(v); + v->id = lastValue; + resize_for_index(lastValue); + RDCASSERTMSG("Forward reference being overwritten with new pointer", at(lastValue) == NULL); + at(lastValue) = v; + lastValue++; + } + } + size_t getRelativeBackwards(uint64_t ref) { return lastValue - (size_t)ref; } + size_t getRelativeForwards(uint64_t ref) { return lastValue + (size_t)ref; } +private: + BumpAllocator &alloc; + size_t functionWatermark = 0; + size_t lastValue = 0; + bool pendingValue = false; +}; + +template +T *cast(Value *v) +{ + if(v && v->kind() == T::Kind) + return (T *)v; + return NULL; +} + +template +const T *cast(const Value *v) +{ + if(v && v->kind() == T::Kind) + return (const T *)v; + return NULL; +} + +enum class GlobalFlags : uint32_t +{ + NoFlags = 0, + ExternalLinkage = 1, + AvailableExternallyLinkage = 2, + LinkOnceAnyLinkage = 3, + LinkOnceODRLinkage = 4, + WeakAnyLinkage = 5, + WeakODRLinkage = 6, + AppendingLinkage = 7, + InternalLinkage = 8, + PrivateLinkage = 9, + ExternalWeakLinkage = 10, + CommonLinkage = 11, + LinkageMask = 0xf, + IsConst = 0x10, + IsExternal = 0x20, + LocalUnnamedAddr = 0x40, + GlobalUnnamedAddr = 0x80, + IsAppending = 0x100, + ExternallyInitialised = 0x200, +}; + +BITMASK_OPERATORS(GlobalFlags); + +struct Literal : public ForwardReferencableValue +{ + static constexpr ValueKind Kind = ValueKind::Literal; + Literal(uint64_t v) : ForwardReferencableValue(Kind), literal(v) {} + uint64_t literal; +}; + +struct Alias : public ForwardReferencableValue +{ + static constexpr ValueKind Kind = ValueKind::Alias; + Alias() : ForwardReferencableValue(Kind) {} + rdcstr name; + Value *val = NULL; +}; + +struct Constant : public ForwardReferencableValue +{ + static constexpr ValueKind Kind = ValueKind::Constant; + Constant() : ForwardReferencableValue(Kind) { u64 = 0; } + Constant(const Type *t, uint32_t v) : ForwardReferencableValue(ValueKind::Constant) + { + type = t; + u64 = 0; + setValue(v); + } + Operation op = Operation::NoOp; + rdcstr str; + // used during encoding to sort constants by number of uses... + uint32_t refCount = 0; + + bool isUndef() const { return (flags & 0x1) != 0; } + bool isNULL() const { return (flags & 0x2) != 0; } + bool isData() const { return (flags & 0x4) != 0; } + bool isLiteral() const { return (flags & 0x10) != 0; } + bool isShaderVal() const { return (flags & 0x20) != 0; } + bool isCast() const { return (flags & 0x40) != 0; } + bool isCompound() const { return (flags & 0x80) != 0; } + void setUndef(bool u) + { + flags &= ~0x1; + flags |= u ? 0x1 : 0x0; + } + void setNULL(bool n) + { + flags &= ~0x2; + flags |= n ? 0x2 : 0x0; + } + void setData(bool d) + { + flags &= ~0x4; + flags |= d ? 0x4 : 0x0; + } + void setValue(uint32_t l) + { + flags &= ~0xf0; + flags |= 0x10; + u32 = l; + } + void setValue(uint64_t l) + { + flags &= ~0xf0; + flags |= 0x10; + u64 = l; + } + void setValue(int64_t l) + { + flags &= ~0xf0; + flags |= 0x10; + s64 = l; + } + void setValue(BumpAllocator &alloc, const ShaderValue &v) + { + flags &= ~0xf0; + flags |= 0x20; + val = alloc.alloc(); + *val = v; + } + void setInner(Value *i) + { + flags &= ~0xf0; + flags |= 0x40; + inner = i; + } + void setCompound(BumpAllocator &alloc, rdcarray &&m) + { + flags &= ~0xf0; + flags |= 0x80; + members = alloc.alloc>(); + new(members) rdcarray(m); + } + void setCompound(BumpAllocator &alloc, const rdcarray &m) + { + flags &= ~0xf0; + flags |= 0x80; + members = alloc.alloc>(); + new(members) rdcarray(m); + } + + uint32_t getU32() const + { + if(flags & 0x10) + return u32; + // silently return 0 for NULL/Undef constants + if(flags & 0x06) + return 0U; + RDCERR("Wrong type of constant being accessed"); + return 0U; + } + + uint64_t getU64() const + { + if(flags & 0x10) + return u64; + // silently return 0 for NULL/Undef constants + if(flags & 0x06) + return 0U; + RDCERR("Wrong type of constant being accessed"); + return 0U; + } + + int64_t getS64() const + { + if(flags & 0x10) + return s64; + // silently return 0 for NULL/Undef constants + if(flags & 0x06) + return 0; + RDCERR("Wrong type of constant being accessed"); + return 0U; + } + + const ShaderValue &getShaderVal() const + { + if(flags & 0x20) + return *val; + static ShaderValue empty; + RDCERR("Wrong type of constant being accessed"); + return empty; + } + + Value *getInner() const + { + if(flags & 0x40) + return inner; + RDCERR("No inner available"); + return NULL; + } + + const rdcarray &getMembers() const + { + if(flags & 0x80) + return *members; + static rdcarray empty; + RDCERR("No members available"); + return empty; + } + + rdcstr toString(bool withType = false) const; + +private: + union + { + uint64_t u64; + int64_t s64; + uint32_t u32; + ShaderValue *val; + Value *inner; + rdcarray *members; + }; +}; + +struct GlobalVar : public ForwardReferencableValue +{ + static constexpr ValueKind Kind = ValueKind::GlobalVar; + GlobalVar() : ForwardReferencableValue(Kind) {} + rdcstr name; + uint64_t align = 0; + int32_t section = -1; + GlobalFlags flags = GlobalFlags::NoFlags; + const Constant *initialiser = NULL; }; struct DIBase @@ -480,9 +769,11 @@ struct DIBase } }; +struct Metadata; + struct DebugLocation { - uint32_t id = ~0U; + uint32_t slot = ~0U; uint64_t line = 0; uint64_t col = 0; @@ -497,16 +788,20 @@ struct DebugLocation rdcstr toString() const; }; -struct Metadata +struct Metadata : public Value { + static constexpr ValueKind Kind = ValueKind::Metadata; + Metadata(size_t idx = 0xffffff) : Value(Kind) { id = idx; } ~Metadata(); - uint32_t id = ~0U; bool isDistinct = false, isConstant = false, isString = false; - Value value; + // only used for disassembly, the number given to metadata that's directly referenced. NOT the + // same as it's id (ha ha) + uint32_t slot = ~0U; + + Value *value = NULL; - const Type *type = NULL; rdcstr str; rdcarray children; DIBase *dwarf = NULL; @@ -516,6 +811,39 @@ struct Metadata rdcstr valString() const; }; +// loose wrapper around an array for metadata pointer. This creates metadata nodes on demand because +// they can be forward referenced (sigh...) +struct MetadataList : private rdcarray +{ + MetadataList(BumpAllocator &alloc) : rdcarray(), alloc(&alloc) {} + MetadataList() : rdcarray(), alloc(NULL) {} + Metadata *&operator[](size_t i) + { + resize_for_index(i); + if(at(i)) + return at(i); + RDCASSERT(alloc); + at(i) = new(*alloc) Metadata(i); + return at(i); + } + void hintExpansion(size_t newValues) { reserve(size() + newValues); } + void beginFunction() { functionWatermark = size(); } + void endFunction() { resize(functionWatermark); } + using rdcarray::size; + using rdcarray::back; + using rdcarray::empty; + using rdcarray::contains; + + Metadata *getDirect(uint64_t id) { return (*this)[size_t(id)]; } + Metadata *getOrNULL(uint64_t id) { return id ? (*this)[size_t(id - 1)] : NULL; } + rdcstr *getStringOrNULL(uint64_t id) { return id ? &(*this)[size_t(id - 1)]->str : NULL; } +private: + BumpAllocator *alloc = NULL; + size_t functionWatermark = 0; + size_t lastValue = 0; + bool pendingValue = false; +}; + struct NamedMetadata : public Metadata { rdcstr name; @@ -573,62 +901,112 @@ enum class InstructionFlags : uint32_t BITMASK_OPERATORS(InstructionFlags); +// pair of typedef rdcarray> AttachedMetadata; -struct Instruction +struct Function; + +struct Instruction : public ForwardReferencableValue { - Operation op = Operation::NoOp; - InstructionFlags opFlags = InstructionFlags::NoFlags; - - // common to all instructions - rdcstr name; + static constexpr ValueKind Kind = ValueKind::Instruction; + Instruction() : ForwardReferencableValue(Kind) {} uint32_t disassemblyLine = 0; - uint32_t resultID = ~0U; uint32_t debugLoc = ~0U; - uint32_t align = 0; - const Type *type = NULL; - rdcarray args; - AttachedMetadata attachedMeta; + Operation op = Operation::NoOp; + uint8_t align = 0; + // number assigned to instructions that don't have names and return a value, for disassembly + uint32_t slot = ~0U; + InstructionFlags &opFlags() { return (InstructionFlags &)flags; } + InstructionFlags opFlags() const { return (InstructionFlags)flags; } + rdcarray args; - // function calls - const AttributeSet *paramAttrs = NULL; - const Function *funcCall = NULL; + struct ExtraInstructionInfo + { + rdcstr name; + AttachedMetadata attachedMeta; + + // function calls + const AttributeSet *paramAttrs = NULL; + const Function *funcCall = NULL; + }; + + const rdcstr &getName() const + { + static rdcstr empty; + if(extraData) + return extraData->name; + return empty; + } + + const AttachedMetadata &getAttachedMeta() const + { + static AttachedMetadata empty; + if(extraData) + return extraData->attachedMeta; + return empty; + } + + const AttributeSet *getParamAttrs() const + { + if(extraData) + return extraData->paramAttrs; + return NULL; + } + + const Function *getFuncCall() const + { + if(extraData) + return extraData->funcCall; + return NULL; + } + + ExtraInstructionInfo &extra(BumpAllocator &alloc) + { + if(!extraData) + extraData = alloc.alloc(); + return *extraData; + } + +private: + ExtraInstructionInfo *extraData = NULL; }; -struct Block +struct Block : public ForwardReferencableValue { - uint32_t resultID = ~0U; - rdcstr name; + static constexpr ValueKind Kind = ValueKind::BasicBlock; + Block(const Type *labelType) : ForwardReferencableValue(Kind) { type = labelType; } + rdcinflexiblestr name; rdcarray preds; + uint32_t slot = ~0U; }; struct UselistEntry { bool block = false; - Value value; + Value *value; rdcarray shuffle; }; -struct Function +// functions are deliberately not forward referenceable since they're larger, and we shouldn't need +// to +struct Function : public Value { + static constexpr ValueKind Kind = ValueKind::Function; + Function() : Value(Kind) {} rdcstr name; - const Type *funcType = NULL; bool external = false; + bool sortedSymtab = true; const AttributeSet *attrs = NULL; uint64_t align = 0; - rdcarray args; - rdcarray instructions; - rdcarray values; + rdcarray args; + rdcarray instructions; - rdcarray valueSymtabOrder; - bool sortedSymtab = true; + rdcarray valueSymtabOrder; - rdcarray blocks; - rdcarray constants; - rdcarray metadata; + rdcarray blocks; rdcarray uselist; @@ -674,27 +1052,33 @@ public: rdcarray &locals) const override; const Metadata *GetMetadataByName(const rdcstr &name) const; - size_t GetMetadataCount() const { return m_Metadata.size() + m_NamedMeta.size(); } uint32_t GetDirectHeapAcessCount() const { return m_directHeapAccessCount; } protected: void MakeDisassemblyString(); - bool ParseDebugMetaRecord(const LLVMBC::BlockOrRecord &metaRecord, Metadata &meta); + void ParseConstant(ValueList &values, const LLVMBC::BlockOrRecord &constant); + bool ParseDebugMetaRecord(MetadataList &metadata, const LLVMBC::BlockOrRecord &metaRecord, + Metadata &meta); rdcstr GetDebugVarName(const DIBase *d); rdcstr GetFunctionScopeName(const DIBase *d); - rdcstr &GetValueSymtabString(const Value &v); + rdcstr GetValueSymtabString(Value *v); + void SetValueSymtabString(Value *v, const rdcstr &s); - uint32_t GetOrAssignMetaID(Metadata *m); - uint32_t GetOrAssignMetaID(DebugLocation &l); - const Type *GetVoidType(bool precache = false); - const Type *GetBoolType(bool precache = false); - const Type *GetInt32Type(bool precache = false); - const Type *GetInt8Type(); + uint32_t GetOrAssignMetaSlot(rdcarray &metaSlots, uint32_t &nextMetaSlot, Metadata *m); + uint32_t GetOrAssignMetaSlot(rdcarray &metaSlots, uint32_t &nextMetaSlot, + DebugLocation &l); + + const Type *GetVoidType() { return m_VoidType; } + const Type *GetBoolType() { return m_BoolType; } + const Type *GetInt32Type() { return m_Int32Type; } + const Type *GetInt8Type() { return m_Int8Type; } const Type *GetPointerType(const Type *type, Type::PointerAddrSpace addrSpace) const; bytebuf m_Bytes; + BumpAllocator alloc; + DXBC::ShaderType m_Type; uint32_t m_Major, m_Minor; uint32_t m_DXILVersion; @@ -702,47 +1086,91 @@ protected: rdcstr m_CompilerSig, m_EntryPoint, m_Profile; ShaderCompileFlags m_CompileFlags; - rdcarray m_GlobalVars; - rdcarray m_Functions; - rdcarray m_Aliases; - rdcarray m_Values; + const Type *m_CurParseType = NULL; + + rdcarray m_GlobalVars; + rdcarray m_Functions; + rdcarray m_Aliases; rdcarray m_Sections; uint32_t m_directHeapAccessCount = 0; rdcarray m_Kinds; - rdcarray m_ValueSymtabOrder; + rdcarray m_ValueSymtabOrder; bool m_SortedSymtab = true; - rdcarray m_Types; + rdcarray m_Types; const Type *m_VoidType = NULL; const Type *m_BoolType = NULL; const Type *m_Int32Type = NULL; const Type *m_Int8Type = NULL; + const Type *m_MetaType = NULL; + const Type *m_LabelType = NULL; rdcarray m_AttributeGroups; rdcarray m_AttributeSets; - rdcarray m_Constants; - - rdcarray m_Metadata; - rdcarray m_NamedMeta; - rdcarray m_NumberedMeta; - uint32_t m_NextMetaID = 0; + rdcarray m_NamedMeta; rdcarray m_DebugLocations; + bool m_Uselists = false; + rdcstr m_Triple, m_Datalayout; rdcstr m_Disassembly; friend struct OpReader; + friend class LLVMOrderAccumulator; }; bool needsEscaping(const rdcstr &name); rdcstr escapeString(const rdcstr &str); rdcstr escapeStringIfNeeded(const rdcstr &name); +class LLVMOrderAccumulator +{ +public: + // types in id order + rdcarray types; + // types in disassembly print order + rdcarray printOrderTypes; + // values in id order + rdcarray values; + // metadata in id order + rdcarray metadata; + + size_t firstConst; + size_t numConsts; + + void processGlobals(Program *p); + + size_t firstFuncConst; + size_t numFuncConsts; + + void processFunction(Function *f); + void exitFunction(); + +private: + size_t functionWaterMark; + bool sortConsts = true; + + void reset(GlobalVar *g); + void reset(Alias *a); + void reset(Constant *c); + void reset(Metadata *m); + void reset(Function *f); + void reset(Block *b); + void reset(Instruction *i); + void reset(Value *v); + + void accumulate(const Value *v); + void accumulate(const Metadata *m); + void accumulateTypePrintOrder(const Type *t); + void accumulateTypePrintOrder(rdcarray &visited, const Metadata *m); + void assignTypeId(const Type *t); +}; + }; // namespace DXIL DECLARE_REFLECTION_ENUM(DXIL::Attribute); diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp b/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp index 80109dbac..2c1b3acdc 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp @@ -35,469 +35,77 @@ typedef DXC_API_IMPORT HRESULT(__stdcall *pDxcCreateInstance)(REFCLSID rclsid, R namespace DXIL { -ProgramEditor::ProgramEditor(const DXBC::DXBCContainer *container, size_t reservationSize, - bytebuf &outBlob) +ProgramEditor::ProgramEditor(const DXBC::DXBCContainer *container, bytebuf &outBlob) : Program(container->GetNonDebugDXILByteCode(), container->GetNonDebugDXILByteCodeSize()), m_OutBlob(outBlob) { m_OutBlob = container->GetShaderBlob(); - // swap into preserved storage - m_Functions.swap(m_OldFunctions); - m_Types.swap(m_OldTypes); - m_Constants.swap(m_OldConstants); - m_Metadata.swap(m_OldMetadata); - - // give all objects an ID so we can find them afterwards uniquely and easy. - for(size_t i = 0; i < m_OldTypes.size(); i++) - m_OldTypes[i].id = (uint16_t)i; - - for(Function &f : m_OldFunctions) + if(!m_VoidType) { - // This ID is only used in disassembly so we can freely overwrite it - for(size_t i = 0; i < f.instructions.size(); i++) - f.instructions[i].resultID = (uint32_t)i; - for(size_t i = 0; i < f.args.size(); i++) - f.args[i].resultID = (uint32_t)i; - - for(size_t i = 0; i < f.blocks.size(); i++) - f.blocks[i].resultID = (uint32_t)i; - - for(size_t i = 0; i < f.metadata.size(); i++) - f.metadata[i].id = (uint32_t)i; - for(size_t i = 0; i < f.constants.size(); i++) - f.constants[i].setID((uint32_t)i); + Type *t = new(alloc) Type; + m_VoidType = t; + t->type = Type::Scalar; + t->scalarType = Type::Void; + m_Types.push_back(t); + } + if(!m_BoolType) + { + Type *t = new(alloc) Type; + m_BoolType = t; + t->type = Type::Scalar; + t->scalarType = Type::Int; + t->bitWidth = 1; + m_Types.push_back(t); + } + if(!m_Int32Type) + { + Type *t = new(alloc) Type; + m_Int32Type = t; + t->type = Type::Scalar; + t->scalarType = Type::Int; + t->bitWidth = 32; + m_Types.push_back(t); + } + if(!m_Int8Type) + { + Type *t = new(alloc) Type; + m_Int8Type = t; + t->type = Type::Scalar; + t->scalarType = Type::Int; + t->bitWidth = 8; + m_Types.push_back(t); } - for(size_t i = 0; i < m_OldMetadata.size(); i++) - m_OldMetadata[i].id = (uint32_t)i; + // enumerate constants for deduplicating. The encoding automatically partitions these into global + // (if they're referenced globally) and function, we don't need to. + // + // We use the accumulator here not because it's efficient, but because it handles all the + // potential cycles that llvm puts in :( - for(size_t i = 0; i < m_OldConstants.size(); i++) - m_OldConstants[i].setID((uint32_t)i); + LLVMOrderAccumulator accum; + accum.processGlobals(this); - // now duplicate. Any copied pointers underneath these elements will *still be valid* and - // importantly *still point to the right element even if we edit the arrays*, because they're now - // pointing into the old arrays above. Just the indices will be wrong, which we only need when - // encoding - m_Functions = m_OldFunctions; - m_Types = m_OldTypes; - m_Constants = m_OldConstants; - m_Metadata = m_OldMetadata; + for(size_t idx = accum.firstConst; idx < accum.firstConst + accum.numConsts; idx++) + m_Constants.push_back((Constant *)cast(accum.values[idx])); - // reserve enough space so that these arrays don't resize out from under us - for(Function &f : m_Functions) + for(Function *f : m_Functions) { - f.instructions.reserve(f.instructions.size() * 4 + reservationSize * 4); - f.constants.reserve(f.constants.size() * 2 + reservationSize * 4); + accum.processFunction(f); + for(size_t idx = accum.firstFuncConst; idx < accum.firstFuncConst + accum.numFuncConsts; idx++) + m_Constants.push_back((Constant *)cast(accum.values[idx])); + accum.exitFunction(); } - m_Functions.reserve(m_Functions.size() * 2 + reservationSize); - m_Types.reserve(m_Types.size() * 2 + reservationSize); - m_Constants.reserve(m_Constants.size() * 2 + reservationSize); - m_Metadata.reserve(m_Metadata.size() * 2 + reservationSize); - -#if ENABLED(RDOC_DEVEL) - m_DebugFunctionsData = m_Functions.data(); - m_DebugFunctions.resize(m_Functions.size()); - for(size_t i = 0; i < m_DebugFunctions.size(); i++) - { - m_DebugFunctions[i].instructions = m_Functions[i].instructions.data(); - m_DebugFunctions[i].constants = m_Functions[i].constants.data(); - } - m_DebugTypesData = m_Types.data(); - m_DebugConstantsData = m_Constants.data(); - m_DebugMetadataData = m_Metadata.data(); -#endif - - // reset cached types - m_VoidType = NULL; - m_BoolType = NULL; - m_Int32Type = NULL; - m_Int8Type = NULL; -} - -#define IN_ARRAY(array, ptr) (array.begin() <= ptr && ptr < array.end()) - -#define GET_META_ID(a) (a)->id -#define GET_CONST_ID(a) (a)->getID() -#define GET_INST_ID(a) (a)->resultID -#define GET_TYPE_ID(a) (a)->id - -// this is our main fixup algorithm. We start from the old ID (which was the index in the old array) -// then start searching forward until we find it, or another valid ID. If we find another valid ID -// that's greater we switch and go backwards (less likely since we don't expect to remove much) to -// find the new location, and then get the updated pointer -// -// we skip any items with an ID of ~0U as these are new items, so we don't know which side our one -// will be - again we assume it will be later as we usually insert, pushing indices later -#define REPOINT_PTR(array, ptr, getId) \ - uint32_t idx = getId(ptr); \ - RDCASSERT(idx < 0x80000000U); \ - if(idx >= array.size()) \ - idx = uint32_t(array.size() - 1); \ - \ - /* search forward first */ \ - for(; idx < array.size(); idx++) \ - { \ - if(getId(&array[idx]) == getId(ptr)) \ - break; \ - \ - if(getId(&array[idx]) >= 0x80000000U) \ - continue; \ - \ - if(getId(&array[idx]) > getId(ptr)) \ - break; \ - } \ - \ - /* if we didn't find it, search back */ \ - if(getId(&array[idx]) != getId(ptr)) \ - { \ - idx = getId(ptr); \ - for(; idx > 0; idx--) \ - { \ - if(getId(&array[idx]) == getId(ptr)) \ - break; \ - \ - if(getId(&array[idx]) >= 0x80000000U) \ - continue; \ - \ - if(getId(&array[idx]) < getId(ptr)) \ - break; \ - } \ - } \ - \ - decltype(array)::value_type *result = NULL; \ - if(getId(&array[idx]) == getId(ptr)) \ - result = &array[idx]; \ - else \ - RDCERR("Couldn't find item in new array!"); - -void ProgramEditor::Fixup(Value &v, Function *oldf, Function *newf) -{ - switch(v.type) - { - case ValueType::Constant: Fixup((Constant *&)v.constant, oldf, newf); break; - case ValueType::Metadata: Fixup((Metadata *&)v.meta, oldf, newf); break; - case ValueType::Instruction: Fixup((Instruction *&)v.instruction, oldf, newf); break; - case ValueType::BasicBlock: Fixup((Block *&)v.block, oldf, newf); break; - case ValueType::Function: - Fixup((Function *&)v.function); - break; - // other value types shouldn't need fixups - default: break; - } -} - -void ProgramEditor::Fixup(Function *&f) -{ - if(!f) - return; - - if(IN_ARRAY(m_OldFunctions, f)) - { - // most of the time we won't modify the functions, check if it's at the same index - size_t idx = f - m_OldFunctions.begin(); - - if(m_Functions[idx].name == f->name) - { - f = &m_Functions[idx]; - } - else - { - // otherwise just do a linear search, we don't expect too many functions - for(Function &newf : m_Functions) - { - if(newf.name == f->name) - { - f = &newf; - break; - } - } - } - } - -#if ENABLED(RDOC_DEVEL) - RDCASSERT(IN_ARRAY(m_Functions, f)); -#endif -} - -void ProgramEditor::Fixup(Type *&t) -{ - if(!t) - return; - - if(IN_ARRAY(m_OldTypes, t)) - { - REPOINT_PTR(m_Types, t, GET_TYPE_ID); - - if(result) - t = result; - } - -#if ENABLED(RDOC_DEVEL) - RDCASSERT(IN_ARRAY(m_Types, t)); -#endif -} - -void ProgramEditor::Fixup(Block *&b, Function *oldf, Function *newf) -{ - if(IN_ARRAY(oldf->blocks, b)) - { - REPOINT_PTR(newf->blocks, b, GET_INST_ID); - - if(result) - b = result; - } - -#if ENABLED(RDOC_DEVEL) - RDCASSERT(IN_ARRAY(newf->blocks, b)); -#endif -} - -// this variant fixes up the pointer itself only, but doesn't recurse. We don't recurse because we -// have a parent loop that iterates over all the instructions in the new function -void ProgramEditor::Fixup(Instruction *&i, Function *oldf, Function *newf) -{ - if(!i) - return; - - if(IN_ARRAY(oldf->args, i)) - { - REPOINT_PTR(newf->args, i, GET_INST_ID); - - if(result) - i = result; - } - - if(IN_ARRAY(oldf->instructions, i)) - { - REPOINT_PTR(newf->instructions, i, GET_INST_ID); - - if(result) - i = result; - } - -#if ENABLED(RDOC_DEVEL) - RDCASSERT(IN_ARRAY(newf->args, i) || IN_ARRAY(newf->instructions, i)); -#endif -} - -void ProgramEditor::Fixup(Constant *&c, Function *oldf, Function *newf) -{ - if(!c) - return; - - if(IN_ARRAY(m_OldConstants, c)) - { - REPOINT_PTR(m_Constants, c, GET_CONST_ID); - - if(result) - c = result; - } - - if(oldf && IN_ARRAY(oldf->constants, c)) - { - REPOINT_PTR(newf->constants, c, GET_CONST_ID); - - if(result) - c = result; - } - -#if ENABLED(RDOC_DEVEL) - RDCASSERT(IN_ARRAY(m_Constants, c) || (newf && IN_ARRAY(newf->constants, c))); -#endif -} - -void ProgramEditor::Fixup(Metadata *&m, Function *oldf, Function *newf) -{ - if(!m) - return; - - if(IN_ARRAY(m_OldMetadata, m)) - { - REPOINT_PTR(m_Metadata, m, GET_META_ID); - - if(result) - m = result; - } - - if(oldf && IN_ARRAY(oldf->metadata, m)) - { - REPOINT_PTR(newf->metadata, m, GET_META_ID); - - if(result) - m = result; - } - -#if ENABLED(RDOC_DEVEL) - RDCASSERT(IN_ARRAY(m_Metadata, m) || (newf && IN_ARRAY(newf->metadata, m))); -#endif + m_Constants.removeIf([](Constant *a) { return a == NULL; }); } ProgramEditor::~ProgramEditor() { -#if ENABLED(RDOC_DEVEL) - RDCASSERTMSG("Function storage has resized", m_DebugFunctionsData == m_Functions.data()); - for(size_t i = 0; i < m_DebugFunctions.size(); i++) - { - RDCASSERTMSG("Instruction storage has resized", - m_DebugFunctions[i].instructions == m_Functions[i].instructions.data()); - RDCASSERTMSG("Function constant storage has resized", - m_DebugFunctions[i].constants == m_Functions[i].constants.data()); - } - RDCASSERTMSG("Types storage has resized", m_DebugTypesData == m_Types.data()); - RDCASSERTMSG("Constants storage has resized", m_DebugConstantsData == m_Constants.data()); - RDCASSERTMSG("Metadata storage has resized", m_DebugMetadataData == m_Metadata.data()); -#endif - - // fixup pass. Find any pointers into m_Old* and repoint them to the appropriate corresponding - // element in m_* - - for(GlobalVar &v : m_GlobalVars) - { - if(v.initialiser && !IN_ARRAY(m_Constants, v.initialiser)) - Fixup(v.initialiser); - - Fixup(v.type); - } - - for(Type &t : m_Types) - { - Fixup(t.inner); - - for(size_t i = 0; i < t.members.size(); i++) - Fixup(t.members[i]); - } - - for(Alias &a : m_Aliases) - { - Fixup(a.type); - Fixup(a.val); - } - - for(Constant &c : m_Constants) - { - Fixup(c.type); - Fixup(c.inner); - for(size_t i = 0; i < c.members.size(); i++) - Fixup(c.members[i]); - } - - for(Metadata &m : m_Metadata) - { - Fixup(m.type); - - Fixup(m.value); - - for(size_t i = 0; i < m.children.size(); i++) - Fixup(m.children[i]); - } - - for(Metadata &m : m_NamedMeta) - { - Fixup(m.type); - - Fixup(m.value); - - for(size_t i = 0; i < m.children.size(); i++) - Fixup(m.children[i]); - } - - for(size_t fidx = 0; fidx < m_Functions.size(); fidx++) - { - Function &oldf = m_OldFunctions[fidx]; - Function &f = m_Functions[fidx]; - - Fixup(f.funcType); - - for(Instruction &i : f.args) - Fixup(i.type); - - for(Instruction &i : f.instructions) - { - Fixup(i.type); - - for(Value &v : i.args) - Fixup(v, &oldf, &f); - - for(rdcpair &m : i.attachedMeta) - Fixup(m.second, &oldf, &f); - - Fixup(i.funcCall); - } - - for(Constant &c : f.constants) - { - Fixup(c.type); - Fixup(c.inner, &oldf, &f); - for(size_t i = 0; i < c.members.size(); i++) - Fixup(c.members[i], &oldf, &f); - } - - for(Value &v : f.values) - Fixup(v, &oldf, &f); - - for(Block &b : f.blocks) - { - for(size_t i = 0; i < b.preds.size(); i++) - Fixup(b.preds[i], &oldf, &f); - } - - for(Metadata &m : f.metadata) - { - Fixup(m.type); - - Fixup(m.value, &oldf, &f); - - for(size_t i = 0; i < m.children.size(); i++) - Fixup(m.children[i], &oldf, &f); - } - - for(UselistEntry &u : f.uselist) - Fixup(u.value, &oldf, &f); - - for(Value &v : f.valueSymtabOrder) - Fixup(v, &oldf, &f); - } - - for(Value &v : m_Values) - Fixup(v); - - for(Value &v : m_ValueSymtabOrder) - Fixup(v); - -#if ENABLED(RDOC_DEVEL) -#define CLEAR_THEN_FILL(array) \ - { \ - sz = array.size(); \ - array.clear(); \ - memset(array.data(), 0xfe, sz); \ - } - size_t sz; - // clear, then fill the old arrays with garbage, so we can be more sure we're not accessing stale - // data - for(Function &f : m_OldFunctions) - { - CLEAR_THEN_FILL(f.instructions); - CLEAR_THEN_FILL(f.constants); - CLEAR_THEN_FILL(f.metadata); - } - - CLEAR_THEN_FILL(m_OldFunctions); - CLEAR_THEN_FILL(m_OldTypes); - CLEAR_THEN_FILL(m_OldConstants); - CLEAR_THEN_FILL(m_OldMetadata); -#endif - - // cache known types for encoding - GetVoidType(true); - GetBoolType(true); - GetInt32Type(true); - // replace the DXIL bytecode in the container with DXBC::DXBCContainer::ReplaceChunk(m_OutBlob, DXBC::FOURCC_DXIL, EncodeProgram()); -#if ENABLED(RDOC_DEVEL) && 0 +#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 // functionality that we only need to create blobs @@ -591,20 +199,59 @@ ProgramEditor::~ProgramEditor() #endif } -const Type *ProgramEditor::GetTypeByName(const rdcstr &name) +Type *ProgramEditor::CreateNewType() +{ + m_Types.push_back(new(alloc) Type); + return m_Types.back(); +} + +Type *ProgramEditor::CreateNamedStructType(const rdcstr &name, rdcarray members) { for(size_t i = 0; i < m_Types.size(); i++) - if(m_Types[i].name == name) - return &m_Types[i]; + if(m_Types[i]->name == name) + return m_Types[i]; - return NULL; + if(members.empty()) + return NULL; + + Type *structType = CreateNewType(); + structType->type = Type::Struct; + structType->name = name; + structType->members = members; + return structType; +} + +DXIL::Type *ProgramEditor::CreateFunctionType(const Type *ret, rdcarray params) +{ + for(Type *type : m_Types) + if(type->type == Type::Function && type->inner == ret && type->members == params) + return type; + + Type *funcType = CreateNewType(); + funcType->type = Type::Function; + funcType->inner = ret; + funcType->members = params; + return funcType; +} + +DXIL::Type *ProgramEditor::CreatePointerType(const Type *inner, Type::PointerAddrSpace addrSpace) +{ + for(Type *type : m_Types) + if(type->type == Type::Pointer && type->inner == inner && type->addrSpace == addrSpace) + return type; + + Type *ptrType = CreateNewType(); + ptrType->type = Type::Pointer; + ptrType->inner = inner; + ptrType->addrSpace = addrSpace; + return ptrType; } Function *ProgramEditor::GetFunctionByName(const rdcstr &name) { for(size_t i = 0; i < m_Functions.size(); i++) - if(m_Functions[i].name == name) - return &m_Functions[i]; + if(m_Functions[i]->name == name) + return m_Functions[i]; return NULL; } @@ -612,64 +259,23 @@ Function *ProgramEditor::GetFunctionByName(const rdcstr &name) Metadata *ProgramEditor::GetMetadataByName(const rdcstr &name) { for(size_t i = 0; i < m_NamedMeta.size(); i++) - if(m_NamedMeta[i].name == name) - return &m_NamedMeta[i]; + if(m_NamedMeta[i]->name == name) + return m_NamedMeta[i]; return NULL; } -const Type *ProgramEditor::AddType(const Type &t) -{ - // check all inner types already point into our array - if(t.inner && !IN_ARRAY(m_Types, t.inner) && !IN_ARRAY(m_OldTypes, t.inner)) - { - RDCERR("New type references invalid inner type"); - return NULL; - } - - for(const Type *c : t.members) - { - if(!IN_ARRAY(m_Types, c) && !IN_ARRAY(m_OldTypes, c)) - { - RDCERR("New type references invalid member type"); - return NULL; - } - } - - m_Types.push_back(t); - return &m_Types.back(); -} - -const Function *ProgramEditor::DeclareFunction(const Function &f) +Function *ProgramEditor::DeclareFunction(const Function &f) { // only accept function declarations, not definitions - if(!f.args.empty() || !f.instructions.empty() || !f.values.empty() || !f.blocks.empty() || - !f.constants.empty() || !f.metadata.empty() || !f.uselist.empty() || !f.attachedMeta.empty() || - !f.valueSymtabOrder.empty()) + if(!f.instructions.empty()) { RDCERR("Only function declarations are allowed"); return NULL; } - // check that inner pointers are valid - if(!IN_ARRAY(m_Types, f.funcType) && !IN_ARRAY(m_OldTypes, f.funcType)) - { - RDCERR("Function references invalid type"); - return NULL; - } - - Value v(&m_Functions[m_Functions.size()]); - - // insert the value after the last current function - for(size_t i = m_Functions.size() - 1; i < m_Values.size(); i++) - { - if(m_Values[i].type == ValueType::Function && - m_Values[i].function->name == m_Functions.back().name) - { - m_Values.insert(i + 1, v); - break; - } - } + m_Functions.push_back(new(alloc) Function(f)); + Function *ret = m_Functions.back(); // functions need to be added to the symtab or dxc complains if(m_SortedSymtab) @@ -682,213 +288,176 @@ const Function *ProgramEditor::DeclareFunction(const Function &f) break; } - m_ValueSymtabOrder.insert(idx, v); + m_ValueSymtabOrder.insert(idx, ret); } else { // otherwise just append - m_ValueSymtabOrder.push_back(v); + m_ValueSymtabOrder.push_back(ret); } - m_Functions.push_back(f); - return &m_Functions.back(); + return ret; } -Metadata *ProgramEditor::AddMetadata(const Metadata &m) +Metadata *ProgramEditor::CreateMetadata() { - if(m.dwarf || m.debugLoc) - { - RDCERR("Metadata with debug information is not supported"); - return NULL; - } - - // check that inner pointers are valid - if(m.type && !IN_ARRAY(m_Types, m.type) && !IN_ARRAY(m_OldTypes, m.type)) - { - RDCERR("Metadata references invalid type"); - return NULL; - } - - for(const Metadata *c : m.children) - { - if(c && !IN_ARRAY(m_Metadata, c) && !IN_ARRAY(m_OldMetadata, c)) - { - RDCERR("New metadata references invalid member metadata"); - return NULL; - } - } - - m_Metadata.push_back(m); - return &m_Metadata.back(); + return new(alloc) Metadata; } -NamedMetadata *ProgramEditor::AddNamedMetadata(const NamedMetadata &m) +Metadata *ProgramEditor::CreateConstantMetadata(uint32_t val) { - if(m.dwarf || m.debugLoc) - { - RDCERR("Metadata with debug information is not supported"); - return NULL; - } - - // check that inner pointers are valid - if(m.type && !IN_ARRAY(m_Types, m.type) && !IN_ARRAY(m_OldTypes, m.type)) - { - RDCERR("Metadata references invalid type"); - return NULL; - } - - for(const Metadata *c : m.children) - { - if(c && !IN_ARRAY(m_Metadata, c) && !IN_ARRAY(m_OldMetadata, c)) - { - RDCERR("New metadata references invalid member metadata"); - return NULL; - } - } - - m_NamedMeta.push_back(m); - return &m_NamedMeta.back(); + Metadata *m = CreateMetadata(); + m->isConstant = true; + m->type = m_Int32Type; + m->value = CreateConstant(Constant(m_Int32Type, val)); + return m; } -const Constant *ProgramEditor::GetOrAddConstant(const Constant &c) +Metadata *ProgramEditor::CreateConstantMetadata(uint8_t val) { - // check that inner pointers are valid - if(!IN_ARRAY(m_Types, c.type) && !IN_ARRAY(m_OldTypes, c.type)) - { - RDCERR("Constant references invalid type"); - return NULL; - } + Metadata *m = CreateMetadata(); + m->isConstant = true; + m->type = m_Int8Type; + m->value = CreateConstant(Constant(m_Int32Type, val)); + return m; +} +Metadata *ProgramEditor::CreateConstantMetadata(const rdcstr &str) +{ + Metadata *m = CreateMetadata(); + m->isConstant = true; + m->isString = true; + m->str = str; + return m; +} + +Metadata *ProgramEditor::CreateConstantMetadata(bool val) +{ + Metadata *m = CreateMetadata(); + m->isConstant = true; + m->type = m_BoolType; + m->value = CreateConstant(Constant(m_BoolType, val)); + return m; +} + +Metadata *ProgramEditor::CreateConstantMetadata(Constant *val) +{ + Metadata *m = CreateMetadata(); + m->isConstant = true; + m->type = val->type; + m->value = val; + return m; +} + +NamedMetadata *ProgramEditor::CreateNamedMetadata(const rdcstr &name) +{ + for(NamedMetadata *m : m_NamedMeta) + if(m->name == name) + return m; + + m_NamedMeta.push_back(new(alloc) NamedMetadata); + return m_NamedMeta.back(); +} + +Constant *ProgramEditor::CreateConstant(const Constant &c) +{ // for scalars, check for an existing constant if(c.type->type == Type::Scalar) { - for(Constant &existing : m_Constants) + for(Constant *existing : m_Constants) { - if(existing.type == c.type && existing.undef == c.undef && - existing.nullconst == c.nullconst && existing.val.u64v[0] == c.val.u64v[0]) + if(existing->type == c.type) { - return &existing; + if(existing->isUndef() && c.isUndef()) + return existing; + if(existing->isNULL() && c.isNULL()) + return existing; + if(existing->isLiteral() && c.isLiteral() && existing->getU64() == c.getU64()) + return existing; } } } - m_Constants.push_back(c); - m_Values.push_back(Value(&m_Constants.back())); - return &m_Constants.back(); + m_Constants.push_back(new(alloc) Constant(c)); + return m_Constants.back(); } -const Constant *ProgramEditor::GetOrAddConstant(Function *f, const Constant &c) +Constant *ProgramEditor::CreateConstant(const Type *type, const rdcarray &members) { - // check that inner pointers are valid - if(!IN_ARRAY(m_Types, c.type) && !IN_ARRAY(m_OldTypes, c.type)) - { - RDCERR("Constant references invalid type"); - return NULL; - } - - // for scalars, check for an existing constant - if(c.type->type == Type::Scalar) - { - for(Constant &existing : f->constants) - { - if(existing.type == c.type && existing.undef == c.undef && - existing.nullconst == c.nullconst && existing.val.u64v[0] == c.val.u64v[0]) - { - return &existing; - } - } - } - - f->constants.push_back(c); - f->values.insert(f->constants.size() - 1, Value(&f->constants.back())); - return &f->constants.back(); + Constant *ret = new(alloc) Constant; + ret->type = type; + ret->setCompound(alloc, members); + return ret; } -Instruction *ProgramEditor::AddInstruction(Function *f, size_t idx, const Instruction &inst) +Instruction *ProgramEditor::CreateInstruction(Operation op) { - size_t valueIdx = RDCMIN(f->values.size() - 1, f->constants.size() + idx); - if(inst.type != m_VoidType) - { - // find the value index for the instruction we're inserting before. This won't match up exactly - // with the instructions sadly as not all - // instructions are values :( - for(; valueIdx > f->constants.size(); valueIdx--) - if(f->values[valueIdx].instruction->resultID == f->instructions[idx].resultID) - break; + Instruction *ret = new(alloc) Instruction; + ret->op = op; + return ret; +} - RDCASSERT(f->values[valueIdx].instruction->resultID == f->instructions[idx].resultID); - } - - f->instructions.insert(idx, inst); - f->instructions[idx].resultID = m_InsertedInstructionID--; - Instruction *ret = &f->instructions[idx]; - if(inst.type != m_VoidType) - f->values.insert(valueIdx, Value(ret)); +Instruction *ProgramEditor::CreateInstruction(const Function *f) +{ + Instruction *ret = CreateInstruction(Operation::Call); + ret->extra(alloc).funcCall = f; return ret; } #define getAttribID(a) uint64_t(a - m_AttributeSets.begin()) -#define getTypeID(t) uint64_t(t - m_Types.begin()) -#define getMetaID(m) uint64_t(m - m_Metadata.begin()) +#define getTypeID(t) uint64_t(t->id) +#define getMetaID(m) uint64_t(m->id) +#define getValueID(v) uint64_t(v->id) #define getMetaIDOrNull(m) (m ? (getMetaID(m) + 1) : 0ULL) -#define getFunctionMetaID(m) \ - uint64_t(m >= m_Metadata.begin() && m < m_Metadata.end() ? m - m_Metadata.begin() \ - : m - f.metadata.begin()) -#define getFunctionMetaIDOrNull(m) (m ? (getFunctionMetaID(m) + 1) : 0ULL) -#define getValueID(v) uint64_t(values.indexOf(v)) - -bytebuf ProgramEditor::EncodeProgram() const +bytebuf ProgramEditor::EncodeProgram() { - rdcarray values = m_Values; - bytebuf ret; LLVMBC::BitcodeWriter writer(ret); LLVMBC::BitcodeWriter::Config cfg = {}; + LLVMOrderAccumulator accum; + accum.processGlobals(this); + + const rdcarray &values = accum.values; + const rdcarray &metadata = accum.metadata; + for(size_t i = 0; i < m_GlobalVars.size(); i++) { - cfg.maxAlign = RDCMAX(m_GlobalVars[i].align, cfg.maxAlign); - RDCASSERT(m_GlobalVars[i].type->type == Type::Pointer); - uint32_t typeIndex = uint32_t(getTypeID(m_GlobalVars[i].type->inner)); + cfg.maxAlign = RDCMAX(m_GlobalVars[i]->align, cfg.maxAlign); + RDCASSERT(m_GlobalVars[i]->type->type == Type::Pointer); + uint32_t typeIndex = uint32_t(getTypeID(m_GlobalVars[i]->type->inner)); cfg.maxGlobalType = RDCMAX(typeIndex, cfg.maxGlobalType); } for(size_t i = 0; i < m_Functions.size(); i++) - cfg.maxAlign = RDCMAX(m_Functions[i].align, cfg.maxAlign); + cfg.maxAlign = RDCMAX(m_Functions[i]->align, cfg.maxAlign); - for(size_t i = 0; i < m_Metadata.size(); i++) + for(size_t i = 0; i < metadata.size(); i++) { - if(m_Metadata[i].isString) + if(metadata[i]->isString) cfg.hasMetaString = true; - if(m_Metadata[i].debugLoc) + if(metadata[i]->debugLoc) cfg.hasDebugLoc = true; } for(size_t i = 0; i < m_NamedMeta.size(); i++) { - if(m_NamedMeta[i].isString) + if(m_NamedMeta[i]->isString) cfg.hasMetaString = true; - if(m_NamedMeta[i].debugLoc) + if(m_NamedMeta[i]->debugLoc) cfg.hasDebugLoc = true; } cfg.hasNamedMeta = !m_NamedMeta.empty(); - for(size_t i = m_GlobalVars.size() + m_Functions.size(); i < m_Values.size(); i++) - { - // stop once we pass constants - if(m_Values[i].type != ValueType::Constant) - break; - } - cfg.numTypes = m_Types.size(); cfg.numSections = m_Sections.size(); - cfg.numGlobalValues = m_Values.size(); + cfg.numGlobalValues = values.size(); writer.ConfigureSizes(cfg); @@ -1003,52 +572,51 @@ bytebuf ProgramEditor::EncodeProgram() const { writer.BeginBlock(LLVMBC::KnownBlock::TYPE_BLOCK); - writer.Record(LLVMBC::TypeRecord::NUMENTRY, (uint32_t)m_Types.size()); + writer.Record(LLVMBC::TypeRecord::NUMENTRY, (uint32_t)accum.types.size()); - for(size_t i = 0; i < m_Types.size(); i++) + for(size_t i = 0; i < accum.types.size(); i++) { - if(m_Types[i].isVoid()) + const Type &t = *accum.types[i]; + if(t.isVoid()) { writer.Record(LLVMBC::TypeRecord::VOID); } - else if(m_Types[i].type == Type::Label) + else if(t.type == Type::Label) { writer.Record(LLVMBC::TypeRecord::LABEL); } - else if(m_Types[i].type == Type::Metadata) + else if(t.type == Type::Metadata) { writer.Record(LLVMBC::TypeRecord::METADATA); } - else if(m_Types[i].type == Type::Scalar && m_Types[i].scalarType == Type::Float) + else if(t.type == Type::Scalar && t.scalarType == Type::Float) { - if(m_Types[i].bitWidth == 16) + if(t.bitWidth == 16) writer.Record(LLVMBC::TypeRecord::HALF); - else if(m_Types[i].bitWidth == 32) + else if(t.bitWidth == 32) writer.Record(LLVMBC::TypeRecord::FLOAT); - else if(m_Types[i].bitWidth == 64) + else if(t.bitWidth == 64) writer.Record(LLVMBC::TypeRecord::DOUBLE); } - else if(m_Types[i].type == Type::Scalar && m_Types[i].scalarType == Type::Int) + else if(t.type == Type::Scalar && t.scalarType == Type::Int) { - writer.Record(LLVMBC::TypeRecord::INTEGER, m_Types[i].bitWidth); + writer.Record(LLVMBC::TypeRecord::INTEGER, t.bitWidth); } - else if(m_Types[i].type == Type::Vector) + else if(t.type == Type::Vector) { - writer.Record(LLVMBC::TypeRecord::VECTOR, - {m_Types[i].elemCount, getTypeID(m_Types[i].inner)}); + writer.Record(LLVMBC::TypeRecord::VECTOR, {t.elemCount, getTypeID(t.inner)}); } - else if(m_Types[i].type == Type::Array) + else if(t.type == Type::Array) { - writer.Record(LLVMBC::TypeRecord::ARRAY, {m_Types[i].elemCount, getTypeID(m_Types[i].inner)}); + writer.Record(LLVMBC::TypeRecord::ARRAY, {t.elemCount, getTypeID(t.inner)}); } - else if(m_Types[i].type == Type::Pointer) + else if(t.type == Type::Pointer) { - writer.Record(LLVMBC::TypeRecord::POINTER, - {getTypeID(m_Types[i].inner), (uint64_t)m_Types[i].addrSpace}); + writer.Record(LLVMBC::TypeRecord::POINTER, {getTypeID(t.inner), (uint64_t)t.addrSpace}); } - else if(m_Types[i].type == Type::Struct) + else if(t.type == Type::Struct) { - if(m_Types[i].opaque) + if(t.opaque) { writer.Record(LLVMBC::TypeRecord::OPAQUE); } @@ -1056,32 +624,32 @@ bytebuf ProgramEditor::EncodeProgram() const { LLVMBC::TypeRecord type = LLVMBC::TypeRecord::STRUCT_ANON; - if(!m_Types[i].name.empty()) + if(!t.name.empty()) { - writer.Record(LLVMBC::TypeRecord::STRUCT_NAME, m_Types[i].name); + writer.Record(LLVMBC::TypeRecord::STRUCT_NAME, t.name); type = LLVMBC::TypeRecord::STRUCT_NAMED; } rdcarray vals; - vals.push_back(m_Types[i].packedStruct ? 1 : 0); + vals.push_back(t.packedStruct ? 1 : 0); - for(const Type *t : m_Types[i].members) - vals.push_back(getTypeID(t)); + for(const Type *member : t.members) + vals.push_back(getTypeID(member)); writer.Record(type, vals); } } - else if(m_Types[i].type == Type::Function) + else if(t.type == Type::Function) { rdcarray vals; - vals.push_back(m_Types[i].vararg ? 1 : 0); + vals.push_back(t.vararg ? 1 : 0); - vals.push_back(getTypeID(m_Types[i].inner)); + vals.push_back(getTypeID(t.inner)); - for(const Type *t : m_Types[i].members) - vals.push_back(getTypeID(t)); + for(const Type *member : t.members) + vals.push_back(getTypeID(member)); writer.Record(LLVMBC::TypeRecord::FUNCTION, vals); } @@ -1109,12 +677,12 @@ bytebuf ProgramEditor::EncodeProgram() const for(size_t i = 0; i < m_GlobalVars.size(); i++) { - const GlobalVar &g = m_GlobalVars[i]; + const GlobalVar &g = *m_GlobalVars[i]; // global vars write the value type, not the pointer uint64_t typeIndex = getTypeID(g.type->inner); - RDCASSERT((size_t)typeIndex < m_Types.size()); + RDCASSERT((size_t)typeIndex < accum.types.size()); uint64_t linkageValue = 0; @@ -1145,7 +713,7 @@ bytebuf ProgramEditor::EncodeProgram() const { typeIndex, uint64_t(((g.flags & GlobalFlags::IsConst) ? 1 : 0) | 0x2 | ((uint32_t)g.type->addrSpace << 2)), - g.initialiser ? getValueID(Value(g.initialiser)) + 1 : 0, linkageValue, + g.initialiser ? getValueID(g.initialiser) + 1 : 0, linkageValue, Log2Floor((uint32_t)g.align) + 1, uint64_t(g.section + 1), // visibility 0U, @@ -1162,10 +730,10 @@ bytebuf ProgramEditor::EncodeProgram() const for(size_t i = 0; i < m_Functions.size(); i++) { - const Function &f = m_Functions[i]; - uint64_t typeIndex = getTypeID(f.funcType); + const Function &f = *m_Functions[i]; + uint64_t typeIndex = getTypeID(f.type); - RDCASSERT((size_t)typeIndex < m_Types.size()); + RDCASSERT((size_t)typeIndex < accum.types.size()); writer.Record(LLVMBC::ModuleRecord::FUNCTION, { @@ -1203,7 +771,7 @@ bytebuf ProgramEditor::EncodeProgram() const for(size_t i = 0; i < m_Aliases.size(); i++) { - const Alias &a = m_Aliases[i]; + const Alias &a = *m_Aliases[i]; uint64_t typeIndex = getTypeID(a.type); writer.Record(LLVMBC::ModuleRecord::ALIAS, { @@ -1217,32 +785,32 @@ bytebuf ProgramEditor::EncodeProgram() const // the symbols for constants start after the global variables and functions which we just // outputted - if(!m_Constants.empty()) + if(accum.numConsts) { writer.BeginBlock(LLVMBC::KnownBlock::CONSTANTS_BLOCK); - EncodeConstants(writer, values, m_Constants); + EncodeConstants(writer, values, accum.firstConst, accum.numConsts); writer.EndBlock(); } - if(!m_Metadata.empty()) + if(!metadata.empty()) { writer.BeginBlock(LLVMBC::KnownBlock::METADATA_BLOCK); writer.EmitMetaDataAbbrev(); - EncodeMetadata(writer, values, m_Metadata); + EncodeMetadata(writer, metadata); rdcarray vals; for(size_t i = 0; i < m_NamedMeta.size(); i++) { - writer.Record(LLVMBC::MetaDataRecord::NAME, m_NamedMeta[i].name); + writer.Record(LLVMBC::MetaDataRecord::NAME, m_NamedMeta[i]->name); vals.clear(); - for(size_t m = 0; m < m_NamedMeta[i].children.size(); m++) - vals.push_back(getMetaID(m_NamedMeta[i].children[m])); + for(size_t m = 0; m < m_NamedMeta[i]->children.size(); m++) + vals.push_back(getMetaID(m_NamedMeta[i]->children[m])); writer.Record(LLVMBC::MetaDataRecord::NAMED_NODE, vals); } @@ -1276,14 +844,14 @@ bytebuf ProgramEditor::EncodeProgram() const { writer.BeginBlock(LLVMBC::KnownBlock::VALUE_SYMTAB_BLOCK); - for(Value v : m_ValueSymtabOrder) + for(Value *v : m_ValueSymtabOrder) { const rdcstr *str = NULL; - switch(v.type) + switch(v->kind()) { - case ValueType::GlobalVar: str = &v.global->name; break; - case ValueType::Function: str = &v.function->name; break; - case ValueType::Alias: str = &v.alias->name; break; + case ValueKind::GlobalVar: str = &cast(v)->name; break; + case ValueKind::Function: str = &cast(v)->name; break; + case ValueKind::Alias: str = &cast(v)->name; break; default: break; } @@ -1294,108 +862,111 @@ bytebuf ProgramEditor::EncodeProgram() const writer.EndBlock(); } -#define encodeRelativeValueID(v) \ - { \ - uint64_t valID = getValueID(v); \ - if(valID <= instValueID) \ - { \ - vals.push_back(instValueID - valID); \ - } \ - else \ - { \ - forwardRefs = true; \ - /* signed integer two's complement for negative */ \ - /* values referencing forward from the instruction */ \ - vals.push_back(0x100000000ULL - (valID - instValueID)); \ - vals.push_back(getTypeID(v.GetType())); \ - } \ +#define encodeRelativeValueID(v) \ + { \ + uint64_t valID = getValueID(v); \ + if(valID <= zeroIdxValueId) \ + { \ + vals.push_back(zeroIdxValueId - valID); \ + } \ + else \ + { \ + forwardRefs = true; \ + /* signed integer two's complement for negative */ \ + /* values referencing forward from the instruction */ \ + vals.push_back(0x100000000ULL - (valID - zeroIdxValueId)); \ + vals.push_back(getTypeID(v->type)); \ + } \ } // some cases don't encode the type even for forward refs, if it's implicit (e.g. second parameter // in a binop). This also doesn't count as a forward ref for the case of breaking the abbrev use -#define encodeRelativeValueIDTypeless(v) \ - { \ - uint64_t valID = getValueID(v); \ - if(valID <= instValueID) \ - { \ - vals.push_back(instValueID - valID); \ - } \ - else \ - { \ - vals.push_back(0x100000000ULL - (valID - instValueID)); \ - } \ +#define encodeRelativeValueIDTypeless(v) \ + { \ + uint64_t valID = getValueID(v); \ + if(valID <= zeroIdxValueId) \ + { \ + vals.push_back(zeroIdxValueId - valID); \ + } \ + else \ + { \ + vals.push_back(0x100000000ULL - (valID - zeroIdxValueId)); \ + } \ } - for(const Function &f : m_Functions) + for(Function *f : m_Functions) { - if(f.external) + if(f->external) continue; - values.append(f.values); - writer.BeginBlock(LLVMBC::KnownBlock::FUNCTION_BLOCK); - writer.Record(LLVMBC::FunctionRecord::DECLAREBLOCKS, f.blocks.size()); + writer.Record(LLVMBC::FunctionRecord::DECLAREBLOCKS, f->blocks.size()); - if(!f.constants.empty()) + accum.processFunction(f); + + if(accum.numFuncConsts) { writer.BeginBlock(LLVMBC::KnownBlock::CONSTANTS_BLOCK); - EncodeConstants(writer, values, f.constants); + EncodeConstants(writer, values, accum.firstFuncConst, accum.numFuncConsts); writer.EndBlock(); } - if(!f.metadata.empty()) - { - writer.BeginBlock(LLVMBC::KnownBlock::METADATA_BLOCK); - - EncodeMetadata(writer, values, f.metadata); - - writer.EndBlock(); - } - - // value IDs for instructions start after all the constants - uint32_t instValueID = uint32_t(m_Values.size() + f.constants.size() + f.args.size()); - uint32_t debugLoc = ~0U; bool forwardRefs = false; rdcarray vals; - bool needMetaAttach = !f.attachedMeta.empty(); + bool needMetaAttach = !f->attachedMeta.empty(); - for(const Instruction &inst : f.instructions) + uint32_t lastValidInstId = + uint32_t(accum.firstFuncConst + accum.numFuncConsts + f->args.size()) - 1; + + for(const Instruction *inst : f->instructions) { forwardRefs = false; vals.clear(); - needMetaAttach |= !inst.attachedMeta.empty(); + if(inst->id != Value::NoID) + lastValidInstId = inst->id; - switch(inst.op) + // a reference to this value ID is '0'. Usually the current instruction, 1 is then the + // previous, etc + uint32_t zeroIdxValueId = inst->id; + // except if the current instruction isn't a value. Then '0' is impossible, 1 still refers to + // the previous. In order to have a value ID to construct relative references, we pretend we + // are on the next value + if(zeroIdxValueId == Value::NoID) + zeroIdxValueId = lastValidInstId + 1; + + needMetaAttach |= !inst->getAttachedMeta().empty(); + + switch(inst->op) { case Operation::NoOp: RDCERR("Unexpected no-op encoding"); continue; case Operation::Call: { - vals.push_back(inst.paramAttrs ? getAttribID(inst.paramAttrs) + 1 : 0); + vals.push_back(inst->getParamAttrs() ? getAttribID(inst->getParamAttrs()) + 1 : 0); // always emit func type uint64_t flags = 1 << 15; - if(inst.opFlags != InstructionFlags::NoFlags) + if(inst->opFlags() != InstructionFlags::NoFlags) flags |= 1 << 17; vals.push_back(flags); - if(inst.opFlags != InstructionFlags::NoFlags) - vals.push_back((uint64_t)inst.opFlags); - vals.push_back(getTypeID(inst.funcCall->funcType)); - encodeRelativeValueID(Value(inst.funcCall)); - for(size_t a = 0; a < inst.args.size(); a++) + if(inst->opFlags() != InstructionFlags::NoFlags) + vals.push_back((uint64_t)inst->opFlags()); + vals.push_back(getTypeID(inst->getFuncCall()->type)); + encodeRelativeValueID(inst->getFuncCall()); + for(size_t a = 0; a < inst->args.size(); a++) { - if(inst.args[a].type == ValueType::Metadata) + if(inst->args[a]->kind() == ValueKind::Metadata) { - vals.push_back(getFunctionMetaID(inst.args[a].meta)); + vals.push_back(getMetaID(cast(inst->args[a]))); } else { - encodeRelativeValueIDTypeless(inst.args[a]); + encodeRelativeValueIDTypeless(inst->args[a]); } } writer.RecordInstruction(LLVMBC::FunctionRecord::INST_CALL, vals, forwardRefs); @@ -1415,26 +986,26 @@ bytebuf ProgramEditor::EncodeProgram() const case Operation::Bitcast: case Operation::AddrSpaceCast: { - encodeRelativeValueID(inst.args[0]); - vals.push_back(getTypeID(inst.type)); - vals.push_back(EncodeCast(inst.op)); + encodeRelativeValueID(inst->args[0]); + vals.push_back(getTypeID(inst->type)); + vals.push_back(EncodeCast(inst->op)); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_CAST, vals, forwardRefs); break; } case Operation::ExtractVal: { - encodeRelativeValueID(inst.args[0]); - for(size_t i = 1; i < inst.args.size(); i++) - vals.push_back(inst.args[i].literal); + encodeRelativeValueID(inst->args[0]); + for(size_t i = 1; i < inst->args.size(); i++) + vals.push_back(cast(inst->args[i])->literal); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_EXTRACTVAL, vals, forwardRefs); break; } case Operation::Ret: { - if(!inst.args.empty()) + if(!inst->args.empty()) { - encodeRelativeValueID(inst.args[0]); + encodeRelativeValueID(inst->args[0]); } writer.RecordInstruction(LLVMBC::FunctionRecord::INST_RET, vals, forwardRefs); break; @@ -1458,15 +1029,15 @@ bytebuf ProgramEditor::EncodeProgram() const case Operation::Or: case Operation::Xor: { - encodeRelativeValueID(inst.args[0]); - encodeRelativeValueIDTypeless(inst.args[1]); + encodeRelativeValueID(inst->args[0]); + encodeRelativeValueIDTypeless(inst->args[1]); - const Type *t = inst.args[0].GetType(); + const Type *t = inst->args[0]->type; const bool isFloatOp = (t->scalarType == Type::Float); uint64_t opcode = 0; - switch(inst.op) + switch(inst->op) { case Operation::FAdd: case Operation::Add: opcode = 0; break; @@ -1490,29 +1061,30 @@ bytebuf ProgramEditor::EncodeProgram() const } vals.push_back(opcode); - if(inst.opFlags != InstructionFlags::NoFlags) + if(inst->opFlags() != InstructionFlags::NoFlags) { uint64_t flags = 0; - if(inst.op == Operation::Add || inst.op == Operation::Sub || - inst.op == Operation::Mul || inst.op == Operation::ShiftLeft) + if(inst->op == Operation::Add || inst->op == Operation::Sub || + inst->op == Operation::Mul || inst->op == Operation::ShiftLeft) { - if(inst.opFlags & InstructionFlags::NoSignedWrap) + if(inst->opFlags() & InstructionFlags::NoSignedWrap) flags |= 0x2; - if(inst.opFlags & InstructionFlags::NoUnsignedWrap) + if(inst->opFlags() & InstructionFlags::NoUnsignedWrap) flags |= 0x1; vals.push_back(flags); } - else if(inst.op == Operation::SDiv || inst.op == Operation::UDiv || - inst.op == Operation::LogicalShiftRight || inst.op == Operation::ArithShiftRight) + else if(inst->op == Operation::SDiv || inst->op == Operation::UDiv || + inst->op == Operation::LogicalShiftRight || + inst->op == Operation::ArithShiftRight) { - if(inst.opFlags & InstructionFlags::Exact) + if(inst->opFlags() & InstructionFlags::Exact) flags |= 0x1; vals.push_back(flags); } else if(isFloatOp) { // fast math flags overlap - vals.push_back(uint64_t(inst.opFlags)); + vals.push_back(uint64_t(inst->opFlags())); } } @@ -1526,13 +1098,13 @@ bytebuf ProgramEditor::EncodeProgram() const } case Operation::Alloca: { - vals.push_back(getTypeID(inst.type->inner)); - vals.push_back(getTypeID(inst.args[0].GetType())); - vals.push_back(getValueID(inst.args[0])); - uint64_t alignAndFlags = Log2Floor(inst.align) + 1; - // DXC always sets this bit, as the type is ap ointer + vals.push_back(getTypeID(inst->type->inner)); + vals.push_back(getTypeID(inst->args[0]->type)); + vals.push_back(getValueID(inst->args[0])); + uint64_t alignAndFlags = inst->align; + // DXC always sets this bit, as the type is a pointer alignAndFlags |= 1U << 6; - if(inst.opFlags & InstructionFlags::ArgumentAlloca) + if(inst->opFlags() & InstructionFlags::ArgumentAlloca) alignAndFlags |= 1U << 5; vals.push_back(alignAndFlags); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_ALLOCA, vals, forwardRefs); @@ -1540,12 +1112,12 @@ bytebuf ProgramEditor::EncodeProgram() const } case Operation::GetElementPtr: { - vals.push_back((inst.opFlags & InstructionFlags::InBounds) ? 1U : 0U); - vals.push_back(getTypeID(inst.args[0].GetType()->inner)); + vals.push_back((inst->opFlags() & InstructionFlags::InBounds) ? 1U : 0U); + vals.push_back(getTypeID(inst->args[0]->type->inner)); - for(size_t i = 0; i < inst.args.size(); i++) + for(size_t i = 0; i < inst->args.size(); i++) { - encodeRelativeValueID(inst.args[i]); + encodeRelativeValueID(inst->args[i]); } writer.RecordInstruction(LLVMBC::FunctionRecord::INST_GEP, vals, forwardRefs); @@ -1553,19 +1125,19 @@ bytebuf ProgramEditor::EncodeProgram() const } case Operation::Load: { - encodeRelativeValueID(inst.args[0]); - vals.push_back(getTypeID(inst.type)); - vals.push_back(Log2Floor(inst.align) + 1); - vals.push_back((inst.opFlags & InstructionFlags::Volatile) ? 1U : 0U); + encodeRelativeValueID(inst->args[0]); + vals.push_back(getTypeID(inst->type)); + vals.push_back(inst->align); + vals.push_back((inst->opFlags() & InstructionFlags::Volatile) ? 1U : 0U); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_LOAD, vals, forwardRefs); break; } case Operation::Store: { - encodeRelativeValueID(inst.args[0]); - encodeRelativeValueID(inst.args[1]); - vals.push_back(Log2Floor(inst.align) + 1); - vals.push_back((inst.opFlags & InstructionFlags::Volatile) ? 1U : 0U); + encodeRelativeValueID(inst->args[0]); + encodeRelativeValueID(inst->args[1]); + vals.push_back(inst->align); + vals.push_back((inst->opFlags() & InstructionFlags::Volatile) ? 1U : 0U); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_STORE, vals, forwardRefs); break; } @@ -1596,11 +1168,11 @@ bytebuf ProgramEditor::EncodeProgram() const case Operation::SLess: case Operation::SLessEqual: { - encodeRelativeValueID(inst.args[0]); - encodeRelativeValueIDTypeless(inst.args[1]); + encodeRelativeValueID(inst->args[0]); + encodeRelativeValueIDTypeless(inst->args[1]); uint64_t opcode = 0; - switch(inst.op) + switch(inst->op) { case Operation::FOrdFalse: opcode = 0; break; case Operation::FOrdEqual: opcode = 1; break; @@ -1635,60 +1207,60 @@ bytebuf ProgramEditor::EncodeProgram() const vals.push_back(opcode); - if(inst.opFlags != InstructionFlags::NoFlags) - vals.push_back((uint64_t)inst.opFlags); + if(inst->opFlags() != InstructionFlags::NoFlags) + vals.push_back((uint64_t)inst->opFlags()); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_CMP2, vals, forwardRefs); break; } case Operation::Select: { - encodeRelativeValueID(inst.args[0]); - encodeRelativeValueIDTypeless(inst.args[1]); - encodeRelativeValueID(inst.args[2]); + encodeRelativeValueID(inst->args[0]); + encodeRelativeValueIDTypeless(inst->args[1]); + encodeRelativeValueID(inst->args[2]); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_VSELECT, vals, forwardRefs); break; } case Operation::ExtractElement: { - encodeRelativeValueID(inst.args[0]); - encodeRelativeValueID(inst.args[1]); + encodeRelativeValueID(inst->args[0]); + encodeRelativeValueID(inst->args[1]); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_EXTRACTELT, vals, forwardRefs); break; } case Operation::InsertElement: { - encodeRelativeValueID(inst.args[0]); - encodeRelativeValueIDTypeless(inst.args[1]); - encodeRelativeValueID(inst.args[2]); + encodeRelativeValueID(inst->args[0]); + encodeRelativeValueIDTypeless(inst->args[1]); + encodeRelativeValueID(inst->args[2]); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_INSERTELT, vals, forwardRefs); break; } case Operation::ShuffleVector: { - encodeRelativeValueID(inst.args[0]); - encodeRelativeValueIDTypeless(inst.args[1]); - encodeRelativeValueID(inst.args[2]); + encodeRelativeValueID(inst->args[0]); + encodeRelativeValueIDTypeless(inst->args[1]); + encodeRelativeValueID(inst->args[2]); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_SHUFFLEVEC, vals, forwardRefs); break; } case Operation::InsertValue: { - encodeRelativeValueID(inst.args[0]); - encodeRelativeValueID(inst.args[1]); - for(size_t i = 2; i < inst.args.size(); i++) - vals.push_back(inst.args[i].literal); + encodeRelativeValueID(inst->args[0]); + encodeRelativeValueID(inst->args[1]); + for(size_t i = 2; i < inst->args.size(); i++) + vals.push_back(cast(inst->args[i])->literal); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_INSERTVAL, vals, forwardRefs); break; } case Operation::Branch: { - vals.push_back(uint64_t(inst.args[0].block - f.blocks.begin())); + vals.push_back(inst->args[0]->id); - if(inst.args.size() > 1) + if(inst->args.size() > 1) { - vals.push_back(uint64_t(inst.args[1].block - f.blocks.begin())); - encodeRelativeValueIDTypeless(inst.args[2]); + vals.push_back(inst->args[1]->id); + encodeRelativeValueIDTypeless(inst->args[2]); } writer.RecordInstruction(LLVMBC::FunctionRecord::INST_BR, vals, forwardRefs); @@ -1696,15 +1268,15 @@ bytebuf ProgramEditor::EncodeProgram() const } case Operation::Phi: { - vals.push_back(getTypeID(inst.type)); + vals.push_back(getTypeID(inst->type)); - for(size_t i = 0; i < inst.args.size(); i += 2) + for(size_t i = 0; i < inst->args.size(); i += 2) { - uint64_t valID = getValueID(inst.args[i]); - int64_t valRef = int64_t(instValueID) - int64_t(valID); + uint64_t valID = getValueID(inst->args[i]); + int64_t valRef = int64_t(inst->id) - int64_t(valID); vals.push_back(LLVMBC::BitWriter::svbr(valRef)); - vals.push_back(uint64_t(inst.args[i + 1].block - f.blocks.begin())); + vals.push_back(inst->args[i + 1]->id); } writer.RecordInstruction(LLVMBC::FunctionRecord::INST_PHI, vals, forwardRefs); @@ -1712,15 +1284,15 @@ bytebuf ProgramEditor::EncodeProgram() const } case Operation::Switch: { - vals.push_back(getTypeID(inst.args[0].GetType())); - encodeRelativeValueIDTypeless(inst.args[0]); + vals.push_back(getTypeID(inst->args[0]->type)); + encodeRelativeValueIDTypeless(inst->args[0]); - vals.push_back(uint64_t(inst.args[1].block - f.blocks.begin())); + vals.push_back(inst->args[1]->id); - for(size_t i = 2; i < inst.args.size(); i += 2) + for(size_t i = 2; i < inst->args.size(); i += 2) { - vals.push_back(getValueID(inst.args[i])); - vals.push_back(uint64_t(inst.args[i + 1].block - f.blocks.begin())); + vals.push_back(getValueID(inst->args[i])); + vals.push_back(inst->args[i + 1]->id); } writer.RecordInstruction(LLVMBC::FunctionRecord::INST_SWITCH, vals, forwardRefs); @@ -1728,48 +1300,48 @@ bytebuf ProgramEditor::EncodeProgram() const } case Operation::Fence: { - vals.push_back(((uint64_t)inst.opFlags & (uint64_t)InstructionFlags::SuccessOrderMask) >> - 12U); - vals.push_back((inst.opFlags & InstructionFlags::SingleThread) ? 0U : 1U); + vals.push_back( + ((uint64_t)inst->opFlags() & (uint64_t)InstructionFlags::SuccessOrderMask) >> 12U); + vals.push_back((inst->opFlags() & InstructionFlags::SingleThread) ? 0U : 1U); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_FENCE, vals, forwardRefs); break; } case Operation::CompareExchange: { - encodeRelativeValueID(inst.args[0]); - encodeRelativeValueID(inst.args[1]); - encodeRelativeValueIDTypeless(inst.args[2]); - vals.push_back((inst.opFlags & InstructionFlags::Volatile) ? 1U : 0U); - vals.push_back(((uint64_t)inst.opFlags & (uint64_t)InstructionFlags::SuccessOrderMask) >> - 12U); - vals.push_back((inst.opFlags & InstructionFlags::SingleThread) ? 0U : 1U); - vals.push_back(((uint64_t)inst.opFlags & (uint64_t)InstructionFlags::FailureOrderMask) >> - 15U); - vals.push_back((inst.opFlags & InstructionFlags::Weak) ? 1U : 0U); + encodeRelativeValueID(inst->args[0]); + encodeRelativeValueID(inst->args[1]); + encodeRelativeValueIDTypeless(inst->args[2]); + vals.push_back((inst->opFlags() & InstructionFlags::Volatile) ? 1U : 0U); + vals.push_back( + ((uint64_t)inst->opFlags() & (uint64_t)InstructionFlags::SuccessOrderMask) >> 12U); + vals.push_back((inst->opFlags() & InstructionFlags::SingleThread) ? 0U : 1U); + vals.push_back( + ((uint64_t)inst->opFlags() & (uint64_t)InstructionFlags::FailureOrderMask) >> 15U); + vals.push_back((inst->opFlags() & InstructionFlags::Weak) ? 1U : 0U); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_CMPXCHG, vals, forwardRefs); break; } case Operation::LoadAtomic: { - encodeRelativeValueID(inst.args[0]); - vals.push_back(getTypeID(inst.type)); - vals.push_back(Log2Floor(inst.align) + 1); - vals.push_back((inst.opFlags & InstructionFlags::Volatile) ? 1U : 0U); - vals.push_back(((uint64_t)inst.opFlags & (uint64_t)InstructionFlags::SuccessOrderMask) >> - 12U); - vals.push_back((inst.opFlags & InstructionFlags::SingleThread) ? 0U : 1U); + encodeRelativeValueID(inst->args[0]); + vals.push_back(getTypeID(inst->type)); + vals.push_back(inst->align); + vals.push_back((inst->opFlags() & InstructionFlags::Volatile) ? 1U : 0U); + vals.push_back( + ((uint64_t)inst->opFlags() & (uint64_t)InstructionFlags::SuccessOrderMask) >> 12U); + vals.push_back((inst->opFlags() & InstructionFlags::SingleThread) ? 0U : 1U); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_LOADATOMIC, vals, forwardRefs); break; } case Operation::StoreAtomic: { - encodeRelativeValueID(inst.args[0]); - encodeRelativeValueID(inst.args[1]); - vals.push_back(Log2Floor(inst.align) + 1); - vals.push_back((inst.opFlags & InstructionFlags::Volatile) ? 1U : 0U); - vals.push_back(((uint64_t)inst.opFlags & (uint64_t)InstructionFlags::SuccessOrderMask) >> - 12U); - vals.push_back((inst.opFlags & InstructionFlags::SingleThread) ? 0U : 1U); + encodeRelativeValueID(inst->args[0]); + encodeRelativeValueID(inst->args[1]); + vals.push_back(inst->align); + vals.push_back((inst->opFlags() & InstructionFlags::Volatile) ? 1U : 0U); + vals.push_back( + ((uint64_t)inst->opFlags() & (uint64_t)InstructionFlags::SuccessOrderMask) >> 12U); + vals.push_back((inst->opFlags() & InstructionFlags::SingleThread) ? 0U : 1U); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_STOREATOMIC, vals, forwardRefs); break; } @@ -1785,11 +1357,11 @@ bytebuf ProgramEditor::EncodeProgram() const case Operation::AtomicUMax: case Operation::AtomicUMin: { - encodeRelativeValueID(inst.args[0]); - encodeRelativeValueIDTypeless(inst.args[1]); + encodeRelativeValueID(inst->args[0]); + encodeRelativeValueIDTypeless(inst->args[1]); uint64_t opcode = 0; - switch(inst.op) + switch(inst->op) { case Operation::AtomicExchange: opcode = 0; break; case Operation::AtomicAdd: opcode = 1; break; @@ -1808,66 +1380,56 @@ bytebuf ProgramEditor::EncodeProgram() const vals.push_back(opcode); - vals.push_back((inst.opFlags & InstructionFlags::Volatile) ? 1U : 0U); - vals.push_back(((uint64_t)inst.opFlags & (uint64_t)InstructionFlags::SuccessOrderMask) >> - 12U); - vals.push_back((inst.opFlags & InstructionFlags::SingleThread) ? 0U : 1U); + vals.push_back((inst->opFlags() & InstructionFlags::Volatile) ? 1U : 0U); + vals.push_back( + ((uint64_t)inst->opFlags() & (uint64_t)InstructionFlags::SuccessOrderMask) >> 12U); + vals.push_back((inst->opFlags() & InstructionFlags::SingleThread) ? 0U : 1U); writer.RecordInstruction(LLVMBC::FunctionRecord::INST_ATOMICRMW, vals, forwardRefs); break; } } - // instruction IDs are the values (i.e. all instructions that return non-void are a value) - if(inst.type != m_VoidType) - { -#if 0 - uint64_t dbgValueId = getValueID(Value(&inst)); - RDCASSERT(instValueID == dbgValueId); -#endif - instValueID++; - } - // no debug location? omit - if(inst.debugLoc == ~0U) + if(inst->debugLoc == ~0U) continue; // same as last time? emit 'again' record - if(inst.debugLoc == debugLoc) + if(inst->debugLoc == debugLoc) writer.Record(LLVMBC::FunctionRecord::DEBUG_LOC_AGAIN); // new debug location - const DebugLocation &loc = m_DebugLocations[inst.debugLoc]; + const DebugLocation &loc = m_DebugLocations[inst->debugLoc]; writer.Record(LLVMBC::FunctionRecord::DEBUG_LOC, { - loc.line, loc.col, getFunctionMetaIDOrNull(loc.scope), - getFunctionMetaIDOrNull(loc.inlinedAt), + loc.line, loc.col, getMetaIDOrNull(loc.scope), getMetaIDOrNull(loc.inlinedAt), }); - debugLoc = inst.debugLoc; + debugLoc = inst->debugLoc; } - if(!f.valueSymtabOrder.empty()) + if(!f->valueSymtabOrder.empty()) { writer.BeginBlock(LLVMBC::KnownBlock::VALUE_SYMTAB_BLOCK); - for(Value v : f.valueSymtabOrder) + for(Value *v : f->valueSymtabOrder) { - const rdcstr *str = NULL; - switch(v.type) + bool found = true; + rdcstr str; + switch(v->kind()) { - case ValueType::Instruction: str = &v.instruction->name; break; - case ValueType::Constant: str = &v.constant->str; break; - case ValueType::BasicBlock: str = &v.block->name; break; - default: break; + case ValueKind::Instruction: str = cast(v)->getName(); break; + case ValueKind::Constant: str = cast(v)->str; break; + case ValueKind::BasicBlock: str = cast(v)->name; break; + default: found = false; break; } - if(str) + if(found) { - if(v.type == ValueType::BasicBlock) - writer.RecordSymTabEntry(v.block - f.blocks.begin(), *str, true); + if(v->kind() == ValueKind::BasicBlock) + writer.RecordSymTabEntry(v->id, str, true); else - writer.RecordSymTabEntry(getValueID(v), *str); + writer.RecordSymTabEntry(getValueID(v), str); } } @@ -1880,28 +1442,28 @@ bytebuf ProgramEditor::EncodeProgram() const vals.clear(); - for(const rdcpair &m : f.attachedMeta) + for(const rdcpair &m : f->attachedMeta) { vals.push_back(m.first); - vals.push_back(getFunctionMetaID(m.second)); + vals.push_back(getMetaID(m.second)); } if(!vals.empty()) writer.Record(LLVMBC::MetaDataRecord::ATTACHMENT, vals); - for(size_t i = 0; i < f.instructions.size(); i++) + for(size_t i = 0; i < f->instructions.size(); i++) { - if(f.instructions[i].attachedMeta.empty()) + if(f->instructions[i]->getAttachedMeta().empty()) continue; vals.clear(); vals.push_back(uint64_t(i)); - for(const rdcpair &m : f.instructions[i].attachedMeta) + for(const rdcpair &m : f->instructions[i]->getAttachedMeta()) { vals.push_back(m.first); - vals.push_back(getFunctionMetaID(m.second)); + vals.push_back(getMetaID(m.second)); } writer.Record(LLVMBC::MetaDataRecord::ATTACHMENT, vals); @@ -1910,11 +1472,11 @@ bytebuf ProgramEditor::EncodeProgram() const writer.EndBlock(); } - if(!f.uselist.empty()) + if(!f->uselist.empty()) { writer.BeginBlock(LLVMBC::KnownBlock::USELIST_BLOCK); - for(const UselistEntry &u : f.uselist) + for(const UselistEntry &u : f->uselist) { vals = u.shuffle; vals.push_back(getValueID(u.value)); @@ -1927,7 +1489,7 @@ bytebuf ProgramEditor::EncodeProgram() const writer.EndBlock(); - values.resize(values.size() - f.values.size()); + accum.exitFunction(); } writer.EndBlock(); @@ -2059,104 +1621,109 @@ void ProgramEditor::RegisterUAV(DXILResourceType type, uint32_t space, uint32_t DXBC::DXBCContainer::StripChunk(m_OutBlob, DXBC::FOURCC_RTS0); } -void ProgramEditor::EncodeConstants(LLVMBC::BitcodeWriter &writer, const rdcarray &values, - const rdcarray &constants) const +void ProgramEditor::EncodeConstants(LLVMBC::BitcodeWriter &writer, + const rdcarray &values, size_t firstIdx, + size_t count) const { const Type *curType = NULL; - for(const Constant &c : constants) + for(size_t i = firstIdx; i < firstIdx + count; i++) { - if(c.type != curType) + const Constant *c = cast(values[i]); + + if(c->type != curType) { - writer.Record(LLVMBC::ConstantsRecord::SETTYPE, getTypeID(c.type)); - curType = c.type; + writer.Record(LLVMBC::ConstantsRecord::SETTYPE, getTypeID(c->type)); + curType = c->type; } - if(c.nullconst) + if(c->isNULL()) { writer.Record(LLVMBC::ConstantsRecord::CONST_NULL); } - else if(c.undef) + else if(c->isUndef()) { writer.Record(LLVMBC::ConstantsRecord::UNDEF); } - else if(c.op == Operation::GetElementPtr) + else if(c->op == Operation::GetElementPtr) { rdcarray vals; - vals.reserve(c.members.size() * 2 + 1); + vals.reserve(c->getMembers().size() * 2 + 1); // DXC's version of llvm always writes the explicit type here - vals.push_back(getTypeID(c.members[0].GetType()->inner)); + vals.push_back(getTypeID(c->getMembers()[0]->type->inner)); - for(size_t m = 0; m < c.members.size(); m++) + for(size_t m = 0; m < c->getMembers().size(); m++) { - vals.push_back(getTypeID(c.members[m].GetType())); - vals.push_back(getValueID(c.members[m])); + vals.push_back(getTypeID(c->getMembers()[m]->type)); + vals.push_back(getValueID(c->getMembers()[m])); } writer.Record(LLVMBC::ConstantsRecord::EVAL_GEP, vals); } - else if(c.op != Operation::NoOp) + else if(c->op != Operation::NoOp) { - uint64_t cast = EncodeCast(c.op); + uint64_t cast = EncodeCast(c->op); RDCASSERT(cast != ~0U); writer.Record(LLVMBC::ConstantsRecord::EVAL_CAST, - {cast, getTypeID(c.inner.GetType()), getValueID(c.inner)}); + {cast, getTypeID(c->getInner()->type), getValueID(c->getInner())}); } - else if(c.data) + else if(c->isData()) { rdcarray vals; - vals.reserve(c.members.size()); - if(c.type->type == Type::Vector) + if(c->type->type == Type::Vector) { - for(uint32_t m = 0; m < c.type->elemCount; m++) - vals.push_back(c.type->bitWidth <= 32 ? c.val.u32v[m] : c.val.u64v[m]); + vals.reserve(c->type->elemCount); + for(uint32_t m = 0; m < c->type->elemCount; m++) + vals.push_back(c->type->bitWidth <= 32 ? c->getShaderVal().u32v[m] + : c->getShaderVal().u64v[m]); } else { - for(size_t m = 0; m < c.members.size(); m++) - vals.push_back(c.members[m].literal); + vals.reserve(c->getMembers().size()); + for(size_t m = 0; m < c->getMembers().size(); m++) + vals.push_back(cast(c->getMembers()[m])->literal); } writer.Record(LLVMBC::ConstantsRecord::DATA, vals); } - else if(c.type->type == Type::Vector || c.type->type == Type::Array || - c.type->type == Type::Struct) + else if(c->type->type == Type::Vector || c->type->type == Type::Array || + c->type->type == Type::Struct) { rdcarray vals; - vals.reserve(c.members.size()); + vals.reserve(c->getMembers().size()); - for(size_t m = 0; m < c.members.size(); m++) - vals.push_back(getValueID(c.members[m])); + for(size_t m = 0; m < c->getMembers().size(); m++) + vals.push_back(getValueID(c->getMembers()[m])); writer.Record(LLVMBC::ConstantsRecord::AGGREGATE, vals); } - else if(c.type->scalarType == Type::Int) + else if(c->type->scalarType == Type::Int) { - writer.Record(LLVMBC::ConstantsRecord::INTEGER, LLVMBC::BitWriter::svbr(c.val.s64v[0])); + writer.Record(LLVMBC::ConstantsRecord::INTEGER, LLVMBC::BitWriter::svbr(c->getS64())); } - else if(c.type->scalarType == Type::Float) + else if(c->type->scalarType == Type::Float) { - writer.Record(LLVMBC::ConstantsRecord::FLOAT, c.val.u64v[0]); + writer.Record(LLVMBC::ConstantsRecord::FLOAT, c->getU64()); } - else if(!c.str.empty()) + else if(!c->str.empty()) { - if(c.str.indexOf('\0') < 0) + if(c->str.indexOf('\0') < 0) { - writer.Record(LLVMBC::ConstantsRecord::CSTRING, c.str); + writer.Record(LLVMBC::ConstantsRecord::CSTRING, c->str); } else { - writer.Record(LLVMBC::ConstantsRecord::STRING, c.str); + writer.Record(LLVMBC::ConstantsRecord::STRING, c->str); } } } } -void ProgramEditor::EncodeMetadata(LLVMBC::BitcodeWriter &writer, const rdcarray &values, - const rdcarray &meta) const +void ProgramEditor::EncodeMetadata(LLVMBC::BitcodeWriter &writer, + const rdcarray &meta) const { rdcarray vals; @@ -2164,39 +1731,33 @@ void ProgramEditor::EncodeMetadata(LLVMBC::BitcodeWriter &writer, const rdcarray for(size_t i = 0; i < meta.size(); i++) { - if(meta[i].isString) + if(meta[i]->isString) { - writer.Record(LLVMBC::MetaDataRecord::STRING_OLD, meta[i].str); + writer.Record(LLVMBC::MetaDataRecord::STRING_OLD, meta[i]->str); } - else if(meta[i].isConstant) + else if(meta[i]->isConstant) { writer.Record(LLVMBC::MetaDataRecord::VALUE, - {getTypeID(meta[i].type), getValueID(meta[i].value)}); + {getTypeID(meta[i]->type), getValueID(meta[i]->value)}); } - else if(meta[i].dwarf || meta[i].debugLoc) + else if(meta[i]->dwarf || meta[i]->debugLoc) { if(!errored) RDCERR("Unexpected debug metadata node - expect to only encode stripped DXIL chunks"); errored = true; - // replace this with the first NULL constant value - for(size_t c = 0; c < m_Constants.size(); c++) - { - if(m_Constants[c].nullconst) - { - writer.Record(LLVMBC::MetaDataRecord::VALUE, {getTypeID(m_Constants[c].type), (uint64_t)c}); - } - } + // replace this with an error. This is an error to reference but we can't get away from that + writer.Record(LLVMBC::MetaDataRecord::STRING_OLD, "unexpected_debug_metadata"); } else { vals.clear(); - for(size_t m = 0; m < meta[i].children.size(); m++) - vals.push_back(getMetaIDOrNull(meta[i].children[m])); + for(size_t m = 0; m < meta[i]->children.size(); m++) + vals.push_back(getMetaIDOrNull(meta[i]->children[m])); - writer.Record( - meta[i].isDistinct ? LLVMBC::MetaDataRecord::DISTINCT_NODE : LLVMBC::MetaDataRecord::NODE, - vals); + writer.Record(meta[i]->isDistinct ? LLVMBC::MetaDataRecord::DISTINCT_NODE + : LLVMBC::MetaDataRecord::NODE, + vals); } } } diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.h b/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.h index e18c86ffc..2121f72e6 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.h +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.h @@ -64,7 +64,7 @@ enum class HandleKind class ProgramEditor : public Program { public: - ProgramEditor(const DXBC::DXBCContainer *container, size_t reservationSize, bytebuf &outBlob); + ProgramEditor(const DXBC::DXBCContainer *container, bytebuf &outBlob); ~ProgramEditor(); // publish these interfaces @@ -75,25 +75,33 @@ public: using Program::GetPointerType; const rdcarray &GetAttributeSets() { return m_AttributeSets; } - // find existing type or metadata by name, returns NULL if not found - const rdcarray &GetTypes() const { return m_Types; } - const Type *GetTypeByName(const rdcstr &name); + const rdcarray &GetTypes() const { return m_Types; } + Type *CreateNamedStructType(const rdcstr &name, rdcarray members); + Type *CreateFunctionType(const Type *ret, rdcarray params); + Type *CreatePointerType(const Type *inner, Type::PointerAddrSpace addrSpace); Function *GetFunctionByName(const rdcstr &name); Metadata *GetMetadataByName(const rdcstr &name); - // add a type, function, or metadata, and return the pointer to the stored one (which can be - // referenced from elsewhere) - const Type *AddType(const Type &t); - const Function *DeclareFunction(const Function &f); - Metadata *AddMetadata(const Metadata &m); - NamedMetadata *AddNamedMetadata(const NamedMetadata &m); + Function *DeclareFunction(const Function &f); + Metadata *CreateMetadata(); + Metadata *CreateConstantMetadata(Constant *val); + Metadata *CreateConstantMetadata(uint32_t val); + Metadata *CreateConstantMetadata(uint8_t val); + Metadata *CreateConstantMetadata(bool val); + Metadata *CreateConstantMetadata(const char *str) { return CreateConstantMetadata(rdcstr(str)); } + Metadata *CreateConstantMetadata(const rdcstr &str); + NamedMetadata *CreateNamedMetadata(const rdcstr &name); // I think constants have to be unique, so this will return an existing constant (for simple cases // like integers or NULL) if it exists - const Constant *GetOrAddConstant(const Constant &c); - const Constant *GetOrAddConstant(Function *f, const Constant &c); + Constant *CreateConstant(const Constant &c); + Constant *CreateConstant(const Type *t, const rdcarray &members); - Instruction *AddInstruction(Function *f, size_t idx, const Instruction &inst); + 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(const Function *f); void RegisterUAV(DXILResourceType type, uint32_t space, uint32_t regBase, uint32_t regEnd, ResourceKind kind); @@ -101,63 +109,15 @@ public: private: bytebuf &m_OutBlob; - void Fixup(Type *&t); - void Fixup(Function *&f); - void Fixup(Block *&b, Function *oldf = NULL, Function *newf = NULL); - void Fixup(Instruction *&i, Function *oldf = NULL, Function *newf = NULL); - void Fixup(Constant *&c, Function *oldf = NULL, Function *newf = NULL); - void Fixup(Metadata *&m, Function *oldf = NULL, Function *newf = NULL); - void Fixup(Value &v, Function *oldf = NULL, Function *newf = NULL); + rdcarray m_Constants; - void Fixup(const Type *&t) { Fixup((Type *&)t); } - void Fixup(const Function *&f) { Fixup((Function *&)f); } - void Fixup(const Block *&b, Function *oldf = NULL, Function *newf = NULL) - { - Fixup((Block *&)b, oldf, newf); - } - void Fixup(const Instruction *&i, Function *oldf = NULL, Function *newf = NULL) - { - Fixup((Instruction *&)i, oldf, newf); - } - void Fixup(const Constant *&c, Function *oldf = NULL, Function *newf = NULL) - { - Fixup((Constant *&)c, oldf, newf); - } + Type *CreateNewType(); - bytebuf EncodeProgram() const; + bytebuf EncodeProgram(); - void EncodeConstants(LLVMBC::BitcodeWriter &writer, const rdcarray &values, - const rdcarray &constants) const; - void EncodeMetadata(LLVMBC::BitcodeWriter &writer, const rdcarray &values, - const rdcarray &meta) const; - - // these are arrays which hold the original program's storage, so all pointers remain valid. We - // then duplicate into the editable arrays - - // in the destructor before encoding we will look up any pointers that still point here and find - // the element in the current arrays - - // every array which might be mutated by editing must be here - rdcarray m_OldFunctions; - rdcarray m_OldTypes; - rdcarray m_OldConstants; - rdcarray m_OldMetadata; - - uint32_t m_InsertedInstructionID = 0xfffffff0; - -#if ENABLED(RDOC_DEVEL) - Function *m_DebugFunctionsData; - Type *m_DebugTypesData; - Constant *m_DebugConstantsData; - Metadata *m_DebugMetadataData; - - struct DebugFunctionData - { - Instruction *instructions; - Constant *constants; - }; - rdcarray m_DebugFunctions; -#endif + void EncodeConstants(LLVMBC::BitcodeWriter &writer, const rdcarray &values, + size_t firstIdx, size_t count) const; + void EncodeMetadata(LLVMBC::BitcodeWriter &writer, const rdcarray &meta) const; }; }; // namespace DXIL diff --git a/renderdoc/driver/shaders/dxil/dxil_debuginfo.cpp b/renderdoc/driver/shaders/dxil/dxil_debuginfo.cpp index 1dd65df7f..8fe08dfda 100644 --- a/renderdoc/driver/shaders/dxil/dxil_debuginfo.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_debuginfo.cpp @@ -29,20 +29,18 @@ namespace DXIL { -bool Program::ParseDebugMetaRecord(const LLVMBC::BlockOrRecord &metaRecord, Metadata &meta) +bool Program::ParseDebugMetaRecord(MetadataList &metadata, const LLVMBC::BlockOrRecord &metaRecord, + Metadata &meta) { LLVMBC::MetaDataRecord id = (LLVMBC::MetaDataRecord)metaRecord.id; -#define getNonNullMeta(id) &m_Metadata[size_t(id)] -#define getMeta(id) (id ? &m_Metadata[size_t(id - 1)] : NULL) -#define getMetaString(id) (id ? &m_Metadata[size_t(id - 1)].str : NULL) - if(id == LLVMBC::MetaDataRecord::FILE) { meta.isDistinct = (metaRecord.ops[0] & 0x1); - meta.dwarf = new DIFile(getMeta(metaRecord.ops[1]), getMeta(metaRecord.ops[2])); - meta.children = {getMeta(metaRecord.ops[1]), getMeta(metaRecord.ops[2])}; + meta.dwarf = + new DIFile(metadata.getOrNULL(metaRecord.ops[1]), metadata.getOrNULL(metaRecord.ops[2])); + meta.children = {metadata.getOrNULL(metaRecord.ops[1]), metadata.getOrNULL(metaRecord.ops[2])}; } else if(id == LLVMBC::MetaDataRecord::COMPILE_UNIT) { @@ -54,35 +52,39 @@ bool Program::ParseDebugMetaRecord(const LLVMBC::BlockOrRecord &metaRecord, Meta meta.isDistinct = true; meta.dwarf = new DICompileUnit( - DW_LANG(metaRecord.ops[1]), getMeta(metaRecord.ops[2]), getMetaString(metaRecord.ops[3]), - metaRecord.ops[4] != 0, getMetaString(metaRecord.ops[5]), metaRecord.ops[6], - getMetaString(metaRecord.ops[7]), metaRecord.ops[8], getMeta(metaRecord.ops[9]), - getMeta(metaRecord.ops[10]), getMeta(metaRecord.ops[11]), getMeta(metaRecord.ops[12]), - getMeta(metaRecord.ops[13])); - meta.children = {getMeta(metaRecord.ops[2]), getMeta(metaRecord.ops[9]), - getMeta(metaRecord.ops[10]), getMeta(metaRecord.ops[11]), - getMeta(metaRecord.ops[12]), getMeta(metaRecord.ops[13])}; + DW_LANG(metaRecord.ops[1]), metadata.getOrNULL(metaRecord.ops[2]), + metadata.getStringOrNULL(metaRecord.ops[3]), metaRecord.ops[4] != 0, + metadata.getStringOrNULL(metaRecord.ops[5]), metaRecord.ops[6], + metadata.getStringOrNULL(metaRecord.ops[7]), metaRecord.ops[8], + metadata.getOrNULL(metaRecord.ops[9]), metadata.getOrNULL(metaRecord.ops[10]), + metadata.getOrNULL(metaRecord.ops[11]), metadata.getOrNULL(metaRecord.ops[12]), + metadata.getOrNULL(metaRecord.ops[13])); + meta.children = { + metadata.getOrNULL(metaRecord.ops[2]), metadata.getOrNULL(metaRecord.ops[9]), + metadata.getOrNULL(metaRecord.ops[10]), metadata.getOrNULL(metaRecord.ops[11]), + metadata.getOrNULL(metaRecord.ops[12]), metadata.getOrNULL(metaRecord.ops[13])}; } else if(id == LLVMBC::MetaDataRecord::BASIC_TYPE) { meta.isDistinct = (metaRecord.ops[0] & 0x1); meta.dwarf = - new DIBasicType(DW_TAG(metaRecord.ops[1]), getMetaString(metaRecord.ops[2]), + new DIBasicType(DW_TAG(metaRecord.ops[1]), metadata.getStringOrNULL(metaRecord.ops[2]), metaRecord.ops[3], metaRecord.ops[4], DW_ENCODING(metaRecord.ops[5])); } else if(id == LLVMBC::MetaDataRecord::DERIVED_TYPE) { meta.isDistinct = (metaRecord.ops[0] & 0x1); - meta.dwarf = new DIDerivedType(DW_TAG(metaRecord.ops[1]), getMetaString(metaRecord.ops[2]), - getMeta(metaRecord.ops[3]), metaRecord.ops[4], - getMeta(metaRecord.ops[5]), getMeta(metaRecord.ops[6]), - metaRecord.ops[7], metaRecord.ops[8], metaRecord.ops[9], - DIFlags(metaRecord.ops[10]), getMeta(metaRecord.ops[11])); + meta.dwarf = new DIDerivedType( + DW_TAG(metaRecord.ops[1]), metadata.getStringOrNULL(metaRecord.ops[2]), + metadata.getOrNULL(metaRecord.ops[3]), metaRecord.ops[4], + metadata.getOrNULL(metaRecord.ops[5]), metadata.getOrNULL(metaRecord.ops[6]), + metaRecord.ops[7], metaRecord.ops[8], metaRecord.ops[9], DIFlags(metaRecord.ops[10]), + metadata.getOrNULL(metaRecord.ops[11])); - meta.children = {getMeta(metaRecord.ops[3]), getMeta(metaRecord.ops[5]), - getMeta(metaRecord.ops[6]), getMeta(metaRecord.ops[11])}; + meta.children = {metadata.getOrNULL(metaRecord.ops[3]), metadata.getOrNULL(metaRecord.ops[5]), + metadata.getOrNULL(metaRecord.ops[6]), metadata.getOrNULL(metaRecord.ops[11])}; } else if(id == LLVMBC::MetaDataRecord::COMPOSITE_TYPE) { @@ -90,58 +92,62 @@ bool Program::ParseDebugMetaRecord(const LLVMBC::BlockOrRecord &metaRecord, Meta // TODO handle forward declarations? meta.dwarf = new DICompositeType( - DW_TAG(metaRecord.ops[1]), getMetaString(metaRecord.ops[2]), getMeta(metaRecord.ops[3]), - metaRecord.ops[4], getMeta(metaRecord.ops[5]), getMeta(metaRecord.ops[6]), + DW_TAG(metaRecord.ops[1]), metadata.getStringOrNULL(metaRecord.ops[2]), + metadata.getOrNULL(metaRecord.ops[3]), metaRecord.ops[4], + metadata.getOrNULL(metaRecord.ops[5]), metadata.getOrNULL(metaRecord.ops[6]), metaRecord.ops[7], metaRecord.ops[8], metaRecord.ops[9], DIFlags(metaRecord.ops[10]), - getMeta(metaRecord.ops[11]), getMeta(metaRecord.ops[14])); + metadata.getOrNULL(metaRecord.ops[11]), metadata.getOrNULL(metaRecord.ops[14])); - meta.children = {getMeta(metaRecord.ops[3]), getMeta(metaRecord.ops[5]), - getMeta(metaRecord.ops[6]), getMeta(metaRecord.ops[11]), - getMeta(metaRecord.ops[14])}; + meta.children = {metadata.getOrNULL(metaRecord.ops[3]), metadata.getOrNULL(metaRecord.ops[5]), + metadata.getOrNULL(metaRecord.ops[6]), metadata.getOrNULL(metaRecord.ops[11]), + metadata.getOrNULL(metaRecord.ops[14])}; } else if(id == LLVMBC::MetaDataRecord::TEMPLATE_TYPE) { meta.isDistinct = (metaRecord.ops[0] & 0x1); - meta.dwarf = - new DITemplateTypeParameter(getMetaString(metaRecord.ops[1]), getMeta(metaRecord.ops[2])); + meta.dwarf = new DITemplateTypeParameter(metadata.getStringOrNULL(metaRecord.ops[1]), + metadata.getOrNULL(metaRecord.ops[2])); - meta.children = {getMeta(metaRecord.ops[2])}; + meta.children = {metadata.getOrNULL(metaRecord.ops[2])}; } else if(id == LLVMBC::MetaDataRecord::TEMPLATE_VALUE) { meta.isDistinct = (metaRecord.ops[0] & 0x1); - meta.dwarf = - new DITemplateValueParameter(DW_TAG(metaRecord.ops[1]), getMetaString(metaRecord.ops[2]), - getMeta(metaRecord.ops[3]), getMeta(metaRecord.ops[4])); + meta.dwarf = new DITemplateValueParameter( + DW_TAG(metaRecord.ops[1]), metadata.getStringOrNULL(metaRecord.ops[2]), + metadata.getOrNULL(metaRecord.ops[3]), metadata.getOrNULL(metaRecord.ops[4])); - meta.children = {getMeta(metaRecord.ops[3]), getMeta(metaRecord.ops[4])}; + meta.children = {metadata.getOrNULL(metaRecord.ops[3]), metadata.getOrNULL(metaRecord.ops[4])}; } else if(id == LLVMBC::MetaDataRecord::SUBPROGRAM) { meta.isDistinct = (metaRecord.ops[0] & 0x1); meta.dwarf = new DISubprogram( - getMeta(metaRecord.ops[1]), getMetaString(metaRecord.ops[2]), - getMetaString(metaRecord.ops[3]), getMeta(metaRecord.ops[4]), metaRecord.ops[5], - getMeta(metaRecord.ops[6]), metaRecord.ops[7] != 0, metaRecord.ops[8] != 0, metaRecord.ops[9], - getMeta(metaRecord.ops[10]), DW_VIRTUALITY(metaRecord.ops[11]), metaRecord.ops[12], - DIFlags(metaRecord.ops[13]), metaRecord.ops[14] != 0, getMeta(metaRecord.ops[15]), - getMeta(metaRecord.ops[16]), getMeta(metaRecord.ops[17]), getMeta(metaRecord.ops[18])); + metadata.getOrNULL(metaRecord.ops[1]), metadata.getStringOrNULL(metaRecord.ops[2]), + metadata.getStringOrNULL(metaRecord.ops[3]), metadata.getOrNULL(metaRecord.ops[4]), + metaRecord.ops[5], metadata.getOrNULL(metaRecord.ops[6]), metaRecord.ops[7] != 0, + metaRecord.ops[8] != 0, metaRecord.ops[9], metadata.getOrNULL(metaRecord.ops[10]), + DW_VIRTUALITY(metaRecord.ops[11]), metaRecord.ops[12], DIFlags(metaRecord.ops[13]), + metaRecord.ops[14] != 0, metadata.getOrNULL(metaRecord.ops[15]), + metadata.getOrNULL(metaRecord.ops[16]), metadata.getOrNULL(metaRecord.ops[17]), + metadata.getOrNULL(metaRecord.ops[18])); - meta.children = {getMeta(metaRecord.ops[1]), getMeta(metaRecord.ops[4]), - getMeta(metaRecord.ops[6]), getMeta(metaRecord.ops[10]), - getMeta(metaRecord.ops[14]), getMeta(metaRecord.ops[15]), - getMeta(metaRecord.ops[16]), getMeta(metaRecord.ops[17])}; + meta.children = { + metadata.getOrNULL(metaRecord.ops[1]), metadata.getOrNULL(metaRecord.ops[4]), + metadata.getOrNULL(metaRecord.ops[6]), metadata.getOrNULL(metaRecord.ops[10]), + metadata.getOrNULL(metaRecord.ops[14]), metadata.getOrNULL(metaRecord.ops[15]), + metadata.getOrNULL(metaRecord.ops[16]), metadata.getOrNULL(metaRecord.ops[17])}; } else if(id == LLVMBC::MetaDataRecord::SUBROUTINE_TYPE) { meta.isDistinct = (metaRecord.ops[0] & 0x1); - meta.dwarf = new DISubroutineType(getMeta(metaRecord.ops[2])); + meta.dwarf = new DISubroutineType(metadata.getOrNULL(metaRecord.ops[2])); - meta.children = {getMeta(metaRecord.ops[2])}; + meta.children = {metadata.getOrNULL(metaRecord.ops[2])}; } else if(id == LLVMBC::MetaDataRecord::GLOBAL_VAR) { @@ -152,14 +158,15 @@ bool Program::ParseDebugMetaRecord(const LLVMBC::BlockOrRecord &metaRecord, Meta if(version == 0) { meta.dwarf = new DIGlobalVariable( - getMeta(metaRecord.ops[1]), getMetaString(metaRecord.ops[2]), - getMetaString(metaRecord.ops[3]), getMeta(metaRecord.ops[4]), metaRecord.ops[5], - getMeta(metaRecord.ops[6]), metaRecord.ops[7] != 0, metaRecord.ops[8] != 0, - getMeta(metaRecord.ops[9]), getMeta(metaRecord.ops[10])); + metadata.getOrNULL(metaRecord.ops[1]), metadata.getStringOrNULL(metaRecord.ops[2]), + metadata.getStringOrNULL(metaRecord.ops[3]), metadata.getOrNULL(metaRecord.ops[4]), + metaRecord.ops[5], metadata.getOrNULL(metaRecord.ops[6]), metaRecord.ops[7] != 0, + metaRecord.ops[8] != 0, metadata.getOrNULL(metaRecord.ops[9]), + metadata.getOrNULL(metaRecord.ops[10])); - meta.children = {getMeta(metaRecord.ops[1]), getMeta(metaRecord.ops[4]), - getMeta(metaRecord.ops[6]), getMeta(metaRecord.ops[9]), - getMeta(metaRecord.ops[10])}; + meta.children = {metadata.getOrNULL(metaRecord.ops[1]), metadata.getOrNULL(metaRecord.ops[4]), + metadata.getOrNULL(metaRecord.ops[6]), metadata.getOrNULL(metaRecord.ops[9]), + metadata.getOrNULL(metaRecord.ops[10])}; } else { @@ -173,31 +180,33 @@ bool Program::ParseDebugMetaRecord(const LLVMBC::BlockOrRecord &metaRecord, Meta meta.debugLoc = new DebugLocation; meta.debugLoc->line = metaRecord.ops[1]; meta.debugLoc->col = metaRecord.ops[2]; - meta.debugLoc->scope = getNonNullMeta(metaRecord.ops[3]); - meta.debugLoc->inlinedAt = getMeta(metaRecord.ops[4]); + meta.debugLoc->scope = metadata.getDirect(metaRecord.ops[3]); + meta.debugLoc->inlinedAt = metadata.getOrNULL(metaRecord.ops[4]); - meta.children = {getNonNullMeta(metaRecord.ops[3]), getMeta(metaRecord.ops[4])}; + meta.children = {metadata.getDirect(metaRecord.ops[3]), metadata.getOrNULL(metaRecord.ops[4])}; } else if(id == LLVMBC::MetaDataRecord::LOCAL_VAR) { meta.isDistinct = (metaRecord.ops[0] & 0x1); meta.dwarf = new DILocalVariable( - DW_TAG(metaRecord.ops[1]), getMeta(metaRecord.ops[2]), getMetaString(metaRecord.ops[3]), - getMeta(metaRecord.ops[4]), metaRecord.ops[5], getMeta(metaRecord.ops[6]), - metaRecord.ops[7], DIFlags(metaRecord.ops[8]), metaRecord.ops[8]); + DW_TAG(metaRecord.ops[1]), metadata.getOrNULL(metaRecord.ops[2]), + metadata.getStringOrNULL(metaRecord.ops[3]), metadata.getOrNULL(metaRecord.ops[4]), + metaRecord.ops[5], metadata.getOrNULL(metaRecord.ops[6]), metaRecord.ops[7], + DIFlags(metaRecord.ops[8]), metaRecord.ops[8]); - meta.children = {getMeta(metaRecord.ops[2]), getMeta(metaRecord.ops[4]), - getMeta(metaRecord.ops[6])}; + meta.children = {metadata.getOrNULL(metaRecord.ops[2]), metadata.getOrNULL(metaRecord.ops[4]), + metadata.getOrNULL(metaRecord.ops[6])}; } else if(id == LLVMBC::MetaDataRecord::LEXICAL_BLOCK) { meta.isDistinct = (metaRecord.ops[0] & 0x1); - meta.dwarf = new DILexicalBlock(getMeta(metaRecord.ops[1]), getMeta(metaRecord.ops[2]), - metaRecord.ops[3], metaRecord.ops[4]); + meta.dwarf = new DILexicalBlock(metadata.getOrNULL(metaRecord.ops[1]), + metadata.getOrNULL(metaRecord.ops[2]), metaRecord.ops[3], + metaRecord.ops[4]); - meta.children = {getMeta(metaRecord.ops[1]), getMeta(metaRecord.ops[2])}; + meta.children = {metadata.getOrNULL(metaRecord.ops[1]), metadata.getOrNULL(metaRecord.ops[2])}; } else if(id == LLVMBC::MetaDataRecord::SUBRANGE) { diff --git a/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp b/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp index 4bbcf566c..c8d6298df 100644 --- a/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp @@ -38,69 +38,6 @@ namespace DXIL { -struct TypeOrderer -{ - rdcarray types; - - rdcarray visitedTypes = {NULL}; - rdcarray visitedMeta; - - void accumulate(const Type *t) - { - if(!t || visitedTypes.contains(t)) - return; - - visitedTypes.push_back(t); - - // LLVM doesn't do quite a depth-first search, so we replicate its search order to ensure types - // are printed in the same order - - rdcarray workingSet; - workingSet.push_back(t); - do - { - const Type *cur = workingSet.back(); - workingSet.pop_back(); - - types.push_back(cur); - - for(size_t i = 0; i < cur->members.size(); i++) - { - size_t idx = cur->members.size() - 1 - i; - if(!visitedTypes.contains(cur->members[idx])) - { - visitedTypes.push_back(cur->members[idx]); - workingSet.push_back(cur->members[idx]); - } - } - - if(!visitedTypes.contains(cur->inner)) - { - visitedTypes.push_back(cur->inner); - workingSet.push_back(cur->inner); - } - } while(!workingSet.empty()); - } - - void accumulate(const Metadata *m) - { - auto it = std::lower_bound(visitedMeta.begin(), visitedMeta.end(), m); - if(it != visitedMeta.end() && *it == m) - return; - visitedMeta.insert(it - visitedMeta.begin(), m); - - if(m->type) - accumulate(m->type); - - if(!m->value.empty()) - accumulate(m->value.GetType()); - - for(const Metadata *c : m->children) - if(c) - accumulate(c); - } -}; - bool needsEscaping(const rdcstr &name) { return name.find_first_not_of( @@ -376,47 +313,12 @@ void Program::MakeDisassemblyString() int instructionLine = 6; - TypeOrderer typeOrderer; - rdcarray orderedTypes; - - for(size_t i = 0; i < m_GlobalVars.size(); i++) - { - const GlobalVar &g = m_GlobalVars[i]; - typeOrderer.accumulate(g.type); - - if(g.initialiser) - typeOrderer.accumulate(g.initialiser->type); - } - - for(size_t i = 0; i < m_Functions.size(); i++) - { - const Function &func = m_Functions[i]; - - // the function type will also accumulate the arguments - typeOrderer.accumulate(func.funcType); - - for(const Instruction &inst : func.instructions) - { - typeOrderer.accumulate(inst.type); - for(size_t a = 0; a < inst.args.size(); a++) - { - if(inst.args[a].type != ValueType::Literal && inst.args[a].type != ValueType::BasicBlock) - typeOrderer.accumulate(inst.args[a].GetType()); - } - - for(size_t m = 0; m < inst.attachedMeta.size(); m++) - typeOrderer.accumulate(inst.attachedMeta[m].second); - } - } - - for(size_t i = 0; i < m_NamedMeta.size(); i++) - { - typeOrderer.accumulate(&m_NamedMeta[i]); - } + LLVMOrderAccumulator accum; + accum.processGlobals(this); bool printedTypes = false; - for(const Type *typ : typeOrderer.types) + for(const Type *typ : accum.printOrderTypes) { if(typ->type == Type::Struct && !typ->name.empty()) { @@ -448,7 +350,7 @@ void Program::MakeDisassemblyString() for(size_t i = 0; i < m_GlobalVars.size(); i++) { - const GlobalVar &g = m_GlobalVars[i]; + const GlobalVar &g = *m_GlobalVars[i]; m_Disassembly += StringFormat::Fmt("@%s = ", escapeStringIfNeeded(g.name).c_str()); switch(g.flags & GlobalFlags::LinkageMask) @@ -504,18 +406,23 @@ void Program::MakeDisassemblyString() rdcstr namedMeta; + rdcarray metaSlots; + uint32_t nextMetaSlot = 0; + // need to disassemble the named metadata here so the IDs are assigned first before any functions // get dibs for(size_t i = 0; i < m_NamedMeta.size(); i++) { - namedMeta += StringFormat::Fmt("!%s = %s!{", m_NamedMeta[i].name.c_str(), - m_NamedMeta[i].isDistinct ? "distinct " : ""); - for(size_t m = 0; m < m_NamedMeta[i].children.size(); m++) + const NamedMetadata &m = *m_NamedMeta[i]; + + namedMeta += StringFormat::Fmt("!%s = %s!{", m.name.c_str(), m.isDistinct ? "distinct " : ""); + for(size_t c = 0; c < m.children.size(); c++) { - if(m != 0) + if(c != 0) namedMeta += ", "; - if(m_NamedMeta[i].children[m]) - namedMeta += StringFormat::Fmt("!%u", GetOrAssignMetaID(m_NamedMeta[i].children[m])); + if(m.children[c]) + namedMeta += + StringFormat::Fmt("!%u", GetOrAssignMetaSlot(metaSlots, nextMetaSlot, m.children[c])); else namedMeta += "null"; } @@ -537,87 +444,97 @@ void Program::MakeDisassemblyString() for(size_t i = 0; i < m_Functions.size(); i++) { - Function &func = m_Functions[i]; + const Function &func = *m_Functions[i]; - auto argToString = [this, &func](Value v, bool withTypes, const rdcstr &attrString = "") { + accum.processFunction(m_Functions[i]); + + auto argToString = [this, &metaSlots, &nextMetaSlot](const Value *v, bool withTypes, + const rdcstr &attrString = "") { rdcstr ret; - switch(v.type) + + if(const Literal *lit = cast(v)) { - case ValueType::Unknown: - case ValueType::Alias: ret = "???"; break; - case ValueType::Literal: - if(withTypes) - ret += "i32 "; - ret += attrString; - ret += StringFormat::Fmt("%llu", v.literal); - break; - case ValueType::Metadata: - if(withTypes) - ret += "metadata "; - ret += attrString; - if(m_Metadata.begin() <= v.meta && v.meta < m_Metadata.end()) + if(withTypes) + ret += "i32 "; + ret += attrString; + ret += StringFormat::Fmt("%llu", lit->literal); + } + else if(const Metadata *meta = cast(v)) + { + const Metadata &m = *meta; + if(withTypes) + ret += "metadata "; + ret += attrString; + { + const Constant *metaConst = cast(m.value); + const GlobalVar *metaGlobal = cast(m.value); + const Instruction *metaInst = cast(m.value); + if(m.isConstant && metaConst && + (metaConst->type->type == Type::Scalar || metaConst->type->type == Type::Vector || + metaConst->isUndef() || metaConst->isNULL() || + metaConst->type->name.beginsWith("class.matrix."))) { - const Metadata &m = *v.meta; - if(m.isConstant && m.value.type == ValueType::Constant && - (m.value.constant->type->type == Type::Scalar || - m.value.constant->type->type == Type::Vector || m.value.constant->undef || - m.value.constant->nullconst || - m.value.constant->type->name.beginsWith("class.matrix."))) - { - ret += m.value.constant->toString(withTypes); - } - else if(m.isConstant && m.value.type == ValueType::GlobalVar) - { - if(withTypes) - ret += m.value.global->type->toString() + " "; - ret += "@" + escapeStringIfNeeded(m.value.global->name); - } - else - { - ret += StringFormat::Fmt("!%u", GetOrAssignMetaID((Metadata *)&m)); - } + ret += metaConst->toString(withTypes); + } + else if(m.isConstant && metaInst) + { + ret += m.valString(); + } + else if(m.isConstant && metaGlobal) + { + if(withTypes) + ret += metaGlobal->type->toString() + " "; + ret += "@" + escapeStringIfNeeded(metaGlobal->name); } else { - ret += v.meta->refString(); + ret += StringFormat::Fmt("!%u", + GetOrAssignMetaSlot(metaSlots, nextMetaSlot, (Metadata *)&m)); } - break; - case ValueType::Function: - ret += attrString; - ret = "@" + escapeStringIfNeeded(v.function->name); - break; - case ValueType::GlobalVar: - if(withTypes) - ret = v.global->type->toString() + " "; - ret += attrString; - ret += "@" + escapeStringIfNeeded(v.global->name); - break; - case ValueType::Constant: - ret += attrString; - ret = v.constant->toString(withTypes); - break; - case ValueType::Instruction: - { - if(withTypes) - ret = v.instruction->type->toString() + " "; - ret += attrString; - if(v.instruction->name.empty()) - ret += StringFormat::Fmt("%%%u", v.instruction->resultID); - else - ret += StringFormat::Fmt("%%%s", escapeStringIfNeeded(v.instruction->name).c_str()); - break; - } - case ValueType::BasicBlock: - { - if(withTypes) - ret = "label "; - ret += attrString; - if(v.block->name.empty()) - ret += StringFormat::Fmt("%%%u", v.block->resultID); - else - ret += StringFormat::Fmt("%%%s", escapeStringIfNeeded(v.block->name).c_str()); } } + else if(const Function *func = cast(v)) + { + ret += attrString; + ret = "@" + escapeStringIfNeeded(func->name); + } + else if(const GlobalVar *global = cast(v)) + { + if(withTypes) + ret = global->type->toString() + " "; + ret += attrString; + ret += "@" + escapeStringIfNeeded(global->name); + } + else if(const Constant *c = cast(v)) + { + ret += attrString; + ret = c->toString(withTypes); + } + else if(const Instruction *inst = cast(v)) + { + if(withTypes) + ret = inst->type->toString() + " "; + ret += attrString; + if(inst->getName().empty()) + ret += StringFormat::Fmt("%%%u", inst->slot); + else + ret += StringFormat::Fmt("%%%s", escapeStringIfNeeded(inst->getName()).c_str()); + } + else if(const Block *block = cast(v)) + { + if(withTypes) + ret = "label "; + ret += attrString; + if(block->name.empty()) + ret += StringFormat::Fmt("%%%u", block->slot); + else + ret += StringFormat::Fmt("%%%s", escapeStringIfNeeded(block->name).c_str()); + } + else + { + ret = "???"; + } + return ret; }; @@ -630,7 +547,7 @@ void Program::MakeDisassemblyString() m_Disassembly += (func.external ? "declare " : "define "); m_Disassembly += - func.funcType->declFunction("@" + escapeStringIfNeeded(func.name), func.args, func.attrs); + func.type->declFunction("@" + escapeStringIfNeeded(func.name), func.args, func.attrs); if(func.attrs && func.attrs->functionSlot) m_Disassembly += StringFormat::Fmt(" #%u", funcAttrGroups.indexOf(func.attrs->functionSlot)); @@ -643,23 +560,23 @@ void Program::MakeDisassemblyString() size_t curBlock = 0; // if the first block has a name, use it - if(!func.blocks[curBlock].name.empty()) + if(!func.blocks[curBlock]->name.empty()) { m_Disassembly += - StringFormat::Fmt("%s:\n", escapeStringIfNeeded(func.blocks[curBlock].name).c_str()); + StringFormat::Fmt("%s:\n", escapeStringIfNeeded(func.blocks[curBlock]->name).c_str()); instructionLine++; } for(size_t funcIdx = 0; funcIdx < func.instructions.size(); funcIdx++) { - Instruction &inst = func.instructions[funcIdx]; + Instruction &inst = *func.instructions[funcIdx]; inst.disassemblyLine = instructionLine; m_Disassembly += " "; - if(!inst.name.empty()) - m_Disassembly += "%" + escapeStringIfNeeded(inst.name) + " = "; - else if(inst.resultID != ~0U) - m_Disassembly += StringFormat::Fmt("%%%u = ", inst.resultID); + if(!inst.getName().empty()) + m_Disassembly += "%" + escapeStringIfNeeded(inst.getName()) + " = "; + else if(inst.slot != ~0U) + m_Disassembly += StringFormat::Fmt("%%%u = ", inst.slot); bool debugCall = false; @@ -668,14 +585,16 @@ void Program::MakeDisassemblyString() case Operation::NoOp: m_Disassembly += "??? "; break; case Operation::Call: { + rdcstr funcCallName = inst.getFuncCall()->name; m_Disassembly += "call " + inst.type->toString(); - m_Disassembly += " @" + escapeStringIfNeeded(inst.funcCall->name); + m_Disassembly += " @" + escapeStringIfNeeded(funcCallName); m_Disassembly += "("; bool first = true; + const AttributeSet *paramAttrs = inst.getParamAttrs(); // attribute args start from 1 size_t argIdx = 1; - for(Value &s : inst.args) + for(const Value *s : inst.args) { if(!first) m_Disassembly += ", "; @@ -683,10 +602,10 @@ void Program::MakeDisassemblyString() // see if we have param attrs for this param rdcstr attrString; - if(inst.paramAttrs && argIdx < inst.paramAttrs->groupSlots.size() && - inst.paramAttrs->groupSlots[argIdx]) + if(paramAttrs && argIdx < paramAttrs->groupSlots.size() && + paramAttrs->groupSlots[argIdx]) { - attrString = inst.paramAttrs->groupSlots[argIdx]->toString(true) + " "; + attrString = paramAttrs->groupSlots[argIdx]->toString(true) + " "; } m_Disassembly += argToString(s, true, attrString); @@ -694,11 +613,11 @@ void Program::MakeDisassemblyString() argIdx++; } m_Disassembly += ")"; - debugCall = inst.funcCall->name.beginsWith("llvm.dbg."); + debugCall = funcCallName.beginsWith("llvm.dbg."); - if(inst.paramAttrs && inst.paramAttrs->functionSlot) + if(paramAttrs && paramAttrs->functionSlot) m_Disassembly += - StringFormat::Fmt(" #%u", funcAttrGroups.indexOf(inst.paramAttrs->functionSlot)); + StringFormat::Fmt(" #%u", funcAttrGroups.indexOf(paramAttrs->functionSlot)); break; } case Operation::Trunc: @@ -743,7 +662,7 @@ void Program::MakeDisassemblyString() m_Disassembly += "extractvalue "; m_Disassembly += argToString(inst.args[0], true); for(size_t n = 1; n < inst.args.size(); n++) - m_Disassembly += StringFormat::Fmt(", %llu", inst.args[n].literal); + m_Disassembly += StringFormat::Fmt(", %llu", cast(inst.args[n])->literal); break; } case Operation::FAdd: @@ -788,7 +707,7 @@ void Program::MakeDisassemblyString() default: break; } - rdcstr opFlagsStr = ToStr(inst.opFlags); + rdcstr opFlagsStr = ToStr(inst.opFlags()); { int offs = opFlagsStr.indexOf('|'); while(offs >= 0) @@ -798,11 +717,11 @@ void Program::MakeDisassemblyString() } } m_Disassembly += opFlagsStr; - if(inst.opFlags != InstructionFlags::NoFlags) + if(inst.opFlags() != InstructionFlags::NoFlags) m_Disassembly += " "; bool first = true; - for(Value &s : inst.args) + for(const Value *s : inst.args) { if(!first) m_Disassembly += ", "; @@ -820,18 +739,18 @@ void Program::MakeDisassemblyString() m_Disassembly += "alloca "; m_Disassembly += inst.type->inner->toString(); if(inst.align > 0) - m_Disassembly += StringFormat::Fmt(", align %u", inst.align); + m_Disassembly += StringFormat::Fmt(", align %u", (1U << inst.align) >> 1); break; } case Operation::GetElementPtr: { m_Disassembly += "getelementptr "; - if(inst.opFlags & InstructionFlags::InBounds) + if(inst.opFlags() & InstructionFlags::InBounds) m_Disassembly += "inbounds "; - m_Disassembly += inst.args[0].GetType()->inner->toString(); + m_Disassembly += inst.args[0]->type->inner->toString(); m_Disassembly += ", "; bool first = true; - for(Value &s : inst.args) + for(const Value *s : inst.args) { if(!first) m_Disassembly += ", "; @@ -844,12 +763,12 @@ void Program::MakeDisassemblyString() case Operation::Load: { m_Disassembly += "load "; - if(inst.opFlags & InstructionFlags::Volatile) + if(inst.opFlags() & InstructionFlags::Volatile) m_Disassembly += "volatile "; m_Disassembly += inst.type->toString(); m_Disassembly += ", "; bool first = true; - for(Value &s : inst.args) + for(const Value *s : inst.args) { if(!first) m_Disassembly += ", "; @@ -858,19 +777,19 @@ void Program::MakeDisassemblyString() first = false; } if(inst.align > 0) - m_Disassembly += StringFormat::Fmt(", align %u", inst.align); + m_Disassembly += StringFormat::Fmt(", align %u", (1U << inst.align) >> 1); break; } case Operation::Store: { m_Disassembly += "store "; - if(inst.opFlags & InstructionFlags::Volatile) + if(inst.opFlags() & InstructionFlags::Volatile) m_Disassembly += "volatile "; m_Disassembly += argToString(inst.args[1], true); m_Disassembly += ", "; m_Disassembly += argToString(inst.args[0], true); if(inst.align > 0) - m_Disassembly += StringFormat::Fmt(", align %u", inst.align); + m_Disassembly += StringFormat::Fmt(", align %u", (1U << inst.align) >> 1); break; } case Operation::FOrdFalse: @@ -891,7 +810,7 @@ void Program::MakeDisassemblyString() case Operation::FOrdTrue: { m_Disassembly += "fcmp "; - rdcstr opFlagsStr = ToStr(inst.opFlags); + rdcstr opFlagsStr = ToStr(inst.opFlags()); { int offs = opFlagsStr.indexOf('|'); while(offs >= 0) @@ -901,7 +820,7 @@ void Program::MakeDisassemblyString() } } m_Disassembly += opFlagsStr; - if(inst.opFlags != InstructionFlags::NoFlags) + if(inst.opFlags() != InstructionFlags::NoFlags) m_Disassembly += " "; switch(inst.op) { @@ -1005,7 +924,7 @@ void Program::MakeDisassemblyString() m_Disassembly += argToString(inst.args[1], true); for(size_t a = 2; a < inst.args.size(); a++) { - m_Disassembly += ", " + ToStr(inst.args[a].literal); + m_Disassembly += ", " + ToStr(cast(inst.args[a])->literal); } break; } @@ -1062,9 +981,9 @@ void Program::MakeDisassemblyString() case Operation::Fence: { m_Disassembly += "fence "; - if(inst.opFlags & InstructionFlags::SingleThread) + if(inst.opFlags() & InstructionFlags::SingleThread) m_Disassembly += "singlethread "; - switch((inst.opFlags & InstructionFlags::SuccessOrderMask)) + switch((inst.opFlags() & InstructionFlags::SuccessOrderMask)) { case InstructionFlags::SuccessUnordered: m_Disassembly += "unordered"; break; case InstructionFlags::SuccessMonotonic: m_Disassembly += "monotonic"; break; @@ -1080,12 +999,12 @@ void Program::MakeDisassemblyString() case Operation::LoadAtomic: { m_Disassembly += "load atomic "; - if(inst.opFlags & InstructionFlags::Volatile) + if(inst.opFlags() & InstructionFlags::Volatile) m_Disassembly += "volatile "; m_Disassembly += inst.type->toString(); m_Disassembly += ", "; bool first = true; - for(Value &s : inst.args) + for(const Value *s : inst.args) { if(!first) m_Disassembly += ", "; @@ -1093,30 +1012,30 @@ void Program::MakeDisassemblyString() m_Disassembly += argToString(s, true); first = false; } - m_Disassembly += StringFormat::Fmt(", align %u", inst.align); + m_Disassembly += StringFormat::Fmt(", align %u", (1U << inst.align) >> 1); break; } case Operation::StoreAtomic: { m_Disassembly += "store atomic "; - if(inst.opFlags & InstructionFlags::Volatile) + if(inst.opFlags() & InstructionFlags::Volatile) m_Disassembly += "volatile "; m_Disassembly += argToString(inst.args[1], true); m_Disassembly += ", "; m_Disassembly += argToString(inst.args[0], true); - m_Disassembly += StringFormat::Fmt(", align %u", inst.align); + m_Disassembly += StringFormat::Fmt(", align %u", (1U << inst.align) >> 1); break; } case Operation::CompareExchange: { m_Disassembly += "cmpxchg "; - if(inst.opFlags & InstructionFlags::Weak) + if(inst.opFlags() & InstructionFlags::Weak) m_Disassembly += "weak "; - if(inst.opFlags & InstructionFlags::Volatile) + if(inst.opFlags() & InstructionFlags::Volatile) m_Disassembly += "volatile "; bool first = true; - for(Value &s : inst.args) + for(const Value *s : inst.args) { if(!first) m_Disassembly += ", "; @@ -1126,9 +1045,9 @@ void Program::MakeDisassemblyString() } m_Disassembly += " "; - if(inst.opFlags & InstructionFlags::SingleThread) + if(inst.opFlags() & InstructionFlags::SingleThread) m_Disassembly += "singlethread "; - switch((inst.opFlags & InstructionFlags::SuccessOrderMask)) + switch((inst.opFlags() & InstructionFlags::SuccessOrderMask)) { case InstructionFlags::SuccessUnordered: m_Disassembly += "unordered"; break; case InstructionFlags::SuccessMonotonic: m_Disassembly += "monotonic"; break; @@ -1141,7 +1060,7 @@ void Program::MakeDisassemblyString() default: break; } m_Disassembly += " "; - switch((inst.opFlags & InstructionFlags::FailureOrderMask)) + switch((inst.opFlags() & InstructionFlags::FailureOrderMask)) { case InstructionFlags::FailureUnordered: m_Disassembly += "unordered"; break; case InstructionFlags::FailureMonotonic: m_Disassembly += "monotonic"; break; @@ -1168,7 +1087,7 @@ void Program::MakeDisassemblyString() case Operation::AtomicUMin: { m_Disassembly += "atomicrmw "; - if(inst.opFlags & InstructionFlags::Volatile) + if(inst.opFlags() & InstructionFlags::Volatile) m_Disassembly += "volatile "; switch(inst.op) { @@ -1187,7 +1106,7 @@ void Program::MakeDisassemblyString() } bool first = true; - for(Value &s : inst.args) + for(const Value *s : inst.args) { if(!first) m_Disassembly += ", "; @@ -1197,9 +1116,9 @@ void Program::MakeDisassemblyString() } m_Disassembly += " "; - if(inst.opFlags & InstructionFlags::SingleThread) + if(inst.opFlags() & InstructionFlags::SingleThread) m_Disassembly += "singlethread "; - switch((inst.opFlags & InstructionFlags::SuccessOrderMask)) + switch((inst.opFlags() & InstructionFlags::SuccessOrderMask)) { case InstructionFlags::SuccessUnordered: m_Disassembly += "unordered"; break; case InstructionFlags::SuccessMonotonic: m_Disassembly += "monotonic"; break; @@ -1219,16 +1138,18 @@ void Program::MakeDisassemblyString() { DebugLocation &debugLoc = m_DebugLocations[inst.debugLoc]; - m_Disassembly += StringFormat::Fmt(", !dbg !%u", GetOrAssignMetaID(debugLoc)); + m_Disassembly += StringFormat::Fmt( + ", !dbg !%u", GetOrAssignMetaSlot(metaSlots, nextMetaSlot, debugLoc)); } - if(!inst.attachedMeta.empty()) + const AttachedMetadata &attachedMeta = inst.getAttachedMeta(); + if(!attachedMeta.empty()) { - for(size_t m = 0; m < inst.attachedMeta.size(); m++) + for(size_t m = 0; m < attachedMeta.size(); m++) { - m_Disassembly += - StringFormat::Fmt(", !%s !%u", m_Kinds[(size_t)inst.attachedMeta[m].first].c_str(), - GetOrAssignMetaID(inst.attachedMeta[m].second)); + m_Disassembly += StringFormat::Fmt( + ", !%s !%u", m_Kinds[(size_t)attachedMeta[m].first].c_str(), + GetOrAssignMetaSlot(metaSlots, nextMetaSlot, attachedMeta[m].second)); } } @@ -1242,15 +1163,15 @@ void Program::MakeDisassemblyString() } } - if(debugCall && inst.funcCall) + if(debugCall) { size_t varIdx = 0, exprIdx = 0; - if(inst.funcCall->name == "llvm.dbg.value") + if(inst.getFuncCall()->name == "llvm.dbg.value") { varIdx = 2; exprIdx = 3; } - else if(inst.funcCall->name == "llvm.dbg.declare") + else if(inst.getFuncCall()->name == "llvm.dbg.declare") { varIdx = 1; exprIdx = 2; @@ -1258,23 +1179,25 @@ void Program::MakeDisassemblyString() if(varIdx > 0) { - RDCASSERT(inst.args[varIdx].type == ValueType::Metadata); - RDCASSERT(inst.args[exprIdx].type == ValueType::Metadata); - m_Disassembly += StringFormat::Fmt( - " ; var:%s ", escapeString(GetDebugVarName(inst.args[varIdx].meta->dwarf)).c_str()); - m_Disassembly += inst.args[exprIdx].meta->valString(); + Metadata *var = cast(inst.args[varIdx]); + Metadata *expr = cast(inst.args[exprIdx]); + RDCASSERT(var); + RDCASSERT(expr); + m_Disassembly += + StringFormat::Fmt(" ; var:%s ", escapeString(GetDebugVarName(var->dwarf)).c_str()); + m_Disassembly += expr->valString(); - rdcstr funcName = GetFunctionScopeName(inst.args[varIdx].meta->dwarf); + rdcstr funcName = GetFunctionScopeName(var->dwarf); if(!funcName.empty()) m_Disassembly += StringFormat::Fmt(" func:%s", escapeString(funcName).c_str()); } } - if(inst.funcCall && inst.funcCall->name.beginsWith("dx.op.")) + if(inst.getFuncCall() && inst.getFuncCall()->name.beginsWith("dx.op.")) { - if(inst.args[0].type == ValueType::Constant) + if(Constant *op = cast(inst.args[0])) { - uint32_t opcode = inst.args[0].constant->val.u32v[0]; + uint32_t opcode = op->getU32(); if(opcode < ARRAY_COUNT(funcSigs)) { m_Disassembly += " ; "; @@ -1283,17 +1206,18 @@ void Program::MakeDisassemblyString() } } - if(inst.funcCall && inst.funcCall->name.beginsWith("dx.op.annotateHandle")) + if(inst.getFuncCall() && inst.getFuncCall()->name.beginsWith("dx.op.annotateHandle")) { - if(inst.args[2].type == ValueType::Constant) + if(const Constant *props = cast(inst.args[2])) { - const Constant *props = inst.args[2].constant; - if(props && !props->nullconst && props->members.size() == 2 && - props->members[0].type == ValueType::Constant) + const Constant *packed[2]; + if(props && !props->isNULL() && props->getMembers().size() == 2 && + (packed[0] = cast(props->getMembers()[0])) != NULL && + (packed[1] = cast(props->getMembers()[1])) != NULL) { uint32_t packedProps[2] = {}; - packedProps[0] = props->members[0].constant->val.u32v[0]; - packedProps[1] = props->members[1].constant->val.u32v[0]; + packedProps[0] = packed[0]->getU32(); + packedProps[1] = packed[1]->getU32(); bool uav = (packedProps[0] & (1 << 12)) != 0; ResourceKind resKind = (ResourceKind)(packedProps[0] & 0xFF); @@ -1420,11 +1344,11 @@ void Program::MakeDisassemblyString() rdcstr labelName; - if(func.blocks[curBlock].name.empty()) - labelName = StringFormat::Fmt(";