mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-25 20:36:54 +01:00
shader: validate shared terminal paths in selections
This commit is contained in:
@@ -1454,24 +1454,19 @@ std::vector<uint32_t> SelectionRegion(const Graph& graph, const BasicBlock& head
|
|||||||
std::vector<uint32_t> region;
|
std::vector<uint32_t> region;
|
||||||
std::vector<uint32_t> pending = {header.terminator.true_block, header.terminator.false_block};
|
std::vector<uint32_t> pending = {header.terminator.true_block, header.terminator.false_block};
|
||||||
const auto* loop = FindInnermostContainingLoop(graph, header.id);
|
const auto* loop = FindInnermostContainingLoop(graph, header.id);
|
||||||
const auto global_merge = graph.FindNearestCommonPostDominator(header.terminator.true_block,
|
|
||||||
header.terminator.false_block);
|
|
||||||
while (!pending.empty()) {
|
while (!pending.empty()) {
|
||||||
const auto block_id = pending.back();
|
const auto block_id = pending.back();
|
||||||
pending.pop_back();
|
pending.pop_back();
|
||||||
if (block_id == merge || Contains(region, block_id) ||
|
if (block_id == merge || Contains(region, block_id) ||
|
||||||
(loop != nullptr && (block_id == loop->merge || block_id == loop->continue_block)) ||
|
(loop != nullptr && (block_id == loop->merge || block_id == loop->continue_block))) {
|
||||||
IsEnclosingLinearExit(graph, header.id, block_id) ||
|
|
||||||
(block_id == global_merge && HasLinearPathToTerminal(graph, block_id))) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto* block = graph.FindBlock(block_id);
|
const auto* block = graph.FindBlock(block_id);
|
||||||
if (block == nullptr) {
|
if (block == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (block->successors.empty()) {
|
// A return terminates its own block; a branch to a shared return still has to
|
||||||
continue;
|
// obey selection entry/exit rules, just like any other branch.
|
||||||
}
|
|
||||||
AddUnique(region, block_id);
|
AddUnique(region, block_id);
|
||||||
pending.insert(pending.end(), block->successors.begin(), block->successors.end());
|
pending.insert(pending.end(), block->successors.begin(), block->successors.end());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8328,6 +8328,38 @@ void TestNewShaderRecompilerCfgSharedReturnAfterNestedSelections() {
|
|||||||
CheckSpirvBinaryValidates(result.spirv);
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestNewShaderRecompilerCfgAlternatingSharedReturns() {
|
||||||
|
// PS 0x845315ec188c33db: nested conditions alternate between two shared
|
||||||
|
// terminal epilogues. Each branch must leave through its own selection merge.
|
||||||
|
const uint32_t shader[] = {
|
||||||
|
EncodeSopc(0x06, 0, 128),
|
||||||
|
EncodeSopp(0x04, 8), // outer -> second return or middle condition
|
||||||
|
EncodeSopc(0x06, 1, 128),
|
||||||
|
EncodeSopp(0x04, 2), // middle -> first return or inner condition
|
||||||
|
EncodeSopc(0x06, 2, 128),
|
||||||
|
EncodeSopp(0x04, 4), // inner -> second return or first return
|
||||||
|
EncodeVop1(0x01, 0, 129),
|
||||||
|
EncodeExp0(0x00, 0x1), EncodeExp1(0, 0, 0, 0),
|
||||||
|
EncodeSopp(0x01),
|
||||||
|
EncodeVop1(0x01, 0, 130),
|
||||||
|
EncodeExp0(0x00, 0x1), EncodeExp1(0, 0, 0, 0),
|
||||||
|
EncodeSopp(0x01),
|
||||||
|
};
|
||||||
|
ShaderRecompiler::Decoder::Program decoded;
|
||||||
|
ShaderRecompiler::Decoder::DecodeProgram(std::span{shader}, decoded);
|
||||||
|
auto graph = ShaderRecompiler::CFG::BuildGraph(decoded);
|
||||||
|
const auto coverage = CfgInstructionCoverage(graph, decoded.instructions.size());
|
||||||
|
Check(graph.blocks.size() == 5u, "alternating returns fixture has the wrong CFG");
|
||||||
|
Check(ShaderRecompiler::CFG::Structurize(graph), graph.unsupported_reason.c_str());
|
||||||
|
Check(CfgInstructionCoverage(graph, decoded.instructions.size()) == coverage,
|
||||||
|
"alternating returns duplicated a terminal epilogue");
|
||||||
|
|
||||||
|
auto result = RecompileForTest(shader, MakeCompileOptions(ShaderType::Pixel));
|
||||||
|
Check(!result.program.dispatcher_fallback,
|
||||||
|
"alternating returns did not use structured control flow");
|
||||||
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
|
}
|
||||||
|
|
||||||
void TestNewShaderRecompilerCfgLoopSharedRegion() {
|
void TestNewShaderRecompilerCfgLoopSharedRegion() {
|
||||||
const uint32_t shader[] = {
|
const uint32_t shader[] = {
|
||||||
EncodeSopc(0x06, 0, 0), // loop condition
|
EncodeSopc(0x06, 0, 0), // loop condition
|
||||||
@@ -12791,6 +12823,7 @@ int main() {
|
|||||||
TestSharedReturnPreservesDescriptorDominance();
|
TestSharedReturnPreservesDescriptorDominance();
|
||||||
TestNewShaderRecompilerCfgNestedTailEarlyExit();
|
TestNewShaderRecompilerCfgNestedTailEarlyExit();
|
||||||
TestNewShaderRecompilerCfgSharedReturnAfterNestedSelections();
|
TestNewShaderRecompilerCfgSharedReturnAfterNestedSelections();
|
||||||
|
TestNewShaderRecompilerCfgAlternatingSharedReturns();
|
||||||
TestNewShaderRecompilerCfgLoopSharedRegion();
|
TestNewShaderRecompilerCfgLoopSharedRegion();
|
||||||
TestNewShaderRecompilerCfgSharedRegionBeforeEarlyBreakLoop();
|
TestNewShaderRecompilerCfgSharedRegionBeforeEarlyBreakLoop();
|
||||||
TestNewShaderRecompilerCfgOverlappingEarlyExitLadder();
|
TestNewShaderRecompilerCfgOverlappingEarlyExitLadder();
|
||||||
|
|||||||
Reference in New Issue
Block a user