Files
renderdoc/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp
T

1283 lines
43 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2021 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "dxil_bytecode_editor.h"
#include "driver/shaders/dxbc/dxbc_container.h"
#include "maths/half_convert.h"
#include "dxil_bytecode.h"
#include "llvm_encoder.h"
DXIL::ProgramEditor::ProgramEditor(const DXBC::DXBCContainer *container, bytebuf &outBlob)
: Program(container->GetDXILByteCode()->GetBytes().data(),
container->GetDXILByteCode()->GetBytes().size()),
m_OutBlob(outBlob)
{
m_OutBlob = container->GetShaderBlob();
}
DXIL::ProgramEditor::~ProgramEditor()
{
DXBC::DXBCContainer::ReplaceDXILBytecode(m_OutBlob, EncodeProgram());
}
#define getAttribID(a) uint64_t(a - m_Attributes.begin())
#define getTypeID(t) uint64_t(t - m_Types.begin())
#define getMetaID(m) uint64_t(m - m_Metadata.begin())
#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 DXIL::ProgramEditor::EncodeProgram() const
{
rdcarray<Value> values = m_Values;
bytebuf ret;
LLVMBC::BitcodeWriter writer(ret);
LLVMBC::BitcodeWriter::Config cfg = {};
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.maxGlobalType = RDCMAX(typeIndex, cfg.maxGlobalType);
}
for(size_t i = 0; i < m_Functions.size(); i++)
cfg.maxAlign = RDCMAX(m_Functions[i].align, cfg.maxAlign);
for(size_t i = 0; i < m_Metadata.size(); i++)
{
if(m_Metadata[i].isString)
cfg.hasMetaString = true;
if(m_Metadata[i].debugLoc)
cfg.hasDebugLoc = true;
}
for(size_t i = 0; i < m_NamedMeta.size(); i++)
{
if(m_NamedMeta[i].isString)
cfg.hasMetaString = true;
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();
writer.ConfigureSizes(cfg);
writer.BeginBlock(LLVMBC::KnownBlock::MODULE_BLOCK);
writer.Record(LLVMBC::ModuleRecord::VERSION, 1U);
{
writer.ModuleBlockInfo();
}
if(!m_AttributeGroups.empty())
{
writer.BeginBlock(LLVMBC::KnownBlock::PARAMATTR_GROUP_BLOCK);
rdcarray<uint64_t> vals;
for(size_t i = 0; i < m_AttributeGroups.size(); i++)
{
if(m_AttributeGroups[i].valid)
{
const Attributes &group = m_AttributeGroups[i];
vals.clear();
vals.push_back(i);
vals.push_back(group.index);
// decompose params bitfield into bits
if(group.params != Attribute::None)
{
uint64_t params = (uint64_t)group.params;
for(uint64_t p = 0; p < 64; p++)
{
if((params & (1ULL << p)) != 0)
{
switch(Attribute(1ULL << p))
{
case Attribute::Alignment:
{
vals.push_back(1);
vals.push_back(p);
vals.push_back(group.align);
break;
}
case Attribute::StackAlignment:
{
vals.push_back(1);
vals.push_back(p);
vals.push_back(group.stackAlign);
break;
}
case Attribute::Dereferenceable:
{
vals.push_back(1);
vals.push_back(p);
vals.push_back(group.derefBytes);
break;
}
case Attribute::DereferenceableOrNull:
{
vals.push_back(1);
vals.push_back(p);
vals.push_back(group.derefOrNullBytes);
break;
}
default:
{
// this attribute just exists or doesn't
vals.push_back(0);
vals.push_back(p);
}
}
}
}
}
if(!group.strs.empty())
{
for(const rdcpair<rdcstr, rdcstr> &strAttr : group.strs)
{
if(strAttr.second.empty())
vals.push_back(3);
else
vals.push_back(4);
// iterate including NULL terminator
for(size_t c = 0; c < strAttr.first.size() + 1; c++)
vals.push_back(uint64_t(strAttr.first[c]));
for(size_t c = 0; !strAttr.second.empty() && c < strAttr.second.size() + 1; c++)
vals.push_back(uint64_t(strAttr.second[c]));
}
}
writer.Record(LLVMBC::ParamAttrGroupRecord::ENTRY, vals);
}
}
writer.EndBlock();
}
if(!m_Attributes.empty())
{
writer.BeginBlock(LLVMBC::KnownBlock::PARAMATTR_BLOCK);
for(size_t i = 0; i < m_Attributes.size(); i++)
writer.Record(LLVMBC::ParamAttrRecord::ENTRY, m_Attributes[i].groups);
writer.EndBlock();
}
{
writer.BeginBlock(LLVMBC::KnownBlock::TYPE_BLOCK);
writer.Record(LLVMBC::TypeRecord::NUMENTRY, (uint32_t)m_Types.size());
for(size_t i = 0; i < m_Types.size(); i++)
{
if(m_Types[i].isVoid())
{
writer.Record(LLVMBC::TypeRecord::VOID);
}
else if(m_Types[i].type == Type::Label)
{
writer.Record(LLVMBC::TypeRecord::LABEL);
}
else if(m_Types[i].type == Type::Metadata)
{
writer.Record(LLVMBC::TypeRecord::METADATA);
}
else if(m_Types[i].type == Type::Scalar && m_Types[i].scalarType == Type::Float)
{
if(m_Types[i].bitWidth == 16)
writer.Record(LLVMBC::TypeRecord::HALF);
else if(m_Types[i].bitWidth == 32)
writer.Record(LLVMBC::TypeRecord::FLOAT);
else if(m_Types[i].bitWidth == 64)
writer.Record(LLVMBC::TypeRecord::DOUBLE);
}
else if(m_Types[i].type == Type::Scalar && m_Types[i].scalarType == Type::Int)
{
writer.Record(LLVMBC::TypeRecord::INTEGER, m_Types[i].bitWidth);
}
else if(m_Types[i].type == Type::Vector)
{
writer.Record(LLVMBC::TypeRecord::VECTOR,
{m_Types[i].elemCount, getTypeID(m_Types[i].inner)});
}
else if(m_Types[i].type == Type::Array)
{
writer.Record(LLVMBC::TypeRecord::ARRAY, {m_Types[i].elemCount, getTypeID(m_Types[i].inner)});
}
else if(m_Types[i].type == Type::Pointer)
{
writer.Record(LLVMBC::TypeRecord::POINTER,
{getTypeID(m_Types[i].inner), (uint64_t)m_Types[i].addrSpace});
}
else if(m_Types[i].type == Type::Struct)
{
if(m_Types[i].members.empty())
{
writer.Record(LLVMBC::TypeRecord::OPAQUE);
}
else
{
LLVMBC::TypeRecord type = LLVMBC::TypeRecord::STRUCT_ANON;
if(!m_Types[i].name.empty())
{
writer.Record(LLVMBC::TypeRecord::STRUCT_NAME, m_Types[i].name);
type = LLVMBC::TypeRecord::STRUCT_NAMED;
}
rdcarray<uint64_t> vals;
vals.push_back(m_Types[i].packedStruct ? 1 : 0);
for(const Type *t : m_Types[i].members)
vals.push_back(getTypeID(t));
writer.Record(type, vals);
}
}
else if(m_Types[i].type == Type::Function)
{
rdcarray<uint64_t> vals;
vals.push_back(m_Types[i].vararg ? 1 : 0);
vals.push_back(getTypeID(m_Types[i].inner));
for(const Type *t : m_Types[i].members)
vals.push_back(getTypeID(t));
writer.Record(LLVMBC::TypeRecord::FUNCTION, vals);
}
}
writer.EndBlock();
}
// COMDAT would be next, but we don't read these (DXIL seems not to use them...?)
if(!m_Triple.empty())
writer.Record(LLVMBC::ModuleRecord::TRIPLE, m_Triple);
if(!m_Datalayout.empty())
writer.Record(LLVMBC::ModuleRecord::DATALAYOUT, m_Datalayout);
// inline asm would go here
// write the sections
for(size_t i = 0; i < m_Sections.size(); i++)
writer.Record(LLVMBC::ModuleRecord::SECTIONNAME, m_Sections[i]);
if(!m_GlobalVars.empty())
writer.EmitGlobalVarAbbrev();
for(size_t i = 0; i < m_GlobalVars.size(); i++)
{
const GlobalVar &g = m_GlobalVars[i];
// global vars write the value type, not the pointer
uint64_t typeIndex = getTypeID(g.type->inner);
uint64_t linkageValue = 0;
switch(g.flags & GlobalFlags::LinkageMask)
{
case GlobalFlags::ExternalLinkage: linkageValue = 0; break;
case GlobalFlags::WeakAnyLinkage: linkageValue = 16; break;
case GlobalFlags::AppendingLinkage: linkageValue = 2; break;
case GlobalFlags::InternalLinkage: linkageValue = 3; break;
case GlobalFlags::LinkOnceAnyLinkage: linkageValue = 18; break;
case GlobalFlags::ExternalWeakLinkage: linkageValue = 7; break;
case GlobalFlags::CommonLinkage: linkageValue = 8; break;
case GlobalFlags::PrivateLinkage: linkageValue = 9; break;
case GlobalFlags::WeakODRLinkage: linkageValue = 17; break;
case GlobalFlags::LinkOnceODRLinkage: linkageValue = 19; break;
case GlobalFlags::AvailableExternallyLinkage: linkageValue = 12; break;
default: break;
}
uint64_t unnamedAddr = 0;
if(g.flags & GlobalFlags::GlobalUnnamedAddr)
unnamedAddr = 1;
else if(g.flags & GlobalFlags::LocalUnnamedAddr)
unnamedAddr = 2;
writer.Record(LLVMBC::ModuleRecord::GLOBALVAR,
{
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,
Log2Floor((uint32_t)g.align) + 1, uint64_t(g.section + 1),
// visibility
0U,
// TLS mode
0U,
// unnamed addr
unnamedAddr, (g.flags & GlobalFlags::ExternallyInitialised) ? 1U : 0U,
// DLL storage class
0U,
// comdat
0U,
});
}
for(size_t i = 0; i < m_Functions.size(); i++)
{
const Function &f = m_Functions[i];
uint64_t typeIndex = getTypeID(f.funcType->inner);
writer.Record(LLVMBC::ModuleRecord::FUNCTION,
{
typeIndex,
// calling convention
0U,
// external/declaration
f.external ? 1U : 0U,
// linkage
0U,
// attributes
uint64_t(f.attrs ? 1U + (f.attrs - m_Attributes.begin()) : 0U),
// alignment
f.align,
// section
0U,
// visibility
0U,
// gc
0U,
// unnamed_addr
0U,
// prologuedata
0U,
// dllstorageclass
0U,
// comdat
0U,
// prefixdata
0U,
// personality
0U,
});
}
for(size_t i = 0; i < m_Aliases.size(); i++)
{
const Alias &a = m_Aliases[i];
uint64_t typeIndex = getTypeID(a.type);
writer.Record(LLVMBC::ModuleRecord::ALIAS, {
typeIndex, a.valID,
// linkage
0U,
// visibility
0U,
});
}
// the symbols for constants start after the global variables and functions which we just
// outputted
if(!m_Constants.empty())
{
writer.BeginBlock(LLVMBC::KnownBlock::CONSTANTS_BLOCK);
EncodeConstants(writer, values, m_Constants);
writer.EndBlock();
}
if(!m_Metadata.empty())
{
writer.BeginBlock(LLVMBC::KnownBlock::METADATA_BLOCK);
writer.EmitMetaDataAbbrev();
EncodeMetadata(writer, values, m_Metadata);
rdcarray<uint64_t> vals;
for(size_t i = 0; i < m_NamedMeta.size(); i++)
{
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]));
writer.Record(LLVMBC::MetaDataRecord::NAMED_NODE, vals);
}
writer.EndBlock();
}
if(!m_Kinds.empty())
{
writer.BeginBlock(LLVMBC::KnownBlock::METADATA_BLOCK);
rdcarray<uint64_t> vals;
for(size_t i = 0; i < m_Kinds.size(); i++)
{
if(m_Kinds[i].empty())
continue;
vals.clear();
vals.push_back(i);
for(char c : m_Kinds[i])
vals.push_back(c);
writer.Record(LLVMBC::MetaDataRecord::KIND, vals);
}
writer.EndBlock();
}
{
writer.BeginBlock(LLVMBC::KnownBlock::VALUE_SYMTAB_BLOCK);
rdcarray<rdcpair<size_t, const rdcstr *>> entries;
for(size_t s = 0; s < m_Values.size(); s++)
{
const rdcstr *str = NULL;
switch(m_Values[s].type)
{
case ValueType::GlobalVar: str = &m_Values[s].global->name; break;
case ValueType::Function: str = &m_Values[s].function->name; break;
case ValueType::Alias: str = &m_Values[s].alias->name; break;
default: break;
}
if(str)
entries.push_back({s, str});
}
// sort the entries by string in order
std::sort(entries.begin(), entries.end(),
[](const rdcpair<size_t, const rdcstr *> &a,
const rdcpair<size_t, const rdcstr *> &b) { return *a.second < *b.second; });
// we use a special function to record the entry so it can take the string as-is to check it for
// validity
for(const rdcpair<size_t, const rdcstr *> &it : entries)
writer.RecordSymTabEntry(it.first, *it.second);
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())); \
} \
}
// 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)); \
} \
}
for(const Function &f : m_Functions)
{
if(f.external)
continue;
values.append(f.values);
writer.BeginBlock(LLVMBC::KnownBlock::FUNCTION_BLOCK);
writer.Record(LLVMBC::FunctionRecord::DECLAREBLOCKS, f.blocks.size());
if(!f.constants.empty())
{
writer.BeginBlock(LLVMBC::KnownBlock::CONSTANTS_BLOCK);
EncodeConstants(writer, values, f.constants);
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<uint64_t> vals;
bool needMetaAttach = !f.attachedMeta.empty();
for(const Instruction &inst : f.instructions)
{
forwardRefs = false;
vals.clear();
needMetaAttach |= !inst.attachedMeta.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);
// always emit func type
uint64_t flags = 1 << 15;
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->inner));
encodeRelativeValueID(Value(inst.funcCall));
for(size_t a = 0; a < inst.args.size(); a++)
{
if(inst.args[a].type == ValueType::Metadata)
{
vals.push_back(getFunctionMetaID(inst.args[a].meta));
}
else
{
encodeRelativeValueIDTypeless(inst.args[a]);
}
}
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_CALL, vals, forwardRefs);
break;
}
case Operation::Trunc:
case Operation::ZExt:
case Operation::SExt:
case Operation::FToU:
case Operation::FToS:
case Operation::UToF:
case Operation::SToF:
case Operation::FPTrunc:
case Operation::FPExt:
case Operation::PtrToI:
case Operation::IToPtr:
case Operation::Bitcast:
case Operation::AddrSpaceCast:
{
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);
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_EXTRACTVAL, vals, forwardRefs);
break;
}
case Operation::Ret:
{
if(!inst.args.empty())
{
encodeRelativeValueID(inst.args[0]);
}
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_RET, vals, forwardRefs);
break;
}
case Operation::FAdd:
case Operation::FSub:
case Operation::FMul:
case Operation::FDiv:
case Operation::FRem:
case Operation::Add:
case Operation::Sub:
case Operation::Mul:
case Operation::UDiv:
case Operation::SDiv:
case Operation::URem:
case Operation::SRem:
case Operation::ShiftLeft:
case Operation::LogicalShiftRight:
case Operation::ArithShiftRight:
case Operation::And:
case Operation::Or:
case Operation::Xor:
{
encodeRelativeValueID(inst.args[0]);
encodeRelativeValueIDTypeless(inst.args[1]);
const Type *t = inst.args[0].GetType();
const bool isFloatOp = (t->scalarType == Type::Float);
uint64_t opcode = 0;
switch(inst.op)
{
case Operation::FAdd:
case Operation::Add: opcode = 0; break;
case Operation::FSub:
case Operation::Sub: opcode = 1; break;
case Operation::FMul:
case Operation::Mul: opcode = 2; break;
case Operation::UDiv: opcode = 3; break;
case Operation::FDiv:
case Operation::SDiv: opcode = 4; break;
case Operation::URem: opcode = 5; break;
case Operation::FRem:
case Operation::SRem: opcode = 6; break;
case Operation::ShiftLeft: opcode = 7; break;
case Operation::LogicalShiftRight: opcode = 8; break;
case Operation::ArithShiftRight: opcode = 9; break;
case Operation::And: opcode = 10; break;
case Operation::Or: opcode = 11; break;
case Operation::Xor: opcode = 12; break;
default: break;
}
vals.push_back(opcode);
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.opFlags & InstructionFlags::NoSignedWrap)
flags |= 0x2;
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)
{
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));
}
}
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_BINOP, vals, forwardRefs);
break;
}
case Operation::Unreachable:
{
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_UNREACHABLE, {}, false);
break;
}
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
alignAndFlags |= 1U << 6;
if(inst.opFlags & InstructionFlags::ArgumentAlloca)
alignAndFlags |= 1U << 5;
vals.push_back(alignAndFlags);
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_ALLOCA, vals, forwardRefs);
break;
}
case Operation::GetElementPtr:
{
vals.push_back((inst.opFlags & InstructionFlags::InBounds) ? 1U : 0U);
vals.push_back(getTypeID(inst.args[0].GetType()->inner));
for(size_t i = 0; i < inst.args.size(); i++)
{
encodeRelativeValueID(inst.args[i]);
}
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_GEP, vals, forwardRefs);
break;
}
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);
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);
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_STORE, vals, forwardRefs);
break;
}
case Operation::FOrdFalse:
case Operation::FOrdEqual:
case Operation::FOrdGreater:
case Operation::FOrdGreaterEqual:
case Operation::FOrdLess:
case Operation::FOrdLessEqual:
case Operation::FOrdNotEqual:
case Operation::FOrd:
case Operation::FUnord:
case Operation::FUnordEqual:
case Operation::FUnordGreater:
case Operation::FUnordGreaterEqual:
case Operation::FUnordLess:
case Operation::FUnordLessEqual:
case Operation::FUnordNotEqual:
case Operation::FOrdTrue:
case Operation::IEqual:
case Operation::INotEqual:
case Operation::UGreater:
case Operation::UGreaterEqual:
case Operation::ULess:
case Operation::ULessEqual:
case Operation::SGreater:
case Operation::SGreaterEqual:
case Operation::SLess:
case Operation::SLessEqual:
{
encodeRelativeValueID(inst.args[0]);
encodeRelativeValueIDTypeless(inst.args[1]);
uint64_t opcode = 0;
switch(inst.op)
{
case Operation::FOrdFalse: opcode = 0; break;
case Operation::FOrdEqual: opcode = 1; break;
case Operation::FOrdGreater: opcode = 2; break;
case Operation::FOrdGreaterEqual: opcode = 3; break;
case Operation::FOrdLess: opcode = 4; break;
case Operation::FOrdLessEqual: opcode = 5; break;
case Operation::FOrdNotEqual: opcode = 6; break;
case Operation::FOrd: opcode = 7; break;
case Operation::FUnord: opcode = 8; break;
case Operation::FUnordEqual: opcode = 9; break;
case Operation::FUnordGreater: opcode = 10; break;
case Operation::FUnordGreaterEqual: opcode = 11; break;
case Operation::FUnordLess: opcode = 12; break;
case Operation::FUnordLessEqual: opcode = 13; break;
case Operation::FUnordNotEqual: opcode = 14; break;
case Operation::FOrdTrue: opcode = 15; break;
case Operation::IEqual: opcode = 32; break;
case Operation::INotEqual: opcode = 33; break;
case Operation::UGreater: opcode = 34; break;
case Operation::UGreaterEqual: opcode = 35; break;
case Operation::ULess: opcode = 36; break;
case Operation::ULessEqual: opcode = 37; break;
case Operation::SGreater: opcode = 38; break;
case Operation::SGreaterEqual: opcode = 39; break;
case Operation::SLess: opcode = 40; break;
case Operation::SLessEqual: opcode = 41; break;
default: break;
}
vals.push_back(opcode);
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]);
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_VSELECT, vals, forwardRefs);
break;
}
case Operation::ExtractElement:
{
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]);
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_INSERTELT, vals, forwardRefs);
break;
}
case Operation::ShuffleVector:
{
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);
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_INSERTVAL, vals, forwardRefs);
break;
}
case Operation::Branch:
{
vals.push_back(uint64_t(inst.args[0].block - f.blocks.begin()));
if(inst.args.size() > 1)
{
vals.push_back(uint64_t(inst.args[1].block - f.blocks.begin()));
encodeRelativeValueIDTypeless(inst.args[2]);
}
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_BR, vals, forwardRefs);
break;
}
case Operation::Phi:
{
vals.push_back(getTypeID(inst.type));
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);
vals.push_back(LLVMBC::BitWriter::svbr(valRef));
vals.push_back(uint64_t(inst.args[i + 1].block - f.blocks.begin()));
}
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_PHI, vals, forwardRefs);
break;
}
case Operation::Switch:
{
vals.push_back(getTypeID(inst.args[0].GetType()));
encodeRelativeValueIDTypeless(inst.args[0]);
vals.push_back(uint64_t(inst.args[1].block - f.blocks.begin()));
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()));
}
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_SWITCH, vals, forwardRefs);
break;
}
case Operation::Fence:
{
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);
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);
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);
writer.RecordInstruction(LLVMBC::FunctionRecord::INST_STOREATOMIC, vals, forwardRefs);
break;
}
case Operation::AtomicExchange:
case Operation::AtomicAdd:
case Operation::AtomicSub:
case Operation::AtomicAnd:
case Operation::AtomicNand:
case Operation::AtomicOr:
case Operation::AtomicXor:
case Operation::AtomicMax:
case Operation::AtomicMin:
case Operation::AtomicUMax:
case Operation::AtomicUMin:
{
encodeRelativeValueID(inst.args[0]);
encodeRelativeValueIDTypeless(inst.args[1]);
uint64_t opcode = 0;
switch(inst.op)
{
case Operation::AtomicExchange: opcode = 0; break;
case Operation::AtomicAdd: opcode = 1; break;
case Operation::AtomicSub: opcode = 2; break;
case Operation::AtomicAnd: opcode = 3; break;
case Operation::AtomicNand: opcode = 4; break;
case Operation::AtomicOr: opcode = 5; break;
case Operation::AtomicXor: opcode = 6; break;
case Operation::AtomicMax: opcode = 7; break;
case Operation::AtomicMin: opcode = 8; break;
case Operation::AtomicUMax: opcode = 9; break;
case Operation::AtomicUMin: opcode = 10; break;
default: break;
}
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);
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)
instValueID++;
// no debug location? omit
if(inst.debugLoc == ~0U)
continue;
// same as last time? emit 'again' record
if(inst.debugLoc == debugLoc)
writer.Record(LLVMBC::FunctionRecord::DEBUG_LOC_AGAIN);
// new debug location
const DebugLocation &loc = m_DebugLocations[inst.debugLoc];
writer.Record(LLVMBC::FunctionRecord::DEBUG_LOC,
{
loc.line, loc.col, getFunctionMetaIDOrNull(loc.scope),
getFunctionMetaIDOrNull(loc.inlinedAt),
});
debugLoc = inst.debugLoc;
}
if(needMetaAttach)
{
writer.BeginBlock(LLVMBC::KnownBlock::METADATA_ATTACHMENT);
vals.clear();
for(const rdcpair<uint64_t, Metadata *> &m : f.attachedMeta)
{
vals.push_back(m.first);
vals.push_back(getFunctionMetaID(m.second));
}
if(!vals.empty())
writer.Record(LLVMBC::MetaDataRecord::ATTACHMENT, vals);
for(size_t i = 0; i < f.instructions.size(); i++)
{
if(f.instructions[i].attachedMeta.empty())
continue;
vals.clear();
vals.push_back(uint64_t(i));
for(const rdcpair<uint64_t, Metadata *> &m : f.instructions[i].attachedMeta)
{
vals.push_back(m.first);
vals.push_back(getFunctionMetaID(m.second));
}
writer.Record(LLVMBC::MetaDataRecord::ATTACHMENT, vals);
}
writer.EndBlock();
}
if(!f.uselist.empty())
{
writer.BeginBlock(LLVMBC::KnownBlock::USELIST_BLOCK);
for(const UselistEntry &u : f.uselist)
{
vals = u.shuffle;
vals.push_back(getValueID(u.value));
writer.Record(u.block ? LLVMBC::UselistRecord::BB : LLVMBC::UselistRecord::DEFAULT, vals);
}
writer.EndBlock();
}
writer.EndBlock();
values.resize(values.size() - f.values.size());
}
writer.EndBlock();
ProgramHeader header;
header.ProgramVersion = ((m_Major & 0xf) << 4) | (m_Minor & 0xf);
header.ProgramType = (uint16_t)m_Type;
header.DxilMagic = MAKE_FOURCC('D', 'X', 'I', 'L');
header.DxilVersion = m_DXILVersion;
header.BitcodeOffset = sizeof(ProgramHeader) - offsetof(ProgramHeader, DxilMagic);
header.BitcodeSize = (uint32_t)ret.size();
header.SizeInUint32 = (uint32_t)AlignUp4(ret.size() + sizeof(ProgramHeader)) / sizeof(uint32_t);
ret.insert(0, (const byte *)&header, sizeof(header));
ret.resize(AlignUp4(ret.size()));
return ret;
}
void DXIL::ProgramEditor::EncodeConstants(LLVMBC::BitcodeWriter &writer,
const rdcarray<Value> &values,
const rdcarray<Constant> &constants) const
{
const Type *curType = NULL;
for(const Constant &c : constants)
{
if(c.type != curType)
{
writer.Record(LLVMBC::ConstantsRecord::SETTYPE, getTypeID(c.type));
curType = c.type;
}
if(c.nullconst)
{
writer.Record(LLVMBC::ConstantsRecord::CONST_NULL);
}
else if(c.undef)
{
writer.Record(LLVMBC::ConstantsRecord::UNDEF);
}
else if(c.op == Operation::GetElementPtr)
{
rdcarray<uint64_t> vals;
vals.reserve(c.members.size() * 2 + 1);
// DXC's version of llvm always writes the explicit type here
vals.push_back(getTypeID(c.members[0].GetType()->inner));
for(size_t m = 0; m < c.members.size(); m++)
{
vals.push_back(getTypeID(c.members[m].GetType()));
vals.push_back(getValueID(c.members[m]));
}
writer.Record(LLVMBC::ConstantsRecord::EVAL_GEP, vals);
}
else if(c.op != Operation::NoOp)
{
uint64_t cast = EncodeCast(c.op);
RDCASSERT(cast != ~0U);
writer.Record(LLVMBC::ConstantsRecord::EVAL_CAST,
{EncodeCast(c.op), getTypeID(c.type), getTypeID(c.inner->type),
getValueID(Value(c.inner))});
}
else if(c.data)
{
rdcarray<uint64_t> vals;
vals.reserve(c.members.size());
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]);
}
else
{
for(size_t m = 0; m < c.members.size(); m++)
vals.push_back(c.members[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)
{
rdcarray<uint64_t> vals;
vals.reserve(c.members.size());
for(size_t m = 0; m < c.members.size(); m++)
vals.push_back(getValueID(c.members[m]));
writer.Record(LLVMBC::ConstantsRecord::AGGREGATE, vals);
}
else if(c.type->scalarType == Type::Int)
{
writer.Record(LLVMBC::ConstantsRecord::INTEGER, LLVMBC::BitWriter::svbr(c.val.s64v[0]));
}
else if(c.type->scalarType == Type::Float)
{
writer.Record(LLVMBC::ConstantsRecord::FLOAT, c.val.u64v[0]);
}
else if(!c.str.empty())
{
if(c.str.indexOf('\0') < 0)
{
writer.Record(LLVMBC::ConstantsRecord::CSTRING, c.str);
}
else
{
writer.Record(LLVMBC::ConstantsRecord::STRING, c.str);
}
}
}
}
void DXIL::ProgramEditor::EncodeMetadata(LLVMBC::BitcodeWriter &writer, const rdcarray<Value> &values,
const rdcarray<Metadata> &meta) const
{
rdcarray<uint64_t> vals;
bool errored = false;
for(size_t i = 0; i < meta.size(); i++)
{
if(meta[i].isString)
{
writer.Record(LLVMBC::MetaDataRecord::STRING_OLD, meta[i].str);
}
else if(meta[i].isConstant)
{
writer.Record(LLVMBC::MetaDataRecord::VALUE,
{getTypeID(meta[i].type), getValueID(meta[i].value)});
}
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});
}
}
}
else
{
vals.clear();
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);
}
}
}