mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-27 08:20:47 +00:00
[decompiler] gkernel offline test (#321)
* working on pointer math * bug fixes * gkernel passing with no anon functions * update tests
This commit is contained in:
parent
814480f9e5
commit
e93d97dd07
@ -144,7 +144,7 @@ bool Function::run_type_analysis_ir2(
|
||||
} catch (std::runtime_error& e) {
|
||||
lg::warn("Function {} failed type prop: {}", guessed_name.to_string(), e.what());
|
||||
warnings.type_prop_warning("{}", e.what());
|
||||
ir2.env.set_types(block_init_types, op_types, *ir2.atomic_ops);
|
||||
ir2.env.set_types(block_init_types, op_types, *ir2.atomic_ops, my_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -198,7 +198,7 @@ bool Function::run_type_analysis_ir2(
|
||||
}
|
||||
}
|
||||
|
||||
ir2.env.set_types(block_init_types, op_types, *ir2.atomic_ops);
|
||||
ir2.env.set_types(block_init_types, op_types, *ir2.atomic_ops, my_type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -107,6 +107,61 @@ FormElement* SetVarConditionOp::get_as_form(FormPool& pool, const Env& env) cons
|
||||
is_sequence_point(), TypeSpec("symbol"));
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::optional<TypeSpec> get_typecast_for_atom(const SimpleAtom& atom,
|
||||
const Env& env,
|
||||
const TypeSpec& expected_type,
|
||||
int my_idx) {
|
||||
auto type_info = env.dts->ts.lookup_type(expected_type);
|
||||
switch (atom.get_kind()) {
|
||||
case SimpleAtom::Kind::VARIABLE: {
|
||||
auto src_type = env.get_types_before_op(my_idx).get(atom.var().reg());
|
||||
|
||||
if (src_type.requires_cast() || !env.dts->ts.tc(expected_type, src_type.typespec())) {
|
||||
// we fail the typecheck for a normal set!, so add a cast.
|
||||
return expected_type;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
} break;
|
||||
case SimpleAtom::Kind::INTEGER_CONSTANT: {
|
||||
std::optional<TypeSpec> cast_for_set, cast_for_define;
|
||||
bool sym_int_or_uint = env.dts->ts.tc(TypeSpec("integer"), expected_type);
|
||||
bool sym_uint = env.dts->ts.tc(TypeSpec("uinteger"), expected_type);
|
||||
bool sym_int = sym_int_or_uint && !sym_uint;
|
||||
|
||||
if (sym_int) {
|
||||
// do nothing for set.
|
||||
return {};
|
||||
} else {
|
||||
// for uint or other
|
||||
return expected_type;
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case SimpleAtom::Kind::SYMBOL_PTR:
|
||||
case SimpleAtom::Kind::SYMBOL_VAL: {
|
||||
assert(atom.get_str() == "#f");
|
||||
|
||||
if (expected_type != TypeSpec("symbol")) {
|
||||
// explicitly cast if we're not using a reference type, including pointers.
|
||||
// otherwise, we allow setting references to #f.
|
||||
if (!type_info->is_reference()) {
|
||||
return expected_type;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
|
||||
if (env.has_type_analysis()) {
|
||||
if (m_addr.is_identity() && m_addr.get_arg(0).is_sym_val()) {
|
||||
@ -248,8 +303,10 @@ FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
|
||||
}
|
||||
assert(!rd.addr_of);
|
||||
auto addr = pool.alloc_element<DerefElement>(source, rd.addr_of, tokens);
|
||||
return pool.alloc_element<StorePlainDeref>(addr, m_value.as_expr(), m_my_idx, ro.var,
|
||||
std::nullopt);
|
||||
|
||||
return pool.alloc_element<StorePlainDeref>(
|
||||
addr, m_value.as_expr(), m_my_idx, ro.var, std::nullopt,
|
||||
get_typecast_for_atom(m_value, env, coerce_to_reg_type(rd.result_type), m_my_idx));
|
||||
}
|
||||
|
||||
std::string cast_type;
|
||||
@ -285,7 +342,8 @@ FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
|
||||
auto deref =
|
||||
pool.alloc_element<DerefElement>(cast_source, false, std::vector<DerefToken>());
|
||||
return pool.alloc_element<StorePlainDeref>(deref, m_value.as_expr(), m_my_idx, ro.var,
|
||||
TypeSpec("pointer", {TypeSpec(cast_type)}));
|
||||
TypeSpec("pointer", {TypeSpec(cast_type)}),
|
||||
std::nullopt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -412,7 +470,8 @@ FormElement* LoadVarOp::get_as_form(FormPool& pool, const Env& env) const {
|
||||
m_type.value_or(TypeSpec("object")));
|
||||
}
|
||||
|
||||
if (input_type.typespec() == TypeSpec("pointer")) {
|
||||
if (input_type.typespec() == TypeSpec("pointer") ||
|
||||
input_type.kind == TP_Type::Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT) {
|
||||
std::string cast_type;
|
||||
switch (m_size) {
|
||||
case 1:
|
||||
|
@ -672,7 +672,8 @@ TP_Type LoadVarOp::get_src_type(const TypeState& input,
|
||||
return TP_Type::make_from_ts(coerce_to_reg_type(rd.result_type));
|
||||
}
|
||||
|
||||
if (input_type.typespec() == TypeSpec("pointer")) {
|
||||
if (input_type.typespec() == TypeSpec("pointer") ||
|
||||
input_type.kind == TP_Type::Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT) {
|
||||
// we got a plain pointer. let's just assume we're loading an integer.
|
||||
// perhaps we should disable this feature by default on 4-byte loads if we're getting
|
||||
// lots of false positives for loading pointers from plain pointers.
|
||||
|
@ -168,6 +168,39 @@ goos::Object Env::get_variable_name_with_cast(Register reg, int atomic_idx, Acce
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<TypeSpec> Env::get_user_cast_for_access(const RegisterAccess& access) const {
|
||||
if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) {
|
||||
auto& var_info = m_var_names.lookup(access.reg(), access.idx(), access.mode());
|
||||
std::string original_name = var_info.name();
|
||||
|
||||
auto type_kv = m_typecasts.find(access.idx());
|
||||
if (type_kv != m_typecasts.end()) {
|
||||
for (auto& x : type_kv->second) {
|
||||
if (x.reg == access.reg()) {
|
||||
// let's make sure the above claim is true
|
||||
TypeSpec type_in_reg;
|
||||
if (has_type_analysis() && access.mode() == AccessMode::READ) {
|
||||
type_in_reg =
|
||||
get_types_for_op_mode(access.idx(), AccessMode::READ).get(access.reg()).typespec();
|
||||
if (type_in_reg.print() != x.type_name) {
|
||||
lg::error(
|
||||
"Decompiler type consistency error. There was a typecast for reg {} at idx {} "
|
||||
"(var {}) to type {}, but the actual type is {} ({})",
|
||||
access.reg().to_charp(), access.idx(), original_name, x.type_name,
|
||||
type_in_reg.print(), type_in_reg.print());
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
auto cast_type = dts->parse_type_spec(x.type_name);
|
||||
return cast_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string Env::get_variable_name(const RegisterAccess& access) const {
|
||||
if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) {
|
||||
std::string lookup_name = m_var_names.lookup(access.reg(), access.idx(), access.mode()).name();
|
||||
@ -186,15 +219,17 @@ std::string Env::get_variable_name(const RegisterAccess& access) const {
|
||||
* NOTE: this is _NOT_ the most specific type known to the decompiler, but instead the type
|
||||
* of the variable.
|
||||
*/
|
||||
TypeSpec Env::get_variable_type(const RegisterAccess& access) const {
|
||||
TypeSpec Env::get_variable_type(const RegisterAccess& access, bool using_user_var_types) const {
|
||||
if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) {
|
||||
auto& var_info = m_var_names.lookup(access.reg(), access.idx(), access.mode());
|
||||
std::string original_name = var_info.name();
|
||||
|
||||
auto type_of_var = var_info.type.typespec();
|
||||
auto retype_kv = m_var_retype.find(original_name);
|
||||
if (retype_kv != m_var_retype.end()) {
|
||||
type_of_var = retype_kv->second;
|
||||
if (using_user_var_types) {
|
||||
auto retype_kv = m_var_retype.find(original_name);
|
||||
if (retype_kv != m_var_retype.end()) {
|
||||
type_of_var = retype_kv->second;
|
||||
}
|
||||
}
|
||||
|
||||
return type_of_var;
|
||||
@ -208,7 +243,8 @@ TypeSpec Env::get_variable_type(const RegisterAccess& access) const {
|
||||
*/
|
||||
void Env::set_types(const std::vector<TypeState>& block_init_types,
|
||||
const std::vector<TypeState>& op_end_types,
|
||||
const FunctionAtomicOps& atomic_ops) {
|
||||
const FunctionAtomicOps& atomic_ops,
|
||||
const TypeSpec& my_type) {
|
||||
m_block_init_types = block_init_types;
|
||||
m_op_end_types = op_end_types;
|
||||
|
||||
@ -230,6 +266,16 @@ void Env::set_types(const std::vector<TypeState>& block_init_types,
|
||||
}
|
||||
|
||||
m_has_types = true;
|
||||
|
||||
// check the actual return type:
|
||||
if (my_type.last_arg() != TypeSpec("none")) {
|
||||
auto as_end = dynamic_cast<const FunctionEndOp*>(atomic_ops.ops.back().get());
|
||||
if (as_end) {
|
||||
m_type_analysis_return_type = get_types_before_op((int)atomic_ops.ops.size() - 1)
|
||||
.get(Register(Reg::GPR, Reg::V0))
|
||||
.typespec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Env::print_local_var_types(const Form* top_level_form) const {
|
||||
|
@ -46,7 +46,8 @@ class Env {
|
||||
// TODO - remove this.
|
||||
goos::Object get_variable_name_with_cast(Register reg, int atomic_idx, AccessMode mode) const;
|
||||
std::string get_variable_name(const RegisterAccess& access) const;
|
||||
TypeSpec get_variable_type(const RegisterAccess& access) const;
|
||||
std::optional<TypeSpec> get_user_cast_for_access(const RegisterAccess& access) const;
|
||||
TypeSpec get_variable_type(const RegisterAccess& access, bool using_user_var_types) const;
|
||||
|
||||
/*!
|
||||
* Get the types in registers _after_ the given operation has completed.
|
||||
@ -80,7 +81,8 @@ class Env {
|
||||
|
||||
void set_types(const std::vector<TypeState>& block_init_types,
|
||||
const std::vector<TypeState>& op_end_types,
|
||||
const FunctionAtomicOps& atomic_ops);
|
||||
const FunctionAtomicOps& atomic_ops,
|
||||
const TypeSpec& my_type);
|
||||
|
||||
void set_local_vars(const VariableNames& names) {
|
||||
m_var_names = names;
|
||||
@ -168,5 +170,6 @@ class Env {
|
||||
std::unordered_map<std::string, LabelType> m_label_types;
|
||||
|
||||
std::unordered_set<std::string> m_vars_defined_in_let;
|
||||
std::optional<TypeSpec> m_type_analysis_return_type;
|
||||
};
|
||||
} // namespace decompiler
|
@ -1415,6 +1415,8 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
|
||||
return "&+!";
|
||||
case FixedOperatorKind::SUBTRACTION:
|
||||
return "-";
|
||||
case FixedOperatorKind::SUBTRACTION_PTR:
|
||||
return "&-";
|
||||
case FixedOperatorKind::MULTIPLICATION:
|
||||
return "*";
|
||||
case FixedOperatorKind::SQRT:
|
||||
@ -1481,6 +1483,8 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
|
||||
return "null?";
|
||||
case FixedOperatorKind::PAIRP:
|
||||
return "pair?";
|
||||
case FixedOperatorKind::NONE:
|
||||
return "none";
|
||||
default:
|
||||
assert(false);
|
||||
return "";
|
||||
@ -1950,22 +1954,35 @@ StorePlainDeref::StorePlainDeref(DerefElement* dst,
|
||||
SimpleExpression expr,
|
||||
int my_idx,
|
||||
RegisterAccess base_var,
|
||||
std::optional<TypeSpec> cast_type)
|
||||
std::optional<TypeSpec> dst_cast_type,
|
||||
std::optional<TypeSpec> src_cast_type)
|
||||
: m_dst(dst),
|
||||
m_expr(std::move(expr)),
|
||||
m_my_idx(my_idx),
|
||||
m_base_var(std::move(base_var)),
|
||||
m_cast_type(cast_type) {}
|
||||
m_base_var(base_var),
|
||||
m_dst_cast_type(std::move(dst_cast_type)),
|
||||
m_src_cast_type(std::move(src_cast_type)) {}
|
||||
|
||||
goos::Object StorePlainDeref::to_form_internal(const Env& env) const {
|
||||
if (!m_cast_type.has_value()) {
|
||||
return pretty_print::build_list("set!", m_dst->to_form(env),
|
||||
m_expr.to_form(env.file->labels, env));
|
||||
std::vector<goos::Object> lst = {pretty_print::to_symbol("set!")};
|
||||
|
||||
if (m_dst_cast_type) {
|
||||
lst.push_back(
|
||||
pretty_print::build_list("the-as", m_dst_cast_type->print(), m_dst->to_form(env)));
|
||||
} else {
|
||||
return pretty_print::build_list(
|
||||
"set!", pretty_print::build_list("the-as", m_cast_type->print(), m_dst->to_form(env)),
|
||||
m_expr.to_form(env.file->labels, env));
|
||||
lst.push_back(m_dst->to_form(env));
|
||||
}
|
||||
|
||||
if (m_src_cast_type) {
|
||||
lst.push_back(pretty_print::build_list("the-as", m_src_cast_type->print(),
|
||||
m_expr.to_form(env.file->labels, env)));
|
||||
} else {
|
||||
lst.push_back(m_expr.to_form(env.file->labels, env));
|
||||
}
|
||||
|
||||
return pretty_print::build_list(lst);
|
||||
}
|
||||
|
||||
void StorePlainDeref::apply(const std::function<void(FormElement*)>& f) {
|
||||
f(this);
|
||||
m_dst->apply(f);
|
||||
|
@ -1136,7 +1136,8 @@ class StorePlainDeref : public FormElement {
|
||||
SimpleExpression expr,
|
||||
int my_idx,
|
||||
RegisterAccess base_var,
|
||||
std::optional<TypeSpec> cast_type);
|
||||
std::optional<TypeSpec> dst_cast_type,
|
||||
std::optional<TypeSpec> src_cast_type);
|
||||
|
||||
goos::Object to_form_internal(const Env& env) const override;
|
||||
void apply(const std::function<void(FormElement*)>& f) override;
|
||||
@ -1150,7 +1151,7 @@ class StorePlainDeref : public FormElement {
|
||||
SimpleExpression m_expr;
|
||||
int m_my_idx = -1;
|
||||
RegisterAccess m_base_var;
|
||||
std::optional<TypeSpec> m_cast_type;
|
||||
std::optional<TypeSpec> m_dst_cast_type, m_src_cast_type;
|
||||
};
|
||||
|
||||
class StoreArrayAccess : public FormElement {
|
||||
|
@ -228,6 +228,20 @@ std::vector<Form*> pop_to_forms(const std::vector<RegisterAccess>& vars,
|
||||
for (auto& x : forms_out) {
|
||||
forms.push_back(pool.alloc_sequence_form(nullptr, x));
|
||||
}
|
||||
|
||||
// add casts, if needed.
|
||||
assert(vars.size() == forms.size());
|
||||
for (size_t i = 0; i < vars.size(); i++) {
|
||||
auto atom = form_as_atom(forms[i]);
|
||||
bool is_var = atom && atom->is_var();
|
||||
auto cast = env.get_user_cast_for_access(vars[i]);
|
||||
// only cast if we didn't get a var (compacting expressions).
|
||||
// there is a separate system for casting variables that will do a better job.
|
||||
if (cast && !is_var) {
|
||||
forms[i] = pool.alloc_single_element_form<CastElement>(nullptr, *cast, forms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return forms;
|
||||
}
|
||||
|
||||
@ -250,6 +264,11 @@ bool is_int_type(const Env& env, int my_idx, RegisterAccess var) {
|
||||
return type == TypeSpec("int");
|
||||
}
|
||||
|
||||
bool is_pointer_type(const Env& env, int my_idx, RegisterAccess var) {
|
||||
auto type = env.get_types_before_op(my_idx).get(var.reg()).typespec();
|
||||
return type.base_type() == "pointer";
|
||||
}
|
||||
|
||||
/*!
|
||||
* type == uint (exactly)?
|
||||
*/
|
||||
@ -258,10 +277,19 @@ bool is_uint_type(const Env& env, int my_idx, RegisterAccess var) {
|
||||
return type == TypeSpec("uint");
|
||||
}
|
||||
|
||||
bool is_ptr_or_child(const Env& env, int my_idx, RegisterAccess var) {
|
||||
auto type = env.get_types_before_op(my_idx).get(var.reg()).typespec().base_type();
|
||||
bool is_ptr_or_child(const Env& env, int my_idx, RegisterAccess var, bool as_var) {
|
||||
auto type = as_var ? env.get_variable_type(var, true).base_type()
|
||||
: env.get_types_before_op(my_idx).get(var.reg()).typespec().base_type();
|
||||
return type == "pointer";
|
||||
}
|
||||
|
||||
bool is_var(Form* form) {
|
||||
auto atom = form_as_atom(form);
|
||||
if (atom) {
|
||||
return atom->is_var();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/*!
|
||||
@ -511,7 +539,6 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
|
||||
bool allow_side_effects) {
|
||||
auto arg0_i = is_int_type(env, m_my_idx, m_expr.get_arg(0).var());
|
||||
auto arg0_u = is_uint_type(env, m_my_idx, m_expr.get_arg(0).var());
|
||||
bool arg0_ptr = is_ptr_or_child(env, m_my_idx, m_expr.get_arg(0).var());
|
||||
|
||||
bool arg1_reg = m_expr.get_arg(1).is_var();
|
||||
bool arg1_i = true;
|
||||
@ -531,6 +558,8 @@ 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)));
|
||||
}
|
||||
|
||||
bool arg0_ptr = is_ptr_or_child(env, m_my_idx, m_expr.get_arg(0).var(), is_var(args.at(0)));
|
||||
|
||||
// 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.
|
||||
@ -802,6 +831,10 @@ void SimpleExpressionElement::update_from_stack_copy_first_int_2(const Env& env,
|
||||
} else {
|
||||
auto cast = pool.alloc_single_element_form<CastElement>(
|
||||
nullptr, TypeSpec(arg0_i ? "int" : "uint"), args.at(1));
|
||||
if (kind == FixedOperatorKind::SUBTRACTION &&
|
||||
is_pointer_type(env, m_my_idx, m_expr.get_arg(0).var())) {
|
||||
kind = FixedOperatorKind::SUBTRACTION_PTR;
|
||||
}
|
||||
auto new_form =
|
||||
pool.alloc_element<GenericElement>(GenericOperator::make_fixed(kind), args.at(0), cast);
|
||||
result->push_back(new_form);
|
||||
@ -1107,38 +1140,39 @@ void StoreInPairElement::push_to_stack(const Env& env, FormPool& pool, FormStack
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
Form* make_optional_cast(const std::optional<TypeSpec>& cast_type, Form* in, FormPool& pool) {
|
||||
if (cast_type) {
|
||||
return pool.alloc_single_element_form<CastElement>(nullptr, *cast_type, in);
|
||||
} else {
|
||||
return in;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void StorePlainDeref::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) {
|
||||
mark_popped();
|
||||
if (m_expr.is_var()) {
|
||||
auto vars = std::vector<RegisterAccess>({m_expr.var(), m_base_var});
|
||||
auto popped = pop_to_forms(vars, env, pool, stack, true);
|
||||
if (m_cast_type.has_value()) {
|
||||
m_dst->set_base(
|
||||
pool.alloc_single_element_form<CastElement>(nullptr, *m_cast_type, popped.at(1)));
|
||||
} else {
|
||||
m_dst->set_base(popped.at(1));
|
||||
}
|
||||
|
||||
m_dst->set_base(make_optional_cast(m_dst_cast_type, popped.at(1), pool));
|
||||
m_dst->mark_popped();
|
||||
m_dst->inline_nested();
|
||||
auto fr = pool.alloc_element<SetFormFormElement>(pool.alloc_single_form(nullptr, m_dst),
|
||||
popped.at(0));
|
||||
auto fr = pool.alloc_element<SetFormFormElement>(
|
||||
pool.alloc_single_form(nullptr, m_dst),
|
||||
make_optional_cast(m_src_cast_type, popped.at(0), pool));
|
||||
fr->mark_popped();
|
||||
stack.push_form_element(fr, true);
|
||||
} else {
|
||||
auto vars = std::vector<RegisterAccess>({m_base_var});
|
||||
auto popped = pop_to_forms(vars, env, pool, stack, true);
|
||||
if (m_cast_type.has_value()) {
|
||||
m_dst->set_base(
|
||||
pool.alloc_single_element_form<CastElement>(nullptr, *m_cast_type, popped.at(1)));
|
||||
} else {
|
||||
m_dst->set_base(popped.at(0));
|
||||
}
|
||||
m_dst->set_base(make_optional_cast(m_dst_cast_type, popped.at(0), pool));
|
||||
m_dst->mark_popped();
|
||||
m_dst->inline_nested();
|
||||
auto val = pool.alloc_single_element_form<SimpleExpressionElement>(nullptr, m_expr, m_my_idx);
|
||||
val->mark_popped();
|
||||
auto fr = pool.alloc_element<SetFormFormElement>(pool.alloc_single_form(nullptr, m_dst), val);
|
||||
auto fr = pool.alloc_element<SetFormFormElement>(
|
||||
pool.alloc_single_form(nullptr, m_dst), make_optional_cast(m_src_cast_type, val, pool));
|
||||
fr->mark_popped();
|
||||
stack.push_form_element(fr, true);
|
||||
}
|
||||
@ -1232,19 +1266,11 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
function_type = tp_type.typespec();
|
||||
}
|
||||
|
||||
// assert(is_method == m_op->is_method());
|
||||
if (is_virtual_method != m_op->is_method()) {
|
||||
lg::error("Disagreement on method!");
|
||||
throw std::runtime_error("Disagreement on method");
|
||||
}
|
||||
|
||||
// if method, don't pop the obj arg.
|
||||
// Variable method_obj_var;
|
||||
// if (is_method) {
|
||||
// method_obj_var = all_pop_vars.at(1);
|
||||
// all_pop_vars.erase(all_pop_vars.begin() + 1);
|
||||
// }
|
||||
|
||||
if (tp_type.kind == TP_Type::Kind::NON_VIRTUAL_METHOD) {
|
||||
std::swap(all_pop_vars.at(0), all_pop_vars.at(1));
|
||||
}
|
||||
@ -1257,20 +1283,39 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
|
||||
std::vector<Form*> arg_forms;
|
||||
|
||||
for (size_t arg_id = 0; arg_id < nargs; arg_id++) {
|
||||
auto val = unstacked.at(arg_id + 1); // first is the function itself.
|
||||
auto& var = all_pop_vars.at(arg_id + 1);
|
||||
if (env.has_type_analysis() && function_type.arg_count() == nargs + 1) {
|
||||
auto actual_arg_type = env.get_types_before_op(var.idx()).get(var.reg()).typespec();
|
||||
auto desired_arg_type = function_type.get_arg(arg_id);
|
||||
if (!env.dts->ts.tc(desired_arg_type, actual_arg_type)) {
|
||||
arg_forms.push_back(
|
||||
pool.alloc_single_element_form<CastElement>(nullptr, desired_arg_type, val));
|
||||
if (is_virtual_method) {
|
||||
for (size_t arg_id = 0; arg_id < nargs; arg_id++) {
|
||||
auto val = unstacked.at(arg_id + 1); // first is the function itself.
|
||||
auto& var = all_pop_vars.at(arg_id + 1);
|
||||
if (env.has_type_analysis() && function_type.arg_count() == nargs + 2) {
|
||||
auto actual_arg_type = env.get_types_before_op(var.idx()).get(var.reg()).typespec();
|
||||
auto desired_arg_type = function_type.get_arg(arg_id + 1);
|
||||
if (!env.dts->ts.tc(desired_arg_type, actual_arg_type)) {
|
||||
arg_forms.push_back(
|
||||
pool.alloc_single_element_form<CastElement>(nullptr, desired_arg_type, val));
|
||||
} else {
|
||||
arg_forms.push_back(val);
|
||||
}
|
||||
} else {
|
||||
arg_forms.push_back(val);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t arg_id = 0; arg_id < nargs; arg_id++) {
|
||||
auto val = unstacked.at(arg_id + 1); // first is the function itself.
|
||||
auto& var = all_pop_vars.at(arg_id + 1);
|
||||
if (env.has_type_analysis() && function_type.arg_count() == nargs + 1) {
|
||||
auto actual_arg_type = env.get_types_before_op(var.idx()).get(var.reg()).typespec();
|
||||
auto desired_arg_type = function_type.get_arg(arg_id);
|
||||
if (!env.dts->ts.tc(desired_arg_type, actual_arg_type)) {
|
||||
arg_forms.push_back(
|
||||
pool.alloc_single_element_form<CastElement>(nullptr, desired_arg_type, val));
|
||||
} else {
|
||||
arg_forms.push_back(val);
|
||||
}
|
||||
} else {
|
||||
arg_forms.push_back(val);
|
||||
}
|
||||
} else {
|
||||
arg_forms.push_back(val);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1650,7 +1695,7 @@ void CondNoElseElement::push_to_stack(const Env& env, FormPool& pool, FormStack&
|
||||
if (used_as_value) {
|
||||
// TODO - is this wrong?
|
||||
stack.push_value_to_reg(final_destination, pool.alloc_single_form(nullptr, this), true,
|
||||
env.get_variable_type(final_destination));
|
||||
env.get_variable_type(final_destination, false));
|
||||
} else {
|
||||
stack.push_form_element(this, true);
|
||||
}
|
||||
@ -1775,7 +1820,7 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac
|
||||
stack.push_form_element(this, true);
|
||||
} else {
|
||||
stack.push_value_to_reg(*last_var, pool.alloc_single_form(nullptr, this), true,
|
||||
env.get_variable_type(*last_var));
|
||||
env.get_variable_type(*last_var, false));
|
||||
}
|
||||
} else {
|
||||
stack.push_form_element(this, true);
|
||||
@ -1834,7 +1879,7 @@ void ShortCircuitElement::push_to_stack(const Env& env, FormPool& pool, FormStac
|
||||
|
||||
assert(used_as_value.has_value());
|
||||
stack.push_value_to_reg(final_result, pool.alloc_single_form(nullptr, this), true,
|
||||
env.get_variable_type(final_result));
|
||||
env.get_variable_type(final_result, false));
|
||||
already_rewritten = true;
|
||||
}
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ FormElement* FormStack::pop_back(FormPool& pool) {
|
||||
namespace {
|
||||
bool is_op_in_place(SetVarElement* elt,
|
||||
FixedOperatorKind op,
|
||||
const Env&,
|
||||
const Env& env,
|
||||
RegisterAccess* base_out,
|
||||
Form** val_out) {
|
||||
auto matcher = Matcher::op(GenericOpMatcher::fixed(op), {Matcher::any_reg(0), Matcher::any(1)});
|
||||
@ -240,6 +240,7 @@ bool is_op_in_place(SetVarElement* elt,
|
||||
if (result.matched) {
|
||||
auto first = result.maps.regs.at(0);
|
||||
assert(first.has_value());
|
||||
|
||||
if (first->reg() != elt->dst().reg()) {
|
||||
return false;
|
||||
}
|
||||
@ -248,6 +249,14 @@ bool is_op_in_place(SetVarElement* elt,
|
||||
return false;
|
||||
}
|
||||
|
||||
auto src_var = env.get_variable_name(*first);
|
||||
auto dst_var = env.get_variable_name(elt->dst());
|
||||
if (src_var != dst_var) {
|
||||
// something like daddu a1-1, a1-0, v0 isn't safe to turn into an in-place, but will pass
|
||||
// the previous two checks.
|
||||
return false;
|
||||
}
|
||||
|
||||
*val_out = result.maps.forms.at(1);
|
||||
*base_out = first.value();
|
||||
return true;
|
||||
|
@ -105,6 +105,7 @@ enum class FixedOperatorKind {
|
||||
ADDITION_IN_PLACE,
|
||||
ADDITION_PTR_IN_PLACE,
|
||||
SUBTRACTION,
|
||||
SUBTRACTION_PTR,
|
||||
MULTIPLICATION,
|
||||
SQRT,
|
||||
ARITH_SHIFT,
|
||||
@ -138,6 +139,7 @@ enum class FixedOperatorKind {
|
||||
METHOD_OF_OBJECT,
|
||||
NULLP,
|
||||
PAIRP,
|
||||
NONE,
|
||||
INVALID
|
||||
};
|
||||
|
||||
|
@ -19,6 +19,31 @@ bool convert_to_expressions(
|
||||
const DecompilerTypeSystem& dts) {
|
||||
assert(top_level_form);
|
||||
|
||||
// set argument names to some reasonable defaults. these will be used if the user doesn't
|
||||
// give us anything more specific.
|
||||
if (f.guessed_name.kind == FunctionName::FunctionKind::GLOBAL) {
|
||||
f.ir2.env.set_remap_for_function(f.type.arg_count() - 1);
|
||||
} else if (f.guessed_name.kind == FunctionName::FunctionKind::METHOD) {
|
||||
if (f.guessed_name.method_id == GOAL_NEW_METHOD) {
|
||||
f.ir2.env.set_remap_for_new_method(f.type.arg_count() - 1);
|
||||
} else {
|
||||
f.ir2.env.set_remap_for_method(f.type.arg_count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// get variable names from the user.
|
||||
f.ir2.env.map_args_from_config(arg_names, var_override_map);
|
||||
|
||||
// override variable types from the user.
|
||||
|
||||
std::unordered_map<std::string, TypeSpec> retype;
|
||||
for (auto& remap : var_override_map) {
|
||||
if (remap.second.type) {
|
||||
retype[remap.first] = dts.parse_type_spec(*remap.second.type);
|
||||
}
|
||||
}
|
||||
f.ir2.env.set_retype_map(retype);
|
||||
|
||||
try {
|
||||
// create the root expression stack for the function
|
||||
FormStack stack(true);
|
||||
@ -45,6 +70,8 @@ bool convert_to_expressions(
|
||||
} else {
|
||||
// or just get all the expressions
|
||||
new_entries = stack.rewrite(pool, f.ir2.env);
|
||||
new_entries.push_back(
|
||||
pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::NONE)));
|
||||
}
|
||||
|
||||
// if we are a totally empty function, insert a placeholder so we don't have to handle
|
||||
@ -70,31 +97,6 @@ bool convert_to_expressions(
|
||||
return false;
|
||||
}
|
||||
|
||||
// set argument names to some reasonable defaults. these will be used if the user doesn't
|
||||
// give us anything more specific.
|
||||
if (f.guessed_name.kind == FunctionName::FunctionKind::GLOBAL) {
|
||||
f.ir2.env.set_remap_for_function(f.type.arg_count() - 1);
|
||||
} else if (f.guessed_name.kind == FunctionName::FunctionKind::METHOD) {
|
||||
if (f.guessed_name.method_id == GOAL_NEW_METHOD) {
|
||||
f.ir2.env.set_remap_for_new_method(f.type.arg_count() - 1);
|
||||
} else {
|
||||
f.ir2.env.set_remap_for_method(f.type.arg_count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// get variable names from the user.
|
||||
f.ir2.env.map_args_from_config(arg_names, var_override_map);
|
||||
|
||||
// override variable types from the user.
|
||||
|
||||
std::unordered_map<std::string, TypeSpec> retype;
|
||||
for (auto& remap : var_override_map) {
|
||||
if (remap.second.type) {
|
||||
retype[remap.first] = dts.parse_type_spec(*remap.second.type);
|
||||
}
|
||||
}
|
||||
f.ir2.env.set_retype_map(retype);
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace decompiler
|
||||
|
@ -222,7 +222,7 @@ Form* insert_cast_for_let(RegisterAccess dst,
|
||||
Form* src,
|
||||
FormPool& pool,
|
||||
const Env& env) {
|
||||
auto dst_type = env.get_variable_type(dst);
|
||||
auto dst_type = env.get_variable_type(dst, true);
|
||||
|
||||
if (src_type != dst_type) {
|
||||
// fmt::print("inserting let cast because {} != {}\n", dst_type.print(), src_type.print());
|
||||
|
@ -2507,7 +2507,7 @@
|
||||
;;(define-extern dma-sync object) ;; unknown type
|
||||
;;(define-extern dma-packet-array object) ;; unknown type
|
||||
(define-extern dma-buffer-inplace-new (function dma-buffer int dma-buffer))
|
||||
(define-extern dma-buffer-length (function dma-buffer uint))
|
||||
(define-extern dma-buffer-length (function dma-buffer int))
|
||||
(define-extern dma-buffer-free (function dma-buffer int))
|
||||
|
||||
;;(define-extern dma-gif-packet object) ;; unknown type
|
||||
|
@ -111,6 +111,7 @@
|
||||
"(method 0 catch-frame)",
|
||||
"throw-dispatch",
|
||||
"set-to-run-bootstrap",
|
||||
"run-function-in-process", // not asm, but it uses the stack.
|
||||
|
||||
// pskernel
|
||||
"return-from-exception", // F: eret
|
||||
|
@ -5,7 +5,13 @@
|
||||
],
|
||||
|
||||
"gkernel": [
|
||||
["L345", "_auto_", true]
|
||||
["L345", "_auto_", true],
|
||||
["L344", "_auto_", true],
|
||||
["L346", "float", true],
|
||||
["L347", "float", true],
|
||||
["L348", "float", true],
|
||||
["L289", "_auto_", true],
|
||||
["L282", "_auto_", true]
|
||||
],
|
||||
|
||||
"math": [
|
||||
|
@ -64,7 +64,7 @@
|
||||
|
||||
"(method 0 dead-pool-heap)": [
|
||||
[60, "v0", "int"], // a lie, actually the 115 is an align16 constant propagated on addr of heap start.
|
||||
[63, "a0", "pointer"],
|
||||
//[63, "a0", "pointer"],
|
||||
[[61, 73], "v0", "dead-pool-heap"]
|
||||
],
|
||||
|
||||
@ -89,13 +89,13 @@
|
||||
"(method 9 process)": [[43, "s5", "process"]],
|
||||
|
||||
"(method 14 dead-pool)": [
|
||||
[[24, 26], "v1", "(pointer process-tree)"],
|
||||
[[24, 25], "v1", "(pointer process-tree)"],
|
||||
[[30, 39], "s4", "(pointer process-tree)"]
|
||||
],
|
||||
|
||||
"inspect-process-heap": [
|
||||
[[4, 11], "s5", "basic"],
|
||||
[17, "s5", "int"]
|
||||
[17, "s5", "pointer"]
|
||||
],
|
||||
|
||||
"name=": [
|
||||
|
@ -191,6 +191,16 @@
|
||||
"vars":{"v1-1":"in-goal-mem"}
|
||||
},
|
||||
|
||||
// GKERNEL
|
||||
|
||||
"(method 0 cpu-thread)":{
|
||||
"vars":{"v0-0":["obj", "cpu-thread"]}
|
||||
},
|
||||
|
||||
"inspect-process-heap":{
|
||||
"vars":{"s5-0":["obj", "pointer"]}
|
||||
},
|
||||
|
||||
"(method 23 dead-pool-heap)":{
|
||||
"args":["this", "rec"]
|
||||
},
|
||||
|
@ -130,10 +130,13 @@ TypeSpec TP_Type::typespec() const {
|
||||
case Kind::PRODUCT_WITH_CONSTANT:
|
||||
return m_ts;
|
||||
case Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT:
|
||||
if (m_ts.base_type() == "pointer") {
|
||||
return TypeSpec("pointer");
|
||||
}
|
||||
// this can be part of an array access, so we don't really know the type.
|
||||
// probably not a good idea to try to do anything with this as a typespec
|
||||
// so let's be very vague
|
||||
return TypeSpec("pointer");
|
||||
return TypeSpec("int");
|
||||
case Kind::OBJECT_NEW_METHOD:
|
||||
// similar to previous case, being more vague than we need to be because we don't
|
||||
// want to assume the return type incorrectly and you shouldn't try to do anything with
|
||||
|
@ -40,6 +40,35 @@ class TP_Type {
|
||||
bool operator!=(const TP_Type& other) const;
|
||||
TypeSpec typespec() const;
|
||||
|
||||
/*!
|
||||
* Returns true if the expression with this type should always be wrapped in a cast.
|
||||
*/
|
||||
bool requires_cast() const {
|
||||
switch (kind) {
|
||||
case Kind::TYPESPEC:
|
||||
case Kind::TYPE_OF_TYPE_OR_CHILD:
|
||||
case Kind::TYPE_OF_TYPE_NO_VIRTUAL:
|
||||
case Kind::FALSE_AS_NULL: // if we want all #f's for references to be cast, move this.
|
||||
case Kind::PRODUCT_WITH_CONSTANT:
|
||||
case Kind::STRING_CONSTANT:
|
||||
case Kind::FORMAT_STRING:
|
||||
case Kind::INTEGER_CONSTANT:
|
||||
case Kind::INTEGER_CONSTANT_PLUS_VAR:
|
||||
case Kind::INTEGER_CONSTANT_PLUS_VAR_MULT:
|
||||
case Kind::VIRTUAL_METHOD:
|
||||
case Kind::NON_VIRTUAL_METHOD:
|
||||
return false;
|
||||
case Kind::UNINITIALIZED:
|
||||
case Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT:
|
||||
case Kind::OBJECT_NEW_METHOD:
|
||||
case Kind::DYNAMIC_METHOD_ACCESS:
|
||||
return true;
|
||||
case Kind::INVALID:
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_constant_string() const { return kind == Kind::STRING_CONSTANT; }
|
||||
bool is_integer_constant() const { return kind == Kind::INTEGER_CONSTANT; }
|
||||
bool is_integer_constant(int64_t value) const { return is_integer_constant() && m_int == value; }
|
||||
|
@ -722,7 +722,7 @@ goos::Object decompile_pair(const DecompilerLabel& label,
|
||||
auto cdr_word = words.at(to_print.target_segment).at((to_print.offset + 2) / 4);
|
||||
// if empty
|
||||
if (cdr_word.kind == LinkedWord::EMPTY_PTR) {
|
||||
return pretty_print::build_list(list_tokens);
|
||||
return pretty_print::build_list("quote", pretty_print::build_list(list_tokens));
|
||||
}
|
||||
// if pointer
|
||||
if (cdr_word.kind == LinkedWord::PTR) {
|
||||
@ -736,7 +736,7 @@ goos::Object decompile_pair(const DecompilerLabel& label,
|
||||
"could not find a test case yet.");
|
||||
list_tokens.push_back(pretty_print::to_symbol("."));
|
||||
list_tokens.push_back(decompile_pair_elt(cdr_word, labels, words, ts));
|
||||
return pretty_print::build_list(list_tokens);
|
||||
return pretty_print::build_list("quote", pretty_print::build_list(list_tokens));
|
||||
} else {
|
||||
if ((to_print.offset % 4) != 0) {
|
||||
throw std::runtime_error(
|
||||
@ -755,7 +755,7 @@ goos::Object decompile_pair(const DecompilerLabel& label,
|
||||
list_tokens.push_back(pretty_print::to_symbol("."));
|
||||
list_tokens.push_back(decompile_pair_elt(
|
||||
words.at(to_print.target_segment).at(to_print.offset / 4), labels, words, ts));
|
||||
return pretty_print::build_list(list_tokens);
|
||||
return pretty_print::build_list("quote", pretty_print::build_list(list_tokens));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,6 +172,11 @@
|
||||
`(/ 0 0)
|
||||
)
|
||||
|
||||
(defmacro break! ()
|
||||
"A breakpoint. Todo - should we use int3 instead?"
|
||||
`(/ 0 0)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;
|
||||
;; GOAL Syntax
|
||||
;;;;;;;;;;;;;;;;;;;
|
||||
@ -379,7 +384,7 @@
|
||||
)
|
||||
|
||||
(defmacro &- (a b)
|
||||
`(- (the-as uint ,a) (the-as uint ,b))
|
||||
`(- (the-as int ,a) (the-as int ,b))
|
||||
)
|
||||
|
||||
(defmacro &-> (&rest args)
|
||||
|
71
test/decompiler/reference/all_forward_declarations.gc
Normal file
71
test/decompiler/reference/all_forward_declarations.gc
Normal file
@ -0,0 +1,71 @@
|
||||
;; GCOMMON
|
||||
(define-extern name= (function basic basic symbol))
|
||||
(define-extern fact (function int int))
|
||||
|
||||
;; KERNEL
|
||||
(declare-type process basic)
|
||||
(declare-type stack-frame basic)
|
||||
(declare-type state basic)
|
||||
(declare-type cpu-thread basic)
|
||||
(declare-type dead-pool basic)
|
||||
(declare-type event-message-block structure)
|
||||
(declare-type thread basic)
|
||||
|
||||
(deftype process (process-tree)
|
||||
((pool dead-pool :offset-assert #x20)
|
||||
(status basic :offset-assert #x24)
|
||||
(pid int32 :offset-assert #x28)
|
||||
(main-thread cpu-thread :offset-assert #x2c)
|
||||
(top-thread thread :offset-assert #x30)
|
||||
(entity basic :offset-assert #x34)
|
||||
(state state :offset-assert #x38)
|
||||
(trans-hook function :offset-assert #x3c)
|
||||
(post-hook function :offset-assert #x40)
|
||||
(event-hook (function basic int basic event-message-block object) :offset-assert #x44)
|
||||
(allocated-length int32 :offset-assert #x48)
|
||||
(next-state state :offset-assert #x4c)
|
||||
(heap-base pointer :offset-assert #x50)
|
||||
(heap-top pointer :offset-assert #x54)
|
||||
(heap-cur pointer :offset-assert #x58)
|
||||
(stack-frame-top stack-frame :offset-assert #x5c)
|
||||
(connection-list connectable :inline :offset-assert #x60)
|
||||
(stack uint8 :dynamic :offset-assert #x70)
|
||||
)
|
||||
|
||||
(:methods
|
||||
(new (symbol type basic int) _type_ 0)
|
||||
(activate (_type_ process-tree basic pointer) process-tree 9)
|
||||
(deactivate (process) none 10)
|
||||
(dummy-method-11 () none 11)
|
||||
(run-logic? (process) symbol 12)
|
||||
(dummy-method () none 13)
|
||||
)
|
||||
|
||||
:size-assert #x70
|
||||
:method-count-assert 14
|
||||
:no-runtime-type ;; already defined by kscheme. Don't do it again.
|
||||
)
|
||||
|
||||
(declare-type dead-pool-heap basic)
|
||||
(define-extern *debug-dead-pool* dead-pool-heap)
|
||||
(define-extern change-parent (function process-tree process-tree process-tree))
|
||||
(define-extern *null-process* process)
|
||||
(define-extern *vis-boot* basic)
|
||||
(define-extern *stdcon* string)
|
||||
(declare-type kernel-context basic)
|
||||
(define-extern iterate-process-tree (function process-tree (function object object) kernel-context object))
|
||||
(define-extern execute-process-tree (function process-tree (function object object) kernel-context object))
|
||||
(define-extern search-process-tree (function process-tree (function process-tree object) process-tree))
|
||||
|
||||
(define-extern *listener-process* process)
|
||||
(define-extern *active-pool* process-tree)
|
||||
(define-extern reset-and-call (function thread function object))
|
||||
(define-extern ash (function int int int))
|
||||
(define-extern inspect-process-tree (function process-tree int int symbol process-tree))
|
||||
(define-extern set-to-run-bootstrap (function none))
|
||||
(define-extern dead-state state)
|
||||
(define-extern *display-pool* process-tree)
|
||||
(define-extern *camera-pool* process-tree)
|
||||
(define-extern *target-pool* process-tree)
|
||||
(define-extern *entity-pool* process-tree)
|
||||
(define-extern *default-pool* process-tree)
|
@ -918,11 +918,12 @@
|
||||
;; WARN: Inline assembly instruction marked with TODO - [TODO.LQ]
|
||||
;; WARN: Inline assembly instruction marked with TODO - [TODO.SQ]
|
||||
(defun qmem-copy->! ((dst pointer) (src pointer) (size int))
|
||||
(local-vars (src-ptr pointer) (dst-ptr pointer) (value int))
|
||||
(local-vars (value int))
|
||||
(let ((result dst))
|
||||
(let ((qwc (sar (+ size 15) 4)))
|
||||
(&+! dst (shl qwc 4))
|
||||
(&+! src (shl qwc 4))
|
||||
(let* ((qwc (sar (+ size 15) 4))
|
||||
(src-ptr (&+ dst (shl qwc 4)))
|
||||
(dst-ptr (&+ src (shl qwc 4)))
|
||||
)
|
||||
(while (nonzero? qwc)
|
||||
(+! qwc -1)
|
||||
(&+! src-ptr -16)
|
||||
@ -1317,4 +1318,7 @@
|
||||
|
||||
;; failed to figure out what this is:
|
||||
(let ((v0-3 0))
|
||||
)
|
||||
)
|
||||
|
||||
;; failed to figure out what this is:
|
||||
(none)
|
@ -379,6 +379,5 @@
|
||||
(let ((v0-11 0))
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
;; failed to figure out what this is:
|
||||
(none)
|
||||
|
1913
test/decompiler/reference/gkernel_REF.gc
Normal file
1913
test/decompiler/reference/gkernel_REF.gc
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,3 +4,6 @@
|
||||
;; failed to figure out what this is:
|
||||
(let ((v0-0 0))
|
||||
)
|
||||
|
||||
;; failed to figure out what this is:
|
||||
(none)
|
@ -315,7 +315,7 @@ TEST_F(DataDecompTest, ContinuePoint) {
|
||||
" -0.1328\n"
|
||||
" 0.5831\n"
|
||||
" )\n"
|
||||
" :load-commands (('special \"citb-exit-plat-4\" #t))\n"
|
||||
" :load-commands '('('special \"citb-exit-plat-4\" #t))\n"
|
||||
" :vis-nick 'fin\n"
|
||||
" :lev0 'finalboss\n"
|
||||
" :disp0 'display\n"
|
||||
|
@ -2372,7 +2372,7 @@ TEST_F(FormRegressionTest, ExprCopyStringString) {
|
||||
" (set! a1-1 (&-> a1-1 1))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! (-> v1-0 0) 0)\n"
|
||||
" (set! (-> v1-0 0) (the-as uint 0))\n"
|
||||
" )\n"
|
||||
" arg0\n"
|
||||
" )";
|
||||
@ -2575,4 +2575,54 @@ TEST_F(FormRegressionTest, MoveFalse) {
|
||||
|
||||
std::string expected = "(nonzero? (logand (+ arg0 12) 1))";
|
||||
test_with_expr(func, type, expected, false, "", {{"L17", "A ~A"}});
|
||||
}
|
||||
|
||||
// Good for testing that in-place ops (+!) check the _variable_ is the same.
|
||||
TEST_F(FormRegressionTest, QMemCpy) {
|
||||
std::string func =
|
||||
"sll r0, r0, 0\n"
|
||||
"L78:\n"
|
||||
" or v0, a0, r0\n"
|
||||
" daddiu v1, a2, 15\n"
|
||||
" dsra v1, v1, 4\n"
|
||||
" dsll a2, v1, 4\n"
|
||||
" daddu a0, a0, a2\n"
|
||||
" dsll a2, v1, 4\n"
|
||||
" daddu a1, a1, a2\n"
|
||||
" beq r0, r0, L80\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
"L79:\n"
|
||||
" daddiu v1, v1, -1\n"
|
||||
" daddiu a0, a0, -16\n"
|
||||
" daddiu a1, a1, -16\n"
|
||||
" lq a2, 0(a1)\n"
|
||||
" sq a2, 0(a0)\n"
|
||||
|
||||
"L80:\n"
|
||||
" bne v1, r0, L79\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" or v1, s7, r0\n"
|
||||
" or v1, s7, r0\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0\n";
|
||||
std::string type = "(function pointer pointer int pointer)";
|
||||
std::string expected =
|
||||
"(let ((v0-0 arg0))\n"
|
||||
" (let* ((v1-1 (sar (+ arg2 15) 4))\n"
|
||||
" (a0-1 (&+ arg0 (shl v1-1 4)))\n"
|
||||
" (a1-1 (&+ arg1 (shl v1-1 4)))\n"
|
||||
" )\n"
|
||||
" (while (nonzero? v1-1)\n"
|
||||
" (+! v1-1 -1)\n"
|
||||
" (&+! a0-1 -16)\n"
|
||||
" (&+! a1-1 -16)\n"
|
||||
" (.lq a2-3 0 a1-1)\n"
|
||||
" (.sq a2-3 0 a0-1)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" v0-0\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
@ -134,6 +134,7 @@ TEST_F(FormRegressionTest, ExprMethod1Thread) {
|
||||
"(begin\n"
|
||||
" (when (= arg0 (-> arg0 process main-thread)) (break!) (let ((v1-3 0))))\n"
|
||||
" (set! (-> arg0 process top-thread) (-> arg0 previous))\n"
|
||||
" (none)\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false);
|
||||
}
|
||||
@ -257,11 +258,13 @@ TEST_F(FormRegressionTest, ExprMethod9Thread) {
|
||||
std::string type = "(function thread int none)";
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (let\n"
|
||||
" ((a2-0 (-> arg0 process)))\n"
|
||||
" (let ((a2-0 (-> arg0 process)))\n"
|
||||
" (cond\n"
|
||||
" ((!= arg0 (-> a2-0 main-thread)) (format 0 \"1 ~A ~%\" a2-0))\n"
|
||||
" ((= (-> arg0 stack-size) arg1))\n"
|
||||
" ((!= arg0 (-> a2-0 main-thread))\n"
|
||||
" (format 0 \"1 ~A ~%\" a2-0)\n"
|
||||
" )\n"
|
||||
" ((= (-> arg0 stack-size) arg1)\n"
|
||||
" )\n"
|
||||
" ((=\n"
|
||||
" (-> a2-0 heap-cur)\n"
|
||||
" (+\n"
|
||||
@ -271,14 +274,21 @@ TEST_F(FormRegressionTest, ExprMethod9Thread) {
|
||||
" )\n"
|
||||
" (set!\n"
|
||||
" (-> a2-0 heap-cur)\n"
|
||||
" (+ (+ (+ arg1 -4) (the-as int (-> arg0 type size))) (the-as int arg0))\n"
|
||||
" (the-as\n"
|
||||
" pointer\n"
|
||||
" (+ (+ (+ arg1 -4) (the-as int (-> arg0 type size))) (the-as int arg0))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! (-> arg0 stack-size) arg1)\n"
|
||||
" )\n"
|
||||
" (else (format 0 \"2 ~A ~%\" a2-0))\n"
|
||||
" (else\n"
|
||||
" (format 0 \"2 ~A ~%\" a2-0)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (let ((v0-2 0)))\n"
|
||||
" (let ((v0-2 0))\n"
|
||||
" )\n"
|
||||
" (none)\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "", {{"L342", "1 ~A ~%"}, {"L341", "2 ~A ~%"}});
|
||||
}
|
||||
@ -325,35 +335,44 @@ TEST_F(FormRegressionTest, ExprMethod0Thread) {
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function symbol type process symbol int pointer cpu-thread)";
|
||||
std::string expected =
|
||||
"(let\n"
|
||||
" ((v0-0\n"
|
||||
" (if\n"
|
||||
" (-> arg2 top-thread)\n"
|
||||
" (&+ arg5 -7164)\n"
|
||||
" (let\n"
|
||||
" ((v1-2 (logand -16 (the-as int (&+ (-> arg2 heap-cur) 15)))))\n"
|
||||
" (set! (-> arg2 heap-cur) (+ (+ v1-2 (the-as int (-> arg1 size))) arg4))\n"
|
||||
" (+ v1-2 4)\n"
|
||||
"(let ((obj (the-as cpu-thread (if (-> arg2 top-thread)\n"
|
||||
" (&+ arg5 -7164)\n"
|
||||
" (let\n"
|
||||
" ((v1-2\n"
|
||||
" (logand\n"
|
||||
" -16\n"
|
||||
" (the-as int (&+ (-> arg2 heap-cur) 15))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set!\n"
|
||||
" (-> arg2 heap-cur)\n"
|
||||
" (the-as\n"
|
||||
" pointer\n"
|
||||
" (+ (+ v1-2 (the-as int (-> arg1 size))) arg4)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (+ v1-2 4)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! (-> (the-as cpu-thread v0-0) type) arg1)\n"
|
||||
" (set! (-> (the-as cpu-thread v0-0) name) arg3)\n"
|
||||
" (set! (-> (the-as cpu-thread v0-0) process) arg2)\n"
|
||||
" (set! (-> (the-as cpu-thread v0-0) sp) arg5)\n"
|
||||
" (set! (-> (the-as cpu-thread v0-0) stack-top) arg5)\n"
|
||||
" (set! (-> (the-as cpu-thread v0-0) previous) (-> arg2 top-thread))\n"
|
||||
" (set! (-> arg2 top-thread) (the-as cpu-thread v0-0))\n"
|
||||
" (set! (-> (the-as cpu-thread v0-0) suspend-hook) (method-of-object (the-as cpu-thread "
|
||||
"v0-0) thread-suspend))\n"
|
||||
" (set! (-> (the-as cpu-thread v0-0) resume-hook) (method-of-object (the-as cpu-thread "
|
||||
"v0-0) thread-resume))\n"
|
||||
" (set! (-> (the-as cpu-thread v0-0) stack-size) arg4)\n"
|
||||
" (the-as cpu-thread v0-0)\n"
|
||||
" (set! (-> obj type) arg1)\n"
|
||||
" (set! (-> obj name) arg3)\n"
|
||||
" (set! (-> obj process) arg2)\n"
|
||||
" (set! (-> obj sp) arg5)\n"
|
||||
" (set! (-> obj stack-top) arg5)\n"
|
||||
" (set! (-> obj previous) (-> arg2 top-thread))\n"
|
||||
" (set! (-> arg2 top-thread) obj)\n"
|
||||
" (set! (-> obj suspend-hook) (method-of-object obj thread-suspend))\n"
|
||||
" (set! (-> obj resume-hook) (method-of-object obj thread-resume))\n"
|
||||
" (set! (-> obj stack-size) arg4)\n"
|
||||
" (the-as cpu-thread (the-as object obj))\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "cpu-thread", {},
|
||||
parse_cast_json("[[[13, 28], \"v0\", \"cpu-thread\"]]"));
|
||||
parse_cast_json("[[[13, 28], \"v0\", \"cpu-thread\"]]"),
|
||||
"{\"vars\":{\"v0-0\":[\"obj\", \"cpu-thread\"]}}");
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod5CpuThread) {
|
||||
@ -433,10 +452,10 @@ TEST_F(FormRegressionTest, RemoveMethod0ProcessTree) {
|
||||
"(let\n"
|
||||
" ((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) #f)\n"
|
||||
" (set! (-> v0-0 brother) #f)\n"
|
||||
" (set! (-> v0-0 child) #f)\n"
|
||||
" (set! (-> v0-0 mask) (the-as uint 256))\n"
|
||||
" (set! (-> v0-0 parent) (the-as (pointer process-tree) #f))\n"
|
||||
" (set! (-> v0-0 brother) (the-as (pointer process-tree) #f))\n"
|
||||
" (set! (-> v0-0 child) (the-as (pointer process-tree) #f))\n"
|
||||
" (set! (-> v0-0 self) v0-0)\n"
|
||||
" (set! (-> v0-0 ppointer) (&-> v0-0 self))\n"
|
||||
" v0-0\n"
|
||||
@ -650,7 +669,8 @@ TEST_F(FormRegressionTest, ExprMethod0Process) {
|
||||
" )\n"
|
||||
" (set! (-> (the-as process v0-0) heap-top) (&-> (the-as process v0-0) stack (-> (the-as "
|
||||
"process v0-0) allocated-length)))\n"
|
||||
" (set! (-> (the-as process v0-0) stack-frame-top) (-> (the-as process v0-0) heap-top))\n"
|
||||
" (set! (-> (the-as process v0-0) stack-frame-top) (the-as stack-frame (-> (the-as process "
|
||||
"v0-0) heap-top)))\n"
|
||||
" (set! (-> (the-as process v0-0) stack-frame-top) #f)\n"
|
||||
" (set! (-> (the-as process v0-0) state) #f)\n"
|
||||
" (set! (-> (the-as process v0-0) next-state) #f)\n"
|
||||
@ -658,9 +678,9 @@ TEST_F(FormRegressionTest, ExprMethod0Process) {
|
||||
" (set! (-> (the-as process v0-0) trans-hook) #f)\n"
|
||||
" (set! (-> (the-as process v0-0) post-hook) #f)\n"
|
||||
" (set! (-> (the-as process v0-0) event-hook) #f)\n"
|
||||
" (set! (-> (the-as process v0-0) parent) #f)\n"
|
||||
" (set! (-> (the-as process v0-0) brother) #f)\n"
|
||||
" (set! (-> (the-as process v0-0) child) #f)\n"
|
||||
" (set! (-> (the-as process v0-0) parent) (the-as (pointer process-tree) #f))\n"
|
||||
" (set! (-> (the-as process v0-0) brother) (the-as (pointer process-tree) #f))\n"
|
||||
" (set! (-> (the-as process v0-0) child) (the-as (pointer process-tree) #f))\n"
|
||||
" (set! (-> (the-as process v0-0) self) (the-as process v0-0))\n"
|
||||
" (set! (-> (the-as process v0-0) ppointer) (&-> (the-as process v0-0) self))\n"
|
||||
" (the-as process v0-0)\n"
|
||||
@ -722,20 +742,18 @@ TEST_F(FormRegressionTest, ExprInspectProcessHeap) {
|
||||
std::string type = "(function process symbol)";
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (let\n"
|
||||
" ((obj (the-as basic (&+ (-> arg0 heap-base) 4))))\n"
|
||||
" (while\n"
|
||||
" (< (the-as int obj) (the-as int (-> arg0 heap-cur)))\n"
|
||||
" (inspect obj)\n"
|
||||
" (+! (the-as int obj) (logand -16 (+ (asize-of obj) 15)))\n"
|
||||
" (let ((obj (&+ (-> arg0 heap-base) 4)))\n"
|
||||
" (while (< (the-as int obj) (the-as int (-> arg0 heap-cur)))\n"
|
||||
" (inspect (the-as basic obj))\n"
|
||||
" (&+! obj (logand -16 (+ (asize-of (the-as basic obj)) 15)))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" #f\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "", {},
|
||||
parse_cast_json("[\t\t[[4,11], \"s5\", \"basic\"],\n"
|
||||
"\t\t[[17,20], \"s5\", \"int\"]]"),
|
||||
"{\"vars\":{\"s5-0\":[\"obj\", \"basic\"]}}");
|
||||
"\t\t[[17,20], \"s5\", \"pointer\"]]"),
|
||||
"{\"vars\":{\"s5-0\":[\"obj\", \"pointer\"]}}");
|
||||
}
|
||||
|
||||
// note: skipped method 3 process
|
||||
@ -822,11 +840,11 @@ TEST_F(FormRegressionTest, ExprMethod2Process) {
|
||||
" (format\n"
|
||||
" #t\n"
|
||||
" \":stack ~D/~D :heap ~D/~D @ #x~X>\"\n"
|
||||
" (- (-> arg0 top-thread stack-top) (the-as uint (-> arg0 top-thread sp)))\n"
|
||||
" (&- (-> arg0 top-thread stack-top) (the-as uint (-> arg0 top-thread sp)))\n"
|
||||
" (-> arg0 main-thread stack-size)\n"
|
||||
" (-\n"
|
||||
" (-> arg0 allocated-length)\n"
|
||||
" (- (-> arg0 heap-top) (the-as uint (-> arg0 heap-cur)))\n"
|
||||
" (&- (-> arg0 heap-top) (the-as uint (-> arg0 heap-cur)))\n"
|
||||
" )\n"
|
||||
" (-> arg0 allocated-length)\n"
|
||||
" arg0\n"
|
||||
@ -930,10 +948,10 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPool) {
|
||||
"(let\n"
|
||||
" ((s3-0 (object-new arg0 arg1 (the-as int (-> arg1 size)))))\n"
|
||||
" (set! (-> s3-0 name) arg4)\n"
|
||||
" (set! (-> s3-0 mask) 256)\n"
|
||||
" (set! (-> s3-0 parent) #f)\n"
|
||||
" (set! (-> s3-0 brother) #f)\n"
|
||||
" (set! (-> s3-0 child) #f)\n"
|
||||
" (set! (-> s3-0 mask) (the-as uint 256))\n"
|
||||
" (set! (-> s3-0 parent) (the-as (pointer process-tree) #f))\n"
|
||||
" (set! (-> s3-0 brother) (the-as (pointer process-tree) #f))\n"
|
||||
" (set! (-> s3-0 child) (the-as (pointer process-tree) #f))\n"
|
||||
" (set! (-> s3-0 self) s3-0)\n"
|
||||
" (set! (-> s3-0 ppointer) (&-> s3-0 self))\n"
|
||||
" (dotimes\n"
|
||||
@ -1126,7 +1144,7 @@ TEST_F(FormRegressionTest, ExprMethod15DeadPool) {
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 16";
|
||||
std::string type = "(function dead-pool process none)";
|
||||
std::string expected = "(change-parent arg1 arg0)";
|
||||
std::string expected = "(begin (change-parent arg1 arg0) (none))";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
@ -1245,11 +1263,11 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPoolHeap) {
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! (-> obj name) arg2)\n"
|
||||
" (set! (-> obj mask) 256)\n"
|
||||
" (set! (-> obj mask) (the-as uint 256))\n"
|
||||
" (set! (-> obj allocated-length) arg3)\n"
|
||||
" (set! (-> obj parent) #f)\n"
|
||||
" (set! (-> obj brother) #f)\n"
|
||||
" (set! (-> obj child) #f)\n"
|
||||
" (set! (-> obj parent) (the-as (pointer process-tree) #f))\n"
|
||||
" (set! (-> obj brother) (the-as (pointer process-tree) #f))\n"
|
||||
" (set! (-> obj child) (the-as (pointer process-tree) #f))\n"
|
||||
" (set! (-> obj self) obj)\n"
|
||||
" (set! (-> obj ppointer) (&-> obj self))\n"
|
||||
" (let\n"
|
||||
@ -1264,7 +1282,7 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPoolHeap) {
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! (-> obj dead-list next) (-> obj process-list))\n"
|
||||
" (set! (-> obj dead-list next) (the-as dead-pool-heap-rec (-> obj process-list)))\n"
|
||||
" (set! (-> obj alive-list process) #f)\n"
|
||||
" (set! (-> obj process-list (+ arg3 -1) next) #f)\n"
|
||||
" (set! (-> obj alive-list prev) (-> obj alive-list))\n"
|
||||
@ -1274,7 +1292,7 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPoolHeap) {
|
||||
" (set! (-> obj first-shrink) #f)\n"
|
||||
" (set!\n"
|
||||
" (-> obj heap base)\n"
|
||||
" (logand -16 (+ (+ (the-as int obj) 115) (* 12 arg3)))\n"
|
||||
" (the-as pointer (logand -16 (+ (+ (the-as int obj) 115) (* 12 arg3))))\n"
|
||||
" )\n"
|
||||
" (set! (-> obj heap current) (-> obj heap base))\n"
|
||||
" (set! (-> obj heap top) (&+ (-> obj heap base) arg4))\n"
|
||||
@ -1314,13 +1332,16 @@ TEST_F(FormRegressionTest, ExprMethod22DeadPoolHeap) {
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function dead-pool-heap dead-pool-heap-rec pointer)";
|
||||
std::string expected =
|
||||
"(if\n"
|
||||
" (-> arg1 process)\n"
|
||||
" (+\n"
|
||||
" (+ (+ (-> arg1 process allocated-length) -4) (the-as int (-> process size)))\n"
|
||||
" (the-as int (-> arg1 process))\n"
|
||||
" )\n"
|
||||
" (-> arg0 heap base)\n"
|
||||
"(the-as pointer (if (-> arg1 process)\n"
|
||||
" (+\n"
|
||||
" (+\n"
|
||||
" (+ (-> arg1 process allocated-length) -4)\n"
|
||||
" (the-as int (-> process size))\n"
|
||||
" )\n"
|
||||
" (the-as int (-> arg1 process))\n"
|
||||
" )\n"
|
||||
" (-> arg0 heap base)\n"
|
||||
" )\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
@ -1382,26 +1403,26 @@ TEST_F(FormRegressionTest, ExprMethod21DeadPoolHeap) {
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function dead-pool-heap dead-pool-heap-rec int)";
|
||||
std::string expected =
|
||||
"(if\n"
|
||||
" (-> arg1 process)\n"
|
||||
"(if (-> arg1 process)\n"
|
||||
" (let\n"
|
||||
" ((v1-3\n"
|
||||
" (&+\n"
|
||||
" (&+ (-> arg1 process) (-> process size))\n"
|
||||
" (&+ (the-as pointer (-> arg1 process)) (-> process size))\n"
|
||||
" (-> arg1 process allocated-length)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (if\n"
|
||||
" (-> arg1 next)\n"
|
||||
" (- (-> arg1 next process) (the-as uint v1-3))\n"
|
||||
" (- (-> arg0 heap top) (the-as uint (&+ v1-3 4)))\n"
|
||||
" (if (-> arg1 next)\n"
|
||||
" (&- (the-as pointer (-> arg1 next process)) (the-as uint v1-3))\n"
|
||||
" (&- (-> arg0 heap top) (the-as uint (&+ v1-3 4)))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (if\n"
|
||||
" (-> arg1 next)\n"
|
||||
" (- (-> arg1 next process) (the-as uint (&+ (-> arg0 heap base) 4)))\n"
|
||||
" (- (-> arg0 heap top) (the-as uint (-> arg0 heap base)))\n"
|
||||
" (if (-> arg1 next)\n"
|
||||
" (&-\n"
|
||||
" (the-as pointer (-> arg1 next process))\n"
|
||||
" (the-as uint (&+ (-> arg0 heap base) 4))\n"
|
||||
" )\n"
|
||||
" (&- (-> arg0 heap top) (the-as uint (-> arg0 heap base)))\n"
|
||||
" )\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "", {},
|
||||
@ -1539,7 +1560,7 @@ TEST_F(FormRegressionTest, ExprMethod3DeadPoolHeap) {
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (let*\n"
|
||||
" ((s5-0 (- (-> arg0 heap top) (the-as uint (-> arg0 heap base))))\n"
|
||||
" ((s5-0 (&- (-> arg0 heap top) (the-as uint (-> arg0 heap base))))\n"
|
||||
" (v1-3\n"
|
||||
" (if\n"
|
||||
" (-> arg0 alive-list prev)\n"
|
||||
@ -1599,7 +1620,8 @@ TEST_F(FormRegressionTest, ExprMethod5DeadPoolHeap) {
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function dead-pool-heap int)";
|
||||
std::string expected = "(+ (- -4 (the-as int arg0)) (-> arg0 heap top))";
|
||||
std::string expected =
|
||||
"(+ (the-as int (- -4 (the-as int arg0))) (the-as int (-> arg0 heap top)))";
|
||||
test_with_expr(func, type, expected, false, "", {},
|
||||
parse_cast_json("[[3, \"v1\", \"int\"], [3, \"a0\", \"int\"]]"));
|
||||
}
|
||||
@ -1665,7 +1687,7 @@ TEST_F(FormRegressionTest, ExprMethod20DeadPoolHeap) {
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function dead-pool-heap int)";
|
||||
std::string expected = "(- (-> arg0 heap top) (the-as uint (-> arg0 heap base)))";
|
||||
std::string expected = "(&- (-> arg0 heap top) (the-as uint (-> arg0 heap base)))";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
@ -1706,7 +1728,7 @@ TEST_F(FormRegressionTest, ExprMethod25DeadPoolHeap) {
|
||||
" (if\n"
|
||||
" (-> arg0 alive-list prev)\n"
|
||||
" (gap-size arg0 (-> arg0 alive-list prev))\n"
|
||||
" (- v1-0 (the-as uint (-> arg0 heap base)))\n"
|
||||
" (&- v1-0 (the-as uint (-> arg0 heap base)))\n"
|
||||
" )\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected);
|
||||
@ -1987,7 +2009,7 @@ TEST_F(FormRegressionTest, ExprMethod14DeadPoolHeap) {
|
||||
"(let\n"
|
||||
" ((s4-0 (-> arg0 dead-list next)) (s3-0 (the-as process #f)))\n"
|
||||
" (let\n"
|
||||
" ((s1-0 (find-gap-by-size arg0 (+ (-> process size) (the-as uint arg2)))))\n"
|
||||
" ((s1-0 (find-gap-by-size arg0 (the-as int (+ (-> process size) (the-as uint arg2))))))\n"
|
||||
" (cond\n"
|
||||
" ((and s4-0 s1-0)\n"
|
||||
" (set! (-> arg0 dead-list next) (-> s4-0 next))\n"
|
||||
@ -2193,8 +2215,7 @@ TEST_F(FormRegressionTest, ExprMethod15DeadPoolHeap) {
|
||||
// NOTE: has wrong types for s5-1, but it's okay
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (if\n"
|
||||
" (!= arg0 (-> arg1 pool))\n"
|
||||
" (if (!= arg0 (-> arg1 pool))\n"
|
||||
" (format\n"
|
||||
" 0\n"
|
||||
" \"ERROR: process ~A does not belong to dead-pool-heap ~A.~%\"\n"
|
||||
@ -2203,35 +2224,36 @@ TEST_F(FormRegressionTest, ExprMethod15DeadPoolHeap) {
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (change-parent arg1 arg0)\n"
|
||||
" (set! (-> arg0 child) #f)\n"
|
||||
" (let\n"
|
||||
" ((s5-1 (-> arg1 ppointer)))\n"
|
||||
" (set! (-> arg0 child) (the-as (pointer process-tree) #f))\n"
|
||||
" (let ((s5-1 (-> arg1 ppointer)))\n"
|
||||
" (if\n"
|
||||
" (or\n"
|
||||
" (= (-> arg0 first-gap) s5-1)\n"
|
||||
" (<\n"
|
||||
" (the-as int (gap-location arg0 s5-1))\n"
|
||||
" (the-as int (gap-location arg0 (the-as dead-pool-heap-rec s5-1)))\n"
|
||||
" (the-as int (gap-location arg0 (-> arg0 first-gap)))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! (-> arg0 first-gap) (-> s5-1 1))\n"
|
||||
" (set! (-> arg0 first-gap) (the-as dead-pool-heap-rec (-> s5-1 1)))\n"
|
||||
" )\n"
|
||||
" (when\n"
|
||||
" (= (-> arg0 first-shrink) s5-1)\n"
|
||||
" (set! (-> arg0 first-shrink) (-> s5-1 1))\n"
|
||||
" (when (not (-> arg0 first-shrink process)) (set! (-> arg0 first-shrink) #f))\n"
|
||||
" (when (= (-> arg0 first-shrink) s5-1)\n"
|
||||
" (set! (-> arg0 first-shrink) (the-as dead-pool-heap-rec (-> s5-1 1)))\n"
|
||||
" (when (not (-> arg0 first-shrink process))\n"
|
||||
" (set! (-> arg0 first-shrink) #f)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! (-> s5-1 1 parent) (-> s5-1 2))\n"
|
||||
" (if\n"
|
||||
" (-> s5-1 2)\n"
|
||||
" (set! (-> s5-1 2 mask) (-> s5-1 1))\n"
|
||||
" (set! (-> arg0 alive-list prev) (-> s5-1 1))\n"
|
||||
" (set! (-> s5-1 1 parent) (the-as (pointer process-tree) (-> s5-1 2)))\n"
|
||||
" (if (-> s5-1 2)\n"
|
||||
" (set! (-> s5-1 2 mask) (the-as uint (-> s5-1 1)))\n"
|
||||
" (set! (-> arg0 alive-list prev) (the-as dead-pool-heap-rec (-> s5-1 1)))\n"
|
||||
" )\n"
|
||||
" (set! (-> s5-1 2) (-> arg0 dead-list next))\n"
|
||||
" (set! (-> arg0 dead-list next) s5-1)\n"
|
||||
" (set! (-> s5-1 2) (the-as process-tree (-> arg0 dead-list next)))\n"
|
||||
" (set! (-> arg0 dead-list next) (the-as dead-pool-heap-rec s5-1))\n"
|
||||
" (set! (-> s5-1 0) *null-process*)\n"
|
||||
" )\n"
|
||||
" (let ((v0-4 0)))\n"
|
||||
" (let ((v0-4 0))\n"
|
||||
" )\n"
|
||||
" (none)\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "",
|
||||
{{"L297", "ERROR: process ~A does not belong to dead-pool-heap ~A.~%"}});
|
||||
@ -2326,35 +2348,41 @@ TEST_F(FormRegressionTest, ExprMethod17DeadPoolHeap) {
|
||||
" daddiu sp, sp, 64";
|
||||
std::string type = "(function dead-pool-heap process dead-pool-heap)";
|
||||
|
||||
// NOTE - this has bad types.
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (if\n"
|
||||
" arg1\n"
|
||||
" (let\n"
|
||||
" ((s5-0 (-> arg1 ppointer)))\n"
|
||||
" (when\n"
|
||||
" (not\n"
|
||||
" (or\n"
|
||||
" (nonzero? (logand (-> arg1 mask) 512))\n"
|
||||
" (and (not (-> arg1 next-state)) (not (-> arg1 state)))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set!\n"
|
||||
" (-> arg1 allocated-length)\n"
|
||||
" (- (-> arg1 heap-cur) (the-as uint (-> arg1 stack)))\n"
|
||||
" )\n"
|
||||
" (set! (-> arg1 heap-top) (&-> arg1 stack (-> arg1 allocated-length)))\n"
|
||||
" (if\n"
|
||||
" (< (the-as int arg1) (the-as int (gap-location arg0 (-> arg0 first-gap))))\n"
|
||||
" (set! (-> arg0 first-gap) (find-gap arg0 s5-0))\n"
|
||||
" )\n"
|
||||
" (set! (-> arg1 mask) (logior (-> arg1 mask) 512))\n"
|
||||
" )\n"
|
||||
" (if\n"
|
||||
" (= (-> arg0 first-shrink) s5-0)\n"
|
||||
" (set! (-> arg0 first-shrink) (-> s5-0 2))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (if arg1 (let ((s5-0 (-> arg1 ppointer)))\n"
|
||||
" (when\n"
|
||||
" (not\n"
|
||||
" (or\n"
|
||||
" (nonzero? (logand (-> arg1 mask) 512))\n"
|
||||
" (and (not (-> arg1 next-state)) (not (-> arg1 state)))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set!\n"
|
||||
" (-> arg1 allocated-length)\n"
|
||||
" (&- (-> arg1 heap-cur) (the-as uint (-> arg1 stack)))\n"
|
||||
" )\n"
|
||||
" (set!\n"
|
||||
" (-> arg1 heap-top)\n"
|
||||
" (&-> arg1 stack (-> arg1 allocated-length))\n"
|
||||
" )\n"
|
||||
" (if\n"
|
||||
" (<\n"
|
||||
" (the-as int arg1)\n"
|
||||
" (the-as int (gap-location arg0 (-> arg0 first-gap)))\n"
|
||||
" )\n"
|
||||
" (set! (-> arg0 first-gap) (find-gap arg0 (the-as dead-pool-heap-rec s5-0)))\n"
|
||||
" )\n"
|
||||
" (set! (-> arg1 mask) (logior (-> arg1 mask) 512))\n"
|
||||
" )\n"
|
||||
" (if (= (-> arg0 first-shrink) s5-0)\n"
|
||||
" (set!\n"
|
||||
" (-> arg0 first-shrink)\n"
|
||||
" (the-as dead-pool-heap-rec (-> s5-0 2))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" arg0\n"
|
||||
" )";
|
||||
@ -2570,8 +2598,8 @@ TEST_F(FormRegressionTest, ExprMethod16DeadPoolHeap) {
|
||||
" ((< f0-2 (l.f L348)) (set! arg1 (shl arg1 1)) (let ((v1-12 arg1))))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! (-> arg0 compact-count-targ) arg1)\n"
|
||||
" (set! (-> arg0 compact-count) 0)\n"
|
||||
" (set! (-> arg0 compact-count-targ) (the-as uint arg1))\n"
|
||||
" (set! (-> arg0 compact-count) (the-as uint 0))\n"
|
||||
" (while\n"
|
||||
" (nonzero? arg1)\n"
|
||||
" (+! arg1 -1)\n"
|
||||
@ -2604,6 +2632,7 @@ TEST_F(FormRegressionTest, ExprMethod16DeadPoolHeap) {
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (let ((v0-8 0)))\n"
|
||||
" (none)\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "", {{"L296", "~3LLow Actor Memory~%~0L"}});
|
||||
}
|
||||
@ -2798,7 +2827,7 @@ TEST_F(FormRegressionTest, ExprMethod18DeadPoolHeap) {
|
||||
" (-> s4-0 process)\n"
|
||||
" (relocate\n"
|
||||
" (-> s4-0 process)\n"
|
||||
" (- (gap-location arg0 a1-3) (the-as uint (&-> (-> s4-0 process) type)))\n"
|
||||
" (&- (gap-location arg0 a1-3) (the-as uint (&-> (-> s4-0 process) type)))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
@ -2806,6 +2835,7 @@ TEST_F(FormRegressionTest, ExprMethod18DeadPoolHeap) {
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (let ((v0-4 0)))\n"
|
||||
" (none)\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
@ -15,8 +15,7 @@ const std::unordered_set<std::string> g_object_files_to_decompile = {"gcommon",
|
||||
// the object files to check against a reference in test/decompiler/reference
|
||||
const std::vector<std::string> g_object_files_to_check_against_reference = {
|
||||
"gcommon", // NOTE: this file needs work, but adding it for now just to test the framework.
|
||||
"gstring-h", "gkernel-h",
|
||||
/*"gkernel"*/};
|
||||
"gstring-h", "gkernel-h", "gkernel"};
|
||||
|
||||
// the functions we expect the decompiler to skip
|
||||
const std::unordered_set<std::string> expected_skip_in_decompiler = {
|
||||
@ -47,25 +46,15 @@ const std::unordered_set<std::string> skip_in_compiling = {
|
||||
//////////////////////
|
||||
|
||||
// these functions are not implemented by the compiler in OpenGOAL, but are in GOAL.
|
||||
"abs",
|
||||
"ash",
|
||||
"min",
|
||||
"max",
|
||||
"lognor",
|
||||
"abs", "ash", "min", "max", "lognor",
|
||||
// weird PS2 specific debug registers:
|
||||
"breakpoint-range-set!",
|
||||
// these require 128-bit integers. We want these eventually, but disabling for now to focus
|
||||
// on more important issues.
|
||||
"(method 3 vec4s)",
|
||||
"(method 2 vec4s)",
|
||||
"qmem-copy<-!",
|
||||
"qmem-copy->!",
|
||||
"(method 2 array)",
|
||||
"(method 3 vec4s)", "(method 2 vec4s)", "qmem-copy<-!", "qmem-copy->!", "(method 2 array)",
|
||||
"(method 3 array)",
|
||||
// does weird stuff with the type system.
|
||||
"print",
|
||||
"printl",
|
||||
"inspect",
|
||||
"print", "printl", "inspect",
|
||||
// inline assembly
|
||||
"valid?",
|
||||
|
||||
@ -74,23 +63,18 @@ const std::unordered_set<std::string> skip_in_compiling = {
|
||||
//////////////////////
|
||||
// bitfields, possibly inline assembly
|
||||
"(method 2 handle)",
|
||||
};
|
||||
|
||||
// The decompiler does not attempt to insert forward definitions, as this would be part of an
|
||||
// unimplemented full-program type analysis pass. For now, we manually specify all functions
|
||||
// that should have a forward definition here.
|
||||
const std::string g_forward_type_defs =
|
||||
// used out of order
|
||||
"(define-extern name= (function basic basic symbol))\n"
|
||||
// recursive
|
||||
"(define-extern fact (function int int))\n"
|
||||
// gkernel-h
|
||||
"(declare-type process basic)\n"
|
||||
"(declare-type stack-frame basic)\n"
|
||||
"(declare-type state basic)\n"
|
||||
"(declare-type cpu-thread basic)\n"
|
||||
"(declare-type dead-pool basic)\n"
|
||||
"(declare-type event-message-block structure)\n";
|
||||
//////////////////////
|
||||
// GKERNEL
|
||||
//////////////////////
|
||||
// these refer to anonymous functions, which aren't yet implemented.
|
||||
"process-by-name", "process-not-name", "process-count", "kill-by-type", "kill-not-type",
|
||||
"kill-by-name", "kill-not-name", "kernel-dispatcher",
|
||||
|
||||
// asm
|
||||
"(method 10 process)"
|
||||
|
||||
};
|
||||
|
||||
// default location for the data. It can be changed with a command line argument.
|
||||
std::string g_iso_data_path = "";
|
||||
@ -358,6 +342,10 @@ TEST_F(OfflineDecompilation, Reference) {
|
||||
|
||||
std::string src = db->ir2_final_out(obj_l.at(0));
|
||||
|
||||
// if (file == "gkernel") {
|
||||
// fmt::print("{}\n", src);
|
||||
// }
|
||||
|
||||
auto reference = file_util::read_text_file(file_util::get_file_path(
|
||||
{"test", "decompiler", "reference", fmt::format("{}_REF.gc", file)}));
|
||||
|
||||
@ -371,7 +359,8 @@ TEST_F(OfflineDecompilation, Reference) {
|
||||
TEST_F(OfflineDecompilation, Compile) {
|
||||
Compiler compiler;
|
||||
|
||||
compiler.run_front_end_on_string(g_forward_type_defs);
|
||||
compiler.run_front_end_on_string(file_util::read_text_file(file_util::get_file_path(
|
||||
{"test", "decompiler", "reference", "all_forward_declarations.gc"})));
|
||||
|
||||
for (auto& file : g_object_files_to_check_against_reference) {
|
||||
auto& obj_l = db->obj_files_by_name.at(file);
|
||||
|
Loading…
Reference in New Issue
Block a user