diff --git a/common/goos/Object.h b/common/goos/Object.h index 6aac254eb4..673a0cee9c 100644 --- a/common/goos/Object.h +++ b/common/goos/Object.h @@ -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; diff --git a/decompiler/IR2/AtomicOp.cpp b/decompiler/IR2/AtomicOp.cpp index 16e0a52cd6..caccad8be3 100644 --- a/decompiler/IR2/AtomicOp.cpp +++ b/decompiler/IR2/AtomicOp.cpp @@ -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& 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); } diff --git a/decompiler/IR2/AtomicOp.h b/decompiler/IR2/AtomicOp.h index 9dcb36791f..93d5b5bd8c 100644 --- a/decompiler/IR2/AtomicOp.h +++ b/decompiler/IR2/AtomicOp.h @@ -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; }; /*! diff --git a/decompiler/IR2/Form.cpp b/decompiler/IR2/Form.cpp index 723e088ddb..49b90c5ca6 100644 --- a/decompiler/IR2/Form.cpp +++ b/decompiler/IR2/Form.cpp @@ -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); diff --git a/decompiler/IR2/Form.h b/decompiler/IR2/Form.h index 3f09a14304..f8ae80a3a4 100644 --- a/decompiler/IR2/Form.h +++ b/decompiler/IR2/Form.h @@ -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& f) override; void apply_form(const std::function& f) override; @@ -324,6 +325,7 @@ class SimpleAtomElement : public FormElement { private: SimpleAtom m_atom; bool m_omit_var_cast; + bool m_no_hex; }; /*! diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index 2ff5958673..4959817cb9 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -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(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(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(elt); + // WARNING : there is an assumption here that derefs DO NOT have implicit casts! + auto b_as_deref = dynamic_cast(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("METER_LENGTH"); + } else if (divisor_num == (float)divisor_int && divisor_int % 2048 == 0) { + divisor = pool.form( + GenericOperator::make_function(pool.form("meters")), + divisor_int % (int)METER_LENGTH + ? pool.form(divisor_num / (float)METER_LENGTH) + : pool.form(divisor_int / 4096, true)); + } else if (divisor_num == divisor_int) { + divisor = pool.form(divisor_int, true); + } else { + // this shouldn't run because of the checks before. + divisor = pool.form(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( + GenericOperator::make_fixed(FixedOperatorKind::DIVISION), mr.maps.forms.at(1), + divisor); + } else { + *m_src->back_ref() = pool.alloc_element( + GenericOperator::make_fixed(FixedOperatorKind::DIVISION), + pool.form(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); diff --git a/decompiler/IR2/GenericElementMatcher.cpp b/decompiler/IR2/GenericElementMatcher.cpp index 65a7e796c2..c9eced46eb 100644 --- a/decompiler/IR2/GenericElementMatcher.cpp +++ b/decompiler/IR2/GenericElementMatcher.cpp @@ -127,6 +127,13 @@ Matcher Matcher::single(std::optional 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(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(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(input->try_as_single_active_element()); if (as_simple_atom) { diff --git a/decompiler/IR2/GenericElementMatcher.h b/decompiler/IR2/GenericElementMatcher.h index 66b9a2b1bc..fd81c1e6e5 100644 --- a/decompiler/IR2/GenericElementMatcher.h +++ b/decompiler/IR2/GenericElementMatcher.h @@ -21,6 +21,7 @@ struct MatchResult { std::unordered_map forms; std::unordered_map label; std::unordered_map ints; + std::unordered_map floats; } maps; Form* int_or_form_to_form(FormPool& pool, int key_idx) { @@ -56,6 +57,7 @@ class Matcher { static Matcher integer(std::optional value); static Matcher any_integer(int match_id = -1); static Matcher single(std::optional 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 m_int_match; std::optional m_float_match;