mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-04-06 06:55:02 +00:00
231 lines
6.4 KiB
C++
231 lines
6.4 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include "Emu/RSX/Common/simple_array.hpp"
|
|
#include "Emu/RSX/Program/Assembler/CFG.h"
|
|
#include "Emu/RSX/Program/Assembler/FPASM.h"
|
|
#include "Emu/RSX/Program/RSXFragmentProgram.h"
|
|
|
|
#include <util/v128.hpp>
|
|
|
|
namespace rsx::assembler
|
|
{
|
|
static const BasicBlock* get_graph_block_by_id(const FlowGraph& graph, u32 id)
|
|
{
|
|
auto found = std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == id));
|
|
return &(*found);
|
|
}
|
|
TEST(CFG, FpToCFG_IF)
|
|
{
|
|
auto ir = FPIR::from_source(R"(
|
|
ADD R0, R0, R0;
|
|
MOV R1, R0;
|
|
IF.LT;
|
|
ADD R1, R1, R0;
|
|
ENDIF;
|
|
MOV R0, R1;
|
|
)");
|
|
|
|
const std::pair<int, size_t> expected_block_data[3] = {
|
|
{ 0, 3 }, // Head
|
|
{ 3, 1 }, // Branch
|
|
{ 4, 1 }, // Merge
|
|
};
|
|
|
|
RSXFragmentProgram program{};
|
|
auto bytecode = ir.compile();
|
|
program.data = bytecode.data();
|
|
|
|
FlowGraph graph = deconstruct_fragment_program(program);
|
|
|
|
ASSERT_EQ(graph.blocks.size(), 3);
|
|
|
|
int i = 0;
|
|
for (auto it = graph.blocks.begin(); it != graph.blocks.end(); ++it)
|
|
{
|
|
const auto& expected = expected_block_data[i++];
|
|
EXPECT_EQ(it->id, expected.first);
|
|
EXPECT_EQ(it->instructions.size(), expected.second);
|
|
}
|
|
|
|
// Check edges
|
|
EXPECT_EQ(get_graph_block_by_id(graph, 3)->pred[0].type, EdgeType::IF);
|
|
EXPECT_EQ(get_graph_block_by_id(graph, 0)->succ[0].type, EdgeType::IF);
|
|
EXPECT_EQ(get_graph_block_by_id(graph, 4)->pred[0].type, EdgeType::ENDIF);
|
|
}
|
|
|
|
TEST(CFG, FpToCFG_NestedIF)
|
|
{
|
|
auto ir = FPIR::from_source(
|
|
"ADD R0, R0, R0;" // 0
|
|
"MOV R1, R0;" // 1
|
|
"IF.LT;" // 2 (BR, 8)
|
|
" ADD R1, R1, R0;" // 3
|
|
" IF.GT;" // 4 (BR, 6)
|
|
" MOV R3, R0;" // 5
|
|
" ENDIF;"
|
|
" MOV R2, R3;" // 6 (merge block 1)
|
|
" ADD R1, R2, R1;" // 7
|
|
"ENDIF;"
|
|
"MOV R0, R1;" // 8 (merge block 2
|
|
);
|
|
|
|
const std::pair<int, size_t> expected_block_data[5] = {
|
|
{ 0, 3 }, // Head
|
|
{ 3, 2 }, // Branch 1
|
|
{ 5, 1 }, // Branch 2
|
|
{ 6, 2 }, // Merge 1
|
|
{ 8, 1 }, // Merge 2
|
|
};
|
|
|
|
RSXFragmentProgram program{};
|
|
auto bytecode = ir.compile();
|
|
program.data = bytecode.data();
|
|
|
|
FlowGraph graph = deconstruct_fragment_program(program);
|
|
|
|
ASSERT_EQ(graph.blocks.size(), 5);
|
|
|
|
int i = 0;
|
|
for (auto it = graph.blocks.begin(); it != graph.blocks.end(); ++it)
|
|
{
|
|
const auto& expected = expected_block_data[i++];
|
|
EXPECT_EQ(it->id, expected.first);
|
|
EXPECT_EQ(it->instructions.size(), expected.second);
|
|
}
|
|
}
|
|
|
|
TEST(CFG, FpToCFG_NestedIF_MultiplePred)
|
|
{
|
|
auto ir = FPIR::from_source(
|
|
"ADD R0, R0, R0;" // 0
|
|
"MOV R1, R0;" // 1
|
|
"IF.LT;" // 2 (BR, 6)
|
|
" ADD R1, R1, R0;" // 3
|
|
" IF.GT;" // 4 (BR, 6)
|
|
" MOV R3, R0;" // 5
|
|
" ENDIF;" // ENDIF (4)
|
|
"ENDIF;" // ENDIF (2)
|
|
"MOV R2, R3;" // 6 (merge block, unified)
|
|
"ADD R1, R2, R1;" // 7
|
|
"MOV R0, R1;" // 8
|
|
);
|
|
|
|
const std::pair<int, size_t> expected_block_data[4] = {
|
|
{ 0, 3 }, // Head
|
|
{ 3, 2 }, // Branch 1
|
|
{ 5, 1 }, // Branch 2
|
|
{ 6, 3 }, // Merge
|
|
};
|
|
|
|
RSXFragmentProgram program{};
|
|
auto bytecode = ir.compile();
|
|
program.data = bytecode.data();
|
|
|
|
FlowGraph graph = deconstruct_fragment_program(program);
|
|
|
|
ASSERT_EQ(graph.blocks.size(), 4);
|
|
|
|
int i = 0;
|
|
for (auto it = graph.blocks.begin(); it != graph.blocks.end(); ++it)
|
|
{
|
|
const auto& expected = expected_block_data[i++];
|
|
EXPECT_EQ(it->id, expected.first);
|
|
EXPECT_EQ(it->instructions.size(), expected.second);
|
|
}
|
|
|
|
const BasicBlock
|
|
*bb0 = get_graph_block_by_id(graph, 0),
|
|
*bb6 = get_graph_block_by_id(graph, 6);
|
|
|
|
// Predecessors must be ordered, closest first
|
|
ASSERT_EQ(bb6->pred.size(), 3);
|
|
EXPECT_EQ(bb6->pred[0].type, EdgeType::ENDIF);
|
|
EXPECT_EQ(bb6->pred[0].from->id, 5);
|
|
EXPECT_EQ(bb6->pred[1].type, EdgeType::ENDIF);
|
|
EXPECT_EQ(bb6->pred[1].from->id, 3);
|
|
EXPECT_EQ(bb6->pred[2].type, EdgeType::ENDIF);
|
|
EXPECT_EQ(bb6->pred[2].from->id, 0);
|
|
|
|
// Successors must also be ordered, closest first
|
|
ASSERT_EQ(bb0->succ.size(), 2);
|
|
EXPECT_EQ(bb0->succ[0].type, EdgeType::IF);
|
|
EXPECT_EQ(bb0->succ[0].to->id, 3);
|
|
EXPECT_EQ(bb0->succ[1].type, EdgeType::ENDIF);
|
|
EXPECT_EQ(bb0->succ[1].to->id, 6);
|
|
}
|
|
|
|
TEST(CFG, FpToCFG_IF_ELSE)
|
|
{
|
|
auto ir = FPIR::from_source(
|
|
"ADD R0, R0, R0;" // 0
|
|
"MOV R1, R0;" // 1
|
|
"IF.LT;" // 2 (BR, 6)
|
|
" ADD R1, R1, R0;" // 3
|
|
"ELSE;" // ELSE (2)
|
|
" MOV R2, R3;" // 4
|
|
" ADD R1, R2, R1;" // 5
|
|
"ENDIF;" // ENDIF (2)
|
|
"MOV R0, R1;" // 6 (merge)
|
|
);
|
|
|
|
const std::pair<int, size_t> expected_block_data[4] = {
|
|
{ 0, 3 }, // Head
|
|
{ 3, 1 }, // Branch positive
|
|
{ 4, 2 }, // Branch negative
|
|
{ 6, 1 }, // Merge
|
|
};
|
|
|
|
RSXFragmentProgram program{};
|
|
auto bytecode = ir.compile();
|
|
program.data = bytecode.data();
|
|
|
|
FlowGraph graph = deconstruct_fragment_program(program);
|
|
|
|
ASSERT_EQ(graph.blocks.size(), 4);
|
|
|
|
int i = 0;
|
|
for (auto it = graph.blocks.begin(); it != graph.blocks.end(); ++it)
|
|
{
|
|
const auto& expected = expected_block_data[i++];
|
|
EXPECT_EQ(it->id, expected.first);
|
|
EXPECT_EQ(it->instructions.size(), expected.second);
|
|
}
|
|
|
|
// The IF and ELSE branches don't link to each other directly. Their predecessor should point to both and they both point to the merge.
|
|
const BasicBlock
|
|
*bb0 = get_graph_block_by_id(graph, 0),
|
|
*bb3 = get_graph_block_by_id(graph, 3),
|
|
*bb4 = get_graph_block_by_id(graph, 4),
|
|
*bb6 = get_graph_block_by_id(graph, 6);
|
|
|
|
EXPECT_EQ(bb0->succ.size(), 3);
|
|
EXPECT_EQ(bb3->succ.size(), 1);
|
|
EXPECT_EQ(bb4->succ.size(), 1);
|
|
|
|
EXPECT_EQ(bb3->succ.front().to, bb6);
|
|
EXPECT_EQ(bb4->succ.front().to, bb6);
|
|
|
|
EXPECT_EQ(bb6->pred.size(), 3);
|
|
EXPECT_EQ(bb6->pred[0].from, bb4);
|
|
EXPECT_EQ(bb6->pred[1].from, bb3);
|
|
EXPECT_EQ(bb6->pred[2].from, bb0);
|
|
}
|
|
|
|
TEST(CFG, FpToCFG_EmptyIF)
|
|
{
|
|
auto ir = FPIR::from_source(
|
|
"IF.LT;" // Empty branch
|
|
"ENDIF;"
|
|
"MOV R0, R1;" // False merge block.
|
|
);
|
|
|
|
RSXFragmentProgram program{};
|
|
auto bytecode = ir.compile();
|
|
program.data = bytecode.data();
|
|
|
|
FlowGraph graph = deconstruct_fragment_program(program);
|
|
|
|
ASSERT_EQ(graph.blocks.size(), 1);
|
|
EXPECT_EQ(graph.blocks.front().instructions.size(), 1);
|
|
}
|
|
}
|