mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
[Decompiler] More progress on method stuff (#264)
* more progress on method * fix method issues
This commit is contained in:
parent
14d602c594
commit
af1691a0bc
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue