mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-12-16 04:09:07 +00:00
rsx/cfg/gtest: Rewrite CFG tests using the assembler
- Also extend IF-ELSE test to catch a broken succession chain
This commit is contained in:
parent
b244c0fa0f
commit
91e19652de
@ -2,89 +2,28 @@
|
||||
|
||||
#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
|
||||
{
|
||||
auto swap_bytes16 = [](u32 dword) -> u32
|
||||
static const BasicBlock* get_graph_block_by_id(const FlowGraph& graph, u32 id)
|
||||
{
|
||||
// Lazy encode, but good enough for what we need here.
|
||||
union v32
|
||||
{
|
||||
u32 HEX;
|
||||
u8 _v[4];
|
||||
};
|
||||
|
||||
u8* src_bytes = reinterpret_cast<u8*>(&dword);
|
||||
v32 dst_bytes;
|
||||
|
||||
dst_bytes._v[0] = src_bytes[1];
|
||||
dst_bytes._v[1] = src_bytes[0];
|
||||
dst_bytes._v[2] = src_bytes[3];
|
||||
dst_bytes._v[3] = src_bytes[2];
|
||||
|
||||
return dst_bytes.HEX;
|
||||
};
|
||||
|
||||
// Instruction mocks because we don't have a working assember (yet)
|
||||
auto encode_instruction = [](u32 opcode, bool end = false) -> v128
|
||||
{
|
||||
OPDEST dst{};
|
||||
dst.opcode = opcode;
|
||||
|
||||
if (end)
|
||||
{
|
||||
dst.end = 1;
|
||||
auto found = std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == id));
|
||||
return &(*found);
|
||||
}
|
||||
|
||||
return v128::from32(swap_bytes16(dst.HEX), 0, 0, 0);
|
||||
};
|
||||
|
||||
auto create_if(u32 end, u32 _else = 0)
|
||||
{
|
||||
OPDEST dst{};
|
||||
dst.opcode = RSX_FP_OPCODE_IFE & 0x3Fu;
|
||||
|
||||
SRC1 src1{};
|
||||
src1.else_offset = (_else ? _else : end) << 2;
|
||||
src1.opcode_is_branch = 1;
|
||||
|
||||
SRC2 src2{};
|
||||
src2.end_offset = end << 2;
|
||||
|
||||
return v128::from32(swap_bytes16(dst.HEX), 0, swap_bytes16(src1.HEX), swap_bytes16(src2.HEX));
|
||||
};
|
||||
|
||||
TEST(CFG, FpToCFG_Basic)
|
||||
{
|
||||
rsx::simple_array<v128> buffer = {
|
||||
encode_instruction(RSX_FP_OPCODE_ADD),
|
||||
encode_instruction(RSX_FP_OPCODE_MOV, true)
|
||||
};
|
||||
|
||||
RSXFragmentProgram program{};
|
||||
program.data = buffer.data();
|
||||
|
||||
FlowGraph graph = deconstruct_fragment_program(program);
|
||||
|
||||
EXPECT_EQ(graph.blocks.size(), 1);
|
||||
EXPECT_EQ(graph.blocks.front().instructions.size(), 2);
|
||||
EXPECT_EQ(graph.blocks.front().instructions.front().length, 4);
|
||||
EXPECT_EQ(graph.blocks.front().instructions[0].addr, 0);
|
||||
EXPECT_EQ(graph.blocks.front().instructions[1].addr, 16);
|
||||
}
|
||||
|
||||
TEST(CFG, FpToCFG_IF)
|
||||
{
|
||||
rsx::simple_array<v128> buffer = {
|
||||
encode_instruction(RSX_FP_OPCODE_ADD), // 0
|
||||
encode_instruction(RSX_FP_OPCODE_MOV), // 1
|
||||
create_if(4), // 2 (BR, 4)
|
||||
encode_instruction(RSX_FP_OPCODE_ADD), // 3
|
||||
encode_instruction(RSX_FP_OPCODE_MOV, true), // 4 (Merge block)
|
||||
};
|
||||
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
|
||||
@ -93,7 +32,8 @@ namespace rsx::assembler
|
||||
};
|
||||
|
||||
RSXFragmentProgram program{};
|
||||
program.data = buffer.data();
|
||||
auto bytecode = ir.compile();
|
||||
program.data = bytecode.data();
|
||||
|
||||
FlowGraph graph = deconstruct_fragment_program(program);
|
||||
|
||||
@ -108,24 +48,26 @@ namespace rsx::assembler
|
||||
}
|
||||
|
||||
// Check edges
|
||||
EXPECT_EQ(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 3))->pred[0].type, EdgeType::IF);
|
||||
EXPECT_EQ(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 0))->succ[0].type, EdgeType::IF);
|
||||
EXPECT_EQ(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 4))->pred[0].type, EdgeType::ENDIF);
|
||||
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)
|
||||
{
|
||||
rsx::simple_array<v128> buffer = {
|
||||
encode_instruction(RSX_FP_OPCODE_ADD), // 0
|
||||
encode_instruction(RSX_FP_OPCODE_MOV), // 1
|
||||
create_if(8), // 2 (BR, 8)
|
||||
encode_instruction(RSX_FP_OPCODE_ADD), // 3
|
||||
create_if(6), // 4 (BR, 6)
|
||||
encode_instruction(RSX_FP_OPCODE_MOV), // 5
|
||||
encode_instruction(RSX_FP_OPCODE_MOV), // 6 (merge block 1)
|
||||
encode_instruction(RSX_FP_OPCODE_ADD), // 7
|
||||
encode_instruction(RSX_FP_OPCODE_MOV, true) // 8 (merge block 2
|
||||
};
|
||||
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
|
||||
@ -136,7 +78,8 @@ namespace rsx::assembler
|
||||
};
|
||||
|
||||
RSXFragmentProgram program{};
|
||||
program.data = buffer.data();
|
||||
auto bytecode = ir.compile();
|
||||
program.data = bytecode.data();
|
||||
|
||||
FlowGraph graph = deconstruct_fragment_program(program);
|
||||
|
||||
@ -153,17 +96,19 @@ namespace rsx::assembler
|
||||
|
||||
TEST(CFG, FpToCFG_NestedIF_MultiplePred)
|
||||
{
|
||||
rsx::simple_array<v128> buffer = {
|
||||
encode_instruction(RSX_FP_OPCODE_ADD), // 0
|
||||
encode_instruction(RSX_FP_OPCODE_MOV), // 1
|
||||
create_if(6), // 2 (BR, 6)
|
||||
encode_instruction(RSX_FP_OPCODE_ADD), // 3
|
||||
create_if(6), // 4 (BR, 6)
|
||||
encode_instruction(RSX_FP_OPCODE_MOV), // 5
|
||||
encode_instruction(RSX_FP_OPCODE_MOV), // 6 (merge block)
|
||||
encode_instruction(RSX_FP_OPCODE_ADD), // 7
|
||||
encode_instruction(RSX_FP_OPCODE_MOV, true) // 8
|
||||
};
|
||||
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
|
||||
@ -173,7 +118,8 @@ namespace rsx::assembler
|
||||
};
|
||||
|
||||
RSXFragmentProgram program{};
|
||||
program.data = buffer.data();
|
||||
auto bytecode = ir.compile();
|
||||
program.data = bytecode.data();
|
||||
|
||||
FlowGraph graph = deconstruct_fragment_program(program);
|
||||
|
||||
@ -187,32 +133,40 @@ namespace rsx::assembler
|
||||
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(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 6))->pred.size(), 2);
|
||||
EXPECT_EQ(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 6))->pred[0].type, EdgeType::ENDIF);
|
||||
EXPECT_EQ(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 6))->pred[0].from->id, 3);
|
||||
EXPECT_EQ(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 6))->pred[1].type, EdgeType::ENDIF);
|
||||
EXPECT_EQ(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 6))->pred[1].from->id, 0);
|
||||
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(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 0))->succ.size(), 2);
|
||||
EXPECT_EQ(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 0))->succ[0].type, EdgeType::IF);
|
||||
EXPECT_EQ(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 0))->succ[0].to->id, 3);
|
||||
EXPECT_EQ(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 0))->succ[1].type, EdgeType::ENDIF);
|
||||
EXPECT_EQ(std::find_if(graph.blocks.begin(), graph.blocks.end(), FN(x.id == 0))->succ[1].to->id, 6);
|
||||
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)
|
||||
{
|
||||
rsx::simple_array<v128> buffer = {
|
||||
encode_instruction(RSX_FP_OPCODE_ADD), // 0
|
||||
encode_instruction(RSX_FP_OPCODE_MOV), // 1
|
||||
create_if(6, 4), // 2 (BR, 6)
|
||||
encode_instruction(RSX_FP_OPCODE_ADD), // 3
|
||||
encode_instruction(RSX_FP_OPCODE_MOV), // 4 (Else)
|
||||
encode_instruction(RSX_FP_OPCODE_ADD), // 5
|
||||
encode_instruction(RSX_FP_OPCODE_MOV, true), // 6 (Merge)
|
||||
};
|
||||
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
|
||||
@ -222,7 +176,8 @@ namespace rsx::assembler
|
||||
};
|
||||
|
||||
RSXFragmentProgram program{};
|
||||
program.data = buffer.data();
|
||||
auto bytecode = ir.compile();
|
||||
program.data = bytecode.data();
|
||||
|
||||
FlowGraph graph = deconstruct_fragment_program(program);
|
||||
|
||||
@ -235,5 +190,24 @@ namespace rsx::assembler
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user