[Decompiler] More progress on method stuff (#264)

* more progress on method

* fix method issues
This commit is contained in:
water111 2021-02-15 11:36:59 -05:00 committed by GitHub
parent 14d602c594
commit af1691a0bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 1281 additions and 75 deletions

View file

@ -101,6 +101,12 @@ class SimpleExpressionElement : public FormElement {
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects);
void update_from_stack_si_1(const Env& env,
FixedOperatorKind kind,
FormPool& pool,
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects);
void update_from_stack_add_i(const Env& env,
FormPool& pool,
FormStack& stack,

View file

@ -257,6 +257,27 @@ void FormElement::update_from_stack(const Env& env,
throw std::runtime_error(fmt::format("update_from_stack NYI for {}", to_string(env)));
}
namespace {
Form* make_cast(Form* in, const TypeSpec& in_type, const TypeSpec& out_type, FormPool& pool) {
if (in_type == out_type) {
return in;
}
return pool.alloc_single_element_form<CastElement>(nullptr, out_type, in);
}
std::vector<Form*> make_cast(const std::vector<Form*>& in,
const std::vector<TypeSpec>& in_types,
const TypeSpec& out_type,
FormPool& pool) {
std::vector<Form*> out;
assert(in.size() == in_types.size());
for (size_t i = 0; i < in_types.size(); i++) {
out.push_back(make_cast(in.at(i), in_types.at(i), out_type, pool));
}
return out;
}
} // namespace
/*!
* Update a LoadSourceElement from the stack.
*/
@ -401,6 +422,18 @@ void SimpleExpressionElement::update_from_stack_float_1(const Env& env,
}
}
void SimpleExpressionElement::update_from_stack_si_1(const Env& env,
FixedOperatorKind kind,
FormPool& pool,
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects) {
auto in_type = env.get_types_before_op(m_my_idx).get(m_expr.get_arg(0).var().reg()).typespec();
auto arg = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0);
result->push_back(pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(kind), make_cast(arg, in_type, TypeSpec("int"), pool)));
}
void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
FormPool& pool,
FormStack& stack,
@ -688,7 +721,8 @@ void SimpleExpressionElement::update_from_stack_int_to_float(const Env& env,
}
result->push_back(pool.alloc_element<CastElement>(TypeSpec("float"), arg, true));
} else {
throw std::runtime_error("Used int to float on a " + type.print());
throw std::runtime_error(fmt::format("Used int to float on a {} from {}: {}", type.print(),
var.to_form(env).print(), arg->to_string(env)));
}
}
@ -753,6 +787,10 @@ void SimpleExpressionElement::update_from_stack(const Env& env,
update_from_stack_float_1(env, FixedOperatorKind::FABS, pool, stack, result,
allow_side_effects);
break;
case SimpleExpression::Kind::NEG:
update_from_stack_si_1(env, FixedOperatorKind::SUBTRACTION, pool, stack, result,
allow_side_effects);
break;
case SimpleExpression::Kind::NEG_S:
update_from_stack_float_1(env, FixedOperatorKind::SUBTRACTION, pool, stack, result,
allow_side_effects);
@ -839,6 +877,12 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
assert(x->parent_form == m_src);
}
assert(m_src->parent_element == this);
if (is_dead_set()) {
stack.push_value_to_reg_dead(m_dst, m_src, true, m_var_info);
return;
}
m_src->update_children_from_stack(env, pool, stack, true);
for (auto x : m_src->elts()) {
assert(x->parent_form == m_src);
@ -980,6 +1024,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
FormElement* new_form = nullptr;
if (is_method) {
// fmt::print("STACK:\n{}\n\n", stack.print(env));
auto matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::METHOD_OF_OBJECT),
{Matcher::any(0), Matcher::any(1)});
auto mr = match(matcher, unstacked.at(0));
@ -988,7 +1033,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
unstacked.at(0)->to_string(env));
}
auto unsafe = stack.unsafe_peek(Register(Reg::GPR, Reg::A0));
auto unsafe = stack.unsafe_peek(Register(Reg::GPR, Reg::A0), env);
if (!unsafe) {
if (!stack.is_root()) {
fmt::print("STACK:\n{}\n\n", stack.print(env));
@ -996,21 +1041,59 @@ void FunctionCallElement::update_from_stack(const Env& env,
}
// failed to peek by reaching the end AND root stack, means we just take the function
// argument.
assert(false); // want to test this before enabling.
unsafe = mr.maps.forms.at(0);
}
bool resolved = false;
if (unsafe) {
if (!unsafe->try_as_single_element()) {
throw std::runtime_error(
fmt::format("Peek got something weird: {}\n", unsafe->to_string(env)));
}
if (unsafe->try_as_single_element() != mr.maps.forms.at(0)->try_as_single_element()) {
throw std::runtime_error(fmt::format("Invalid method call. {} vs {}.",
unsafe->to_string(env),
mr.maps.forms.at(0)->to_string(env)));
if (unsafe->try_as_single_element() == mr.maps.forms.at(0)->try_as_single_element()) {
resolved = true;
}
} else {
throw std::runtime_error("Failed to peek for arg0");
if (!resolved) {
if (unsafe->try_as_single_element()->to_form(env) ==
mr.maps.forms.at(0)->try_as_single_element()->to_form(env)) {
resolved = true;
lg::warn(
fmt::format("Rare method call (type 1). {} vs {}. Not an error, but check carefully",
unsafe->to_string(env), mr.maps.forms.at(0)->to_string(env)));
}
}
if (!resolved) {
lg::warn(
fmt::format("Rare method call (type 2). {} vs {}. Not an error, but check carefully",
unsafe->to_string(env), mr.maps.forms.at(0)->to_string(env)));
auto unsafe_as_se = dynamic_cast<SimpleExpressionElement*>(unsafe->try_as_single_element());
if (unsafe_as_se && unsafe_as_se->expr().is_identity() &&
unsafe_as_se->expr().get_arg(0).is_var()) {
auto var = unsafe_as_se->expr().get_arg(0).var();
auto unsafe_2 = stack.unsafe_peek(var.reg(), env);
if (unsafe_2) {
if (unsafe_2->try_as_single_element() == mr.maps.forms.at(0)->try_as_single_element()) {
resolved = true;
unsafe = unsafe_2;
} else {
if (unsafe_2->try_as_single_element()->to_form(env) ==
mr.maps.forms.at(0)->try_as_single_element()->to_form(env)) {
lg::warn("Check even more carefully");
resolved = true;
unsafe = unsafe_2;
}
}
}
}
}
}
if (!resolved) {
throw std::runtime_error("Failed to resolve.");
}
arg_forms.insert(arg_forms.begin(), unsafe);
@ -1178,6 +1261,34 @@ void DerefElement::update_from_stack(const Env& env,
}
}
if (m_tokens.size() >= 3) {
auto& method_name = m_tokens.at(m_tokens.size() - 1);
auto& mbn = m_tokens.at(m_tokens.size() - 2);
auto& type = m_tokens.at(m_tokens.size() - 3);
if (method_name.kind() == DerefToken::Kind::FIELD_NAME &&
mbn.kind() == DerefToken::Kind::FIELD_NAME && mbn.field_name() == "methods-by-name" &&
type.kind() == DerefToken::Kind::FIELD_NAME && type.field_name() == "type") {
std::string name = method_name.field_name();
m_tokens.pop_back();
m_tokens.pop_back();
m_tokens.pop_back();
if (m_tokens.empty()) {
auto method_op = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::METHOD_OF_OBJECT), m_base,
pool.alloc_single_element_form<ConstantTokenElement>(nullptr, name));
result->push_back(method_op);
} else {
auto method_op = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::METHOD_OF_OBJECT),
pool.alloc_single_form(nullptr, this),
pool.alloc_single_element_form<ConstantTokenElement>(nullptr, name));
result->push_back(method_op);
}
return;
}
}
// rewrite access to the method table to use method-of-object
// (-> <some-object> type methods-by-name <method-name>)
// (method-of-object <some-object> <method-name>)
@ -1219,8 +1330,10 @@ void UntilElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stac
}
void WhileElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) {
for (auto form : {condition, body}) {
FormStack temp_stack(false);
bool first = true;
for (auto form : {body, condition}) {
FormStack temp_stack(first && stack.is_root());
first = false;
for (auto& entry : form->elts()) {
entry->push_to_stack(env, pool, temp_stack);
}
@ -1475,27 +1588,6 @@ void ShortCircuitElement::update_from_stack(const Env& env,
// ConditionElement
///////////////////
namespace {
Form* make_cast(Form* in, const TypeSpec& in_type, const TypeSpec& out_type, FormPool& pool) {
if (in_type == out_type) {
return in;
}
return pool.alloc_single_element_form<CastElement>(nullptr, out_type, in);
}
std::vector<Form*> make_cast(const std::vector<Form*>& in,
const std::vector<TypeSpec>& in_types,
const TypeSpec& out_type,
FormPool& pool) {
std::vector<Form*> out;
assert(in.size() == in_types.size());
for (size_t i = 0; i < in_types.size(); i++) {
out.push_back(make_cast(in.at(i), in_types.at(i), out_type, pool));
}
return out;
}
} // namespace
FormElement* ConditionElement::make_generic(const Env&,
FormPool& pool,
const std::vector<Form*>& source_forms,
@ -1721,12 +1813,11 @@ void GenericElement::update_from_stack(const Env& env,
FormStack& stack,
std::vector<FormElement*>* result,
bool) {
for (auto it = m_elts.rbegin(); it != m_elts.rend(); it++) {
(*it)->update_children_from_stack(env, pool, stack, false);
}
// TODO improve.
if (m_head.m_kind == GenericOperator::Kind::FUNCTION_EXPR) {
m_head.m_function->update_children_from_stack(env, pool, stack, false);
} else {
m_elts.back()->update_children_from_stack(env, pool, stack, false);
}
result->push_back(this);
}

View file

@ -39,6 +39,20 @@ void FormStack::push_value_to_reg(Variable var,
m_stack.push_back(entry);
}
void FormStack::push_value_to_reg_dead(Variable var,
Form* value,
bool sequence_point,
const SetVarInfo& info) {
assert(value);
StackEntry entry;
entry.active = false;
entry.sequence_point = sequence_point;
entry.destination = var;
entry.source = value;
entry.set_info = info;
m_stack.push_back(entry);
}
void FormStack::push_non_seq_reg_to_reg(const Variable& dst,
const Variable& src,
Form* src_as_form,
@ -146,17 +160,22 @@ Form* FormStack::pop_reg(Register reg,
}
}
}
} else {
if (entry.destination.has_value() && entry.destination->reg() == reg) {
return nullptr;
}
}
}
// we didn't have it...
return nullptr;
}
Form* FormStack::unsafe_peek(Register reg) {
Form* FormStack::unsafe_peek(Register reg, const Env& env) {
RegSet modified;
for (size_t i = m_stack.size(); i-- > 0;) {
auto& entry = m_stack.at(i);
if (entry.active) {
fmt::print("PEEK ERROR {}:\n{}\n", reg.to_string(), print(env));
throw std::runtime_error("Failed to unsafe peek 1");
}

View file

@ -21,6 +21,10 @@ class FormStack {
const Variable& src,
Form* src_as_form,
const SetVarInfo& info = {});
void push_value_to_reg_dead(Variable var,
Form* value,
bool sequence_point,
const SetVarInfo& info = {});
void push_form_element(FormElement* elt, bool sequence_point);
Form* pop_reg(const Variable& var,
const RegSet& barrier,
@ -32,7 +36,7 @@ class FormStack {
const Env& env,
bool allow_side_effects,
int begin_idx = -1);
Form* unsafe_peek(Register reg);
Form* unsafe_peek(Register reg, const Env& env);
bool is_single_expression();
std::vector<FormElement*> rewrite(FormPool& pool);
std::string print(const Env& env);

View file

@ -1232,7 +1232,8 @@ void convert_and_inline(FormPool& pool, Function& f, const BlockVtx* as_block, T
// check for deadness
if (op_as_set->is_dead_set()) {
// we want to eliminate, but we should we fix up the register info.
add = false;
// now adding.
// add = false;
auto consumed_expr =
dynamic_cast<SimpleExpressionElement*>(op_as_set->src()->try_as_single_element());
assert(consumed_expr);

View file

@ -361,7 +361,7 @@ int DecompilerTypeSystem::get_format_arg_count(const std::string& str) const {
for (size_t i = 0; i < str.length(); i++) {
if (str.at(i) == '~') {
i++; // also eat the next character.
if (i < str.length() && (str.at(i) == '%' || str.at(i) == 'T')) {
if (i < str.length() && (str.at(i) == '%' || str.at(i) == 'T' || str.at(i) == '0')) {
// newline (~%) or tab (~T) don't take an argument.
continue;
}

View file

@ -734,6 +734,7 @@ TEST_F(FormRegressionTest, NestedAndOr) {
" (set! (car s3-0) s1-0)\n" // set iter's car to cadr
" (set! v1-4 (cdr s3-0))\n" // current cdr
" (set! (car v1-4) s2-0)\n" // set cadr
" (set! v1-5 s2-0)\n"
" )\n"
" (set! s3-0 (cdr s3-0))\n" // increment!
" )\n"

File diff suppressed because it is too large Load diff