fix ash showing up as sc

This commit is contained in:
water
2020-10-04 10:44:28 -04:00
parent e195e2b299
commit 4b90634e93
5 changed files with 232 additions and 20 deletions
+33
View File
@@ -515,6 +515,17 @@ std::shared_ptr<IR> try_sll(Instruction& instr, int idx) {
return nullptr;
}
std::shared_ptr<IR> try_dsrav(Instruction& instr, int idx) {
if (is_gpr_3(instr, InstructionKind::DSRAV, {}, {}, {}) &&
!instr.get_src(0).is_reg(make_gpr(Reg::S7)) && !instr.get_src(1).is_reg(make_gpr(Reg::S7))) {
return make_set(IR_Set::REG_64, make_reg(instr.get_dst(0).get_reg(), idx),
std::make_shared<IR_IntMath2>(IR_IntMath2::RIGHT_SHIFT_ARITH,
make_reg(instr.get_src(0).get_reg(), idx),
make_reg(instr.get_src(1).get_reg(), idx)));
}
return nullptr;
}
std::shared_ptr<IR> try_sw(Instruction& instr, int idx) {
if (instr.kind == InstructionKind::SW && instr.get_src(1).is_sym() &&
instr.get_src(2).is_reg(make_gpr(Reg::S7))) {
@@ -706,6 +717,12 @@ BranchDelay get_branch_delay(Instruction& i, int idx) {
b.destination = make_reg(i.get_dst(0).get_reg(), idx);
return b;
}
} else if (i.kind == InstructionKind::DSLLV) {
BranchDelay b(BranchDelay::DSLLV);
b.destination = make_reg(i.get_dst(0).get_reg(), idx);
b.source = make_reg(i.get_src(0).get_reg(), idx);
b.source2 = make_reg(i.get_src(1).get_reg(), idx);
return b;
}
BranchDelay b(BranchDelay::UNKNOWN);
return b;
@@ -792,6 +809,16 @@ std::shared_ptr<IR> try_bgtzl(Instruction& instr, Instruction& next_instr, int i
return nullptr;
}
std::shared_ptr<IR> try_bgezl(Instruction& instr, Instruction& next_instr, int idx) {
if (instr.kind == InstructionKind::BGEZL) {
return std::make_shared<IR_Branch>(
Condition(Condition::GEQ_ZERO_SIGNED, make_reg(instr.get_src(0).get_reg(), idx), nullptr,
nullptr),
instr.get_src(1).get_label(), get_branch_delay(next_instr, idx), true);
}
return nullptr;
}
std::shared_ptr<IR> try_daddiu(Instruction& i0, Instruction& i1, int idx) {
if (i0.kind == InstructionKind::DADDIU && i1.kind == InstructionKind::MOVN &&
i0.get_src(0).get_reg() == make_gpr(Reg::S7)) {
@@ -1249,6 +1276,9 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
case InstructionKind::BGTZL:
result = try_bgtzl(i, next, instr);
break;
case InstructionKind::BGEZL:
result = try_bgezl(i, next, instr);
break;
case InstructionKind::BEQL:
result = try_beql(i, next, instr);
break;
@@ -1419,6 +1449,9 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
case InstructionKind::CVTSW:
result = try_cvtsw(i, instr);
break;
case InstructionKind::DSRAV:
result = try_dsrav(i, instr);
break;
default:
result = nullptr;
}
+152 -10
View File
@@ -1,7 +1,10 @@
#include "third-party/fmt/format.h"
#include <unordered_set>
#include "common/util/MatchParam.h"
#include "CfgBuilder.h"
#include "decompiler/Function/CfgVtx.h"
#include "decompiler/Function/Function.h"
#include "decompiler/Disasm/InstructionMatching.h"
namespace {
@@ -130,11 +133,8 @@ void clean_up_cond_with_else(std::shared_ptr<IR>* ir, LinkedObjectFile& file) {
}
}
/*
* before we do this we'll have to to recognize ash...
bool try_clean_up_sc_as_and(const std::shared_ptr<IR_ShortCircuit>& ir) {
for(size_t i = 0; i < ir->entries.size(); i++) {
for (size_t i = 0; i < ir->entries.size(); i++) {
auto& e = ir->entries.at(i);
if (i < ir->entries.size() - 1) {
// check we load the delay slot with false
@@ -142,16 +142,15 @@ bool try_clean_up_sc_as_and(const std::shared_ptr<IR_ShortCircuit>& ir) {
assert(branch.first);
}
}
// ir->kind = IR_ShortCircuit::AND;
// return true;
// ir->kind = IR_ShortCircuit::AND;
// return true;
return false;
}
void clean_up_sc(std::shared_ptr<IR_ShortCircuit> ir) {
assert(ir->entries.size() > 1);
//try_clean_up_sc_as_and(ir);
// try_clean_up_sc_as_and(ir);
}
*/
/*!
* A GOAL comparison which produces a boolean is recognized as a cond-no-else by the CFG analysis.
@@ -252,6 +251,143 @@ void clean_up_cond_no_else(std::shared_ptr<IR>* ir, LinkedObjectFile& file) {
}
}
bool is_int_math_3(IR* ir,
MatchParam<IR_IntMath2::Kind> kind,
MatchParam<Register> dst,
MatchParam<Register> src0,
MatchParam<Register> src1,
Register* dst_out = nullptr,
Register* src0_out = nullptr,
Register* src1_out = nullptr) {
// should be a set reg to int math 2 ir
auto set = dynamic_cast<IR_Set*>(ir);
if (!set) {
return false;
}
// destination should be a register
auto dest = dynamic_cast<IR_Register*>(set->dst.get());
if (!dest || dst != dest->reg) {
return false;
}
auto math = dynamic_cast<IR_IntMath2*>(set->src.get());
if (!math || kind != math->kind) {
return false;
}
auto arg0 = dynamic_cast<IR_Register*>(math->arg0.get());
auto arg1 = dynamic_cast<IR_Register*>(math->arg1.get());
if (!arg0 || src0 != arg0->reg || !arg1 || src1 != arg1->reg) {
return false;
}
// it's a match!
if (dst_out) {
*dst_out = dest->reg;
}
if (src0_out) {
*src0_out = arg0->reg;
}
if (src1_out) {
*src1_out = arg1->reg;
}
return true;
}
std::shared_ptr<IR> try_sc_as_ash(Function& f, LinkedObjectFile& file, ShortCircuit* vtx) {
if (vtx->entries.size() != 2) {
return nullptr;
}
// todo, I think b0 could possibly be something more complicated, depending on how we order.
auto b0 = dynamic_cast<BlockVtx*>(vtx->entries.at(0));
auto b1 = dynamic_cast<BlockVtx*>(vtx->entries.at(1));
if (!b0 || !b1) {
return nullptr;
}
auto b0_ptr = cfg_to_ir(f, file, b0);
auto b0_ir = dynamic_cast<IR_Begin*>(b0_ptr.get());
auto b1_ptr = cfg_to_ir(f, file, b1);
auto b1_ir = dynamic_cast<IR_Begin*>(b1_ptr.get());
if (!b0_ir || !b1_ir) {
return nullptr;
}
auto branch = dynamic_cast<IR_Branch*>(b0_ir->forms.back().get());
if (!branch || b1_ir->forms.size() != 2) {
return nullptr;
}
// check the branch instruction
if (!branch->likely || branch->condition.kind != Condition::GEQ_ZERO_SIGNED ||
branch->branch_delay.kind != BranchDelay::DSLLV) {
return nullptr;
}
/*
* bgezl s5, L109 ; s5 is the shift amount
dsllv a0, a0, s5 ; a0 is both input and output here
dsubu a1, r0, s5 ; a1 is a temp here
dsrav a0, a0, a1 ; a0 is both input and output here
*/
auto sa_in = dynamic_cast<IR_Register*>(branch->condition.src0.get());
assert(sa_in);
auto result = dynamic_cast<IR_Register*>(branch->branch_delay.destination.get());
auto value_in = dynamic_cast<IR_Register*>(branch->branch_delay.source.get());
auto sa_in2 = dynamic_cast<IR_Register*>(branch->branch_delay.source2.get());
assert(result && value_in && sa_in2);
assert(sa_in->reg == sa_in2->reg);
auto dsubu_candidate = b1_ir->forms.at(0);
auto dsrav_candidate = b1_ir->forms.at(1);
Register clobber;
if (!is_int_math_3(dsubu_candidate.get(), IR_IntMath2::SUB, {}, make_gpr(Reg::R0), sa_in->reg,
&clobber)) {
return nullptr;
}
assert(result);
assert(value_in);
if (!is_int_math_3(dsrav_candidate.get(), IR_IntMath2::RIGHT_SHIFT_ARITH, result->reg,
value_in->reg, clobber)) {
return nullptr;
}
std::shared_ptr<IR> clobber_ir = nullptr;
auto dsubu_set = dynamic_cast<IR_Set*>(dsubu_candidate.get());
auto dsrav_set = dynamic_cast<IR_Set*>(dsrav_candidate.get());
if (clobber != result->reg) {
clobber_ir = dsubu_set->dst;
}
std::shared_ptr<IR> dest_ir = branch->branch_delay.destination;
std::shared_ptr<IR> shift_ir = branch->condition.src0;
std::shared_ptr<IR> value_ir = dynamic_cast<IR_IntMath2*>(dsrav_set->src.get())->arg0;
if (b0_ir->forms.size() == 1) {
// this is probably fine but happens to not occur in anything we try yet.
assert(false);
} else {
// remove the branch
b0_ir->forms.pop_back();
// add the ash
b0_ir->forms.push_back(std::make_shared<IR_Set>(
IR_Set::REG_64, dest_ir, std::make_shared<IR_Ash>(shift_ir, value_ir, clobber_ir)));
return b0_ptr;
}
return nullptr;
}
/*!
* Try to convert a short circuiting expression into a "type-of" expression.
* We do this before attempting the normal and/or expressions.
@@ -363,7 +499,7 @@ std::shared_ptr<IR> try_sc_as_type_of(Function& f, LinkedObjectFile& file, Short
assert(offset->value == -4);
std::shared_ptr<IR> clobber = nullptr;
if (temp_reg->reg != src_reg->reg && temp_reg->reg != dst_reg->reg) {
if (temp_reg->reg != dst_reg->reg) {
clobber = first_branch->condition.src0;
}
if (b0_ir->forms.size() == 2) {
@@ -503,7 +639,13 @@ std::shared_ptr<IR> cfg_to_ir(Function& f, LinkedObjectFile& file, CfgVtx* vtx)
if (as_type_of) {
return as_type_of;
}
auto as_ash = try_sc_as_ash(f, file, svtx);
if (as_ash) {
return as_ash;
}
// now try as a normal and/or
std::vector<IR_ShortCircuit::Entry> entries;
for (auto& x : svtx->entries) {
IR_ShortCircuit::Entry e;
@@ -511,7 +653,7 @@ std::shared_ptr<IR> cfg_to_ir(Function& f, LinkedObjectFile& file, CfgVtx* vtx)
entries.push_back(e);
}
auto result = std::make_shared<IR_ShortCircuit>(entries);
clean_up_sc(result);
// clean_up_sc(result);
return result;
} else if (dynamic_cast<CondNoElse*>(vtx)) {
auto* cvtx = dynamic_cast<CondNoElse*>(vtx);
+21 -7
View File
@@ -19,12 +19,6 @@ std::vector<std::shared_ptr<IR>> IR::get_all_ir(LinkedObjectFile& file) const {
last_checked = end_of_check;
}
// Todo, remove this check which is just for debugging.
std::unordered_set<std::shared_ptr<IR>> unique_ir;
for (auto& x : result) {
unique_ir.insert(x);
}
assert(unique_ir.size() == result.size());
return result;
}
@@ -343,6 +337,9 @@ std::shared_ptr<Form> BranchDelay::to_form(const LinkedObjectFile& file) const {
return buildList(toForm("set!"), destination->to_form(file), "binteger");
case SET_PAIR:
return buildList(toForm("set!"), destination->to_form(file), "pair");
case DSLLV:
return buildList(toForm("set!"), destination->to_form(file),
buildList("shl", source->to_form(file), source2->to_form(file)));
case UNKNOWN:
return buildList("unknown-branch-delay");
default:
@@ -358,6 +355,10 @@ void BranchDelay::get_children(std::vector<std::shared_ptr<IR>>* output) const {
if (source) {
output->push_back(source);
}
if (source2) {
output->push_back(source2);
}
}
std::shared_ptr<Form> IR_Nop::to_form(const LinkedObjectFile& file) const {
@@ -387,6 +388,7 @@ int Condition::num_args() const {
case FALSE:
case TRUTHY:
case GREATER_THAN_ZERO_SIGNED:
case GEQ_ZERO_SIGNED:
return 1;
case ALWAYS:
return 0;
@@ -467,7 +469,10 @@ std::shared_ptr<Form> Condition::to_form(const LinkedObjectFile& file) const {
condtion_operator = ">=.f";
break;
case GREATER_THAN_ZERO_SIGNED:
condtion_operator = ">0.s";
condtion_operator = ">0.si";
break;
case GEQ_ZERO_SIGNED:
condtion_operator = ">=0.si";
break;
default:
assert(false);
@@ -675,3 +680,12 @@ void IR_ShortCircuit::get_children(std::vector<std::shared_ptr<IR>>* output) con
}
}
}
std::shared_ptr<Form> IR_Ash::to_form(const LinkedObjectFile& file) const {
return buildList("ash", value->to_form(file), shift_amount->to_form(file));
}
void IR_Ash::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(value);
output->push_back(shift_amount);
}
+25 -2
View File
@@ -170,8 +170,17 @@ class IR_IntegerConstant : public IR {
};
struct BranchDelay {
enum Kind { NOP, SET_REG_FALSE, SET_REG_TRUE, SET_REG_REG, SET_BINTEGER, SET_PAIR, UNKNOWN } kind;
std::shared_ptr<IR> destination = nullptr, source = nullptr;
enum Kind {
NOP,
SET_REG_FALSE,
SET_REG_TRUE,
SET_REG_REG,
SET_BINTEGER,
SET_PAIR,
DSLLV,
UNKNOWN
} kind;
std::shared_ptr<IR> destination = nullptr, source = nullptr, source2 = nullptr;
explicit BranchDelay(Kind _kind) : kind(_kind) {}
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const;
void get_children(std::vector<std::shared_ptr<IR>>* output) const;
@@ -186,6 +195,7 @@ struct Condition {
LEQ_SIGNED,
GEQ_SIGNED,
GREATER_THAN_ZERO_SIGNED,
GEQ_ZERO_SIGNED,
LESS_THAN_UNSIGNED,
GREATER_THAN_UNSIGNED,
LEQ_UNSIGNED,
@@ -337,4 +347,17 @@ class IR_ShortCircuit : public IR {
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_Ash : public IR {
public:
std::shared_ptr<IR> shift_amount, value, clobber;
IR_Ash(std::shared_ptr<IR> _shift_amount,
std::shared_ptr<IR> _value,
std::shared_ptr<IR> _clobber)
: shift_amount(std::move(_shift_amount)),
value(std::move(_value)),
clobber(std::move(_clobber)) {}
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
#endif // JAK_IR_H
@@ -30,7 +30,7 @@
"asm_functions_by_name":[
// gcommon
"ash", "abs", "min", "max", "(method 2 vec4s)", "quad-copy!", "(method 3 vec4s)", "breakpoint-range-set!",
"abs", "min", "max", "(method 2 vec4s)", "quad-copy!", "(method 3 vec4s)", "breakpoint-range-set!",
// pskernel
"resend-exception", "kernel-set-interrupt-vector", "kernel-set-exception-vector", "return-from-exception",