diff --git a/rpcs3/Emu/RSX/Program/Assembler/FPASM.cpp b/rpcs3/Emu/RSX/Program/Assembler/FPASM.cpp index f8fcb3c571..d1ef30b695 100644 --- a/rpcs3/Emu/RSX/Program/Assembler/FPASM.cpp +++ b/rpcs3/Emu/RSX/Program/Assembler/FPASM.cpp @@ -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(target->opcode)), "Invalid operand count for opcode"); if (dst.empty()) { diff --git a/rpcs3/Emu/RSX/Program/Assembler/FPOpcodes.cpp b/rpcs3/Emu/RSX/Program/Assembler/FPOpcodes.cpp index 401bfd5492..890653f569 100644 --- a/rpcs3/Emu/RSX/Program/Assembler/FPOpcodes.cpp +++ b/rpcs3/Emu/RSX/Program/Assembler/FPOpcodes.cpp @@ -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; diff --git a/rpcs3/Emu/RSX/Program/Assembler/IR.h b/rpcs3/Emu/RSX/Program/Assembler/IR.h index 0fbd1f98ae..fff3d5aa83 100644 --- a/rpcs3/Emu/RSX/Program/Assembler/IR.h +++ b/rpcs3/Emu/RSX/Program/Assembler/IR.h @@ -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 diff --git a/rpcs3/Emu/RSX/Program/Assembler/Passes/FP/RegisterAnnotationPass.cpp b/rpcs3/Emu/RSX/Program/Assembler/Passes/FP/RegisterAnnotationPass.cpp index 196bc19361..ea0e9216fb 100644 --- a/rpcs3/Emu/RSX/Program/Assembler/Passes/FP/RegisterAnnotationPass.cpp +++ b/rpcs3/Emu/RSX/Program/Assembler/Passes/FP/RegisterAnnotationPass.cpp @@ -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) { diff --git a/rpcs3/tests/test_rsx_fp_asm.cpp b/rpcs3/tests/test_rsx_fp_asm.cpp index 1fe899e925..9ae23fa81c 100644 --- a/rpcs3/tests/test_rsx_fp_asm.cpp +++ b/rpcs3/tests/test_rsx_fp_asm.cpp @@ -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); + } }