[decompiler] Several small fixes (#775)

* fix assorted bugs

* stricter return types

* only look for functions in code files
This commit is contained in:
water111
2021-08-19 20:35:12 -04:00
committed by GitHub
parent 69d0840080
commit 11e11d2c04
48 changed files with 461 additions and 304 deletions
+15
View File
@@ -1121,6 +1121,21 @@ void StoreOp::collect_vars(RegAccessSet& vars) const {
// LoadVarOp
/////////////////////////////
std::string load_kind_to_string(LoadVarOp::Kind kind) {
switch (kind) {
case LoadVarOp::Kind::FLOAT:
return "float";
case LoadVarOp::Kind::VECTOR_FLOAT:
return "vector-float";
case LoadVarOp::Kind::SIGNED:
return "signed";
case LoadVarOp::Kind::UNSIGNED:
return "unsigned";
default:
assert(false);
}
}
LoadVarOp::LoadVarOp(Kind kind, int size, RegisterAccess dst, SimpleExpression src, int my_idx)
: AtomicOp(my_idx), m_kind(kind), m_size(size), m_dst(dst), m_src(std::move(src)) {}
+8 -1
View File
@@ -467,7 +467,7 @@ class StoreOp : public AtomicOp {
*/
class LoadVarOp : public AtomicOp {
public:
enum class Kind { UNSIGNED, SIGNED, FLOAT, VECTOR_FLOAT };
enum class Kind { UNSIGNED, SIGNED, FLOAT, VECTOR_FLOAT, INVALID };
LoadVarOp(Kind kind, int size, RegisterAccess dst, SimpleExpression src, int my_idx);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env& env) const override;
bool operator==(const AtomicOp& other) const override;
@@ -490,6 +490,13 @@ class LoadVarOp : public AtomicOp {
std::optional<TypeSpec> m_type;
};
std::string load_kind_to_string(LoadVarOp::Kind kind);
FormElement* make_label_load(int label_idx,
const Env& env,
FormPool& pool,
int load_size,
LoadVarOp::Kind load_kind);
/*!
* This represents one of the possible instructions that can go in a branch delay slot.
* These will be "absorbed" into higher level structures, but for the purpose of printing AtomicOps,
+85 -68
View File
@@ -476,6 +476,82 @@ FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
return pool.alloc_element<StoreElement>(this);
}
FormElement* make_label_load(int label_idx,
const Env& env,
FormPool& pool,
int load_size,
LoadVarOp::Kind load_kind) {
auto label = env.file->labels.at(label_idx);
auto label_name = label.name;
auto hint = env.label_types().find(label_name);
if (hint != env.label_types().end()) {
if (hint->second.is_const) {
if ((load_kind == LoadVarOp::Kind::FLOAT || load_kind == LoadVarOp::Kind::SIGNED) &&
load_size == 4 && hint->second.type_name == "float") {
assert((label.offset % 4) == 0);
auto word = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
assert(word.kind == LinkedWord::PLAIN_DATA);
float value;
memcpy(&value, &word.data, 4);
return pool.alloc_element<ConstantFloatElement>(value);
} else if (hint->second.type_name == "uint64" && load_kind != LoadVarOp::Kind::FLOAT &&
load_size == 8) {
assert((label.offset % 8) == 0);
auto word0 = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
auto word1 = env.file->words_by_seg.at(label.target_segment).at(1 + (label.offset / 4));
assert(word0.kind == LinkedWord::PLAIN_DATA);
assert(word1.kind == LinkedWord::PLAIN_DATA);
u64 value;
memcpy(&value, &word0.data, 4);
memcpy(((u8*)&value) + 4, &word1.data, 4);
return pool.alloc_element<CastElement>(TypeSpec("uint"),
pool.alloc_single_element_form<SimpleAtomElement>(
nullptr, SimpleAtom::make_int_constant(value)));
}
// is it a constant bitfield?
auto& ts = env.dts->ts;
auto as_bitfield = dynamic_cast<BitFieldType*>(ts.lookup_type(hint->second.type_name));
if (as_bitfield && load_kind != LoadVarOp::Kind::FLOAT && load_size == 8) {
// get the data
assert((label.offset % 8) == 0);
auto word0 = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
auto word1 = env.file->words_by_seg.at(label.target_segment).at(1 + (label.offset / 4));
assert(word0.kind == LinkedWord::PLAIN_DATA);
assert(word1.kind == LinkedWord::PLAIN_DATA);
u64 value;
memcpy(&value, &word0.data, 4);
memcpy(((u8*)&value) + 4, &word1.data, 4);
// for some reason, GOAL would use a 64-bit constant for all bitfields, even if they are
// smaller. We should check that the higher bits are all zero.
int bits = as_bitfield->get_size_in_memory() * 8;
assert(bits <= 64);
if (bits < 64) {
assert((value >> bits) == 0);
// technically ub if bits == 64.
}
TypeSpec typespec(hint->second.type_name);
auto defs = decompile_bitfield_from_int(typespec, ts, value);
return pool.alloc_element<BitfieldStaticDefElement>(typespec, defs, pool);
}
}
}
if (load_kind == LoadVarOp::Kind::FLOAT && load_size == 4) {
assert((label.offset % 4) == 0);
const auto& words = env.file->words_by_seg.at(label.target_segment);
if ((int)words.size() > label.offset / 4) {
auto word = words.at(label.offset / 4);
assert(word.kind == LinkedWord::PLAIN_DATA);
float value;
memcpy(&value, &word.data, 4);
return pool.alloc_element<ConstantFloatElement>(value);
}
}
return nullptr;
}
Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
if (env.has_type_analysis()) {
IR2_RegOffset ro;
@@ -584,6 +660,12 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
return pool.alloc_single_element_form<DerefElement>(nullptr, source, rd.addr_of, tokens);
}
if (ro.offset == 0 && input_type.kind == TP_Type::Kind::LABEL_ADDR) {
// we no longer resolve label stuff here because sometimes we need expressions for this
return pool.alloc_single_element_form<LabelDerefElement>(nullptr, input_type.label_id(),
m_size, m_kind, ro.var);
}
if (ro.offset == 0 && (input_type.typespec() == TypeSpec("pointer") ||
input_type.kind == TP_Type::Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT)) {
std::string cast_type;
@@ -623,74 +705,9 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
}
if (m_src.is_identity() && m_src.get_arg(0).is_label()) {
// try to see if we're loading a constant
auto label = env.file->labels.at(m_src.get_arg(0).label());
auto label_name = label.name;
auto hint = env.label_types().find(label_name);
if (hint != env.label_types().end()) {
if (hint->second.is_const) {
if ((m_kind == Kind::FLOAT || m_kind == Kind::SIGNED) && m_size == 4 &&
hint->second.type_name == "float") {
assert((label.offset % 4) == 0);
auto word = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
assert(word.kind == LinkedWord::PLAIN_DATA);
float value;
memcpy(&value, &word.data, 4);
return pool.alloc_single_element_form<ConstantFloatElement>(nullptr, value);
} else if (hint->second.type_name == "uint64" && m_kind != Kind::FLOAT && m_size == 8) {
assert((label.offset % 8) == 0);
auto word0 = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
auto word1 = env.file->words_by_seg.at(label.target_segment).at(1 + (label.offset / 4));
assert(word0.kind == LinkedWord::PLAIN_DATA);
assert(word1.kind == LinkedWord::PLAIN_DATA);
u64 value;
memcpy(&value, &word0.data, 4);
memcpy(((u8*)&value) + 4, &word1.data, 4);
return pool.alloc_single_element_form<CastElement>(
nullptr, TypeSpec("uint"),
pool.alloc_single_element_form<SimpleAtomElement>(
nullptr, SimpleAtom::make_int_constant(value)));
}
// is it a constant bitfield?
auto& ts = env.dts->ts;
auto as_bitfield = dynamic_cast<BitFieldType*>(ts.lookup_type(hint->second.type_name));
if (as_bitfield && m_kind != Kind::FLOAT && m_size == 8) {
// get the data
assert((label.offset % 8) == 0);
auto word0 = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
auto word1 = env.file->words_by_seg.at(label.target_segment).at(1 + (label.offset / 4));
assert(word0.kind == LinkedWord::PLAIN_DATA);
assert(word1.kind == LinkedWord::PLAIN_DATA);
u64 value;
memcpy(&value, &word0.data, 4);
memcpy(((u8*)&value) + 4, &word1.data, 4);
// for some reason, GOAL would use a 64-bit constant for all bitfields, even if they are
// smaller. We should check that the higher bits are all zero.
int bits = as_bitfield->get_size_in_memory() * 8;
assert(bits <= 64);
if (bits < 64) {
assert((value >> bits) == 0);
// technically ub if bits == 64.
}
TypeSpec typespec(hint->second.type_name);
auto defs = decompile_bitfield_from_int(typespec, ts, value);
return pool.alloc_single_element_form<BitfieldStaticDefElement>(nullptr, typespec, defs,
pool);
}
}
}
if (m_kind == Kind::FLOAT && m_size == 4) {
assert((label.offset % 4) == 0);
const auto& words = env.file->words_by_seg.at(label.target_segment);
if ((int)words.size() > label.offset / 4) {
auto word = words.at(label.offset / 4);
assert(word.kind == LinkedWord::PLAIN_DATA);
float value;
memcpy(&value, &word.data, 4);
return pool.alloc_single_element_form<ConstantFloatElement>(nullptr, value);
}
auto label_load_element = make_label_load(m_src.get_arg(0).label(), env, pool, m_size, m_kind);
if (label_load_element) {
return pool.alloc_single_form(nullptr, label_load_element);
}
}
+1 -1
View File
@@ -137,7 +137,7 @@ TP_Type SimpleAtom::get_type(const TypeState& input,
}
// todo: should we take out this warning?
lg::warn("IR_StaticAddress does not know the type of {}", label.name);
return TP_Type::make_label_addr();
return TP_Type::make_label_addr(m_int);
}
case Kind::INVALID:
default:
+26
View File
@@ -2776,6 +2776,32 @@ void LabelElement::apply_form(const std::function<void(Form*)>&) {}
void LabelElement::collect_vars(RegAccessSet&, bool) const {}
void LabelElement::get_modified_regs(RegSet&) const {}
////////////////////////////////
// LabelDerefElement
///////////////////////////////
LabelDerefElement::LabelDerefElement(int lid,
int size,
LoadVarOp::Kind load_kind,
RegisterAccess var)
: m_lid(lid), m_size(size), m_load_kind(load_kind), m_var(var) {}
goos::Object LabelDerefElement::to_form_internal(const Env& env) const {
return pretty_print::build_list(fmt::format("label-deref {} :label {} :size {} :kind {}",
m_var.to_string(env), env.file->labels.at(m_lid).name,
m_size, load_kind_to_string(m_load_kind)));
}
void LabelDerefElement::apply(const std::function<void(FormElement*)>& f) {
f(this);
}
void LabelDerefElement::apply_form(const std::function<void(Form*)>&) {}
void LabelDerefElement::collect_vars(RegAccessSet& regs, bool) const {
regs.insert(m_var);
}
void LabelDerefElement::get_modified_regs(RegSet&) const {}
////////////////////////////////
// GetSymbolStringPointer
//////////////////////////////
+22
View File
@@ -632,6 +632,7 @@ class ReturnElement : public FormElement {
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
void get_modified_regs(RegSet& regs) const override;
std::optional<TypeSpec> return_type;
};
/*!
@@ -1549,6 +1550,27 @@ class LabelElement : public FormElement {
int m_lid = -1;
};
class LabelDerefElement : public FormElement {
public:
LabelDerefElement(int lid, int size, LoadVarOp::Kind load_kind, RegisterAccess var);
goos::Object to_form_internal(const Env& env) const override;
void apply(const std::function<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& f) override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
void update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects) override;
private:
int m_lid = -1;
int m_size = -1;
LoadVarOp::Kind m_load_kind = LoadVarOp::Kind::INVALID;
RegisterAccess m_var;
};
class GetSymbolStringPointer : public FormElement {
public:
GetSymbolStringPointer(Form* src);
+68 -21
View File
@@ -3549,6 +3549,24 @@ FormElement* ConditionElement::make_zero_check_generic(const Env& env,
return pool.alloc_element<GenericElement>(GenericOperator::make_compare(m_kind), source_forms);
}
FormElement* try_make_nonzero_logtest(Form* in, FormPool& pool) {
/*
(defmacro logtest? (a b)
"does a have any of the bits in b?"
`(nonzero? (logand ,a ,b))
)
*/
auto logand_matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::LOGAND),
{Matcher::any(0), Matcher::any(1)});
auto mr_logand = match(logand_matcher, in);
if (mr_logand.matched) {
return pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::LOGTEST), mr_logand.maps.forms.at(0),
mr_logand.maps.forms.at(1));
}
return nullptr;
}
FormElement* ConditionElement::make_nonzero_check_generic(const Env& env,
FormPool& pool,
const std::vector<Form*>& source_forms,
@@ -3578,19 +3596,9 @@ FormElement* ConditionElement::make_nonzero_check_generic(const Env& env,
std::vector<Form*>{mr.maps.forms.at(0), value_form});
}
/*
(defmacro logtest? (a b)
"does a have any of the bits in b?"
`(nonzero? (logand ,a ,b))
)
*/
auto logand_matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::LOGAND),
{Matcher::any(0), Matcher::any(1)});
auto mr_logand = match(logand_matcher, source_forms.at(0));
if (mr_logand.matched) {
return pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::LOGTEST), mr_logand.maps.forms.at(0),
mr_logand.maps.forms.at(1));
auto as_logand = try_make_nonzero_logtest(source_forms.at(0), pool);
if (as_logand) {
return as_logand;
}
return pool.alloc_element<GenericElement>(GenericOperator::make_compare(m_kind), source_forms);
@@ -3967,7 +3975,8 @@ void ReturnElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
return_code->push_back(new_entries.back());
if (var) {
const auto& func_type = env.func->type.last_arg();
if (!env.dts->ts.tc(func_type, env.get_variable_type(*var, false))) {
return_type = env.get_variable_type(*var, false);
if (func_type != return_type) {
auto as_cast = return_code->try_as_element<CastElement>();
if (as_cast) {
return_code->clear();
@@ -4643,13 +4652,25 @@ void ConditionalMoveFalseElement::push_to_stack(const Env& env, FormPool& pool,
stack.push_form_element(this, true);
return;
}
stack.push_value_to_reg(dest,
pool.alloc_single_element_form<GenericElement>(
nullptr,
GenericOperator::make_compare(on_zero ? IR2_Condition::Kind::NONZERO
: IR2_Condition::Kind::ZERO),
std::vector<Form*>{popped.at(1)}),
true, TypeSpec("symbol"));
Form* val = nullptr;
if (!val && on_zero) {
auto as_logtest = try_make_nonzero_logtest(popped.at(1), pool);
if (as_logtest) {
val = pool.alloc_single_form(nullptr, as_logtest);
}
}
if (!val) {
val = pool.alloc_single_element_form<GenericElement>(
nullptr,
GenericOperator::make_compare(on_zero ? IR2_Condition::Kind::NONZERO
: IR2_Condition::Kind::ZERO),
std::vector<Form*>{popped.at(1)});
}
stack.push_value_to_reg(dest, val, true, TypeSpec("symbol"));
}
///////////////////////////
@@ -4951,6 +4972,32 @@ void DefstateElement::update_from_stack(const Env&,
result->push_back(this);
}
void LabelDerefElement::update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects) {
mark_popped();
auto label_var = pop_to_forms({m_var}, env, pool, stack, allow_side_effects).at(0);
auto atom = form_as_atom(label_var);
if (!atom || !atom->is_label()) {
throw std::runtime_error(fmt::format("LabelDerefElement didn't get a label, got {} instead",
label_var->to_string(env)));
}
if (atom->label() != m_lid) {
throw std::runtime_error(
fmt::format("Label ID error in LabelDerefElement: {} vs {}", atom->label(), m_lid));
}
auto as_label = make_label_load(m_lid, env, pool, m_size, m_load_kind);
if (!as_label) {
throw std::runtime_error(
fmt::format("Unable to figure out label load for {}\n", env.file->labels.at(m_lid).name));
}
result->push_back(as_label);
}
void LabelElement::push_to_stack(const Env&, FormPool&, FormStack& stack) {
mark_popped();
stack.push_form_element(this, true);
+5 -3
View File
@@ -158,9 +158,11 @@ class ObjectFileDB {
void for_each_function_in_seg(int seg, Func f) {
for_each_obj([&](ObjectFileData& data) {
int fn = 0;
for (size_t j = data.linked_data.functions_by_seg.at(seg).size(); j-- > 0;) {
f(data.linked_data.functions_by_seg.at(seg).at(j), data);
fn++;
if (data.linked_data.segments == 3) {
for (size_t j = data.linked_data.functions_by_seg.at(seg).size(); j-- > 0;) {
f(data.linked_data.functions_by_seg.at(seg).at(j), data);
fn++;
}
}
});
}
+23
View File
@@ -84,8 +84,31 @@ bool convert_to_expressions(
}
}
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) {
+1 -2
View File
@@ -640,7 +640,6 @@
:bitfield #t
:type uint32
(display-marks 0)
(bit0 0) ;; TODO - nav-enemy::45
(bit1 1)
(bit2 2)
(bit3 3) ;; TODO - nav-enemy::45
@@ -14752,7 +14751,7 @@
(define-extern show-iop-info (function dma-buffer int))
(define-extern show-iop-memory (function dma-buffer int))
(define-extern make-sqrt-table (function int))
(define-extern flava-lookup (function symbol int integer))
(define-extern flava-lookup (function symbol int int))
;; - Symbols
+1 -1
View File
@@ -70,7 +70,7 @@ std::string TP_Type::print() const {
case Kind::PCPYUD_BITFIELD_AND:
return fmt::format("<pcpyud-and {}>", m_ts.print());
case Kind::LABEL_ADDR:
return "<label-addr>";
return fmt::format("<label-{}>", m_int);
case Kind::ENTER_STATE_FUNCTION:
return "<enter-state-func>";
case Kind::INVALID:
+7 -1
View File
@@ -262,9 +262,10 @@ class TP_Type {
return result;
}
static TP_Type make_label_addr() {
static TP_Type make_label_addr(int label_id) {
TP_Type result;
result.kind = Kind::LABEL_ADDR;
result.m_int = label_id;
return result;
}
@@ -342,6 +343,11 @@ class TP_Type {
return m_flipped_order;
}
int label_id() const {
assert(kind == Kind::LABEL_ADDR);
return m_int;
}
private:
TypeSpec m_ts;
TypeSpec m_method_from_type;
+26 -7
View File
@@ -1255,11 +1255,22 @@ std::vector<std::string> decompile_bitfield_enum_from_int(const TypeSpec& type,
return type_info->entries().at(a) < type_info->entries().at(b);
});
for (auto& field_name : bit_sorted_names) {
u64 mask = ((u64)1) << type_info->entries().at(field_name);
for (auto& kv : type_info->entries()) {
u64 mask = ((u64)1) << kv.second;
if (value & mask) {
reconstructed |= mask;
result.push_back(field_name);
result.push_back(kv.first);
}
}
int bit_count = 0;
{
u64 x = value;
while (x) {
if (x & 1) {
bit_count++;
}
x >>= 1;
}
}
@@ -1270,10 +1281,18 @@ std::vector<std::string> decompile_bitfield_enum_from_int(const TypeSpec& type,
type.print(), value, reconstructed));
}
// unordered map will give us these fields in a weird order, let's order them explicitly.
std::sort(result.begin(), result.end(), [&](const std::string& a, const std::string& b) {
return type_info->entries().at(a) < type_info->entries().at(b);
});
if (bit_count == (int)result.size()) {
// unordered map will give us these fields in a weird order, let's order them explicitly.
// because we have exactly one name per bit, we can just order them in bit order.
std::sort(result.begin(), result.end(), [&](const std::string& a, const std::string& b) {
return type_info->entries().at(a) < type_info->entries().at(b);
});
} else {
// we have multiple. Just sort alphabetically and complain.
lg::warn("Enum type {} has multiple entries with the same value.", type_info->get_name());
std::sort(result.begin(), result.end());
}
return result;
}