mirror of
https://github.com/open-goal/jak-project
synced 2026-08-21 23:00:45 -04:00
@@ -959,29 +959,17 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
|
||||
}
|
||||
assert(m_src->parent_element == this);
|
||||
|
||||
// hack for method stuff
|
||||
if (is_dead_set()) {
|
||||
stack.push_value_to_reg_dead(m_dst, m_src, true, m_var_info);
|
||||
return;
|
||||
}
|
||||
|
||||
m_src->update_children_from_stack(env, pool, stack, true);
|
||||
|
||||
for (auto x : m_src->elts()) {
|
||||
assert(x->parent_form == m_src);
|
||||
}
|
||||
|
||||
// if we are a reg-reg move that consumes the original, push it without popping from stack.
|
||||
// it is the Stack's responsibility to untangle these later on.
|
||||
if (m_src->is_single_element()) {
|
||||
auto src_as_se = dynamic_cast<SimpleExpressionElement*>(m_src->back());
|
||||
if (src_as_se) {
|
||||
if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY &&
|
||||
m_dst.reg().get_kind() == Reg::FPR && src_as_se->expr().get_arg(0).is_int() &&
|
||||
src_as_se->expr().get_arg(0).get_int() == 0) {
|
||||
stack.push_value_to_reg(m_dst,
|
||||
pool.alloc_single_element_form<ConstantFloatElement>(nullptr, 0.0),
|
||||
true, m_var_info);
|
||||
return;
|
||||
}
|
||||
|
||||
if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY &&
|
||||
src_as_se->expr().get_arg(0).is_var()) {
|
||||
// this can happen late in the case of coloring moves which are also gpr -> fpr's
|
||||
@@ -1001,6 +989,37 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
|
||||
}
|
||||
}
|
||||
|
||||
// we aren't a reg-reg move, so update our source
|
||||
m_src->update_children_from_stack(env, pool, stack, true);
|
||||
|
||||
for (auto x : m_src->elts()) {
|
||||
assert(x->parent_form == m_src);
|
||||
}
|
||||
|
||||
if (m_src->is_single_element()) {
|
||||
auto src_as_se = dynamic_cast<SimpleExpressionElement*>(m_src->back());
|
||||
if (src_as_se) {
|
||||
if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY &&
|
||||
m_dst.reg().get_kind() == Reg::FPR && src_as_se->expr().get_arg(0).is_int() &&
|
||||
src_as_se->expr().get_arg(0).get_int() == 0) {
|
||||
// not sure this is the best place for this.
|
||||
stack.push_value_to_reg(m_dst,
|
||||
pool.alloc_single_element_form<ConstantFloatElement>(nullptr, 0.0),
|
||||
true, m_var_info);
|
||||
return;
|
||||
}
|
||||
|
||||
// this might get skipped earlier because gpr->fpr gets wrapped in an operation that's
|
||||
// stripped off by update_children_from_stack.
|
||||
if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY &&
|
||||
src_as_se->expr().get_arg(0).is_var()) {
|
||||
if (env.op_id_is_eliminated_coloring_move(src_as_se->expr().get_arg(0).var().idx())) {
|
||||
m_var_info.is_eliminated_coloring_move = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stack.push_value_to_reg(m_dst, m_src, true, m_var_info);
|
||||
for (auto x : m_src->elts()) {
|
||||
assert(x->parent_form == m_src);
|
||||
|
||||
@@ -7,13 +7,25 @@ namespace decompiler {
|
||||
std::string FormStack::StackEntry::print(const Env& env) const {
|
||||
if (destination.has_value()) {
|
||||
assert(source && !elt);
|
||||
return fmt::format("d: {} s: {} | {} <- {} f: {}", active, sequence_point,
|
||||
destination.value().reg().to_charp(), source->to_string(env),
|
||||
non_seq_source.has_value());
|
||||
if (active) {
|
||||
return fmt::format("d: {} s: {} | {} <- {} f: {} w: {}", active, sequence_point,
|
||||
destination.value().reg().to_charp(), source->to_string(env),
|
||||
non_seq_source.has_value(), is_compactable);
|
||||
} else {
|
||||
return fmt::format(" d: {} s: {} | {} <- {} f: {} w: {}", active, sequence_point,
|
||||
destination.value().reg().to_charp(), source->to_string(env),
|
||||
non_seq_source.has_value(), is_compactable);
|
||||
}
|
||||
|
||||
} else {
|
||||
assert(elt && !source);
|
||||
return fmt::format("d: {} s: {} | {} f: {}", active, sequence_point, elt->to_string(env),
|
||||
non_seq_source.has_value());
|
||||
if (active) {
|
||||
return fmt::format("d: {} s: {} | {} f: {}", active, sequence_point, elt->to_string(env),
|
||||
non_seq_source.has_value());
|
||||
} else {
|
||||
return fmt::format(" d: {} s: {} | {} f: {}", active, sequence_point, elt->to_string(env),
|
||||
non_seq_source.has_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +78,7 @@ void FormStack::push_non_seq_reg_to_reg(const Variable& dst,
|
||||
entry.non_seq_source = src;
|
||||
entry.source = src_as_form;
|
||||
entry.set_info = info;
|
||||
entry.is_compactable = true;
|
||||
m_stack.push_back(entry);
|
||||
}
|
||||
|
||||
@@ -109,6 +122,7 @@ Form* FormStack::pop_reg(Register reg,
|
||||
const Env& env,
|
||||
bool allow_side_effects,
|
||||
int begin_idx) {
|
||||
assert(allow_side_effects);
|
||||
(void)env; // keep this for easy debugging.
|
||||
RegSet modified;
|
||||
size_t begin = m_stack.size();
|
||||
@@ -279,8 +293,32 @@ std::vector<FormElement*> FormStack::rewrite(FormPool& pool, const Env& env) {
|
||||
if (e.destination.has_value()) {
|
||||
// (set! x (+ x y)) -> (+! x y)
|
||||
|
||||
auto elt =
|
||||
pool.alloc_element<SetVarElement>(*e.destination, e.source, e.sequence_point, e.set_info);
|
||||
// we want to untangle coloring moves here
|
||||
auto simplified_source = e.source;
|
||||
auto src_as_var = dynamic_cast<SimpleExpressionElement*>(e.source->try_as_single_element());
|
||||
if (src_as_var && src_as_var->expr().is_var() && e.is_compactable) {
|
||||
bool keep_going = true;
|
||||
auto var_to_get = src_as_var->expr().var();
|
||||
while (keep_going && !result.empty()) {
|
||||
keep_going = false;
|
||||
auto last_op_as_set = dynamic_cast<SetVarElement*>(result.back());
|
||||
if (last_op_as_set && last_op_as_set->dst().reg() == var_to_get.reg()) {
|
||||
result.pop_back();
|
||||
auto as_one = dynamic_cast<SimpleExpressionElement*>(
|
||||
last_op_as_set->src()->try_as_single_element());
|
||||
if (as_one && as_one->expr().is_identity() && as_one->expr().is_var() &&
|
||||
!result.empty()) {
|
||||
keep_going = true;
|
||||
var_to_get = as_one->expr().var();
|
||||
}
|
||||
simplified_source = last_op_as_set->src();
|
||||
// result = last_op_as_set->src()->elts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto elt = pool.alloc_element<SetVarElement>(*e.destination, simplified_source,
|
||||
e.sequence_point, e.set_info);
|
||||
e.source->parent_element = elt;
|
||||
|
||||
auto final_elt = try_rewrites_in_place(elt, env, pool);
|
||||
@@ -296,15 +334,38 @@ void rewrite_to_get_var(std::vector<FormElement*>& default_result,
|
||||
FormPool& pool,
|
||||
const Variable& var,
|
||||
const Env&) {
|
||||
auto last_op_as_set = dynamic_cast<SetVarElement*>(default_result.back());
|
||||
if (last_op_as_set && last_op_as_set->dst().reg() == var.reg()) {
|
||||
default_result.pop_back();
|
||||
for (auto form : last_op_as_set->src()->elts()) {
|
||||
form->parent_form = nullptr; // will get set later, this makes it obvious if I forget.
|
||||
default_result.push_back(form);
|
||||
bool keep_going = true;
|
||||
Variable var_to_get = var;
|
||||
|
||||
std::vector<FormElement*> result;
|
||||
|
||||
bool first = true;
|
||||
while (keep_going) {
|
||||
keep_going = false;
|
||||
auto last_op_as_set = dynamic_cast<SetVarElement*>(default_result.back());
|
||||
if (last_op_as_set && last_op_as_set->dst().reg() == var_to_get.reg() &&
|
||||
(first || last_op_as_set->info().is_compactable)) {
|
||||
default_result.pop_back();
|
||||
auto as_one =
|
||||
dynamic_cast<SimpleExpressionElement*>(last_op_as_set->src()->try_as_single_element());
|
||||
if (as_one && as_one->expr().is_identity() && as_one->expr().is_var() &&
|
||||
!default_result.empty()) {
|
||||
keep_going = true;
|
||||
var_to_get = as_one->expr().var();
|
||||
}
|
||||
|
||||
result = last_op_as_set->src()->elts();
|
||||
}
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (result.empty()) {
|
||||
default_result.push_back(pool.alloc_element<SimpleAtomElement>(SimpleAtom::make_var(var)));
|
||||
} else {
|
||||
for (auto x : result) {
|
||||
x->parent_form = nullptr;
|
||||
default_result.push_back(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ class FormStack {
|
||||
FormElement* elt = nullptr;
|
||||
bool sequence_point = false;
|
||||
TP_Type type;
|
||||
bool is_compactable = false;
|
||||
|
||||
SetVarInfo set_info;
|
||||
|
||||
|
||||
@@ -166,6 +166,9 @@ struct SetVarInfo {
|
||||
bool is_dead_set = false;
|
||||
// is this a (set! var #f) where the value of #f isn't used?
|
||||
bool is_dead_false = false;
|
||||
// it this a move that can be compacted? For now, this is always false, but we may want to
|
||||
// allow more advanced compaction later.
|
||||
bool is_compactable = false;
|
||||
};
|
||||
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -2503,4 +2503,35 @@ TEST_F(FormRegressionTest, StringLt) {
|
||||
" #f\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "");
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprAssert) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" daddiu sp, sp, -16\n"
|
||||
" sd ra, 0(sp)\n"
|
||||
" sd fp, 8(sp)\n"
|
||||
" or fp, t9, r0\n"
|
||||
|
||||
" bne s7, a0, L12\n"
|
||||
" or v1, s7, r0\n"
|
||||
|
||||
" lw t9, format(s7)\n"
|
||||
" daddiu a0, s7, #t\n"
|
||||
" daddiu v1, fp, L17\n"
|
||||
" or a2, a1, r0\n"
|
||||
" or a1, v1, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
"L12:\n"
|
||||
" or v0, r0, r0\n"
|
||||
" ld ra, 0(sp)\n"
|
||||
" ld fp, 8(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 16";
|
||||
std::string type = "(function symbol string int)";
|
||||
|
||||
std::string expected = "(begin (if (not arg0) (format (quote #t) \"A ~A\" arg1)) 0)";
|
||||
test_with_expr(func, type, expected, false, "", {{"L17", "A ~A"}});
|
||||
}
|
||||
Reference in New Issue
Block a user