decompiler: detect and turn inverse mult to div

This commit is contained in:
ManDude
2024-07-17 21:55:14 +01:00
committed by Hat Kid
parent 8690104ba0
commit c3067c37ef
8 changed files with 127 additions and 3 deletions
+15 -1
View File
@@ -57,6 +57,8 @@
#include "common/util/Assert.h"
#include "common/util/crc32.h"
#include "fmt/core.h"
namespace goos {
/*!
@@ -234,10 +236,14 @@ class Object {
ObjectType type = ObjectType::INVALID;
private:
bool disallow_hex_for_int = false;
public:
std::string print() const {
switch (type) {
case ObjectType::INTEGER:
return integer_obj.print();
return disallow_hex_for_int ? fmt::format("{}", integer_obj.value) : integer_obj.print();
case ObjectType::FLOAT:
return float_obj.print();
case ObjectType::CHAR:
@@ -278,6 +284,14 @@ class Object {
return o;
}
static Object make_integer_no_hex(IntType value) {
Object o;
o.type = ObjectType::INTEGER;
o.integer_obj.value = value;
o.disallow_hex_for_int = true;
return o;
}
static Object make_float(FloatType value) {
Object o;
o.type = ObjectType::FLOAT;
+7
View File
@@ -149,6 +149,11 @@ SimpleAtom SimpleAtom::make_static_address(int static_label_id) {
return result;
}
void SimpleAtom::mark_as_no_hex() {
ASSERT(is_int() && !is_integer_promoted_to_float());
m_no_display_int_as_hex = true;
}
/*!
* Mark this atom as a float. It will be printed as a float.
* This can only be applied to an "integer" atom.
@@ -194,6 +199,8 @@ goos::Object SimpleAtom::to_form(const std::vector<DecompilerLabel>& labels, con
if (std::abs(m_int) > INT32_MAX) {
u64 v = m_int;
return pretty_print::to_symbol(fmt::format("#x{:x}", v));
} else if (m_no_display_int_as_hex) {
return goos::Object::make_integer_no_hex(m_int);
} else {
return goos::Object::make_integer(m_int);
}
+2
View File
@@ -190,6 +190,7 @@ class SimpleAtom {
ASSERT(is_sym_ptr() || is_sym_val() || is_sym_val_ptr());
return m_string;
}
void mark_as_no_hex();
void mark_as_float();
bool is_integer_promoted_to_float() const;
float get_integer_promoted_to_float() const;
@@ -200,6 +201,7 @@ class SimpleAtom {
s64 m_int = -1; // for integer constant and static address label id
RegisterAccess m_variable;
bool m_display_int_as_float = false;
bool m_no_display_int_as_hex = false;
};
/*!
+8 -1
View File
@@ -281,12 +281,19 @@ void LoadSourceElement::get_modified_regs(RegSet& regs) const {
/////////////////////////////
SimpleAtomElement::SimpleAtomElement(const SimpleAtom& atom, bool omit_var_cast)
: m_atom(atom), m_omit_var_cast(omit_var_cast) {
: m_atom(atom), m_omit_var_cast(omit_var_cast), m_no_hex(false) {
if (m_omit_var_cast) {
ASSERT(atom.is_var());
}
}
SimpleAtomElement::SimpleAtomElement(int int_val, bool no_hex)
: m_atom(SimpleAtom::make_int_constant(int_val)), m_omit_var_cast(false), m_no_hex(no_hex) {
if (m_no_hex) {
m_atom.mark_as_no_hex();
}
}
goos::Object SimpleAtomElement::to_form_internal(const Env& env) const {
if (m_omit_var_cast) {
return m_atom.var().to_form(env, RegisterAccess::Print::AS_VARIABLE_NO_CAST);
+2
View File
@@ -309,6 +309,7 @@ class LoadSourceElement : public FormElement {
class SimpleAtomElement : public FormElement {
public:
explicit SimpleAtomElement(const SimpleAtom& var, bool omit_var_cast = false);
SimpleAtomElement(int int_val, bool no_hex = false);
goos::Object to_form_internal(const Env& env) const override;
void apply(const std::function<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& f) override;
@@ -324,6 +325,7 @@ class SimpleAtomElement : public FormElement {
private:
SimpleAtom m_atom;
bool m_omit_var_cast;
bool m_no_hex;
};
/*!
+58 -1
View File
@@ -965,7 +965,7 @@ FormElement* make_and_compact_math_op(Form* arg0,
/*!
* Update a two-argument form that uses two floats.
* This is for operations like * and + that can be nested
* (* (* a b)) -> (* a b c)
* (* (* a b) c) -> (* a b c)
* Note that we only apply this to the _first_ argument to keep the order of operations the same.
*/
void SimpleExpressionElement::update_from_stack_float_2_nestable(const Env& env,
@@ -2603,6 +2603,9 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
ASSERT(x->parent_form == m_src);
}
if (auto test0 = m_src->to_string(env) == "(* 0.00024414062 (-> arg0 y))") {
printf("");
}
if (m_src->is_single_element()) {
auto src_as_se = dynamic_cast<SimpleExpressionElement*>(m_src->back());
if (src_as_se) {
@@ -2624,6 +2627,60 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
}
}
}
// (* 0.125 b) -> (/ b 8)
// adds explicit cast b to float if necessary
auto src_as_ge = dynamic_cast<GenericElement*>(m_src->back());
if (src_as_ge) {
auto mr = match(Matcher::op_fixed(FixedOperatorKind::MULTIPLICATION,
{Matcher::any_single(0), Matcher::any(1)}),
m_src);
if (mr.matched && std::abs(mr.maps.floats.at(0)) < 1 && mr.maps.floats.at(0) != 0) {
float divisor_num = 1.0f / mr.maps.floats.at(0);
int divisor_int = (int)divisor_num;
// note: for some reason 0.016666668 shows up constantly where 1/60 should be. we won't
// force it to 60, but we will transform it anyway
if (divisor_int == divisor_num || mr.maps.floats.at(0) == 0.016666668f) {
auto elt = mr.maps.forms.at(1)->try_as_single_element();
auto b_as_simple = dynamic_cast<SimpleExpressionElement*>(elt);
// WARNING : there is an assumption here that derefs DO NOT have implicit casts!
auto b_as_deref = dynamic_cast<DerefElement*>(elt);
if (b_as_deref ||
(b_as_simple && b_as_simple->expr().kind() == SimpleExpression::Kind::IDENTITY)) {
// TODO check if op is float, cast if so
Form* divisor = nullptr;
if (divisor_num == 4096.0f) {
divisor = pool.form<ConstantTokenElement>("METER_LENGTH");
} else if (divisor_num == (float)divisor_int && divisor_int % 2048 == 0) {
divisor = pool.form<GenericElement>(
GenericOperator::make_function(pool.form<ConstantTokenElement>("meters")),
divisor_int % (int)METER_LENGTH
? pool.form<ConstantFloatElement>(divisor_num / (float)METER_LENGTH)
: pool.form<SimpleAtomElement>(divisor_int / 4096, true));
} else if (divisor_num == divisor_int) {
divisor = pool.form<SimpleAtomElement>(divisor_int, true);
} else {
// this shouldn't run because of the checks before.
divisor = pool.form<ConstantFloatElement>(divisor_num);
}
if (divisor) {
if (b_as_deref || (env.get_types_before_op(b_as_simple->expr().var().idx())
.get(b_as_simple->expr().var().reg())
.typespec() == TypeSpec("float"))) {
*m_src->back_ref() = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::DIVISION), mr.maps.forms.at(1),
divisor);
} else {
*m_src->back_ref() = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::DIVISION),
pool.form<CastElement>(TypeSpec("float"), mr.maps.forms.at(1), true), divisor);
}
m_src->back()->parent_form = m_src;
}
}
}
}
}
}
stack.push_value_to_reg(m_dst, m_src, true, m_src_type, m_var_info);
+31
View File
@@ -127,6 +127,13 @@ Matcher Matcher::single(std::optional<float> value) {
return m;
}
Matcher Matcher::any_single(int match_id) {
Matcher m;
m.m_kind = Kind::ANY_FLOAT;
m.m_float_out_id = match_id;
return m;
}
Matcher Matcher::any_quoted_symbol(int match_id) {
Matcher m;
m.m_kind = Kind::ANY_QUOTED_SYMBOL;
@@ -477,6 +484,30 @@ bool Matcher::do_match(Form* input, MatchResult::Maps* maps_out, const Env* cons
return false;
} break;
case Kind::ANY_FLOAT: {
auto as_const_float =
dynamic_cast<ConstantFloatElement*>(input->try_as_single_active_element());
if (as_const_float) {
if (m_float_out_id != -1) {
maps_out->floats[m_float_out_id] = as_const_float->value();
}
return true;
}
auto as_expr = dynamic_cast<SimpleExpressionElement*>(input->try_as_single_active_element());
if (as_expr && as_expr->expr().is_identity()) {
const auto& atom = as_expr->expr().get_arg(0);
if (atom.is_integer_promoted_to_float()) {
if (m_float_out_id != -1) {
maps_out->floats[m_float_out_id] = atom.get_integer_promoted_to_float();
}
return true;
}
}
return false;
} break;
case Kind::ANY_QUOTED_SYMBOL: {
auto as_simple_atom = dynamic_cast<SimpleAtomElement*>(input->try_as_single_active_element());
if (as_simple_atom) {
+4
View File
@@ -21,6 +21,7 @@ struct MatchResult {
std::unordered_map<int, Form*> forms;
std::unordered_map<int, s64> label;
std::unordered_map<int, s64> ints;
std::unordered_map<int, float> floats;
} maps;
Form* int_or_form_to_form(FormPool& pool, int key_idx) {
@@ -56,6 +57,7 @@ class Matcher {
static Matcher integer(std::optional<int> value);
static Matcher any_integer(int match_id = -1);
static Matcher single(std::optional<float> value);
static Matcher any_single(int match_id = -1);
static Matcher any_reg_cast_to_int_or_uint(int match_id = -1);
static Matcher any_quoted_symbol(int match_id = -1);
static Matcher any_symbol(int match_id = -1);
@@ -91,6 +93,7 @@ class Matcher {
INT,
ANY_INT,
FLOAT,
ANY_FLOAT,
ANY_QUOTED_SYMBOL,
ANY_SYMBOL,
DEREF_OP,
@@ -132,6 +135,7 @@ class Matcher {
int m_form_match;
int m_label_out_id;
int m_int_out_id;
int m_float_out_id;
};
std::optional<int> m_int_match;
std::optional<float> m_float_match;