mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
refactor(shader): drop unused folded constants and unreachable blocks after specialization
SpecializeSpirv hoists every instruction it folds into a module-level constant and keeps that constant after its last use has folded away too. It also leaves each block that a folded branch cuts off as OpLabel; OpUnreachable. A formatted buffer helper's 77-case format switch leaves about 1,800 such blocks. Emit only the folded constants that something still refers to, and remove OpLabel; OpUnreachable blocks that no instruction names. Merge and continue targets of live constructs are named, so they stay. The specialized modules lose words, not behaviour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e7c70a2df9
commit
3ea89674d2
2 files changed
+98
No files matched your search
@@ -223,7 +223,59 @@ void testStructExtractsAreNotShuffled() {
|
||||
}
|
||||
}
|
||||
|
||||
void testFoldingLeavesNoDeadBlocksOrConstants() {
|
||||
const auto op = [](std::uint32_t count, spv::Op code) { return (count << 16u) | static_cast<std::uint32_t>(code); };
|
||||
const std::vector<std::uint32_t> words{
|
||||
spv::MagicNumber, 0x00010300u, 0u, 30u, 0u,
|
||||
op(2, spv::OpCapability), spv::CapabilityShader,
|
||||
op(3, spv::OpMemoryModel), spv::AddressingModelLogical, spv::MemoryModelGLSL450,
|
||||
op(6, spv::OpEntryPoint), spv::ExecutionModelGLCompute, 11u, 0x6e69616du, 0u, 10u,
|
||||
op(6, spv::OpExecutionMode), 11u, spv::ExecutionModeLocalSize, 1u, 1u, 1u,
|
||||
op(2, spv::OpTypeVoid), 1u,
|
||||
op(4, spv::OpTypeInt), 2u, 32u, 0u,
|
||||
op(4, spv::OpTypePointer), 4u, spv::StorageClassPrivate, 2u,
|
||||
op(3, spv::OpTypeFunction), 5u, 1u,
|
||||
op(4, spv::OpConstant), 2u, 6u, 2u,
|
||||
op(4, spv::OpConstant), 2u, 7u, 10u,
|
||||
op(4, spv::OpConstant), 2u, 8u, 20u,
|
||||
op(4, spv::OpVariable), 4u, 10u, spv::StorageClassPrivate,
|
||||
op(5, spv::OpFunction), 1u, 11u, 0u, 5u,
|
||||
op(2, spv::OpLabel), 12u,
|
||||
op(5, spv::OpIAdd), 2u, 13u, 6u, 7u,
|
||||
op(5, spv::OpIMul), 2u, 14u, 13u, 7u,
|
||||
op(3, spv::OpSelectionMerge), 20u, spv::SelectionControlMaskNone,
|
||||
op(7, spv::OpSwitch), 6u, 15u, 1u, 16u, 2u, 17u,
|
||||
op(2, spv::OpLabel), 15u,
|
||||
op(3, spv::OpStore), 10u, 7u,
|
||||
op(2, spv::OpBranch), 20u,
|
||||
op(2, spv::OpLabel), 16u,
|
||||
op(3, spv::OpStore), 10u, 8u,
|
||||
op(2, spv::OpBranch), 20u,
|
||||
op(2, spv::OpLabel), 17u,
|
||||
op(3, spv::OpStore), 10u, 14u,
|
||||
op(2, spv::OpBranch), 20u,
|
||||
op(2, spv::OpLabel), 20u,
|
||||
op(1, spv::OpReturn),
|
||||
op(1, spv::OpFunctionEnd),
|
||||
};
|
||||
const auto specialized = ShaderRecompiler::SpecializeSpirv(words);
|
||||
const auto counts = opcodeCounts(specialized);
|
||||
std::set<std::uint32_t> constants;
|
||||
bool storesFolded = false;
|
||||
for (std::size_t cursor = 5; cursor < specialized.size(); cursor += specialized[cursor] >> 16u) {
|
||||
if ((specialized[cursor] >> 16u) == 0u) break;
|
||||
const auto opcode = specialized[cursor] & 0xffffu;
|
||||
if (opcode == spv::OpConstant) constants.insert(specialized[cursor + 2u]);
|
||||
if (opcode == spv::OpStore && specialized[cursor + 2u] == 14u) storesFolded = true;
|
||||
}
|
||||
check(!counts.contains(spv::OpUnreachable), "blocks the folded switch left unreachable were kept");
|
||||
check(counts.at(spv::OpLabel) == 3u, "the specialized function does not have exactly its three live blocks");
|
||||
check(storesFolded && constants.contains(14u), "the folded product the live case stores is missing");
|
||||
check(!constants.contains(13u), "a folded constant without uses was kept");
|
||||
}
|
||||
|
||||
int main() {
|
||||
testFoldingLeavesNoDeadBlocksOrConstants();
|
||||
testStructExtractsAreNotShuffled();
|
||||
testRepeatedTypesShareOneId();
|
||||
testOperandShapesDoNotCollide();
|
||||
|
||||
@@ -257,6 +257,8 @@ public:
|
||||
changed |= propagateCopies();
|
||||
}
|
||||
removeDeadComputations();
|
||||
removeUnreachableBlocks();
|
||||
removeUnusedConstants();
|
||||
orderPhis();
|
||||
std::vector<std::uint32_t> words = header;
|
||||
bool inserted = false;
|
||||
@@ -505,6 +507,50 @@ private:
|
||||
return changed;
|
||||
}
|
||||
|
||||
std::set<std::uint32_t> referencedIds() const {
|
||||
std::set<std::uint32_t> referenced;
|
||||
for (const auto& instruction : instructions) {
|
||||
if (instruction.empty()) continue;
|
||||
const auto op = Opcode(instruction);
|
||||
if (op == spv::OpName || op == spv::OpMemberName || op == spv::OpDecorate || op == spv::OpMemberDecorate || op == spv::OpLabel) continue;
|
||||
for (std::size_t index = 1; index < instruction.size(); ++index) referenced.insert(instruction[index]);
|
||||
}
|
||||
return referenced;
|
||||
}
|
||||
|
||||
void removeUnreachableBlocks() {
|
||||
const auto referenced = referencedIds();
|
||||
const auto next = [&](std::size_t index) {
|
||||
while (++index < instructions.size() && instructions[index].empty()) {}
|
||||
return index;
|
||||
};
|
||||
std::size_t previous = instructions.size();
|
||||
for (std::size_t index = 0; index < instructions.size(); ++index) {
|
||||
if (instructions[index].empty()) continue;
|
||||
const auto op = Opcode(instructions[index]);
|
||||
const auto following = next(index);
|
||||
const bool entry = previous < instructions.size() && (Opcode(instructions[previous]) == spv::OpFunction || Opcode(instructions[previous]) == spv::OpFunctionParameter);
|
||||
if (op == spv::OpLabel && !entry && following < instructions.size() && Opcode(instructions[following]) == spv::OpUnreachable && !referenced.contains(instructions[index].at(1))) {
|
||||
removed.insert(instructions[index].at(1));
|
||||
instructions[index].clear();
|
||||
instructions[following].clear();
|
||||
index = following;
|
||||
continue;
|
||||
}
|
||||
previous = index;
|
||||
}
|
||||
}
|
||||
|
||||
void removeUnusedConstants() {
|
||||
const auto referenced = referencedIds();
|
||||
std::erase_if(constants, [&](const Instruction& constant) {
|
||||
const auto id = Result(constant);
|
||||
if (referenced.contains(id)) return false;
|
||||
removed.insert(id);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void orderPhis() {
|
||||
std::vector<Instruction> ordered;
|
||||
std::vector<Instruction> copies;
|
||||
|
||||
Reference in new issue
Block a user