mirror of
https://github.com/open-goal/jak-project
synced 2026-06-22 17:13:01 -04:00
[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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
////////////////////////////////
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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"]
|
||||
},
|
||||
|
||||
@@ -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) == '~') {
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
@@ -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)))
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)}));
|
||||
|
||||
Reference in New Issue
Block a user