rsx/cfg: Add support for multi-slot instructions with literals

- Also fix pred edge direction bug
This commit is contained in:
kd-11 2025-11-23 21:11:05 +03:00 committed by kd-11
parent 42d9065c11
commit 9d92e190eb
3 changed files with 52 additions and 10 deletions

View file

@ -1,17 +1,14 @@
#include "stdafx.h"
#pragma optimize("", off)
#include "CFG.h"
#include "Emu/RSX/Common/simple_array.hpp"
#include "Emu/RSX/Program/RSXFragmentProgram.h"
#include "Emu/RSX/Program/ProgramStateCache.h"
#include <util/asm.hpp>
#include <util/v128.hpp>
#include <span>
using namespace program_hash_util;
namespace rsx::assembler
{
@ -75,6 +72,13 @@ namespace rsx::assembler
return graph.push(parent, id, edge_type);
};
auto includes_literal_constant = [&]()
{
return src0.reg_type == RSX_FP_REGISTER_TYPE_CONSTANT ||
src1.reg_type == RSX_FP_REGISTER_TYPE_CONSTANT ||
src2.reg_type == RSX_FP_REGISTER_TYPE_CONSTANT;
};
while (!end)
{
BasicBlock** found = end_blocks.find_if(FN(x->id == pc));
@ -110,13 +114,21 @@ namespace rsx::assembler
bb->instructions.push_back({});
auto& ir_inst = bb->instructions.back();
std::memcpy(ir_inst.bytecode, &decoded._u32[0], 16);
ir_inst.length = 4;
ir_inst.addr = pc * 16;
switch (opcode)
{
case RSX_FP_OPCODE_BRK:
break;
case RSX_FP_OPCODE_CAL:
// Unimplemented. Also unused by the RSX compiler
fmt::throw_exception("Unimplemented FP CAL instruction.");
break;
case RSX_FP_OPCODE_FENCT:
break;
case RSX_FP_OPCODE_FENCB:
break;
case RSX_FP_OPCODE_RET:
// Outside a subroutine, this doesn't mean much. The main block can conditionally return to stop execution early.
// This will not alter flow control.
@ -143,8 +155,13 @@ namespace rsx::assembler
break;
}
default:
if (fragment_program_utils::is_any_src_constant(decoded))
if (includes_literal_constant())
{
const v128 constant_literal = v128::loadu(data, pc);
v128 decoded_literal = decode_instruction(constant_literal);
std::memcpy(ir_inst.bytecode + 4, &decoded_literal._u32[0], 16);
ir_inst.length += 4;
pc++;
}
}
@ -152,6 +169,14 @@ namespace rsx::assembler
pc++;
}
// Sort edges for each block by distance
for (auto& block : graph.blocks)
{
std::sort(block.pred.begin(), block.pred.end(), FN(x.from->id > y.from->id));
std::sort(block.succ.begin(), block.succ.end(), FN(x.to->id < y.to->id));
}
// Sort block nodes by distance
graph.blocks.sort(FN(x.id < y.id));
return graph;
}

View file

@ -33,11 +33,20 @@ namespace rsx::assembler
struct Instruction
{
// Raw data. Every instruction is max 128 bits
u32 bytecode[4];
// Raw data. Every instruction is max 128 bits.
// Each instruction can also have 128 bits of literal/embedded data.
u32 bytecode[8]{ {} };
u32 addr = 0;
// Decoded
u32 opcode = 0;
u8 length = 4; // Length in dwords
// Padding
u8 reserved0 = 0;
u16 reserved1 = 0;
// References
std::vector<RegisterRef> srcs;
std::vector<RegisterRef> dsts;
};
@ -49,7 +58,7 @@ namespace rsx::assembler
ELSE,
ENDIF,
LOOP,
ENDLOOP
ENDLOOP,
};
struct FlowEdge
@ -78,7 +87,7 @@ namespace rsx::assembler
FlowEdge* insert_pred(BasicBlock* b, EdgeType type = EdgeType::NONE)
{
FlowEdge e{ .type = type, .from = this, .to = b };
FlowEdge e{ .type = type, .from = b, .to = this };
pred.push_back(e);
return &pred.back();
}

View file

@ -1,4 +1,3 @@
#pragma optimize("", off)
#include <gtest/gtest.h>
#include "Emu/RSX/Common/simple_array.hpp"
@ -72,6 +71,8 @@ namespace rsx::assembler
EXPECT_EQ(graph.blocks.size(), 1);
EXPECT_EQ(graph.blocks.front().instructions.size(), 2);
EXPECT_EQ(graph.blocks.front().instructions.front().length, 4);
EXPECT_NE(graph.blocks.front().instructions.front().addr, 0);
}
TEST(CFG, FpToCFG_IF)
@ -184,6 +185,13 @@ namespace rsx::assembler
EXPECT_EQ(it->id, expected.first);
EXPECT_EQ(it->instructions.size(), expected.second);
}
// 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);
}
TEST(CFG, FpToCFG_IF_ELSE)