mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 13:56:59 +01:00
shader: route nested shared regions
This commit is contained in:
@@ -1520,10 +1520,7 @@ bool RouteOneSharedArm(Graph& graph, uint32_t original_block_count, uint32_t out
|
|||||||
uint32_t route_variable, GotoRouteBlocks& route) {
|
uint32_t route_variable, GotoRouteBlocks& route) {
|
||||||
auto* outer = graph.FindBlock(outer_id);
|
auto* outer = graph.FindBlock(outer_id);
|
||||||
if (outer == nullptr || outer->inst_begin == outer->inst_end ||
|
if (outer == nullptr || outer->inst_begin == outer->inst_end ||
|
||||||
outer->terminator.kind != TerminatorKind::ConditionalBranch ||
|
outer->terminator.kind != TerminatorKind::ConditionalBranch) {
|
||||||
std::ranges::any_of(graph.natural_loops, [outer_id](const auto& loop) {
|
|
||||||
return Contains(loop.body_blocks, outer_id);
|
|
||||||
})) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1553,21 +1550,7 @@ bool RouteOneSharedArm(Graph& graph, uint32_t original_block_count, uint32_t out
|
|||||||
if (first_arm >= original_block_count || outer_id >= inner_id || inner_id >= first_arm) {
|
if (first_arm >= original_block_count || outer_id >= inner_id || inner_id >= first_arm) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto* shared_block = graph.FindBlock(shared);
|
if (graph.FindNearestCommonPostDominator(shared, other) == UINT32_MAX) {
|
||||||
const auto* other_block = graph.FindBlock(other);
|
|
||||||
if (shared_block == nullptr || other_block == nullptr ||
|
|
||||||
shared_block->terminator.kind != TerminatorKind::Branch ||
|
|
||||||
other_block->terminator.kind != TerminatorKind::Branch ||
|
|
||||||
shared_block->successors.size() != 1u ||
|
|
||||||
shared_block->successors != other_block->successors) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const auto in_loop = [&](uint32_t block_id) {
|
|
||||||
return std::ranges::any_of(graph.natural_loops, [&](const auto& loop) {
|
|
||||||
return Contains(loop.body_blocks, block_id);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (in_loop(inner_id) || in_loop(shared) || in_loop(other)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1592,32 +1575,32 @@ bool RouteOneSharedArm(Graph& graph, uint32_t original_block_count, uint32_t out
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RouteTerminalSharedSelectionArm(Graph& graph) {
|
bool RouteSharedSelectionArms(Graph& graph) {
|
||||||
if (graph.irreducible) {
|
if (graph.irreducible) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto original_block_count = static_cast<uint32_t>(graph.blocks.size());
|
const auto route_budget = static_cast<uint32_t>(graph.blocks.size());
|
||||||
GotoRouteBlocks route;
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (uint32_t block_id = 0; block_id < original_block_count; block_id++) {
|
for (uint32_t route_variable = 0; route_variable < route_budget; route_variable++) {
|
||||||
GotoRouteBlocks next;
|
const auto block_count = static_cast<uint32_t>(graph.blocks.size());
|
||||||
if (RouteOneSharedArm(graph, original_block_count, block_id, 0u, next)) {
|
bool routed = false;
|
||||||
if (found) {
|
for (uint32_t block_id = 0; block_id < block_count; block_id++) {
|
||||||
// Multiple interacting overlaps remain on the dispatcher path.
|
GotoRouteBlocks route;
|
||||||
return false;
|
if (!RouteOneSharedArm(graph, block_count, block_id, route_variable, route)) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
found = true;
|
PlaceGotoRouteBlocks(graph, block_count, route);
|
||||||
route = next;
|
|
||||||
RebuildPredecessors(graph);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
PlaceGotoRouteBlocks(graph, original_block_count, route);
|
|
||||||
RebuildPredecessors(graph);
|
RebuildPredecessors(graph);
|
||||||
RecomputeAnalyses(graph);
|
RecomputeAnalyses(graph);
|
||||||
return true;
|
found = true;
|
||||||
|
routed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!routed) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -1939,7 +1922,7 @@ bool Structurize(Graph& graph, std::string* error) {
|
|||||||
if (StructurizeImpl(graph, error)) {
|
if (StructurizeImpl(graph, error)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!RouteTerminalSharedSelectionArm(original)) {
|
if (!RouteSharedSelectionArms(original)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Graph failed_graph = std::move(graph);
|
Graph failed_graph = std::move(graph);
|
||||||
|
|||||||
@@ -7109,6 +7109,74 @@ void TestNewShaderRecompilerCfgExecSccSharedArm() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestNewShaderRecompilerCfgLoopSharedRegion() {
|
||||||
|
const uint32_t shader[] = {
|
||||||
|
EncodeSopc(0x06, 0, 0), // loop condition
|
||||||
|
EncodeSopp(0x04, 14), // loop exit -> end
|
||||||
|
EncodeSopc(0x06, 1, 1), // outer condition
|
||||||
|
EncodeSopp(0x04, 2), // outer -> shared region or inner
|
||||||
|
EncodeSopc(0x06, 2, 2), // inner condition
|
||||||
|
EncodeSopp(0x04, 6), // inner -> common tail or shared region
|
||||||
|
EncodeSopc(0x06, 3, 3), // shared region condition
|
||||||
|
EncodeSopp(0x04, 2), // shared region -> right or left
|
||||||
|
EncodeSMovB32(4, 129), // shared left work
|
||||||
|
EncodeSopp(0x02, 2), // shared left -> common tail
|
||||||
|
EncodeSMovB32(5, 129), // shared right work
|
||||||
|
EncodeSopp(0x02, 0), // shared right -> common tail
|
||||||
|
EncodeSop2(0x00, 6, 6, 129), // common tail work
|
||||||
|
EncodeSopp(0x02, 0), // common tail -> continue
|
||||||
|
EncodeSop2(0x00, 7, 7, 129), // continue work
|
||||||
|
EncodeSopp(0x02, 0xfff0u), // backedge -> loop header
|
||||||
|
0xbf810000u,
|
||||||
|
};
|
||||||
|
|
||||||
|
ShaderRecompiler::Decoder::Program decoded;
|
||||||
|
std::string error;
|
||||||
|
Check(ShaderRecompiler::Decoder::DecodeProgram(std::span{shader}, decoded,
|
||||||
|
&error),
|
||||||
|
error.c_str());
|
||||||
|
ShaderRecompiler::CFG::Graph graph;
|
||||||
|
Check(ShaderRecompiler::CFG::BuildGraph(decoded, graph, &error),
|
||||||
|
error.c_str());
|
||||||
|
const auto original_block_count = graph.blocks.size();
|
||||||
|
const auto original_coverage =
|
||||||
|
CfgInstructionCoverage(graph, decoded.instructions.size());
|
||||||
|
Check(graph.natural_loops.size() == 1u,
|
||||||
|
"loop shared-region fixture has the wrong native CFG");
|
||||||
|
const bool structured = ShaderRecompiler::CFG::Structurize(graph, &error);
|
||||||
|
Check(structured, error.c_str());
|
||||||
|
const auto *loop_header = graph.FindBlockByPc(0x00u);
|
||||||
|
Check(graph.natural_loops.size() == 1u && graph.back_edges.size() == 1u &&
|
||||||
|
loop_header != nullptr && loop_header->terminator.loop_header &&
|
||||||
|
loop_header->terminator.continue_block != UINT32_MAX,
|
||||||
|
"loop shared-region routing did not preserve the natural loop");
|
||||||
|
|
||||||
|
uint32_t route_selects = 0;
|
||||||
|
uint32_t route_sets = 0;
|
||||||
|
for (const auto &block : graph.blocks) {
|
||||||
|
route_selects += block.terminator.condition ==
|
||||||
|
ShaderRecompiler::CFG::BranchCondition::GotoVariable;
|
||||||
|
route_sets += block.terminator.goto_value >= 0;
|
||||||
|
}
|
||||||
|
Check(route_selects == 1u && route_sets == 3u &&
|
||||||
|
graph.blocks.size() >= original_block_count + 5u &&
|
||||||
|
CfgInstructionCoverage(graph, decoded.instructions.size()) ==
|
||||||
|
original_coverage,
|
||||||
|
"loop shared region was not routed without semantic duplication");
|
||||||
|
|
||||||
|
auto options = MakeCompileOptions(ShaderType::Compute);
|
||||||
|
options.dump_ir = true;
|
||||||
|
ShaderRecompiler::CompileResult result;
|
||||||
|
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error),
|
||||||
|
error.c_str());
|
||||||
|
Check(!result.program.dispatcher_fallback &&
|
||||||
|
Common::ContainsStr(result.ir_dump, "mode=structured") &&
|
||||||
|
SpirvInstructionOpcodeCount(result.spirv, 246) == 1u &&
|
||||||
|
SpirvInstructionOpcodeCount(result.spirv, 251) == 0u,
|
||||||
|
"loop shared region unexpectedly selected dispatcher fallback");
|
||||||
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
|
}
|
||||||
|
|
||||||
void TestNewShaderRecompilerCfgOverlappingEarlyExitLadder() {
|
void TestNewShaderRecompilerCfgOverlappingEarlyExitLadder() {
|
||||||
const uint32_t shader[] = {
|
const uint32_t shader[] = {
|
||||||
EncodeSopc(0x06, 0, 0), // block 0
|
EncodeSopc(0x06, 0, 0), // block 0
|
||||||
@@ -9409,6 +9477,7 @@ int main() {
|
|||||||
TestNewShaderRecompilerCfgDuplicateMergeStructuredSplit();
|
TestNewShaderRecompilerCfgDuplicateMergeStructuredSplit();
|
||||||
TestNewShaderRecompilerCfgNestedEarlyExitLoopForwarders();
|
TestNewShaderRecompilerCfgNestedEarlyExitLoopForwarders();
|
||||||
TestNewShaderRecompilerCfgExecSccSharedArm();
|
TestNewShaderRecompilerCfgExecSccSharedArm();
|
||||||
|
TestNewShaderRecompilerCfgLoopSharedRegion();
|
||||||
TestNewShaderRecompilerCfgOverlappingEarlyExitLadder();
|
TestNewShaderRecompilerCfgOverlappingEarlyExitLadder();
|
||||||
TestNewShaderRecompilerCfgExternallyEnteredSelectionDispatcher();
|
TestNewShaderRecompilerCfgExternallyEnteredSelectionDispatcher();
|
||||||
TestNewShaderRecompilerCfgIrreducibleDispatcher();
|
TestNewShaderRecompilerCfgIrreducibleDispatcher();
|
||||||
|
|||||||
Reference in New Issue
Block a user