jak-project/decompiler/analysis/expression_build.cpp
water111 78cde74d5a
update readme and fix always playing str (#1139)
* update readme deps

* replace assert

* bump timeout

* fix memory corruption in kernel

* use unknown if level name is invalid
2022-02-08 19:02:47 -05:00

175 lines
5.8 KiB
C++

#include "expression_build.h"
#include "common/goos/PrettyPrinter.h"
#include "decompiler/Function/Function.h"
#include "decompiler/IR2/Form.h"
#include "decompiler/IR2/FormStack.h"
#include "decompiler/util/DecompilerTypeSystem.h"
namespace decompiler {
/*!
* The main expression building pass.
*/
bool convert_to_expressions(
Form* top_level_form,
FormPool& pool,
Function& f,
const std::vector<std::string>& arg_names,
const std::unordered_map<std::string, LocalVarOverride>& var_override_map,
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.guessed_name.kind == FunctionName::FunctionKind::UNIDENTIFIED ||
f.guessed_name.kind == FunctionName::FunctionKind::NV_STATE ||
f.guessed_name.kind == FunctionName::FunctionKind::V_STATE) {
f.ir2.env.set_remap_for_function(f.type);
} else if (f.guessed_name.kind == FunctionName::FunctionKind::METHOD) {
auto method_type =
dts.ts.lookup_method(f.guessed_name.type_name, f.guessed_name.method_id).type;
if (f.guessed_name.method_id == GOAL_NEW_METHOD) {
f.ir2.env.set_remap_for_new_method(method_type);
} else {
f.ir2.env.set_remap_for_method(method_type);
}
}
// get variable names from the user.
f.ir2.env.map_args_from_config(arg_names, var_override_map);
// convert to typespec
for (auto& info : f.ir2.env.stack_slot_entries) {
auto rename = f.ir2.env.var_remap_map().find(info.second.name());
if (rename != f.ir2.env.var_remap_map().end()) {
info.second.name_override = rename->second;
}
// debug
// fmt::print("STACK {} : {} ({})\n", info.first, info.second.typespec.print(),
// info.second.tp_type.print());
}
// 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);
// and add all entries
for (auto& entry : top_level_form->elts()) {
entry->push_to_stack(f.ir2.env, pool, stack);
}
// rewrite the stack to get the correct final value
std::vector<FormElement*> new_entries;
if (f.type.last_arg() != TypeSpec("none")) {
auto return_var = f.ir2.atomic_ops->end_op().return_var();
new_entries = rewrite_to_get_var(stack, pool, return_var, f.ir2.env);
TypeSpec return_type = f.ir2.env.get_types_after_op(f.ir2.atomic_ops->ops.size() - 1)
.get(return_var.reg())
.typespec();
auto back_as_atom = form_element_as_atom(new_entries.back());
if (back_as_atom && back_as_atom->is_var()) {
return_type = f.ir2.env.get_variable_type(back_as_atom->var(), true);
auto var_cast = f.ir2.env.get_variable_and_cast(back_as_atom->var());
if (var_cast.cast) {
return_type = *var_cast.cast;
}
}
bool needs_cast = false;
if (!dts.ts.tc(f.type.last_arg(), return_type)) {
// we need to cast the final value.
needs_cast = true;
} else {
bool found_early_return = false;
for (auto e : new_entries) {
e->apply([&](FormElement* elt) {
auto as_ret = dynamic_cast<ReturnElement*>(elt);
if (as_ret) {
found_early_return = true;
}
});
if (found_early_return) {
break;
}
}
if (!found_early_return && f.type.last_arg() != return_type) {
needs_cast = true;
}
}
if (needs_cast) {
auto to_cast = new_entries.back();
auto as_cast = dynamic_cast<CastElement*>(to_cast);
if (as_cast) {
as_cast->set_type(f.type.last_arg());
} else {
new_entries.pop_back();
auto cast = pool.alloc_element<CastElement>(f.type.last_arg(),
pool.alloc_single_form(nullptr, to_cast));
new_entries.push_back(cast);
}
}
} 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
// the zero element case ever.
if (new_entries.empty()) {
new_entries.push_back(pool.alloc_element<EmptyElement>());
}
// turn us back into a form.
top_level_form->clear();
for (auto x : new_entries) {
top_level_form->push_back(x);
}
// and sanity check for tree errors.
for (auto x : top_level_form->elts()) {
ASSERT(x->parent_form == top_level_form);
}
// if we were don't return, make sure we didn't find a return form.
if (f.type.last_arg() == TypeSpec("none")) {
bool found_return = false;
top_level_form->apply([&](FormElement* elt) {
if (dynamic_cast<ReturnElement*>(elt)) {
found_return = true;
}
});
if (found_return) {
auto warn = fmt::format(
"Function {} has a return type of none, but the expression builder found a return "
"statement.",
f.name());
f.warnings.expression_build_warning(warn);
lg::warn(warn);
}
}
} catch (std::exception& e) {
f.warnings.expression_build_warning("In {}: {}", f.name(), e.what());
lg::warn("In {}: {}", f.name(), e.what());
return false;
}
return true;
}
} // namespace decompiler