mirror of
https://github.com/open-goal/jak-project
synced 2026-08-21 23:00:45 -04:00
[decompiler] More inline vector functions (#3861)
This adds more recognition for inlined vector functions to the decompiler, which can clean up a bunch of ugly looking code/`rlet`s.  Unfortunately, this changes the numbering of ops in the decomp, since all the vector instructions get combined in a single "operation" by the decompiler. I really tried to avoid having this ever happen in the decompiler and this is one of the few cases where it has. So I had to update a bunch of type casts. For that reason I haven't turned this on in Jak 2 yet, although I am planning to do that at some point. (probably at the same time as porting back a bunch of jak 3 improvements to jak 2) --------- Co-authored-by: water111 <awaterford1111445@gmail.com>
This commit is contained in:
@@ -1962,6 +1962,75 @@ std::unique_ptr<AtomicOp> convert_6(const Instruction& i0,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicOp> convert_vector_length_squared(const Instruction* instrs, int idx) {
|
||||
// 0: lqc2 vf1, 0(a0)
|
||||
if (instrs[0].kind != InstructionKind::LQC2 || instrs[0].get_dst(0).get_reg() != make_vf(1) ||
|
||||
!instrs[0].get_src(0).is_imm(0)) {
|
||||
return nullptr;
|
||||
}
|
||||
Register vec_src = instrs[0].get_src(1).get_reg();
|
||||
|
||||
auto vf0 = make_vf(0);
|
||||
auto vf1 = make_vf(1);
|
||||
auto vf2 = make_vf(2);
|
||||
|
||||
// 1: vaddw.x vf2, vf0, vf0
|
||||
if (instrs[1].kind != InstructionKind::VADD_BC || instrs[1].get_dst(0).get_reg() != vf2 ||
|
||||
instrs[1].get_src(0).get_reg() != vf0 || instrs[1].get_src(1).get_reg() != vf0 ||
|
||||
instrs[1].cop2_dest != 0b1000 || instrs[1].cop2_bc != 3) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 2: vmul.xyzw vf1, vf1, vf1
|
||||
if (instrs[2].kind != InstructionKind::VMUL || instrs[2].get_dst(0).get_reg() != vf1 ||
|
||||
instrs[2].get_src(0).get_reg() != vf1 || instrs[2].get_src(1).get_reg() != vf1 ||
|
||||
instrs[2].cop2_dest != 0b1111) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 3: vmulax.x acc, vf2, vf1
|
||||
if (instrs[3].kind != InstructionKind::VMULA_BC || instrs[3].get_src(0).get_reg() != vf2 ||
|
||||
instrs[3].get_src(1).get_reg() != vf1 || instrs[3].cop2_dest != 0b1000 ||
|
||||
instrs[3].cop2_bc != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 4: vmadday.x acc, vf2, vf1
|
||||
if (instrs[4].kind != InstructionKind::VMADDA_BC || instrs[4].get_src(0).get_reg() != vf2 ||
|
||||
instrs[4].get_src(1).get_reg() != vf1 || instrs[4].cop2_dest != 0b1000 ||
|
||||
instrs[4].cop2_bc != 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 5: vmaddz.x vf1, vf2, vf1
|
||||
if (instrs[5].kind != InstructionKind::VMADD_BC || instrs[5].get_dst(0).get_reg() != vf1 ||
|
||||
instrs[5].get_src(0).get_reg() != vf2 || instrs[5].get_src(1).get_reg() != vf1 ||
|
||||
instrs[5].cop2_dest != 0b1000 || instrs[5].cop2_bc != 2) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 6: qmfc2.i v0, vf1
|
||||
if (instrs[6].kind != InstructionKind::QMFC2 || instrs[6].src->get_reg() != vf1) {
|
||||
return nullptr;
|
||||
}
|
||||
auto dst = instrs[6].get_dst(0).get_reg();
|
||||
return std::make_unique<SetVarOp>(
|
||||
make_dst_var(dst, idx),
|
||||
SimpleExpression(SimpleExpression::Kind::VECTOR_LENGTH_SQUARED, make_src_atom(vec_src, idx)),
|
||||
idx);
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicOp> convert_7(const Instruction* instrs, int idx, GameVersion version) {
|
||||
if (version == GameVersion::Jak3) {
|
||||
auto as_vector_length_squared = convert_vector_length_squared(instrs, idx);
|
||||
if (as_vector_length_squared) {
|
||||
return as_vector_length_squared;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicOp> convert_vector_plus_float_times(const Instruction* instrs, int idx) {
|
||||
// lqc2 vf2, 0(a2)
|
||||
if (instrs[0].kind != InstructionKind::LQC2 || instrs[0].get_dst(0).get_reg() != make_vf(2) ||
|
||||
@@ -2029,11 +2098,83 @@ std::unique_ptr<AtomicOp> convert_vector_plus_float_times(const Instruction* ins
|
||||
idx);
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicOp> convert_8(const Instruction* instrs, int idx) {
|
||||
std::unique_ptr<AtomicOp> convert_vector_plus_times(const Instruction* instrs, int idx) {
|
||||
// 0: mfc1 a3, f0
|
||||
if (instrs[0].kind != InstructionKind::MFC1) {
|
||||
return nullptr;
|
||||
}
|
||||
Register flt_src_3 = instrs[0].get_src(0).get_reg();
|
||||
Register temp = instrs[0].get_dst(0).get_reg();
|
||||
|
||||
// 1: qmtc2.i vf7, a3
|
||||
if (instrs[1].kind != InstructionKind::QMTC2 || instrs[1].get_dst(0).get_reg() != make_vf(7) ||
|
||||
instrs[1].get_src(0).get_reg() != temp) {
|
||||
return nullptr;
|
||||
}
|
||||
// 2: lqc2 vf5, 0(a2)
|
||||
if (instrs[2].kind != InstructionKind::LQC2 || instrs[2].get_dst(0).get_reg() != make_vf(5) ||
|
||||
!instrs[2].get_src(0).is_imm(0)) {
|
||||
return nullptr;
|
||||
}
|
||||
Register vec_src_2 = instrs[2].get_src(1).get_reg();
|
||||
|
||||
// 3: lqc2 vf4, 0(a1)
|
||||
if (instrs[3].kind != InstructionKind::LQC2 || instrs[3].get_dst(0).get_reg() != make_vf(4) ||
|
||||
!instrs[3].get_src(0).is_imm(0)) {
|
||||
return nullptr;
|
||||
}
|
||||
Register vec_src_1 = instrs[3].get_src(1).get_reg();
|
||||
|
||||
// 4: vaddx.w vf6, vf0, vf0
|
||||
if (instrs[4].kind != InstructionKind::VADD_BC || instrs[4].get_dst(0).get_reg() != make_vf(6) ||
|
||||
instrs[4].get_src(0).get_reg() != make_vf(0) ||
|
||||
instrs[4].get_src(1).get_reg() != make_vf(0) || instrs[4].cop2_bc != 0 ||
|
||||
instrs[4].cop2_dest != 0b0001) {
|
||||
return nullptr;
|
||||
}
|
||||
// 5: vmulax.xyz acc, vf5, vf7
|
||||
// vmulax.xyz acc, vf5, vf7
|
||||
if (instrs[5].kind != InstructionKind::VMULA_BC || instrs[5].get_src(0).get_reg() != make_vf(5) ||
|
||||
instrs[5].get_src(1).get_reg() != make_vf(7) || instrs[5].cop2_dest != 0b1110 ||
|
||||
instrs[5].cop2_bc != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 6: vmaddw.xyz vf6, vf4, vf0
|
||||
if (instrs[6].kind != InstructionKind::VMADD_BC || instrs[6].get_dst(0).get_reg() != make_vf(6) ||
|
||||
instrs[6].get_src(0).get_reg() != make_vf(4) ||
|
||||
instrs[6].get_src(1).get_reg() != make_vf(0) || instrs[6].cop2_dest != 0b1110 ||
|
||||
instrs[6].cop2_bc != 3) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 7: sqc2 vf6, 0(a0)
|
||||
if (instrs[7].kind != InstructionKind::SQC2 || instrs[7].get_src(0).get_reg() != make_vf(6) ||
|
||||
!instrs[7].get_src(1).is_imm(0)) {
|
||||
return nullptr;
|
||||
}
|
||||
Register dst = instrs[7].get_src(2).get_reg();
|
||||
|
||||
return std::make_unique<SetVarOp>(
|
||||
make_dst_var(dst, idx),
|
||||
SimpleExpression(SimpleExpression::Kind::VECTOR_PLUS_TIMES, make_src_atom(dst, idx),
|
||||
make_src_atom(vec_src_1, idx), make_src_atom(vec_src_2, idx),
|
||||
make_src_atom(flt_src_3, idx)),
|
||||
idx);
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicOp> convert_8(const Instruction* instrs, int idx, GameVersion version) {
|
||||
auto as_vector_float_plus_times = convert_vector_plus_float_times(instrs, idx);
|
||||
if (as_vector_float_plus_times) {
|
||||
return as_vector_float_plus_times;
|
||||
}
|
||||
|
||||
if (version == GameVersion::Jak3) {
|
||||
auto as_vector_plus_times = convert_vector_plus_times(instrs, idx);
|
||||
if (as_vector_plus_times) {
|
||||
return as_vector_plus_times;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -2378,13 +2519,21 @@ int convert_block_to_atomic_ops(int begin_idx,
|
||||
}
|
||||
|
||||
if (!converted && n_instr >= 8) {
|
||||
op = convert_8(&instr[0], op_idx);
|
||||
op = convert_8(&instr[0], op_idx, version);
|
||||
if (op) {
|
||||
converted = true;
|
||||
length = 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (!converted && n_instr >= 7) {
|
||||
op = convert_7(&instr[0], op_idx, version);
|
||||
if (op) {
|
||||
converted = true;
|
||||
length = 7;
|
||||
}
|
||||
}
|
||||
|
||||
if (!converted && n_instr >= 6) {
|
||||
// try 6 instructions
|
||||
op = convert_6(instr[0], instr[1], instr[2], instr[3], instr[4], instr[5], op_idx);
|
||||
|
||||
Reference in New Issue
Block a user