Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

decompiler: detect and turn inverse mult to div #3795

Merged
merged 12 commits into from
Dec 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
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
Loading