mirror of
https://github.com/doitsujin/dxvk.git
synced 2024-12-02 19:24:12 +01:00
[spirv] Implement basic dead code elimination
Fixes invalid SPIR-V in Trails through Daybreak.
This commit is contained in:
parent
6308266a0f
commit
813b653645
@ -50,6 +50,18 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
void SpirvCodeBuffer::append(const SpirvInstruction& ins) {
|
||||
const size_t size = m_code.size();
|
||||
|
||||
m_code.resize(size + ins.length());
|
||||
|
||||
for (uint32_t i = 0; i < ins.length(); i++)
|
||||
m_code[size + i] = ins.arg(i);
|
||||
|
||||
m_ptr += ins.length();
|
||||
}
|
||||
|
||||
|
||||
void SpirvCodeBuffer::append(const SpirvCodeBuffer& other) {
|
||||
if (other.size() != 0) {
|
||||
const size_t size = m_code.size();
|
||||
|
@ -89,6 +89,14 @@ namespace dxvk {
|
||||
*/
|
||||
uint32_t allocId();
|
||||
|
||||
/**
|
||||
* \brief Appends an instruction
|
||||
*
|
||||
* Slightly faster than individually adding words.
|
||||
* \param [in] ins Instruction
|
||||
*/
|
||||
void append(const SpirvInstruction& ins);
|
||||
|
||||
/**
|
||||
* \brief Merges two code buffers
|
||||
*
|
||||
|
@ -15,7 +15,7 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
SpirvCodeBuffer SpirvModule::compile() const {
|
||||
SpirvCodeBuffer SpirvModule::compile() {
|
||||
SpirvCodeBuffer result;
|
||||
result.putHeader(m_version, m_id);
|
||||
result.append(m_capabilities);
|
||||
@ -28,7 +28,35 @@ namespace dxvk {
|
||||
result.append(m_annotations);
|
||||
result.append(m_typeConstDefs);
|
||||
result.append(m_variables);
|
||||
result.append(m_code);
|
||||
|
||||
// Perform some crude dead code elimination. In some cases, our compilers
|
||||
// may emit invalid code, such as an unreachable block branching to a loop's
|
||||
// continue block, but those cases cannot be reasonably detected up-front.
|
||||
std::unordered_set<uint32_t> reachableBlocks;
|
||||
std::unordered_set<uint32_t> mergeBlocks;
|
||||
|
||||
classifyBlocks(reachableBlocks, mergeBlocks);
|
||||
|
||||
bool reachable = true;
|
||||
|
||||
for (auto ins : m_code) {
|
||||
if (ins.opCode() == spv::OpFunctionEnd) {
|
||||
reachable = true;
|
||||
result.append(ins);
|
||||
} else if (ins.opCode() == spv::OpLabel) {
|
||||
uint32_t labelId = ins.arg(1);
|
||||
|
||||
if ((reachable = reachableBlocks.find(labelId) != reachableBlocks.end())) {
|
||||
result.append(ins);
|
||||
} else if (mergeBlocks.find(labelId) != mergeBlocks.end()) {
|
||||
result.append(ins);
|
||||
result.putIns(spv::OpUnreachable, 1);
|
||||
}
|
||||
} else if (reachable) {
|
||||
result.append(ins);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -3905,4 +3933,69 @@ namespace dxvk {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SpirvModule::classifyBlocks(
|
||||
std::unordered_set<uint32_t>& reachableBlocks,
|
||||
std::unordered_set<uint32_t>& mergeBlocks) {
|
||||
std::unordered_multimap<uint32_t, uint32_t> branches;
|
||||
std::queue<uint32_t> blockQueue;
|
||||
|
||||
uint32_t blockId = 0;
|
||||
|
||||
for (auto ins : m_code) {
|
||||
switch (ins.opCode()) {
|
||||
case spv::OpLabel: {
|
||||
uint32_t id = ins.arg(1);
|
||||
|
||||
if (!blockId)
|
||||
branches.insert({ 0u, id });
|
||||
|
||||
blockId = id;
|
||||
} break;
|
||||
|
||||
case spv::OpFunction: {
|
||||
blockId = 0u;
|
||||
} break;
|
||||
|
||||
case spv::OpBranch: {
|
||||
branches.insert({ blockId, ins.arg(1) });
|
||||
} break;
|
||||
|
||||
case spv::OpBranchConditional: {
|
||||
branches.insert({ blockId, ins.arg(2) });
|
||||
branches.insert({ blockId, ins.arg(3) });
|
||||
} break;
|
||||
|
||||
case spv::OpSwitch: {
|
||||
branches.insert({ blockId, ins.arg(2) });
|
||||
|
||||
for (uint32_t i = 4; i < ins.length(); i += 2)
|
||||
branches.insert({ blockId, ins.arg(i) });
|
||||
} break;
|
||||
|
||||
case spv::OpSelectionMerge:
|
||||
case spv::OpLoopMerge: {
|
||||
mergeBlocks.insert(ins.arg(1));
|
||||
} break;
|
||||
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
blockQueue.push(0);
|
||||
|
||||
while (!blockQueue.empty()) {
|
||||
uint32_t id = blockQueue.front();
|
||||
|
||||
auto range = branches.equal_range(id);
|
||||
|
||||
for (auto i = range.first; i != range.second; i++) {
|
||||
if (reachableBlocks.insert(i->second).second)
|
||||
blockQueue.push(i->second);
|
||||
}
|
||||
|
||||
blockQueue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "spirv_code_buffer.h"
|
||||
@ -59,7 +61,7 @@ namespace dxvk {
|
||||
|
||||
~SpirvModule();
|
||||
|
||||
SpirvCodeBuffer compile() const;
|
||||
SpirvCodeBuffer compile();
|
||||
|
||||
size_t getInsertionPtr() {
|
||||
return m_code.getInsertionPtr();
|
||||
@ -1326,6 +1328,10 @@ namespace dxvk {
|
||||
bool isInterfaceVar(
|
||||
spv::StorageClass sclass) const;
|
||||
|
||||
void classifyBlocks(
|
||||
std::unordered_set<uint32_t>& reachableBlocks,
|
||||
std::unordered_set<uint32_t>& mergeBlocks);
|
||||
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user