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(";