mirror of
https://github.com/open-goal/jak-project
synced 2026-08-19 06:02:15 -04:00
fix ash showing up as sc
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user