mirror of
https://github.com/open-goal/jak-project
synced 2026-07-09 14:55:51 -04:00
[Decompiler] Fix up pretty printing and other small changes (#311)
* fix up pretty printing * wip * fix pointer cast signed vs unsigned issue
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cinttypes>
|
||||
#include "Object.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
@@ -92,7 +93,7 @@ std::string fixed_to_string(FloatType x) {
|
||||
|
||||
// it's an integer number, so let's just get this over with asap
|
||||
if (exact_int) {
|
||||
sprintf(buff, "%lld.0", rounded);
|
||||
sprintf(buff, "%" PRId64 ".0", rounded);
|
||||
return {buff};
|
||||
} else {
|
||||
// not an integer - see how many decimal cases we need
|
||||
|
||||
@@ -289,7 +289,7 @@ static PrettyPrinterNode* propagatePretty(NodePool& pool,
|
||||
for (auto* n = list; n; n = n->next) {
|
||||
if (n->is_line_separator) {
|
||||
previous_line_sep = true;
|
||||
offset = indentStack.back() += n->specialIndentDelta;
|
||||
offset = indentStack.back() + n->specialIndentDelta;
|
||||
} else {
|
||||
if (previous_line_sep) {
|
||||
line_start = n;
|
||||
@@ -483,6 +483,12 @@ void breakList(NodePool& pool, PrettyPrinterNode* leftParen, PrettyPrinterNode*
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
const std::unordered_set<std::string> control_flow_start_forms = {
|
||||
"while", "dotimes", "until", "if", "when",
|
||||
};
|
||||
}
|
||||
|
||||
void insertSpecialBreaks(NodePool& pool, PrettyPrinterNode* node) {
|
||||
for (; node; node = node->next) {
|
||||
if (!node->is_line_separator && node->tok->kind == FormToken::TokenKind::STRING) {
|
||||
@@ -494,12 +500,78 @@ void insertSpecialBreaks(NodePool& pool, PrettyPrinterNode* node) {
|
||||
}
|
||||
}
|
||||
|
||||
if (name == "defun" || name == "defmethod" || name == "defun-debug") {
|
||||
if (name == "defun" || name == "defmethod" || name == "defun-debug" || name == "let" ||
|
||||
name == "let*") {
|
||||
auto* parent_type_dec = getNextListOnLine(node);
|
||||
if (parent_type_dec) {
|
||||
insertNewlineAfter(pool, parent_type_dec->paren, 0);
|
||||
breakList(pool, node->paren, parent_type_dec);
|
||||
}
|
||||
|
||||
if ((name == "let" || name == "let*") && parent_type_dec) {
|
||||
if (parent_type_dec->tok->kind == FormToken::TokenKind::OPEN_PAREN) {
|
||||
breakList(pool, parent_type_dec);
|
||||
}
|
||||
auto open_paren = node->prev;
|
||||
if (open_paren && open_paren->tok->kind == FormToken::TokenKind::OPEN_PAREN) {
|
||||
// insertNewlineBefore(pool, open_paren, 0);
|
||||
if (open_paren->prev) {
|
||||
breakList(pool, open_paren->prev->paren, open_paren);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (control_flow_start_forms.find(name) != control_flow_start_forms.end()) {
|
||||
auto* parent_type_dec = getNextListOnLine(node);
|
||||
if (parent_type_dec) {
|
||||
insertNewlineAfter(pool, parent_type_dec->paren, 0);
|
||||
breakList(pool, node->paren, parent_type_dec);
|
||||
auto open_paren = node->prev;
|
||||
if (open_paren && open_paren->tok->kind == FormToken::TokenKind::OPEN_PAREN) {
|
||||
if (open_paren->prev && !open_paren->prev->is_line_separator) {
|
||||
if (open_paren->prev) {
|
||||
auto to_break = open_paren->prev->paren;
|
||||
if (to_break->tok && to_break->tok->kind == FormToken::TokenKind::OPEN_PAREN) {
|
||||
breakList(pool, open_paren->prev->paren, open_paren);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (name == "cond") {
|
||||
auto* start_of_case = getNextListOnLine(node);
|
||||
while (true) {
|
||||
// let's break this case:
|
||||
assert(start_of_case->tok->kind == FormToken::TokenKind::OPEN_PAREN);
|
||||
auto end_of_case = start_of_case->paren;
|
||||
assert(end_of_case->tok->kind == FormToken::TokenKind::CLOSE_PAREN);
|
||||
// get the first thing in the case
|
||||
|
||||
// and break there.
|
||||
breakList(pool, start_of_case);
|
||||
// now, look for the next case.
|
||||
auto next = end_of_case->next;
|
||||
while (next &&
|
||||
(next->is_line_separator || next->tok->kind == FormToken::TokenKind::WHITESPACE)) {
|
||||
next = next->next;
|
||||
}
|
||||
if (!next) {
|
||||
break;
|
||||
}
|
||||
if (next->tok->kind == FormToken::TokenKind::CLOSE_PAREN) {
|
||||
break;
|
||||
}
|
||||
if (next->tok->kind != FormToken::TokenKind::OPEN_PAREN) {
|
||||
break;
|
||||
}
|
||||
start_of_case = next;
|
||||
}
|
||||
|
||||
// break cond into a multi-line always
|
||||
breakList(pool, node->paren);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,6 +267,13 @@ FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (m_value.is_var()) {
|
||||
auto input_var_type = env.get_types_before_op(m_my_idx).get(m_value.var().reg());
|
||||
if (env.dts->ts.tc(TypeSpec("uinteger"), input_var_type.typespec())) {
|
||||
cast_type.insert(cast_type.begin(), 'u');
|
||||
}
|
||||
}
|
||||
|
||||
auto source = pool.alloc_single_element_form<SimpleExpressionElement>(
|
||||
nullptr, SimpleAtom::make_var(ro.var).as_expr(), m_my_idx);
|
||||
auto cast_source = pool.alloc_single_element_form<CastElement>(
|
||||
|
||||
@@ -1467,6 +1467,10 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
|
||||
return "!=";
|
||||
case FixedOperatorKind::METHOD_OF_OBJECT:
|
||||
return "method-of-object";
|
||||
case FixedOperatorKind::NULLP:
|
||||
return "null?";
|
||||
case FixedOperatorKind::PAIRP:
|
||||
return "pair?";
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
+16
-1
@@ -436,11 +436,26 @@ class ConditionElement : public FormElement {
|
||||
FormPool& pool,
|
||||
const std::vector<Form*>& source_forms,
|
||||
const std::vector<TypeSpec>& types);
|
||||
|
||||
FormElement* make_zero_check_generic(const Env& env,
|
||||
FormPool& pool,
|
||||
const std::vector<Form*>& source_forms,
|
||||
const std::vector<TypeSpec>& types);
|
||||
FormElement* make_equal_check_generic(const Env& env,
|
||||
FormPool& pool,
|
||||
const std::vector<Form*>& source_forms,
|
||||
const std::vector<TypeSpec>& types);
|
||||
FormElement* make_not_equal_check_generic(const Env& env,
|
||||
FormPool& pool,
|
||||
const std::vector<Form*>& source_forms,
|
||||
const std::vector<TypeSpec>& types);
|
||||
FormElement* make_less_than_zero_signed_check_generic(const Env& env,
|
||||
FormPool& pool,
|
||||
const std::vector<Form*>& source_forms,
|
||||
const std::vector<TypeSpec>& types);
|
||||
FormElement* make_geq_zero_signed_check_generic(const Env& env,
|
||||
FormPool& pool,
|
||||
const std::vector<Form*>& source_forms,
|
||||
const std::vector<TypeSpec>& types);
|
||||
|
||||
private:
|
||||
IR2_Condition::Kind m_kind;
|
||||
|
||||
@@ -1878,7 +1878,7 @@ FormElement* ConditionElement::make_zero_check_generic(const Env&,
|
||||
const std::vector<Form*>& source_forms,
|
||||
const std::vector<TypeSpec>&) {
|
||||
// (zero? (+ thing small-integer)) -> (= thing (- small-integer))
|
||||
|
||||
assert(source_forms.size() == 1);
|
||||
auto mr = match(Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::ADDITION),
|
||||
{Matcher::any(0), Matcher::any_integer(1)}),
|
||||
source_forms.at(0));
|
||||
@@ -1893,6 +1893,109 @@ FormElement* ConditionElement::make_zero_check_generic(const Env&,
|
||||
}
|
||||
}
|
||||
|
||||
FormElement* ConditionElement::make_equal_check_generic(const Env&,
|
||||
FormPool& pool,
|
||||
const std::vector<Form*>& source_forms,
|
||||
const std::vector<TypeSpec>&) {
|
||||
assert(source_forms.size() == 2);
|
||||
// (= thing '())
|
||||
auto ref = source_forms.at(1);
|
||||
auto ref_atom = form_as_atom(ref);
|
||||
if (ref_atom && ref_atom->get_kind() == SimpleAtom::Kind::EMPTY_LIST) {
|
||||
// null?
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::NULLP),
|
||||
source_forms.at(0));
|
||||
} else {
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::EQ),
|
||||
source_forms);
|
||||
}
|
||||
}
|
||||
|
||||
FormElement* ConditionElement::make_not_equal_check_generic(const Env&,
|
||||
FormPool& pool,
|
||||
const std::vector<Form*>& source_forms,
|
||||
const std::vector<TypeSpec>&) {
|
||||
assert(source_forms.size() == 2);
|
||||
// (!= thing '())
|
||||
auto ref = source_forms.at(1);
|
||||
auto ref_atom = form_as_atom(ref);
|
||||
if (ref_atom && ref_atom->get_kind() == SimpleAtom::Kind::EMPTY_LIST) {
|
||||
// null?
|
||||
return pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_compare(IR2_Condition::Kind::FALSE),
|
||||
pool.alloc_single_element_form<GenericElement>(
|
||||
nullptr, GenericOperator::make_fixed(FixedOperatorKind::NULLP), source_forms.at(0)));
|
||||
} else {
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::NEQ),
|
||||
source_forms);
|
||||
}
|
||||
}
|
||||
|
||||
FormElement* ConditionElement::make_less_than_zero_signed_check_generic(
|
||||
const Env&,
|
||||
FormPool& pool,
|
||||
const std::vector<Form*>& source_forms,
|
||||
const std::vector<TypeSpec>& types) {
|
||||
assert(source_forms.size() == 1);
|
||||
// (< (shl (the-as int iter) 62) 0) -> (pair? iter)
|
||||
|
||||
// match (shl [(the-as int [x]) | [x]] 62)
|
||||
auto shift_match =
|
||||
match(Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::SHL),
|
||||
{
|
||||
Matcher::match_or({Matcher::cast("int", Matcher::any(0)),
|
||||
Matcher::any(0)}), // the val
|
||||
Matcher::integer(62) // get the bit in the highest position.
|
||||
}),
|
||||
source_forms.at(0));
|
||||
|
||||
if (shift_match.matched) {
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::PAIRP),
|
||||
shift_match.maps.forms.at(0));
|
||||
} else {
|
||||
auto casted = make_cast(source_forms, types, TypeSpec("int"), pool);
|
||||
auto zero = pool.alloc_single_element_form<SimpleAtomElement>(nullptr,
|
||||
SimpleAtom::make_int_constant(0));
|
||||
casted.push_back(zero);
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::LT),
|
||||
casted);
|
||||
}
|
||||
}
|
||||
|
||||
FormElement* ConditionElement::make_geq_zero_signed_check_generic(
|
||||
const Env&,
|
||||
FormPool& pool,
|
||||
const std::vector<Form*>& source_forms,
|
||||
const std::vector<TypeSpec>& types) {
|
||||
assert(source_forms.size() == 1);
|
||||
// (>= (shl (the-as int iter) 62) 0) -> (not (pair? iter))
|
||||
|
||||
// match (shl [(the-as int [x]) | [x]] 62)
|
||||
auto shift_match =
|
||||
match(Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::SHL),
|
||||
{
|
||||
Matcher::match_or({Matcher::cast("int", Matcher::any(0)),
|
||||
Matcher::any(0)}), // the val
|
||||
Matcher::integer(62) // get the bit in the highest position.
|
||||
}),
|
||||
source_forms.at(0));
|
||||
|
||||
if (shift_match.matched) {
|
||||
return pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_compare(IR2_Condition::Kind::FALSE),
|
||||
pool.alloc_single_element_form<GenericElement>(
|
||||
nullptr, GenericOperator::make_fixed(FixedOperatorKind::PAIRP),
|
||||
shift_match.maps.forms.at(0)));
|
||||
} else {
|
||||
auto casted = make_cast(source_forms, types, TypeSpec("int"), pool);
|
||||
auto zero = pool.alloc_single_element_form<SimpleAtomElement>(nullptr,
|
||||
SimpleAtom::make_int_constant(0));
|
||||
casted.push_back(zero);
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::GEQ),
|
||||
casted);
|
||||
}
|
||||
}
|
||||
|
||||
FormElement* ConditionElement::make_generic(const Env& env,
|
||||
FormPool& pool,
|
||||
const std::vector<Form*>& source_forms,
|
||||
@@ -1911,11 +2014,10 @@ FormElement* ConditionElement::make_generic(const Env& env,
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_compare(m_kind),
|
||||
source_forms);
|
||||
case IR2_Condition::Kind::EQUAL:
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::EQ),
|
||||
source_forms);
|
||||
return make_equal_check_generic(env, pool, source_forms, types);
|
||||
|
||||
case IR2_Condition::Kind::NOT_EQUAL:
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::NEQ),
|
||||
source_forms);
|
||||
return make_not_equal_check_generic(env, pool, source_forms, types);
|
||||
|
||||
case IR2_Condition::Kind::LESS_THAN_SIGNED:
|
||||
return pool.alloc_element<GenericElement>(
|
||||
@@ -1937,12 +2039,7 @@ FormElement* ConditionElement::make_generic(const Env& env,
|
||||
make_cast(source_forms, types, TypeSpec("int"), pool));
|
||||
|
||||
case IR2_Condition::Kind::LESS_THAN_ZERO_SIGNED: {
|
||||
auto casted = make_cast(source_forms, types, TypeSpec("int"), pool);
|
||||
auto zero = pool.alloc_single_element_form<SimpleAtomElement>(
|
||||
nullptr, SimpleAtom::make_int_constant(0));
|
||||
casted.push_back(zero);
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::LT),
|
||||
casted);
|
||||
return make_less_than_zero_signed_check_generic(env, pool, source_forms, types);
|
||||
}
|
||||
|
||||
case IR2_Condition::Kind::LEQ_ZERO_SIGNED: {
|
||||
@@ -1955,12 +2052,7 @@ FormElement* ConditionElement::make_generic(const Env& env,
|
||||
}
|
||||
|
||||
case IR2_Condition::Kind::GEQ_ZERO_SIGNED: {
|
||||
auto casted = make_cast(source_forms, types, TypeSpec("int"), pool);
|
||||
auto zero = pool.alloc_single_element_form<SimpleAtomElement>(
|
||||
nullptr, SimpleAtom::make_int_constant(0));
|
||||
casted.push_back(zero);
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::GEQ),
|
||||
casted);
|
||||
return make_geq_zero_signed_check_generic(env, pool, source_forms, types);
|
||||
}
|
||||
|
||||
case IR2_Condition::Kind::GREATER_THAN_ZERO_UNSIGNED: {
|
||||
|
||||
@@ -136,6 +136,8 @@ enum class FixedOperatorKind {
|
||||
NEQ,
|
||||
CONS,
|
||||
METHOD_OF_OBJECT,
|
||||
NULLP,
|
||||
PAIRP,
|
||||
INVALID
|
||||
};
|
||||
|
||||
@@ -154,23 +156,29 @@ struct UseDefInfo {
|
||||
void disable_use(int op_id) {
|
||||
for (auto& x : uses) {
|
||||
if (x.op_id == op_id) {
|
||||
assert(!x.disabled);
|
||||
if (x.disabled) {
|
||||
throw std::runtime_error("Invalid disable use twice");
|
||||
}
|
||||
x.disabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
|
||||
throw std::runtime_error("Invalid disable use");
|
||||
}
|
||||
|
||||
void disable_def(int op_id) {
|
||||
for (auto& x : defs) {
|
||||
if (x.op_id == op_id) {
|
||||
assert(!x.disabled);
|
||||
if (x.disabled) {
|
||||
throw std::runtime_error("Invalid disable def twice");
|
||||
}
|
||||
x.disabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
|
||||
throw std::runtime_error("Invalid disable def");
|
||||
}
|
||||
|
||||
int use_count() const {
|
||||
|
||||
@@ -40,9 +40,9 @@ struct OpenGOALAsm {
|
||||
|
||||
bool valid = true;
|
||||
bool todo = false;
|
||||
Instruction instr;
|
||||
std::optional<RegisterAccess> m_dst;
|
||||
std::vector<std::optional<RegisterAccess>> m_src;
|
||||
Instruction instr;
|
||||
OpenGOALAsm::Function func;
|
||||
|
||||
std::string full_function_name();
|
||||
|
||||
@@ -678,8 +678,12 @@ void clean_up_cond_no_else_final(Function& func, CondNoElseElement* cne) {
|
||||
auto reg = cne->entries.at(i).false_destination;
|
||||
assert(reg.has_value());
|
||||
assert(branch);
|
||||
assert(branch_info_i.written_and_unused.find(reg->reg()) !=
|
||||
branch_info_i.written_and_unused.end());
|
||||
if (branch_info_i.written_and_unused.find(reg->reg()) ==
|
||||
branch_info_i.written_and_unused.end()) {
|
||||
throw std::runtime_error("Bad delay slot in clean_up_cond_no_else_final");
|
||||
}
|
||||
// assert(branch_info_i.written_and_unused.find(reg->reg()) !=
|
||||
// branch_info_i.written_and_unused.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -228,10 +228,11 @@ std::string write_from_top_level(const Function& top_level,
|
||||
auto method_match_result = match(method_def_matcher, &f);
|
||||
if (method_match_result.matched) {
|
||||
auto func = file.try_get_function_at_label(method_match_result.maps.label.at(method_label));
|
||||
if (func) {
|
||||
if (func && func->guessed_name.kind == FunctionName::FunctionKind::METHOD) {
|
||||
something_matched = true;
|
||||
result += fmt::format(";; definition for method of type {}\n",
|
||||
method_match_result.maps.strings.at(type_name));
|
||||
result +=
|
||||
fmt::format(";; definition for method {} of type {}\n", func->guessed_name.method_id,
|
||||
method_match_result.maps.strings.at(type_name));
|
||||
if (skip_functions.find(func->guessed_name.to_string()) == skip_functions.end()) {
|
||||
result += careful_function_to_string(func, dts);
|
||||
} else {
|
||||
@@ -248,7 +249,7 @@ std::string write_from_top_level(const Function& top_level,
|
||||
if (dts.ts.fully_defined_type_exists(name)) {
|
||||
result += fmt::format(";; definition of type {}\n", name);
|
||||
result += dts.ts.generate_deftype(dts.ts.lookup_type(name));
|
||||
result += "\n\n";
|
||||
result += "\n";
|
||||
} else {
|
||||
result += fmt::format(
|
||||
";; type {} is defined here, but it is unknown to the decompiler\n\n", name);
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace decompiler {
|
||||
bool rewrite_inline_asm_instructions(Form* top_level_form,
|
||||
FormPool& pool,
|
||||
Function& f,
|
||||
const DecompilerTypeSystem& dts) {
|
||||
const DecompilerTypeSystem&) {
|
||||
assert(top_level_form);
|
||||
|
||||
try {
|
||||
@@ -37,7 +37,7 @@ bool rewrite_inline_asm_instructions(Form* top_level_form,
|
||||
// If its an invalid or unsupported exception, skip it
|
||||
/*lg::warn("[ASM Re-Write] - Unsupported inline assembly instruction kind - [{}]",
|
||||
asmOp.instr.kind);*/
|
||||
f.warnings.general_warning(";; Unsupported inline assembly instruction kind - [{}]",
|
||||
f.warnings.general_warning("Unsupported inline assembly instruction kind - [{}]",
|
||||
asmOp.instr.kind);
|
||||
new_entries.push_back(entry);
|
||||
continue;
|
||||
@@ -51,7 +51,7 @@ bool rewrite_inline_asm_instructions(Form* top_level_form,
|
||||
// If its an invalid or unsupported exception, skip it
|
||||
/*lg::warn("[ASM Re-Write] - Inline assembly instruction marked with TODO - [{}]",
|
||||
asmOp.full_function_name());*/
|
||||
f.warnings.general_warning(";; Inline assembly instruction marked with TODO - [{}]",
|
||||
f.warnings.general_warning("Inline assembly instruction marked with TODO - [{}]",
|
||||
asmOp.full_function_name());
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,42 @@ FormElement* rewrite_as_dotimes(LetElement* in, const Env& env, FormPool& pool)
|
||||
mr.maps.forms.at(1), body);
|
||||
}
|
||||
|
||||
FormElement* fix_up_abs(LetElement* in, const Env& env, FormPool& pool) {
|
||||
/*
|
||||
(let ((v0-0 x)
|
||||
)
|
||||
(abs v0-0)
|
||||
)
|
||||
*/
|
||||
|
||||
if (in->entries().size() != 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// look for setting a temp.
|
||||
auto temp = in->entries().at(0).dest;
|
||||
auto temp_name = env.get_variable_name(temp);
|
||||
|
||||
Form* src = in->entries().at(0).src;
|
||||
|
||||
auto body_matcher =
|
||||
Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::ABS), {Matcher::any_reg(0)});
|
||||
auto mr = match(body_matcher, in->body());
|
||||
if (!mr.matched) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
assert(mr.maps.regs.at(0));
|
||||
auto abs_var_name = env.get_variable_name(*mr.maps.regs.at(0));
|
||||
if (abs_var_name != temp_name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// success!
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::ABS),
|
||||
src);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Attempt to rewrite a let as another form. If it cannot be rewritten, this will return nullptr.
|
||||
*/
|
||||
@@ -172,6 +208,11 @@ FormElement* rewrite_let(LetElement* in, const Env& env, FormPool& pool) {
|
||||
return as_dotimes;
|
||||
}
|
||||
|
||||
auto as_abs = fix_up_abs(in, env, pool);
|
||||
if (as_abs) {
|
||||
return as_abs;
|
||||
}
|
||||
|
||||
// nothing matched.
|
||||
return nullptr;
|
||||
}
|
||||
@@ -239,7 +280,8 @@ LetStats insert_lets(const Function& func, Env& env, FormPool& pool, Form* top_l
|
||||
kv.second.lca_form->at(i)->collect_vars(ras, true);
|
||||
bool uses = false;
|
||||
for (auto& ra : ras) {
|
||||
if (env.get_variable_name(ra) == kv.second.var_name) {
|
||||
if ((ra.reg().get_kind() == Reg::FPR || ra.reg().get_kind() == Reg::GPR) &&
|
||||
env.get_variable_name(ra) == kv.second.var_name) {
|
||||
uses = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,14 +148,11 @@
|
||||
},
|
||||
|
||||
"(method 2 array)":{
|
||||
"vars":{"v1-1":"content-type-sym","s5-0":"i","s5-1":"i","s5-2":"i"
|
||||
,"s5-3":"i","s5-4":"i","s5-5":"i","s5-6":"i","s5-7":"i","s5-8":"i",
|
||||
"s5-9":"i","s5-10":"i","s5-11":"i"}
|
||||
"vars":{"v1-1":"content-type-sym"}
|
||||
},
|
||||
|
||||
"(method 3 array)":{
|
||||
"vars":{"v1-1":"content-type-sym","s5-0":"i","s5-1":"i","s5-2":"i"
|
||||
,"s5-3":"i","s5-4":"i","s5-5":"i","s5-6":"i","s5-7":"i","s5-8":"i","s5-9":"i","s5-10":"i","s5-11":"i"}
|
||||
"vars":{"v1-1":"content-type-sym"}
|
||||
},
|
||||
|
||||
"mem-copy!":{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -266,7 +266,7 @@ TEST_F(FormRegressionTest, ExprAbs) {
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function int int)";
|
||||
std::string expected = "(let ((v0-0 arg0)) (abs v0-0))";
|
||||
std::string expected = "(abs arg0)";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
@@ -704,13 +704,13 @@ TEST_F(FormRegressionTest, ExprPairMethod4) {
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (cond\n"
|
||||
" ((= arg0 (quote ())) (set! v0-0 0))\n"
|
||||
" ((null? arg0) (set! v0-0 0))\n"
|
||||
" (else\n"
|
||||
" (let\n"
|
||||
" ((v1-1 (cdr arg0)))\n"
|
||||
" (set! v0-0 1)\n"
|
||||
" (while\n"
|
||||
" (and (!= v1-1 (quote ())) (< (shl (the-as int v1-1) 62) 0))\n"
|
||||
" (and (not (null? v1-1)) (pair? v1-1))\n"
|
||||
" (+! v0-0 1)\n"
|
||||
" (set! v1-1 (cdr v1-1))\n"
|
||||
" )\n"
|
||||
@@ -762,7 +762,7 @@ TEST_F(FormRegressionTest, ExprLast) {
|
||||
std::string expected =
|
||||
"(let\n"
|
||||
" ((v0-0 arg0))\n"
|
||||
" (while (!= (cdr v0-0) (quote ())) (nop!) (nop!) (set! v0-0 (cdr v0-0)))\n"
|
||||
" (while (not (null? (cdr v0-0))) (nop!) (nop!) (set! v0-0 (cdr v0-0)))\n"
|
||||
" v0-0\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, true, "");
|
||||
@@ -812,10 +812,10 @@ TEST_F(FormRegressionTest, ExprMember) {
|
||||
"(let\n"
|
||||
" ((v1-0 arg1))\n"
|
||||
" (while\n"
|
||||
" (not (or (= v1-0 (quote ())) (= (car v1-0) arg0)))\n"
|
||||
" (not (or (null? v1-0) (= (car v1-0) arg0)))\n"
|
||||
" (set! v1-0 (cdr v1-0))\n"
|
||||
" )\n"
|
||||
" (if (!= v1-0 (quote ())) v1-0)\n"
|
||||
" (if (not (null? v1-0)) v1-0)\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, true, "");
|
||||
}
|
||||
@@ -874,10 +874,10 @@ TEST_F(FormRegressionTest, ExprNmember) {
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (while\n"
|
||||
" (not (or (= arg1 '()) (name= (the-as basic (car arg1)) arg0)))\n"
|
||||
" (not (or (null? arg1) (name= (the-as basic (car arg1)) arg0)))\n"
|
||||
" (set! arg1 (cdr arg1))\n"
|
||||
" )\n"
|
||||
" (if (!= arg1 '()) arg1)\n"
|
||||
" (if (not (null? arg1)) arg1)\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, true, "");
|
||||
}
|
||||
@@ -925,10 +925,10 @@ TEST_F(FormRegressionTest, ExprAssoc) {
|
||||
"(let\n"
|
||||
" ((v1-0 arg1))\n"
|
||||
" (while\n"
|
||||
" (not (or (= v1-0 (quote ())) (= (car (car v1-0)) arg0)))\n"
|
||||
" (not (or (null? v1-0) (= (car (car v1-0)) arg0)))\n"
|
||||
" (set! v1-0 (cdr v1-0))\n"
|
||||
" )\n"
|
||||
" (if (!= v1-0 (quote ())) (car v1-0))\n"
|
||||
" (if (not (null? v1-0)) (car v1-0))\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, true, "");
|
||||
}
|
||||
@@ -989,14 +989,14 @@ TEST_F(FormRegressionTest, ExprAssoce) {
|
||||
" (while\n"
|
||||
" (not\n"
|
||||
" (or\n"
|
||||
" (= v1-0 (quote ()))\n"
|
||||
" (null? v1-0)\n"
|
||||
" (= (car (car v1-0)) arg0)\n"
|
||||
" (= (car (car v1-0)) (quote else))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! v1-0 (cdr v1-0))\n"
|
||||
" )\n"
|
||||
" (if (!= v1-0 (quote ())) (car v1-0))\n"
|
||||
" (if (not (null? v1-0)) (car v1-0))\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, true, "");
|
||||
}
|
||||
@@ -1074,7 +1074,7 @@ TEST_F(FormRegressionTest, ExprNassoc) {
|
||||
" (while\n"
|
||||
" (not\n"
|
||||
" (or\n"
|
||||
" (= arg1 (quote ()))\n"
|
||||
" (null? arg1)\n"
|
||||
" (let\n"
|
||||
" ((a1-1 (car (car arg1))))\n"
|
||||
" (if\n"
|
||||
@@ -1087,7 +1087,7 @@ TEST_F(FormRegressionTest, ExprNassoc) {
|
||||
" )\n"
|
||||
" (set! arg1 (cdr arg1))\n"
|
||||
" )\n"
|
||||
" (if (!= arg1 (quote ())) (car arg1))\n"
|
||||
" (if (not (null? arg1)) (car arg1))\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, true, "");
|
||||
}
|
||||
@@ -1176,7 +1176,7 @@ TEST_F(FormRegressionTest, ExprNassoce) {
|
||||
" (while\n"
|
||||
" (not\n"
|
||||
" (or\n"
|
||||
" (= arg1 (quote ()))\n"
|
||||
" (null? arg1)\n"
|
||||
" (let\n"
|
||||
" ((s4-0 (car (car arg1))))\n"
|
||||
" (if\n"
|
||||
@@ -1192,7 +1192,7 @@ TEST_F(FormRegressionTest, ExprNassoce) {
|
||||
" )\n"
|
||||
" (set! arg1 (cdr arg1))\n"
|
||||
" )\n"
|
||||
" (if (!= arg1 (quote ())) (car arg1))\n"
|
||||
" (if (not (null? arg1)) (car arg1))\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, true, "");
|
||||
}
|
||||
@@ -1243,12 +1243,12 @@ TEST_F(FormRegressionTest, ExprAppend) {
|
||||
|
||||
std::string expected =
|
||||
"(cond\n"
|
||||
" ((= arg0 (quote ())) arg1)\n"
|
||||
" ((null? arg0) arg1)\n"
|
||||
" (else\n"
|
||||
" (let\n"
|
||||
" ((v1-1 arg0))\n"
|
||||
" (while (!= (cdr v1-1) (quote ())) (nop!) (nop!) (set! v1-1 (cdr v1-1)))\n"
|
||||
" (if (!= v1-1 (quote ())) (set! (cdr v1-1) arg1))\n"
|
||||
" (while (not (null? (cdr v1-1))) (nop!) (nop!) (set! v1-1 (cdr v1-1)))\n"
|
||||
" (if (not (null? v1-1)) (set! (cdr v1-1) arg1))\n"
|
||||
" )\n"
|
||||
" arg0\n"
|
||||
" )\n"
|
||||
@@ -1320,11 +1320,11 @@ TEST_F(FormRegressionTest, ExprDelete) {
|
||||
" (let\n"
|
||||
" ((v1-1 arg1) (a2-0 (cdr arg1)))\n"
|
||||
" (while\n"
|
||||
" (not (or (= a2-0 (quote ())) (= (car a2-0) arg0)))\n"
|
||||
" (not (or (null? a2-0) (= (car a2-0) arg0)))\n"
|
||||
" (set! v1-1 a2-0)\n"
|
||||
" (set! a2-0 (cdr a2-0))\n"
|
||||
" )\n"
|
||||
" (if (!= a2-0 (quote ())) (set! (cdr v1-1) (cdr a2-0)))\n"
|
||||
" (if (not (null? a2-0)) (set! (cdr v1-1) (cdr a2-0)))\n"
|
||||
" )\n"
|
||||
" arg1\n"
|
||||
" )\n"
|
||||
@@ -1399,11 +1399,11 @@ TEST_F(FormRegressionTest, ExprDeleteCar) {
|
||||
" (let\n"
|
||||
" ((v1-2 arg1) (a2-0 (cdr arg1)))\n"
|
||||
" (while\n"
|
||||
" (not (or (= a2-0 (quote ())) (= (car (car a2-0)) arg0)))\n"
|
||||
" (not (or (null? a2-0) (= (car (car a2-0)) arg0)))\n"
|
||||
" (set! v1-2 a2-0)\n"
|
||||
" (set! a2-0 (cdr a2-0))\n"
|
||||
" )\n"
|
||||
" (if (!= a2-0 (quote ())) (set! (cdr v1-2) (cdr a2-0)))\n"
|
||||
" (if (not (null? a2-0)) (set! (cdr v1-2) (cdr a2-0)))\n"
|
||||
" )\n"
|
||||
" arg1\n"
|
||||
" )\n"
|
||||
@@ -1560,7 +1560,7 @@ TEST_F(FormRegressionTest, ExprSort) {
|
||||
" ((s3-0 arg0))\n"
|
||||
" (while\n"
|
||||
" (not\n"
|
||||
" (or (= (cdr s3-0) (quote ())) (>= (shl (the-as int (cdr s3-0)) 62) 0))\n"
|
||||
" (or (null? (cdr s3-0)) (not (pair? (cdr s3-0))))\n"
|
||||
" )\n"
|
||||
" (let*\n"
|
||||
" ((s2-0 (car s3-0)) (s1-0 (car (cdr s3-0))) (v1-1 (arg1 s2-0 s1-0)))\n"
|
||||
@@ -1858,7 +1858,7 @@ TEST_F(FormRegressionTest, ExprMemCopy) {
|
||||
" ((v0-0 arg0))\n"
|
||||
" (dotimes\n"
|
||||
" (v1-0 arg2)\n"
|
||||
" (set! (-> (the-as (pointer int8) arg0)) (-> (the-as (pointer uint8) arg1)))\n"
|
||||
" (set! (-> (the-as (pointer uint8) arg0)) (-> (the-as (pointer uint8) arg1)))\n"
|
||||
" (&+! arg0 1)\n"
|
||||
" (&+! arg1 1)\n"
|
||||
" )\n"
|
||||
@@ -1940,7 +1940,7 @@ TEST_F(FormRegressionTest, ExprMemOr) {
|
||||
" (dotimes\n"
|
||||
" (v1-0 arg2)\n"
|
||||
" (set!\n"
|
||||
" (-> (the-as (pointer int8) arg0))\n"
|
||||
" (-> (the-as (pointer uint8) arg0))\n"
|
||||
" (logior\n"
|
||||
" (-> (the-as (pointer uint8) arg0))\n"
|
||||
" (-> (the-as (pointer uint8) arg1))\n"
|
||||
|
||||
@@ -39,20 +39,29 @@ const std::unordered_set<std::string> expected_skip_in_decompiler = {
|
||||
};
|
||||
|
||||
const std::unordered_set<std::string> skip_in_compiling = {
|
||||
//////////////////////
|
||||
// GCOMMON
|
||||
//////////////////////
|
||||
|
||||
// these functions are not implemented by the compiler in OpenGOAL, but are in GOAL.
|
||||
"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)",
|
||||
// these should pass eventually
|
||||
"(method 2 array)", "(method 3 array)", "valid?", "mem-copy!", "qmem-copy<-!", "qmem-copy->!",
|
||||
"mem-or!", "breakpoint-range-set!", "print", "printl", "inspect"};
|
||||
"(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",
|
||||
// inline assembly
|
||||
"valid?"};
|
||||
|
||||
// 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 =
|
||||
"(define-extern name= (function basic basic symbol))\n"
|
||||
// todo - check if recursive?
|
||||
"(define-extern fact (function int int))";
|
||||
|
||||
// default location for the data. It can be changed with a command line argument.
|
||||
|
||||
Reference in New Issue
Block a user