Handle attribute sets with per-function-parameter attributes in DXIL

* This also fixes the string (not enum) attribute decoding
This commit is contained in:
baldurk
2021-09-21 18:04:57 +01:00
parent a67b0beb7f
commit fccaee058e
4 changed files with 152 additions and 57 deletions
+36 -19
View File
@@ -463,8 +463,8 @@ Program::Program(const byte *bytes, size_t length)
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_Attributes.size())
f.attrs = &m_Attributes[(size_t)rootchild.ops[4] - 1];
if(rootchild.ops[4] > 0 && rootchild.ops[4] - 1 < m_AttributeSets.size())
f.attrs = &m_AttributeSets[(size_t)rootchild.ops[4] - 1];
f.align = rootchild.ops[5];
@@ -531,11 +531,10 @@ Program::Program(const byte *bytes, size_t length)
continue;
}
Attributes group;
AttributeGroup group;
size_t id = (size_t)attrgroup.ops[0];
group.index = attrgroup.ops[1];
group.valid = true;
group.slotIndex = (uint32_t)attrgroup.ops[1];
for(size_t i = 2; i < attrgroup.ops.size(); i++)
{
@@ -565,8 +564,22 @@ Program::Program(const byte *bytes, size_t length)
}
default:
{
rdcstr a = attrgroup.getString(i + 1);
rdcstr b = attrgroup.getString(i + 1 + a.size() + 1);
rdcstr a, b;
a = attrgroup.getString(i + 1);
a.resize(strlen(a.c_str()));
if(attrgroup.ops[i] == 4)
{
b = attrgroup.getString(i + 1 + a.size() + 1);
b.resize(strlen(b.c_str()));
i += a.size() + b.size() + 2;
}
else
{
i += a.size() + 1;
}
group.strs.push_back({a, b});
break;
}
@@ -593,21 +606,25 @@ Program::Program(const byte *bytes, size_t length)
continue;
}
Attributes attrs;
attrs.index = m_Attributes.size();
attrs.groups = paramattr.ops;
AttributeSet attrs;
attrs.orderedGroups = paramattr.ops;
for(uint64_t g : paramattr.ops)
{
if(g < m_AttributeGroups.size())
{
Attributes &other = m_AttributeGroups[(size_t)g];
attrs.params |= other.params;
attrs.align = RDCMAX(attrs.align, other.align);
attrs.stackAlign = RDCMAX(attrs.stackAlign, other.stackAlign);
attrs.derefBytes = RDCMAX(attrs.derefBytes, other.derefBytes);
attrs.derefOrNullBytes = RDCMAX(attrs.derefOrNullBytes, other.derefOrNullBytes);
attrs.strs.append(other.strs);
const AttributeGroup &group = m_AttributeGroups[(size_t)g];
if(group.slotIndex == AttributeGroup::FunctionSlot)
{
RDCASSERT(attrs.functionSlot == NULL);
attrs.functionSlot = &group;
}
else
{
attrs.groupSlots.resize_for_index(group.slotIndex);
attrs.groupSlots[group.slotIndex] = &m_AttributeGroups[(size_t)g];
}
}
else
{
@@ -615,7 +632,7 @@ Program::Program(const byte *bytes, size_t length)
}
}
m_Attributes.push_back(attrs);
m_AttributeSets.push_back(attrs);
}
}
else if(IS_KNOWN(rootchild.id, KnownBlock::TYPE_BLOCK))
@@ -1315,7 +1332,7 @@ Program::Program(const byte *bytes, size_t length)
inst.op = Operation::Call;
size_t attr = op.get<size_t>();
if(attr > 0)
inst.paramAttrs = &m_Attributes[attr - 1];
inst.paramAttrs = &m_AttributeSets[attr - 1];
uint64_t callingFlags = op.get<uint64_t>();
+27 -10
View File
@@ -49,6 +49,9 @@ struct ProgramHeader
uint32_t BitcodeSize; // Size of LLVM bitcode.
};
struct Instruction;
struct AttributeSet;
struct Type
{
enum TypeKind
@@ -85,7 +88,8 @@ struct Type
bool isVoid() const { return type == Scalar && scalarType == Void; }
rdcstr toString() const;
rdcstr declFunction(rdcstr funcName) const;
rdcstr declFunction(rdcstr funcName, const rdcarray<Instruction> &args,
const AttributeSet *attrs) const;
// for scalars, arrays, vectors, pointers
union
@@ -261,18 +265,31 @@ enum class Attribute : uint64_t
BITMASK_OPERATORS(Attribute);
struct Attributes
struct AttributeGroup
{
bool valid = false;
uint64_t index = 0;
enum Slot : uint64_t
{
InvalidSlot = ~0U - 1,
FunctionSlot = ~0U,
ReturnSlot = 0U,
Param1Slot = 1U,
};
rdcarray<uint64_t> groups;
uint32_t slotIndex = InvalidSlot;
Attribute params = Attribute::None;
uint64_t align = 0, stackAlign = 0, derefBytes = 0, derefOrNullBytes = 0;
rdcarray<rdcpair<rdcstr, rdcstr>> strs;
rdcstr toString() const;
rdcstr toString(bool stringAttrs) const;
};
struct AttributeSet
{
const AttributeGroup *functionSlot = NULL;
rdcarray<const AttributeGroup *> groupSlots;
rdcarray<uint64_t> orderedGroups;
};
enum class Operation : uint8_t
@@ -568,7 +585,7 @@ struct Instruction
AttachedMetadata attachedMeta;
// function calls
const Attributes *paramAttrs = NULL;
const AttributeSet *paramAttrs = NULL;
const Function *funcCall = NULL;
};
@@ -592,7 +609,7 @@ struct Function
const Type *funcType = NULL;
bool external = false;
const Attributes *attrs = NULL;
const AttributeSet *attrs = NULL;
uint64_t align = 0;
@@ -686,8 +703,8 @@ protected:
const Type *m_VoidType = NULL;
const Type *m_BoolType = NULL;
rdcarray<Attributes> m_AttributeGroups;
rdcarray<Attributes> m_Attributes;
rdcarray<AttributeGroup> m_AttributeGroups;
rdcarray<AttributeSet> m_AttributeSets;
rdcarray<Constant> m_Constants;
@@ -41,7 +41,7 @@ DXIL::ProgramEditor::~ProgramEditor()
DXBC::DXBCContainer::ReplaceDXILBytecode(m_OutBlob, EncodeProgram());
}
#define getAttribID(a) uint64_t(a - m_Attributes.begin())
#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 getMetaIDOrNull(m) (m ? (getMetaID(m) + 1) : 0ULL)
@@ -122,13 +122,13 @@ bytebuf DXIL::ProgramEditor::EncodeProgram() const
for(size_t i = 0; i < m_AttributeGroups.size(); i++)
{
if(m_AttributeGroups[i].valid)
if(m_AttributeGroups[i].slotIndex != AttributeGroup::InvalidSlot)
{
const Attributes &group = m_AttributeGroups[i];
const AttributeGroup &group = m_AttributeGroups[i];
vals.clear();
vals.push_back(i);
vals.push_back(group.index);
vals.push_back(group.slotIndex);
// decompose params bitfield into bits
if(group.params != Attribute::None)
@@ -204,12 +204,12 @@ bytebuf DXIL::ProgramEditor::EncodeProgram() const
writer.EndBlock();
}
if(!m_Attributes.empty())
if(!m_AttributeSets.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);
for(size_t i = 0; i < m_AttributeSets.size(); i++)
writer.Record(LLVMBC::ParamAttrRecord::ENTRY, m_AttributeSets[i].orderedGroups);
writer.EndBlock();
}
@@ -387,7 +387,7 @@ bytebuf DXIL::ProgramEditor::EncodeProgram() const
// linkage
0U,
// attributes
uint64_t(f.attrs ? 1U + (f.attrs - m_Attributes.begin()) : 0U),
uint64_t(f.attrs ? 1U + getAttribID(f.attrs) : 0U),
// alignment
f.align,
// section
@@ -522,11 +522,23 @@ void Program::MakeDisassemblyString()
namedMeta += "}\n";
}
rdcarray<const AttributeGroup *> funcAttrGroups;
for(size_t i = 0; i < m_AttributeGroups.size(); i++)
{
if(m_AttributeGroups[i].slotIndex != AttributeGroup::FunctionSlot)
continue;
if(funcAttrGroups.contains(&m_AttributeGroups[i]))
continue;
funcAttrGroups.push_back(&m_AttributeGroups[i]);
}
for(size_t i = 0; i < m_Functions.size(); i++)
{
Function &func = m_Functions[i];
auto argToString = [this, &func](Value v, bool withTypes) {
auto argToString = [this, &func](Value v, bool withTypes, const rdcstr &attrString = "") {
rdcstr ret;
switch(v.type)
{
@@ -535,11 +547,13 @@ void Program::MakeDisassemblyString()
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())
{
const Metadata &m = *v.meta;
@@ -567,17 +581,25 @@ void Program::MakeDisassemblyString()
ret += v.meta->refString();
}
break;
case ValueType::Function: ret = "@" + escapeStringIfNeeded(v.function->name); 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 = v.constant->toString(withTypes); 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
@@ -588,6 +610,7 @@ void Program::MakeDisassemblyString()
{
if(withTypes)
ret = "label ";
ret += attrString;
if(v.block->name.empty())
ret += StringFormat::Fmt("%%%u", v.block->resultID);
else
@@ -597,17 +620,19 @@ void Program::MakeDisassemblyString()
return ret;
};
if(func.attrs)
if(func.attrs && func.attrs->functionSlot)
{
m_Disassembly += StringFormat::Fmt("; Function Attrs: %s\n", func.attrs->toString().c_str());
m_Disassembly += StringFormat::Fmt("; Function Attrs: %s\n",
func.attrs->functionSlot->toString(false).c_str());
instructionLine++;
}
m_Disassembly += (func.external ? "declare " : "define ");
m_Disassembly += func.funcType->inner->declFunction("@" + escapeStringIfNeeded(func.name));
m_Disassembly += func.funcType->inner->declFunction("@" + escapeStringIfNeeded(func.name),
func.args, func.attrs);
if(func.attrs)
m_Disassembly += StringFormat::Fmt(" #%u", func.attrs->index);
if(func.attrs && func.attrs->functionSlot)
m_Disassembly += StringFormat::Fmt(" #%u", funcAttrGroups.indexOf(func.attrs->functionSlot));
if(!func.external)
{
@@ -646,18 +671,33 @@ void Program::MakeDisassemblyString()
m_Disassembly += " @" + escapeStringIfNeeded(inst.funcCall->name);
m_Disassembly += "(";
bool first = true;
// attribute args start from 1
size_t argIdx = 1;
for(Value &s : inst.args)
{
if(!first)
m_Disassembly += ", ";
first = false;
m_Disassembly += argToString(s, true);
// see if we have param attrs for this param
rdcstr attrString;
if(inst.paramAttrs && argIdx < inst.paramAttrs->groupSlots.size() &&
inst.paramAttrs->groupSlots[argIdx])
{
attrString = inst.paramAttrs->groupSlots[argIdx]->toString(true) + " ";
}
m_Disassembly += argToString(s, true, attrString);
argIdx++;
}
m_Disassembly += ")";
debugCall = inst.funcCall->name.beginsWith("llvm.dbg.");
if(inst.paramAttrs)
m_Disassembly += StringFormat::Fmt(" #%u", inst.paramAttrs - m_Attributes.begin());
if(inst.paramAttrs && inst.paramAttrs->functionSlot)
m_Disassembly +=
StringFormat::Fmt(" #%u", funcAttrGroups.indexOf(inst.paramAttrs->functionSlot));
break;
}
case Operation::Trunc:
@@ -1415,11 +1455,13 @@ void Program::MakeDisassemblyString()
}
}
for(size_t i = 0; i < m_Attributes.size(); i++)
m_Disassembly +=
StringFormat::Fmt("attributes #%zu = { %s }\n", i, m_Attributes[i].toString().c_str());
for(size_t i = 0; i < funcAttrGroups.size(); i++)
{
m_Disassembly += StringFormat::Fmt("attributes #%zu = { %s }\n", i,
funcAttrGroups[i]->toString(true).c_str());
}
if(!m_Attributes.empty())
if(!funcAttrGroups.empty())
m_Disassembly += "\n";
m_Disassembly += namedMeta + "\n";
@@ -1491,7 +1533,7 @@ rdcstr Type::toString() const
else
return StringFormat::Fmt("%s addrspace(%d)*", inner->toString().c_str(), addrSpace);
case Array: return StringFormat::Fmt("[%u x %s]", elemCount, inner->toString().c_str());
case Function: return declFunction(rdcstr());
case Function: return declFunction(rdcstr(), {}, NULL);
case Struct:
{
rdcstr ret;
@@ -1518,7 +1560,8 @@ rdcstr Type::toString() const
}
}
rdcstr Type::declFunction(rdcstr funcName) const
rdcstr Type::declFunction(rdcstr funcName, const rdcarray<Instruction> &args,
const AttributeSet *attrs) const
{
rdcstr ret = inner->toString();
ret += " " + funcName + "(";
@@ -1527,12 +1570,20 @@ rdcstr Type::declFunction(rdcstr funcName) const
if(i > 0)
ret += ", ";
ret += members[i]->toString();
if(attrs && i + 1 < attrs->groupSlots.size() && attrs->groupSlots[i + 1])
{
ret += " " + attrs->groupSlots[i + 1]->toString(true);
}
if(i < args.size() && !args[i].name.empty())
ret += " %" + escapeStringIfNeeded(args[i].name);
}
ret += ")";
return ret;
}
rdcstr Attributes::toString() const
rdcstr AttributeGroup::toString(bool stringAttrs) const
{
rdcstr ret = "";
Attribute p = params;
@@ -1569,8 +1620,18 @@ rdcstr Attributes::toString() const
}
}
for(const rdcpair<rdcstr, rdcstr> &str : strs)
ret += " " + escapeString(str.first) + "=" + escapeString(str.second);
if(stringAttrs)
{
ret.trim();
for(const rdcpair<rdcstr, rdcstr> &str : strs)
{
if(str.second.empty())
ret += " " + escapeString(str.first);
else
ret += " " + escapeString(str.first) + "=" + escapeString(str.second);
}
}
return ret.trimmed();
}