[Decompiler] Misc fixes for gkernel/math (#257)

* more cases

* some work on math and floating point stuff

* some decompiling for fun
This commit is contained in:
water111 2021-02-13 11:32:52 -05:00 committed by GitHub
parent a3b31d3c0e
commit 126dfc1c45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 1388 additions and 358 deletions

View file

@ -439,11 +439,9 @@ bool TypeSystem::try_reverse_lookup_other(const FieldReverseLookupInput& input,
if (field.is_inline()) { if (field.is_inline()) {
expected_offset_into_field = lookup_type(field.type())->get_offset(); expected_offset_into_field = lookup_type(field.type())->get_offset();
} }
if (offset_into_field == expected_offset_into_field && !input.deref.has_value()) { if (offset_into_field == expected_offset_into_field && !input.deref.has_value() &&
// get the inline field. !input.stride) {
if (input.stride) { // get the inline field exactly
continue;
}
path->push_back(token); path->push_back(token);
*result_type = field_deref.type; *result_type = field_deref.type;
*addr_of = false; *addr_of = false;

View file

@ -1200,185 +1200,6 @@ TypeSpec coerce_to_reg_type(const TypeSpec& in) {
return in; return in;
} }
bool debug_reverse_deref = false;
/*!
* Todo:
* - I suspect inlined basics will be off by 4-bytes, depending on where the basic field starts.
* - Inline array is not yet implemented.
*/
bool TypeSystem::reverse_deref(const ReverseDerefInputInfo& input,
std::vector<ReverseDerefInfo::DerefToken>* path,
bool* addr_of,
TypeSpec* result_type) const {
if (!input.mem_deref) {
assert(input.load_size == 0);
}
if (debug_reverse_deref) {
fmt::print("Reverse Deref Type {} Offset {} Deref {} Load Size {} Signed {}\n",
input.input_type.print(), input.offset, input.mem_deref, input.load_size,
input.sign_extend);
}
if (input.offset == 0 && !input.mem_deref) {
// base case, we are here!
*addr_of = false;
return true;
}
auto base_input_type = input.input_type.base_type();
if (base_input_type == "pointer") {
auto di = get_deref_info(input.input_type);
int closest_index = input.offset / di.stride;
int offset_into_elt = input.offset - (closest_index * di.stride);
auto base_type = di.result_type;
ReverseDerefInfo::DerefToken token;
token.kind = ReverseDerefInfo::DerefToken::INDEX;
token.index = closest_index;
if (!di.mem_deref) {
return false;
}
assert(di.mem_deref);
if (offset_into_elt == 0) {
if (input.mem_deref) {
// todo - this is a hack to let quadword loads always succeed because we don't support it
// correctly at this point.
if (input.load_size == 16 ||
(di.load_size == input.load_size && di.sign_extend == input.sign_extend)) {
path->push_back(token);
*addr_of = false;
*result_type = base_type;
return true;
} else {
if (debug_reverse_deref) {
fmt::print("load size {} {}, sext {} {}, input {}\n", di.load_size, input.load_size,
di.sign_extend, input.sign_extend, input.input_type.print().c_str());
}
return false;
}
} else {
path->push_back(token);
*addr_of = true;
*result_type = make_pointer_typespec(base_type);
return true;
}
} else {
return false;
}
} else if (base_input_type == "inline-array") {
if (debug_reverse_deref) {
fmt::print("Got inline-array case\n");
}
// todo
return false;
} else {
auto type_info = lookup_type(input.input_type);
auto structure_type = dynamic_cast<StructureType*>(type_info);
if (!structure_type) {
if (debug_reverse_deref) {
fmt::print("Failed structure type check\n");
}
return false;
}
auto corrected_offset = input.offset + type_info->get_offset();
for (auto& field : structure_type->fields()) {
auto field_deref = lookup_field_info(type_info->get_name(), field.name());
if (debug_reverse_deref) {
fmt::print("Offset is {}, {} try field {} {} which is {}, {}\n", corrected_offset,
corrected_offset + input.load_size, field.name(), field_deref.type.print(),
field.offset(), field.offset() + get_size_in_type(field));
}
if (corrected_offset >= field.offset() && (corrected_offset + std::max(1, input.load_size) <=
field.offset() + get_size_in_type(field) ||
field.is_dynamic())) {
if (debug_reverse_deref) {
fmt::print(" ok, using field {}\n", field.name());
}
// we are somewhere in this field!
int offset_into_field = corrected_offset - field.offset();
ReverseDerefInfo::DerefToken token;
token.kind = ReverseDerefInfo::DerefToken::FIELD;
token.name = field.name();
if (offset_into_field == 0) {
if (field_deref.needs_deref) {
if (input.mem_deref) {
// perfect match to a field requiring a deref, which we have.
TypeSpec loc_type = make_pointer_typespec(field_deref.type);
auto di = get_deref_info(loc_type);
if (di.load_size == input.load_size && di.sign_extend == input.sign_extend) {
path->push_back(token);
*addr_of = false;
*result_type = field_deref.type;
return true;
} else {
return false;
}
} else {
// we didn't deref the field, so it's an addr of
path->push_back(token);
*addr_of = true;
*result_type = make_pointer_typespec(field_deref.type);
return true;
}
} else {
// field doesn't need deref to access.
if (input.mem_deref) {
// but we did deref...
// let's look deeper in this field.
path->push_back(token);
ReverseDerefInputInfo r_input = input;
r_input.offset = offset_into_field;
r_input.input_type = field_deref.type;
return reverse_deref(r_input, path, addr_of, result_type);
} else {
// and we didn't deref.
path->push_back(token);
*result_type = field_deref.type;
*addr_of = false;
return true;
}
}
} else {
// we are partially inside of a field here.
if (field_deref.needs_deref) {
// hmm.. shouldn't be possible
if (debug_reverse_deref) {
fmt::print("Failed extra deref case: {}.\n", field.print());
}
return false;
} else {
// we should try again.
path->push_back(token);
ReverseDerefInputInfo r_input = input;
r_input.offset = offset_into_field;
r_input.input_type = field_deref.type;
return reverse_deref(r_input, path, addr_of, result_type);
}
}
}
}
}
if (debug_reverse_deref) {
fmt::print("Failed (reached end)\n");
}
return false;
}
ReverseDerefInfo TypeSystem::get_reverse_deref_info(const ReverseDerefInputInfo& input) const {
if (!input.mem_deref) {
assert(input.load_size == 0);
}
ReverseDerefInfo result;
result.success = reverse_deref(input, &result.deref_path, &result.addr_of, &result.result_type);
return result;
}
/*! /*!
* Is the given type a bitfield type? * Is the given type a bitfield type?
*/ */

View file

@ -54,38 +54,6 @@ struct DerefInfo {
TypeSpec result_type; TypeSpec result_type;
}; };
struct ReverseDerefInfo {
struct DerefToken {
enum Kind { INDEX, FIELD } kind;
std::string name;
int index;
std::string print() const {
switch (kind) {
case INDEX:
return std::to_string(index);
case FIELD:
return name;
default:
assert(false);
}
}
};
TypeSpec result_type;
std::vector<DerefToken> deref_path;
bool success = false;
bool addr_of = false;
};
struct ReverseDerefInputInfo {
int offset = -1;
bool mem_deref = false;
RegClass reg = RegClass::INVALID;
int load_size = -1;
bool sign_extend = false;
TypeSpec input_type;
};
/*! /*!
* A description of a dereference (size + sign extend) * A description of a dereference (size + sign extend)
*/ */
@ -129,7 +97,6 @@ class TypeSystem {
std::string get_runtime_type(const TypeSpec& ts); std::string get_runtime_type(const TypeSpec& ts);
DerefInfo get_deref_info(const TypeSpec& ts) const; DerefInfo get_deref_info(const TypeSpec& ts) const;
ReverseDerefInfo get_reverse_deref_info(const ReverseDerefInputInfo& input) const;
FieldReverseLookupOutput reverse_field_lookup(const FieldReverseLookupInput& input) const; FieldReverseLookupOutput reverse_field_lookup(const FieldReverseLookupInput& input) const;
bool fully_defined_type_exists(const std::string& name) const; bool fully_defined_type_exists(const std::string& name) const;
@ -216,10 +183,6 @@ class TypeSystem {
TypeSpec lowest_common_ancestor(const std::vector<TypeSpec>& types) const; TypeSpec lowest_common_ancestor(const std::vector<TypeSpec>& types) const;
private: private:
bool reverse_deref(const ReverseDerefInputInfo& input,
std::vector<ReverseDerefInfo::DerefToken>* path,
bool* addr_of,
TypeSpec* result_type) const;
bool try_reverse_lookup(const FieldReverseLookupInput& input, bool try_reverse_lookup(const FieldReverseLookupInput& input,
std::vector<FieldReverseLookupOutput::Token>* path, std::vector<FieldReverseLookupOutput::Token>* path,
bool* addr_of, bool* addr_of,

View file

@ -698,6 +698,20 @@ IR2_Condition::Kind get_condition_opposite(IR2_Condition::Kind kind) {
} }
} }
bool condition_uses_float(IR2_Condition::Kind kind) {
switch (kind) {
case IR2_Condition::Kind::FLOAT_LESS_THAN:
case IR2_Condition::Kind::FLOAT_LEQ:
case IR2_Condition::Kind::FLOAT_NOT_EQUAL:
case IR2_Condition::Kind::FLOAT_EQUAL:
case IR2_Condition::Kind::FLOAT_GEQ:
case IR2_Condition::Kind::FLOAT_GREATER_THAN:
return true;
default:
return false;
}
}
IR2_Condition::IR2_Condition(Kind kind) : m_kind(kind) { IR2_Condition::IR2_Condition(Kind kind) : m_kind(kind) {
assert(get_condition_num_args(m_kind) == 0); assert(get_condition_num_args(m_kind) == 0);
} }

View file

@ -377,6 +377,7 @@ class IR2_Condition {
std::string get_condition_kind_name(IR2_Condition::Kind kind); std::string get_condition_kind_name(IR2_Condition::Kind kind);
int get_condition_num_args(IR2_Condition::Kind kind); int get_condition_num_args(IR2_Condition::Kind kind);
IR2_Condition::Kind get_condition_opposite(IR2_Condition::Kind kind); IR2_Condition::Kind get_condition_opposite(IR2_Condition::Kind kind);
bool condition_uses_float(IR2_Condition::Kind kind);
/*! /*!
* Set a variable to a GOAL boolean, based off of a condition. * Set a variable to a GOAL boolean, based off of a condition.

View file

@ -17,20 +17,6 @@ RegClass get_reg_kind(const Register& r) {
assert(false); assert(false);
} }
} }
DerefToken to_token(FieldReverseLookupOutput::Token in) {
switch (in.kind) {
case FieldReverseLookupOutput::Token::Kind::FIELD:
return DerefToken::make_field_name(in.name);
case FieldReverseLookupOutput::Token::Kind::CONSTANT_IDX:
return DerefToken::make_int_constant(in.idx);
case FieldReverseLookupOutput::Token::Kind::VAR_IDX:
return DerefToken::make_expr_placeholder();
default:
// temp
throw std::runtime_error("Cannot convert rd lookup token to deref token");
}
}
} // namespace } // namespace
ConditionElement* BranchOp::get_condition_as_form(FormPool& pool, const Env& env) const { ConditionElement* BranchOp::get_condition_as_form(FormPool& pool, const Env& env) const {

View file

@ -135,6 +135,10 @@ TP_Type SimpleExpression::get_type(const TypeState& input,
return m_args[0].get_type(input, env, dts); return m_args[0].get_type(input, env, dts);
case Kind::GPR_TO_FPR: { case Kind::GPR_TO_FPR: {
const auto& in_type = input.get(get_arg(0).var().reg()); const auto& in_type = input.get(get_arg(0).var().reg());
if (in_type.is_integer_constant(0)) {
// GOAL is smart enough to use binary 0b0 as floating point 0.
return TP_Type::make_from_ts("float");
}
return in_type; return in_type;
} }
case Kind::FPR_TO_GPR: case Kind::FPR_TO_GPR:
@ -146,7 +150,11 @@ TP_Type SimpleExpression::get_type(const TypeState& input,
case Kind::ABS_S: case Kind::ABS_S:
case Kind::NEG_S: case Kind::NEG_S:
case Kind::INT_TO_FLOAT: case Kind::INT_TO_FLOAT:
case Kind::MIN_S:
case Kind::MAX_S:
return TP_Type::make_from_ts("float"); return TP_Type::make_from_ts("float");
case Kind::FLOAT_TO_INT:
return TP_Type::make_from_ts("int");
case Kind::ADD: case Kind::ADD:
case Kind::SUB: case Kind::SUB:
case Kind::MUL_SIGNED: case Kind::MUL_SIGNED:
@ -249,6 +257,11 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
// no need to track the type because we don't know the method index anyway. // no need to track the type because we don't know the method index anyway.
return TP_Type::make_partial_dyanmic_vtable_access(); return TP_Type::make_partial_dyanmic_vtable_access();
} }
if (arg1_type.is_integer_constant() && is_int_or_uint(dts, arg0_type)) {
return TP_Type::make_from_integer_constant_plus_var(arg1_type.get_integer_constant(),
arg0_type.typespec());
}
break; break;
default: default:
@ -418,6 +431,12 @@ TypeState SetVarOp::propagate_types_internal(const TypeState& input,
const Env& env, const Env& env,
DecompilerTypeSystem& dts) { DecompilerTypeSystem& dts) {
TypeState result = input; TypeState result = input;
if (m_dst.reg().get_kind() == Reg::FPR && m_src.is_identity() && m_src.get_arg(0).is_int() &&
m_src.get_arg(0).get_int() == 0) {
// mtc fX, r0 should be a float type. GOAL was smart enough to do this.
result.get(m_dst.reg()) = TP_Type::make_from_ts("float");
return result;
}
result.get(m_dst.reg()) = m_src.get_type(input, env, dts); result.get(m_dst.reg()) = m_src.get_type(input, env, dts);
return result; return result;
} }

View file

@ -16,6 +16,7 @@ void Env::set_remap_for_function(int nargs) {
var_name.push_back('0'); var_name.push_back('0');
m_var_remap[var_name] = ("arg" + std::to_string(i)); m_var_remap[var_name] = ("arg" + std::to_string(i));
} }
m_var_remap["s6-0"] = "pp";
} }
void Env::set_remap_for_new_method(int nargs) { void Env::set_remap_for_new_method(int nargs) {
@ -29,6 +30,7 @@ void Env::set_remap_for_new_method(int nargs) {
var_name.push_back('0'); var_name.push_back('0');
m_var_remap[var_name] = ("arg" + std::to_string(i - 2)); m_var_remap[var_name] = ("arg" + std::to_string(i - 2));
} }
m_var_remap["s6-0"] = "pp";
} }
void Env::set_remap_for_method(int nargs) { void Env::set_remap_for_method(int nargs) {
@ -41,6 +43,7 @@ void Env::set_remap_for_method(int nargs) {
var_name.push_back('0'); var_name.push_back('0');
m_var_remap[var_name] = ("arg" + std::to_string(i - 1)); m_var_remap[var_name] = ("arg" + std::to_string(i - 1));
} }
m_var_remap["s6-0"] = "pp";
} }
void Env::map_args_from_config(const std::vector<std::string>& args_names, void Env::map_args_from_config(const std::vector<std::string>& args_names,

View file

@ -3,6 +3,7 @@
#include <utility> #include <utility>
#include "decompiler/ObjectFile/LinkedObjectFile.h" #include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "common/goos/PrettyPrinter.h" #include "common/goos/PrettyPrinter.h"
#include "common/type_system/TypeSystem.h"
namespace decompiler { namespace decompiler {
@ -1133,6 +1134,12 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
return "min"; return "min";
case FixedOperatorKind::MAX: case FixedOperatorKind::MAX:
return "max"; return "max";
case FixedOperatorKind::FABS:
return "fabs";
case FixedOperatorKind::FMIN:
return "fmin";
case FixedOperatorKind::FMAX:
return "fmax";
case FixedOperatorKind::LOGAND: case FixedOperatorKind::LOGAND:
return "logand"; return "logand";
case FixedOperatorKind::LOGIOR: case FixedOperatorKind::LOGIOR:
@ -1173,6 +1180,8 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
return "="; return "=";
case FixedOperatorKind::NEQ: case FixedOperatorKind::NEQ:
return "!="; return "!=";
case FixedOperatorKind::METHOD_OF_OBJECT:
return "method-of-object";
default: default:
assert(false); assert(false);
} }
@ -1390,6 +1399,20 @@ void DerefToken::get_modified_regs(RegSet& regs) const {
} }
} }
DerefToken to_token(const FieldReverseLookupOutput::Token& in) {
switch (in.kind) {
case FieldReverseLookupOutput::Token::Kind::FIELD:
return DerefToken::make_field_name(in.name);
case FieldReverseLookupOutput::Token::Kind::CONSTANT_IDX:
return DerefToken::make_int_constant(in.idx);
case FieldReverseLookupOutput::Token::Kind::VAR_IDX:
return DerefToken::make_expr_placeholder();
default:
// temp
throw std::runtime_error("Cannot convert rd lookup token to deref token");
}
}
DerefElement::DerefElement(Form* base, bool is_addr_of, DerefToken token) DerefElement::DerefElement(Form* base, bool is_addr_of, DerefToken token)
: m_base(base), m_is_addr_of(is_addr_of), m_tokens({std::move(token)}) { : m_base(base), m_is_addr_of(is_addr_of), m_tokens({std::move(token)}) {
m_base->parent_element = this; m_base->parent_element = this;
@ -1564,4 +1587,18 @@ void StringConstantElement::apply_form(const std::function<void(Form*)>&) {}
void StringConstantElement::collect_vars(VariableSet&) const {} void StringConstantElement::collect_vars(VariableSet&) const {}
void StringConstantElement::get_modified_regs(RegSet&) const {} void StringConstantElement::get_modified_regs(RegSet&) const {}
/////////////////////////////
// ConstantTokenElement
/////////////////////////////
ConstantTokenElement::ConstantTokenElement(const std::string& value) : m_value(value) {}
goos::Object ConstantTokenElement::to_form_internal(const Env&) const {
return pretty_print::to_symbol(m_value);
}
void ConstantTokenElement::apply(const std::function<void(FormElement*)>&) {}
void ConstantTokenElement::apply_form(const std::function<void(Form*)>&) {}
void ConstantTokenElement::collect_vars(VariableSet&) const {}
void ConstantTokenElement::get_modified_regs(RegSet&) const {}
} // namespace decompiler } // namespace decompiler

View file

@ -7,6 +7,7 @@
#include "decompiler/Disasm/Register.h" #include "decompiler/Disasm/Register.h"
#include "decompiler/IR2/AtomicOp.h" #include "decompiler/IR2/AtomicOp.h"
#include "common/goos/Object.h" #include "common/goos/Object.h"
#include "common/type_system/TypeSystem.h"
namespace decompiler { namespace decompiler {
class Form; class Form;
@ -132,6 +133,11 @@ class SimpleExpressionElement : public FormElement {
FormStack& stack, FormStack& stack,
std::vector<FormElement*>* result, std::vector<FormElement*>* result,
bool allow_side_effects); bool allow_side_effects);
void update_from_stack_float_to_int(const Env& env,
FormPool& pool,
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects);
void update_from_stack_copy_first_int_2(const Env& env, void update_from_stack_copy_first_int_2(const Env& env,
FixedOperatorKind kind, FixedOperatorKind kind,
FormPool& pool, FormPool& pool,
@ -856,6 +862,8 @@ class DerefToken {
Form* m_expr = nullptr; Form* m_expr = nullptr;
}; };
DerefToken to_token(const FieldReverseLookupOutput::Token& in);
class DerefElement : public FormElement { class DerefElement : public FormElement {
public: public:
DerefElement(Form* base, bool is_addr_of, DerefToken token); DerefElement(Form* base, bool is_addr_of, DerefToken token);
@ -947,6 +955,24 @@ class StringConstantElement : public FormElement {
void apply_form(const std::function<void(Form*)>& f) override; void apply_form(const std::function<void(Form*)>& f) override;
void collect_vars(VariableSet& vars) const override; void collect_vars(VariableSet& vars) const override;
void get_modified_regs(RegSet& regs) const override; void get_modified_regs(RegSet& regs) const override;
void update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects) override;
private:
std::string m_value;
};
class ConstantTokenElement : public FormElement {
public:
ConstantTokenElement(const std::string& value);
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;
void collect_vars(VariableSet& vars) const override;
void get_modified_regs(RegSet& regs) const override;
private: private:
std::string m_value; std::string m_value;

View file

@ -331,13 +331,14 @@ void SimpleExpressionElement::update_from_stack_fpr_to_gpr(const Env& env,
bool allow_side_effects) { bool allow_side_effects) {
auto src = m_expr.get_arg(0); auto src = m_expr.get_arg(0);
auto src_type = env.get_types_before_op(m_my_idx).get(src.var().reg()); auto src_type = env.get_types_before_op(m_my_idx).get(src.var().reg());
if (src_type.typespec() == TypeSpec("float")) { if (src_type.typespec() == TypeSpec("float") || src_type.typespec() == TypeSpec("int")) {
// set ourself to identity. // set ourself to identity.
m_expr = src.as_expr(); m_expr = src.as_expr();
// then go again. // then go again.
update_from_stack(env, pool, stack, result, allow_side_effects); update_from_stack(env, pool, stack, result, allow_side_effects);
} else { } else {
throw std::runtime_error(fmt::format("FPR -> GPR applied to a {}", src_type.print())); throw std::runtime_error(
fmt::format("FPR -> GPR applied to a {} in {}", src_type.print(), to_string(env)));
} }
} }
@ -376,7 +377,11 @@ void SimpleExpressionElement::update_from_stack_float_2(const Env& env,
args.at(0), args.at(1)); args.at(0), args.at(1));
result->push_back(new_form); result->push_back(new_form);
} else { } else {
throw std::runtime_error(fmt::format("Floating point math attempted on invalid types.")); auto type0 = env.get_types_before_op(m_my_idx).get(m_expr.get_arg(0).var().reg());
auto type1 = env.get_types_before_op(m_my_idx).get(m_expr.get_arg(1).var().reg());
throw std::runtime_error(
fmt::format("Floating point math attempted on invalid types: {} and {} in op {}.",
type0.print(), type1.print(), to_string(env)));
} }
} }
@ -422,6 +427,47 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
args.push_back(pool.alloc_single_element_form<SimpleAtomElement>(nullptr, m_expr.get_arg(1))); args.push_back(pool.alloc_single_element_form<SimpleAtomElement>(nullptr, m_expr.get_arg(1)));
} }
// Look for getting an address inside of an object.
// (+ <integer 108 + int> process). array style access with a stride of 1.
// in the case, both are vars.
if (arg1_reg) {
// lookup types.
auto arg1_type = env.get_types_before_op(m_my_idx).get(m_expr.get_arg(1).var().reg());
auto arg0_type = env.get_types_before_op(m_my_idx).get(m_expr.get_arg(0).var().reg());
if (arg0_type.kind == TP_Type::Kind::INTEGER_CONSTANT_PLUS_VAR) {
// try to see if this is valid, from the type system.
FieldReverseLookupInput input;
input.offset = arg0_type.get_integer_constant();
input.stride = 1;
input.base_type = arg1_type.typespec();
auto out = env.dts->ts.reverse_field_lookup(input);
if (out.success) {
// it is. now we have to modify things
// first, look for the index
auto arg0_matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::ADDITION),
{Matcher::any(0), Matcher::integer(input.offset)});
auto match_result = match(arg0_matcher, args.at(0));
if (match_result.matched) {
bool used_index = false;
std::vector<DerefToken> tokens;
for (auto& tok : out.tokens) {
if (tok.kind == FieldReverseLookupOutput::Token::Kind::VAR_IDX) {
assert(!used_index);
used_index = true;
tokens.push_back(DerefToken::make_int_expr(match_result.maps.forms.at(0)));
} else {
tokens.push_back(to_token(tok));
}
}
result->push_back(pool.alloc_element<DerefElement>(args.at(1), out.addr_of, tokens));
return;
} else {
throw std::runtime_error("Failed to match for stride 1 address access with add.");
}
}
}
}
if ((arg0_i && arg1_i) || (arg0_u && arg1_u)) { if ((arg0_i && arg1_i) || (arg0_u && arg1_u)) {
auto new_form = pool.alloc_element<GenericElement>( auto new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::ADDITION), args.at(0), args.at(1)); GenericOperator::make_fixed(FixedOperatorKind::ADDITION), args.at(0), args.at(1));
@ -612,6 +658,21 @@ void SimpleExpressionElement::update_from_stack_int_to_float(const Env& env,
} }
} }
void SimpleExpressionElement::update_from_stack_float_to_int(const Env& env,
FormPool& pool,
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects) {
auto var = m_expr.get_arg(0).var();
auto arg = pop_to_forms({var}, env, pool, stack, allow_side_effects).at(0);
auto type = env.get_types_before_op(var.idx()).get(var.reg()).typespec();
if (type == TypeSpec("float")) {
result->push_back(pool.alloc_element<CastElement>(TypeSpec("int"), arg, true));
} else {
throw std::runtime_error("Used float to int on a " + type.print());
}
}
void SimpleExpressionElement::update_from_stack(const Env& env, void SimpleExpressionElement::update_from_stack(const Env& env,
FormPool& pool, FormPool& pool,
FormStack& stack, FormStack& stack,
@ -642,12 +703,20 @@ void SimpleExpressionElement::update_from_stack(const Env& env,
update_from_stack_float_2(env, FixedOperatorKind::ADDITION, pool, stack, result, update_from_stack_float_2(env, FixedOperatorKind::ADDITION, pool, stack, result,
allow_side_effects); allow_side_effects);
break; break;
case SimpleExpression::Kind::MAX_S:
update_from_stack_float_2(env, FixedOperatorKind::FMAX, pool, stack, result,
allow_side_effects);
break;
case SimpleExpression::Kind::MIN_S:
update_from_stack_float_2(env, FixedOperatorKind::FMIN, pool, stack, result,
allow_side_effects);
break;
case SimpleExpression::Kind::SQRT_S: case SimpleExpression::Kind::SQRT_S:
update_from_stack_float_1(env, FixedOperatorKind::SQRT, pool, stack, result, update_from_stack_float_1(env, FixedOperatorKind::SQRT, pool, stack, result,
allow_side_effects); allow_side_effects);
break; break;
case SimpleExpression::Kind::ABS_S: case SimpleExpression::Kind::ABS_S:
update_from_stack_float_1(env, FixedOperatorKind::ABS, pool, stack, result, update_from_stack_float_1(env, FixedOperatorKind::FABS, pool, stack, result,
allow_side_effects); allow_side_effects);
break; break;
case SimpleExpression::Kind::NEG_S: case SimpleExpression::Kind::NEG_S:
@ -718,6 +787,9 @@ void SimpleExpressionElement::update_from_stack(const Env& env,
case SimpleExpression::Kind::INT_TO_FLOAT: case SimpleExpression::Kind::INT_TO_FLOAT:
update_from_stack_int_to_float(env, pool, stack, result, allow_side_effects); update_from_stack_int_to_float(env, pool, stack, result, allow_side_effects);
break; break;
case SimpleExpression::Kind::FLOAT_TO_INT:
update_from_stack_float_to_int(env, pool, stack, result, allow_side_effects);
break;
default: default:
throw std::runtime_error( throw std::runtime_error(
fmt::format("SimpleExpressionElement::update_from_stack NYI for {}", to_string(env))); fmt::format("SimpleExpressionElement::update_from_stack NYI for {}", to_string(env)));
@ -737,11 +809,18 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
for (auto x : m_src->elts()) { for (auto x : m_src->elts()) {
assert(x->parent_form == m_src); assert(x->parent_form == m_src);
} }
if (m_src->is_single_element()) { if (m_src->is_single_element()) {
auto src_as_se = dynamic_cast<SimpleExpressionElement*>(m_src->back()); auto src_as_se = dynamic_cast<SimpleExpressionElement*>(m_src->back());
if (src_as_se) { if (src_as_se) {
if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY && if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY &&
src_as_se->expr().get_arg(0).is_var()) { 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
// so they don't get caught by SetVarOp::get_as_form's check.
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;
}
auto var = src_as_se->expr().get_arg(0).var(); auto var = src_as_se->expr().get_arg(0).var();
auto& info = env.reg_use().op.at(var.idx()); auto& info = env.reg_use().op.at(var.idx());
if (info.consumes.find(var.reg()) != info.consumes.end()) { if (info.consumes.find(var.reg()) != info.consumes.end()) {
@ -996,6 +1075,8 @@ void DerefElement::update_from_stack(const Env& env,
bool allow_side_effects) { bool allow_side_effects) {
// todo - update var tokens from stack? // todo - update var tokens from stack?
m_base->update_children_from_stack(env, pool, stack, allow_side_effects); m_base->update_children_from_stack(env, pool, stack, allow_side_effects);
// merge nested ->'s
auto as_deref = dynamic_cast<DerefElement*>(m_base->try_as_single_element()); auto as_deref = dynamic_cast<DerefElement*>(m_base->try_as_single_element());
if (as_deref) { if (as_deref) {
if (!m_is_addr_of && !as_deref->is_addr_of()) { if (!m_is_addr_of && !as_deref->is_addr_of()) {
@ -1003,7 +1084,25 @@ void DerefElement::update_from_stack(const Env& env,
m_base = as_deref->m_base; m_base = as_deref->m_base;
} }
} }
result->push_back(this);
// rewrite access to the method table to use method-of-object
// (-> <some-object> type methods-by-name <method-name>)
// (method-of-object <some-object> <method-name>)
auto get_method_matcher = Matcher::deref(
Matcher::any(0), false,
{DerefTokenMatcher::string("type"), DerefTokenMatcher::string("methods-by-name"),
DerefTokenMatcher::any_string(1)});
Form hack_form;
hack_form.elts() = {this};
auto mr = match(get_method_matcher, &hack_form);
if (mr.matched) {
auto method_op = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::METHOD_OF_OBJECT), mr.maps.forms.at(0),
pool.alloc_single_element_form<ConstantTokenElement>(nullptr, mr.maps.strings.at(1)));
result->push_back(method_op);
} else {
result->push_back(this);
}
} }
/////////////////// ///////////////////
@ -1341,6 +1440,11 @@ FormElement* ConditionElement::make_generic(const Env&,
GenericOperator::make_fixed(FixedOperatorKind::GEQ), GenericOperator::make_fixed(FixedOperatorKind::GEQ),
make_cast(source_forms, types, TypeSpec("uint"), pool)); make_cast(source_forms, types, TypeSpec("uint"), pool));
case IR2_Condition::Kind::GEQ_SIGNED:
return pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::GEQ),
make_cast(source_forms, types, TypeSpec("int"), pool));
case IR2_Condition::Kind::LESS_THAN_ZERO_SIGNED: { case IR2_Condition::Kind::LESS_THAN_ZERO_SIGNED: {
auto casted = make_cast(source_forms, types, TypeSpec("int"), pool); auto casted = make_cast(source_forms, types, TypeSpec("int"), pool);
auto zero = pool.alloc_single_element_form<SimpleAtomElement>( auto zero = pool.alloc_single_element_form<SimpleAtomElement>(
@ -1415,7 +1519,13 @@ void ConditionElement::push_to_stack(const Env& env, FormPool& pool, FormStack&
vars.push_back(var); vars.push_back(var);
source_types.push_back(env.get_types_before_op(var.idx()).get(var.reg()).typespec()); source_types.push_back(env.get_types_before_op(var.idx()).get(var.reg()).typespec());
} else if (m_src[i]->is_int()) { } else if (m_src[i]->is_int()) {
source_types.push_back(TypeSpec("int")); if (m_src[i]->get_int() == 0 && condition_uses_float(m_kind)) {
// if we're doing a floating point comparison, and one of our arguments is a constant
// which is an "integer zero", treat it as a floating point zero.
source_types.push_back(TypeSpec("float"));
} else {
source_types.push_back(TypeSpec("int"));
}
} else { } else {
throw std::runtime_error("Unsupported atom in ConditionElement::push_to_stack"); throw std::runtime_error("Unsupported atom in ConditionElement::push_to_stack");
} }
@ -1639,7 +1749,7 @@ void ArrayFieldAccess::update_from_stack(const Env& env,
if (m_expected_stride == 1) { if (m_expected_stride == 1) {
// reg0 is idx // reg0 is idx
auto reg0_matcher = auto reg0_matcher =
Matcher::match_or({Matcher::any_reg(0), Matcher::cast("int", Matcher::any_reg(0))}); Matcher::match_or({Matcher::any(0), Matcher::cast("int", Matcher::any_reg(0))});
// reg1 is base // reg1 is base
auto reg1_matcher = auto reg1_matcher =
Matcher::match_or({Matcher::any_reg(1), Matcher::cast("int", Matcher::any_reg(1))}); Matcher::match_or({Matcher::any_reg(1), Matcher::cast("int", Matcher::any_reg(1))});
@ -1649,14 +1759,14 @@ void ArrayFieldAccess::update_from_stack(const Env& env,
throw std::runtime_error("Couldn't match ArrayFieldAccess (stride 1) values: " + throw std::runtime_error("Couldn't match ArrayFieldAccess (stride 1) values: " +
new_val->to_string(env)); new_val->to_string(env));
} }
auto idx = match_result.maps.regs.at(0); auto idx = match_result.maps.forms.at(0);
auto base = match_result.maps.regs.at(1); auto base = match_result.maps.regs.at(1);
assert(idx.has_value() && base.has_value()); assert(idx && base.has_value());
std::vector<DerefToken> tokens = m_deref_tokens; std::vector<DerefToken> tokens = m_deref_tokens;
for (auto& x : tokens) { for (auto& x : tokens) {
if (x.kind() == DerefToken::Kind::EXPRESSION_PLACEHOLDER) { if (x.kind() == DerefToken::Kind::EXPRESSION_PLACEHOLDER) {
x = DerefToken::make_int_expr(var_to_form(idx.value(), pool)); x = DerefToken::make_int_expr(idx);
} }
} }
// tokens.push_back(DerefToken::make_int_expr(var_to_form(idx.value(), pool))); // tokens.push_back(DerefToken::make_int_expr(var_to_form(idx.value(), pool)));
@ -1760,4 +1870,12 @@ void SimpleExpressionElement::push_to_stack(const Env&, FormPool&, FormStack& st
stack.push_form_element(this, true); stack.push_form_element(this, true);
} }
void StringConstantElement::update_from_stack(const Env&,
FormPool&,
FormStack&,
std::vector<FormElement*>* result,
bool) {
result->push_back(this);
}
} // namespace decompiler } // namespace decompiler

View file

@ -101,6 +101,9 @@ enum class FixedOperatorKind {
ABS, ABS,
MIN, MIN,
MAX, MAX,
FABS,
FMIN,
FMAX,
LOGAND, LOGAND,
LOGIOR, LOGIOR,
LOGXOR, LOGXOR,
@ -121,6 +124,7 @@ enum class FixedOperatorKind {
EQ, EQ,
NEQ, NEQ,
CONS, CONS,
METHOD_OF_OBJECT,
INVALID INVALID
}; };

View file

@ -260,6 +260,17 @@ bool is_saved_reg(Register r) {
} }
} }
bool is_possible_coloring_move(Register dst, Register src) {
if (is_arg_reg(src) && is_saved_reg(dst)) {
return true;
}
if (dst.get_kind() == Reg::FPR && dst.get_fpr() < 20 && is_arg_reg(src)) {
return true;
}
return false;
}
/*! /*!
* Create a "really crude" SSA, as described in * Create a "really crude" SSA, as described in
* "Aycock and Horspool Simple Generation of Static Single-Assignment Form" * "Aycock and Horspool Simple Generation of Static Single-Assignment Form"
@ -321,22 +332,20 @@ SSA make_rc_ssa(const Function& function, const RegUsageInfo& rui, const Functio
SSA::Ins ssa_i(op_id); SSA::Ins ssa_i(op_id);
if (block_id == 0 && !got_not_arg_coloring) { if (block_id == 0 && !got_not_arg_coloring) {
got_not_arg_coloring = true;
auto as_set = dynamic_cast<const SetVarOp*>(op.get()); auto as_set = dynamic_cast<const SetVarOp*>(op.get());
if (as_set) { if (as_set) {
if (as_set->src().is_identity() && as_set->src().get_arg(0).is_var()) { if ((as_set->src().kind() == SimpleExpression::Kind::GPR_TO_FPR ||
as_set->src().is_identity()) &&
as_set->src().get_arg(0).is_var()) {
auto src = as_set->src().get_arg(0).var().reg(); auto src = as_set->src().get_arg(0).var().reg();
auto dst = as_set->dst().reg(); auto dst = as_set->dst().reg();
if (is_arg_reg(src) && is_saved_reg(dst) && if (is_possible_coloring_move(dst, src) &&
rui.op.at(op_id).consumes.find(src) != rui.op.at(op_id).consumes.end()) { rui.op.at(op_id).consumes.find(src) != rui.op.at(op_id).consumes.end()) {
ssa_i.is_arg_coloring_move = true; ssa_i.is_arg_coloring_move = true;
} else { got_not_arg_coloring = false;
got_not_arg_coloring = true;
} }
} else {
got_not_arg_coloring = true;
} }
} else {
got_not_arg_coloring = true;
} }
} }

View file

@ -702,6 +702,22 @@
;;;;;;;;;;;; START OF GAME.CGO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;; START OF GAME.CGO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; TYPES-H ;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
(deftype time-frame (int64)
()
:flag-assert #x900000008
)
(deftype part-id (uint32)
()
:flag-assert #x900000004
)
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; MATH ;;;;;;;;;;;;;;;;;;;;;; ;;;; MATH ;;;;;;;;;;;;;;;;;;;;;;
@ -724,27 +740,36 @@
(define-extern seek (function float float float float)) (define-extern seek (function float float float float))
(define-extern lerp (function float float float float)) (define-extern lerp (function float float float float))
(define-extern lerp-scale (function float float float float float float)) (define-extern lerp-scale (function float float float float float float))
(define-extern lerp-clamp (function float float float float))
(define-extern seekl (function int int int int))
(define-extern rand-vu-init (function float float))
(define-extern rand-vu (function float))
(define-extern rand-vu-nostep (function float))
(define-extern rand-vu-float-range (function float float float))
(define-extern rand-vu-percent? (function float symbol))
(define-extern rand-vu-int-range (function int int int))
(define-extern rand-vu-int-count (function int int))
; ;;(define-extern xyzwh object) ;; unknown type ; ;;(define-extern xyzwh object) ;; unknown type
; (define-extern rand-vu-init function) ;
; (define-extern rand-vu-nostep function) ;
; (define-extern rand-vu function) ;
; (define-extern lerp function) ; (define-extern lerp function)
; (define-extern lerp-scale function) ; (define-extern lerp-scale function)
; (define-extern rand-vu-int-range function) ;
; (define-extern lerp-clamp function)
; ;;(define-extern random-generator object) ;; unknown type ; ;;(define-extern random-generator object) ;; unknown type
; (define-extern integral? function) ; (define-extern integral? function)
; (define-extern rand-uint31-gen function) ; (define-extern rand-uint31-gen function)
; (define-extern rand-vu-int-count function) ;
; (define-extern fractional-part function) ; (define-extern fractional-part function)
; (define-extern seek function) ; (define-extern seek function)
; ;;(define-extern xyzw object) ;; unknown type ; ;;(define-extern xyzw object) ;; unknown type
; (define-extern rand-vu-percent? function) ;
; ;;(define-extern *random-generator* object) ;; unknown type ; ;;(define-extern *random-generator* object) ;; unknown type
; (define-extern rand-vu-float-range function) ;
; ;;(define-extern rgba object) ;; unknown type ; ;;(define-extern rgba object) ;; unknown type
; (define-extern seekl function) ;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
@ -757,16 +782,18 @@
(deftype bit-array (basic) (deftype bit-array (basic)
((length int32 :offset-assert 4) ((length int32 :offset-assert 4)
(allocated-length int32 :offset-assert 8) (allocated-length int32 :offset-assert 8)
(_pad uint8) (_pad uint8 :offset-assert 12)
(bytes uint8 :dynamic :offset 12)
) )
:method-count-assert 13 :method-count-assert 13
:size-assert #xd :size-assert #xd
:flag-assert #xd0000000d :flag-assert #xd0000000d
(:methods (:methods
(dummy-9 () none 9) (new (symbol type int) _type_ 0)
(dummy-10 () none 10) (get-bit (_type_ int) symbol 9)
(dummy-11 () none 11) (clear-bit (_type_ int) int 10)
(dummy-12 () none 12) (set-bit (_type_ int) int 11)
(clear (_type_) _type_ 12)
) )
) )
@ -997,6 +1024,9 @@
) )
;; todo isphere ;; todo isphere
(deftype isphere (vec4s)
()
)
(deftype box8s (structure) (deftype box8s (structure)
((data float 8 :offset-assert 0) ((data float 8 :offset-assert 0)
@ -1103,8 +1133,12 @@
(define-extern *zero-vector* vector) (define-extern *zero-vector* vector)
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; BOUNDING-BOX-H ;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;; bounding-box-h
(deftype bounding-box (structure) (deftype bounding-box (structure)
((min vector :inline :offset-assert 0) ((min vector :inline :offset-assert 0)
(max vector :inline :offset-assert 16) (max vector :inline :offset-assert 16)
@ -1123,7 +1157,6 @@
) )
) )
;; bounding-box-h
(deftype bounding-box4w (structure) (deftype bounding-box4w (structure)
((min vector4w :inline :offset-assert 0) ((min vector4w :inline :offset-assert 0)
(max vector4w :inline :offset-assert 16) (max vector4w :inline :offset-assert 16)
@ -1133,7 +1166,6 @@
:flag-assert #x900000020 :flag-assert #x900000020
) )
;; bounding-box-h
(deftype bounding-box-both (structure) (deftype bounding-box-both (structure)
((box bounding-box :inline :offset-assert 0) ((box bounding-box :inline :offset-assert 0)
(box4w bounding-box4w :inline :offset-assert 32) (box4w bounding-box4w :inline :offset-assert 32)
@ -1143,7 +1175,12 @@
:flag-assert #x900000040 :flag-assert #x900000040
) )
;; matrix-h ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; MATRIX-H ;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
(deftype matrix (structure) (deftype matrix (structure)
((data float 16 :offset-assert 0) ((data float 16 :offset-assert 0)
(vector vector 4 :offset 0) (vector vector 4 :offset 0)
@ -1167,7 +1204,6 @@
:flag-assert #x900000030 :flag-assert #x900000030
) )
;; guess on signs here
(deftype matrix4h (structure) (deftype matrix4h (structure)
((data int16 16 :offset-assert 0) ((data int16 16 :offset-assert 0)
(vector4h vector4h 4 :offset 0) (vector4h vector4h 4 :offset 0)
@ -1178,6 +1214,13 @@
:flag-assert #x900000020 :flag-assert #x900000020
) )
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; QUATERNION-H ;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
(deftype quaternion (structure) (deftype quaternion (structure)
((data float 4 :offset-assert 0) ((data float 4 :offset-assert 0)
(x float :offset 0) (x float :offset 0)
@ -1192,7 +1235,12 @@
:flag-assert #x900000010 :flag-assert #x900000010
) )
;; euler-h ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; EULER-H ;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
(deftype euler-angles (vector) (deftype euler-angles (vector)
() ()
:method-count-assert 9 :method-count-assert 9
@ -1200,7 +1248,12 @@
:flag-assert #x900000010 :flag-assert #x900000010
) )
;; transform-h ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; TRANSFORM-H ;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
(deftype transform (structure) (deftype transform (structure)
((trans vector :inline :offset-assert 0) ((trans vector :inline :offset-assert 0)
(rot vector :inline :offset-assert 16) (rot vector :inline :offset-assert 16)
@ -1211,7 +1264,6 @@
:flag-assert #x900000030 :flag-assert #x900000030
) )
;; transform-h
(deftype trs (basic) (deftype trs (basic)
((trans vector :inline :offset-assert 16) ((trans vector :inline :offset-assert 16)
(rot vector :inline :offset-assert 32) (rot vector :inline :offset-assert 32)
@ -1222,7 +1274,12 @@
:flag-assert #x900000040 :flag-assert #x900000040
) )
;; geometry-h ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; GEOMETRY-H ;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
(deftype curve (structure) (deftype curve (structure)
((cverts uint32 :offset-assert 0) ((cverts uint32 :offset-assert 0)
(num-cverts int32 :offset-assert 4) (num-cverts int32 :offset-assert 4)
@ -1235,7 +1292,6 @@
:flag-assert #x900000014 :flag-assert #x900000014
) )
;; geometry-h
(deftype border-plane (basic) (deftype border-plane (basic)
((name basic :offset-assert 4) ((name basic :offset-assert 4)
(action basic :offset-assert 8) (action basic :offset-assert 8)
@ -1252,7 +1308,12 @@
) )
) )
; ;; transformq-h ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; TRANSFORMQ-H ;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
(deftype transformq (transform) (deftype transformq (transform)
((quat quaternion :inline :offset 16) ((quat quaternion :inline :offset 16)
) )
@ -1269,8 +1330,6 @@
:flag-assert #x900000040 :flag-assert #x900000040
) )
;; transformq-h
(deftype trsqv (trsq) (deftype trsqv (trsq)
((pause-adjust-distance float :offset 4) ;; todo meters ((pause-adjust-distance float :offset 4) ;; todo meters
(nav-radius float :offset 8) ;; todo meters (nav-radius float :offset 8) ;; todo meters
@ -1307,6 +1366,15 @@
) )
) )
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; BOUNDING-BOX ;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
(define-extern box-vector-inside? (function bounding-box vector symbol))
(define-extern box-vector-enside? (function bounding-box vector symbol))
(deftype sound-rpc-cmd (structure) (deftype sound-rpc-cmd (structure)
((rsvd1 uint16 :offset-assert 0) ((rsvd1 uint16 :offset-assert 0)
(command uint16 :offset-assert 2) (command uint16 :offset-assert 2)
@ -31505,8 +31573,7 @@
;;(define-extern trsq object) ;; unknown type ;;(define-extern trsq object) ;; unknown type
(define-extern deg-diff function) (define-extern deg-diff function)
(define-extern vector-y-angle function) (define-extern vector-y-angle function)
(define-extern box-vector-inside? function)
(define-extern box-vector-enside? function)
(define-extern vector3s-rotate*! function) (define-extern vector3s-rotate*! function)
(define-extern matrix-inverse-of-rot-trans! function) (define-extern matrix-inverse-of-rot-trans! function)
(define-extern matrix-3x3-determinant function) (define-extern matrix-3x3-determinant function)

View file

@ -61,10 +61,6 @@
[3, ["v1", "int"], ["a0", "int"]] [3, ["v1", "int"], ["a0", "int"]]
], ],
"remove-exit":[
[0, ["s6", "process"]]
],
"(method 0 process)":[ "(method 0 process)":[
[12, ["a0", "int"]], [12, ["a0", "int"]],
[13, ["v0", "process"]] [13, ["v0", "process"]]

View file

@ -192,6 +192,46 @@
"valid?":{ "valid?":{
"args":["obj", "expected-type", "name", "allow-false", "print-dest"], "args":["obj", "expected-type", "name", "allow-false", "print-dest"],
"vars":{"v1-1":"in-goal-mem"} "vars":{"v1-1":"in-goal-mem"}
},
"seek":{
"args":["x", "target", "diff"],
"vars":{"f2-0":"err"}
},
"lerp":{
"args":["minimum", "maximum", "amount"]
},
"lerp-scale":{
"args":["min-out", "max-out", "in", "min-in", "max-in"],
"vars":{"f0-1":"scale"}
},
"lerp-clamp":{
"args":["minimum", "maximum", "amount"]
},
"rand-vu-int-range":{
"args":["first", "second"],
"vars":{"f0-4":"float-in-range"}
},
"(method 0 bit-array)":{
"args":["allocation", "type-to-make", "length"],
"vars":{"v0-0":"obj"}
},
"(method 12 bit-array)":{
"vars":{"v1-2":"idx"}
},
"box-vector-enside?":{
"args":["box", "pt"]
},
"box-vector-inside?":{
"args":["box", "pt"]
} }

View file

@ -173,7 +173,7 @@ TP_Type DecompilerTypeSystem::tp_lca(const TP_Type& existing,
bool* changed) const { bool* changed) const {
// starting from most vague to most specific // starting from most vague to most specific
// simplist case, no difference. // simplest case, no difference.
if (existing == add) { if (existing == add) {
*changed = false; *changed = false;
return existing; return existing;
@ -252,6 +252,18 @@ TP_Type DecompilerTypeSystem::tp_lca(const TP_Type& existing,
*changed = true; *changed = true;
return TP_Type::make_from_ts(TypeSpec("string")); return TP_Type::make_from_ts(TypeSpec("string"));
} }
case TP_Type::Kind::INTEGER_CONSTANT_PLUS_VAR:
if (existing.get_integer_constant() == add.get_integer_constant()) {
auto new_child = TP_Type::make_from_integer_constant_plus_var(
existing.get_integer_constant(),
coerce_to_reg_type(ts.lowest_common_ancestor(existing.get_objects_typespec(),
add.get_objects_typespec())));
*changed = (new_child != existing);
return new_child;
} else {
*changed = true;
return TP_Type::make_from_ts("int");
}
case TP_Type::Kind::FALSE_AS_NULL: case TP_Type::Kind::FALSE_AS_NULL:
case TP_Type::Kind::UNINITIALIZED: case TP_Type::Kind::UNINITIALIZED:

View file

@ -47,6 +47,8 @@ std::string TP_Type::print() const {
return fmt::format("<string with {} args>", m_int); return fmt::format("<string with {} args>", m_int);
case Kind::INTEGER_CONSTANT: case Kind::INTEGER_CONSTANT:
return fmt::format("<integer {}>", m_int); return fmt::format("<integer {}>", m_int);
case Kind::INTEGER_CONSTANT_PLUS_VAR:
return fmt::format("<integer {} + {}>", m_int, m_ts.print());
case Kind::DYNAMIC_METHOD_ACCESS: case Kind::DYNAMIC_METHOD_ACCESS:
return fmt::format("<dynamic-method-access>"); return fmt::format("<dynamic-method-access>");
case Kind::INVALID: case Kind::INVALID:
@ -81,6 +83,8 @@ bool TP_Type::operator==(const TP_Type& other) const {
return m_int == other.m_int; return m_int == other.m_int;
case Kind::FORMAT_STRING: case Kind::FORMAT_STRING:
return m_int == other.m_int; return m_int == other.m_int;
case Kind::INTEGER_CONSTANT_PLUS_VAR:
return m_int == other.m_int && m_ts == other.m_ts;
case Kind::DYNAMIC_METHOD_ACCESS: case Kind::DYNAMIC_METHOD_ACCESS:
return true; return true;
case Kind::INVALID: case Kind::INVALID:
@ -119,6 +123,8 @@ TypeSpec TP_Type::typespec() const {
return TypeSpec("string"); return TypeSpec("string");
case Kind::INTEGER_CONSTANT: case Kind::INTEGER_CONSTANT:
return TypeSpec("int"); return TypeSpec("int");
case Kind::INTEGER_CONSTANT_PLUS_VAR:
return m_ts;
case Kind::DYNAMIC_METHOD_ACCESS: case Kind::DYNAMIC_METHOD_ACCESS:
return TypeSpec("object"); return TypeSpec("object");
case Kind::FORMAT_STRING: case Kind::FORMAT_STRING:

View file

@ -21,11 +21,13 @@ class TP_Type {
UNINITIALIZED, // representing data which is uninitialized. UNINITIALIZED, // representing data which is uninitialized.
PRODUCT_WITH_CONSTANT, // representing: (val * multiplier) PRODUCT_WITH_CONSTANT, // representing: (val * multiplier)
OBJECT_PLUS_PRODUCT_WITH_CONSTANT, // address: obj + (val * multiplier) OBJECT_PLUS_PRODUCT_WITH_CONSTANT, // address: obj + (val * multiplier)
OBJECT_NEW_METHOD, // the method new of object, as used in an (object-new) or similar. OBJECT_NEW_METHOD, // the method new of object, as used in an (object-new) or similar.
STRING_CONSTANT, // a string that's part of the string pool STRING_CONSTANT, // a string that's part of the string pool
FORMAT_STRING, // a string with a given number of format arguments FORMAT_STRING, // a string with a given number of format arguments
INTEGER_CONSTANT, // a constant integer. INTEGER_CONSTANT, // a constant integer.
DYNAMIC_METHOD_ACCESS, // partial access into a INTEGER_CONSTANT_PLUS_VAR, // constant + variable. used in stuff like (&-> obj inline-val-arr
// x)
DYNAMIC_METHOD_ACCESS, // partial access into a
INVALID INVALID
} kind = Kind::UNINITIALIZED; } kind = Kind::UNINITIALIZED;
TP_Type() = default; TP_Type() = default;
@ -104,6 +106,14 @@ class TP_Type {
return result; return result;
} }
static TP_Type make_from_integer_constant_plus_var(int64_t value, const TypeSpec& var_type) {
TP_Type result;
result.kind = Kind::INTEGER_CONSTANT_PLUS_VAR;
result.m_int = value;
result.m_ts = var_type;
return result;
}
static TP_Type make_from_product(int64_t multiplier, bool is_signed) { static TP_Type make_from_product(int64_t multiplier, bool is_signed) {
TP_Type result; TP_Type result;
result.kind = Kind::PRODUCT_WITH_CONSTANT; result.kind = Kind::PRODUCT_WITH_CONSTANT;
@ -134,7 +144,7 @@ class TP_Type {
} }
const TypeSpec& get_objects_typespec() const { const TypeSpec& get_objects_typespec() const {
assert(kind == Kind::TYPESPEC); assert(kind == Kind::TYPESPEC || kind == Kind::INTEGER_CONSTANT_PLUS_VAR);
return m_ts; return m_ts;
} }
@ -159,7 +169,7 @@ class TP_Type {
} }
uint64_t get_integer_constant() const { uint64_t get_integer_constant() const {
assert(kind == Kind::INTEGER_CONSTANT); assert(kind == Kind::INTEGER_CONSTANT || kind == Kind::INTEGER_CONSTANT_PLUS_VAR);
return m_int; return m_int;
} }

View file

@ -13,3 +13,14 @@
- Many more useless `set!`s will be removed - Many more useless `set!`s will be removed
- Stores into arrays are supported - Stores into arrays are supported
- Fixed bug where unused/eliminated temporaries would sometimes be used as the result of a block - Fixed bug where unused/eliminated temporaries would sometimes be used as the result of a block
## Version 3
- Normal use of the process pointer will now show up as `pp`. Weird use will still be weird.
- `(method-of-object ...)` will now be recognized
- Accessing the address of a variable element of an inline array is supported in some cases. More examples are needed before all work. But for example: `(&-> v0-0 stack (-> v0-0 allocated-length))` where `stack` is an `uint8 :dynamic`.
- Cleaned up unneeded casts floating point constant `0.0`.
- Support for `fmin`/`fmax`
- Fixed a bug where integer `abs` appeared instead of `fabs`.
- Some support for float -> integer conversions, but it is not 100% yet.
- Eliminate inserted coloring moves for function arguments that use `mtc1`.
- Support for `>=` for signed numbers.

View file

@ -5,3 +5,26 @@
;; name in dgo: bounding-box ;; name in dgo: bounding-box
;; dgos: GAME, ENGINE ;; dgos: GAME, ENGINE
(defun box-vector-enside? ((box bounding-box) (pt vector))
"Is the point in the box? On the edge doesn't count"
(and
(< (-> box min data 0) (-> pt data 0))
(< (-> box min data 1) (-> pt data 1))
(< (-> box min data 2) (-> pt data 2))
(< (-> pt data 0) (-> box max data 0))
(< (-> pt data 1) (-> box max data 1))
(< (-> pt data 2) (-> box max data 2))
)
)
(defun box-vector-inside? ((box bounding-box) (pt vector))
"Is the point in the box? On the edge counts."
(and
(>= (-> pt data 0) (-> box min data 0))
(>= (-> pt data 1) (-> box min data 1))
(>= (-> pt data 2) (-> box min data 2))
(>= (-> box max data 0) (-> pt data 0))
(>= (-> box max data 1) (-> pt data 1))
(>= (-> box max data 2) (-> pt data 2))
)
)

View file

@ -5,3 +5,14 @@
;; name in dgo: euler-h ;; name in dgo: euler-h
;; dgos: GAME, ENGINE ;; dgos: GAME, ENGINE
;; maybe euler angle storage orders?
(define EulSafe (new 'static 'boxed-array int32 4 0 1 2 0))
(define EulNext (new 'static 'boxed-array int32 4 1 2 0 1))
;; just uses the same xyzw and data array as vector.
(deftype euler-angles (vector)
()
:method-count-assert 9
:size-assert #x10
:flag-assert #x900000010
)

View file

@ -43,26 +43,61 @@
(- x (truncate x)) (- x (truncate x))
) )
;; todo, rgba, xyzw, xyzwh ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; bitfield types
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO: verify these are in the right order.
;; maybe used in timer-h for the color field of profile-frame?
(deftype rgba (uint32)
((r uint8 :offset 0)
(g uint8 :offset 8)
(b uint8 :offset 16)
(a uint8 :offset 24)
)
:flag-assert #x900000004
)
;; TODO: fields
(deftype xyzw (uint128)
()
:flag-assert #x900000010
)
;; TODO: fields
(deftype xyzwh (uint128)
()
:flag-assert #x900000010
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; utility functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun log2 ((x int)) (defun log2 ((x int))
"Straight out of Bit Twiddling Hacks graphics.stanford.edu" "Straight out of Bit Twiddling Hacks graphics.stanford.edu.
(- (sar (the-as integer (the float x)) 23) 127) This website is old enough that they possibly used this back
in 1999."
(- (sar (the-as int (the float x)) 23) 127)
) )
(defun seek ((x float) (target float) (diff float)) (defun seek ((x float) (target float) (diff float))
"Move x toward target by at most diff, with floats" "Move x toward target by at most diff, with floats"
(let ((err (- target x))) (let ((err (- target x)))
;; if we can get there all at once (cond
(if (<= (fabs err) diff) ((>= diff (fabs err))
(return-from #f target) ;; can get there in one go
target
)
((>= err 0)
;; increase
(+ x diff)
)
(else
(- x diff)
)
) )
)
(if (>= err 0)
(+ x diff)
(- x diff)
)
)
) )
(defun lerp ((minimum float) (maximum float) (amount float)) (defun lerp ((minimum float) (maximum float) (amount float))
@ -83,19 +118,19 @@
) )
(defun lerp-clamp ((minimum float) (maximum float) (amount float)) (defun lerp-clamp ((minimum float) (maximum float) (amount float))
"Interpolate between minimum and maximum. Clamp output. "Interpolate between minimum and maximum, but saturate the amount to [0, 1]"
For some reason, the interpolate here is done in a less efficient way than lerp." (cond
(if (<= amount 0.0) ((>= 0.0 amount)
(return-from #f minimum) minimum)
) ((>= amount 1.0)
maximum)
(if (>= amount 1.0) (else
(return-from #f maximum) ;; the implementation in lerp uses fewer operations...
) (+ (* (- 1.0 amount) minimum)
;; lerp computes this part, but more efficiently (* amount maximum)
(+ (* (- 1.0 amount) minimum) )
(* amount maximum)
) )
)
) )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -117,10 +152,107 @@
) )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; random vu ;;;; random vu hardware
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; added in OpenGOAL
(define *_vu-reg-R_* 0)
;; TODO ;; TODO
(defun rand-vu-init ((seed float))
"Initialize the VU0 random generator"
;; (.ctc2.i R arg0)
;; (.cfc2.i v0 R)
(set! *_vu-reg-R_*
(logior #x3F800000 (logand (the-as int seed) #x007FFFFF))
)
(the-as float *_vu-reg-R_*)
)
(rand-vu-init 1.418091058731079)
;; rand-vu
(defun rand-vu ()
"Get a random number in [0, 1) and advance the random generator."
;; (.vrget.xyzw vf1) - get current random
(let ((current-random *_vu-reg-R_*))
;; here they update the random generate with some junk
;; for now, we don't do this in OpenGOAL.
;; (.vsqrty Q vf1)
;; (.vaddq.x vf2 vf0 Q) ;; you're not allowed to do this!
;; (.vrxorw vf2)
;; and advance
;; (.vrnext.xyzw vf1)
(let ((x (logand 1 (shr current-random 4)))
(y (logand 1 (shr current-random 22)))
)
(set! current-random (shl current-random 1))
(set! current-random (logxor current-random (logxor x y)))
(set! *_vu-reg-R_* (logior #x3f800000 (logand current-random #x7fffff)))
)
)
;; (.vsubw.xyzw vf1 vf1 vf0)
;; (.qmfc2.i v0 vf1)
(- (the-as float *_vu-reg-R_*) 1.0)
)
;; rand-vu-nostep
(defun rand-vu-nostep ()
"Get the number currently in the random generator.
This will be equal to the last call of (rand-vu)
This will not update the random generator"
(- (the-as float *_vu-reg-R_*) 1.0)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; random vu utilities
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun rand-vu-float-range ((minimum float) (maximum float))
"Get a random number in the given range.
TODO: is this inclusive? I think it's [min, max)"
(+ minimum (* (rand-vu) (- maximum minimum)))
)
(defun rand-vu-percent? ((prob float))
"Get a boolean that's true with the given probability."
(>= prob (rand-vu))
)
(defun rand-vu-int-range ((first int) (second int))
"Get an integer the given range. Inclusive of both?
It looks like they actually did this right??"
(local-vars (float-in-range float))
;; increment the larger of the range, so it is inclusive
;; (we should have (max - min) + 1 buckets)
(if (< first second)
(set! second (+ second 1))
(set! first (+ first 1))
)
;; get a float in the range
(set! float-in-range
(rand-vu-float-range (the float first) (the float second))
)
;; negatives round up and positives round down.
;; but we want all to round consistently, so we subtract one from negatives.
(when (< float-in-range 0)
(set! float-in-range (+ -1.0 float-in-range))
)
;; and back to integer.
(the int float-in-range)
)
(defun rand-vu-int-count ((maximum int))
"Get an integer in [0, maximum)"
(the int (* (rand-vu) (the float maximum)))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; terrible random integer generator ;;;; terrible random integer generator
@ -166,4 +298,4 @@
(set! (-> gen seed) result) (set! (-> gen seed) result)
result result
) )
) )

View file

@ -5,3 +5,61 @@
;; name in dgo: transformq-h ;; name in dgo: transformq-h
;; dgos: GAME, ENGINE ;; dgos: GAME, ENGINE
;; transforms, but using quaternions to represent rotations.
(deftype transformq (transform)
;; this overlays the rot field of transform.
((quat quaternion :inline :offset 16)
)
:method-count-assert 9
:size-assert #x30
:flag-assert #x900000030
)
(deftype trsq (trs)
;; this overlays the rot field of trs.
((quat quaternion :inline :offset 32)
)
:method-count-assert 9
:size-assert #x40
:flag-assert #x900000040
)
;; Representing a translate/rotate/scale with a quaternion and a velocity.
(deftype trsqv (trsq)
((pause-adjust-distance float :offset 4) ;; todo meters
(nav-radius float :offset 8) ;; todo meters
(transv vector :inline :offset-assert 64)
(rotv vector :inline :offset-assert 80)
(scalev vector :inline :offset-assert 96)
(dir-targ quaternion :inline :offset-assert 112)
(angle-change-time uint64 :offset-assert 128)
(old-y-angle-diff float :offset-assert 136)
)
:method-count-assert 28
:size-assert #x8c
:flag-assert #x1c0000008c
(:methods
(dummy-9 () none 9)
(dummy-10 () none 10)
(dummy-11 () none 11)
(dummy-12 () none 12)
(dummy-13 () none 13)
(dummy-14 () none 14)
(dummy-15 () none 15)
(dummy-16 () none 16)
(dummy-17 () none 17)
(dummy-18 () none 18)
(dummy-19 () none 19)
(dummy-20 () none 20)
(dummy-21 () none 21)
(dummy-22 () none 22)
(dummy-23 () none 23)
(dummy-24 () none 24)
(dummy-25 () none 25)
(dummy-26 () none 26)
(dummy-27 () none 27)
)
)
;; todo trsqv methods 23 and 24

View file

@ -5,3 +5,4 @@
;; name in dgo: trigonometry-h ;; name in dgo: trigonometry-h
;; dgos: GAME, ENGINE ;; dgos: GAME, ENGINE
;; this file generates no code.

View file

@ -12,27 +12,96 @@
(deftype bit-array (basic) (deftype bit-array (basic)
((length int32 :offset-assert 4) ((length int32 :offset-assert 4)
(allocated-length int32 :offset-assert 8) (allocated-length int32 :offset-assert 8)
(_pad uint8) ;; neither of these show up in the inspect.
;; it seems like there's a single byte of data array
;; included in the type already
(_pad uint8 :offset-assert 12)
(bytes uint8 :dynamic :offset 12)
) )
:method-count-assert 13 :method-count-assert 13
:size-assert #xd :size-assert #xd
:flag-assert #xd0000000d :flag-assert #xd0000000d
(:methods (:methods
(dummy-9 () none 9) (new (symbol type int) _type_ 0)
(dummy-10 () none 10) (get-bit (_type_ int) symbol 9)
(dummy-11 () none 11) (clear-bit (_type_ int) int 10)
(dummy-12 () none 12) (set-bit (_type_ int) int 11)
) (clear (_type_) _type_ 12)
)
) )
;; todo new (defmethod new bit-array ((allocation symbol) (type-to-make type) (length int))
;; todo 4 "Allocate a new bit-array which can hold length bits.
;; todo 5 Sets both the length and the allocated-length to this length."
;; todo 9 (local-vars (obj bit-array))
;; todo 10 ;; remove one byte, we get one in the type already.
;; todo 11 (set! obj (object-new allocation type-to-make
;; todo 12 (+ (+ (sar (logand -8 (+ length 7)) 3) -1)
;; (the-as int (-> type-to-make size))
)
)
)
(set! (-> obj length) length)
(set! (-> obj allocated-length) length)
obj
)
(defmethod length bit-array ((obj bit-array))
"Get the length (in bits)"
(-> obj length)
)
(defmethod asize-of bit-array ((obj bit-array))
"Get the size in memory.
It is wrong and says its one bit longer, which is safe."
(the-as int
(+ (-> obj type size)
(the-as uint (sar (logand -8 (+ (-> obj allocated-length) 7)) 3))
)
)
)
(defmethod get-bit bit-array ((obj bit-array) (idx int))
"Is the bit at idx set or not?"
(local-vars (byte uint))
;; read the byte
(set! byte (-> obj bytes (sar idx 3)))
(nonzero? (logand byte (the-as uint (ash 1 (logand idx 7)))))
)
(defmethod clear-bit bit-array ((obj bit-array) (idx int))
"Clear the bit at position idx"
(set! (-> obj bytes (sar idx 3))
(logand (-> obj bytes (sar idx 3))
(the-as uint (lognot (ash 1 (logand idx 7))))
)
)
0
)
(defmethod set-bit bit-array ((obj bit-array) (idx int))
"Set the bit at position idx"
(set! (-> obj bytes (sar idx 3))
(logior (-> obj bytes (sar idx 3))
(the-as uint (ash 1 (logand idx 7)))
)
)
0
)
(defmethod clear bit-array ((obj bit-array))
"Set all bits to zero."
(local-vars (idx int))
(let ((idx (sar (logand -8 (+ (-> obj allocated-length) 7)) 3)))
(while (nonzero? idx)
(set! idx (+ idx -1))
(nop!)
(nop!)
(set! (-> obj bytes idx) 0)
)
obj
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; vector types (integer) ;; vector types (integer)
@ -315,13 +384,13 @@
:flag-assert #x900000010 :flag-assert #x900000010
) )
; todo
; (deftype isphere (vec4s) (deftype isphere (vec4s)
; () ()
; :method-count-assert 9 :method-count-assert 9
; :size-assert #x10 :size-assert #x10
; :flag-assert #x900000010 :flag-assert #x900000010
; ) )
(deftype box8s (structure) (deftype box8s (structure)
((data float 8 :offset-assert 0) ((data float 8 :offset-assert 0)
@ -543,4 +612,4 @@
dst dst
) )
(define *zero-vector* (new 'static 'vector :x 0. :y 0. :z 0. :w 0.)) (define *zero-vector* (new 'static 'vector :x 0. :y 0. :z 0. :w 0.))

View file

@ -140,7 +140,8 @@ class Compiler {
Val* compile_stack_new(const goos::Object& form, Val* compile_stack_new(const goos::Object& form,
const goos::Object& type, const goos::Object& type,
const goos::Object* rest, const goos::Object* rest,
Env* env); Env* env,
bool call_constructor);
TypeSystem m_ts; TypeSystem m_ts;
std::unique_ptr<GlobalEnv> m_global_env = nullptr; std::unique_ptr<GlobalEnv> m_global_env = nullptr;

View file

@ -785,7 +785,8 @@ Val* Compiler::compile_static_new(const goos::Object& form,
Val* Compiler::compile_stack_new(const goos::Object& form, Val* Compiler::compile_stack_new(const goos::Object& form,
const goos::Object& type, const goos::Object& type,
const goos::Object* rest, const goos::Object* rest,
Env* env) { Env* env,
bool call_constructor) {
auto type_of_object = parse_typespec(unquote(type)); auto type_of_object = parse_typespec(unquote(type));
auto fe = get_parent_env_of_type<FunctionEnv>(env); auto fe = get_parent_env_of_type<FunctionEnv>(env);
if (type_of_object == TypeSpec("inline-array") || type_of_object == TypeSpec("array")) { if (type_of_object == TypeSpec("inline-array") || type_of_object == TypeSpec("array")) {
@ -841,20 +842,30 @@ Val* Compiler::compile_stack_new(const goos::Object& form,
// allocation // allocation
auto mem = fe->allocate_aligned_stack_variable(type_of_object, ti->get_size_in_memory(), 16) auto mem = fe->allocate_aligned_stack_variable(type_of_object, ti->get_size_in_memory(), 16)
->to_gpr(env); ->to_gpr(env);
// the new method actual takes a "symbol" according the type system. So we have to cheat it. if (call_constructor) {
mem->set_type(TypeSpec("symbol")); // the new method actual takes a "symbol" according the type system. So we have to cheat it.
args.push_back(mem); mem->set_type(TypeSpec("symbol"));
// type args.push_back(mem);
args.push_back(compile_get_symbol_value(form, type_of_object.base_type(), env)->to_reg(env)); // type
// the other arguments args.push_back(compile_get_symbol_value(form, type_of_object.base_type(), env)->to_reg(env));
for_each_in_list(*rest, [&](const goos::Object& o) { // the other arguments
args.push_back(compile_error_guard(o, env)->to_reg(env)); for_each_in_list(*rest, [&](const goos::Object& o) {
}); args.push_back(compile_error_guard(o, env)->to_reg(env));
});
auto new_method = compile_get_method_of_type(form, type_of_object, "new", env); auto new_method = compile_get_method_of_type(form, type_of_object, "new", env);
auto new_obj = compile_real_function_call(form, new_method, args, env); auto new_obj = compile_real_function_call(form, new_method, args, env);
new_obj->set_type(type_of_object); new_obj->set_type(type_of_object);
return new_obj; return new_obj;
} else {
if (ti->get_offset()) {
throw std::runtime_error("Cannot stack allocate with no constructor for a " +
ti->get_name());
} else {
mem->set_type(type_of_object);
return mem;
}
}
} }
} }
@ -872,7 +883,9 @@ Val* Compiler::compile_new(const goos::Object& form, const goos::Object& _rest,
// put in code. // put in code.
return compile_static_new(form, type, rest, env); return compile_static_new(form, type, rest, env);
} else if (allocation == "stack") { } else if (allocation == "stack") {
return compile_stack_new(form, type, rest, env); return compile_stack_new(form, type, rest, env, true);
} else if (allocation == "stack-no-constructor") {
return compile_stack_new(form, type, rest, env, false);
} }
throw_compiler_error(form, "Unsupported new form"); throw_compiler_error(form, "Unsupported new form");

View file

@ -24,6 +24,7 @@ add_executable(goalc-test
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_FormExpressionBuildLong.cpp ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_FormExpressionBuildLong.cpp
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_InstructionParser.cpp ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_InstructionParser.cpp
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_gkernel_decomp.cpp ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_gkernel_decomp.cpp
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_math_decomp.cpp
${GOALC_TEST_FRAMEWORK_SOURCES} ${GOALC_TEST_FRAMEWORK_SOURCES}
${GOALC_TEST_CASES}) ${GOALC_TEST_CASES})

View file

@ -276,3 +276,477 @@ TEST_F(FormRegressionTest, ExprMethod9Thread) {
" )"; " )";
test_with_expr(func, type, expected, false, "", {{"L342", "1 ~A ~%"}, {"L341", "2 ~A ~%"}}); test_with_expr(func, type, expected, false, "", {{"L342", "1 ~A ~%"}, {"L341", "2 ~A ~%"}});
} }
TEST_F(FormRegressionTest, ExprMethod0Thread) {
std::string func =
" sll r0, r0, 0\n"
" lwu v1, 44(a2)\n"
" beq s7, v1, L266\n"
" sll r0, r0, 0\n"
" daddiu v0, t1, -7164\n"
" beq r0, r0, L267\n"
" sll r0, r0, 0\n"
"L266:\n"
" addiu v1, r0, -16\n"
" lwu a0, 84(a2)\n"
" daddiu a0, a0, 15\n"
" and v1, v1, a0\n"
" lhu a0, 8(a1)\n"
" daddu a0, v1, a0\n"
" daddu a0, a0, t0\n"
" sw a0, 84(a2)\n"
" daddiu v0, v1, 4\n"
"L267:\n"
" sw a1, -4(v0)\n"
" sw a3, 0(v0)\n"
" sw a2, 4(v0)\n"
" sw t1, 24(v0)\n"
" sw t1, 28(v0)\n"
" lwu v1, 44(a2)\n"
" sw v1, 8(v0)\n"
" sw v0, 44(a2)\n"
" lwu v1, -4(v0)\n"
" lwu v1, 56(v1)\n"
" sw v1, 12(v0)\n"
" lwu v1, -4(v0)\n"
" lwu v1, 60(v1)\n"
" sw v1, 16(v0)\n"
" sw t0, 32(v0)\n"
" jr ra\n"
" daddu sp, sp, r0";
std::string type = "(function symbol type process symbol int pointer thread)";
std::string expected =
"(begin\n"
" (set!\n"
" v0-0\n"
" (cond\n"
" ((-> arg2 top-thread) (+ arg5 (the-as uint -7164)))\n"
" (else\n"
" (set!\n"
" v1-2\n"
" (logand -16 (the-as int (+ (-> arg2 heap-cur) (the-as uint 15))))\n"
" )\n"
" (set! (-> arg2 heap-cur) (+ (+ v1-2 (the-as int (-> arg1 size))) arg4))\n"
" (+ v1-2 4)\n"
" )\n"
" )\n"
" )\n"
" (set! (-> (the-as cpu-thread v0-0) type) arg1)\n"
" (set! (-> v0-0 name) arg3)\n"
" (set! (-> v0-0 process) arg2)\n"
" (set! (-> v0-0 sp) arg5)\n"
" (set! (-> v0-0 stack-top) arg5)\n"
" (set! (-> v0-0 previous) (-> arg2 top-thread))\n"
" (set! (-> arg2 top-thread) v0-0)\n"
// TODO - we could make this method access nicer.
" (set! (-> v0-0 suspend-hook) (method-of-object v0-0 thread-suspend))\n"
" (set! (-> v0-0 resume-hook) (method-of-object v0-0 thread-resume))\n"
" (set! (-> v0-0 stack-size) arg4)\n"
" v0-0\n"
" )";
test_with_expr(func, type, expected, false, "cpu-thread", {},
parse_hint_json("[[13, [\"v0\", \"cpu-thread\"]]]"));
}
TEST_F(FormRegressionTest, ExprMethod5CpuThread) {
std::string func =
" sll r0, r0, 0\n"
"L264:\n"
" lwu v1, -4(a0)\n"
" lhu v1, 8(v1)\n"
" lw a0, 32(a0)\n"
" daddu v0, v1, a0\n"
" jr ra\n"
" daddu sp, sp, r0";
std::string type = "(function cpu-thread int)";
std::string expected = "(the-as int (+ (-> arg0 type size) (the-as uint (-> arg0 stack-size))))";
test_with_expr(func, type, expected, false);
}
TEST_F(FormRegressionTest, RemoveExit) {
std::string func =
" sll r0, r0, 0\n"
"L262:\n"
" lwu v1, 88(s6)\n"
" beq s7, v1, L263\n"
" or v0, s7, r0\n"
" lwu v1, 88(s6)\n"
" lwu v0, 4(v1)\n"
" sw v0, 88(s6)\n"
"L263:\n"
" jr ra\n"
" daddu sp, sp, r0";
std::string type = "(function stack-frame)";
std::string expected =
"(when\n"
" (-> pp stack-frame-top)\n"
" (set! v0-0 (-> pp stack-frame-top next))\n"
" (set! (-> pp stack-frame-top) v0-0)\n"
" v0-0\n"
" )";
test_with_expr(func, type, expected, false);
}
TEST_F(FormRegressionTest, RemoveMethod0ProcessTree) {
std::string func =
" sll r0, r0, 0\n"
" daddiu sp, sp, -32\n"
" sd ra, 0(sp)\n"
" sq gp, 16(sp)\n"
" or gp, a2, r0\n"
" lw v1, object(s7)\n"
" lwu t9, 16(v1)\n"
" or v1, a1, r0\n"
" lhu a2, 8(a1)\n"
" or a1, v1, r0\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" sw gp, 0(v0)\n"
" addiu v1, r0, 256\n"
" sw v1, 4(v0)\n"
" sw s7, 8(v0)\n"
" sw s7, 12(v0)\n"
" sw s7, 16(v0)\n"
" sw v0, 24(v0)\n"
" daddiu v1, v0, 24\n"
" sw v1, 20(v0)\n"
" ld ra, 0(sp)\n"
" lq gp, 16(sp)\n"
" jr ra\n"
" daddiu sp, sp, 32";
std::string type = "(function symbol type basic process-tree)";
std::string expected =
"(begin\n"
" (set! v0-0 (object-new arg0 arg1 (the-as int (-> arg1 size))))\n"
" (set! (-> v0-0 name) arg2)\n"
" (set! (-> v0-0 mask) 256)\n"
" (set! (-> v0-0 parent) (quote #f))\n"
" (set! (-> v0-0 brother) (quote #f))\n"
" (set! (-> v0-0 child) (quote #f))\n"
" (set! (-> v0-0 self) v0-0)\n"
" (set! (-> v0-0 ppointer) (&-> v0-0 self))\n"
" v0-0\n"
" )";
test_with_expr(func, type, expected, false, "process-tree");
}
TEST_F(FormRegressionTest, RemoveMethod3ProcessTree) {
std::string func =
" sll r0, r0, 0\n"
" daddiu sp, sp, -32\n"
" sd ra, 0(sp)\n"
" sd fp, 8(sp)\n"
" or fp, t9, r0\n"
" sq gp, 16(sp)\n"
" or gp, a0, r0\n"
" lw t9, format(s7)\n"
" daddiu a0, s7, #t\n"
" daddiu a1, fp, L340\n"
" or a2, gp, r0\n"
" lwu a3, -4(gp)\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" lw t9, format(s7)\n"
" daddiu a0, s7, #t\n"
" daddiu a1, fp, L339\n"
" lwu a2, 0(gp)\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" lw t9, format(s7)\n"
" daddiu a0, s7, #t\n"
" daddiu a1, fp, L338\n"
" lwu a2, 4(gp)\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" lw t9, format(s7)\n"
" daddiu a0, s7, #t\n"
" daddiu a1, fp, L337\n"
" lwu v1, 8(gp)\n"
" beq s7, v1, L258\n"
" or a2, s7, r0\n"
" lwu v1, 0(v1)\n"
" lwu a2, 24(v1)\n"
"L258:\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" lw t9, format(s7)\n"
" daddiu a0, s7, #t\n"
" daddiu a1, fp, L336\n"
" lwu v1, 12(gp)\n"
" beq s7, v1, L259\n"
" or a2, s7, r0\n"
" lwu v1, 0(v1)\n"
" lwu a2, 24(v1)\n"
"L259:\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" lw t9, format(s7)\n"
" daddiu a0, s7, #t\n"
" daddiu a1, fp, L335\n"
" lwu v1, 16(gp)\n"
" beq s7, v1, L260\n"
" or a2, s7, r0\n"
" lwu v1, 0(v1)\n"
" lwu a2, 24(v1)\n"
"L260:\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" or v0, gp, r0\n"
" ld ra, 0(sp)\n"
" ld fp, 8(sp)\n"
" lq gp, 16(sp)\n"
" jr ra\n"
" daddiu sp, sp, 32";
std::string type = "(function process-tree process-tree)";
// gross, but expected. see https://github.com/water111/jak-project/issues/254
std::string expected =
"(begin\n"
" (format (quote #t) \"[~8x] ~A~%\" arg0 (-> arg0 type))\n"
" (format (quote #t) \"~Tname: ~S~%\" (-> arg0 name))\n"
" (format (quote #t) \"~Tmask: #x~X~%\" (-> arg0 mask))\n"
" (set! t9-3 format)\n"
" (set! a0-4 (quote #t))\n"
" (set! a1-3 \"~Tparent: ~A~%\")\n"
" (set! v1-0 (-> arg0 parent))\n"
" (t9-3 a0-4 a1-3 (if v1-0 (-> v1-0 0 self)))\n"
" (set! t9-4 format)\n"
" (set! a0-5 (quote #t))\n"
" (set! a1-4 \"~Tbrother: ~A~%\")\n"
" (set! v1-2 (-> arg0 brother))\n"
" (set! a2-4 (if v1-2 (-> v1-2 0 self)))\n"
" (t9-4 a0-5 a1-4 a2-4)\n"
" (set! t9-5 format)\n"
" (set! a0-6 (quote #t))\n"
" (set! a1-5 \"~Tchild: ~A~%\")\n"
" (set! v1-4 (-> arg0 child))\n"
" (t9-5 a0-6 a1-5 (if v1-4 (-> v1-4 0 self)))\n"
" arg0\n"
" )";
test_with_expr(func, type, expected, false, "process-tree",
{{"L340", "[~8x] ~A~%"},
{"L339", "~Tname: ~S~%"},
{"L338", "~Tmask: #x~X~%"},
{"L337", "~Tparent: ~A~%"},
{"L336", "~Tbrother: ~A~%"},
{"L335", "~Tchild: ~A~%"}});
}
TEST_F(FormRegressionTest, ExprMethod0Process) {
std::string func =
" sll r0, r0, 0\n"
"L254:\n"
" daddiu sp, sp, -48\n"
" sd ra, 0(sp)\n"
" sq s5, 16(sp)\n"
" sq gp, 32(sp)\n"
" or s5, a2, r0\n"
" or gp, a3, r0\n"
" lw v1, symbol(s7)\n"
" lwu a2, -4(a0)\n"
" bne a2, v1, L255\n"
" sll r0, r0, 0\n"
" lw v1, object(s7)\n"
" lwu t9, 16(v1)\n"
" lw v1, process(s7)\n"
" lhu v1, 8(v1)\n"
" daddu a2, v1, gp\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" beq r0, r0, L256\n"
" sll r0, r0, 0\n"
"L255:\n"
" daddiu v0, a0, 4\n"
"L256:\n"
" sw s5, 0(v0)\n"
" daddiu v1, s7, dead\n"
" sw v1, 32(v0)\n"
" sw r0, 36(v0)\n"
" sw s7, 28(v0)\n"
" sw gp, 68(v0)\n"
" sw s7, 44(v0)\n"
" sw s7, 40(v0)\n"
" daddiu v1, v0, 108\n"
" sw v1, 84(v0)\n"
" sw v1, 76(v0)\n"
" lw v1, 68(v0)\n"
" daddiu v1, v1, 108\n"
" daddu v1, v1, v0\n"
" sw v1, 80(v0)\n"
" lwu v1, 80(v0)\n"
" sw v1, 88(v0)\n"
" sw s7, 88(v0)\n"
" sw s7, 52(v0)\n"
" sw s7, 72(v0)\n"
" sw s7, 48(v0)\n"
" sw s7, 56(v0)\n"
" sw s7, 60(v0)\n"
" sw s7, 64(v0)\n"
" sw s7, 8(v0)\n"
" sw s7, 12(v0)\n"
" sw s7, 16(v0)\n"
" sw v0, 24(v0)\n"
" daddiu v1, v0, 24\n"
" sw v1, 20(v0)\n"
" ld ra, 0(sp)\n"
" lq gp, 32(sp)\n"
" lq s5, 16(sp)\n"
" jr ra\n"
" daddiu sp, sp, 48";
std::string type = "(function symbol type basic int process)";
std::string expected =
"(begin\n"
" (set!\n"
" v0-0\n"
" (if\n"
" (= (-> arg0 type) symbol)\n"
" (object-new arg0 arg1 (the-as int (+ (-> process size) (the-as uint arg3))))\n"
" (+ (the-as int arg0) 4)\n"
" )\n"
" )\n"
" (set! (-> (the-as process v0-0) name) arg2)\n"
" (set! (-> v0-0 status) (quote dead))\n"
" (set! (-> v0-0 pid) 0)\n"
" (set! (-> v0-0 pool) (quote #f))\n"
" (set! (-> v0-0 allocated-length) arg3)\n"
" (set! (-> v0-0 top-thread) (quote #f))\n"
" (set! (-> v0-0 main-thread) (quote #f))\n"
" (set! v1-5 (-> v0-0 stack))\n"
" (set! (-> v0-0 heap-cur) v1-5)\n"
" (set! (-> v0-0 heap-base) v1-5)\n"
" (set! (-> v0-0 heap-top) (&-> v0-0 stack (-> v0-0 allocated-length)))\n"
" (set! (-> v0-0 stack-frame-top) (-> v0-0 heap-top))\n"
" (set! (-> v0-0 stack-frame-top) (quote #f))\n"
" (set! (-> v0-0 state) (quote #f))\n"
" (set! (-> v0-0 next-state) (quote #f))\n"
" (set! (-> v0-0 entity) (quote #f))\n"
" (set! (-> v0-0 trans-hook) (quote #f))\n"
" (set! (-> v0-0 post-hook) (quote #f))\n"
" (set! (-> v0-0 event-hook) (quote #f))\n"
" (set! (-> v0-0 parent) (quote #f))\n"
" (set! (-> v0-0 brother) (quote #f))\n"
" (set! (-> v0-0 child) (quote #f))\n"
" (set! (-> v0-0 self) v0-0)\n"
" (set! (-> v0-0 ppointer) (&-> v0-0 self))\n"
" v0-0\n"
" )";
test_with_expr(func, type, expected, false, "process", {},
parse_hint_json("[\t\t[12, [\"a0\", \"int\"]],\n"
"\t\t[13, [\"v0\", \"process\"]]]"));
}
// TEST_F(FormRegressionTest, ExprInspectProcessHeap) {
// std::string func =
// " sll r0, r0, 0\n"
// "L251:\n"
// " daddiu sp, sp, -64\n"
// " sd ra, 0(sp)\n"
// " sq s4, 16(sp)\n"
// " sq s5, 32(sp)\n"
// " sq gp, 48(sp)\n"
//
// " or gp, a0, r0\n"
// " lwu v1, 76(gp)\n"
// " daddiu s5, v1, 4\n"
// " beq r0, r0, L253\n"
// " sll r0, r0, 0\n"
//
// "L252:\n"
// " or a0, s5, r0\n"
// " lwu v1, -4(a0)\n"
// " lwu t9, 28(v1)\n"
// " jalr ra, t9\n"
// " sll v0, ra, 0\n"
//
// " or v1, v0, r0\n"
// " addiu s4, r0, -16\n"
// " or a0, s5, r0\n"
// " lwu v1, -4(a0)\n"
// " lwu t9, 36(v1)\n"
// " jalr ra, t9\n"
// " sll v0, ra, 0\n"
//
// " or v1, v0, r0\n"
// " daddiu v1, v1, 15\n"
// " and v1, s4, v1\n"
// " daddu s5, s5, v1\n"
//
// "L253:\n"
// " lwu v1, 84(gp)\n"
// " slt v1, s5, v1\n"
// " bne v1, r0, L252\n"
// " sll r0, r0, 0\n"
//
// " or v0, s7, r0\n"
// " ld ra, 0(sp)\n"
// " lq gp, 48(sp)\n"
// " lq s5, 32(sp)\n"
// " lq s4, 16(sp)\n"
// " jr ra\n"
// " daddiu sp, sp, 64";
// std::string type = "(function process symbol)";
// std::string expected =
// "(begin\n"
// " (set!\n"
// " v0-0\n"
// " (if\n"
// " (= (-> arg0 type) symbol)\n"
// " (object-new arg0 arg1 (the-as int (+ (-> process size) (the-as uint arg3))))\n"
// " (+ (the-as int arg0) 4)\n"
// " )\n"
// " )\n"
// " (set! (-> (the-as process v0-0) name) arg2)\n"
// " (set! (-> v0-0 status) (quote dead))\n"
// " (set! (-> v0-0 pid) 0)\n"
// " (set! (-> v0-0 pool) (quote #f))\n"
// " (set! (-> v0-0 allocated-length) arg3)\n"
// " (set! (-> v0-0 top-thread) (quote #f))\n"
// " (set! (-> v0-0 main-thread) (quote #f))\n"
// " (set! v1-5 (-> v0-0 stack))\n"
// " (set! (-> v0-0 heap-cur) v1-5)\n"
// " (set! (-> v0-0 heap-base) v1-5)\n"
// " (set! (-> v0-0 heap-top) (&-> v0-0 stack (-> v0-0 allocated-length)))\n"
// " (set! (-> v0-0 stack-frame-top) (-> v0-0 heap-top))\n"
// " (set! (-> v0-0 stack-frame-top) (quote #f))\n"
// " (set! (-> v0-0 state) (quote #f))\n"
// " (set! (-> v0-0 next-state) (quote #f))\n"
// " (set! (-> v0-0 entity) (quote #f))\n"
// " (set! (-> v0-0 trans-hook) (quote #f))\n"
// " (set! (-> v0-0 post-hook) (quote #f))\n"
// " (set! (-> v0-0 event-hook) (quote #f))\n"
// " (set! (-> v0-0 parent) (quote #f))\n"
// " (set! (-> v0-0 brother) (quote #f))\n"
// " (set! (-> v0-0 child) (quote #f))\n"
// " (set! (-> v0-0 self) v0-0)\n"
// " (set! (-> v0-0 ppointer) (&-> v0-0 self))\n"
// " v0-0\n"
// " )";
// test_with_expr(func, type, expected, false, "", {},
// parse_hint_json("[\t\t[4, [\"s5\", \"basic\"]],\n"
// "\t\t[17, [\"s5\", \"int\"]]]"));
//}

View file

@ -0,0 +1,106 @@
#include "gtest/gtest.h"
#include "FormRegressionTest.h"
using namespace decompiler;
TEST_F(FormRegressionTest, ExprTruncate) {
std::string func =
" sll r0, r0, 0\n"
" mtc1 f0, a0\n"
" cvt.w.s f0, f0\n"
" cvt.s.w f0, f0\n"
" mfc1 v0, f0\n"
" jr ra\n"
" daddu sp, sp, r0";
std::string type = "(function float float)";
std::string expected = "(the float (the int arg0))";
test_with_expr(func, type, expected);
}
TEST_F(FormRegressionTest, ExprIntegralP) {
std::string func =
" sll r0, r0, 0\n"
" mtc1 f0, a0\n"
" cvt.w.s f0, f0\n"
" cvt.s.w f0, f0\n"
" mfc1 v1, f0\n"
" mtc1 f0, v1\n"
" mtc1 f1, a0\n"
" c.eq.s f0, f1\n"
" bc1t L31\n"
" daddiu v0, s7, 8\n"
" or v0, s7, r0\n"
"L31:\n"
" jr ra\n"
" daddu sp, sp, r0";
std::string type = "(function float float)";
std::string expected = "(the-as float (= (the float (the int arg0)) arg0))";
test_with_expr(func, type, expected);
}
TEST_F(FormRegressionTest, ExprFractionalPart) {
std::string func =
" sll r0, r0, 0\n"
" mtc1 f0, a0\n"
" mtc1 f1, a0\n"
" cvt.w.s f1, f1\n"
" cvt.s.w f1, f1\n"
" mfc1 v1, f1\n"
" mtc1 f1, v1\n"
" sub.s f0, f0, f1\n"
" mfc1 v0, f0\n"
" jr ra\n"
" daddu sp, sp, r0";
std::string type = "(function float float)";
std::string expected = "(- arg0 (the float (the int arg0)))";
test_with_expr(func, type, expected);
}
TEST_F(FormRegressionTest, ExprSeek) {
std::string func =
" sll r0, r0, 0\n"
"L24:\n"
" mtc1 f0, a0\n"
" mtc1 f3, a1\n"
" mtc1 f1, a2\n"
" sub.s f2, f3, f0\n"
" abs.s f4, f2\n"
" c.lt.s f1, f4\n"
" bc1t L25\n"
" sll r0, r0, 0\n"
" mfc1 v0, f3\n"
" beq r0, r0, L27\n"
" sll r0, r0, 0\n"
"L25:\n"
" mtc1 f3, r0\n"
" c.lt.s f2, f3\n"
" bc1t L26\n"
" sll r0, r0, 0\n"
" add.s f0, f0, f1\n"
" mfc1 v0, f0\n"
" beq r0, r0, L27\n"
" sll r0, r0, 0\n"
"L26:\n"
" sub.s f0, f0, f1\n"
" mfc1 v0, f0\n"
"L27:\n"
" jr ra\n"
" daddu sp, sp, r0";
std::string type = "(function float float float float)";
std::string expected =
"(begin\n"
" (set! f2-0 (- arg1 arg0))\n"
" (cond\n"
" ((>= arg2 (fabs f2-0)) arg1)\n"
" ((>= f2-0 0) (+ arg0 arg2))\n"
" (else (- arg0 arg2))\n"
" )\n"
" )";
test_with_expr(func, type, expected);
}