mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-12-16 04:09:07 +00:00
rsx/cfg: Handle nested IF/LOOP blocks falling out to unsuitable nodes (ELSE).
- In that case, find the node's END node and link to that instead. ELSE nodes are not reachable from children of the preceding IF. - ELSE nodes are special this way, all other types of nodes are reachable by adjacency.
This commit is contained in:
parent
26fd0510ab
commit
d23ea4760b
@ -74,8 +74,19 @@ namespace rsx::assembler
|
|||||||
{
|
{
|
||||||
if (auto found = find_block_for_pc(id))
|
if (auto found = find_block_for_pc(id))
|
||||||
{
|
{
|
||||||
parent->insert_succ(found, edge_type);
|
auto succ = found;
|
||||||
found->insert_pred(parent, edge_type);
|
if (found->is_of_type(EdgeType::ELSE) &&
|
||||||
|
(edge_type == EdgeType::ENDIF || edge_type == EdgeType::ENDLOOP))
|
||||||
|
{
|
||||||
|
// If we landed on an "ELSE" node, link to its "ENDIF" counterpart
|
||||||
|
auto if_parent = found->pred.front().from;
|
||||||
|
auto endif_edge = std::find_if(if_parent->succ.begin(), if_parent->succ.end(), FN(x.type == EdgeType::ENDIF));
|
||||||
|
ensure(endif_edge != if_parent->succ.end(), "CFG: Invalid ELSE node");
|
||||||
|
succ = endif_edge->to;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent->insert_succ(succ, edge_type);
|
||||||
|
succ->insert_pred(parent, edge_type);
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +120,7 @@ namespace rsx::assembler
|
|||||||
case EdgeType::IF:
|
case EdgeType::IF:
|
||||||
case EdgeType::ELSE:
|
case EdgeType::ELSE:
|
||||||
{
|
{
|
||||||
// Find the merge node from the parent
|
// Find the merge node from the parent.
|
||||||
auto succ = std::find_if(parent->succ.begin(), parent->succ.end(), FN(x.type == EdgeType::ENDIF));
|
auto succ = std::find_if(parent->succ.begin(), parent->succ.end(), FN(x.type == EdgeType::ENDIF));
|
||||||
ensure(succ != parent->succ.end(), "CFG: Broken IF linkage. Please report to developers.");
|
ensure(succ != parent->succ.end(), "CFG: Broken IF linkage. Please report to developers.");
|
||||||
bb->insert_succ(succ->to, EdgeType::ENDIF);
|
bb->insert_succ(succ->to, EdgeType::ENDIF);
|
||||||
|
|||||||
@ -91,6 +91,7 @@ namespace rsx::assembler
|
|||||||
struct BasicBlock
|
struct BasicBlock
|
||||||
{
|
{
|
||||||
u32 id = 0;
|
u32 id = 0;
|
||||||
|
|
||||||
std::vector<Instruction> instructions; // Program instructions for the RSX processor
|
std::vector<Instruction> instructions; // Program instructions for the RSX processor
|
||||||
std::vector<FlowEdge> succ; // Forward edges. Sorted closest first.
|
std::vector<FlowEdge> succ; // Forward edges. Sorted closest first.
|
||||||
std::vector<FlowEdge> pred; // Back edges. Sorted closest first.
|
std::vector<FlowEdge> pred; // Back edges. Sorted closest first.
|
||||||
|
|||||||
@ -404,4 +404,72 @@ namespace rsx::assembler
|
|||||||
EXPECT_EQ(SRC0{ .HEX = bb1->epilogue[1].bytecode[1] }.swizzle_x, 2);
|
EXPECT_EQ(SRC0{ .HEX = bb1->epilogue[1].bytecode[1] }.swizzle_x, 2);
|
||||||
EXPECT_EQ(SRC0{ .HEX = bb1->epilogue[1].bytecode[1] }.swizzle_y, 3);
|
EXPECT_EQ(SRC0{ .HEX = bb1->epilogue[1].bytecode[1] }.swizzle_y, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(TestFPIR, RegisterDependencyPass_Complex_IF_ELSE_Simpsons)
|
||||||
|
{
|
||||||
|
// Complex IF-ELSE nest observed in Simpson's game. Rewritten for simplicity.
|
||||||
|
// There is no tail block. No epilogues should be injected in this scenario since H4 (the trigger) is defined on all branches.
|
||||||
|
// R2 is indeed clobbered but the outer ELSE branch should not be able to see the inner IF-ELSE blocks as predecessors.
|
||||||
|
auto ir = FPIR::from_source(R"(
|
||||||
|
MOV R2, #{ 0.25 };
|
||||||
|
IF.GT;
|
||||||
|
SLT R4, H2, #{ 0.125 };
|
||||||
|
IF.GT;
|
||||||
|
ADD H2, H0, H3;
|
||||||
|
FMA H4, R2, H2, H3;
|
||||||
|
ELSE;
|
||||||
|
MOV H2, #{ 0.125 };
|
||||||
|
ADD H0, H0, H2;
|
||||||
|
FMA H4, R2, H2, H3;
|
||||||
|
ENDIF;
|
||||||
|
ELSE;
|
||||||
|
FMA H4, R2, H2, H3;
|
||||||
|
MOV H0, H4;
|
||||||
|
ENDIF;
|
||||||
|
)");
|
||||||
|
|
||||||
|
auto bytecode = ir.compile();
|
||||||
|
|
||||||
|
RSXFragmentProgram prog{};
|
||||||
|
prog.data = bytecode.data();
|
||||||
|
auto graph = deconstruct_fragment_program(prog);
|
||||||
|
|
||||||
|
ASSERT_EQ(graph.blocks.size(), 6);
|
||||||
|
|
||||||
|
FP::RegisterAnnotationPass annotation_pass{ prog };
|
||||||
|
FP::RegisterDependencyPass deps_pass{};
|
||||||
|
|
||||||
|
annotation_pass.run(graph);
|
||||||
|
deps_pass.run(graph);
|
||||||
|
|
||||||
|
const BasicBlock
|
||||||
|
*bb0 = get_graph_block(graph, 0),
|
||||||
|
*bb1 = get_graph_block(graph, 1),
|
||||||
|
*bb2 = get_graph_block(graph, 2),
|
||||||
|
*bb3 = get_graph_block(graph, 3),
|
||||||
|
*bb4 = get_graph_block(graph, 4),
|
||||||
|
*bb5 = get_graph_block(graph, 5);
|
||||||
|
|
||||||
|
// Sanity
|
||||||
|
EXPECT_EQ(bb0->instructions.size(), 2);
|
||||||
|
EXPECT_EQ(bb1->instructions.size(), 2);
|
||||||
|
EXPECT_EQ(bb2->instructions.size(), 2);
|
||||||
|
EXPECT_EQ(bb3->instructions.size(), 3);
|
||||||
|
EXPECT_EQ(bb4->instructions.size(), 2);
|
||||||
|
EXPECT_EQ(bb5->instructions.size(), 0); // Phi/Merge only.
|
||||||
|
|
||||||
|
// Nested children must recursively fall out to the closest ENDIF
|
||||||
|
ASSERT_EQ(bb4->pred.size(), 1);
|
||||||
|
EXPECT_EQ(bb4->pred.front().type, EdgeType::ELSE);
|
||||||
|
EXPECT_EQ(bb5->pred.size(), 4); // 2 IF and 2 ELSE paths exist
|
||||||
|
|
||||||
|
// Check that we get no epilogues
|
||||||
|
EXPECT_EQ(bb0->epilogue.size(), 0);
|
||||||
|
EXPECT_EQ(bb1->epilogue.size(), 0);
|
||||||
|
EXPECT_EQ(bb2->epilogue.size(), 0);
|
||||||
|
EXPECT_EQ(bb3->epilogue.size(), 0);
|
||||||
|
EXPECT_EQ(bb4->epilogue.size(), 0);
|
||||||
|
EXPECT_EQ(bb5->epilogue.size(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user