rsx/cfg: Fix handling of instructions with literal input

- The hardware is pretty dumb about it, it just unconditionally skips the next instruction
This commit is contained in:
kd-11 2026-03-21 01:06:42 +03:00 committed by Ani
parent 2ba7756c0e
commit 67f69bb4b3
3 changed files with 34 additions and 2 deletions

View File

@ -165,6 +165,14 @@ namespace rsx::assembler
if (opcode == RSX_FP_OPCODE_NOP)
{
if (includes_literal_constant())
{
// Verified behavior on real hardware
// If any input on a non-flow-control instruction is of literal type the next instruction is assumed to be data
// You can actually use this behavior to mask off instructions completely
pc++;
}
pc++;
continue;
}

View File

@ -679,8 +679,7 @@ fragment_program_utils::fragment_program_metadata fragment_program_utils::analys
break;
}
if (rsx::assembler::FP::get_operand_count(opcode) > 0 &&
is_any_src_constant(inst))
if (is_any_src_constant(inst))
{
// Instruction references constant, skip one slot occupied by data
index++;

View File

@ -251,4 +251,29 @@ namespace rsx::assembler
EXPECT_EQ(SRC0{ .HEX = graph.blocks.front().instructions[0].bytecode[1] }.exec_if_gr, 1);
EXPECT_EQ(SRC0{ .HEX = graph.blocks.front().instructions[0].bytecode[1] }.exec_if_eq, 1);
}
TEST(CFG, FpToCFG_SkipOverImmediateOperand)
{
auto ir = FPIR::from_source(
"MOV R0, #{ 0.25 };" // NOP with real dst and one literal input
"MOV R0, R1;" // False merge block.
);
RSXFragmentProgram program{};
auto bytecode = ir.compile();
program.data = bytecode.data();
ASSERT_EQ(bytecode.size(), 12);
// Patch the first instruction to be a NOP with a literal as input
const u32 decoded_d0 = ((bytecode[0] & 0xFF00FF00u) >> 16u) | ((bytecode[0] & 0x00FF00FFu) << 16u);
OPDEST d0{ .HEX = decoded_d0 };
d0.opcode = RSX_FP_OPCODE_NOP;
bytecode[0] = ((d0.HEX & 0xFF00FF00u) >> 16u) | ((d0.HEX & 0x00FF00FFu) << 16u);
FlowGraph graph = deconstruct_fragment_program(program);
ASSERT_EQ(graph.blocks.size(), 1);
ASSERT_EQ(graph.blocks.front().instructions.size(), 1);
}
}