Split some parts of SPIRVEditor into SPIRVProcessor

* This will then be shared by a read-only Reflector, with common processing
  to both happening in SPIRVProcessor with extra work happening in each child
  depending on whether it's reflecting/disassembling or editing.
This commit is contained in:
baldurk
2019-08-16 17:38:35 +01:00
parent 936876234c
commit 47e10a5c2d
10 changed files with 973 additions and 767 deletions
@@ -100,6 +100,8 @@ set(sources
spirv_compile.h
spirv_reflect.cpp
spirv_reflect.h
spirv_processor.cpp
spirv_processor.h
spirv_disassemble.cpp
spirv_stringise.cpp
${glslang_sources})
@@ -162,6 +162,7 @@
<ForcedIncludeFiles>precompiled.h</ForcedIncludeFiles>
</ClCompile>
<ClCompile Include="spirv_editor.cpp" />
<ClCompile Include="spirv_processor.cpp" />
<ClCompile Include="spirv_reflect.cpp" />
<ClCompile Include="spirv_gen.cpp" />
<ClCompile Include="spirv_stringise.cpp" />
@@ -219,6 +220,7 @@
<ClInclude Include="spirv_editor.h" />
<ClInclude Include="spirv_gen.h" />
<ClInclude Include="spirv_op_helpers.h" />
<ClInclude Include="spirv_processor.h" />
<ClInclude Include="spirv_reflect.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
@@ -142,6 +142,7 @@
</ClCompile>
<ClCompile Include="spirv_reflect.cpp" />
<ClCompile Include="glslang_compile.cpp" />
<ClCompile Include="spirv_processor.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\3rdparty\glslang\OGLCompilersDLL\InitializeDll.h">
@@ -293,5 +294,6 @@
<ClInclude Include="spirv_op_helpers.h">
<Filter>JSON-Generated helpers</Filter>
</ClInclude>
<ClInclude Include="spirv_processor.h" />
</ItemGroup>
</Project>
+197 -454
View File
@@ -54,153 +54,16 @@ Scalar::Scalar(Iter it)
}
}
Operation Vector::decl(Editor &editor) const
Editor::Editor(std::vector<uint32_t> &spirvWords) : m_ExternalSPIRV(spirvWords)
{
return OpTypeVector(Id(), editor.DeclareType(scalar), count);
}
Operation Matrix::decl(Editor &editor) const
void Editor::Prepare()
{
return OpTypeMatrix(Id(), editor.DeclareType(vector), count);
}
Processor::Parse(m_ExternalSPIRV);
Operation Pointer::decl(Editor &editor) const
{
return OpTypePointer(Id(), storage, baseId);
}
Operation Image::decl(Editor &editor) const
{
return OpTypeImage(Id(), editor.DeclareType(retType), dim, depth, arrayed, ms, sampled, format);
}
Operation Sampler::decl(Editor &editor) const
{
return OpTypeSampler(Id());
}
Operation SampledImage::decl(Editor &editor) const
{
return OpTypeSampledImage(Id(), baseId);
}
Operation Function::decl(Editor &editor) const
{
return OpTypeFunction(Id(), returnId, argumentIds);
}
Editor::Editor(std::vector<uint32_t> &spirvWords) : spirv(spirvWords)
{
if(spirv.size() < FirstRealWord || spirv[0] != MagicNumber)
{
RDCERR("Empty or invalid SPIR-V module");
if(m_SPIRV.empty())
return;
}
idOffsets.resize(spirv[3]);
idTypes.resize(spirv[3]);
// [4] is reserved
RDCASSERT(spirv[4] == 0);
// simple state machine to track which section we're in.
// Note that a couple of sections are optional and could be skipped over, at which point we insert
// a dummy OpNop so they're not empty (which will be stripped later) and record them as in
// between.
//
// We only handle single-shader modules at the moment, so some things are required by virtue of
// being required in a shader - e.g. at least the Shader capability, at least one entry point, etc
//
// Capabilities: REQUIRED (we assume - must declare Shader capability)
// Extensions: OPTIONAL
// ExtInst: OPTIONAL
// MemoryModel: REQUIRED (required by spec)
// EntryPoints: REQUIRED (we assume)
// ExecutionMode: OPTIONAL
// Debug: OPTIONAL
// Annotations: OPTIONAL (in theory - would require empty shader)
// TypesVariables: REQUIRED (must at least have the entry point function type)
// Functions: REQUIRED (must have the entry point)
// set the book-ends: start of the first section and end of the last
sections[Section::Count - 1].endOffset = spirvWords.size();
#define START_SECTION(section) \
if(sections[section].startOffset == 0) \
sections[section].startOffset = it.offs();
for(Iter it(spirv, FirstRealWord); it; it++)
{
Op opcode = it.opcode();
if(opcode == Op::Capability)
{
START_SECTION(Section::Capabilities);
}
else if(opcode == Op::Extension)
{
START_SECTION(Section::Extensions);
}
else if(opcode == Op::ExtInstImport)
{
START_SECTION(Section::ExtInst);
}
else if(opcode == Op::MemoryModel)
{
START_SECTION(Section::MemoryModel);
}
else if(opcode == Op::EntryPoint)
{
START_SECTION(Section::EntryPoints);
}
else if(opcode == Op::ExecutionMode || opcode == Op::ExecutionModeId)
{
START_SECTION(Section::ExecutionMode);
}
else if(opcode == Op::String || opcode == Op::Source || opcode == Op::SourceContinued ||
opcode == Op::SourceExtension || opcode == Op::Name || opcode == Op::MemberName ||
opcode == Op::ModuleProcessed)
{
START_SECTION(Section::Debug);
}
else if(opcode == Op::Decorate || opcode == Op::MemberDecorate || opcode == Op::GroupDecorate ||
opcode == Op::GroupMemberDecorate || opcode == Op::DecorationGroup ||
opcode == Op::DecorateStringGOOGLE || opcode == Op::MemberDecorateStringGOOGLE)
{
START_SECTION(Section::Annotations);
}
else if(opcode == Op::Function)
{
START_SECTION(Section::Functions);
}
else
{
// if we've reached another instruction, check if we've reached the function section yet. If
// we have then assume it's an instruction inside a function and ignore. If we haven't, assume
// it's a type/variable/constant type instruction
if(sections[Section::Functions].startOffset == 0)
{
START_SECTION(Section::TypesVariablesConstants);
}
}
RegisterOp(it);
}
#undef START_SECTION
// ensure we got everything right. First section should start at the beginning
RDCASSERTEQUAL(sections[Section::First].startOffset, FirstRealWord);
// we now set the endOffset of each section to the start of the next. Any empty sections
// temporarily have startOffset set to endOffset, we'll pad them with a nop below.
for(int s = Section::Count - 1; s > 0; s--)
{
RDCASSERTEQUAL(sections[s - 1].endOffset, 0);
sections[s - 1].endOffset = sections[s].startOffset;
if(sections[s - 1].startOffset == 0)
sections[s - 1].startOffset = sections[s - 1].endOffset;
}
// find any empty sections and insert a nop into the stream there. We need to fixup later section
// offsets by hand as addWords doesn't handle empty sections properly (it thinks we're inserting
@@ -208,20 +71,20 @@ Editor::Editor(std::vector<uint32_t> &spirvWords) : spirv(spirvWords)
// padding nops in the first place!
for(uint32_t s = 0; s < Section::Count; s++)
{
if(sections[s].startOffset == sections[s].endOffset)
if(m_Sections[s].startOffset == m_Sections[s].endOffset)
{
spirv.insert(spirv.begin() + sections[s].startOffset, OpNopWord);
sections[s].endOffset++;
m_SPIRV.insert(m_SPIRV.begin() + m_Sections[s].startOffset, OpNopWord);
m_Sections[s].endOffset++;
for(uint32_t t = s + 1; t < Section::Count; t++)
{
sections[t].startOffset++;
sections[t].endOffset++;
m_Sections[t].startOffset++;
m_Sections[t].endOffset++;
}
// look through every id, and update its offset
for(size_t &o : idOffsets)
if(o >= sections[s].startOffset)
if(o >= m_Sections[s].startOffset)
o++;
}
}
@@ -229,31 +92,31 @@ Editor::Editor(std::vector<uint32_t> &spirvWords) : spirv(spirvWords)
// each section should now precisely match each other end-to-end and not be empty
for(uint32_t s = Section::First; s < Section::Count; s++)
{
RDCASSERTNOTEQUAL(sections[s].startOffset, 0);
RDCASSERTNOTEQUAL(sections[s].endOffset, 0);
RDCASSERTNOTEQUAL(m_Sections[s].startOffset, 0);
RDCASSERTNOTEQUAL(m_Sections[s].endOffset, 0);
RDCASSERT(sections[s].endOffset - sections[s].startOffset > 0, sections[s].startOffset,
sections[s].endOffset);
RDCASSERT(m_Sections[s].endOffset - m_Sections[s].startOffset > 0, m_Sections[s].startOffset,
m_Sections[s].endOffset);
if(s != 0)
RDCASSERTEQUAL(sections[s - 1].endOffset, sections[s].startOffset);
RDCASSERTEQUAL(m_Sections[s - 1].endOffset, m_Sections[s].startOffset);
if(s + 1 < Section::Count)
RDCASSERTEQUAL(sections[s].endOffset, sections[s + 1].startOffset);
RDCASSERTEQUAL(m_Sections[s].endOffset, m_Sections[s + 1].startOffset);
}
}
void Editor::StripNops()
Editor::~Editor()
{
for(size_t i = FirstRealWord; i < spirv.size();)
for(size_t i = FirstRealWord; i < m_SPIRV.size();)
{
while(spirv[i] == OpNopWord)
while(m_SPIRV[i] == OpNopWord)
{
spirv.erase(spirv.begin() + i);
m_SPIRV.erase(m_SPIRV.begin() + i);
addWords(i, -1);
}
uint32_t len = spirv[i] >> WordCountShift;
uint32_t len = m_SPIRV[i] >> WordCountShift;
if(len == 0)
{
@@ -263,14 +126,15 @@ void Editor::StripNops()
i += len;
}
m_ExternalSPIRV.swap(m_SPIRV);
}
Id Editor::MakeId()
{
uint32_t ret = spirv[3];
spirv[3]++;
idOffsets.resize(spirv[3]);
idTypes.resize(spirv[3]);
uint32_t ret = m_SPIRV[3];
m_SPIRV[3]++;
Processor::PreParse(m_SPIRV[3]);
return Id::fromWord(ret);
}
@@ -293,16 +157,16 @@ void Editor::SetName(Id id, const char *name)
break;
}
op.insertInto(spirv, it.offs());
RegisterOp(Iter(spirv, it.offs()));
op.insertInto(m_SPIRV, it.offs());
RegisterOp(Iter(m_SPIRV, it.offs()));
addWords(it.offs(), op.size());
}
void Editor::AddDecoration(const Operation &op)
{
size_t offset = sections[Section::Annotations].endOffset;
op.insertInto(spirv, offset);
RegisterOp(Iter(spirv, offset));
size_t offset = m_Sections[Section::Annotations].endOffset;
op.insertInto(m_SPIRV, offset);
RegisterOp(Iter(m_SPIRV, offset));
addWords(offset, op.size());
}
@@ -314,8 +178,8 @@ void Editor::AddCapability(Capability cap)
// insert the operation at the very start
Operation op(Op::Capability, {(uint32_t)cap});
op.insertInto(spirv, FirstRealWord);
RegisterOp(Iter(spirv, FirstRealWord));
op.insertInto(m_SPIRV, FirstRealWord);
RegisterOp(Iter(m_SPIRV, FirstRealWord));
addWords(FirstRealWord, op.size());
}
@@ -326,7 +190,7 @@ void Editor::AddExtension(const rdcstr &extension)
return;
// start at the beginning
Iter it(spirv, FirstRealWord);
Iter it(m_SPIRV, FirstRealWord);
// skip past any capabilities
while(it.opcode() == Op::Capability)
@@ -338,17 +202,17 @@ void Editor::AddExtension(const rdcstr &extension)
memcpy(&uintName[0], extension.c_str(), sz);
Operation op(Op::Extension, uintName);
op.insertInto(spirv, it.offs());
op.insertInto(m_SPIRV, it.offs());
RegisterOp(it);
addWords(it.offs(), op.size());
}
void Editor::AddExecutionMode(const Operation &mode)
{
size_t offset = sections[Section::ExecutionMode].endOffset;
size_t offset = m_Sections[Section::ExecutionMode].endOffset;
mode.insertInto(spirv, offset);
RegisterOp(Iter(spirv, offset));
mode.insertInto(m_SPIRV, offset);
RegisterOp(Iter(m_SPIRV, offset));
addWords(offset, mode.size());
}
@@ -360,7 +224,7 @@ Id Editor::ImportExtInst(const char *setname)
return ret;
// start at the beginning
Iter it(spirv, FirstRealWord);
Iter it(m_SPIRV, FirstRealWord);
// skip past any capabilities and extensions
while(it.opcode() == Op::Capability || it.opcode() == Op::Extension)
@@ -376,7 +240,7 @@ Id Editor::ImportExtInst(const char *setname)
uintName.insert(uintName.begin(), ret.value());
Operation op(Op::ExtInstImport, uintName);
op.insertInto(spirv, it.offs());
op.insertInto(m_SPIRV, it.offs());
RegisterOp(it);
addWords(it.offs(), op.size());
@@ -387,48 +251,45 @@ Id Editor::ImportExtInst(const char *setname)
Id Editor::AddType(const Operation &op)
{
size_t offset = sections[Section::Types].endOffset;
size_t offset = m_Sections[Section::Types].endOffset;
Id id = Id::fromWord(op[1]);
idOffsets[id.value()] = offset;
op.insertInto(spirv, offset);
RegisterOp(Iter(spirv, offset));
op.insertInto(m_SPIRV, offset);
RegisterOp(Iter(m_SPIRV, offset));
addWords(offset, op.size());
return id;
}
Id Editor::AddVariable(const Operation &op)
{
size_t offset = sections[Section::Variables].endOffset;
size_t offset = m_Sections[Section::Variables].endOffset;
Id id = Id::fromWord(op[2]);
idOffsets[id.value()] = offset;
op.insertInto(spirv, offset);
RegisterOp(Iter(spirv, offset));
op.insertInto(m_SPIRV, offset);
RegisterOp(Iter(m_SPIRV, offset));
addWords(offset, op.size());
return id;
}
Id Editor::AddConstant(const Operation &op)
{
size_t offset = sections[Section::Constants].endOffset;
size_t offset = m_Sections[Section::Constants].endOffset;
Id id = Id::fromWord(op[2]);
idOffsets[id.value()] = offset;
op.insertInto(spirv, offset);
RegisterOp(Iter(spirv, offset));
op.insertInto(m_SPIRV, offset);
RegisterOp(Iter(m_SPIRV, offset));
addWords(offset, op.size());
return id;
}
void Editor::AddFunction(const Operation *ops, size_t count)
{
idOffsets[ops[0][2]] = spirv.size();
size_t offset = m_SPIRV.size();
for(size_t i = 0; i < count; i++)
ops[i].appendTo(spirv);
ops[i].appendTo(m_SPIRV);
RegisterOp(Iter(spirv, idOffsets[ops[0][2]]));
RegisterOp(Iter(m_SPIRV, offset));
}
Iter Editor::GetID(Id id)
@@ -436,15 +297,15 @@ Iter Editor::GetID(Id id)
size_t offs = idOffsets[id.value()];
if(offs)
return Iter(spirv, offs);
return Iter(m_SPIRV, offs);
return Iter();
}
Iter Editor::GetEntry(Id id)
{
Iter it(spirv, sections[Section::EntryPoints].startOffset);
Iter end(spirv, sections[Section::EntryPoints].endOffset);
Iter it(m_SPIRV, m_Sections[Section::EntryPoints].startOffset);
Iter end(m_SPIRV, m_Sections[Section::EntryPoints].endOffset);
while(it && it < end)
{
@@ -471,7 +332,7 @@ void Editor::AddOperation(Iter iter, const Operation &op)
return;
// add op
op.insertInto(spirv, iter.offs());
op.insertInto(m_SPIRV, iter.offs());
// update offsets
addWords(iter.offs(), op.size());
@@ -479,301 +340,119 @@ void Editor::AddOperation(Iter iter, const Operation &op)
void Editor::RegisterOp(Iter it)
{
Op opcode = it.opcode();
Processor::RegisterOp(it);
OpDecoder opdata(it);
if(opdata.result != Id() && opdata.resultType != Id())
{
RDCASSERT(opdata.result.value() < idTypes.size());
idTypes[opdata.result.value()] = opdata.resultType;
}
if(opdata.result != Id())
idOffsets[opdata.result.value()] = it.offs();
if(opcode == Op::EntryPoint)
if(opdata.op == Op::TypeVoid || opdata.op == Op::TypeBool || opdata.op == Op::TypeInt ||
opdata.op == Op::TypeFloat)
{
entries.push_back(OpEntryPoint(it));
Scalar scalar(it);
scalarTypeToId[scalar] = opdata.result;
}
else if(opcode == Op::MemoryModel)
else if(opdata.op == Op::TypeVector)
{
OpMemoryModel decoded(it);
addressmodel = decoded.addressingModel;
memorymodel = decoded.memoryModel;
OpTypeVector decoded(it);
vectorTypeToId[Vector(scalarTypes[decoded.componentType], decoded.componentCount)] =
decoded.result;
}
else if(opcode == Op::Capability)
else if(opdata.op == Op::TypeMatrix)
{
OpCapability decoded(it);
capabilities.insert(decoded.capability);
OpTypeMatrix decoded(it);
matrixTypeToId[Matrix(vectorTypes[decoded.columnType], decoded.columnCount)] = decoded.result;
}
else if(opcode == Op::Extension)
else if(opdata.op == Op::TypeImage)
{
OpExtension decoded(it);
extensions.insert(decoded.name);
OpTypeImage decoded(it);
imageTypeToId[Image(scalarTypes[decoded.sampledType], decoded.dim, decoded.depth, decoded.arrayed,
decoded.mS, decoded.sampled, decoded.imageFormat)] = decoded.result;
}
else if(opcode == Op::ExtInstImport)
else if(opdata.op == Op::TypeSampler)
{
OpExtInstImport decoded(it);
extSets[decoded.name] = decoded.result;
samplerTypeToId[Sampler()] = opdata.result;
}
else if(opcode == Op::Function)
else if(opdata.op == Op::TypeSampledImage)
{
functions.push_back(opdata.result);
OpTypeSampledImage decoded(it);
sampledImageTypeToId[SampledImage(decoded.imageType)] = decoded.result;
}
else if(opcode == Op::Variable)
else if(opdata.op == Op::TypePointer)
{
variables.push_back(OpVariable(it));
OpTypePointer decoded(it);
pointerTypeToId[Pointer(decoded.type, decoded.storageClass)] = decoded.result;
}
else if(opcode == Op::Decorate)
else if(opdata.op == Op::TypeFunction)
{
OpTypeFunction decoded(it);
functionTypeToId[FunctionType(decoded.returnType, decoded.parameters)] = decoded.result;
}
else if(opdata.op == Op::Decorate)
{
OpDecorate decorate(it);
auto it = std::lower_bound(decorations.begin(), decorations.end(), decorate,
[](const OpDecorate &a, const OpDecorate &b) { return a < b; });
decorations.insert(it, decorate);
if(decorate.decoration == Decoration::DescriptorSet)
bindings[decorate.target].set = decorate.decoration.descriptorSet;
if(decorate.decoration == Decoration::Binding)
bindings[decorate.target].binding = decorate.decoration.binding;
}
else if(opcode == Op::TypeVoid || opcode == Op::TypeBool || opcode == Op::TypeInt ||
opcode == Op::TypeFloat)
{
Scalar scalar(it);
scalarTypes[scalar] = opdata.result;
}
else if(opcode == Op::TypeVector)
{
OpTypeVector decoded(it);
Iter scalarIt = GetID(decoded.componentType);
if(!scalarIt)
{
RDCERR("Vector type declared with unknown scalar component type %u", decoded.componentType);
return;
}
vectorTypes[Vector(scalarIt, decoded.componentCount)] = decoded.result;
}
else if(opcode == Op::TypeMatrix)
{
OpTypeMatrix decodedMatrix(it);
Iter vectorIt = GetID(decodedMatrix.columnType);
if(!vectorIt)
{
RDCERR("Matrix type declared with unknown vector component type %u", decodedMatrix.columnType);
return;
}
OpTypeVector decodedVector(vectorIt);
Iter scalarIt = GetID(decodedVector.componentType);
matrixTypes[Matrix(Vector(scalarIt, decodedVector.componentCount), decodedMatrix.columnCount)] =
decodedMatrix.result;
}
else if(opcode == Op::TypeImage)
{
OpTypeImage decoded(it);
Iter scalarIt = GetID(decoded.sampledType);
if(!scalarIt)
{
RDCERR("Image type declared with unknown scalar component type %u", decoded.sampledType);
return;
}
imageTypes[Image(scalarIt, decoded.dim, decoded.depth, decoded.arrayed, decoded.mS,
decoded.sampled, decoded.imageFormat)] = decoded.result;
}
else if(opcode == Op::TypeSampler)
{
samplerTypes[Sampler()] = opdata.result;
}
else if(opcode == Op::TypeSampledImage)
{
OpTypeSampledImage decoded(it);
sampledImageTypes[SampledImage(decoded.imageType)] = decoded.result;
}
else if(opcode == Op::TypePointer)
{
OpTypePointer decoded(it);
pointerTypes[Pointer(decoded.type, decoded.storageClass)] = decoded.result;
}
else if(opcode == Op::TypeStruct)
{
structTypes.insert(opdata.result);
}
else if(opcode == Op::TypeFunction)
{
OpTypeFunction decoded(it);
functionTypes[Function(decoded.returnType, decoded.parameters)] = decoded.result;
}
}
void Editor::UnregisterOp(Iter it)
{
Op opcode = it.opcode();
Processor::UnregisterOp(it);
OpDecoder opdata(it);
if(opdata.result != Id() && opdata.resultType != Id())
idTypes[opdata.result.value()] = Id();
if(opdata.result != Id())
idOffsets[opdata.result.value()] = 0;
if(opcode == Op::EntryPoint)
if(opdata.op == Op::TypeVoid || opdata.op == Op::TypeBool || opdata.op == Op::TypeInt ||
opdata.op == Op::TypeFloat)
{
OpEntryPoint decoded(it);
for(auto entryIt = entries.begin(); entryIt != entries.end(); ++entryIt)
{
if(entryIt->entryPoint == decoded.entryPoint)
{
entries.erase(entryIt);
break;
}
}
Scalar scalar(it);
scalarTypeToId.erase(scalar);
}
else if(opcode == Op::Function)
else if(opdata.op == Op::TypeVector)
{
for(auto funcIt = functions.begin(); funcIt != functions.end(); ++funcIt)
{
if(*funcIt == opdata.result)
{
functions.erase(funcIt);
break;
}
}
OpTypeVector decoded(it);
vectorTypeToId.erase(Vector(scalarTypes[decoded.componentType], decoded.componentCount));
}
else if(opcode == Op::Variable)
else if(opdata.op == Op::TypeMatrix)
{
for(auto varIt = variables.begin(); varIt != variables.end(); ++varIt)
{
if(varIt->result == opdata.result)
{
variables.erase(varIt);
break;
}
}
OpTypeMatrix decoded(it);
matrixTypeToId.erase(Matrix(vectorTypes[decoded.columnType], decoded.columnCount));
}
else if(opcode == Op::Decorate)
else if(opdata.op == Op::TypeImage)
{
OpTypeImage decoded(it);
imageTypeToId.erase(Image(scalarTypes[decoded.sampledType], decoded.dim, decoded.depth,
decoded.arrayed, decoded.mS, decoded.sampled, decoded.imageFormat));
}
else if(opdata.op == Op::TypeSampler)
{
samplerTypeToId.erase(Sampler());
}
else if(opdata.op == Op::TypeSampledImage)
{
OpTypeSampledImage decoded(it);
sampledImageTypeToId.erase(SampledImage(decoded.imageType));
}
else if(opdata.op == Op::TypePointer)
{
OpTypePointer decoded(it);
pointerTypeToId.erase(Pointer(decoded.type, decoded.storageClass));
}
else if(opdata.op == Op::TypeFunction)
{
OpTypeFunction decoded(it);
functionTypeToId.erase(FunctionType(decoded.returnType, decoded.parameters));
}
else if(opdata.op == Op::Decorate)
{
OpDecorate decorate(it);
auto it = std::lower_bound(decorations.begin(), decorations.end(), decorate,
[](const OpDecorate &a, const OpDecorate &b) { return a < b; });
if(it != decorations.end() && *it == decorate)
decorations.erase(it);
if(decorate.decoration == Decoration::DescriptorSet)
bindings[decorate.target].set = Binding().set;
if(decorate.decoration == Decoration::Binding)
bindings[decorate.target].binding = Binding().binding;
}
else if(opcode == Op::Capability)
{
OpCapability decoded(it);
capabilities.erase(decoded.capability);
}
else if(opcode == Op::Extension)
{
OpExtension decoded(it);
extensions.erase(decoded.name);
}
else if(opcode == Op::ExtInstImport)
{
OpExtInstImport decoded(it);
extSets.erase(decoded.name);
}
else if(opcode == Op::TypeVoid || opcode == Op::TypeBool || opcode == Op::TypeInt ||
opcode == Op::TypeFloat)
{
Scalar scalar(it);
scalarTypes.erase(scalar);
}
else if(opcode == Op::TypeVector)
{
OpTypeVector decoded(it);
Iter scalarIt = GetID(decoded.componentType);
if(!scalarIt)
{
RDCERR("Vector type declared with unknown scalar component type %u", decoded.componentType);
return;
}
vectorTypes.erase(Vector(scalarIt, decoded.componentCount));
}
else if(opcode == Op::TypeMatrix)
{
OpTypeMatrix decodedMatrix(it);
Iter vectorIt = GetID(decodedMatrix.columnType);
if(!vectorIt)
{
RDCERR("Matrix type declared with unknown vector component type %u", decodedMatrix.columnType);
return;
}
OpTypeVector decodedVector(vectorIt);
Iter scalarIt = GetID(decodedVector.componentType);
matrixTypes.erase(
Matrix(Vector(scalarIt, decodedVector.componentCount), decodedMatrix.columnCount));
}
else if(opcode == Op::TypeImage)
{
OpTypeImage decoded(it);
Iter scalarIt = GetID(decoded.sampledType);
if(!scalarIt)
{
RDCERR("Image type declared with unknown scalar component type %u", decoded.sampledType);
return;
}
imageTypes.erase(Image(scalarIt, decoded.dim, decoded.depth, decoded.arrayed, decoded.mS,
decoded.sampled, decoded.imageFormat));
}
else if(opcode == Op::TypeSampler)
{
samplerTypes.erase(Sampler());
}
else if(opcode == Op::TypeSampledImage)
{
OpTypeSampledImage decoded(it);
sampledImageTypes.erase(SampledImage(decoded.imageType));
}
else if(opcode == Op::TypePointer)
{
OpTypePointer decoded(it);
pointerTypes.erase(Pointer(decoded.type, decoded.storageClass));
}
else if(opcode == Op::TypeStruct)
{
structTypes.erase(opdata.result);
}
else if(opcode == Op::TypeFunction)
{
OpTypeFunction decoded(it);
functionTypes.erase(Function(decoded.returnType, decoded.parameters));
}
}
void Editor::addWords(size_t offs, int32_t num)
@@ -781,7 +460,7 @@ void Editor::addWords(size_t offs, int32_t num)
// look through every section, any that are >= this point, adjust the offsets
// note that if we're removing words then any offsets pointing directly to the removed words
// will go backwards - but they no longer have anywhere valid to point.
for(LogicalSection &section : sections)
for(LogicalSection &section : m_Sections)
{
// we have three cases to consider: either the offset matches start, is within (up to and
// including end) or is outside the section.
@@ -817,6 +496,56 @@ void Editor::addWords(size_t offs, int32_t num)
o += num;
}
Operation Editor::MakeDeclaration(const Scalar &s)
{
if(s.type == Op::TypeVoid)
return OpTypeVoid(Id());
else if(s.type == Op::TypeBool)
return OpTypeBool(Id());
else if(s.type == Op::TypeFloat)
return OpTypeFloat(Id(), s.width);
else if(s.type == Op::TypeInt)
return OpTypeInt(Id(), s.width, s.signedness ? 1U : 0U);
else
return OpNop();
}
Operation Editor::MakeDeclaration(const Vector &v)
{
return OpTypeVector(Id(), DeclareType(v.scalar), v.count);
}
Operation Editor::MakeDeclaration(const Matrix &m)
{
return OpTypeMatrix(Id(), DeclareType(m.vector), m.count);
}
Operation Editor::MakeDeclaration(const Pointer &p)
{
return OpTypePointer(Id(), p.storage, p.baseId);
}
Operation Editor::MakeDeclaration(const Image &i)
{
return OpTypeImage(Id(), DeclareType(i.retType), i.dim, i.depth, i.arrayed, i.ms, i.sampled,
i.format);
}
Operation Editor::MakeDeclaration(const Sampler &s)
{
return OpTypeSampler(Id());
}
Operation Editor::MakeDeclaration(const SampledImage &s)
{
return OpTypeSampledImage(Id(), s.baseId);
}
Operation Editor::MakeDeclaration(const FunctionType &f)
{
return OpTypeFunction(Id(), f.returnId, f.argumentIds);
}
#define TYPETABLE(StructType, variable) \
template <> \
std::map<StructType, Id> &Editor::GetTable<StructType>() \
@@ -829,14 +558,14 @@ void Editor::addWords(size_t offs, int32_t num)
return variable; \
}
TYPETABLE(Scalar, scalarTypes);
TYPETABLE(Vector, vectorTypes);
TYPETABLE(Matrix, matrixTypes);
TYPETABLE(Pointer, pointerTypes);
TYPETABLE(Image, imageTypes);
TYPETABLE(Sampler, samplerTypes);
TYPETABLE(SampledImage, sampledImageTypes);
TYPETABLE(Function, functionTypes);
TYPETABLE(Scalar, scalarTypeToId);
TYPETABLE(Vector, vectorTypeToId);
TYPETABLE(Matrix, matrixTypeToId);
TYPETABLE(Pointer, pointerTypeToId);
TYPETABLE(Image, imageTypeToId);
TYPETABLE(Sampler, samplerTypeToId);
TYPETABLE(SampledImage, sampledImageTypeToId);
TYPETABLE(FunctionType, functionTypeToId);
}; // namespace rdcspv
@@ -852,6 +581,8 @@ static void RemoveSection(std::vector<uint32_t> &spirv, size_t offsets[rdcspv::S
{
rdcspv::Editor ed(spirv);
ed.Prepare();
for(rdcspv::Iter it = ed.Begin(section), end = ed.End(section); it < end; it++)
ed.Remove(it);
@@ -883,7 +614,7 @@ static void CheckSPIRV(rdcspv::Editor &ed, size_t offsets[rdcspv::Section::Count
// should only be one entry point
REQUIRE(ed.GetEntries().size() == 1);
rdcspv::Id entryId = ed.GetEntries()[0].entryPoint;
rdcspv::Id entryId = ed.GetEntries()[0].id;
// check that the iterator places us precisely at the start of the functions section
CHECK(ed.GetID(entryId).offs() == ed.Begin(rdcspv::Section::Functions).offs());
@@ -956,6 +687,8 @@ void main() {
{
rdcspv::Editor ed(spirv);
ed.Prepare();
CheckSPIRV(ed, offsets);
}
@@ -967,6 +700,8 @@ void main() {
{
rdcspv::Editor ed(spirv);
ed.Prepare();
CheckSPIRV(ed, offsets);
}
@@ -976,6 +711,8 @@ void main() {
{
rdcspv::Editor ed(spirv);
ed.Prepare();
CheckSPIRV(ed, offsets);
}
@@ -985,6 +722,8 @@ void main() {
{
rdcspv::Editor ed(spirv);
ed.Prepare();
CheckSPIRV(ed, offsets);
}
@@ -994,6 +733,8 @@ void main() {
{
rdcspv::Editor ed(spirv);
ed.Prepare();
CheckSPIRV(ed, offsets);
}
@@ -1003,6 +744,8 @@ void main() {
{
rdcspv::Editor ed(spirv);
ed.Prepare();
CheckSPIRV(ed, offsets);
}
}
+51 -293
View File
@@ -33,6 +33,7 @@
#include "common/common.h"
#include "spirv_common.h"
#include "spirv_op_helpers.h"
#include "spirv_processor.h"
namespace rdcspv
{
@@ -56,241 +57,19 @@ struct Binding
bool operator==(const Binding &o) const { return set == o.set && binding == o.binding; }
};
struct Scalar
{
Scalar() : type(Op::Max), width(0), signedness(false) {}
constexpr Scalar(Op t, uint32_t w, bool s) : type(t), width(w), signedness(s) {}
Scalar(Iter op);
template <typename SPIRVType>
using TypeToId = std::pair<SPIRVType, Id>;
Op type;
uint32_t width;
bool signedness;
template <typename SPIRVType>
using TypeToIds = std::vector<TypeToId<SPIRVType>>;
bool operator<(const Scalar &o) const
{
if(type != o.type)
return type < o.type;
if(signedness != o.signedness)
return signedness < o.signedness;
return width < o.width;
}
bool operator!=(const Scalar &o) const { return !operator==(o); }
bool operator==(const Scalar &o) const
{
return type == o.type && width == o.width && signedness == o.signedness;
}
Operation decl(Editor &editor) const
{
if(type == Op::TypeVoid)
return OpTypeVoid(Id());
else if(type == Op::TypeBool)
return OpTypeBool(Id());
else if(type == Op::TypeFloat)
return OpTypeFloat(Id(), width);
else if(type == Op::TypeInt)
return OpTypeInt(Id(), width, signedness ? 1U : 0U);
else
return OpNop();
}
};
// helper to create Scalar objects for known types
template <typename T>
inline constexpr Scalar scalar();
#define SCALAR_TYPE(ctype, op, width, sign) \
template <> \
inline constexpr Scalar scalar<ctype>() \
{ \
return Scalar(op, width, sign); \
}
SCALAR_TYPE(void, Op::TypeVoid, 0, false);
SCALAR_TYPE(bool, Op::TypeBool, 0, false);
SCALAR_TYPE(uint8_t, Op::TypeInt, 8, false);
SCALAR_TYPE(uint16_t, Op::TypeInt, 16, false);
SCALAR_TYPE(uint32_t, Op::TypeInt, 32, false);
SCALAR_TYPE(uint64_t, Op::TypeInt, 64, false);
SCALAR_TYPE(int8_t, Op::TypeInt, 8, true);
SCALAR_TYPE(int16_t, Op::TypeInt, 16, true);
SCALAR_TYPE(int32_t, Op::TypeInt, 32, true);
SCALAR_TYPE(int64_t, Op::TypeInt, 64, true);
SCALAR_TYPE(float, Op::TypeFloat, 32, false);
SCALAR_TYPE(double, Op::TypeFloat, 64, false);
struct Vector
{
Vector(const Scalar &s, uint32_t c) : scalar(s), count(c) {}
Scalar scalar;
uint32_t count;
bool operator<(const Vector &o) const
{
if(scalar != o.scalar)
return scalar < o.scalar;
return count < o.count;
}
bool operator!=(const Vector &o) const { return !operator==(o); }
bool operator==(const Vector &o) const { return scalar == o.scalar && count == o.count; }
Operation decl(Editor &editor) const;
};
struct Matrix
{
Matrix(const Vector &v, uint32_t c) : vector(v), count(c) {}
Vector vector;
uint32_t count;
bool operator<(const Matrix &o) const
{
if(vector != o.vector)
return vector < o.vector;
return count < o.count;
}
bool operator!=(const Matrix &o) const { return !operator==(o); }
bool operator==(const Matrix &o) const { return vector == o.vector && count == o.count; }
Operation decl(Editor &editor) const;
};
struct Pointer
{
Pointer(Id b, StorageClass s) : baseId(b), storage(s) {}
Id baseId;
StorageClass storage;
bool operator<(const Pointer &o) const
{
if(baseId != o.baseId)
return baseId < o.baseId;
return storage < o.storage;
}
bool operator!=(const Pointer &o) const { return !operator==(o); }
bool operator==(const Pointer &o) const { return baseId == o.baseId && storage == o.storage; }
Operation decl(Editor &editor) const;
};
struct Image
{
Image(Scalar ret, Dim d, uint32_t dp, uint32_t ar, uint32_t m, uint32_t samp, ImageFormat f)
: retType(ret), dim(d), depth(dp), arrayed(ar), ms(m), sampled(samp), format(f)
{
}
Scalar retType;
Dim dim;
uint32_t depth;
uint32_t arrayed;
uint32_t ms;
uint32_t sampled;
ImageFormat format;
bool operator<(const Image &o) const
{
if(retType != o.retType)
return retType < o.retType;
if(dim != o.dim)
return dim < o.dim;
if(depth != o.depth)
return depth < o.depth;
if(arrayed != o.arrayed)
return arrayed < o.arrayed;
if(ms != o.ms)
return ms < o.ms;
if(sampled != o.sampled)
return sampled < o.sampled;
return format < o.format;
}
bool operator!=(const Image &o) const { return !operator==(o); }
bool operator==(const Image &o) const
{
return retType == o.retType && dim == o.dim && depth == o.depth && arrayed == o.arrayed &&
ms == o.ms && sampled == o.sampled && format == o.format;
}
Operation decl(Editor &editor) const;
};
struct Sampler
{
// no properties, all sampler types are equal
bool operator<(const Sampler &o) const { return false; }
bool operator!=(const Sampler &o) const { return false; }
bool operator==(const Sampler &o) const { return true; }
Operation decl(Editor &editor) const;
};
struct SampledImage
{
SampledImage(Id b) : baseId(b) {}
Id baseId;
bool operator<(const SampledImage &o) const { return baseId < o.baseId; }
bool operator!=(const SampledImage &o) const { return !operator==(o); }
bool operator==(const SampledImage &o) const { return baseId == o.baseId; }
Operation decl(Editor &editor) const;
};
struct Function
{
Function(Id ret, const rdcarray<Id> &args) : returnId(ret), argumentIds(args) {}
Id returnId;
rdcarray<Id> argumentIds;
bool operator<(const Function &o) const
{
if(returnId != o.returnId)
return returnId < o.returnId;
return argumentIds < o.argumentIds;
}
bool operator!=(const Function &o) const { return !operator==(o); }
bool operator==(const Function &o) const
{
return returnId == o.returnId && argumentIds == o.argumentIds;
}
Operation decl(Editor &editor) const;
};
template <typename Type>
using TypeId = std::pair<Type, Id>;
template <typename Type>
using TypeIds = std::vector<TypeId<Type>>;
// hack around enum class being useless for array indices :(
struct Section
{
enum Type
{
Capabilities,
First = Capabilities,
Extensions,
ExtInst,
MemoryModel,
EntryPoints,
ExecutionMode,
Debug,
Annotations,
TypesVariablesConstants,
// handy aliases
Types = TypesVariablesConstants,
Variables = TypesVariablesConstants,
Constants = TypesVariablesConstants,
Functions,
Count,
};
};
class Editor
class Editor : public Processor
{
public:
Editor(std::vector<uint32_t> &spirvWords);
~Editor() { StripNops(); }
void StripNops();
~Editor();
void Prepare();
Id MakeId();
@@ -325,33 +104,33 @@ public:
// the entry point has 'two' opcodes, the entrypoint declaration and the function.
// This returns the first, GetID returns the second.
Iter GetEntry(Id id);
Iter Begin(Section::Type section) { return Iter(spirv, sections[section].startOffset); }
Iter End(Section::Type section) { return Iter(spirv, sections[section].endOffset); }
Iter Begin(Section::Type section) { return Iter(m_SPIRV, m_Sections[section].startOffset); }
Iter End(Section::Type section) { return Iter(m_SPIRV, m_Sections[section].endOffset); }
// fetches the id of this type. If it exists already the old ID will be returned, otherwise it
// will be declared and the new ID returned
template <typename Type>
Id DeclareType(const Type &t)
template <typename SPIRVType>
Id DeclareType(const SPIRVType &t)
{
std::map<Type, Id> &table = GetTable<Type>();
std::map<SPIRVType, Id> &table = GetTable<SPIRVType>();
auto it = table.lower_bound(t);
if(it != table.end() && it->first == t)
return it->second;
Operation decl = t.decl(*this);
Operation decl = MakeDeclaration(t);
Id id = MakeId();
decl[1] = id.value();
AddType(decl);
table.insert(it, std::pair<Type, Id>(t, id));
table.insert(it, std::pair<SPIRVType, Id>(t, id));
return id;
}
template <typename Type>
Id GetType(const Type &t)
template <typename SPIRVType>
Id GetType(const SPIRVType &t)
{
std::map<Type, Id> &table = GetTable<Type>();
std::map<SPIRVType, Id> &table = GetTable<SPIRVType>();
auto it = table.find(t);
if(it != table.end())
@@ -360,12 +139,12 @@ public:
return Id();
}
template <typename Type>
TypeIds<Type> GetTypes()
template <typename SPIRVType>
TypeToIds<SPIRVType> GetTypes()
{
std::map<Type, Id> &table = GetTable<Type>();
std::map<SPIRVType, Id> &table = GetTable<SPIRVType>();
TypeIds<Type> ret;
TypeToIds<SPIRVType> ret;
for(auto it = table.begin(); it != table.end(); ++it)
ret.push_back(*it);
@@ -373,10 +152,10 @@ public:
return ret;
}
template <typename Type>
const std::map<Type, Id> &GetTypeInfo() const
template <typename SPIRVType>
const std::map<SPIRVType, Id> &GetTypeInfo() const
{
return GetTable<Type>();
return GetTable<SPIRVType>();
}
Binding GetBinding(Id id) const
@@ -386,7 +165,7 @@ public:
return Binding();
return it->second;
}
const std::set<Id> &GetStructTypes() const { return structTypes; }
Id DeclareStructType(const std::vector<Id> &members);
// helper for AddConstant
@@ -403,62 +182,41 @@ public:
return AddConstant(Operation(Op::Constant, words));
}
// accessors to structs/vectors of data
const std::vector<OpEntryPoint> &GetEntries() { return entries; }
const std::vector<OpVariable> &GetVariables() { return variables; }
const std::vector<Id> &GetFunctions() { return functions; }
Id GetIDType(Id id) { return idTypes[id.value()]; }
private:
using Processor::Parse;
inline void addWords(size_t offs, size_t num) { addWords(offs, (int32_t)num); }
void addWords(size_t offs, int32_t num);
void RegisterOp(Iter iter);
void UnregisterOp(Iter iter);
Operation MakeDeclaration(const Scalar &s);
Operation MakeDeclaration(const Vector &v);
Operation MakeDeclaration(const Matrix &m);
Operation MakeDeclaration(const Pointer &p);
Operation MakeDeclaration(const Image &i);
Operation MakeDeclaration(const Sampler &s);
Operation MakeDeclaration(const SampledImage &s);
Operation MakeDeclaration(const FunctionType &f);
struct LogicalSection
{
size_t startOffset = 0;
size_t endOffset = 0;
};
LogicalSection sections[Section::Count];
AddressingModel addressmodel;
MemoryModel memorymodel;
std::vector<OpDecorate> decorations;
virtual void RegisterOp(Iter iter);
virtual void UnregisterOp(Iter iter);
std::map<Id, Binding> bindings;
std::vector<size_t> idOffsets;
std::vector<Id> idTypes;
std::map<Scalar, Id> scalarTypeToId;
std::map<Vector, Id> vectorTypeToId;
std::map<Matrix, Id> matrixTypeToId;
std::map<Pointer, Id> pointerTypeToId;
std::map<Image, Id> imageTypeToId;
std::map<Sampler, Id> samplerTypeToId;
std::map<SampledImage, Id> sampledImageTypeToId;
std::map<FunctionType, Id> functionTypeToId;
std::vector<OpEntryPoint> entries;
std::vector<OpVariable> variables;
std::vector<Id> functions;
std::set<rdcstr> extensions;
std::set<Capability> capabilities;
template <typename SPIRVType>
std::map<SPIRVType, Id> &GetTable();
std::map<rdcstr, Id> extSets;
template <typename SPIRVType>
const std::map<SPIRVType, Id> &GetTable() const;
std::map<Scalar, Id> scalarTypes;
std::map<Vector, Id> vectorTypes;
std::map<Matrix, Id> matrixTypes;
std::map<Pointer, Id> pointerTypes;
std::map<Image, Id> imageTypes;
std::map<Sampler, Id> samplerTypes;
std::map<SampledImage, Id> sampledImageTypes;
std::map<Function, Id> functionTypes;
std::set<Id> structTypes;
template <typename Type>
std::map<Type, Id> &GetTable();
template <typename Type>
const std::map<Type, Id> &GetTable() const;
std::vector<uint32_t> &spirv;
std::vector<uint32_t> &m_ExternalSPIRV;
};
inline bool operator<(const OpDecorate &a, const OpDecorate &b)
@@ -0,0 +1,348 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019 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 "spirv_processor.h"
#include "spirv_op_helpers.h"
namespace rdcspv
{
Processor::Processor()
{
}
void Processor::Parse(const std::vector<uint32_t> &spirvWords)
{
m_SPIRV = spirvWords;
if(m_SPIRV.size() < FirstRealWord || m_SPIRV[0] != MagicNumber)
{
RDCERR("Empty or invalid SPIR-V module");
m_SPIRV.clear();
return;
}
// [4] is reserved
RDCASSERT(m_SPIRV[4] == 0);
PreParse(m_SPIRV[3]);
// simple state machine to track which section we're in.
// Note that a couple of sections are optional and could be skipped over, at which point we insert
// a dummy OpNop so they're not empty (which will be stripped later) and record them as in
// between.
//
// We only handle single-shader modules at the moment, so some things are required by virtue of
// being required in a shader - e.g. at least the Shader capability, at least one entry point, etc
//
// Capabilities: REQUIRED (we assume - must declare Shader capability)
// Extensions: OPTIONAL
// ExtInst: OPTIONAL
// MemoryModel: REQUIRED (required by spec)
// EntryPoints: REQUIRED (we assume)
// ExecutionMode: OPTIONAL
// Debug: OPTIONAL
// Annotations: OPTIONAL (in theory - would require empty shader)
// TypesVariables: REQUIRED (must at least have the entry point function type)
// Functions: REQUIRED (must have the entry point)
// set the book-ends: start of the first section and end of the last
m_Sections[Section::Count - 1].endOffset = m_SPIRV.size();
#define START_SECTION(section) \
if(m_Sections[section].startOffset == 0) \
m_Sections[section].startOffset = it.offs();
for(Iter it(m_SPIRV, FirstRealWord); it; it++)
{
Op opcode = it.opcode();
if(opcode == Op::Capability)
{
START_SECTION(Section::Capabilities);
}
else if(opcode == Op::Extension)
{
START_SECTION(Section::Extensions);
}
else if(opcode == Op::ExtInstImport)
{
START_SECTION(Section::ExtInst);
}
else if(opcode == Op::MemoryModel)
{
START_SECTION(Section::MemoryModel);
}
else if(opcode == Op::EntryPoint)
{
START_SECTION(Section::EntryPoints);
}
else if(opcode == Op::ExecutionMode || opcode == Op::ExecutionModeId)
{
START_SECTION(Section::ExecutionMode);
}
else if(opcode == Op::String || opcode == Op::Source || opcode == Op::SourceContinued ||
opcode == Op::SourceExtension || opcode == Op::Name || opcode == Op::MemberName ||
opcode == Op::ModuleProcessed)
{
START_SECTION(Section::Debug);
}
else if(opcode == Op::Decorate || opcode == Op::MemberDecorate || opcode == Op::GroupDecorate ||
opcode == Op::GroupMemberDecorate || opcode == Op::DecorationGroup ||
opcode == Op::DecorateStringGOOGLE || opcode == Op::MemberDecorateStringGOOGLE)
{
START_SECTION(Section::Annotations);
}
else if(opcode == Op::Function)
{
START_SECTION(Section::Functions);
}
else
{
// if we've reached another instruction, check if we've reached the function section yet. If
// we have then assume it's an instruction inside a function and ignore. If we haven't, assume
// it's a type/variable/constant type instruction
if(m_Sections[Section::Functions].startOffset == 0)
{
START_SECTION(Section::TypesVariablesConstants);
}
}
RegisterOp(it);
}
#undef START_SECTION
PostParse();
// ensure we got everything right. First section should start at the beginning
RDCASSERTEQUAL(m_Sections[Section::First].startOffset, FirstRealWord);
// we now set the endOffset of each section to the start of the next. Any empty sections
// temporarily have startOffset set to endOffset, we'll pad them with a nop below.
for(int s = Section::Count - 1; s > 0; s--)
{
RDCASSERTEQUAL(m_Sections[s - 1].endOffset, 0);
m_Sections[s - 1].endOffset = m_Sections[s].startOffset;
if(m_Sections[s - 1].startOffset == 0)
m_Sections[s - 1].startOffset = m_Sections[s - 1].endOffset;
}
}
void Processor::PreParse(uint32_t maxId)
{
idOffsets.resize(maxId);
idTypes.resize(maxId);
}
void Processor::RegisterOp(Iter it)
{
OpDecoder opdata(it);
if(opdata.result != Id() && opdata.resultType != Id())
{
RDCASSERT(opdata.result.value() < idTypes.size());
idTypes[opdata.result.value()] = opdata.resultType;
}
if(opdata.result != Id())
idOffsets[opdata.result.value()] = it.offs();
if(opdata.op == Op::Capability)
{
OpCapability decoded(it);
capabilities.insert(decoded.capability);
}
else if(opdata.op == Op::Extension)
{
OpExtension decoded(it);
extensions.insert(decoded.name);
}
else if(opdata.op == Op::ExtInstImport)
{
OpExtInstImport decoded(it);
extSets[decoded.name] = decoded.result;
}
else if(opdata.op == Op::Function)
{
functions.push_back(opdata.result);
}
else if(opdata.op == Op::EntryPoint)
{
OpEntryPoint decoded(it);
entries.push_back(EntryPoint(decoded.executionModel, decoded.entryPoint, decoded.name));
}
else if(opdata.op == Op::Variable)
{
OpVariable decoded(it);
// only register global variables here
if(decoded.storageClass != rdcspv::StorageClass::Function)
globals.push_back(Variable(decoded.resultType, decoded.result, decoded.storageClass));
}
else if(opdata.op == Op::TypeVoid || opdata.op == Op::TypeBool || opdata.op == Op::TypeInt ||
opdata.op == Op::TypeFloat)
{
scalarTypes[opdata.result] = Scalar(it);
}
else if(opdata.op == Op::TypeVector)
{
OpTypeVector decoded(it);
vectorTypes[opdata.result] = Vector(scalarTypes[decoded.componentType], decoded.componentCount);
}
else if(opdata.op == Op::TypeMatrix)
{
OpTypeMatrix decoded(it);
matrixTypes[opdata.result] = Matrix(vectorTypes[decoded.columnType], decoded.columnCount);
}
else if(opdata.op == Op::TypeImage)
{
OpTypeImage decoded(it);
imageTypes[opdata.result] =
Image(scalarTypes[decoded.sampledType], decoded.dim, decoded.depth, decoded.arrayed,
decoded.mS, decoded.sampled, decoded.imageFormat);
}
else if(opdata.op == Op::TypeSampler)
{
samplerTypes[opdata.result] = Sampler();
}
else if(opdata.op == Op::TypeSampledImage)
{
OpTypeSampledImage decoded(it);
sampledImageTypes[decoded.result] = SampledImage(decoded.imageType);
}
else if(opdata.op == Op::TypePointer)
{
OpTypePointer decoded(it);
pointerTypes[decoded.result] = Pointer(decoded.type, decoded.storageClass);
}
else if(opdata.op == Op::TypeFunction)
{
OpTypeFunction decoded(it);
functionTypes[decoded.result] = FunctionType(decoded.returnType, decoded.parameters);
}
}
void Processor::UnregisterOp(Iter it)
{
OpDecoder opdata(it);
if(opdata.result != Id() && opdata.resultType != Id())
idTypes[opdata.result.value()] = Id();
if(opdata.result != Id())
idOffsets[opdata.result.value()] = 0;
if(opdata.op == Op::Capability)
{
OpCapability decoded(it);
capabilities.erase(decoded.capability);
}
else if(opdata.op == Op::Extension)
{
OpExtension decoded(it);
extensions.erase(decoded.name);
}
else if(opdata.op == Op::ExtInstImport)
{
OpExtInstImport decoded(it);
extSets.erase(decoded.name);
}
else if(opdata.op == Op::Function)
{
for(auto funcIt = functions.begin(); funcIt != functions.end(); ++funcIt)
{
if(*funcIt == opdata.result)
{
functions.erase(funcIt);
break;
}
}
}
else if(opdata.op == Op::EntryPoint)
{
OpEntryPoint decoded(it);
for(auto entryIt = entries.begin(); entryIt != entries.end(); ++entryIt)
{
if(entryIt->id == decoded.entryPoint)
{
entries.erase(entryIt);
break;
}
}
}
else if(opdata.op == Op::Variable)
{
for(auto varIt = globals.begin(); varIt != globals.end(); ++varIt)
{
if(varIt->id == opdata.result)
{
globals.erase(varIt);
break;
}
}
}
else if(opdata.op == Op::TypeVoid || opdata.op == Op::TypeBool || opdata.op == Op::TypeInt ||
opdata.op == Op::TypeFloat)
{
scalarTypes.erase(opdata.result);
}
else if(opdata.op == Op::TypeVector)
{
vectorTypes.erase(opdata.result);
}
else if(opdata.op == Op::TypeMatrix)
{
matrixTypes.erase(opdata.result);
}
else if(opdata.op == Op::TypeImage)
{
imageTypes.erase(opdata.result);
}
else if(opdata.op == Op::TypeSampler)
{
samplerTypes.erase(opdata.result);
}
else if(opdata.op == Op::TypeSampledImage)
{
sampledImageTypes.erase(opdata.result);
}
else if(opdata.op == Op::TypePointer)
{
pointerTypes.erase(opdata.result);
}
else if(opdata.op == Op::TypeFunction)
{
functionTypes.erase(opdata.result);
}
}
void Processor::PostParse()
{
}
}; // namespace rdcspv
@@ -0,0 +1,347 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019 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.
******************************************************************************/
#pragma once
#include <stdint.h>
#include <map>
#include <set>
#include <string>
#include <vector>
#include "common/common.h"
#include "spirv_common.h"
namespace rdcspv
{
struct Scalar
{
Scalar() : type(Op::Max), width(0), signedness(false) {}
constexpr Scalar(Op t, uint32_t w, bool s) : type(t), width(w), signedness(s) {}
Scalar(Iter op);
Op type;
uint32_t width;
bool signedness;
bool operator<(const Scalar &o) const
{
if(type != o.type)
return type < o.type;
if(signedness != o.signedness)
return signedness < o.signedness;
return width < o.width;
}
bool operator!=(const Scalar &o) const { return !operator==(o); }
bool operator==(const Scalar &o) const
{
return type == o.type && width == o.width && signedness == o.signedness;
}
};
// helper to create Scalar objects for known types
template <typename T>
inline constexpr Scalar scalar();
#define SCALAR_TYPE(ctype, op, width, sign) \
template <> \
inline constexpr Scalar scalar<ctype>() \
{ \
return Scalar(op, width, sign); \
}
SCALAR_TYPE(void, Op::TypeVoid, 0, false);
SCALAR_TYPE(bool, Op::TypeBool, 0, false);
SCALAR_TYPE(uint8_t, Op::TypeInt, 8, false);
SCALAR_TYPE(uint16_t, Op::TypeInt, 16, false);
SCALAR_TYPE(uint32_t, Op::TypeInt, 32, false);
SCALAR_TYPE(uint64_t, Op::TypeInt, 64, false);
SCALAR_TYPE(int8_t, Op::TypeInt, 8, true);
SCALAR_TYPE(int16_t, Op::TypeInt, 16, true);
SCALAR_TYPE(int32_t, Op::TypeInt, 32, true);
SCALAR_TYPE(int64_t, Op::TypeInt, 64, true);
SCALAR_TYPE(float, Op::TypeFloat, 32, false);
SCALAR_TYPE(double, Op::TypeFloat, 64, false);
struct Vector
{
Vector() : scalar(), count(0) {}
Vector(const Scalar &s, uint32_t c) : scalar(s), count(c) {}
Scalar scalar;
uint32_t count;
bool operator<(const Vector &o) const
{
if(scalar != o.scalar)
return scalar < o.scalar;
return count < o.count;
}
bool operator!=(const Vector &o) const { return !operator==(o); }
bool operator==(const Vector &o) const { return scalar == o.scalar && count == o.count; }
};
struct Matrix
{
Matrix() : vector(), count(0) {}
Matrix(const Vector &v, uint32_t c) : vector(v), count(c) {}
Vector vector;
uint32_t count;
bool operator<(const Matrix &o) const
{
if(vector != o.vector)
return vector < o.vector;
return count < o.count;
}
bool operator!=(const Matrix &o) const { return !operator==(o); }
bool operator==(const Matrix &o) const { return vector == o.vector && count == o.count; }
};
struct Pointer
{
Pointer() : baseId(), storage(StorageClass::Max) {}
Pointer(Id b, StorageClass s) : baseId(b), storage(s) {}
Id baseId;
StorageClass storage;
bool operator<(const Pointer &o) const
{
if(baseId != o.baseId)
return baseId < o.baseId;
return storage < o.storage;
}
bool operator!=(const Pointer &o) const { return !operator==(o); }
bool operator==(const Pointer &o) const { return baseId == o.baseId && storage == o.storage; }
};
struct Image
{
Image()
: retType(), dim(Dim::Max), depth(0), arrayed(0), ms(0), sampled(0), format(ImageFormat::Max)
{
}
Image(Scalar ret, Dim d, uint32_t dp, uint32_t ar, uint32_t m, uint32_t samp, ImageFormat f)
: retType(ret), dim(d), depth(dp), arrayed(ar), ms(m), sampled(samp), format(f)
{
}
Scalar retType;
Dim dim;
uint32_t depth;
uint32_t arrayed;
uint32_t ms;
uint32_t sampled;
ImageFormat format;
bool operator<(const Image &o) const
{
if(retType != o.retType)
return retType < o.retType;
if(dim != o.dim)
return dim < o.dim;
if(depth != o.depth)
return depth < o.depth;
if(arrayed != o.arrayed)
return arrayed < o.arrayed;
if(ms != o.ms)
return ms < o.ms;
if(sampled != o.sampled)
return sampled < o.sampled;
return format < o.format;
}
bool operator!=(const Image &o) const { return !operator==(o); }
bool operator==(const Image &o) const
{
return retType == o.retType && dim == o.dim && depth == o.depth && arrayed == o.arrayed &&
ms == o.ms && sampled == o.sampled && format == o.format;
}
};
struct Sampler
{
// no properties, all sampler types are equal
bool operator<(const Sampler &o) const { return false; }
bool operator!=(const Sampler &o) const { return false; }
bool operator==(const Sampler &o) const { return true; }
};
struct SampledImage
{
SampledImage() = default;
SampledImage(Id b) : baseId(b) {}
Id baseId;
bool operator<(const SampledImage &o) const { return baseId < o.baseId; }
bool operator!=(const SampledImage &o) const { return !operator==(o); }
bool operator==(const SampledImage &o) const { return baseId == o.baseId; }
};
struct FunctionType
{
FunctionType() = default;
FunctionType(Id ret, const rdcarray<Id> &args) : returnId(ret), argumentIds(args) {}
Id returnId;
rdcarray<Id> argumentIds;
bool operator<(const FunctionType &o) const
{
if(returnId != o.returnId)
return returnId < o.returnId;
return argumentIds < o.argumentIds;
}
bool operator!=(const FunctionType &o) const { return !operator==(o); }
bool operator==(const FunctionType &o) const
{
return returnId == o.returnId && argumentIds == o.argumentIds;
}
};
struct EntryPoint
{
EntryPoint() = default;
EntryPoint(ExecutionModel e, Id i, rdcstr n) : executionModel(e), id(i), name(n) {}
ExecutionModel executionModel;
Id id;
rdcstr name;
bool operator<(const EntryPoint &o) const
{
if(id != o.id)
return id < o.id;
return name < o.name;
}
bool operator!=(const EntryPoint &o) const { return !operator==(o); }
bool operator==(const EntryPoint &o) const { return id == o.id && name == o.name; }
};
struct Variable
{
Variable() = default;
Variable(Id t, Id i, StorageClass s) : type(t), id(i), storage(s) {}
Id type;
Id id;
StorageClass storage;
bool operator<(const Variable &o) const
{
if(id != o.id)
return id < o.id;
if(type != o.type)
return type < o.type;
return storage < o.storage;
}
bool operator!=(const Variable &o) const { return !operator==(o); }
bool operator==(const Variable &o) const
{
return id == o.id && type == o.type && storage == o.storage;
}
};
// hack around enum class being useless for array indices :(
struct Section
{
enum Type
{
Capabilities,
First = Capabilities,
Extensions,
ExtInst,
MemoryModel,
EntryPoints,
ExecutionMode,
Debug,
Annotations,
TypesVariablesConstants,
// handy aliases
Types = TypesVariablesConstants,
Variables = TypesVariablesConstants,
Constants = TypesVariablesConstants,
Functions,
Count,
};
};
class Processor
{
public:
Processor();
// accessors to structs/vectors of data
const std::vector<Id> &GetFunctions() { return functions; }
const std::vector<EntryPoint> &GetEntries() { return entries; }
const std::vector<Variable> &GetGlobals() { return globals; }
Id GetIDType(Id id) { return idTypes[id.value()]; }
protected:
virtual void Parse(const std::vector<uint32_t> &spirvWords);
std::vector<uint32_t> m_SPIRV;
// before parsing - e.g. to prepare any arrays that are max-id sized
virtual void PreParse(uint32_t maxId);
// even though we only need UnregisterOp when editing, we declare it here for ease of organisation
// so we can define pairs of logic for the same things. Rather than having mismatched code with
// the register of some map in one place and the unregister somewhere else that would be easy to
// break.
virtual void RegisterOp(Iter iter);
virtual void UnregisterOp(Iter iter);
// after parsing - e.g. to do any deferred post-processing
virtual void PostParse();
std::vector<size_t> idOffsets;
std::vector<Id> idTypes;
std::vector<Id> functions;
std::vector<EntryPoint> entries;
std::vector<Variable> globals;
std::set<rdcstr> extensions;
std::set<Capability> capabilities;
std::map<Id, Scalar> scalarTypes;
std::map<Id, Vector> vectorTypes;
std::map<Id, Matrix> matrixTypes;
std::map<Id, Pointer> pointerTypes;
std::map<Id, Image> imageTypes;
std::map<Id, Sampler> samplerTypes;
std::map<Id, SampledImage> sampledImageTypes;
std::map<Id, FunctionType> functionTypes;
std::map<rdcstr, Id> extSets;
struct LogicalSection
{
size_t startOffset = 0;
size_t endOffset = 0;
};
LogicalSection m_Sections[Section::Count];
};
}; // namespace rdcspv
@@ -59,15 +59,17 @@ void AddXFBAnnotations(const ShaderReflection &refl, const SPIRVPatchData &patch
{
rdcspv::Editor editor(modSpirv);
editor.Prepare();
rdcarray<SigParameter> outsig = refl.outputSignature;
std::vector<SPIRVPatchData::InterfaceAccess> outpatch = patchData.outputs;
rdcspv::Id entryid;
for(const rdcspv::OpEntryPoint &entry : editor.GetEntries())
for(const rdcspv::EntryPoint &entry : editor.GetEntries())
{
if(entry.name == entryName)
{
entryid = entry.entryPoint;
entryid = entry.id;
break;
}
}
@@ -41,6 +41,8 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName,
{
rdcspv::Editor editor(modSpirv);
editor.Prepare();
const bool useBufferAddress = (addr != 0);
rdcspv::Id uint32ID = editor.DeclareType(rdcspv::scalar<uint32_t>());
@@ -84,16 +86,16 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName,
// iterate over all variables. We do this here because in the absence of the buffer address
// extension we might declare our own below and patch bindings - so we need to look these up now
for(const rdcspv::OpVariable &var : editor.GetVariables())
for(const rdcspv::Variable &var : editor.GetGlobals())
{
// skip variables without one of these storage classes, as they are not descriptors
if(var.storageClass != rdcspv::StorageClass::UniformConstant &&
var.storageClass != rdcspv::StorageClass::Uniform &&
var.storageClass != rdcspv::StorageClass::StorageBuffer)
if(var.storage != rdcspv::StorageClass::UniformConstant &&
var.storage != rdcspv::StorageClass::Uniform &&
var.storage != rdcspv::StorageClass::StorageBuffer)
continue;
// get this variable's binding info
rdcspv::Binding bind = editor.GetBinding(var.result);
rdcspv::Binding bind = editor.GetBinding(var.id);
// if this is one of the bindings we care about
auto it = offsetMap.find(bind);
@@ -102,8 +104,7 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName,
// store the offset for this variable so we watch for access chains and know where to store to
if(useBufferAddress)
{
rdcspv::Id id = varLookup[var.result] =
editor.AddConstantImmediate<uint64_t>(it->second.offset);
rdcspv::Id id = varLookup[var.id] = editor.AddConstantImmediate<uint64_t>(it->second.offset);
editor.SetName(
id, StringFormat::Fmt("__feedbackOffset_set%u_bind%u", it->first.set, it->first.binding)
@@ -114,8 +115,7 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName,
// check that the offset fits in 32-bit word, convert byte offset to uint32 index
uint64_t index = it->second.offset / 4;
RDCASSERT(index < 0xFFFFFFFFULL, bind.set, bind.binding, it->second.offset);
rdcspv::Id id = varLookup[var.result] =
editor.AddConstantImmediate<uint32_t>(uint32_t(index));
rdcspv::Id id = varLookup[var.id] = editor.AddConstantImmediate<uint32_t>(uint32_t(index));
editor.SetName(
id, StringFormat::Fmt("__feedbackIndex_set%u_bind%u", it->first.set, it->first.binding)
@@ -209,16 +209,16 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName,
intTypeLookup[scalarType.second] = scalarType.first;
rdcspv::Id entryID;
for(const rdcspv::OpEntryPoint &entry : editor.GetEntries())
for(const rdcspv::EntryPoint &entry : editor.GetEntries())
{
if(entry.name == entryName)
{
entryID = entry.entryPoint;
entryID = entry.id;
break;
}
}
rdcspv::TypeIds<rdcspv::Function> funcTypes = editor.GetTypes<rdcspv::Function>();
rdcspv::TypeToIds<rdcspv::FunctionType> funcTypes = editor.GetTypes<rdcspv::FunctionType>();
// functions that have been patched with annotation & extra function parameters if needed
std::set<rdcspv::Id> patchedFunctions;
@@ -254,11 +254,11 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName,
rdcspv::OpFunction func(it);
// find the function's type declaration, add the necessary arguments, redeclare and patch it
for(const rdcspv::TypeId<rdcspv::Function> &funcType : funcTypes)
for(const rdcspv::TypeToId<rdcspv::FunctionType> &funcType : funcTypes)
{
if(funcType.second == func.functionType)
{
rdcspv::Function patchedFuncType = funcType.first;
rdcspv::FunctionType patchedFuncType = funcType.first;
for(size_t i = 0; i < patchArgIndices.size(); i++)
patchedFuncType.argumentIds.push_back(funcParamType);
+6 -4
View File
@@ -56,6 +56,8 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
{
rdcspv::Editor editor(modSpirv);
editor.Prepare();
uint32_t numInputs = (uint32_t)refl.inputSignature.size();
uint32_t numOutputs = (uint32_t)refl.outputSignature.size();
@@ -348,12 +350,12 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
std::set<rdcspv::Id> entries;
for(const rdcspv::OpEntryPoint &entry : editor.GetEntries())
for(const rdcspv::EntryPoint &entry : editor.GetEntries())
{
if(entry.name == entryName)
entryID = entry.entryPoint;
entryID = entry.id;
entries.insert(entry.entryPoint);
entries.insert(entry.id);
}
RDCASSERT(entryID);
@@ -740,7 +742,7 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
std::vector<rdcspv::Operation> ops;
rdcspv::Id voidType = editor.DeclareType(rdcspv::scalar<void>());
rdcspv::Id funcType = editor.DeclareType(rdcspv::Function(voidType, {}));
rdcspv::Id funcType = editor.DeclareType(rdcspv::FunctionType(voidType, {}));
ops.push_back(rdcspv::OpFunction(voidType, wrapperEntry, rdcspv::FunctionControl::None, funcType));