[decompiler] ASM Branching Support (#677)

* basic example working in geometry

* before updating offline'

* clean up

* temp

* progress
This commit is contained in:
water111
2021-07-05 16:07:07 -04:00
committed by GitHub
parent 54c63ff42c
commit 551a9c4955
42 changed files with 4161 additions and 272 deletions
+145 -3
View File
@@ -299,6 +299,8 @@ std::unique_ptr<AtomicOp> make_asm_op(const Instruction& i0, int idx) {
case InstructionKind::MULAS:
case InstructionKind::MADDAS:
case InstructionKind::MADDS:
case InstructionKind::MSUBAS:
case InstructionKind::MSUBS:
case InstructionKind::ADDAS:
// Moves / Loads / Stores
@@ -361,6 +363,14 @@ std::unique_ptr<AtomicOp> make_asm_op(const Instruction& i0, int idx) {
}
}
std::unique_ptr<AtomicOp> convert_1_allow_asm(const Instruction& i0, int idx) {
auto as_normal = convert_1(i0, idx, false);
if (as_normal) {
return as_normal;
}
return make_asm_op(i0, idx);
}
////////////////////////
// Branch Helpers
////////////////////////
@@ -407,7 +417,11 @@ std::unique_ptr<AtomicOp> make_branch(const IR2_Condition& condition,
if (branch_delay.is_known()) {
return std::make_unique<BranchOp>(likely, condition, dest_label, branch_delay, my_idx);
} else {
auto delay_op = std::shared_ptr<AtomicOp>(convert_1(delay, my_idx, false));
auto delay_op = std::shared_ptr<AtomicOp>(convert_1_allow_asm(delay, my_idx));
if (!delay_op) {
throw std::runtime_error(
fmt::format("Failed to convert branch delay slot instruction for branch at {}", my_idx));
}
return std::make_unique<AsmBranchOp>(likely, condition, dest_label, delay_op, my_idx);
}
}
@@ -421,6 +435,14 @@ std::unique_ptr<AtomicOp> make_branch_no_delay(const IR2_Condition& condition,
return std::make_unique<BranchOp>(likely, condition, dest_label, delay, my_idx);
}
std::unique_ptr<AtomicOp> make_asm_branch_no_delay(const IR2_Condition& condition,
bool likely,
int dest_label,
int my_idx) {
assert(likely);
return std::make_unique<AsmBranchOp>(likely, condition, dest_label, nullptr, my_idx);
}
///////////////////////
// OP 1 Conversions
//////////////////////
@@ -880,6 +902,23 @@ std::unique_ptr<AtomicOp> convert_1(const Instruction& i0, int idx, bool hint_in
// OP 2 Conversions
//////////////////////
std::unique_ptr<AtomicOp> convert_fp_branch_asm(const Instruction& i0,
const Instruction& i1,
IR2_Condition::Kind kind,
int idx) {
if (i1.kind == InstructionKind::BC1TL || i1.kind == InstructionKind::BC1FL) {
IR2_Condition condition(kind, make_src_atom(i0.get_src(0).get_reg(), idx),
make_src_atom(i0.get_src(1).get_reg(), idx));
if (i1.kind == InstructionKind::BC1FL) {
condition.invert();
}
// return make_branch(condition, i2, false, i1.get_src(0).get_label(), idx);
return make_asm_branch_no_delay(condition, true, i1.get_src(0).get_label(), idx);
}
return nullptr;
}
std::unique_ptr<AtomicOp> convert_division_2(const Instruction& i0,
const Instruction& i1,
int idx,
@@ -1071,6 +1110,8 @@ std::unique_ptr<AtomicOp> convert_2(const Instruction& i0, const Instruction& i1
return convert_slt_2(i0, i1, idx, true);
case InstructionKind::SLTU:
return convert_slt_2(i0, i1, idx, false);
case InstructionKind::CLTS:
return convert_fp_branch_asm(i0, i1, IR2_Condition::Kind::FLOAT_LESS_THAN, idx);
default:
return nullptr;
}
@@ -1605,6 +1646,98 @@ std::unique_ptr<AtomicOp> convert_6(const Instruction& i0,
return nullptr;
}
bool is_lwc(const Instruction& instr, int offset) {
return instr.kind == InstructionKind::LWC1 && instr.get_src(0).is_imm(offset);
}
// 9 instructions
std::unique_ptr<AtomicOp> convert_vector3_dot(const Instruction* instrs, int idx) {
// lwc1 f0, 0(a0)
if (!is_lwc(instrs[0], 0)) {
return nullptr;
}
auto t0 = instrs[0].get_dst(0).get_reg();
// lwc1 f1, 4(a0)
if (!is_lwc(instrs[1], 4)) {
return nullptr;
}
auto t1 = instrs[1].get_dst(0).get_reg();
// lwc1 f2, 8(a0)
if (!is_lwc(instrs[2], 8)) {
return nullptr;
}
auto t2 = instrs[2].get_dst(0).get_reg();
// lwc1 f3, 0(v1)
if (!is_lwc(instrs[3], 0)) {
return nullptr;
}
auto t3 = instrs[3].get_dst(0).get_reg();
// lwc1 f4, 4(v1)
if (!is_lwc(instrs[4], 4)) {
return nullptr;
}
auto t4 = instrs[4].get_dst(0).get_reg();
// lwc1 f5, 8(v1)
if (!is_lwc(instrs[5], 8)) {
return nullptr;
}
auto t5 = instrs[5].get_dst(0).get_reg();
auto src0 = instrs[0].get_src(1).get_reg();
auto src1 = instrs[3].get_src(1).get_reg();
if (instrs[1].get_src(1).get_reg() != src0) {
return nullptr;
}
if (instrs[2].get_src(1).get_reg() != src0) {
return nullptr;
}
if (instrs[4].get_src(1).get_reg() != src1) {
return nullptr;
}
if (instrs[5].get_src(1).get_reg() != src1) {
return nullptr;
}
// mula.s f0, f3
if (instrs[6].kind != InstructionKind::MULAS || instrs[6].get_src(0).get_reg() != t0 ||
instrs[6].get_src(1).get_reg() != t3) {
return nullptr;
}
// madda.s f1, f4
if (instrs[7].kind != InstructionKind::MADDAS || instrs[7].get_src(0).get_reg() != t1 ||
instrs[7].get_src(1).get_reg() != t4) {
return nullptr;
}
// madd.s f0, f2, f5
if (instrs[8].kind != InstructionKind::MADDS || instrs[8].get_src(0).get_reg() != t2 ||
instrs[8].get_src(1).get_reg() != t5) {
return nullptr;
}
auto dst = instrs[8].get_dst(0).get_reg();
return std::make_unique<SetVarOp>(
make_dst_var(dst, idx),
SimpleExpression(SimpleExpression::Kind::VECTOR_3_DOT, make_src_atom(src0, idx),
make_src_atom(src1, idx)),
idx);
}
std::unique_ptr<AtomicOp> convert_9(const Instruction* instrs, int idx) {
auto as_vector3_dot = convert_vector3_dot(instrs, idx);
if (as_vector3_dot) {
return as_vector3_dot;
}
return nullptr;
}
} // namespace
/*!
@@ -1637,7 +1770,15 @@ int convert_block_to_atomic_ops(int begin_idx,
warnings.warn_sq_lq();
}
if (n_instr >= 6) {
if (n_instr >= 9) {
op = convert_9(&instr[0], op_idx);
if (op) {
converted = true;
length = 9;
}
}
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);
if (op) {
@@ -1702,7 +1843,8 @@ int convert_block_to_atomic_ops(int begin_idx,
if (!converted) {
// failed!
throw std::runtime_error("Failed to convert " + instr->to_string(labels));
throw std::runtime_error(
fmt::format("Failed to convert ({} instrs) {}\n", n_instr, instr->to_string(labels)));
// lg::die("Failed to convert instruction {} to an atomic op",
// instr->to_string(labels));
}