Skip to content

Commit

Permalink
decompiler: detect and turn inverse mult to div (#3795)
Browse files Browse the repository at this point in the history
Co-authored-by: ManDude <[email protected]>
  • Loading branch information
Hat-Kid and ManDude authored Dec 7, 2024
1 parent a2f9d36 commit 51d008f
Show file tree
Hide file tree
Showing 886 changed files with 4,062 additions and 3,888 deletions.
16 changes: 15 additions & 1 deletion common/goos/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
#include "common/util/Assert.h"
#include "common/util/crc32.h"

#include "fmt/core.h"

namespace goos {

/*!
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions decompiler/IR2/AtomicOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions decompiler/IR2/AtomicOp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
};

/*!
Expand Down
9 changes: 8 additions & 1 deletion decompiler/IR2/Form.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions decompiler/IR2/Form.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -324,6 +325,7 @@ class SimpleAtomElement : public FormElement {
private:
SimpleAtom m_atom;
bool m_omit_var_cast;
bool m_no_hex;
};

/*!
Expand Down
70 changes: 69 additions & 1 deletion decompiler/IR2/FormExpressionAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand All @@ -2624,6 +2627,71 @@ 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) {
auto inverse_mult = mr.maps.floats.at(0);
float divisor_num = 1.0f / inverse_mult;

// note: float inaccuracies lead to convergent values, so we can do this safely
if ((int)divisor_num != divisor_num && 1.0f / std::roundf(divisor_num) == inverse_mult) {
lg::debug("managed to round divisor - cool !! {} -> {} ({})", divisor_num,
std::roundf(divisor_num), inverse_mult);
divisor_num = std::roundf(divisor_num);
}

int divisor_int = (int)divisor_num;
bool integer = divisor_int == divisor_num;
if (integer) {
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 (integer && divisor_int % 4096 == 0) {
divisor = pool.form<GenericElement>(
GenericOperator::make_function(pool.form<ConstantTokenElement>("meters")),
pool.form<SimpleAtomElement>(divisor_int / 4096, true));
} else if (integer && divisor_int % 2048 == 0) {
divisor = pool.form<GenericElement>(
GenericOperator::make_function(pool.form<ConstantTokenElement>("meters")),
pool.form<ConstantFloatElement>(divisor_num / (float)METER_LENGTH));
} else if (integer) {
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 || (b_as_simple->expr().is_var() &&
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);
Expand Down
31 changes: 31 additions & 0 deletions decompiler/IR2/GenericElementMatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions decompiler/IR2/GenericElementMatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -91,6 +93,7 @@ class Matcher {
INT,
ANY_INT,
FLOAT,
ANY_FLOAT,
ANY_QUOTED_SYMBOL,
ANY_SYMBOL,
DEREF_OP,
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 11 additions & 4 deletions goal_src/jak1/engine/anim/joint-exploder.gc
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@
(:methods
(new (symbol type int) _type_)))


(deftype joint-exploder-static-joint-params (structure)
((joint-index int16)
(parent-joint-index int16)))


(deftype joint-exploder-static-params (basic)
((joints (array joint-exploder-static-joint-params))))


(deftype joint-exploder-joint (structure)
((next int16)
(prev int16)
Expand All @@ -38,18 +41,21 @@
(transv vector :inline)
(prev-pos vector :inline)))


(deftype joint-exploder-joints (basic)
((num-joints int32)
(joint joint-exploder-joint :inline :dynamic :offset 16))
(:methods
(new (symbol type joint-exploder-static-params) _type_)))


(deftype joint-exploder-list (structure)
((head int32)
(pre-moved? symbol)
(bbox-valid? symbol)
(bbox bounding-box :inline)))


(deftype joint-exploder (process-drawable)
((parent-override (pointer process-drawable) :overlay-at parent)
(die-if-below-y float)
Expand All @@ -73,6 +79,7 @@
(:states
joint-exploder-shatter))


(defmethod asize-of ((this joint-exploder-joints))
(the-as int (+ (-> this type size) (* 176 (-> this num-joints)))))

Expand Down Expand Up @@ -270,7 +277,7 @@
(set! (-> s5-1 transv y) (* 0.7 f30-0))))
(+! (-> s4-0 y) (* 40.96 (-> s3-0 normal y)))
(set! (-> s4-0 w) 1.0)
(set! (-> s5-1 rspeed) (* 0.5 (-> s5-1 rspeed)))))
(set! (-> s5-1 rspeed) (/ (-> s5-1 rspeed) 2))))
(set! v1-2 (-> s5-1 next)))))
#f)

Expand Down Expand Up @@ -381,9 +388,9 @@
(add-point! s5-1 (the-as vector3s (+ (the-as uint (-> gp-0 joint 0 mat vector 3)) (* 176 s4-1)))))))
#f)))

(defmethod relocate ((this joint-exploder) (arg0 int))
(if (nonzero? (-> this joints)) (&+! (-> this joints) arg0))
(the-as joint-exploder ((method-of-type process-drawable relocate) this arg0)))
(defmethod relocate ((this joint-exploder) (offset int))
(if (nonzero? (-> this joints)) (&+! (-> this joints) offset))
(the-as joint-exploder ((method-of-type process-drawable relocate) this offset)))

(defbehavior joint-exploder-init-by-other joint-exploder ((arg0 skeleton-group) (arg1 int) (arg2 joint-exploder-static-params) (arg3 joint-exploder-static-params))
(set! (-> self static-params) arg3)
Expand Down
Loading

0 comments on commit 51d008f

Please sign in to comment.