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] gkernel offline test #321

Merged
merged 4 commits into from
Mar 14, 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
4 changes: 2 additions & 2 deletions decompiler/Function/TypeAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ bool Function::run_type_analysis_ir2(
} catch (std::runtime_error& e) {
lg::warn("Function {} failed type prop: {}", guessed_name.to_string(), e.what());
warnings.type_prop_warning("{}", e.what());
ir2.env.set_types(block_init_types, op_types, *ir2.atomic_ops);
ir2.env.set_types(block_init_types, op_types, *ir2.atomic_ops, my_type);
return false;
}

Expand Down Expand Up @@ -198,7 +198,7 @@ bool Function::run_type_analysis_ir2(
}
}

ir2.env.set_types(block_init_types, op_types, *ir2.atomic_ops);
ir2.env.set_types(block_init_types, op_types, *ir2.atomic_ops, my_type);

return true;
}
Expand Down
67 changes: 63 additions & 4 deletions decompiler/IR2/AtomicOpForm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,61 @@ FormElement* SetVarConditionOp::get_as_form(FormPool& pool, const Env& env) cons
is_sequence_point(), TypeSpec("symbol"));
}

namespace {
std::optional<TypeSpec> get_typecast_for_atom(const SimpleAtom& atom,
const Env& env,
const TypeSpec& expected_type,
int my_idx) {
auto type_info = env.dts->ts.lookup_type(expected_type);
switch (atom.get_kind()) {
case SimpleAtom::Kind::VARIABLE: {
auto src_type = env.get_types_before_op(my_idx).get(atom.var().reg());

if (src_type.requires_cast() || !env.dts->ts.tc(expected_type, src_type.typespec())) {
// we fail the typecheck for a normal set!, so add a cast.
return expected_type;
} else {
return {};
}

} break;
case SimpleAtom::Kind::INTEGER_CONSTANT: {
std::optional<TypeSpec> cast_for_set, cast_for_define;
bool sym_int_or_uint = env.dts->ts.tc(TypeSpec("integer"), expected_type);
bool sym_uint = env.dts->ts.tc(TypeSpec("uinteger"), expected_type);
bool sym_int = sym_int_or_uint && !sym_uint;

if (sym_int) {
// do nothing for set.
return {};
} else {
// for uint or other
return expected_type;
}

} break;

case SimpleAtom::Kind::SYMBOL_PTR:
case SimpleAtom::Kind::SYMBOL_VAL: {
assert(atom.get_str() == "#f");

if (expected_type != TypeSpec("symbol")) {
// explicitly cast if we're not using a reference type, including pointers.
// otherwise, we allow setting references to #f.
if (!type_info->is_reference()) {
return expected_type;
}
return {};
}
} break;

default:
assert(false);
}
return {};
}
} // namespace

FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
if (env.has_type_analysis()) {
if (m_addr.is_identity() && m_addr.get_arg(0).is_sym_val()) {
Expand Down Expand Up @@ -248,8 +303,10 @@ FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
}
assert(!rd.addr_of);
auto addr = pool.alloc_element<DerefElement>(source, rd.addr_of, tokens);
return pool.alloc_element<StorePlainDeref>(addr, m_value.as_expr(), m_my_idx, ro.var,
std::nullopt);

return pool.alloc_element<StorePlainDeref>(
addr, m_value.as_expr(), m_my_idx, ro.var, std::nullopt,
get_typecast_for_atom(m_value, env, coerce_to_reg_type(rd.result_type), m_my_idx));
}

std::string cast_type;
Expand Down Expand Up @@ -285,7 +342,8 @@ FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
auto deref =
pool.alloc_element<DerefElement>(cast_source, false, std::vector<DerefToken>());
return pool.alloc_element<StorePlainDeref>(deref, m_value.as_expr(), m_my_idx, ro.var,
TypeSpec("pointer", {TypeSpec(cast_type)}));
TypeSpec("pointer", {TypeSpec(cast_type)}),
std::nullopt);
}
}
}
Expand Down Expand Up @@ -412,7 +470,8 @@ FormElement* LoadVarOp::get_as_form(FormPool& pool, const Env& env) const {
m_type.value_or(TypeSpec("object")));
}

if (input_type.typespec() == TypeSpec("pointer")) {
if (input_type.typespec() == TypeSpec("pointer") ||
input_type.kind == TP_Type::Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT) {
std::string cast_type;
switch (m_size) {
case 1:
Expand Down
3 changes: 2 additions & 1 deletion decompiler/IR2/AtomicOpTypeAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,8 @@ TP_Type LoadVarOp::get_src_type(const TypeState& input,
return TP_Type::make_from_ts(coerce_to_reg_type(rd.result_type));
}

if (input_type.typespec() == TypeSpec("pointer")) {
if (input_type.typespec() == TypeSpec("pointer") ||
input_type.kind == TP_Type::Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT) {
// we got a plain pointer. let's just assume we're loading an integer.
// perhaps we should disable this feature by default on 4-byte loads if we're getting
// lots of false positives for loading pointers from plain pointers.
Expand Down
56 changes: 51 additions & 5 deletions decompiler/IR2/Env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,39 @@ goos::Object Env::get_variable_name_with_cast(Register reg, int atomic_idx, Acce
}
}

std::optional<TypeSpec> Env::get_user_cast_for_access(const RegisterAccess& access) const {
if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) {
auto& var_info = m_var_names.lookup(access.reg(), access.idx(), access.mode());
std::string original_name = var_info.name();

auto type_kv = m_typecasts.find(access.idx());
if (type_kv != m_typecasts.end()) {
for (auto& x : type_kv->second) {
if (x.reg == access.reg()) {
// let's make sure the above claim is true
TypeSpec type_in_reg;
if (has_type_analysis() && access.mode() == AccessMode::READ) {
type_in_reg =
get_types_for_op_mode(access.idx(), AccessMode::READ).get(access.reg()).typespec();
if (type_in_reg.print() != x.type_name) {
lg::error(
"Decompiler type consistency error. There was a typecast for reg {} at idx {} "
"(var {}) to type {}, but the actual type is {} ({})",
access.reg().to_charp(), access.idx(), original_name, x.type_name,
type_in_reg.print(), type_in_reg.print());
assert(false);
}
}

auto cast_type = dts->parse_type_spec(x.type_name);
return cast_type;
}
}
}
}
return {};
}

std::string Env::get_variable_name(const RegisterAccess& access) const {
if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) {
std::string lookup_name = m_var_names.lookup(access.reg(), access.idx(), access.mode()).name();
Expand All @@ -186,15 +219,17 @@ std::string Env::get_variable_name(const RegisterAccess& access) const {
* NOTE: this is _NOT_ the most specific type known to the decompiler, but instead the type
* of the variable.
*/
TypeSpec Env::get_variable_type(const RegisterAccess& access) const {
TypeSpec Env::get_variable_type(const RegisterAccess& access, bool using_user_var_types) const {
if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) {
auto& var_info = m_var_names.lookup(access.reg(), access.idx(), access.mode());
std::string original_name = var_info.name();

auto type_of_var = var_info.type.typespec();
auto retype_kv = m_var_retype.find(original_name);
if (retype_kv != m_var_retype.end()) {
type_of_var = retype_kv->second;
if (using_user_var_types) {
auto retype_kv = m_var_retype.find(original_name);
if (retype_kv != m_var_retype.end()) {
type_of_var = retype_kv->second;
}
}

return type_of_var;
Expand All @@ -208,7 +243,8 @@ TypeSpec Env::get_variable_type(const RegisterAccess& access) const {
*/
void Env::set_types(const std::vector<TypeState>& block_init_types,
const std::vector<TypeState>& op_end_types,
const FunctionAtomicOps& atomic_ops) {
const FunctionAtomicOps& atomic_ops,
const TypeSpec& my_type) {
m_block_init_types = block_init_types;
m_op_end_types = op_end_types;

Expand All @@ -230,6 +266,16 @@ void Env::set_types(const std::vector<TypeState>& block_init_types,
}

m_has_types = true;

// check the actual return type:
if (my_type.last_arg() != TypeSpec("none")) {
auto as_end = dynamic_cast<const FunctionEndOp*>(atomic_ops.ops.back().get());
if (as_end) {
m_type_analysis_return_type = get_types_before_op((int)atomic_ops.ops.size() - 1)
.get(Register(Reg::GPR, Reg::V0))
.typespec();
}
}
}

std::string Env::print_local_var_types(const Form* top_level_form) const {
Expand Down
7 changes: 5 additions & 2 deletions decompiler/IR2/Env.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ class Env {
// TODO - remove this.
goos::Object get_variable_name_with_cast(Register reg, int atomic_idx, AccessMode mode) const;
std::string get_variable_name(const RegisterAccess& access) const;
TypeSpec get_variable_type(const RegisterAccess& access) const;
std::optional<TypeSpec> get_user_cast_for_access(const RegisterAccess& access) const;
TypeSpec get_variable_type(const RegisterAccess& access, bool using_user_var_types) const;

/*!
* Get the types in registers _after_ the given operation has completed.
Expand Down Expand Up @@ -80,7 +81,8 @@ class Env {

void set_types(const std::vector<TypeState>& block_init_types,
const std::vector<TypeState>& op_end_types,
const FunctionAtomicOps& atomic_ops);
const FunctionAtomicOps& atomic_ops,
const TypeSpec& my_type);

void set_local_vars(const VariableNames& names) {
m_var_names = names;
Expand Down Expand Up @@ -168,5 +170,6 @@ class Env {
std::unordered_map<std::string, LabelType> m_label_types;

std::unordered_set<std::string> m_vars_defined_in_let;
std::optional<TypeSpec> m_type_analysis_return_type;
};
} // namespace decompiler
35 changes: 26 additions & 9 deletions decompiler/IR2/Form.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,8 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
return "&+!";
case FixedOperatorKind::SUBTRACTION:
return "-";
case FixedOperatorKind::SUBTRACTION_PTR:
return "&-";
case FixedOperatorKind::MULTIPLICATION:
return "*";
case FixedOperatorKind::SQRT:
Expand Down Expand Up @@ -1481,6 +1483,8 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
return "null?";
case FixedOperatorKind::PAIRP:
return "pair?";
case FixedOperatorKind::NONE:
return "none";
default:
assert(false);
return "";
Expand Down Expand Up @@ -1950,22 +1954,35 @@ StorePlainDeref::StorePlainDeref(DerefElement* dst,
SimpleExpression expr,
int my_idx,
RegisterAccess base_var,
std::optional<TypeSpec> cast_type)
std::optional<TypeSpec> dst_cast_type,
std::optional<TypeSpec> src_cast_type)
: m_dst(dst),
m_expr(std::move(expr)),
m_my_idx(my_idx),
m_base_var(std::move(base_var)),
m_cast_type(cast_type) {}
m_base_var(base_var),
m_dst_cast_type(std::move(dst_cast_type)),
m_src_cast_type(std::move(src_cast_type)) {}

goos::Object StorePlainDeref::to_form_internal(const Env& env) const {
if (!m_cast_type.has_value()) {
return pretty_print::build_list("set!", m_dst->to_form(env),
m_expr.to_form(env.file->labels, env));
std::vector<goos::Object> lst = {pretty_print::to_symbol("set!")};

if (m_dst_cast_type) {
lst.push_back(
pretty_print::build_list("the-as", m_dst_cast_type->print(), m_dst->to_form(env)));
} else {
return pretty_print::build_list(
"set!", pretty_print::build_list("the-as", m_cast_type->print(), m_dst->to_form(env)),
m_expr.to_form(env.file->labels, env));
lst.push_back(m_dst->to_form(env));
}

if (m_src_cast_type) {
lst.push_back(pretty_print::build_list("the-as", m_src_cast_type->print(),
m_expr.to_form(env.file->labels, env)));
} else {
lst.push_back(m_expr.to_form(env.file->labels, env));
}

return pretty_print::build_list(lst);
}

void StorePlainDeref::apply(const std::function<void(FormElement*)>& f) {
f(this);
m_dst->apply(f);
Expand Down
5 changes: 3 additions & 2 deletions decompiler/IR2/Form.h
Original file line number Diff line number Diff line change
Expand Up @@ -1136,7 +1136,8 @@ class StorePlainDeref : public FormElement {
SimpleExpression expr,
int my_idx,
RegisterAccess base_var,
std::optional<TypeSpec> cast_type);
std::optional<TypeSpec> dst_cast_type,
std::optional<TypeSpec> src_cast_type);

goos::Object to_form_internal(const Env& env) const override;
void apply(const std::function<void(FormElement*)>& f) override;
Expand All @@ -1150,7 +1151,7 @@ class StorePlainDeref : public FormElement {
SimpleExpression m_expr;
int m_my_idx = -1;
RegisterAccess m_base_var;
std::optional<TypeSpec> m_cast_type;
std::optional<TypeSpec> m_dst_cast_type, m_src_cast_type;
};

class StoreArrayAccess : public FormElement {
Expand Down
Loading