mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -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()) {
|
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;
|
||||||
|
|
|
@ -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?
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"]]
|
||||||
|
|
|
@ -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"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
|
@ -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))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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})
|
||||||
|
|
||||||
|
|
|
@ -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\"]]]"));
|
||||||
|
//}
|
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