mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-12-06 07:12:28 +01: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"
|
#include "stdafx.h"
|
||||||
|
|
||||||
#pragma optimize("", off)
|
|
||||||
|
|
||||||
#include "CFG.h"
|
#include "CFG.h"
|
||||||
|
|
||||||
#include "Emu/RSX/Common/simple_array.hpp"
|
#include "Emu/RSX/Common/simple_array.hpp"
|
||||||
#include "Emu/RSX/Program/RSXFragmentProgram.h"
|
#include "Emu/RSX/Program/RSXFragmentProgram.h"
|
||||||
#include "Emu/RSX/Program/ProgramStateCache.h"
|
|
||||||
|
|
||||||
#include <util/asm.hpp>
|
#include <util/asm.hpp>
|
||||||
|
#include <util/v128.hpp>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
||||||
using namespace program_hash_util;
|
|
||||||
|
|
||||||
namespace rsx::assembler
|
namespace rsx::assembler
|
||||||
{
|
{
|
||||||
|
|
@ -75,6 +72,13 @@ namespace rsx::assembler
|
||||||
return graph.push(parent, id, edge_type);
|
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)
|
while (!end)
|
||||||
{
|
{
|
||||||
BasicBlock** found = end_blocks.find_if(FN(x->id == pc));
|
BasicBlock** found = end_blocks.find_if(FN(x->id == pc));
|
||||||
|
|
@ -110,13 +114,21 @@ namespace rsx::assembler
|
||||||
bb->instructions.push_back({});
|
bb->instructions.push_back({});
|
||||||
auto& ir_inst = bb->instructions.back();
|
auto& ir_inst = bb->instructions.back();
|
||||||
std::memcpy(ir_inst.bytecode, &decoded._u32[0], 16);
|
std::memcpy(ir_inst.bytecode, &decoded._u32[0], 16);
|
||||||
|
ir_inst.length = 4;
|
||||||
|
ir_inst.addr = pc * 16;
|
||||||
|
|
||||||
switch (opcode)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
|
case RSX_FP_OPCODE_BRK:
|
||||||
|
break;
|
||||||
case RSX_FP_OPCODE_CAL:
|
case RSX_FP_OPCODE_CAL:
|
||||||
// Unimplemented. Also unused by the RSX compiler
|
// Unimplemented. Also unused by the RSX compiler
|
||||||
fmt::throw_exception("Unimplemented FP CAL instruction.");
|
fmt::throw_exception("Unimplemented FP CAL instruction.");
|
||||||
break;
|
break;
|
||||||
|
case RSX_FP_OPCODE_FENCT:
|
||||||
|
break;
|
||||||
|
case RSX_FP_OPCODE_FENCB:
|
||||||
|
break;
|
||||||
case RSX_FP_OPCODE_RET:
|
case RSX_FP_OPCODE_RET:
|
||||||
// Outside a subroutine, this doesn't mean much. The main block can conditionally return to stop execution early.
|
// Outside a subroutine, this doesn't mean much. The main block can conditionally return to stop execution early.
|
||||||
// This will not alter flow control.
|
// This will not alter flow control.
|
||||||
|
|
@ -143,8 +155,13 @@ namespace rsx::assembler
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
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++;
|
pc++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,6 +169,14 @@ namespace rsx::assembler
|
||||||
pc++;
|
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));
|
graph.blocks.sort(FN(x.id < y.id));
|
||||||
return graph;
|
return graph;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,20 @@ namespace rsx::assembler
|
||||||
|
|
||||||
struct Instruction
|
struct Instruction
|
||||||
{
|
{
|
||||||
// Raw data. Every instruction is max 128 bits
|
// Raw data. Every instruction is max 128 bits.
|
||||||
u32 bytecode[4];
|
// Each instruction can also have 128 bits of literal/embedded data.
|
||||||
|
u32 bytecode[8]{ {} };
|
||||||
|
u32 addr = 0;
|
||||||
|
|
||||||
// Decoded
|
// Decoded
|
||||||
u32 opcode = 0;
|
u32 opcode = 0;
|
||||||
|
u8 length = 4; // Length in dwords
|
||||||
|
|
||||||
|
// Padding
|
||||||
|
u8 reserved0 = 0;
|
||||||
|
u16 reserved1 = 0;
|
||||||
|
|
||||||
|
// References
|
||||||
std::vector<RegisterRef> srcs;
|
std::vector<RegisterRef> srcs;
|
||||||
std::vector<RegisterRef> dsts;
|
std::vector<RegisterRef> dsts;
|
||||||
};
|
};
|
||||||
|
|
@ -49,7 +58,7 @@ namespace rsx::assembler
|
||||||
ELSE,
|
ELSE,
|
||||||
ENDIF,
|
ENDIF,
|
||||||
LOOP,
|
LOOP,
|
||||||
ENDLOOP
|
ENDLOOP,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FlowEdge
|
struct FlowEdge
|
||||||
|
|
@ -78,7 +87,7 @@ namespace rsx::assembler
|
||||||
|
|
||||||
FlowEdge* insert_pred(BasicBlock* b, EdgeType type = EdgeType::NONE)
|
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);
|
pred.push_back(e);
|
||||||
return &pred.back();
|
return &pred.back();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
#pragma optimize("", off)
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "Emu/RSX/Common/simple_array.hpp"
|
#include "Emu/RSX/Common/simple_array.hpp"
|
||||||
|
|
@ -72,6 +71,8 @@ namespace rsx::assembler
|
||||||
|
|
||||||
EXPECT_EQ(graph.blocks.size(), 1);
|
EXPECT_EQ(graph.blocks.size(), 1);
|
||||||
EXPECT_EQ(graph.blocks.front().instructions.size(), 2);
|
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)
|
TEST(CFG, FpToCFG_IF)
|
||||||
|
|
@ -184,6 +185,13 @@ namespace rsx::assembler
|
||||||
EXPECT_EQ(it->id, expected.first);
|
EXPECT_EQ(it->id, expected.first);
|
||||||
EXPECT_EQ(it->instructions.size(), expected.second);
|
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)
|
TEST(CFG, FpToCFG_IF_ELSE)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue