Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Decompiler] Fix up pretty printing and other small changes #311

Merged
merged 4 commits into from
Mar 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion common/goos/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
*
*/

#include <cinttypes>
#include "Object.h"
#include "common/util/FileUtil.h"
#include "third-party/fmt/core.h"
Expand Down Expand Up @@ -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
Expand Down
76 changes: 74 additions & 2 deletions common/goos/PrettyPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions decompiler/IR2/AtomicOpForm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>(
Expand Down
4 changes: 4 additions & 0 deletions decompiler/IR2/Form.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
17 changes: 16 additions & 1 deletion decompiler/IR2/Form.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
126 changes: 109 additions & 17 deletions decompiler/IR2/FormExpressionAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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,
Expand All @@ -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>(
Expand All @@ -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: {
Expand All @@ -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: {
Expand Down
16 changes: 12 additions & 4 deletions decompiler/IR2/IR2_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ enum class FixedOperatorKind {
NEQ,
CONS,
METHOD_OF_OBJECT,
NULLP,
PAIRP,
INVALID
};

Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion decompiler/IR2/OpenGoalMapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading