mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-12-16 04:09:07 +00:00
rsx/cfg: Add support for multi-slot instructions with literals
- Also fix pred edge direction bug
This commit is contained in:
parent
42d9065c11
commit
9d92e190eb
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user