[Decompiler] WIP: Stack Spills (#382)

* set up types

* cleaned up type analysis and got things working through atomic ops

* expression working, need types

* improved types and names

* getting close

* finish up dma-disasm

* fix
This commit is contained in:
water111
2021-04-25 14:48:54 -04:00
committed by GitHub
parent 54ccc9db97
commit 2002db359a
43 changed files with 3187 additions and 99 deletions
+3 -1
View File
@@ -156,7 +156,9 @@ void TextDb::inherit_info(const Object& parent, const Object& child) {
while (!children.empty()) {
auto top = children.back();
children.pop_back();
map[top->heap_obj] = parent_kv->second;
if (map.find(top->heap_obj) == map.end()) {
map[top->heap_obj] = parent_kv->second;
}
if (top->as_pair()->car.is_pair()) {
children.push_back(&top->as_pair()->car);
}
+31
View File
@@ -437,6 +437,37 @@ MethodInfo TypeSystem::lookup_method(const std::string& type_name,
throw std::runtime_error("lookup_method failed");
}
bool TypeSystem::try_lookup_method(const std::string& type_name,
const std::string& method_name,
MethodInfo* info) const {
auto kv = m_types.find(type_name);
if (kv == m_types.end()) {
return false;
}
auto* iter_type = kv->second.get();
// look up the method
while (true) {
if (method_name == "new") {
if (iter_type->get_my_new_method(info)) {
return true;
}
} else {
if (iter_type->get_my_method(method_name, info)) {
return true;
}
}
if (iter_type->has_parent()) {
iter_type = lookup_type(iter_type->get_parent());
} else {
// couldn't find method.
break;
}
}
return false;
}
/*!
* Like lookup_method, but won't throw or print an error when things go wrong.
*/
+3
View File
@@ -128,6 +128,9 @@ class TypeSystem {
MethodInfo add_new_method(Type* type, const TypeSpec& ts);
MethodInfo lookup_method(const std::string& type_name, const std::string& method_name) const;
MethodInfo lookup_method(const std::string& type_name, int method_id) const;
bool try_lookup_method(const std::string& type_name,
const std::string& method_name,
MethodInfo* info) const;
bool try_lookup_method(const std::string& type_name, int method_id, MethodInfo* info) const;
MethodInfo lookup_new_method(const std::string& type_name) const;
void assert_method_id(const std::string& type_name, const std::string& method_name, int id);
+1
View File
@@ -10,6 +10,7 @@ add_library(
analysis/inline_asm_rewrite.cpp
analysis/insert_lets.cpp
analysis/reg_usage.cpp
analysis/stack_spill.cpp
analysis/type_analysis.cpp
analysis/variable_naming.cpp
-4
View File
@@ -6,9 +6,6 @@
* Can print itself (within the context of a LinkedObjectFile).
*/
#ifndef NEXT_INSTRUCTION_H
#define NEXT_INSTRUCTION_H
#include <vector>
#include "OpcodeInfo.h"
#include "Register.h"
@@ -112,4 +109,3 @@ class Instruction {
int cop2_dest_mask_intel() const;
};
} // namespace decompiler
#endif // NEXT_INSTRUCTION_H
+90
View File
@@ -1639,4 +1639,94 @@ void FunctionEndOp::collect_vars(RegAccessSet& vars) const {
vars.insert(m_return_reg);
}
}
/////////////////////////////
// StackSpillStoreOp
/////////////////////////////
StackSpillStoreOp::StackSpillStoreOp(RegisterAccess value, int size, int offset, int my_idx)
: AtomicOp(my_idx), m_value(value), m_size(size), m_offset(offset) {
assert(m_value.mode() == AccessMode::READ);
}
goos::Object StackSpillStoreOp::to_form(const std::vector<DecompilerLabel>&, const Env& env) const {
return pretty_print::build_list(
fmt::format("stack-store {} :offset {} :sz {}", m_value.to_string(env), m_offset, m_size));
}
bool StackSpillStoreOp::operator==(const AtomicOp& other) const {
if (typeid(StackSpillStoreOp) != typeid(other)) {
return false;
}
auto po = dynamic_cast<const StackSpillStoreOp*>(&other);
assert(po);
return m_size == po->m_size && m_value == po->m_value && m_offset == po->m_offset;
}
bool StackSpillStoreOp::is_sequence_point() const {
return true; // this might not be totally true, but it seems kind of scary to allow it to
// reorder.
}
void StackSpillStoreOp::update_register_info() {
m_read_regs.push_back(m_value.reg());
}
void StackSpillStoreOp::collect_vars(RegAccessSet& vars) const {
vars.insert(m_value);
}
RegisterAccess StackSpillStoreOp::get_set_destination() const {
throw std::runtime_error("StackSpillStoreOp cannot be treated as a set! operation");
}
/////////////////////////////
// StackSpillLoadOp
/////////////////////////////
StackSpillLoadOp::StackSpillLoadOp(RegisterAccess dst,
int size,
int offset,
bool is_signed,
int my_idx)
: AtomicOp(my_idx), m_dst(dst), m_size(size), m_offset(offset), m_is_signed(is_signed) {
assert(m_dst.mode() == AccessMode::WRITE);
}
goos::Object StackSpillLoadOp::to_form(const std::vector<DecompilerLabel>&, const Env& env) const {
return pretty_print::build_list(fmt::format("stack-load {} :offset {} :sz {} :sext #{}",
m_dst.to_string(env), m_offset, m_size,
m_is_signed ? 't' : 'f'));
}
bool StackSpillLoadOp::operator==(const AtomicOp& other) const {
if (typeid(StackSpillStoreOp) != typeid(other)) {
return false;
}
auto po = dynamic_cast<const StackSpillLoadOp*>(&other);
assert(po);
return m_size == po->m_size && m_dst == po->m_dst && m_offset == po->m_offset &&
m_is_signed == po->m_is_signed;
}
bool StackSpillLoadOp::is_sequence_point() const {
return true; // this might not be totally true, but it seems kind of scary to allow it to
// reorder.
}
void StackSpillLoadOp::update_register_info() {
m_write_regs.push_back(m_dst.reg());
}
void StackSpillLoadOp::collect_vars(RegAccessSet& vars) const {
vars.insert(m_dst);
}
RegisterAccess StackSpillLoadOp::get_set_destination() const {
// todo!
throw std::runtime_error("StackSpillLoadOp cannot be treated as a set! operation");
}
} // namespace decompiler
+48 -2
View File
@@ -697,8 +697,7 @@ struct IR2_RegOffset {
class FunctionEndOp : public AtomicOp {
public:
explicit FunctionEndOp(int my_idx);
virtual goos::Object to_form(const std::vector<DecompilerLabel>& labels,
const Env& env) const override;
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env& env) const override;
bool operator==(const AtomicOp& other) const override;
bool is_sequence_point() const override;
RegisterAccess get_set_destination() const override;
@@ -719,5 +718,52 @@ class FunctionEndOp : public AtomicOp {
RegisterAccess m_return_reg;
};
/*!
* An operation to store a variable from the stack.
*/
class StackSpillStoreOp : public AtomicOp {
public:
StackSpillStoreOp(RegisterAccess value, int size, int offset, int my_idx);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env& env) const override;
bool operator==(const AtomicOp& other) const override;
bool is_sequence_point() const override;
FormElement* get_as_form(FormPool& pool, const Env& env) const override;
RegisterAccess get_set_destination() const override;
void update_register_info() override;
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
void collect_vars(RegAccessSet& vars) const override;
private:
RegisterAccess m_value;
int m_size;
int m_offset;
};
/*!
* An operation to load a variable from the stack.
*/
class StackSpillLoadOp : public AtomicOp {
public:
StackSpillLoadOp(RegisterAccess dst, int size, int offset, bool is_signed, int my_idx);
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env& env) const override;
bool operator==(const AtomicOp& other) const override;
bool is_sequence_point() const override;
FormElement* get_as_form(FormPool& pool, const Env& env) const override;
RegisterAccess get_set_destination() const override;
void update_register_info() override;
TypeState propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem& dts) override;
void collect_vars(RegAccessSet& vars) const override;
private:
RegisterAccess m_dst;
int m_size;
int m_offset;
bool m_is_signed;
};
bool get_as_reg_offset(const SimpleExpression& expr, IR2_RegOffset* out);
} // namespace decompiler
+25
View File
@@ -723,4 +723,29 @@ FormElement* FunctionEndOp::get_as_form(FormPool& pool, const Env&) const {
FormElement* AsmBranchOp::get_as_form(FormPool& pool, const Env&) const {
return pool.alloc_element<AtomicOpElement>(this);
}
FormElement* StackSpillLoadOp::get_as_form(FormPool& pool, const Env& env) const {
TypeSpec type("object");
auto kv = env.stack_slot_entries.find(m_offset);
if (kv != env.stack_slot_entries.end()) {
type = kv->second.typespec;
}
return pool.alloc_element<SetVarElement>(m_dst,
pool.alloc_single_element_form<StackSpillValueElement>(
nullptr, m_size, m_offset, m_is_signed),
true, type);
}
FormElement* StackSpillStoreOp::get_as_form(FormPool& pool, const Env& env) const {
auto& slot_type = env.stack_slot_entries.at(m_offset).typespec;
auto src_type = env.get_types_before_op(m_my_idx).get(m_value.reg()).typespec();
std::optional<TypeSpec> cast_type;
if (!env.dts->ts.tc(slot_type, src_type)) {
// we fail the typecheck for a normal set!, so add a cast.
cast_type = slot_type;
}
return pool.alloc_element<StackSpillStoreElement>(m_value, m_size, m_offset, cast_type);
}
} // namespace decompiler
+64 -1
View File
@@ -3,6 +3,7 @@
#include "common/log/log.h"
#include "AtomicOp.h"
#include "decompiler/util/DecompilerTypeSystem.h"
#include "decompiler/IR2/bitfields.h"
namespace decompiler {
@@ -284,7 +285,13 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
if (m_args[1].is_int() && is_int_or_uint(dts, arg0_type)) {
assert(m_args[1].get_int() >= 0);
assert(m_args[1].get_int() < 64);
return TP_Type::make_from_product(1ull << m_args[1].get_int(), is_signed(dts, arg0_type));
// this could be a bitfield access or a multiply.
// we pick bitfield access if the parent is a bitfield.
if (dynamic_cast<BitFieldType*>(dts.ts.lookup_type(arg0_type.typespec()))) {
return TP_Type::make_from_left_shift_bitfield(arg0_type.typespec(), m_args[1].get_int());
} else {
return TP_Type::make_from_product(1ull << m_args[1].get_int(), is_signed(dts, arg0_type));
}
}
if (m_args[1].is_int() && dts.ts.tc(TypeSpec("pointer"), arg0_type.typespec())) {
@@ -293,6 +300,27 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
}
break;
case Kind::RIGHT_SHIFT_ARITH:
case Kind::RIGHT_SHIFT_LOGIC: {
bool is_unsigned = m_kind == Kind::RIGHT_SHIFT_LOGIC;
if (arg0_type.kind == TP_Type::Kind::LEFT_SHIFTED_BITFIELD && m_args[1].is_int()) {
// second op in left/right shift combo
int end_bit = 64 - arg0_type.get_left_shift();
int size = 64 - m_args[1].get_int();
int start_bit = end_bit - size;
if (start_bit < 0) {
throw std::runtime_error("Bad bitfield start bit");
}
auto type = dts.ts.lookup_type(arg0_type.get_bitfield_type());
auto as_bitfield = dynamic_cast<BitFieldType*>(type);
assert(as_bitfield);
auto field = find_field(dts.ts, as_bitfield, start_bit, size, is_unsigned);
return TP_Type::make_from_ts(field.type());
}
} break;
case Kind::MUL_SIGNED: {
if (arg0_type.is_integer_constant() && is_int_or_uint(dts, arg1_type)) {
return TP_Type::make_from_product(arg0_type.get_integer_constant(),
@@ -1023,4 +1051,39 @@ TypeState AsmBranchOp::propagate_types_internal(const TypeState& input,
return output;
}
TypeState StackSpillLoadOp::propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem&) {
// stack slot load
auto info = env.stack_spills().lookup(m_offset);
if (info.size != m_size) {
throw std::runtime_error(fmt::format(
"Stack slot load mismatch: defined as size {}, got size {}\n", info.size, m_size));
}
if (info.is_signed != m_is_signed) {
throw std::runtime_error("Stack slot signed mismatch");
}
auto& loaded_type = input.get_slot(m_offset);
auto result = input;
result.get(m_dst.reg()) = loaded_type;
return result;
}
TypeState StackSpillStoreOp::propagate_types_internal(const TypeState& input,
const Env& env,
DecompilerTypeSystem&) {
auto info = env.stack_spills().lookup(m_offset);
if (info.size != m_size) {
throw std::runtime_error(fmt::format(
"Stack slot load mismatch: defined as size {}, got size {}\n", info.size, m_size));
}
auto& stored_type = input.get(m_value.reg());
auto result = input;
result.spill_slots[m_offset] = stored_type;
return result;
}
} // namespace decompiler
+53 -17
View File
@@ -91,12 +91,17 @@ const std::string& Env::remapped_name(const std::string& name) const {
}
goos::Object Env::get_variable_name_with_cast(const RegisterAccess& access) const {
return get_variable_name_with_cast(access.reg(), access.idx(), access.mode());
auto result = get_variable_and_cast(access);
if (result.cast) {
return pretty_print::build_list("the-as", result.cast->print(), result.name);
} else {
return pretty_print::to_symbol(result.name);
}
}
goos::Object Env::get_variable_name_with_cast(Register reg, int atomic_idx, AccessMode mode) const {
if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR) {
auto& var_info = m_var_names.lookup(reg, atomic_idx, mode);
VariableWithCast Env::get_variable_and_cast(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());
// this is a bit of a confusing process. The first step is to grab the auto-generated name:
std::string original_name = var_info.name();
auto lookup_name = original_name;
@@ -118,27 +123,32 @@ goos::Object Env::get_variable_name_with_cast(Register reg, int atomic_idx, Acce
}
// next, we insert type casts that make enforce the user override.
auto type_kv = m_typecasts.find(atomic_idx);
auto type_kv = m_typecasts.find(access.idx());
if (type_kv != m_typecasts.end()) {
for (auto& x : type_kv->second) {
if (x.reg == reg) {
if (x.reg == access.reg()) {
// let's make sure the above claim is true
TypeSpec type_in_reg;
if (has_type_analysis() && mode == AccessMode::READ) {
type_in_reg = get_types_for_op_mode(atomic_idx, AccessMode::READ).get(reg).typespec();
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 {} ({})",
reg.to_charp(), atomic_idx, lookup_name, x.type_name, type_in_reg.print(),
type_in_reg.print());
access.reg().to_charp(), access.idx(), lookup_name, x.type_name,
type_in_reg.print(), type_in_reg.print());
assert(false);
}
}
if (type_of_var != type_in_reg) {
// TODO - use the when possible?
return pretty_print::build_list("the-as", x.type_name, lookup_name);
VariableWithCast result;
result.cast = TypeSpec(x.type_name);
result.name = lookup_name;
return result;
}
}
}
@@ -146,8 +156,9 @@ goos::Object Env::get_variable_name_with_cast(Register reg, int atomic_idx, Acce
// type analysis stuff runs before variable types, so we insert casts that account
// for the changing types due to the lca(uses) that is used to generate variable types.
auto type_of_reg = get_types_for_op_mode(atomic_idx, mode).get(reg).typespec();
if (mode == AccessMode::READ) {
auto type_of_reg =
get_types_for_op_mode(access.idx(), access.mode()).get(access.reg()).typespec();
if (access.mode() == AccessMode::READ) {
// note - this may be stricter than needed. but that's ok.
if (type_of_var != type_of_reg) {
@@ -156,7 +167,10 @@ goos::Object Env::get_variable_name_with_cast(Register reg, int atomic_idx, Acce
// {}\n ",
// lookup_name, reg.to_charp(), atomic_idx, type_of_reg.print(),
// var_info.type.typespec().print(), type_of_var.print());
return pretty_print::build_list("the-as", type_of_reg.print(), lookup_name);
VariableWithCast result;
result.cast = type_of_reg;
result.name = lookup_name;
return result;
}
} else {
// if we're setting a variable, we are a little less strict.
@@ -170,12 +184,21 @@ goos::Object Env::get_variable_name_with_cast(Register reg, int atomic_idx, Acce
}
}
return pretty_print::to_symbol(lookup_name);
VariableWithCast result;
result.name = lookup_name;
return result;
} else {
return pretty_print::to_symbol(reg.to_charp());
VariableWithCast result;
result.name = access.reg().to_charp();
return result;
}
}
goos::Object Env::get_variable_name_with_cast(Register reg, int atomic_idx, AccessMode mode) const {
return get_variable_name_with_cast(RegisterAccess(mode, reg, atomic_idx, true));
}
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());
@@ -420,9 +443,22 @@ goos::Object Env::local_var_type_list(const Form* top_level_form,
}
count++;
elts.push_back(pretty_print::build_list(lookup_name, x.type.typespec().print()));
}
// sort in increasing offset.
// it looks like this is the order the GOAL compiler itself used.
std::vector<StackSpillEntry> spills;
for (auto& x : stack_slot_entries) {
spills.push_back(x.second);
}
std::sort(spills.begin(), spills.end(),
[](const StackSpillEntry& a, const StackSpillEntry& b) { return a.offset < b.offset; });
for (auto& x : spills) {
elts.push_back(pretty_print::build_list(x.name(), x.typespec.print()));
count++;
}
if (count_out) {
*count_out = count;
}
+42 -5
View File
@@ -5,6 +5,7 @@
#include <cassert>
#include <common/goos/Object.h>
#include "decompiler/util/TP_Type.h"
#include "decompiler/util/StackSpillMap.h"
#include "decompiler/Disasm/Register.h"
#include "decompiler/IR2/IR2_common.h"
#include "decompiler/analysis/reg_usage.h"
@@ -16,12 +17,31 @@ class Form;
class DecompilerTypeSystem;
struct FunctionAtomicOps;
struct VariableWithCast {
std::string name;
std::optional<TypeSpec> cast;
};
struct StackVarEntry {
StackVariableHint hint;
TypeSpec ref_type; // the actual type of the address.
int size = -1;
};
struct StackSpillEntry {
TP_Type tp_type;
TypeSpec typespec;
int offset;
std::optional<std::string> name_override;
std::string name() const {
if (name_override) {
return *name_override;
} else {
return fmt::format("sv-{}", offset);
}
}
};
/*!
* An "environment" for a single function.
* This contains data for an entire function, like which registers are live when, the types of
@@ -34,17 +54,16 @@ class Env {
bool has_local_vars() const { return m_has_local_vars; }
bool has_type_analysis() const { return m_has_types; }
bool has_reg_use() const { return m_has_reg_use; }
const RegUsageInfo& reg_use() const {
assert(m_has_reg_use);
return m_reg_use;
}
void set_reg_use(const RegUsageInfo& info) {
m_reg_use = info;
m_has_reg_use = true;
}
const RegUsageInfo& reg_use() const {
assert(m_has_reg_use);
return m_reg_use;
}
RegUsageInfo& reg_use() {
assert(m_has_reg_use);
return m_reg_use;
@@ -54,6 +73,7 @@ class Env {
goos::Object get_variable_name_with_cast(Register reg, int atomic_idx, AccessMode mode) const;
goos::Object get_variable_name_with_cast(const RegisterAccess& access) const;
std::string get_variable_name(const RegisterAccess& access) const;
VariableWithCast get_variable_and_cast(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;
@@ -158,9 +178,24 @@ class Env {
void set_retype_map(const std::unordered_map<std::string, TypeSpec>& map) { m_var_retype = map; }
void set_stack_spills(const StackSpillMap& map) { m_stack_spill_map = map; }
const StackSpillMap& stack_spills() const { return m_stack_spill_map; }
// todo - remove these hacks at some point.
LinkedObjectFile* file = nullptr;
DecompilerTypeSystem* dts = nullptr;
std::unordered_map<int, StackSpillEntry> stack_slot_entries;
std::string get_spill_slot_var_name(int offset) const {
auto kv = stack_slot_entries.find(offset);
if (kv == stack_slot_entries.end()) {
return fmt::format("sv-{}", offset);
} else {
return kv->second.name();
}
}
const std::unordered_map<std::string, std::string>& var_remap_map() const { return m_var_remap; }
private:
RegisterAccess m_end_var;
@@ -186,5 +221,7 @@ class Env {
std::unordered_set<std::string> m_vars_defined_in_let;
std::optional<TypeSpec> m_type_analysis_return_type;
StackSpillMap m_stack_spill_map;
};
} // namespace decompiler
+46
View File
@@ -2291,6 +2291,52 @@ void VectorFloatLoadStoreElement::collect_vf_regs(RegSet& regs) const {
regs.insert(m_vf_reg);
}
////////////////////////////////
// StackSpillStoreElement
////////////////////////////////
StackSpillStoreElement::StackSpillStoreElement(RegisterAccess value,
int size,
int stack_offset,
const std::optional<TypeSpec>& cast_type)
: m_value(value), m_size(size), m_stack_offset(stack_offset), m_cast_type(cast_type) {}
goos::Object StackSpillStoreElement::to_form_internal(const Env& env) const {
return pretty_print::build_list(
fmt::format("set! {}", env.get_spill_slot_var_name(m_stack_offset)), m_value.to_form(env));
}
void StackSpillStoreElement::apply(const std::function<void(FormElement*)>& f) {
f(this);
}
void StackSpillStoreElement::apply_form(const std::function<void(Form*)>&) {}
void StackSpillStoreElement::collect_vars(RegAccessSet& vars, bool) const {
vars.insert(m_value);
}
void StackSpillStoreElement::get_modified_regs(RegSet&) const {}
////////////////////////////////
// StackSpillValueElement
////////////////////////////////
StackSpillValueElement::StackSpillValueElement(int size, int stack_offset, bool is_signed)
: m_size(size), m_stack_offset(stack_offset), m_is_signed(is_signed) {}
goos::Object StackSpillValueElement::to_form_internal(const Env& env) const {
return pretty_print::to_symbol(env.get_spill_slot_var_name(m_stack_offset));
}
void StackSpillValueElement::apply(const std::function<void(FormElement*)>& f) {
f(this);
}
void StackSpillValueElement::apply_form(const std::function<void(Form*)>&) {}
void StackSpillValueElement::collect_vars(RegAccessSet&, bool) const {}
void StackSpillValueElement::get_modified_regs(RegSet&) const {}
////////////////////////////////
// Utilities
////////////////////////////////
+42
View File
@@ -1316,6 +1316,48 @@ class VectorFloatLoadStoreElement : public FormElement {
bool m_is_load = false;
};
class StackSpillStoreElement : public FormElement {
public:
StackSpillStoreElement(RegisterAccess value,
int size,
int stack_offset,
const std::optional<TypeSpec>& cast_type);
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 push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
const std::optional<TypeSpec>& cast_type() const { return m_cast_type; }
private:
RegisterAccess m_value;
int m_size = -1;
int m_stack_offset = -1;
std::optional<TypeSpec> m_cast_type;
};
// the value from a stack load.
class StackSpillValueElement : public FormElement {
public:
StackSpillValueElement(int size, int stack_offset, bool is_signed);
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_size = -1;
int m_stack_offset = -1;
bool m_is_signed = false;
};
/*!
* A Form is a wrapper around one or more FormElements.
* This is done for two reasons:
+45 -14
View File
@@ -308,8 +308,8 @@ bool is_uint_type(const Env& env, int my_idx, RegisterAccess var) {
bool is_ptr_or_child(const Env& env, int my_idx, RegisterAccess var, bool) {
// Now that decompiler types are synced up properly, we don't want this.
// auto type = as_var ? env.get_variable_type(var, true).base_type()
// : env.get_types_before_op(my_idx).get(var.reg()).typespec().base_type();
// auto type = as_var ? env.get_variable_type(var, true).base_type()
// : env.get_types_before_op(my_idx).get(var.reg()).typespec().base_type();
auto type = env.get_types_before_op(my_idx).get(var.reg()).typespec().base_type();
return type == "pointer";
}
@@ -603,7 +603,7 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
args.push_back(pool.alloc_single_element_form<SimpleAtomElement>(nullptr, m_expr.get_arg(1)));
}
bool arg0_ptr = is_ptr_or_child(env, m_my_idx, m_expr.get_arg(0).var(), is_var(args.at(0)));
bool arg0_ptr = is_ptr_or_child(env, m_my_idx, m_expr.get_arg(0).var(), true);
// Look for getting an address inside of an object.
// (+ <integer 108 + int> process). array style access with a stride of 1.
@@ -943,7 +943,7 @@ void SimpleExpressionElement::update_from_stack_logor_or_logand(const Env& env,
}
BitfieldManip step(manip_kind, m_expr.get_arg(1).get_int());
auto other = read_elt->push_step(step, env.dts->ts, pool);
auto other = read_elt->push_step(step, env.dts->ts, pool, env);
if (other) {
result->push_back(other);
} else {
@@ -983,7 +983,7 @@ void SimpleExpressionElement::update_from_stack_logor_or_logand(const Env& env,
assert(false);
}
BitfieldManip step(manip_kind, arg1_atom->get_int());
auto other = read_elt->push_step(step, env.dts->ts, pool);
auto other = read_elt->push_step(step, env.dts->ts, pool, env);
assert(!other); // shouldn't be complete.
result->push_back(read_elt);
return;
@@ -997,7 +997,7 @@ void SimpleExpressionElement::update_from_stack_logor_or_logand(const Env& env,
assert(false);
}
auto step = BitfieldManip::from_form(manip_kind, stripped_arg1);
auto other = read_elt->push_step(step, env.dts->ts, pool);
auto other = read_elt->push_step(step, env.dts->ts, pool, env);
if (other) {
result->push_back(other);
} else {
@@ -1066,7 +1066,7 @@ void SimpleExpressionElement::update_from_stack_left_shift(const Env& env,
auto base = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0);
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
BitfieldManip step(BitfieldManip::Kind::LEFT_SHIFT, m_expr.get_arg(1).get_int());
auto other = read_elt->push_step(step, env.dts->ts, pool);
auto other = read_elt->push_step(step, env.dts->ts, pool, env);
assert(!other); // shouldn't be complete.
result->push_back(read_elt);
} else {
@@ -1087,7 +1087,7 @@ void SimpleExpressionElement::update_from_stack_right_shift_logic(const Env& env
auto base = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0);
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_LOGICAL, m_expr.get_arg(1).get_int());
auto other = read_elt->push_step(step, env.dts->ts, pool);
auto other = read_elt->push_step(step, env.dts->ts, pool, env);
assert(other); // should be a high field.
result->push_back(other);
} else {
@@ -1100,7 +1100,7 @@ void SimpleExpressionElement::update_from_stack_right_shift_logic(const Env& env
if (as_bitfield_access) {
BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_LOGICAL, m_expr.get_arg(1).get_int());
auto next = as_bitfield_access->push_step(step, env.dts->ts, pool);
auto next = as_bitfield_access->push_step(step, env.dts->ts, pool, env);
if (next) {
result->push_back(next);
} else {
@@ -1139,7 +1139,7 @@ void SimpleExpressionElement::update_from_stack_right_shift_arith(const Env& env
auto base = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0);
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_ARITH, m_expr.get_arg(1).get_int());
auto other = read_elt->push_step(step, env.dts->ts, pool);
auto other = read_elt->push_step(step, env.dts->ts, pool, env);
assert(other); // should be a high field.
result->push_back(other);
} else {
@@ -2068,6 +2068,12 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac
form->push_back(stack.pop_back(pool));
} else {
FormStack temp_stack(false);
if (form == entry.body) {
auto as_setvar = dynamic_cast<SetVarElement*>(form->elts().back());
if (as_setvar && as_setvar->is_dead_set() && as_setvar->src_type() != TypeSpec("float")) {
rewrite_as_set = false;
}
}
for (auto& elt : form->elts()) {
elt->push_to_stack(env, pool, temp_stack);
}
@@ -2119,8 +2125,9 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac
source_types.push_back(last_in_body->src_type());
}
last_var = last_in_body->dst();
} // For now, I am fine with letting this fail. For example, if the set is eliminated by a
// coloring move. If this makes really ugly code later on, we could use this to disable
}
// For now, I am fine with letting this fail. For example, if the set is eliminated by a
// coloring move. If this makes really ugly code later on, we could use this to disable
// write as set.
}
@@ -2320,7 +2327,7 @@ FormElement* ConditionElement::make_nonzero_check_generic(const Env& env,
dynamic_cast<BitfieldAccessElement*>(source_forms.at(0)->try_as_single_element());
if (as_bitfield_op) {
bitfield_compare = as_bitfield_op->push_step(
BitfieldManip(BitfieldManip::Kind::NONZERO_COMPARE, 0), env.dts->ts, pool);
BitfieldManip(BitfieldManip::Kind::NONZERO_COMPARE, 0), env.dts->ts, pool, env);
}
if (bitfield_compare) {
@@ -2698,7 +2705,7 @@ void push_asm_srl_to_stack(const AsmOp* op,
auto base = pop_to_forms({*var}, env, pool, stack, true).at(0);
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_LOGICAL_32BIT, integer);
auto other = read_elt->push_step(step, env.dts->ts, pool);
auto other = read_elt->push_step(step, env.dts->ts, pool, env);
assert(other); // should be a high field.
stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, other), true,
env.get_variable_type(*dst, true));
@@ -2740,6 +2747,7 @@ void AtomicOpElement::push_to_stack(const Env& env, FormPool& pool, FormStack& s
}
return;
}
throw std::runtime_error("Can't push atomic op to stack: " + m_op->to_string(env));
}
@@ -3079,6 +3087,20 @@ void ConditionalMoveFalseElement::push_to_stack(const Env& env, FormPool& pool,
true, TypeSpec("symbol"));
}
///////////////////////////
// StackSpillStoreElement
///////////////////////////
void StackSpillStoreElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) {
mark_popped();
auto src = pop_to_forms({m_value}, env, pool, stack, true).at(0);
auto dst = pool.alloc_single_element_form<ConstantTokenElement>(
nullptr, env.get_spill_slot_var_name(m_stack_offset));
if (m_cast_type) {
src = cast_form(src, *m_cast_type, pool, env);
}
stack.push_form_element(pool.alloc_element<SetFormFormElement>(dst, src), true);
}
void VectorFloatLoadStoreElement::push_to_stack(const Env&, FormPool&, FormStack& stack) {
mark_popped();
stack.push_form_element(this, true);
@@ -3147,4 +3169,13 @@ void StackVarDefElement::update_from_stack(const Env&,
result->push_back(this);
}
void StackSpillValueElement::update_from_stack(const Env&,
FormPool&,
FormStack&,
std::vector<FormElement*>* result,
bool) {
mark_popped();
result->push_back(this);
}
} // namespace decompiler
+3 -4
View File
@@ -143,7 +143,6 @@ void BitfieldAccessElement::get_modified_regs(RegSet& regs) const {
m_base->get_modified_regs(regs);
}
namespace {
const BitField& find_field(const TypeSystem& ts,
const BitFieldType* type,
int start_bit,
@@ -172,6 +171,7 @@ const BitField& find_field(const TypeSystem& ts,
!looking_for_unsigned, type->get_name()));
}
namespace {
std::optional<BitField> find_field_from_mask(const TypeSystem& ts,
const BitFieldType* type,
uint64_t mask) {
@@ -227,7 +227,8 @@ std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
*/
FormElement* BitfieldAccessElement::push_step(const BitfieldManip step,
const TypeSystem& ts,
FormPool& pool) {
FormPool& pool,
const Env& env) {
if (m_steps.empty() && step.kind == BitfieldManip::Kind::LEFT_SHIFT) {
// for left/right shift combo to get a field.
m_steps.push_back(step);
@@ -351,8 +352,6 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step,
return pool.alloc_element<ModifiedCopyBitfieldElement>(m_type, m_base,
std::vector<BitFieldDef>{*val});
// todo check that the mask and the set are compatible with eachother
}
throw std::runtime_error("Unknown state in BitfieldReadElement");
+10 -1
View File
@@ -66,7 +66,10 @@ class BitfieldAccessElement : public FormElement {
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;
FormElement* push_step(const BitfieldManip step, const TypeSystem& ts, FormPool& pool);
FormElement* push_step(const BitfieldManip step,
const TypeSystem& ts,
FormPool& pool,
const Env& env);
private:
Form* m_base = nullptr;
@@ -151,4 +154,10 @@ Form* cast_to_int_enum(const EnumType* type_info,
Form* in);
std::optional<u64> get_goal_integer_constant(Form* in, const Env&);
const BitField& find_field(const TypeSystem& ts,
const BitFieldType* type,
int start_bit,
int size,
std::optional<bool> looking_for_unsigned);
} // namespace decompiler
+1
View File
@@ -67,6 +67,7 @@ class ObjectFileDB {
void analyze_functions_ir1();
void analyze_functions_ir2(const std::string& output_dir);
void ir2_top_level_pass();
void ir2_stack_spill_slot_pass();
void ir2_basic_block_pass();
void ir2_atomic_op_pass();
void ir2_type_analysis_pass();
@@ -17,6 +17,7 @@
#include "decompiler/analysis/final_output.h"
#include "decompiler/analysis/expression_build.h"
#include "decompiler/analysis/inline_asm_rewrite.h"
#include "decompiler/analysis/stack_spill.h"
#include "decompiler/analysis/anonymous_function_def.h"
#include "common/goos/PrettyPrinter.h"
#include "decompiler/IR2/Form.h"
@@ -34,6 +35,8 @@ void ObjectFileDB::analyze_functions_ir2(const std::string& output_dir) {
ir2_top_level_pass();
lg::info("Processing basic blocks and control flow graph...");
ir2_basic_block_pass();
lg::info("Finding stack spills...");
ir2_stack_spill_slot_pass();
lg::info("Converting to atomic ops...");
ir2_atomic_op_pass();
lg::info("Running type analysis...");
@@ -239,6 +242,23 @@ void ObjectFileDB::ir2_basic_block_pass() {
100.f * inspect_methods / total_functions);
}
void ObjectFileDB::ir2_stack_spill_slot_pass() {
Timer timer;
int functions_with_spills = 0;
int total_slots = 0;
for_each_function_def_order([&](Function& func, int, ObjectFileData&) {
auto spill_map = build_spill_map(func.instructions, {func.prologue_end, func.epilogue_start});
auto map_size = spill_map.size();
if (map_size) {
functions_with_spills++;
total_slots += map_size;
}
func.ir2.env.set_stack_spills(spill_map);
});
lg::info("Analyzed stack spills: found {} functions will spills (total {} vars), took {:.2f} ms",
functions_with_spills, total_slots, timer.getMs());
}
/*!
* Conversion of MIPS instructions into AtomicOps. The AtomicOps represent what we
* think are IR of the original GOAL compiler.
+21 -2
View File
@@ -41,6 +41,10 @@ Register rv0() {
return make_gpr(Reg::V0);
}
Register rsp() {
return make_gpr(Reg::SP);
}
/////////////////////////
// Variable Helpers
/////////////////////////
@@ -149,6 +153,12 @@ std::unique_ptr<AtomicOp> make_standard_load(const Instruction& i0,
if (i0.get_src(0).is_label() && i0.get_src(1).is_reg(rfp())) {
// it's an FP relative load.
src = SimpleAtom::make_static_address(i0.get_src(0).get_label()).as_expr();
} else if (i0.get_src(0).is_imm() && i0.get_src(1).is_reg(rsp()) &&
(kind == LoadVarOp::Kind::SIGNED || kind == LoadVarOp::Kind::UNSIGNED)) {
// it's a stack spill.
return std::make_unique<StackSpillLoadOp>(make_dst_var(i0, idx), load_size,
i0.get_src(0).get_imm(),
kind == LoadVarOp::Kind::SIGNED, idx);
} else if (i0.get_src(0).is_imm() && i0.get_src(0).get_imm() == 0) {
// the offset is 0
src = make_src_atom(i0.get_src(1).get_reg(), idx).as_expr();
@@ -165,8 +175,17 @@ std::unique_ptr<AtomicOp> make_standard_store(const Instruction& i0,
int idx,
int store_size,
StoreOp::Kind kind) {
if (i0.get_src(2).is_reg(Register(Reg::GPR, Reg::SP))) {
return std::make_unique<AsmOp>(i0, idx);
if (i0.get_src(2).is_reg(Register(Reg::GPR, Reg::SP)) && kind == StoreOp::Kind::INTEGER) {
if (kind == StoreOp::Kind::INTEGER && store_size == 4 && i0.get_src(1).get_imm() == 0) {
// this is a bit of a hack. enter-state does a sw onto the stack that's not a spill, but
// instead manipulates the stores "ra" register that will later be restored.
// I believe sw is never used for stack spills, and no stack variable is ever located at
// sp + 0, so this should be safe.
return std::make_unique<AsmOp>(i0, idx);
}
// it's a stack spill.
return std::make_unique<StackSpillStoreOp>(make_src_var(i0.get_src(0).get_reg(), idx),
store_size, i0.get_src(1).get_imm(), idx);
}
SimpleAtom val;
SimpleExpression dst;
+11 -1
View File
@@ -34,8 +34,18 @@ bool convert_to_expressions(
// get variable names from the user.
f.ir2.env.map_args_from_config(arg_names, var_override_map);
// override variable types from the user.
// 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) {
+97
View File
@@ -0,0 +1,97 @@
#include <stdexcept>
#include "third-party/fmt/core.h"
#include "stack_spill.h"
#include "decompiler/Disasm/DecompilerLabel.h"
namespace decompiler {
std::string StackSpillSlot::print() const {
return fmt::format("[{:3d}] {}{}", offset, is_signed ? 's' : 'u', size * 8);
}
void StackSpillMap::add_access(const StackSpillSlot& access) {
auto existing = m_slot_map.find(access.offset);
if (existing != m_slot_map.end()) {
if (access != existing->second) {
throw std::runtime_error(fmt::format("Inconsistent stack access:\n{}\n{}\n",
existing->second.print(), access.print()));
}
} else {
m_slot_map.insert({access.offset, access});
}
}
const StackSpillSlot& StackSpillMap::lookup(int offset) const {
auto result = m_slot_map.find(offset);
if (result == m_slot_map.end()) {
throw std::runtime_error(fmt::format("unknown stack spill slot at offset {}", offset));
}
return result->second;
}
void StackSpillMap::finalize() {
// how many variables exist at each byte. should be 1 or 0.
int max_offset = 0;
for (auto& slot : m_slot_map) {
max_offset = std::max(max_offset, slot.second.offset + slot.second.size);
}
assert(max_offset < 4096); // just a sanity check here
std::vector<int> var_count(max_offset, 0);
for (auto& slot : m_slot_map) {
for (int i = 0; i < slot.second.size; i++) {
var_count.at(slot.second.offset + i)++;
}
}
for (size_t i = 0; i < var_count.size(); i++) {
if (var_count[i] > 1) {
throw std::runtime_error(
fmt::format("There are {} variables at stack offset {}", var_count[i], i));
}
}
}
int StackSpillMap::size() const {
return m_slot_map.size();
}
namespace {
struct StackInstrInfo {
InstructionKind kind;
bool is_load;
int size;
bool is_signed;
};
constexpr StackInstrInfo stack_instrs[] = {{InstructionKind::SQ, false, 16, false},
{InstructionKind::LQ, true, 16, false}};
} // namespace
StackSpillMap build_spill_map(const std::vector<Instruction>& instructions, Range<int> range) {
StackSpillMap map;
for (auto idx : range) {
auto& instr = instructions.at(idx);
for (auto& instr_template : stack_instrs) {
if (instr.kind == instr_template.kind) {
// we are the right kind.
auto src_reg = instr.get_src(instr_template.is_load ? 1 : 2).get_reg();
if (src_reg == Register(Reg::GPR, Reg::SP)) {
StackSpillSlot slot;
slot.offset = instr.get_src(instr_template.is_load ? 0 : 1).get_imm();
slot.size = instr_template.size;
slot.is_signed = instr_template.is_signed;
map.add_access(slot);
}
break;
}
}
}
map.finalize();
return map;
}
} // namespace decompiler
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <unordered_map>
#include <vector>
#include <string>
#include "decompiler/Disasm/Instruction.h"
#include "common/util/Range.h"
#include "decompiler/util/StackSpillMap.h"
namespace decompiler {
/*!
* Given the instructions for a function, build a StackSpillMap containing all memory used to
* spill register variables. The range should be the non-prologue/non-epilogue instruction range.
*/
StackSpillMap build_spill_map(const std::vector<Instruction>& instructions, Range<int> range);
} // namespace decompiler
+41 -6
View File
@@ -2,7 +2,7 @@
namespace decompiler {
namespace {
TypeState construct_initial_typestate(const TypeSpec& f_ts) {
TypeState construct_initial_typestate(const TypeSpec& f_ts, const Env& env) {
TypeState result;
int goal_args[] = {Reg::A0, Reg::A1, Reg::A2, Reg::A3, Reg::T0, Reg::T1, Reg::T2, Reg::T3};
assert(f_ts.base_type() == "function");
@@ -11,11 +11,16 @@ TypeState construct_initial_typestate(const TypeSpec& f_ts) {
for (int i = 0; i < int(f_ts.arg_count()) - 1; i++) {
auto reg_id = goal_args[i];
auto reg_type = f_ts.get_arg(i);
result.gpr_types[reg_id] = TP_Type::make_from_ts(reg_type);
result.get(Register(Reg::GPR, reg_id)) = TP_Type::make_from_ts(reg_type);
}
// todo, more specific process types for behaviors.
result.gpr_types[Reg::S6] = TP_Type::make_from_ts(TypeSpec("process"));
result.get(Register(Reg::GPR, Reg::S6)) = TP_Type::make_from_ts(TypeSpec("process"));
// initialize stack slots as uninitialized
for (auto slot_info : env.stack_spills().map()) {
result.spill_slots.insert({slot_info.first, {}});
}
return result;
}
@@ -80,7 +85,7 @@ bool run_type_analysis_ir2(const TypeSpec& my_type, DecompilerTypeSystem& dts, F
op_types.resize(func.ir2.atomic_ops->ops.size());
auto& aop = func.ir2.atomic_ops;
// STEP 1 - topologocial sort the blocks. This gives us an order where we:
// STEP 1 - topological sort the blocks. This gives us an order where we:
// - never visit unreachable blocks (we can't type propagate these)
// - always visit at least one predecessor of a block before that block
auto order = func.bb_topo_sort();
@@ -88,7 +93,7 @@ bool run_type_analysis_ir2(const TypeSpec& my_type, DecompilerTypeSystem& dts, F
assert(order.vist_order.front() == 0);
// STEP 2 - initialize type state for the first block to the function argument types.
block_init_types.at(0) = construct_initial_typestate(my_type);
block_init_types.at(0) = construct_initial_typestate(my_type, func.ir2.env);
// STEP 3 - propagate types until the result stops changing
bool run_again = true;
@@ -109,7 +114,8 @@ bool run_type_analysis_ir2(const TypeSpec& my_type, DecompilerTypeSystem& dts, F
try {
op_types.at(op_id) = op->propagate_types(*init_types, func.ir2.env, dts);
} catch (std::runtime_error& e) {
lg::warn("Function {} failed type prop: {}", func.guessed_name.to_string(), e.what());
lg::warn("Function {} failed type prop at op {}: {}", func.guessed_name.to_string(),
op_id, e.what());
func.warnings.type_prop_warning("{}", e.what());
func.ir2.env.set_types(block_init_types, op_types, *func.ir2.atomic_ops, my_type);
return false;
@@ -162,6 +168,35 @@ bool run_type_analysis_ir2(const TypeSpec& my_type, DecompilerTypeSystem& dts, F
}
}
// figure out the types of stack spill variables:
auto& env = func.ir2.env;
bool changed;
for (auto& type_info : op_types) {
for (auto& spill : type_info.spill_slots) {
auto& slot_info = env.stack_slot_entries[spill.first];
slot_info.tp_type =
dts.tp_lca(env.stack_slot_entries[spill.first].tp_type, spill.second, &changed);
slot_info.offset = spill.first;
}
}
for (auto& type_info : block_init_types) {
for (auto& spill : type_info.spill_slots) {
auto& slot_info = env.stack_slot_entries[spill.first];
slot_info.tp_type =
dts.tp_lca(env.stack_slot_entries[spill.first].tp_type, spill.second, &changed);
slot_info.offset = spill.first;
}
}
// convert to typespec
for (auto& info : env.stack_slot_entries) {
info.second.typespec = info.second.tp_type.typespec();
// debug
// fmt::print("STACK {} : {} ({})\n", info.first, info.second.typespec.print(),
// info.second.tp_type.print());
}
func.ir2.env.set_types(block_init_types, op_types, *func.ir2.atomic_ops, my_type);
return true;
+4 -3
View File
@@ -2444,6 +2444,7 @@
(unpack-v4-16 109)
(unpack-v4-8 110)
(unpack-v4-5 111)
(cmd-mask 239) ;; not sure what this is
)
(defenum vif-cmd-32
@@ -2772,12 +2773,12 @@
)
(define-extern *vif-disasm-table* (array vif-disasm-element)) ;; unknown type
;;(define-extern disasm-vif-tag (function (pointer uint32) int symbol int symbol))
(define-extern disasm-dma-tag (function dma-tag symbol int))
(define-extern disasm-vif-tag (function (pointer vif-tag) int symbol symbol int))
(define-extern disasm-dma-tag (function dma-tag symbol none))
(define-extern disasm-vif-details (function symbol (pointer uint8) vif-cmd int symbol))
(define-extern vif-disasm-element type)
(define-extern *dma-disasm* symbol)
(define-extern disasm-dma-list function)
(define-extern disasm-dma-list (function dma-packet symbol symbol symbol int symbol))
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -181,6 +181,36 @@
[[202, 225], "s3", "(pointer uint16)"]
],
"disasm-vif-tag": [
[[81, 85], "t1", "vif-stcycl-imm"],
[242, "a0", "vif-unpack-imm"]
],
"disasm-dma-list": [
[25, "v1", "dma-tag"],
[153, "v1", "dma-packet"],
[189, "v1", "dma-packet"],
[229, "v1", "dma-packet"],
[258, "v1", "dma-packet"],
[302, "v1", "dma-packet"],
[308, "v1", "dma-packet"],
//[133, "v1", "(pointer uint64)"],
[152, "v1", "(pointer uint64)"],
[167, "v1", "(pointer uint64)"],
[176, "v1", "(pointer uint64)"],
[198, "v1", "(pointer uint64)"],
[207, "v1", "(pointer uint64)"],
[238, "v1", "(pointer uint64)"],
[247, "v1", "(pointer uint64)"],
[282, "v1", "(pointer uint64)"],
[291, "v1", "(pointer uint64)"],
[324, "v1", "(pointer uint64)"],
[334, "v1", "(pointer uint64)"]
],
// LEVEL
"lookup-level-info": [
[3, "a1", "symbol"],
@@ -687,6 +687,42 @@
"vars": { "s4-0": "count2", "s3-0": "data-ptr", "s2-0": "i" }
},
"disasm-vif-tag": {
"args": ["data", "words", "stream", "details"],
"vars": {
"gp-0": "byte-idx",
"v1-0": "cmd-template-idx",
"a0-12": "print-kind",
"s1-0": "first-tag",
"s0-0": "packet-size",
"t1-1": ["stcycl-imm", "vif-stcycl-imm"],
"sv-16": "cmd",
"sv-32": "data-ptr",
"sv-48": "data-idx",
"sv-64": "unpack-imm"
}
},
"disasm-dma-list": {
"args": ["data", "mode", "verbose", "stream", "expected-size"],
"vars": {
"sv-16": "addr",
"sv-32": "data-2",
"sv-48": "qwc",
"sv-64": "ra-1",
"sv-80": "ra-2",
"sv-96": "call-depth",
"sv-112": "current-tag",
"s2-0": "mode-2",
"s3-0": "verbose-2",
"gp-0": "stream-2",
"s1-0": "expected-size-2",
"s0-0": "end-condition",
"s4-0": "total-qwc",
"s5-0": "total-tags"
}
},
"cpad-invalid!": {
"args": ["pad"]
},
+18
View File
@@ -367,10 +367,28 @@ bool DecompilerTypeSystem::tp_lca(TypeState* combined, const TypeState& add) {
}
}
for (auto& x : add.spill_slots) {
// auto existing = combined->spill_slots.find(x.first);
// if (existing == combined->spill_slots.end()) {
// result = true;
// combined->spill_slots.insert({existing->first, existing->second});
// }
bool diff = false;
auto new_type = tp_lca(combined->spill_slots[x.first], x.second, &diff);
if (diff) {
result = true;
combined->spill_slots[x.first] = new_type;
}
}
return result;
}
int DecompilerTypeSystem::get_format_arg_count(const std::string& str) const {
// temporary hack, remove this.
if (str == "ERROR: dma tag has data in reserved bits ~X~%") {
return 0;
}
int arg_count = 0;
for (size_t i = 0; i < str.length(); i++) {
if (str.at(i) == '~') {
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <unordered_map>
#include <vector>
#include <string>
namespace decompiler {
/*!
* Memory on the stack used to spill a register.
* This just has a size/is_signed? and doesn't know anything about types.
*/
struct StackSpillSlot {
int offset = -1; // relative to sp
int size = -1; // bytes
bool is_signed = false; // set to false for quadwords/doublewords
bool operator==(const StackSpillSlot& other) const {
return offset == other.offset && size == other.size && is_signed == other.is_signed;
}
bool operator!=(const StackSpillSlot& other) const { return !((*this) == other); }
std::string print() const;
};
/*!
* Map of StackSpillSlots for a function.
*/
class StackSpillMap {
public:
void add_access(const StackSpillSlot& access);
void finalize();
const StackSpillSlot& lookup(int offset) const;
int size() const;
const std::unordered_map<int, StackSpillSlot>& map() const { return m_slot_map; }
private:
std::unordered_map<int, StackSpillSlot> m_slot_map;
};
} // namespace decompiler
+6
View File
@@ -59,6 +59,8 @@ std::string TP_Type::print() const {
return fmt::format("<vmethod {}>", m_ts.print());
case Kind::NON_VIRTUAL_METHOD:
return fmt::format("<method {}>", m_ts.print());
case Kind::LEFT_SHIFTED_BITFIELD:
return fmt::format("(<{}> << {})", m_ts.print(), m_int);
case Kind::INVALID:
default:
assert(false);
@@ -105,6 +107,8 @@ bool TP_Type::operator==(const TP_Type& other) const {
case Kind::INTEGER_CONSTANT_PLUS_VAR_MULT:
return m_int == other.m_int && m_ts == other.m_ts &&
m_extra_multiplier == other.m_extra_multiplier;
case Kind::LEFT_SHIFTED_BITFIELD:
return m_int == other.m_int && m_ts == other.m_ts;
case Kind::INVALID:
default:
assert(false);
@@ -157,6 +161,8 @@ TypeSpec TP_Type::typespec() const {
return m_ts;
case Kind::NON_VIRTUAL_METHOD:
return m_ts;
case Kind::LEFT_SHIFTED_BITFIELD:
return TypeSpec("int"); // ideally this is never used.
case Kind::INVALID:
default:
assert(false);
+31
View File
@@ -32,6 +32,7 @@ class TP_Type {
DYNAMIC_METHOD_ACCESS, // partial access into a
VIRTUAL_METHOD,
NON_VIRTUAL_METHOD,
LEFT_SHIFTED_BITFIELD, // (bitfield << some-constant)
INVALID
} kind = Kind::UNINITIALIZED;
TP_Type() = default;
@@ -57,6 +58,7 @@ class TP_Type {
case Kind::INTEGER_CONSTANT_PLUS_VAR_MULT:
case Kind::VIRTUAL_METHOD:
case Kind::NON_VIRTUAL_METHOD:
case Kind::LEFT_SHIFTED_BITFIELD:
return false;
case Kind::UNINITIALIZED:
case Kind::OBJECT_NEW_METHOD:
@@ -209,6 +211,14 @@ class TP_Type {
return result;
}
static TP_Type make_from_left_shift_bitfield(const TypeSpec& ts, int amount) {
TP_Type result;
result.kind = Kind::LEFT_SHIFTED_BITFIELD;
result.m_ts = ts;
result.m_int = amount;
return result;
}
const TypeSpec& get_objects_typespec() const {
assert(kind == Kind::TYPESPEC || kind == Kind::INTEGER_CONSTANT_PLUS_VAR);
return m_ts;
@@ -249,6 +259,16 @@ class TP_Type {
return m_extra_multiplier;
}
int get_left_shift() const {
assert(kind == Kind::LEFT_SHIFTED_BITFIELD);
return m_int;
}
const TypeSpec& get_bitfield_type() const {
assert(kind == Kind::LEFT_SHIFTED_BITFIELD);
return m_ts;
}
private:
TypeSpec m_ts;
std::string m_str;
@@ -260,6 +280,7 @@ class TP_Type {
struct TypeState {
TP_Type gpr_types[32];
TP_Type fpr_types[32];
std::unordered_map<int, TP_Type> spill_slots;
std::string print_gpr_masked(u32 mask) const;
TP_Type& get(const Register& r) {
@@ -285,6 +306,16 @@ struct TypeState {
throw std::runtime_error("TP_Type::get failed");
}
}
const TP_Type& get_slot(int offset) const {
auto result = spill_slots.find(offset);
if (result == spill_slots.end()) {
throw std::runtime_error("TP_Type::get_slot failed: " + std::to_string(offset));
}
return result->second;
}
TP_Type& get_slot(int offset) { return spill_slots[offset]; }
};
u32 regs_to_gpr_mask(const std::vector<Register>& regs);
+597
View File
@@ -56,3 +56,600 @@
(new 'static 'vif-disasm-element :print #x8)))
(defun disasm-vif-details ((stream symbol) (data (pointer uint8)) (kind vif-cmd) (count int))
(cond
((= kind (vif-cmd unpack-v4-8))
(let ((data-ptr (&-> data 4)))
(dotimes (i count)
(format stream " #x~X: #x~2X #x~2X #x~2X #x~2X~%"
(+ (+ (shl i 2) 4) (the-as int data))
(-> data-ptr (shl i 2))
(-> data-ptr (+ (shl i 2) 1))
(-> data-ptr (+ (shl i 2) 2))
(-> data-ptr (+ (shl i 2) 3))
)
)
)
)
((= kind (vif-cmd unpack-s-8))
(let ((s3-1 (&-> data 4)))
(dotimes (s2-1 count)
;; this is messed up and I think somebody put a parenthesis in
;; the wrong spot. I believe the format below only has one
;; format argument but should have 2.
(format stream " #x~X: #x~2x~%"
(+ (+ s2-1 4) (the-as int data))
count
)
;; the actual assembly is very strange here.
(let ((v1-21 (-> s3-1 (* 3 s2-1))))
)
(let ((v1-26 (-> s3-1 (+ (* 3 s2-1) 1))))
)
)
)
)
((= kind (vif-cmd unpack-v4-32))
(let ((s3-2 (the-as (pointer uint32) (&-> data 4))))
(dotimes (s2-2 count)
(format stream " #x~X: #x~8x #x~8x #x~8x #x~8x~%"
(+ (+ (shl s2-2 4) 4) (the-as int data))
(-> s3-2 (shl s2-2 2))
(-> s3-2 (+ (shl s2-2 2) 1))
(-> s3-2 (+ (shl s2-2 2) 2))
(-> s3-2 (+ (shl s2-2 2) 3))
)
)
)
)
((= kind (vif-cmd unpack-v4-16))
(let ((s3-3 (the-as (pointer uint16) (&-> data 4))))
(dotimes (s2-3 count)
(format stream " #x~X: #x~4x #x~4x #x~4x #x~4x~%"
(+ (+ (shl s2-3 3) 4) (the-as int data))
(-> s3-3 (shl s2-3 2))
(-> s3-3 (+ (shl s2-3 2) 1))
(-> s3-3 (+ (shl s2-3 2) 2))
(-> s3-3 (+ (shl s2-3 2) 3))
)
)
)
)
((= kind (vif-cmd unpack-v3-32))
(let ((s3-4 (the-as (pointer uint32) (&-> data 4))))
(dotimes (s2-4 count)
(format stream " #x~X: #x~8x #x~8x #x~8x~%"
(+ (+ (* 12 s2-4) 4) (the-as int data))
(-> (&+ s3-4 (* 12 s2-4)) 0)
(-> s3-4 (+ (* 3 s2-4) 1))
(-> s3-4 (+ (* 3 s2-4) 2))
)
)
)
)
((= kind (vif-cmd unpack-v3-16))
(let ((s3-5 (the-as (pointer uint16) (&-> data 4))))
(dotimes (s2-5 count)
(format stream " #x~X: #x~4x #x~4x #x~4x~%"
(+ (+ (* 6 s2-5) 4) (the-as int data))
(-> (&+ s3-5 (* 6 s2-5)) 0)
(-> s3-5 (+ (* 3 s2-5) 1))
(-> s3-5 (+ (* 3 s2-5) 2))
)
)
)
)
((= kind (vif-cmd unpack-v2-16))
(let ((s3-6 (the-as (pointer uint16) (&-> data 4))))
(dotimes (s2-6 count)
(format stream " #x~X: #x~4x #x~4x~%"
(+ (+ (shl s2-6 2) 4) (the-as int data))
(-> (&+ s3-6 (* 6 s2-6)) 0)
(-> s3-6 (+ (* 3 s2-6) 1))
)
)
)
)
(else
(format stream " #x~X: Data format #b~b not yet supported, add it for yourself!~%"
(&-> data 4)
kind
)
)
)
#f
)
(defun disasm-vif-tag ((data (pointer vif-tag)) (words int) (stream symbol) (details symbol))
"Print out a vif code and the immediate data.
Will print stuff until the number of words has been reached.
Returns how many bytes we overshot by."
(local-vars
(cmd vif-cmd)
(data-ptr (pointer vif-tag))
(data-idx int)
(unpack-imm vif-unpack-imm)
)
(let ((byte-idx 0))
(while (< byte-idx (shl words 2))
(let ((packet-size 4)) ;; default packet size is 32-bits.
(let ((first-tag (-> data 0)))
;; print the packet's address.
(format stream " #x~X:" data)
;; iterate through the disasm table, looking for a match
(dotimes (cmd-template-idx (-> *vif-disasm-table* length))
(set! cmd (-> first-tag cmd))
;; check the command against the table's mask and tag
(when (= (logand cmd (the-as uint (-> *vif-disasm-table* cmd-template-idx mask)))
(-> *vif-disasm-table* cmd-template-idx tag)
)
(let* ((print-kind (-> *vif-disasm-table* cmd-template-idx print))
(v0-1 (cond
((zero? print-kind)
;; just the name and irq bit.
(format stream " (~s :irq ~D)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
)
)
((= print-kind 1)
;; name and immediate register value.
(format stream " (~s :irq ~D :~s #x~X)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> *vif-disasm-table* cmd-template-idx string2)
(-> first-tag imm)
)
)
((= print-kind 2)
;; name and stcycl immediate
(let ((stcycl-imm (the-as vif-stcycl-imm (-> first-tag imm))))
(format stream " (~s :irq ~D :wl ~D :cl ~D)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> stcycl-imm wl)
(-> stcycl-imm cl)
)
)
)
((= print-kind 3)
;; name and a single word of extra data
(set! packet-size 8) ;; 4 + 4 = 8 byte packet.
(format stream " (~s :irq ~D :~s #x~X)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> *vif-disasm-table* cmd-template-idx string2)
(-> data 1)
)
)
((= print-kind 4)
;; 4x 1 word extra data
(set! packet-size 20) ;; 4 + 16 = 20 byte packet.
(format stream " (~s :irq ~D :~s "
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> *vif-disasm-table* cmd-template-idx string2)
)
(format stream "#x~X #x~X #x~X #x~X)~%"
(-> data 1)
(-> data 2)
(-> data 3)
(-> data 4)
)
)
((= print-kind 5)
(format stream " (~s :irq ~D :instructions #x~D :addr #x~X)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> first-tag num)
(-> first-tag imm)
)
)
((= print-kind 6)
;; imm is quadword count.
;; This packet size calculation is wrong.
;; this doesn't seem to be a decompiler error, this matches
;; the assembly, but makes no sense.
(set! packet-size
(the-as int (if (-> first-tag imm)
#x100000
(shl (-> first-tag imm) 4)
)
)
)
(format stream " (~s :irq ~D :qwc #x~D)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> first-tag imm)
)
;; loop over data and print it.
(set! data-ptr (&-> data 1))
(set! data-idx 0)
(while (< data-idx (the-as int (-> first-tag imm)))
(format stream " #x~X: #x~8x #x~8x #x~8x #x~8x~%"
(+ (+ (shl data-idx 4) 4) (the-as int data))
(-> data-ptr (shl data-idx 2))
(-> data-ptr (+ (shl data-idx 2) 1))
(-> data-ptr (+ (shl data-idx 2) 2))
(-> data-ptr (+ (shl data-idx 2) 3))
)
(set! data-idx (+ data-idx 1))
)
#f
)
((= print-kind 7)
(set! packet-size
(the-as int
(+
(logand
-4
(+ (* (-> *vif-disasm-table* cmd-template-idx val)
(the-as uint (-> first-tag num)))
3
)
)
4
)
)
)
(set! unpack-imm (the-as vif-unpack-imm (-> first-tag imm)))
(format stream " (~s :irq ~D :num ~D :addr #x~X "
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> first-tag num)
(-> unpack-imm addr)
)
(format stream ":msk ~D :flg ~D :usn ~D [skip ~d])~%"
(-> first-tag msk)
(-> unpack-imm flg)
(-> unpack-imm usn)
(the-as uint packet-size)
)
(if details
(disasm-vif-details
stream
(the-as (pointer uint8) data)
(logand cmd (vif-cmd cmd-mask))
(the-as int (-> first-tag num))
)
)
)
((= print-kind 8)
(format stream " (*unknown* vif-tag #x~X)~%"
(-> first-tag cmd)
)
)
)
)
)
)
;; we matched, skip to the end.
(set! cmd-template-idx (-> *vif-disasm-table* length))
)
)
)
;; increment counters.
(+! byte-idx packet-size)
(&+! data packet-size)
)
)
(- byte-idx (shl words 2))
)
)
(defun disasm-dma-tag ((arg0 dma-tag) (arg1 symbol))
(format arg1 "(dma-tag ")
;; this is a case statement.
(let ((t9-1 format)
(a0-2 arg1)
(a1-2 "~s")
(v1-1 (-> arg0 id))
)
(t9-1 a0-2 a1-2 (cond
((= v1-1 (dma-tag-id end))
"end"
)
((= v1-1 (dma-tag-id ret))
"ret"
)
((= v1-1 (dma-tag-id call))
"call"
)
((= v1-1 (dma-tag-id refs))
"refs"
)
((= v1-1 (dma-tag-id ref))
"ref"
)
((= v1-1 (dma-tag-id next))
"next"
)
((= v1-1 (dma-tag-id cnt))
"cnt"
)
((zero? v1-1)
"refe"
)
(else
"*unknown*"
)
)
)
)
(if (> (the-as uint (-> arg0 addr)) 0)
(format arg1 " :addr #x~8x" (-> arg0 addr))
)
(if (> (the-as uint (-> arg0 qwc)) 0)
(format arg1 " :qwc ~d" (-> arg0 qwc))
)
(if (> (the-as uint (-> arg0 spr)) 0)
(format arg1 " :spr ~d" (-> arg0 spr))
)
(if (> (the-as uint (-> arg0 irq)) 0)
(format arg1 " :irq ~d" (-> arg0 irq))
)
(if (> (the-as uint (-> arg0 pce)) 0)
(format arg1 " :pce ~d" (-> arg0 pce))
)
(format arg1 ")~%")
(none)
)
;; this is unused.
(define *dma-disasm* #t)
(defun disasm-dma-list ((data dma-packet) (mode symbol) (verbose symbol) (stream symbol) (expected-size int))
"Disassemble a dma list, starting from the given packet."
(local-vars
(addr object)
(data-2 dma-packet)
(qwc int)
(ra-1 object)
(ra-2 object)
(call-depth int)
(current-tag dma-tag)
)
;; this is a little messed up because of stack spills.
(set! data-2 data)
(let ((mode-2 mode)
(verbose-2 verbose)
(stream-2 stream)
(expected-size-2 expected-size)
)
(if verbose-2
(format stream-2 "~%--- ~X -----------------------------~%" data-2)
)
;; the end-condition will get set to #t when the end of the chain is reached,
;; or 'error if invalid data is found.
(let ((end-condition #f))
;; statistics
(let ((total-qwc 0)
(total-tags 0)
)
(set! addr 0)
(set! qwc 0)
;; for the "call" feature
(set! ra-1 0)
(set! ra-2 1)
(set! call-depth -1)
;; the tag we're currently exploring.
(set! current-tag (new 'static 'dma-tag))
;; loop until tag is done
(while (not end-condition)
;; first, we should verify that the data pointer is valid so we don't crash
(cond
((not (valid? data-2 (the-as type #f) "dma-list tag pointer" #t stream-2))
(format stream-2 "ERROR: dma-list tag pointer invalid~%")
(set! end-condition 'error)
)
(else
;; load the tag
(set! current-tag (-> data-2 dma))
;; check the address. it is unset on the first pass so we skip this check then.
(when (not (or (zero? total-tags)
(valid? addr (the-as type #f) "dma-list data pointer" #t stream-2)
)
)
(format stream-2 "ERROR: dma-list data pointer invalid~%")
(set! end-condition 'error)
)
;; check that the tag's value makes sense.
(when (nonzero? (logand #x3ff0000 (the-as int current-tag)))
(format stream-2 "ERROR: dma tag has data in reserved bits ~X~%")
(set! end-condition 'error)
)
)
)
;; next, disassembly the dma-tag.
;; only do it if verbose is set, or we have encountered an error.
(when (or verbose-2 (= end-condition 'error))
(format stream-2 "#x~8x: " data-2)
(cond
((zero? call-depth)
(format stream-2 " ")
)
((= call-depth 1)
(format stream-2 " ")
)
)
(disasm-dma-tag current-tag stream-2)
)
;; now the dma data.
(if end-condition
(none) ;; do nothing if we want to end.
(cond
;; check if we are an addr in addr field dma tag.
((or (zero? (+ (the-as uint (-> current-tag id)) (the-as uint -3))) ;; ref
(zero? (+ (the-as uint (-> current-tag id)) (the-as uint -4))) ;; refs
(zero? (-> current-tag id)) ;; refe
)
;; set addresss and qwc from the tag.
(set! addr (-> current-tag addr))
(set! qwc (the-as int (-> current-tag qwc)))
;; optionally disassemble vif tags.
(if mode-2
;; I don't quite understand this. The first thing is for the tag transferred due to tte.
;; but I don't understand what the v0-9 offset is.
(let ((v0-9 (disasm-vif-tag (&-> data-2 vif0) 2 stream-2 (= mode-2 'details))))
(disasm-vif-tag
(the-as (pointer vif-tag) (+ (the-as uint addr) (the-as uint v0-9)))
(the-as int (- (shl (the-as int qwc) 2) (the-as uint (sar v0-9 2))))
stream-2
(= mode-2 'details)
)
)
)
;; move on to next dma-packet. it is adjacent in memory for these modes.
(set! data-2 (the-as dma-packet (&-> (the-as (pointer uint64) data-2) 2)))
;; if we were a refe, it is now the end!
(if (zero? (-> current-tag id)) ;; check refe.
(set! end-condition #t)
)
)
(else
(cond
((= (-> current-tag id) (dma-tag-id cnt))
;; cnt: address is after tag, next tag is after data.
;; get the address from after the tag
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
;; qwc from the tag
(set! qwc (the-as int (-> current-tag qwc)))
;; disassemble vif.
(if mode-2
(disasm-vif-tag
(the-as (pointer vif-tag) (&-> (the-as (pointer uint64) data-2) 1))
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
;; next data is after vif data.
(set! data-2 (the-as dma-packet (+ (the-as uint data-2)
(the-as uint (shl (the-as int (+ (the-as uint qwc)
(the-as uint 1)))
4)
)
)
)
)
)
((= (-> current-tag id) (dma-tag-id next))
;; address after tag and qwc in tag.
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(if mode-2
(disasm-vif-tag
(the-as (pointer vif-tag) (&-> (the-as (pointer uint64) data-2) 1))
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
;; addr is the next tag. check for infinite loop before continuing.
(when (= data-2 (-> current-tag addr))
(format stream-2 "ERROR: next tag creates infinite loop.~%")
(set! end-condition 'error)
)
(set! data-2 (the-as dma-packet (-> current-tag addr)))
)
(else
(cond
((= (-> current-tag id) (dma-tag-id call))
;; this "calls" a DMA chain, which should then return to here.
;; the stack is only two deep.
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(if mode-2
(disasm-vif-tag (the-as (pointer vif-tag) (&-> (the-as (pointer uint64) data-2) 1))
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
(set! data-2 (the-as dma-packet (-> current-tag addr)))
;; increment stack
(set! call-depth (+ call-depth 1))
;; and store the return.
(cond
((zero? call-depth)
(set! ra-1 (&+ (the pointer addr) qwc))
)
(else
(set! ra-2 (&+ (the pointer addr) qwc))
)
)
)
((= (-> current-tag id) (dma-tag-id ret))
;; return from a "called" dma chain.
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(if mode-2
(disasm-vif-tag (the-as (pointer vif-tag) (&-> (the-as (pointer uint64) data-2) 1))
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
;; restore the address from the stack
;; likely a case.
(let ((v1-123 call-depth))
(cond
((zero? v1-123)
(set! data-2 (the-as dma-packet ra-1))
)
((= v1-123 1)
(set! data-2 (the-as dma-packet ra-2))
)
(else
(set! end-condition #t)
)
)
)
(set! call-depth (+ call-depth -1))
)
((= (-> current-tag id) (dma-tag-id end))
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(set! end-condition #t)
(if mode-2
(disasm-vif-tag (the-as (pointer vif-tag) (&-> (the-as (pointer uint64) data-2) 1))
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
)
(else
(format stream-2 "ERROR: Unknown DMA TAG command.~%")
(set! end-condition 'error)
)
)
)
)
)
)
)
;; increment stats
(+! total-qwc qwc)
(+! total-tags 1)
;; end, if we hit the tag limit.
(if (and (>= expected-size-2 0) (>= total-tags expected-size-2))
(set! end-condition #t)
)
)
(when (or verbose-2 (= end-condition 'error))
(format stream-2 "NOTICE: Total tags: ~d~%" total-tags)
(format stream-2 "NOTICE: Total QWC: ~d~%" total-qwc)
(format stream-2 "--------------------------------~%~%")
)
)
(!= end-condition 'error)
)
)
)
+1
View File
@@ -294,6 +294,7 @@
(unpack-v4-16 109)
(unpack-v4-8 110)
(unpack-v4-5 111)
(cmd-mask 239) ;; not sure what this is.
)
;; this makes a copy of the above type, but uses a uint32.
+2 -1
View File
@@ -270,7 +270,8 @@ class Compiler {
RegVal* compile_get_method_of_object(const goos::Object& form,
RegVal* object,
const std::string& method_name,
Env* env);
Env* env,
bool error_message_function_or_method = false);
Val* compile_define_constant(const goos::Object& form,
const goos::Object& rest,
Env* env,
+3 -2
View File
@@ -472,9 +472,10 @@ Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* en
if (eval_args.empty()) {
throw_compiler_error(form, "Unrecognized symbol {} as head of form.", uneval_head.print());
}
// get the method function pointer
head = compile_get_method_of_object(form, eval_args.front(), symbol_string(uneval_head), env);
fmt::format("method of object {} {}\n", head->print(), head->type().print());
head = compile_get_method_of_object(form, eval_args.front(), symbol_string(uneval_head), env,
true);
}
// convert the head to a GPR (if function, this is already done)
+13 -2
View File
@@ -53,9 +53,20 @@ RegVal* Compiler::compile_get_method_of_type(const goos::Object& form,
RegVal* Compiler::compile_get_method_of_object(const goos::Object& form,
RegVal* object,
const std::string& method_name,
Env* env) {
Env* env,
bool error_message_function_or_method) {
auto& compile_time_type = object->type();
auto method_info = m_ts.lookup_method(compile_time_type.base_type(), method_name);
MethodInfo method_info;
if (!m_ts.try_lookup_method(compile_time_type.base_type(), method_name, &method_info)) {
if (error_message_function_or_method) {
throw_compiler_error(form, "No method or function named {} for type {}", method_name,
compile_time_type.print());
} else {
throw_compiler_error(form, "Type {} has no method {}", compile_time_type.print(),
method_name);
}
}
method_info.type = method_info.type.substitute_for_method_call(compile_time_type.base_type());
auto fe = get_parent_env_of_type<FunctionEnv>(env);
+1
View File
@@ -29,6 +29,7 @@ add_executable(goalc-test
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_gkernel_decomp.cpp
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_math_decomp.cpp
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_DataParser.cpp
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_DisasmVifDecompile.cpp
${GOALC_TEST_FRAMEWORK_SOURCES}
${GOALC_TEST_CASES})
+6
View File
@@ -6,6 +6,7 @@
#include "decompiler/analysis/cfg_builder.h"
#include "decompiler/analysis/expression_build.h"
#include "decompiler/analysis/final_output.h"
#include "decompiler/analysis/stack_spill.h"
#include "decompiler/analysis/insert_lets.h"
#include "decompiler/util/config_parsers.h"
#include "common/goos/PrettyPrinter.h"
@@ -153,6 +154,11 @@ std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
fmt::print("CFG:\n{}\n", test->func.cfg->to_dot());
}
// find stack spill slots
auto spill_map = build_spill_map(test->func.instructions,
{test->func.prologue_end, test->func.epilogue_start});
test->func.ir2.env.set_stack_spills(spill_map);
// convert instruction to atomic ops
DecompWarnings warnings;
auto ops = convert_function_to_atomic_ops(test->func, program.labels, warnings);
@@ -3,6 +3,7 @@
(define-extern fact (function int int))
(define-extern max (function int int int))
(define-extern min (function int int int))
(define-extern valid? (function object type basic basic object symbol))
;; KERNEL
(declare-type process basic)
@@ -82,6 +83,10 @@
'(none)
)
(defmacro empty-form ()
'(none)
)
;; math
(define-extern fabs (function float float))
@@ -172,6 +177,7 @@
(unpack-v4-16 109)
(unpack-v4-8 110)
(unpack-v4-5 111)
(cmd-mask 239)
)
(defenum vif-cmd-32
+917
View File
@@ -0,0 +1,917 @@
;;-*-Lisp-*-
(in-package goal)
;; this file is debug only
(when *debug-segment*
;; definition of type vif-disasm-element
(deftype vif-disasm-element (structure)
((mask uint32 :offset-assert 0)
(tag vif-cmd-32 :offset-assert 4)
(val uint32 :offset-assert 8)
(print uint32 :offset-assert 12)
(string1 string :offset-assert 16)
(string2 string :offset-assert 20)
)
:method-count-assert 9
:size-assert #x18
:flag-assert #x900000018
)
;; definition for method 3 of type vif-disasm-element
(defmethod inspect vif-disasm-element ((obj vif-disasm-element))
(format #t "[~8x] ~A~%" obj 'vif-disasm-element)
(format #t "~Tmask: ~D~%" (-> obj mask))
(format #t "~Ttag: ~D~%" (-> obj tag))
(format #t "~Tval: ~D~%" (-> obj val))
(format #t "~Tprint: ~D~%" (-> obj print))
(format #t "~Tstring1: ~A~%" (-> obj string1))
(format #t "~Tstring2: ~A~%" (-> obj string2))
obj
)
;; definition for symbol *vif-disasm-table*, type (array vif-disasm-element)
(define
*vif-disasm-table*
(the-as (array vif-disasm-element)
(new
'static
'boxed-array
vif-disasm-element
34
(new 'static 'vif-disasm-element :mask #x7f :string1 "nop")
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 stcycl)
:print #x2
:string1 "stcycl"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 offset)
:print #x1
:string1 "offset"
:string2 "offset"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 base)
:print #x1
:string1 "base"
:string2 "base"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 itop)
:print #x1
:string1 "itop"
:string2 "addr"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 stmod)
:print #x1
:string1 "stmod"
:string2 "mode"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 mskpath3)
:print #x1
:string1 "mskpath3"
:string2 "mask"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 mark)
:print #x1
:string1 "mark"
:string2 "mark"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 flushe)
:string1 "flushe"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 flush)
:string1 "flush"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 flusha)
:string1 "flusha"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 mscal)
:print #x1
:string1 "mscal"
:string2 "addr"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 mscnt)
:string1 "mscnt"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 mscalf)
:print #x1
:string1 "mscalf"
:string2 "addr"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 stmask)
:print #x3
:string1 "stmask"
:string2 "mask"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 strow)
:print #x4
:string1 "strow"
:string2 "row"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 stcol)
:print #x4
:string1 "stcol"
:string2 "col"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 mpg)
:print #x5
:string1 "mpg"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 direct)
:print #x6
:string1 "direct"
)
(new 'static 'vif-disasm-element
:mask #x7f
:tag (vif-cmd-32 directhl)
:print #x6
:string1 "directhl"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-s-32)
:val #x10
:print #x7
:string1 "unpack-s-32"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-s-16)
:val #x8
:print #x7
:string1 "unpack-s-16"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-s-8)
:val #x4
:print #x7
:string1 "unpack-s-8"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-v2-32)
:val #x8
:print #x7
:string1 "unpack-v2-32"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-v2-16)
:val #x4
:print #x7
:string1 "unpack-v2-16"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-v2-8)
:val #x2
:print #x7
:string1 "unpack-v2-8"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-v3-32)
:val #xc
:print #x7
:string1 "unpack-v3-32"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-v3-16)
:val #x6
:print #x7
:string1 "unpack-v3-16"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-v3-8)
:val #x3
:print #x7
:string1 "unpack-v3-8"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-v4-32)
:val #x10
:print #x7
:string1 "unpack-v4-32"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-v4-16)
:val #x8
:print #x7
:string1 "unpack-v4-16"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-v4-8)
:val #x4
:print #x7
:string1 "unpack-v4-8"
)
(new 'static 'vif-disasm-element
:mask #x6f
:tag (vif-cmd-32 unpack-v4-5)
:val #x2
:print #x7
:string1 "unpack-v4-5"
)
(new 'static 'vif-disasm-element :print #x8)
)
)
)
;; definition for function disasm-vif-details
(defun
disasm-vif-details
((stream symbol) (data (pointer uint8)) (kind vif-cmd) (count int))
(let ((count2 count))
(cond
((= kind (vif-cmd unpack-v4-8))
(let ((data-ptr (&-> data 4)))
(dotimes (i count2)
(format
stream
" #x~X: #x~2X #x~2X #x~2X #x~2X~%"
(+ (+ (shl i 2) 4) (the-as int data))
(-> data-ptr (shl i 2))
(-> data-ptr (+ (shl i 2) 1))
(-> data-ptr (+ (shl i 2) 2))
(-> data-ptr (+ (shl i 2) 3))
)
)
)
)
((= kind (vif-cmd unpack-s-8))
(let ((s3-1 (&-> data 4)))
(dotimes (s2-1 count2)
(format
stream
" #x~X: #x~2x~%"
(+ (+ s2-1 4) (the-as int data))
count
)
(let ((v1-21 (-> s3-1 (* 3 s2-1))))
)
(let ((v1-26 (-> s3-1 (+ (* 3 s2-1) 1))))
)
)
)
)
((= kind (vif-cmd unpack-v4-32))
(let ((s3-2 (the-as (pointer uint32) (&-> data 4))))
(dotimes (s2-2 count2)
(format
stream
" #x~X: #x~8x #x~8x #x~8x #x~8x~%"
(+ (+ (shl s2-2 4) 4) (the-as int data))
(-> s3-2 (shl s2-2 2))
(-> s3-2 (+ (shl s2-2 2) 1))
(-> s3-2 (+ (shl s2-2 2) 2))
(-> s3-2 (+ (shl s2-2 2) 3))
)
)
)
)
((= kind (vif-cmd unpack-v4-16))
(let ((s3-3 (the-as (pointer uint16) (&-> data 4))))
(dotimes (s2-3 count2)
(format
stream
" #x~X: #x~4x #x~4x #x~4x #x~4x~%"
(+ (+ (shl s2-3 3) 4) (the-as int data))
(-> s3-3 (shl s2-3 2))
(-> s3-3 (+ (shl s2-3 2) 1))
(-> s3-3 (+ (shl s2-3 2) 2))
(-> s3-3 (+ (shl s2-3 2) 3))
)
)
)
)
((= kind (vif-cmd unpack-v3-32))
(let ((s3-4 (the-as (pointer uint32) (&-> data 4))))
(dotimes (s2-4 count2)
(format
stream
" #x~X: #x~8x #x~8x #x~8x~%"
(+ (+ (* 12 s2-4) 4) (the-as int data))
(-> (&+ s3-4 (* 12 s2-4)) 0)
(-> s3-4 (+ (* 3 s2-4) 1))
(-> s3-4 (+ (* 3 s2-4) 2))
)
)
)
)
((= kind (vif-cmd unpack-v3-16))
(let ((s3-5 (the-as (pointer uint16) (&-> data 4))))
(dotimes (s2-5 count2)
(format
stream
" #x~X: #x~4x #x~4x #x~4x~%"
(+ (+ (* 6 s2-5) 4) (the-as int data))
(-> (&+ s3-5 (* 6 s2-5)) 0)
(-> s3-5 (+ (* 3 s2-5) 1))
(-> s3-5 (+ (* 3 s2-5) 2))
)
)
)
)
((= kind (vif-cmd unpack-v2-16))
(let ((s3-6 (the-as (pointer uint16) (&-> data 4))))
(dotimes (s2-6 count2)
(format
stream
" #x~X: #x~4x #x~4x~%"
(+ (+ (shl s2-6 2) 4) (the-as int data))
(-> (&+ s3-6 (* 6 s2-6)) 0)
(-> s3-6 (+ (* 3 s2-6) 1))
)
)
)
)
(else
(format
stream
" #x~X: Data format #b~b not yet supported, add it for yourself!~%"
(&-> data 4)
kind
)
)
)
)
#f
)
;; definition for function disasm-vif-tag
;; Used lq/sq
(defun
disasm-vif-tag
((data (pointer vif-tag)) (words int) (stream symbol) (details symbol))
(local-vars
(cmd vif-cmd)
(data-ptr (pointer vif-tag))
(data-idx int)
(unpack-imm vif-unpack-imm)
)
(let ((byte-idx 0))
(while (< byte-idx (shl words 2))
(let ((packet-size 4))
(let ((first-tag (-> data 0)))
(format stream " #x~X:" data)
(dotimes (cmd-template-idx (-> *vif-disasm-table* length))
(set! cmd (-> first-tag cmd))
(when
(=
(logand
cmd
(the-as uint (-> *vif-disasm-table* cmd-template-idx mask))
)
(-> *vif-disasm-table* cmd-template-idx tag)
)
(let* ((print-kind (-> *vif-disasm-table* cmd-template-idx print))
(v0-1 (cond
((zero? print-kind)
(format
stream
" (~s :irq ~D)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
)
)
((= print-kind 1)
(format
stream
" (~s :irq ~D :~s #x~X)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> *vif-disasm-table* cmd-template-idx string2)
(-> first-tag imm)
)
)
((= print-kind 2)
(let
((stcycl-imm (the-as vif-stcycl-imm (-> first-tag imm)))
)
(format
stream
" (~s :irq ~D :wl ~D :cl ~D)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> stcycl-imm wl)
(-> stcycl-imm cl)
)
)
)
((= print-kind 3)
(set! packet-size 8)
(format
stream
" (~s :irq ~D :~s #x~X)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> *vif-disasm-table* cmd-template-idx string2)
(-> data 1)
)
)
((= print-kind 4)
(set! packet-size 20)
(format
stream
" (~s :irq ~D :~s "
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> *vif-disasm-table* cmd-template-idx string2)
)
(format
stream
"#x~X #x~X #x~X #x~X)~%"
(-> data 1)
(-> data 2)
(-> data 3)
(-> data 4)
)
)
((= print-kind 5)
(format
stream
" (~s :irq ~D :instructions #x~D :addr #x~X)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> first-tag num)
(-> first-tag imm)
)
)
((= print-kind 6)
(if (-> first-tag imm)
(set! packet-size #x100000)
(set!
packet-size
(the-as int (shl (the-as int (-> first-tag imm)) 4))
)
)
(format
stream
" (~s :irq ~D :qwc #x~D)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> first-tag imm)
)
(set! data-ptr (&-> data 1))
(set! data-idx 0)
(while (< data-idx (the-as int (-> first-tag imm)))
(format
stream
" #x~X: #x~8x #x~8x #x~8x #x~8x~%"
(+ (+ (shl data-idx 4) 4) (the-as int data))
(-> data-ptr (shl data-idx 2))
(-> data-ptr (+ (shl data-idx 2) 1))
(-> data-ptr (+ (shl data-idx 2) 2))
(-> data-ptr (+ (shl data-idx 2) 3))
)
(set! data-idx (+ data-idx 1))
)
#f
)
((= print-kind 7)
(set!
packet-size
(the-as
int
(+
(logand
-4
(the-as
int
(+
(*
(-> *vif-disasm-table* cmd-template-idx val)
(the-as uint (-> first-tag num))
)
3
)
)
)
4
)
)
)
(set!
unpack-imm
(the-as vif-unpack-imm (-> first-tag imm))
)
(format
stream
" (~s :irq ~D :num ~D :addr #x~X "
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> first-tag num)
(-> unpack-imm addr)
)
(format
stream
":msk ~D :flg ~D :usn ~D [skip ~d])~%"
(-> first-tag msk)
(-> unpack-imm flg)
(-> unpack-imm usn)
(the-as uint packet-size)
)
(if details
(disasm-vif-details
stream
(the-as (pointer uint8) data)
(logand cmd (vif-cmd cmd-mask))
(the-as int (-> first-tag num))
)
)
)
((= print-kind 8)
(format
stream
" (*unknown* vif-tag #x~X)~%"
(-> first-tag cmd)
)
)
)
)
)
)
(set! cmd-template-idx (-> *vif-disasm-table* length))
)
)
)
(+! byte-idx packet-size)
(&+! data packet-size)
)
)
(- byte-idx (shl words 2))
)
)
;; definition for function disasm-dma-tag
;; INFO: Return type mismatch object vs none.
(defun disasm-dma-tag ((arg0 dma-tag) (arg1 symbol))
(format arg1 "(dma-tag ")
(let ((t9-1 format)
(a0-2 arg1)
(a1-2 "~s")
(v1-1 (-> arg0 id))
)
(t9-1 a0-2 a1-2 (cond
((= v1-1 (dma-tag-id end))
"end"
)
((= v1-1 (dma-tag-id ret))
"ret"
)
((= v1-1 (dma-tag-id call))
"call"
)
((= v1-1 (dma-tag-id refs))
"refs"
)
((= v1-1 (dma-tag-id ref))
"ref"
)
((= v1-1 (dma-tag-id next))
"next"
)
((= v1-1 (dma-tag-id cnt))
"cnt"
)
((zero? v1-1)
"refe"
)
(else
"*unknown*"
)
)
)
)
(if (> (the-as uint (-> arg0 addr)) 0)
(format arg1 " :addr #x~8x" (-> arg0 addr))
)
(if (> (the-as uint (-> arg0 qwc)) 0)
(format arg1 " :qwc ~d" (-> arg0 qwc))
)
(if (> (the-as uint (-> arg0 spr)) 0)
(format arg1 " :spr ~d" (-> arg0 spr))
)
(if (> (the-as uint (-> arg0 irq)) 0)
(format arg1 " :irq ~d" (-> arg0 irq))
)
(if (> (the-as uint (-> arg0 pce)) 0)
(format arg1 " :pce ~d" (-> arg0 pce))
)
(format arg1 ")~%")
(none)
)
;; definition for symbol *dma-disasm*, type symbol
(define *dma-disasm* #t)
;; definition for function disasm-dma-list
;; WARN: Check prologue - tricky store of a0
;; Used lq/sq
(defun
disasm-dma-list
((data dma-packet)
(mode symbol)
(verbose symbol)
(stream symbol)
(expected-size int)
)
(local-vars
(addr object)
(data-2 dma-packet)
(qwc int)
(ra-1 object)
(ra-2 object)
(call-depth int)
(current-tag dma-tag)
)
(set! data-2 data)
(let ((mode-2 mode)
(verbose-2 verbose)
(stream-2 stream)
(expected-size-2 expected-size)
)
(if verbose-2
(format stream-2 "~%--- ~X -----------------------------~%" data-2)
)
(let ((end-condition #f))
(let ((total-qwc 0)
(total-tags 0)
)
(set! addr 0)
(set! qwc 0)
(set! ra-1 0)
(set! ra-2 1)
(set! call-depth -1)
(set! current-tag (new 'static 'dma-tag))
(while (not end-condition)
(cond
((not
(valid? data-2 (the-as type #f) "dma-list tag pointer" #t stream-2)
)
(format stream-2 "ERROR: dma-list tag pointer invalid~%")
(set! end-condition 'error)
)
(else
(set! current-tag (-> data-2 dma))
(when
(not
(or
(zero? total-tags)
(valid? addr (the-as type #f) "dma-list data pointer" #t stream-2)
)
)
(format stream-2 "ERROR: dma-list data pointer invalid~%")
(set! end-condition 'error)
)
(when (nonzero? (logand #x3ff0000 (the-as int current-tag)))
(format stream-2 "ERROR: dma tag has data in reserved bits ~X~%")
(set! end-condition 'error)
)
)
)
(when (or verbose-2 (= end-condition 'error))
(format stream-2 "#x~8x: " data-2)
(cond
((zero? call-depth)
(format stream-2 " ")
)
((= call-depth 1)
(format stream-2 " ")
)
)
(disasm-dma-tag current-tag stream-2)
)
(if end-condition
(empty-form)
(cond
((or
(zero? (+ (the-as uint (-> current-tag id)) (the-as uint -3)))
(zero? (+ (the-as uint (-> current-tag id)) (the-as uint -4)))
(zero? (-> current-tag id))
)
(set! addr (-> current-tag addr))
(set! qwc (the-as int (-> current-tag qwc)))
(if mode-2
(let
((v0-9
(disasm-vif-tag (&-> data-2 vif0) 2 stream-2 (= mode-2 'details))
)
)
(disasm-vif-tag
(the-as (pointer vif-tag) (+ (the-as uint addr) (the-as uint v0-9)))
(the-as int (- (shl (the-as int qwc) 2) (the-as uint (sar v0-9 2))))
stream-2
(= mode-2 'details)
)
)
)
(set!
data-2
(the-as dma-packet (&-> (the-as (pointer uint64) data-2) 2))
)
(if (zero? (-> current-tag id))
(set! end-condition #t)
)
)
(else
(cond
((= (-> current-tag id) (dma-tag-id cnt))
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(if mode-2
(disasm-vif-tag
(the-as (pointer vif-tag) (&-> (the-as (pointer uint64) data-2) 1))
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
(set!
data-2
(the-as
dma-packet
(+
(the-as uint data-2)
(the-as
uint
(shl (the-as int (+ (the-as uint qwc) (the-as uint 1))) 4)
)
)
)
)
(let ((v1-68 data-2))
)
)
((= (-> current-tag id) (dma-tag-id next))
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(if mode-2
(disasm-vif-tag
(the-as (pointer vif-tag) (&-> (the-as (pointer uint64) data-2) 1))
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
(when (= data-2 (-> current-tag addr))
(format stream-2 "ERROR: next tag creates infinite loop.~%")
(set! end-condition 'error)
)
(set! data-2 (the-as dma-packet (-> current-tag addr)))
(let ((v1-88 data-2))
)
)
(else
(cond
((= (-> current-tag id) (dma-tag-id call))
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(if mode-2
(disasm-vif-tag
(the-as
(pointer vif-tag)
(&-> (the-as (pointer uint64) data-2) 1)
)
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
(set! data-2 (the-as dma-packet (-> current-tag addr)))
(set! call-depth (+ call-depth 1))
(cond
((zero? call-depth)
(set! ra-1 (&+ addr qwc))
(let ((v1-108 (the-as (pointer uint64) ra-1)))
)
)
(else
(set! ra-2 (&+ addr qwc))
(let ((v1-111 (the-as (pointer uint64) ra-2)))
)
)
)
)
((= (-> current-tag id) (dma-tag-id ret))
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(if mode-2
(disasm-vif-tag
(the-as
(pointer vif-tag)
(&-> (the-as (pointer uint64) data-2) 1)
)
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
(let ((v1-123 call-depth))
(cond
((zero? v1-123)
(set! data-2 (the-as dma-packet ra-1))
(let ((v1-125 data-2))
)
)
((= v1-123 1)
(set! data-2 (the-as dma-packet ra-2))
(let ((v1-127 data-2))
)
)
(else
(set! end-condition #t)
)
)
)
(set! call-depth (+ call-depth -1))
(let ((v1-131 call-depth))
)
)
((= (-> current-tag id) (dma-tag-id end))
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(set! end-condition #t)
(let ((v0-16 (if mode-2
(disasm-vif-tag
(the-as
(pointer vif-tag)
(&-> (the-as (pointer uint64) data-2) 1)
)
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
)
)
)
)
(else
(format stream-2 "ERROR: Unknown DMA TAG command.~%")
(set! end-condition 'error)
)
)
)
)
)
)
)
(+! total-qwc qwc)
(+! total-tags 1)
(if (and (>= expected-size-2 0) (>= total-tags expected-size-2))
(set! end-condition #t)
)
)
(when (or verbose-2 (= end-condition 'error))
(format stream-2 "NOTICE: Total tags: ~d~%" total-tags)
(format stream-2 "NOTICE: Total QWC: ~d~%" total-qwc)
(format stream-2 "--------------------------------~%~%")
)
)
(!= end-condition 'error)
)
)
)
)
+25 -13
View File
@@ -562,22 +562,34 @@
(>= (the-as uint 102) (-> next-char-2 0))
)
)
(set!
result
(if
(and
(>= (-> next-char-2 0) (the-as uint 65))
(>= (the-as uint 70) (-> next-char-2 0))
)
(+ (+ (-> next-char-2 0) -55) (the-as uint (shl result 4)))
(if
(and
(>= (-> next-char-2 0) (the-as uint 65))
(>= (the-as uint 70) (-> next-char-2 0))
)
(set!
result
(the-as
int
(if
(and
(>= (-> next-char-2 0) (the-as uint 97))
(>= (the-as uint 102) (-> next-char-2 0))
)
(+ (+ (-> next-char-2 0) -55) (the-as uint (shl result 4)))
)
)
(if
(and
(>= (-> next-char-2 0) (the-as uint 97))
(>= (the-as uint 102) (-> next-char-2 0))
)
(set!
result
(the-as
int
(+ (+ (-> next-char-2 0) -87) (the-as uint (shl result 4)))
)
)
(set!
result
(the-as
int
(+ (+ (-> next-char-2 0) -48) (the-as uint (shl result 4)))
)
)
+710
View File
@@ -0,0 +1,710 @@
#include "gtest/gtest.h"
#include "FormRegressionTest.h"
using namespace decompiler;
TEST_F(FormRegressionTest, ExprDisasmVif) {
std::string func =
" sll r0, r0, 0\n"
"L53:\n"
" daddiu sp, sp, -192\n"
" sd ra, 0(sp)\n"
" sd fp, 8(sp)\n"
" or fp, t9, r0\n"
" sq s0, 80(sp)\n"
" sq s1, 96(sp)\n"
" sq s2, 112(sp)\n"
" sq s3, 128(sp)\n"
" sq s4, 144(sp)\n"
" sq s5, 160(sp)\n"
" sq gp, 176(sp)\n"
" or s4, a0, r0\n"
" or s5, a1, r0\n"
" or s3, a2, r0\n"
" or s2, a3, r0\n"
" addiu gp, r0, 0\n"
" beq r0, r0, L72\n"
" sll r0, r0, 0\n"
"L54:\n"
" addiu s0, r0, 4\n"
" lwu s1, 0(s4)\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L139\n"
" or a2, s4, r0\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" addiu v1, r0, 0\n"
" beq r0, r0, L71\n"
" sll r0, r0, 0\n"
"L55:\n"
" dsll32 a0, s1, 1\n"
" dsrl32 a0, a0, 25\n"
" sq a0, 16(sp)\n"
" dsll a0, v1, 2\n"
" lw a1, *vif-disasm-table*(s7)\n"
" daddu a0, a0, a1\n"
" lwu a0, 12(a0)\n"
" lwu a0, 4(a0)\n"
" lq a1, 16(sp)\n"
" dsll a2, v1, 2\n"
" lw a3, *vif-disasm-table*(s7)\n"
" daddu a2, a2, a3\n"
" lwu a2, 12(a2)\n"
" lwu a2, 0(a2)\n"
" and a1, a1, a2\n"
" bne a1, a0, L70\n"
" or a0, s7, r0\n"
" dsll a0, v1, 2\n"
" lw a1, *vif-disasm-table*(s7)\n"
" daddu a0, a0, a1\n"
" lwu a0, 12(a0)\n"
" lwu a0, 12(a0)\n"
" bne a0, r0, L56\n"
" or a1, s7, r0\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L138\n"
" dsll v1, v1, 2\n"
" lw a2, *vif-disasm-table*(s7)\n"
" daddu v1, v1, a2\n"
" lwu v1, 12(v1)\n"
" lwu a2, 16(v1)\n"
" srl a3, s1, 31\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" beq r0, r0, L69\n"
" sll r0, r0, 0\n"
"L56:\n"
" addiu a1, r0, 1\n"
" bne a0, a1, L57\n"
" or a1, s7, r0\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L137\n"
" dsll a2, v1, 2\n"
" lw a3, *vif-disasm-table*(s7)\n"
" daddu a2, a2, a3\n"
" lwu a2, 12(a2)\n"
" lwu a2, 16(a2)\n"
" srl a3, s1, 31\n"
" dsll v1, v1, 2\n"
" lw t0, *vif-disasm-table*(s7)\n"
" daddu v1, v1, t0\n"
" lwu v1, 12(v1)\n"
" lwu t0, 20(v1)\n"
" dsll32 v1, s1, 16\n"
" dsrl32 t1, v1, 16\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" beq r0, r0, L69\n"
" sll r0, r0, 0\n"
"L57:\n"
" addiu a1, r0, 2\n"
" bne a0, a1, L58\n"
" or a1, s7, r0\n"
" dsll32 a0, s1, 16\n"
" dsrl32 t1, a0, 16\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L136\n"
" dsll v1, v1, 2\n"
" lw a2, *vif-disasm-table*(s7)\n"
" daddu v1, v1, a2\n"
" lwu v1, 12(v1)\n"
" lwu a2, 16(v1)\n"
" srl a3, s1, 31\n"
" dsll32 v1, t1, 16\n"
" dsrl32 t0, v1, 24\n"
" dsll32 v1, t1, 24\n"
" dsrl32 t1, v1, 24\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" beq r0, r0, L69\n"
" sll r0, r0, 0\n"
"L58:\n"
" addiu a1, r0, 3\n"
" bne a0, a1, L59\n"
" or a1, s7, r0\n"
" addiu s0, r0, 8\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L137\n"
" dsll a2, v1, 2\n"
" lw a3, *vif-disasm-table*(s7)\n"
" daddu a2, a2, a3\n"
" lwu a2, 12(a2)\n"
" lwu a2, 16(a2)\n"
" srl a3, s1, 31\n"
" dsll v1, v1, 2\n"
" lw t0, *vif-disasm-table*(s7)\n"
" daddu v1, v1, t0\n"
" lwu v1, 12(v1)\n"
" lwu t0, 20(v1)\n"
" lwu t1, 4(s4)\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" beq r0, r0, L69\n"
" sll r0, r0, 0\n"
"L59:\n"
" addiu a1, r0, 4\n"
" bne a0, a1, L60\n"
" or a1, s7, r0\n"
" addiu s0, r0, 20\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L135\n"
" dsll a2, v1, 2\n"
" lw a3, *vif-disasm-table*(s7)\n"
" daddu a2, a2, a3\n"
" lwu a2, 12(a2)\n"
" lwu a2, 16(a2)\n"
" srl a3, s1, 31\n"
" dsll v1, v1, 2\n"
" lw t0, *vif-disasm-table*(s7)\n"
" daddu v1, v1, t0\n"
" lwu v1, 12(v1)\n"
" lwu t0, 20(v1)\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L134\n"
" lwu a2, 4(s4)\n"
" lwu a3, 8(s4)\n"
" lwu t0, 12(s4)\n"
" lwu t1, 16(s4)\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" beq r0, r0, L69\n"
" sll r0, r0, 0\n"
"L60:\n"
" addiu a1, r0, 5\n"
" bne a0, a1, L61\n"
" or a1, s7, r0\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L133\n"
" dsll v1, v1, 2\n"
" lw a2, *vif-disasm-table*(s7)\n"
" daddu v1, v1, a2\n"
" lwu v1, 12(v1)\n"
" lwu a2, 16(v1)\n"
" srl a3, s1, 31\n"
" dsll32 v1, s1, 8\n"
" dsrl32 t0, v1, 24\n"
" dsll32 v1, s1, 16\n"
" dsrl32 t1, v1, 16\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" beq r0, r0, L69\n"
" sll r0, r0, 0\n"
"L61:\n"
" addiu a1, r0, 6\n"
" bne a0, a1, L66\n"
" or a1, s7, r0\n"
" dsll32 a0, s1, 16\n"
" dsrl32 a0, a0, 16\n"
" beq s7, a0, L62\n"
" sll r0, r0, 0\n"
" lui s0, 16\n"
" or a0, s0, r0\n"
" beq r0, r0, L63\n"
" sll r0, r0, 0\n"
"L62:\n"
" dsll32 a0, s1, 16\n"
" dsrl32 a0, a0, 16\n"
" dsll s0, a0, 4\n"
" or a0, s0, r0\n"
"L63:\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L132\n"
" dsll v1, v1, 2\n"
" lw a2, *vif-disasm-table*(s7)\n"
" daddu v1, v1, a2\n"
" lwu v1, 12(v1)\n"
" lwu a2, 16(v1)\n"
" srl a3, s1, 31\n"
" dsll32 v1, s1, 16\n"
" dsrl32 t0, v1, 16\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" daddiu v1, s4, 4\n"
" sq v1, 32(sp)\n"
" addiu v1, r0, 0\n"
" sq v1, 48(sp)\n"
" beq r0, r0, L65\n"
" sll r0, r0, 0\n"
"L64:\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L145\n"
" lq v1, 48(sp)\n"
" dsll v1, v1, 4\n"
" daddiu v1, v1, 4\n"
" daddu a2, v1, s4\n"
" lq v1, 32(sp)\n"
" lq a3, 48(sp)\n"
" dsll a3, a3, 2\n"
" dsll a3, a3, 2\n"
" daddu v1, v1, a3\n"
" lwu a3, 0(v1)\n"
" lq v1, 32(sp)\n"
" lq t0, 48(sp)\n"
" dsll t0, t0, 2\n"
" daddiu t0, t0, 1\n"
" dsll t0, t0, 2\n"
" daddu v1, v1, t0\n"
" lwu t0, 0(v1)\n"
" lq v1, 32(sp)\n"
" lq t1, 48(sp)\n"
" dsll t1, t1, 2\n"
" daddiu t1, t1, 2\n"
" dsll t1, t1, 2\n"
" daddu v1, v1, t1\n"
" lwu t1, 0(v1)\n"
" lq v1, 32(sp)\n"
" lq t2, 48(sp)\n"
" dsll t2, t2, 2\n"
" daddiu t2, t2, 3\n"
" dsll t2, t2, 2\n"
" daddu v1, v1, t2\n"
" lwu t2, 0(v1)\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" lq v1, 48(sp)\n"
" daddiu v1, v1, 1\n"
" sq v1, 48(sp)\n"
"L65:\n"
" lq v1, 48(sp)\n"
" dsll32 a0, s1, 16\n"
" dsrl32 a0, a0, 16\n"
" slt v1, v1, a0\n"
" bne v1, r0, L64\n"
" sll r0, r0, 0\n"
" or v1, s7, r0\n"
" or v1, s7, r0\n"
" or v0, v1, r0\n"
" beq r0, r0, L69\n"
" sll r0, r0, 0\n"
"L66:\n"
" addiu a1, r0, 7\n"
" bne a0, a1, L68\n"
" or a1, s7, r0\n"
" addiu a0, r0, -4\n"
" dsll a1, v1, 2\n"
" lw a2, *vif-disasm-table*(s7)\n"
" daddu a1, a1, a2\n"
" lwu a1, 12(a1)\n"
" lwu a1, 8(a1)\n"
" dsll32 a2, s1, 8\n"
" dsrl32 a2, a2, 24\n"
" multu3 a1, a1, a2\n"
" daddiu a1, a1, 3\n"
" and a0, a0, a1\n"
" daddiu s0, a0, 4\n"
" dsll32 a0, s1, 16\n"
" dsrl32 a0, a0, 16\n"
" sq a0, 64(sp)\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L131\n"
" dsll v1, v1, 2\n"
" lw a2, *vif-disasm-table*(s7)\n"
" daddu v1, v1, a2\n"
" lwu v1, 12(v1)\n"
" lwu a2, 16(v1)\n"
" srl a3, s1, 31\n"
" dsll32 v1, s1, 8\n"
" dsrl32 t0, v1, 24\n"
" lq v1, 64(sp)\n"
" dsll32 v1, v1, 22\n"
" dsrl32 t1, v1, 22\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L130\n"
" dsll32 v1, s1, 3\n"
" dsrl32 a2, v1, 31\n"
" lq v1, 64(sp)\n"
" dsll32 v1, v1, 16\n"
" dsrl32 a3, v1, 31\n"
" lq v1, 64(sp)\n"
" dsll32 v1, v1, 17\n"
" dsrl32 t0, v1, 31\n"
" or t1, s0, r0\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" beq s7, s2, L67\n"
" or v0, s7, r0\n"
" lw t9, disasm-vif-details(s7)\n"
" or a0, s3, r0\n"
" or a1, s4, r0\n"
" lq v1, 16(sp)\n"
" andi a2, v1, 239\n"
" dsll32 v1, s1, 8\n"
" dsrl32 a3, v1, 24\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
"L67:\n"
" or v1, v0, r0\n"
" or v0, v1, r0\n"
" beq r0, r0, L69\n"
" sll r0, r0, 0\n"
"L68:\n"
" addiu v1, r0, 8\n"
" bne a0, v1, L69\n"
" or v0, s7, r0\n"
" lw t9, format(s7)\n"
" or a0, s3, r0\n"
" daddiu a1, fp, L129\n"
" dsll32 v1, s1, 1\n"
" dsrl32 a2, v1, 25\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
"L69:\n"
" or v1, v0, r0\n"
" lw v1, *vif-disasm-table*(s7)\n"
" lw v1, 0(v1)\n"
" or a0, v1, r0\n"
"L70:\n"
" daddiu v1, v1, 1\n"
"L71:\n"
" lw a0, *vif-disasm-table*(s7)\n"
" lw a0, 0(a0)\n"
" slt a0, v1, a0\n"
" bne a0, r0, L55\n"
" sll r0, r0, 0\n"
" or v1, s7, r0\n"
" or v1, s7, r0\n"
" daddu gp, gp, s0\n"
" daddu s4, s4, s0\n"
" or v1, s4, r0\n"
"L72:\n"
" dsll v1, s5, 2\n"
" slt v1, gp, v1\n"
" bne v1, r0, L54\n"
" sll r0, r0, 0\n"
" or v1, s7, r0\n"
" dsll v1, s5, 2\n"
" dsubu v0, gp, v1\n"
" ld ra, 0(sp)\n"
" ld fp, 8(sp)\n"
" lq gp, 176(sp)\n"
" lq s5, 160(sp)\n"
" lq s4, 144(sp)\n"
" lq s3, 128(sp)\n"
" lq s2, 112(sp)\n"
" lq s1, 96(sp)\n"
" lq s0, 80(sp)\n"
" jr ra\n"
" daddiu sp, sp, 192";
std::string type = "(function (pointer vif-tag) int symbol symbol int)";
std::string expected =
"(let ((gp-0 0))\n"
" (while (< gp-0 (shl arg1 2))\n"
" (let ((s0-0 4))\n"
" (let ((s1-0 (-> arg0 0)))\n"
" (format arg2 \" #x~X:\" arg0)\n"
" (dotimes (v1-0 (-> *vif-disasm-table* length))\n"
" (set! sv-16 (-> s1-0 cmd))\n"
" (when\n"
" (=\n"
" (logand sv-16 (the-as uint (-> *vif-disasm-table* v1-0 mask)))\n"
" (-> *vif-disasm-table* v1-0 tag)\n"
" )\n"
" (let* ((a0-12 (-> *vif-disasm-table* v1-0 print))\n"
" (v0-1 (cond\n"
" ((zero? a0-12)\n"
" (format\n"
" arg2\n"
" \" (~s :irq ~D)~%\"\n"
" (-> *vif-disasm-table* v1-0 string1)\n"
" (-> s1-0 irq)\n"
" )\n"
" )\n"
" ((= a0-12 1)\n"
" (format\n"
" arg2\n"
" \" (~s :irq ~D :~s #x~X)~%\"\n"
" (-> *vif-disasm-table* v1-0 string1)\n"
" (-> s1-0 irq)\n"
" (-> *vif-disasm-table* v1-0 string2)\n"
" (-> s1-0 imm)\n"
" )\n"
" )\n"
" ((= a0-12 2)\n"
" (let ((t1-1 (-> s1-0 imm)))\n"
" (format\n"
" arg2\n"
" \" (~s :irq ~D :wl ~D :cl ~D)~%\"\n"
" (-> *vif-disasm-table* v1-0 string1)\n"
" (-> s1-0 irq)\n"
" (shr (shl (the-as int t1-1) 48) 56)\n"
" (shr (shl (the-as int t1-1) 56) 56)\n"
" )\n"
" )\n"
" )\n"
" ((= a0-12 3)\n"
" (set! s0-0 8)\n"
" (format\n"
" arg2\n"
" \" (~s :irq ~D :~s #x~X)~%\"\n"
" (-> *vif-disasm-table* v1-0 string1)\n"
" (-> s1-0 irq)\n"
" (-> *vif-disasm-table* v1-0 string2)\n"
" (-> arg0 1)\n"
" )\n"
" )\n"
" ((= a0-12 4)\n"
" (set! s0-0 20)\n"
" (format\n"
" arg2\n"
" \" (~s :irq ~D :~s \"\n"
" (-> *vif-disasm-table* v1-0 string1)\n"
" (-> s1-0 irq)\n"
" (-> *vif-disasm-table* v1-0 string2)\n"
" )\n"
" (format\n"
" arg2\n"
" \"#x~X #x~X #x~X #x~X)~%\"\n"
" (-> arg0 1)\n"
" (-> arg0 2)\n"
" (-> arg0 3)\n"
" (-> arg0 4)\n"
" )\n"
" )\n"
" ((= a0-12 5)\n"
" (format\n"
" arg2\n"
" \" (~s :irq ~D :instructions #x~D :addr #x~X)~%\"\n"
" (-> *vif-disasm-table* v1-0 string1)\n"
" (-> s1-0 irq)\n"
" (-> s1-0 num)\n"
" (-> s1-0 imm)\n"
" )\n"
" )\n"
" ((= a0-12 6)\n"
" (if (-> s1-0 imm)\n"
" (set! s0-0 #x100000)\n"
" (set!\n"
" s0-0\n"
" (the-as int (shl (the-as int (-> s1-0 imm)) 4))\n"
" )\n"
" )\n"
" (format\n"
" arg2\n"
" \" (~s :irq ~D :qwc #x~D)~%\"\n"
" (-> *vif-disasm-table* v1-0 string1)\n"
" (-> s1-0 irq)\n"
" (-> s1-0 imm)\n"
" )\n"
" (set! sv-32 (&-> arg0 1))\n"
" (set! sv-48 0)\n"
" (while (< sv-48 (the-as int (-> s1-0 imm)))\n"
" (format\n"
" arg2\n"
" \" #x~X: #x~8x #x~8x #x~8x #x~8x~%\"\n"
" (+ (+ (shl sv-48 4) 4) (the-as int arg0))\n"
" (-> sv-32 (shl sv-48 2))\n"
" (-> sv-32 (+ (shl sv-48 2) 1))\n"
" (-> sv-32 (+ (shl sv-48 2) 2))\n"
" (-> sv-32 (+ (shl sv-48 2) 3))\n"
" )\n"
" (set! sv-48 (+ sv-48 1))\n"
" )\n"
" #f\n"
" )\n"
" ((= a0-12 7)\n"
" (set!\n"
" s0-0\n"
" (the-as\n"
" int\n"
" (+\n"
" (logand\n"
" -4\n"
" (the-as\n"
" int\n"
" (+\n"
" (*\n"
" (-> *vif-disasm-table* v1-0 val)\n"
" (the-as uint (-> s1-0 num))\n"
" )\n"
" 3\n"
" )\n"
" )\n"
" )\n"
" 4\n"
" )\n"
" )\n"
" )\n"
" (set! sv-64 (-> s1-0 imm))\n"
" (format\n"
" arg2\n"
" \" (~s :irq ~D :num ~D :addr #x~X \"\n"
" (-> *vif-disasm-table* v1-0 string1)\n"
" (-> s1-0 irq)\n"
" (-> s1-0 num)\n"
" (shr (shl (the-as int sv-64) 54) 54)\n"
" )\n"
" (format\n"
" arg2\n"
" \":msk ~D :flg ~D :usn ~D [skip ~d])~%\"\n"
" (-> s1-0 msk)\n"
" (shr (shl (the-as int sv-64) 48) 63)\n"
" (shr (shl (the-as int sv-64) 49) 63)\n"
" (the-as uint s0-0)\n"
" )\n"
" (if arg3\n"
" (disasm-vif-details\n"
" arg2\n"
" (the-as (pointer uint8) arg0)\n"
" (logand sv-16 (vif-cmd cmd-mask))\n"
" (the-as int (-> s1-0 num))\n"
" )\n"
" )\n"
" )\n"
" ((= a0-12 8)\n"
" (format arg2 \" (*unknown* vif-tag #x~X)~%\" (-> s1-0 cmd))\n"
" )\n"
" )\n"
" )\n"
" )\n"
" )\n"
" (set! v1-0 (-> *vif-disasm-table* length))\n"
" )\n"
" )\n"
" )\n"
" (+! gp-0 s0-0)\n"
" (&+! arg0 s0-0)\n"
" )\n"
" )\n"
" (- gp-0 (shl arg1 2))\n"
" )";
test_with_expr(func, type, expected, false, "",
{{"L139", " #x~X:"},
{"L138", " (~s :irq ~D)~%"},
{"L137", " (~s :irq ~D :~s #x~X)~%"},
{"L136", " (~s :irq ~D :wl ~D :cl ~D)~%"},
{"L135", " (~s :irq ~D :~s "},
{"L134", "#x~X #x~X #x~X #x~X)~%"},
{"L133", " (~s :irq ~D :instructions #x~D :addr #x~X)~%"},
{"L132", " (~s :irq ~D :qwc #x~D)~%"},
{"L145", " #x~X: #x~8x #x~8x #x~8x #x~8x~%"},
{"L131", " (~s :irq ~D :num ~D :addr #x~X "},
{"L130", ":msk ~D :flg ~D :usn ~D [skip ~d])~%"},
{"L129", " (*unknown* vif-tag #x~X)~%"}});
}
@@ -2373,24 +2373,27 @@ TEST_F(FormRegressionTest, ExprStringToInt) {
" (>= (the-as uint 102) (-> a0-3 0))\n"
" )\n"
" )\n"
" (set!\n"
" v0-0\n"
" (if\n"
" (and\n"
" (>= (-> a0-3 0) (the-as uint 65))\n"
" (>= (the-as uint 70) (-> a0-3 0))\n"
" )\n"
" (set!\n"
" v0-0\n"
" (the-as int (+ (+ (-> a0-3 0) -55) (the-as uint (shl v0-0 4))))\n"
" )\n"
" (if\n"
" (and\n"
" (>= (-> a0-3 0) (the-as uint 65))\n"
" (>= (the-as uint 70) (-> a0-3 0))\n"
" (>= (-> a0-3 0) (the-as uint 97))\n"
" (>= (the-as uint 102) (-> a0-3 0))\n"
" )\n"
" (+ (+ (-> a0-3 0) -55) (the-as uint (shl v0-0 4)))\n"
" (the-as\n"
" int\n"
" (if\n"
" (and\n"
" (>= (-> a0-3 0) (the-as uint 97))\n"
" (>= (the-as uint 102) (-> a0-3 0))\n"
" )\n"
" (+ (+ (-> a0-3 0) -87) (the-as uint (shl v0-0 4)))\n"
" (+ (+ (-> a0-3 0) -48) (the-as uint (shl v0-0 4)))\n"
" )\n"
" (set!\n"
" v0-0\n"
" (the-as int (+ (+ (-> a0-3 0) -87) (the-as uint (shl v0-0 4))))\n"
" )\n"
" (set!\n"
" v0-0\n"
" (the-as int (+ (+ (-> a0-3 0) -48) (the-as uint (shl v0-0 4))))\n"
" )\n"
" )\n"
" )\n"
+8 -5
View File
@@ -17,7 +17,7 @@ const std::unordered_set<std::string> g_object_files_to_decompile = {
"trigonometry-h", /* transformq-h */ "matrix", "transform", "quaternion",
"euler", /* geometry, trigonometry, */
"gsound-h", "timer-h", "timer", "vif-h", "dma-h", "video-h", "vu1-user-h", "dma", "dma-buffer",
"dma-bucket",
"dma-bucket", "dma-disasm",
/* gap */
"bounding-box",
/* gap */
@@ -32,7 +32,7 @@ const std::vector<std::string> g_object_files_to_check_against_reference = {
/* transformq-h, */
"matrix", "transform", "quaternion", "euler", /* geometry, trigonometry */
"gsound-h", "timer-h", /* timer, */ "vif-h", "dma-h", "video-h", "vu1-user-h", "dma",
"dma-buffer", "dma-bucket",
"dma-buffer", "dma-bucket", "dma-disasm",
/* gap */ "bounding-box",
/* gap */
"sync-info-h", "sync-info"};
@@ -117,6 +117,9 @@ const std::unordered_set<std::string> skip_in_compiling = {
"(method 3 profile-frame)", // double definition.
// dma-disasm
"disasm-dma-list",
// sync-info
"(method 15 sync-info)", // needs display stuff first
"(method 15 sync-info-eased)", // needs display stuff first
@@ -389,9 +392,9 @@ TEST_F(OfflineDecompilation, Reference) {
std::string src = db->ir2_final_out(obj_l.at(0));
// if (file == "gstate") {
// fmt::print("{}\n", src);
// }
/* if (file == "gstring") {
fmt::print("{}\n", src);
}*/
auto reference = file_util::read_text_file(file_util::get_file_path(
{"test", "decompiler", "reference", fmt::format("{}_REF.gc", file)}));