This commit is contained in:
water 2020-10-04 11:37:47 -04:00
parent 4b90634e93
commit f70ae6dad3
5 changed files with 104 additions and 25 deletions

View File

@ -723,6 +723,11 @@ BranchDelay get_branch_delay(Instruction& i, int 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;
} else if (is_gpr_3(i, InstructionKind::DSUBU, {}, make_gpr(Reg::R0), {})) {
BranchDelay b(BranchDelay::NEGATE);
b.destination = make_reg(i.get_dst(0).get_reg(), idx);
b.source = make_reg(i.get_src(1).get_reg(), idx);
return b;
}
BranchDelay b(BranchDelay::UNKNOWN);
return b;
@ -819,6 +824,16 @@ std::shared_ptr<IR> try_bgezl(Instruction& instr, Instruction& next_instr, int i
return nullptr;
}
std::shared_ptr<IR> try_bltzl(Instruction& instr, Instruction& next_instr, int idx) {
if (instr.kind == InstructionKind::BLTZL) {
return std::make_shared<IR_Branch>(
Condition(Condition::LESS_THAN_ZERO, 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)) {
@ -1279,6 +1294,9 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
case InstructionKind::BGEZL:
result = try_bgezl(i, next, instr);
break;
case InstructionKind::BLTZL:
result = try_bltzl(i, next, instr);
break;
case InstructionKind::BEQL:
result = try_beql(i, next, instr);
break;

View File

@ -133,25 +133,6 @@ void clean_up_cond_with_else(std::shared_ptr<IR>* ir, LinkedObjectFile& file) {
}
}
bool try_clean_up_sc_as_and(const std::shared_ptr<IR_ShortCircuit>& ir) {
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
auto branch = get_condition_branch(&e.condition);
assert(branch.first);
}
}
// 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);
}
/*!
* A GOAL comparison which produces a boolean is recognized as a cond-no-else by the CFG analysis.
* But it should not be decompiled as a branching statement.
@ -251,6 +232,9 @@ void clean_up_cond_no_else(std::shared_ptr<IR>* ir, LinkedObjectFile& file) {
}
}
/*!
* Match for a (set! reg (math reg reg)) form
*/
bool is_int_math_3(IR* ir,
MatchParam<IR_IntMath2::Kind> kind,
MatchParam<Register> dst,
@ -298,6 +282,62 @@ bool is_int_math_3(IR* ir,
return true;
}
bool is_same_reg(IR* a, IR* b) {
auto ar = dynamic_cast<IR_Register*>(a);
auto br = dynamic_cast<IR_Register*>(b);
return ar && br && ar->reg == br->reg;
}
std::shared_ptr<IR> try_sc_as_abs(Function& f, LinkedObjectFile& file, ShortCircuit* vtx) {
if (vtx->entries.size() != 1) {
return nullptr;
}
auto b0 = dynamic_cast<BlockVtx*>(vtx->entries.at(0));
if (!b0) {
return nullptr;
}
// todo, seems possible to be a single op instead of a begin here.
auto b0_ptr = cfg_to_ir(f, file, b0);
auto b0_ir = dynamic_cast<IR_Begin*>(b0_ptr.get());
auto branch = dynamic_cast<IR_Branch*>(b0_ir->forms.back().get());
if (!branch) {
return nullptr;
}
// check the branch instruction
if (!branch->likely || branch->condition.kind != Condition::LESS_THAN_ZERO ||
branch->branch_delay.kind != BranchDelay::NEGATE) {
return nullptr;
}
auto input = branch->condition.src0;
auto output = branch->branch_delay.destination;
assert(is_same_reg(input.get(), branch->branch_delay.source.get()));
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, output, std::make_shared<IR_IntMath1>(IR_IntMath1::ABS, input)));
return b0_ptr;
}
return nullptr;
}
/*!
* Attempt to convert a short circuit expression into an arithmetic shift.
* GOAL's shift function accepts positive/negative numbers to determine the direction
* of the shift.
*/
std::shared_ptr<IR> try_sc_as_ash(Function& f, LinkedObjectFile& file, ShortCircuit* vtx) {
if (vtx->entries.size() != 2) {
return nullptr;
@ -310,6 +350,7 @@ std::shared_ptr<IR> try_sc_as_ash(Function& f, LinkedObjectFile& file, ShortCirc
return nullptr;
}
// todo, seems possible to be a single op instead of a begin...
auto b0_ptr = cfg_to_ir(f, file, b0);
auto b0_ir = dynamic_cast<IR_Begin*>(b0_ptr.get());
@ -644,8 +685,12 @@ std::shared_ptr<IR> cfg_to_ir(Function& f, LinkedObjectFile& file, CfgVtx* vtx)
if (as_ash) {
return as_ash;
}
// now try as a normal and/or
auto as_abs = try_sc_as_abs(f, file, svtx);
if (as_abs) {
return as_abs;
}
// now try as a normal and/or
std::vector<IR_ShortCircuit::Entry> entries;
for (auto& x : svtx->entries) {
IR_ShortCircuit::Entry e;
@ -653,7 +698,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);
// todo clean these into real and/or.
return result;
} else if (dynamic_cast<CondNoElse*>(vtx)) {
auto* cvtx = dynamic_cast<CondNoElse*>(vtx);

View File

@ -270,6 +270,9 @@ std::shared_ptr<Form> IR_IntMath1::to_form(const LinkedObjectFile& file) const {
case NOT:
math_operator = "lognot";
break;
case ABS:
math_operator = "abs.si";
break;
default:
assert(false);
}
@ -340,6 +343,9 @@ std::shared_ptr<Form> BranchDelay::to_form(const LinkedObjectFile& file) const {
case DSLLV:
return buildList(toForm("set!"), destination->to_form(file),
buildList("shl", source->to_form(file), source2->to_form(file)));
case NEGATE:
return buildList(toForm("set!"), destination->to_form(file),
buildList("-", source->to_form(file)));
case UNKNOWN:
return buildList("unknown-branch-delay");
default:
@ -389,6 +395,7 @@ int Condition::num_args() const {
case TRUTHY:
case GREATER_THAN_ZERO_SIGNED:
case GEQ_ZERO_SIGNED:
case LESS_THAN_ZERO:
return 1;
case ALWAYS:
return 0;
@ -474,6 +481,9 @@ std::shared_ptr<Form> Condition::to_form(const LinkedObjectFile& file) const {
case GEQ_ZERO_SIGNED:
condtion_operator = ">=0.si";
break;
case LESS_THAN_ZERO:
condtion_operator = "<0.si";
break;
default:
assert(false);
}

View File

@ -147,7 +147,7 @@ class IR_IntMath2 : public IR {
class IR_IntMath1 : public IR {
public:
enum Kind { NOT } kind;
enum Kind { NOT, ABS } kind;
IR_IntMath1(Kind _kind, std::shared_ptr<IR> _arg) : kind(_kind), arg(std::move(_arg)) {}
std::shared_ptr<IR> arg;
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
@ -178,6 +178,7 @@ struct BranchDelay {
SET_BINTEGER,
SET_PAIR,
DSLLV,
NEGATE,
UNKNOWN
} kind;
std::shared_ptr<IR> destination = nullptr, source = nullptr, source2 = nullptr;
@ -195,6 +196,7 @@ struct Condition {
LEQ_SIGNED,
GEQ_SIGNED,
GREATER_THAN_ZERO_SIGNED,
LESS_THAN_ZERO,
GEQ_ZERO_SIGNED,
LESS_THAN_UNSIGNED,
GREATER_THAN_UNSIGNED,

View File

@ -30,7 +30,7 @@
"asm_functions_by_name":[
// gcommon
"abs", "min", "max", "(method 2 vec4s)", "quad-copy!", "(method 3 vec4s)", "breakpoint-range-set!",
"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",
@ -42,12 +42,16 @@
// this one fails due to false compaction where an else case has only a not expression in it.
"master-is-hopeful-better?",
// temp remove me
// fails for unknown reason
"target-falling-anim-trans",
// these are all valid, but use short circuiting branches in strange ways. There's probably a few compiler uses that we're not
"(method 21 actor-link-info)","(method 20 actor-link-info)","(method 28 collide-shape-prim-mesh)", "(method 35 collide-shape)",
"debug-menu-item-var-render", "(method 14 level)","add-blue-motion","anim-tester-add-newobj","(method 27 orb-cache-top)",
// real asm
"cspace<-parented-transformq-joint!", "blerc-a-fragment", "render-boundary-tri", "render-boundary-quad",
"(method 19 collide-shape-prim-sphere)","vector-segment-distance-point!", "exp",
"(method 19 collide-shape-prim-sphere)","vector-segment-distance-point!", "exp", "(method 11 collide-mesh-cache)",
"(method 11 cpu-thread)", "atan0", "sincos!", "sincos-rad!", "disasm-dma-list", "vblank-handler", "vif1-handler",
"vif1-handler-debug", "entity-actor-count", "decompress-frame-data-pair-to-accumulator",