[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.


![image](https://github.com/user-attachments/assets/1f7b4627-81bd-481b-b828-76b9f7ba13b3)

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:
water111
2025-02-16 15:59:17 -05:00
committed by GitHub
parent b07d0d9ced
commit 2a4d3d7a4a
340 changed files with 22771 additions and 36134 deletions
+151 -2
View File
@@ -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);