[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;
} }
} }
// 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); 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()) {
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")); 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

@ -25,6 +25,8 @@ class TP_Type {
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.
INTEGER_CONSTANT_PLUS_VAR, // constant + variable. used in stuff like (&-> obj inline-val-arr
// x)
DYNAMIC_METHOD_ACCESS, // partial access into a DYNAMIC_METHOD_ACCESS, // partial access into a
INVALID INVALID
} kind = Kind::UNINITIALIZED; } kind = Kind::UNINITIALIZED;
@ -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,27 +43,62 @@
(- 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)
(if (>= err 0) ;; increase
(+ x diff) (+ x diff)
)
(else
(- x diff) (- x diff)
) )
) )
) )
)
(defun lerp ((minimum float) (maximum float) (amount float)) (defun lerp ((minimum float) (maximum float) (amount float))
"Interpolate between minimum and maximum. The output is not clamped." "Interpolate between minimum and maximum. The output is not clamped."
@ -83,20 +118,20 @@
) )
(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...
)
;; lerp computes this part, but more efficiently
(+ (* (- 1.0 amount) minimum) (+ (* (- 1.0 amount) minimum)
(* amount maximum) (* amount maximum)
) )
) )
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; integer utility ;;;; integer utility
@ -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

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)

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,6 +842,7 @@ 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);
if (call_constructor) {
// the new method actual takes a "symbol" according the type system. So we have to cheat it. // the new method actual takes a "symbol" according the type system. So we have to cheat it.
mem->set_type(TypeSpec("symbol")); mem->set_type(TypeSpec("symbol"));
args.push_back(mem); args.push_back(mem);
@ -855,6 +857,15 @@ Val* Compiler::compile_stack_new(const goos::Object& form,
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);
}