mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 00:57:44 -04:00
[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:
parent
a3b31d3c0e
commit
126dfc1c45
|
@ -439,11 +439,9 @@ bool TypeSystem::try_reverse_lookup_other(const FieldReverseLookupInput& input,
|
|||
if (field.is_inline()) {
|
||||
expected_offset_into_field = lookup_type(field.type())->get_offset();
|
||||
}
|
||||
if (offset_into_field == expected_offset_into_field && !input.deref.has_value()) {
|
||||
// get the inline field.
|
||||
if (input.stride) {
|
||||
continue;
|
||||
}
|
||||
if (offset_into_field == expected_offset_into_field && !input.deref.has_value() &&
|
||||
!input.stride) {
|
||||
// get the inline field exactly
|
||||
path->push_back(token);
|
||||
*result_type = field_deref.type;
|
||||
*addr_of = false;
|
||||
|
|
|
@ -1200,185 +1200,6 @@ TypeSpec coerce_to_reg_type(const TypeSpec& 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?
|
||||
*/
|
||||
|
|
|
@ -54,38 +54,6 @@ struct DerefInfo {
|
|||
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)
|
||||
*/
|
||||
|
@ -129,7 +97,6 @@ class TypeSystem {
|
|||
std::string get_runtime_type(const TypeSpec& ts);
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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,
|
||||
std::vector<FieldReverseLookupOutput::Token>* path,
|
||||
bool* addr_of,
|
||||
|
|
|
@ -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) {
|
||||
assert(get_condition_num_args(m_kind) == 0);
|
||||
}
|
||||
|
|
|
@ -377,6 +377,7 @@ class IR2_Condition {
|
|||
std::string get_condition_kind_name(IR2_Condition::Kind kind);
|
||||
int get_condition_num_args(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.
|
||||
|
|
|
@ -17,20 +17,6 @@ RegClass get_reg_kind(const Register& r) {
|
|||
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
|
||||
|
||||
ConditionElement* BranchOp::get_condition_as_form(FormPool& pool, const Env& env) const {
|
||||
|
|
|
@ -135,6 +135,10 @@ TP_Type SimpleExpression::get_type(const TypeState& input,
|
|||
return m_args[0].get_type(input, env, dts);
|
||||
case Kind::GPR_TO_FPR: {
|
||||
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;
|
||||
}
|
||||
case Kind::FPR_TO_GPR:
|
||||
|
@ -146,7 +150,11 @@ TP_Type SimpleExpression::get_type(const TypeState& input,
|
|||
case Kind::ABS_S:
|
||||
case Kind::NEG_S:
|
||||
case Kind::INT_TO_FLOAT:
|
||||
case Kind::MIN_S:
|
||||
case Kind::MAX_S:
|
||||
return TP_Type::make_from_ts("float");
|
||||
case Kind::FLOAT_TO_INT:
|
||||
return TP_Type::make_from_ts("int");
|
||||
case Kind::ADD:
|
||||
case Kind::SUB:
|
||||
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.
|
||||
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;
|
||||
|
||||
default:
|
||||
|
@ -418,6 +431,12 @@ TypeState SetVarOp::propagate_types_internal(const TypeState& input,
|
|||
const Env& env,
|
||||
DecompilerTypeSystem& dts) {
|
||||
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);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ void Env::set_remap_for_function(int nargs) {
|
|||
var_name.push_back('0');
|
||||
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) {
|
||||
|
@ -29,6 +30,7 @@ void Env::set_remap_for_new_method(int nargs) {
|
|||
var_name.push_back('0');
|
||||
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) {
|
||||
|
@ -41,6 +43,7 @@ void Env::set_remap_for_method(int nargs) {
|
|||
var_name.push_back('0');
|
||||
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,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <utility>
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
#include "common/type_system/TypeSystem.h"
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
|
@ -1133,6 +1134,12 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
|
|||
return "min";
|
||||
case FixedOperatorKind::MAX:
|
||||
return "max";
|
||||
case FixedOperatorKind::FABS:
|
||||
return "fabs";
|
||||
case FixedOperatorKind::FMIN:
|
||||
return "fmin";
|
||||
case FixedOperatorKind::FMAX:
|
||||
return "fmax";
|
||||
case FixedOperatorKind::LOGAND:
|
||||
return "logand";
|
||||
case FixedOperatorKind::LOGIOR:
|
||||
|
@ -1173,6 +1180,8 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
|
|||
return "=";
|
||||
case FixedOperatorKind::NEQ:
|
||||
return "!=";
|
||||
case FixedOperatorKind::METHOD_OF_OBJECT:
|
||||
return "method-of-object";
|
||||
default:
|
||||
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)
|
||||
: m_base(base), m_is_addr_of(is_addr_of), m_tokens({std::move(token)}) {
|
||||
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::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
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "decompiler/Disasm/Register.h"
|
||||
#include "decompiler/IR2/AtomicOp.h"
|
||||
#include "common/goos/Object.h"
|
||||
#include "common/type_system/TypeSystem.h"
|
||||
|
||||
namespace decompiler {
|
||||
class Form;
|
||||
|
@ -132,6 +133,11 @@ class SimpleExpressionElement : public FormElement {
|
|||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
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,
|
||||
FixedOperatorKind kind,
|
||||
FormPool& pool,
|
||||
|
@ -856,6 +862,8 @@ class DerefToken {
|
|||
Form* m_expr = nullptr;
|
||||
};
|
||||
|
||||
DerefToken to_token(const FieldReverseLookupOutput::Token& in);
|
||||
|
||||
class DerefElement : public FormElement {
|
||||
public:
|
||||
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 collect_vars(VariableSet& vars) 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:
|
||||
std::string m_value;
|
||||
|
|
|
@ -331,13 +331,14 @@ void SimpleExpressionElement::update_from_stack_fpr_to_gpr(const Env& env,
|
|||
bool allow_side_effects) {
|
||||
auto src = m_expr.get_arg(0);
|
||||
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.
|
||||
m_expr = src.as_expr();
|
||||
// then go again.
|
||||
update_from_stack(env, pool, stack, result, allow_side_effects);
|
||||
} 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));
|
||||
result->push_back(new_form);
|
||||
} 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)));
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
auto new_form = pool.alloc_element<GenericElement>(
|
||||
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,
|
||||
FormPool& pool,
|
||||
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,
|
||||
allow_side_effects);
|
||||
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:
|
||||
update_from_stack_float_1(env, FixedOperatorKind::SQRT, pool, stack, result,
|
||||
allow_side_effects);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
case SimpleExpression::Kind::NEG_S:
|
||||
|
@ -718,6 +787,9 @@ void SimpleExpressionElement::update_from_stack(const Env& env,
|
|||
case SimpleExpression::Kind::INT_TO_FLOAT:
|
||||
update_from_stack_int_to_float(env, pool, stack, result, allow_side_effects);
|
||||
break;
|
||||
case SimpleExpression::Kind::FLOAT_TO_INT:
|
||||
update_from_stack_float_to_int(env, pool, stack, result, allow_side_effects);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
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()) {
|
||||
assert(x->parent_form == m_src);
|
||||
}
|
||||
|
||||
if (m_src->is_single_element()) {
|
||||
auto src_as_se = dynamic_cast<SimpleExpressionElement*>(m_src->back());
|
||||
if (src_as_se) {
|
||||
if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY &&
|
||||
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& info = env.reg_use().op.at(var.idx());
|
||||
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) {
|
||||
// todo - update var tokens from stack?
|
||||
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());
|
||||
if (as_deref) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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),
|
||||
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: {
|
||||
auto casted = make_cast(source_forms, types, TypeSpec("int"), pool);
|
||||
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);
|
||||
source_types.push_back(env.get_types_before_op(var.idx()).get(var.reg()).typespec());
|
||||
} 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 {
|
||||
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) {
|
||||
// reg0 is idx
|
||||
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
|
||||
auto reg1_matcher =
|
||||
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: " +
|
||||
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);
|
||||
assert(idx.has_value() && base.has_value());
|
||||
assert(idx && base.has_value());
|
||||
|
||||
std::vector<DerefToken> tokens = m_deref_tokens;
|
||||
for (auto& x : tokens) {
|
||||
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)));
|
||||
|
@ -1760,4 +1870,12 @@ void SimpleExpressionElement::push_to_stack(const Env&, FormPool&, FormStack& st
|
|||
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
|
||||
|
|
|
@ -101,6 +101,9 @@ enum class FixedOperatorKind {
|
|||
ABS,
|
||||
MIN,
|
||||
MAX,
|
||||
FABS,
|
||||
FMIN,
|
||||
FMAX,
|
||||
LOGAND,
|
||||
LOGIOR,
|
||||
LOGXOR,
|
||||
|
@ -121,6 +124,7 @@ enum class FixedOperatorKind {
|
|||
EQ,
|
||||
NEQ,
|
||||
CONS,
|
||||
METHOD_OF_OBJECT,
|
||||
INVALID
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
* "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);
|
||||
|
||||
if (block_id == 0 && !got_not_arg_coloring) {
|
||||
got_not_arg_coloring = true;
|
||||
auto as_set = dynamic_cast<const SetVarOp*>(op.get());
|
||||
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 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()) {
|
||||
ssa_i.is_arg_coloring_move = true;
|
||||
} else {
|
||||
got_not_arg_coloring = true;
|
||||
got_not_arg_coloring = false;
|
||||
}
|
||||
} else {
|
||||
got_not_arg_coloring = true;
|
||||
}
|
||||
} else {
|
||||
got_not_arg_coloring = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -702,6 +702,22 @@
|
|||
;;;;;;;;;;;; START OF GAME.CGO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; TYPES-H ;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
(deftype time-frame (int64)
|
||||
()
|
||||
:flag-assert #x900000008
|
||||
)
|
||||
|
||||
(deftype part-id (uint32)
|
||||
()
|
||||
:flag-assert #x900000004
|
||||
)
|
||||
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; MATH ;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -724,27 +740,36 @@
|
|||
(define-extern seek (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-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 rand-vu-init function)
|
||||
; (define-extern rand-vu-nostep function)
|
||||
; (define-extern rand-vu function)
|
||||
;
|
||||
;
|
||||
;
|
||||
; (define-extern lerp 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 integral? function)
|
||||
; (define-extern rand-uint31-gen function)
|
||||
; (define-extern rand-vu-int-count function)
|
||||
;
|
||||
; (define-extern fractional-part function)
|
||||
; (define-extern seek function)
|
||||
; ;;(define-extern xyzw object) ;; unknown type
|
||||
; (define-extern rand-vu-percent? function)
|
||||
;
|
||||
; ;;(define-extern *random-generator* object) ;; unknown type
|
||||
; (define-extern rand-vu-float-range function)
|
||||
;
|
||||
; ;;(define-extern rgba object) ;; unknown type
|
||||
; (define-extern seekl function)
|
||||
;
|
||||
|
||||
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
@ -757,16 +782,18 @@
|
|||
(deftype bit-array (basic)
|
||||
((length int32 :offset-assert 4)
|
||||
(allocated-length int32 :offset-assert 8)
|
||||
(_pad uint8)
|
||||
(_pad uint8 :offset-assert 12)
|
||||
(bytes uint8 :dynamic :offset 12)
|
||||
)
|
||||
:method-count-assert 13
|
||||
:size-assert #xd
|
||||
:flag-assert #xd0000000d
|
||||
(:methods
|
||||
(dummy-9 () none 9)
|
||||
(dummy-10 () none 10)
|
||||
(dummy-11 () none 11)
|
||||
(dummy-12 () none 12)
|
||||
(new (symbol type int) _type_ 0)
|
||||
(get-bit (_type_ int) symbol 9)
|
||||
(clear-bit (_type_ int) int 10)
|
||||
(set-bit (_type_ int) int 11)
|
||||
(clear (_type_) _type_ 12)
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -997,6 +1024,9 @@
|
|||
)
|
||||
|
||||
;; todo isphere
|
||||
(deftype isphere (vec4s)
|
||||
()
|
||||
)
|
||||
|
||||
(deftype box8s (structure)
|
||||
((data float 8 :offset-assert 0)
|
||||
|
@ -1103,8 +1133,12 @@
|
|||
(define-extern *zero-vector* vector)
|
||||
|
||||
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; BOUNDING-BOX-H ;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
;; bounding-box-h
|
||||
(deftype bounding-box (structure)
|
||||
((min vector :inline :offset-assert 0)
|
||||
(max vector :inline :offset-assert 16)
|
||||
|
@ -1123,7 +1157,6 @@
|
|||
)
|
||||
)
|
||||
|
||||
;; bounding-box-h
|
||||
(deftype bounding-box4w (structure)
|
||||
((min vector4w :inline :offset-assert 0)
|
||||
(max vector4w :inline :offset-assert 16)
|
||||
|
@ -1133,7 +1166,6 @@
|
|||
:flag-assert #x900000020
|
||||
)
|
||||
|
||||
;; bounding-box-h
|
||||
(deftype bounding-box-both (structure)
|
||||
((box bounding-box :inline :offset-assert 0)
|
||||
(box4w bounding-box4w :inline :offset-assert 32)
|
||||
|
@ -1143,7 +1175,12 @@
|
|||
:flag-assert #x900000040
|
||||
)
|
||||
|
||||
;; matrix-h
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; MATRIX-H ;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
(deftype matrix (structure)
|
||||
((data float 16 :offset-assert 0)
|
||||
(vector vector 4 :offset 0)
|
||||
|
@ -1167,7 +1204,6 @@
|
|||
:flag-assert #x900000030
|
||||
)
|
||||
|
||||
;; guess on signs here
|
||||
(deftype matrix4h (structure)
|
||||
((data int16 16 :offset-assert 0)
|
||||
(vector4h vector4h 4 :offset 0)
|
||||
|
@ -1178,6 +1214,13 @@
|
|||
:flag-assert #x900000020
|
||||
)
|
||||
|
||||
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; QUATERNION-H ;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
(deftype quaternion (structure)
|
||||
((data float 4 :offset-assert 0)
|
||||
(x float :offset 0)
|
||||
|
@ -1192,7 +1235,12 @@
|
|||
:flag-assert #x900000010
|
||||
)
|
||||
|
||||
;; euler-h
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; EULER-H ;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
(deftype euler-angles (vector)
|
||||
()
|
||||
:method-count-assert 9
|
||||
|
@ -1200,7 +1248,12 @@
|
|||
:flag-assert #x900000010
|
||||
)
|
||||
|
||||
;; transform-h
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; TRANSFORM-H ;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
(deftype transform (structure)
|
||||
((trans vector :inline :offset-assert 0)
|
||||
(rot vector :inline :offset-assert 16)
|
||||
|
@ -1211,7 +1264,6 @@
|
|||
:flag-assert #x900000030
|
||||
)
|
||||
|
||||
;; transform-h
|
||||
(deftype trs (basic)
|
||||
((trans vector :inline :offset-assert 16)
|
||||
(rot vector :inline :offset-assert 32)
|
||||
|
@ -1222,7 +1274,12 @@
|
|||
:flag-assert #x900000040
|
||||
)
|
||||
|
||||
;; geometry-h
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; GEOMETRY-H ;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
(deftype curve (structure)
|
||||
((cverts uint32 :offset-assert 0)
|
||||
(num-cverts int32 :offset-assert 4)
|
||||
|
@ -1235,7 +1292,6 @@
|
|||
:flag-assert #x900000014
|
||||
)
|
||||
|
||||
;; geometry-h
|
||||
(deftype border-plane (basic)
|
||||
((name basic :offset-assert 4)
|
||||
(action basic :offset-assert 8)
|
||||
|
@ -1252,7 +1308,12 @@
|
|||
)
|
||||
)
|
||||
|
||||
; ;; transformq-h
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; TRANSFORMQ-H ;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
(deftype transformq (transform)
|
||||
((quat quaternion :inline :offset 16)
|
||||
)
|
||||
|
@ -1269,8 +1330,6 @@
|
|||
:flag-assert #x900000040
|
||||
)
|
||||
|
||||
|
||||
;; transformq-h
|
||||
(deftype trsqv (trsq)
|
||||
((pause-adjust-distance float :offset 4) ;; 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)
|
||||
((rsvd1 uint16 :offset-assert 0)
|
||||
(command uint16 :offset-assert 2)
|
||||
|
@ -31505,8 +31573,7 @@
|
|||
;;(define-extern trsq object) ;; unknown type
|
||||
(define-extern deg-diff 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 matrix-inverse-of-rot-trans! function)
|
||||
(define-extern matrix-3x3-determinant function)
|
||||
|
|
|
@ -61,10 +61,6 @@
|
|||
[3, ["v1", "int"], ["a0", "int"]]
|
||||
],
|
||||
|
||||
"remove-exit":[
|
||||
[0, ["s6", "process"]]
|
||||
],
|
||||
|
||||
"(method 0 process)":[
|
||||
[12, ["a0", "int"]],
|
||||
[13, ["v0", "process"]]
|
||||
|
|
|
@ -192,6 +192,46 @@
|
|||
"valid?":{
|
||||
"args":["obj", "expected-type", "name", "allow-false", "print-dest"],
|
||||
"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"]
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ TP_Type DecompilerTypeSystem::tp_lca(const TP_Type& existing,
|
|||
bool* changed) const {
|
||||
// starting from most vague to most specific
|
||||
|
||||
// simplist case, no difference.
|
||||
// simplest case, no difference.
|
||||
if (existing == add) {
|
||||
*changed = false;
|
||||
return existing;
|
||||
|
@ -252,6 +252,18 @@ TP_Type DecompilerTypeSystem::tp_lca(const TP_Type& existing,
|
|||
*changed = true;
|
||||
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::UNINITIALIZED:
|
||||
|
|
|
@ -47,6 +47,8 @@ std::string TP_Type::print() const {
|
|||
return fmt::format("<string with {} args>", m_int);
|
||||
case Kind::INTEGER_CONSTANT:
|
||||
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:
|
||||
return fmt::format("<dynamic-method-access>");
|
||||
case Kind::INVALID:
|
||||
|
@ -81,6 +83,8 @@ bool TP_Type::operator==(const TP_Type& other) const {
|
|||
return m_int == other.m_int;
|
||||
case Kind::FORMAT_STRING:
|
||||
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:
|
||||
return true;
|
||||
case Kind::INVALID:
|
||||
|
@ -119,6 +123,8 @@ TypeSpec TP_Type::typespec() const {
|
|||
return TypeSpec("string");
|
||||
case Kind::INTEGER_CONSTANT:
|
||||
return TypeSpec("int");
|
||||
case Kind::INTEGER_CONSTANT_PLUS_VAR:
|
||||
return m_ts;
|
||||
case Kind::DYNAMIC_METHOD_ACCESS:
|
||||
return TypeSpec("object");
|
||||
case Kind::FORMAT_STRING:
|
||||
|
|
|
@ -21,11 +21,13 @@ class TP_Type {
|
|||
UNINITIALIZED, // representing data which is uninitialized.
|
||||
PRODUCT_WITH_CONSTANT, // representing: (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.
|
||||
STRING_CONSTANT, // a string that's part of the string pool
|
||||
FORMAT_STRING, // a string with a given number of format arguments
|
||||
INTEGER_CONSTANT, // a constant integer.
|
||||
DYNAMIC_METHOD_ACCESS, // partial access into a
|
||||
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
|
||||
FORMAT_STRING, // a string with a given number of format arguments
|
||||
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
|
||||
INVALID
|
||||
} kind = Kind::UNINITIALIZED;
|
||||
TP_Type() = default;
|
||||
|
@ -104,6 +106,14 @@ class TP_Type {
|
|||
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) {
|
||||
TP_Type result;
|
||||
result.kind = Kind::PRODUCT_WITH_CONSTANT;
|
||||
|
@ -134,7 +144,7 @@ class TP_Type {
|
|||
}
|
||||
|
||||
const TypeSpec& get_objects_typespec() const {
|
||||
assert(kind == Kind::TYPESPEC);
|
||||
assert(kind == Kind::TYPESPEC || kind == Kind::INTEGER_CONSTANT_PLUS_VAR);
|
||||
return m_ts;
|
||||
}
|
||||
|
||||
|
@ -159,7 +169,7 @@ class TP_Type {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,3 +13,14 @@
|
|||
- Many more useless `set!`s will be removed
|
||||
- Stores into arrays are supported
|
||||
- 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.
|
|
@ -5,3 +5,26 @@
|
|||
;; name in dgo: bounding-box
|
||||
;; 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))
|
||||
)
|
||||
)
|
||||
|
|
|
@ -5,3 +5,14 @@
|
|||
;; name in dgo: euler-h
|
||||
;; 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
|
||||
)
|
||||
|
|
|
@ -43,26 +43,61 @@
|
|||
(- 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))
|
||||
"Straight out of Bit Twiddling Hacks graphics.stanford.edu"
|
||||
(- (sar (the-as integer (the float x)) 23) 127)
|
||||
"Straight out of Bit Twiddling Hacks graphics.stanford.edu.
|
||||
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))
|
||||
"Move x toward target by at most diff, with floats"
|
||||
(let ((err (- target x)))
|
||||
;; if we can get there all at once
|
||||
(if (<= (fabs err) diff)
|
||||
(return-from #f target)
|
||||
(cond
|
||||
((>= diff (fabs err))
|
||||
;; 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))
|
||||
|
@ -83,19 +118,19 @@
|
|||
)
|
||||
|
||||
(defun lerp-clamp ((minimum float) (maximum float) (amount float))
|
||||
"Interpolate between minimum and maximum. Clamp output.
|
||||
For some reason, the interpolate here is done in a less efficient way than lerp."
|
||||
(if (<= amount 0.0)
|
||||
(return-from #f minimum)
|
||||
)
|
||||
|
||||
(if (>= amount 1.0)
|
||||
(return-from #f maximum)
|
||||
)
|
||||
;; lerp computes this part, but more efficiently
|
||||
(+ (* (- 1.0 amount) minimum)
|
||||
(* amount maximum)
|
||||
"Interpolate between minimum and maximum, but saturate the amount to [0, 1]"
|
||||
(cond
|
||||
((>= 0.0 amount)
|
||||
minimum)
|
||||
((>= amount 1.0)
|
||||
maximum)
|
||||
(else
|
||||
;; the implementation in lerp uses fewer operations...
|
||||
(+ (* (- 1.0 amount) minimum)
|
||||
(* amount maximum)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -117,10 +152,107 @@
|
|||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; random vu
|
||||
;;;; random vu hardware
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; added in OpenGOAL
|
||||
(define *_vu-reg-R_* 0)
|
||||
|
||||
;; 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
|
||||
|
@ -166,4 +298,4 @@
|
|||
(set! (-> gen seed) result)
|
||||
result
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -5,3 +5,61 @@
|
|||
;; name in dgo: transformq-h
|
||||
;; 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
|
||||
|
|
|
@ -5,3 +5,4 @@
|
|||
;; name in dgo: trigonometry-h
|
||||
;; dgos: GAME, ENGINE
|
||||
|
||||
;; this file generates no code.
|
|
@ -12,27 +12,96 @@
|
|||
(deftype bit-array (basic)
|
||||
((length int32 :offset-assert 4)
|
||||
(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
|
||||
:size-assert #xd
|
||||
:flag-assert #xd0000000d
|
||||
(:methods
|
||||
(dummy-9 () none 9)
|
||||
(dummy-10 () none 10)
|
||||
(dummy-11 () none 11)
|
||||
(dummy-12 () none 12)
|
||||
)
|
||||
(new (symbol type int) _type_ 0)
|
||||
(get-bit (_type_ int) symbol 9)
|
||||
(clear-bit (_type_ int) int 10)
|
||||
(set-bit (_type_ int) int 11)
|
||||
(clear (_type_) _type_ 12)
|
||||
)
|
||||
)
|
||||
|
||||
;; todo new
|
||||
;; todo 4
|
||||
;; todo 5
|
||||
;; todo 9
|
||||
;; todo 10
|
||||
;; todo 11
|
||||
;; todo 12
|
||||
;;
|
||||
(defmethod new bit-array ((allocation symbol) (type-to-make type) (length int))
|
||||
"Allocate a new bit-array which can hold length bits.
|
||||
Sets both the length and the allocated-length to this length."
|
||||
(local-vars (obj bit-array))
|
||||
;; remove one byte, we get one in the type already.
|
||||
(set! obj (object-new allocation type-to-make
|
||||
(+ (+ (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)
|
||||
|
@ -315,13 +384,13 @@
|
|||
:flag-assert #x900000010
|
||||
)
|
||||
|
||||
; todo
|
||||
; (deftype isphere (vec4s)
|
||||
; ()
|
||||
; :method-count-assert 9
|
||||
; :size-assert #x10
|
||||
; :flag-assert #x900000010
|
||||
; )
|
||||
|
||||
(deftype isphere (vec4s)
|
||||
()
|
||||
:method-count-assert 9
|
||||
:size-assert #x10
|
||||
:flag-assert #x900000010
|
||||
)
|
||||
|
||||
(deftype box8s (structure)
|
||||
((data float 8 :offset-assert 0)
|
||||
|
@ -543,4 +612,4 @@
|
|||
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.))
|
||||
|
|
|
@ -140,7 +140,8 @@ class Compiler {
|
|||
Val* compile_stack_new(const goos::Object& form,
|
||||
const goos::Object& type,
|
||||
const goos::Object* rest,
|
||||
Env* env);
|
||||
Env* env,
|
||||
bool call_constructor);
|
||||
|
||||
TypeSystem m_ts;
|
||||
std::unique_ptr<GlobalEnv> m_global_env = nullptr;
|
||||
|
|
|
@ -785,7 +785,8 @@ Val* Compiler::compile_static_new(const goos::Object& form,
|
|||
Val* Compiler::compile_stack_new(const goos::Object& form,
|
||||
const goos::Object& type,
|
||||
const goos::Object* rest,
|
||||
Env* env) {
|
||||
Env* env,
|
||||
bool call_constructor) {
|
||||
auto type_of_object = parse_typespec(unquote(type));
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
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
|
||||
auto mem = fe->allocate_aligned_stack_variable(type_of_object, ti->get_size_in_memory(), 16)
|
||||
->to_gpr(env);
|
||||
// the new method actual takes a "symbol" according the type system. So we have to cheat it.
|
||||
mem->set_type(TypeSpec("symbol"));
|
||||
args.push_back(mem);
|
||||
// type
|
||||
args.push_back(compile_get_symbol_value(form, type_of_object.base_type(), env)->to_reg(env));
|
||||
// the other arguments
|
||||
for_each_in_list(*rest, [&](const goos::Object& o) {
|
||||
args.push_back(compile_error_guard(o, env)->to_reg(env));
|
||||
});
|
||||
if (call_constructor) {
|
||||
// the new method actual takes a "symbol" according the type system. So we have to cheat it.
|
||||
mem->set_type(TypeSpec("symbol"));
|
||||
args.push_back(mem);
|
||||
// type
|
||||
args.push_back(compile_get_symbol_value(form, type_of_object.base_type(), env)->to_reg(env));
|
||||
// the other arguments
|
||||
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_obj = compile_real_function_call(form, new_method, args, env);
|
||||
new_obj->set_type(type_of_object);
|
||||
return new_obj;
|
||||
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);
|
||||
new_obj->set_type(type_of_object);
|
||||
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.
|
||||
return compile_static_new(form, type, rest, env);
|
||||
} 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");
|
||||
|
|
|
@ -24,6 +24,7 @@ add_executable(goalc-test
|
|||
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_FormExpressionBuildLong.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_InstructionParser.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_CASES})
|
||||
|
||||
|
|
|
@ -276,3 +276,477 @@ TEST_F(FormRegressionTest, ExprMethod9Thread) {
|
|||
" )";
|
||||
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\"]]]"));
|
||||
//}
|
106
test/decompiler/test_math_decomp.cpp
Normal file
106
test/decompiler/test_math_decomp.cpp
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue