mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-12-16 04:09:07 +00:00
rsx: Add UTs for register annotation pass and fix uncovered bugs
This commit is contained in:
parent
88a54e2d98
commit
aaaa6feb5a
@ -223,21 +223,48 @@ namespace rsx::assembler
|
||||
auto encode_opcode = [](const std::string& op, Instruction* inst)
|
||||
{
|
||||
OPDEST d0 { .HEX = inst->bytecode[0] };
|
||||
SRC0 s0 { .HEX = inst->bytecode[1] };
|
||||
|
||||
#define SET_OPCODE(code) \
|
||||
do { \
|
||||
inst->opcode = d0.opcode = code; \
|
||||
s0.exec_if_eq = s0.exec_if_gr = s0.exec_if_lt = 1; \
|
||||
inst->bytecode[0] = d0.HEX; \
|
||||
inst->bytecode[1] = s0.HEX; \
|
||||
} while (0)
|
||||
|
||||
if (op == "MOV")
|
||||
{
|
||||
inst->opcode = d0.opcode = RSX_FP_OPCODE_MOV;
|
||||
inst->bytecode[0] = d0.HEX;
|
||||
SET_OPCODE(RSX_FP_OPCODE_MOV);
|
||||
return;
|
||||
}
|
||||
|
||||
if (op == "ADD")
|
||||
{
|
||||
inst->opcode = d0.opcode = RSX_FP_OPCODE_ADD;
|
||||
inst->bytecode[0] = d0.HEX;
|
||||
SET_OPCODE(RSX_FP_OPCODE_ADD);
|
||||
return;
|
||||
}
|
||||
|
||||
if (op == "MAD" || op == "FMA")
|
||||
{
|
||||
SET_OPCODE(RSX_FP_OPCODE_MAD);
|
||||
return;
|
||||
}
|
||||
|
||||
if (op == "UP4S")
|
||||
{
|
||||
SET_OPCODE(RSX_FP_OPCODE_UP4);
|
||||
return;
|
||||
}
|
||||
|
||||
if (op == "PK4S")
|
||||
{
|
||||
SET_OPCODE(RSX_FP_OPCODE_PK4);
|
||||
return;
|
||||
}
|
||||
|
||||
#undef SET_OPCODE
|
||||
|
||||
fmt::throw_exception("Unhandled instruction '%s'", op);
|
||||
};
|
||||
|
||||
@ -262,6 +289,7 @@ namespace rsx::assembler
|
||||
Instruction* target = &ir.m_instructions.back();
|
||||
|
||||
encode_opcode(op, target);
|
||||
ensure(sources.size() == FP::get_operand_count(static_cast<FP_opcode>(target->opcode)), "Invalid operand count for opcode");
|
||||
|
||||
if (dst.empty())
|
||||
{
|
||||
|
||||
@ -15,6 +15,7 @@ namespace rsx::assembler::FP
|
||||
case RSX_FP_OPCODE_NOP:
|
||||
return 0;
|
||||
case RSX_FP_OPCODE_MOV:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_MUL:
|
||||
case RSX_FP_OPCODE_ADD:
|
||||
return 2;
|
||||
@ -290,8 +291,8 @@ namespace rsx::assembler::FP
|
||||
u32 get_src_vector_lane_mask_shuffled(const RSXFragmentProgram& prog, const Instruction* instruction, u32 operand)
|
||||
{
|
||||
// Brute-force this. There's only 16 permutations.
|
||||
constexpr u32 x = 0b0000;
|
||||
constexpr u32 y = 0b0000;
|
||||
constexpr u32 x = 0b0001;
|
||||
constexpr u32 y = 0b0010;
|
||||
constexpr u32 z = 0b0100;
|
||||
constexpr u32 w = 0b1000;
|
||||
|
||||
|
||||
@ -10,6 +10,16 @@ namespace rsx::assembler
|
||||
{
|
||||
int id = 0;
|
||||
bool f16 = false;
|
||||
|
||||
bool operator == (const Register& other) const
|
||||
{
|
||||
return id == other.id && f16 == other.f16;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return std::string(f16 ? "H" : "R") + std::to_string(id);
|
||||
}
|
||||
};
|
||||
|
||||
struct RegisterRef
|
||||
@ -34,6 +44,11 @@ namespace rsx::assembler
|
||||
{
|
||||
return !!mask;
|
||||
}
|
||||
|
||||
bool operator == (const RegisterRef& other) const
|
||||
{
|
||||
return reg == other.reg && mask == other.mask;
|
||||
}
|
||||
};
|
||||
|
||||
struct Instruction
|
||||
|
||||
@ -9,7 +9,7 @@ namespace rsx::assembler::FP
|
||||
{
|
||||
static constexpr u32 register_file_length = 48 * 8; // 24 F32 or 48 F16 registers
|
||||
static constexpr char content_unknown = 0;
|
||||
static constexpr char content_float32 = 'F';
|
||||
static constexpr char content_float32 = 'R';
|
||||
static constexpr char content_float16 = 'H';
|
||||
static constexpr char content_dual = 'D';
|
||||
|
||||
@ -125,7 +125,7 @@ namespace rsx::assembler::FP
|
||||
for (const auto& src : instruction.srcs)
|
||||
{
|
||||
const auto read_bytes = get_register_file_range(src);
|
||||
const char expected_type = src.reg.f16 ? content_float16 : content_float16;
|
||||
const char expected_type = src.reg.f16 ? content_float16 : content_float32;
|
||||
for (const auto& index : read_bytes)
|
||||
{
|
||||
if (output_register_file[index] != content_unknown)
|
||||
@ -156,7 +156,7 @@ namespace rsx::assembler::FP
|
||||
{
|
||||
const auto& dst = instruction.dsts.front();
|
||||
const auto write_bytes = get_register_file_range(dst);
|
||||
const char expected_type = dst.reg.f16 ? content_float16 : content_float16;
|
||||
const char expected_type = dst.reg.f16 ? content_float16 : content_float32;
|
||||
|
||||
for (const auto& index : write_bytes)
|
||||
{
|
||||
|
||||
@ -2,15 +2,56 @@
|
||||
|
||||
#include "Emu/RSX/Common/simple_array.hpp"
|
||||
#include "Emu/RSX/Program/Assembler/FPASM.h"
|
||||
#include "Emu/RSX/Program/Assembler/Passes/FP/RegisterAnnotationPass.h"
|
||||
#include "Emu/RSX/Program/RSXFragmentProgram.h"
|
||||
|
||||
namespace rsx::assembler
|
||||
{
|
||||
#define DECLARE_REG32(num)\
|
||||
Register R##num{ .id = num, .f16 = false }
|
||||
|
||||
#define DECLARE_REG16(num)\
|
||||
Register H##num{ .id = num, .f16 = true }
|
||||
|
||||
DECLARE_REG32(0);
|
||||
DECLARE_REG32(1);
|
||||
DECLARE_REG32(2);
|
||||
DECLARE_REG32(3);
|
||||
DECLARE_REG32(4);
|
||||
DECLARE_REG32(5);
|
||||
DECLARE_REG32(6);
|
||||
DECLARE_REG32(7);
|
||||
DECLARE_REG32(8);
|
||||
|
||||
DECLARE_REG16(0);
|
||||
DECLARE_REG16(1);
|
||||
DECLARE_REG16(2);
|
||||
DECLARE_REG16(3);
|
||||
DECLARE_REG16(4);
|
||||
DECLARE_REG16(5);
|
||||
DECLARE_REG16(6);
|
||||
DECLARE_REG16(7);
|
||||
DECLARE_REG16(8);
|
||||
|
||||
#undef DECLARE_REG32
|
||||
#undef DECLARE_REG16
|
||||
|
||||
static FlowGraph CFG_from_source(const std::string& asm_)
|
||||
{
|
||||
auto ir = FPIR::from_source(asm_);
|
||||
|
||||
FlowGraph graph{};
|
||||
graph.blocks.push_back({});
|
||||
|
||||
auto& bb = graph.blocks.back();
|
||||
bb.instructions = ir.build();
|
||||
return graph;
|
||||
}
|
||||
TEST(TestFPIR, FromSource)
|
||||
{
|
||||
auto ir = FPIR::from_source(R"(
|
||||
MOV R0, #{ 0.125 };
|
||||
ADD R1, R0;
|
||||
ADD R1, R0, R0;
|
||||
)");
|
||||
|
||||
const auto instructions = ir.build();
|
||||
@ -30,4 +71,32 @@ namespace rsx::assembler
|
||||
EXPECT_EQ(SRC0{ .HEX = instructions[1].bytecode[1] }.reg_type, RSX_FP_REGISTER_TYPE_TEMP);
|
||||
EXPECT_EQ(instructions[1].length, 4);
|
||||
}
|
||||
|
||||
TEST(TestFPIR, RegisterAnnotationPass)
|
||||
{
|
||||
// Code snippet reads from R0 and R2, clobbers R0, R1, H0
|
||||
auto graph = CFG_from_source(R"(
|
||||
ADD R1, R0, R1;
|
||||
MOV H0, H4;
|
||||
)");
|
||||
|
||||
ASSERT_EQ(graph.blocks.size(), 1);
|
||||
ASSERT_EQ(graph.blocks.front().instructions.size(), 2);
|
||||
|
||||
auto& block = graph.blocks.front();
|
||||
RSXFragmentProgram prog{};
|
||||
FP::RegisterAnnotationPass annotation_pass(prog);
|
||||
|
||||
annotation_pass.run(graph);
|
||||
|
||||
ASSERT_EQ(block.clobber_list.size(), 2);
|
||||
ASSERT_EQ(block.input_list.size(), 3);
|
||||
|
||||
EXPECT_EQ(block.clobber_list[0].reg, H0);
|
||||
EXPECT_EQ(block.clobber_list[1].reg, R1);
|
||||
|
||||
EXPECT_EQ(block.input_list[0].reg, H4);
|
||||
EXPECT_EQ(block.input_list[1].reg, R0);
|
||||
EXPECT_EQ(block.input_list[2].reg, R1);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user