mirror of
https://github.com/open-goal/jak-project
synced 2026-06-01 01:40:07 -04:00
more fixes
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include <stdexcept>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "AtomicOp.h"
|
||||
#include "Form.h"
|
||||
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
@@ -546,6 +547,16 @@ FunctionVariableDefinitions Env::local_var_type_list(const Form* top_level_form,
|
||||
int nargs_to_ignore) const {
|
||||
ASSERT(nargs_to_ignore <= 8);
|
||||
auto vars = extract_visible_variables(top_level_form);
|
||||
std::unordered_set<int> visible_stack_slots;
|
||||
if (top_level_form) {
|
||||
RegAccessSet var_set;
|
||||
top_level_form->collect_vars(var_set, true);
|
||||
for (const auto& var : var_set) {
|
||||
if (is_stack_slot_access(var)) {
|
||||
visible_stack_slots.insert(get_stack_slot_offset_from_access(var));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FunctionVariableDefinitions result;
|
||||
std::vector<goos::Object> elts;
|
||||
@@ -580,6 +591,9 @@ FunctionVariableDefinitions Env::local_var_type_list(const Form* top_level_form,
|
||||
// it looks like this is the order the GOAL compiler itself used.
|
||||
std::vector<StackSpillEntry> spills;
|
||||
for (auto& x : stack_slot_entries) {
|
||||
if (top_level_form && !visible_stack_slots.count(x.first)) {
|
||||
continue;
|
||||
}
|
||||
spills.push_back(x.second);
|
||||
}
|
||||
std::sort(spills.begin(), spills.end(),
|
||||
@@ -608,17 +622,32 @@ std::unordered_set<RegId, RegId::hash> Env::get_ssa_var(const RegAccessSet& vars
|
||||
}
|
||||
|
||||
RegId Env::get_program_var_id(const RegisterAccess& var) const {
|
||||
if (is_stack_slot_access(var)) {
|
||||
return RegId(Register(Reg::GPR, Reg::SP), get_stack_slot_offset_from_access(var));
|
||||
}
|
||||
return m_var_names.lookup(var.reg(), var.idx(), var.mode()).reg_id;
|
||||
}
|
||||
|
||||
const UseDefInfo& Env::get_use_def_info(const RegisterAccess& ra) const {
|
||||
ASSERT(has_local_vars());
|
||||
if (is_stack_slot_access(ra)) {
|
||||
return m_stack_slot_use_def_info.at(get_program_var_id(ra));
|
||||
}
|
||||
auto var_id = get_program_var_id(ra);
|
||||
return m_var_names.use_def_info.at(var_id);
|
||||
}
|
||||
|
||||
void Env::disable_def(const RegisterAccess& access, DecompWarnings& warnings) {
|
||||
if (is_stack_slot_access(access)) {
|
||||
auto& info = m_stack_slot_use_def_info.at(get_program_var_id(access));
|
||||
for (auto& def : info.defs) {
|
||||
if (!def.disabled) {
|
||||
def.disabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
warnings.warning("disable stack def twice: {}",
|
||||
get_spill_slot_var_name(get_stack_slot_offset_from_access(access)));
|
||||
return;
|
||||
}
|
||||
if (has_local_vars()) {
|
||||
@@ -628,6 +657,16 @@ void Env::disable_def(const RegisterAccess& access, DecompWarnings& warnings) {
|
||||
|
||||
void Env::disable_use(const RegisterAccess& access) {
|
||||
if (is_stack_slot_access(access)) {
|
||||
auto& info = m_stack_slot_use_def_info.at(get_program_var_id(access));
|
||||
for (auto& use : info.uses) {
|
||||
if (!use.disabled) {
|
||||
use.disabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(
|
||||
fmt::format("Invalid disable use on stack slot {}",
|
||||
get_spill_slot_var_name(get_stack_slot_offset_from_access(access))));
|
||||
return;
|
||||
}
|
||||
if (has_local_vars()) {
|
||||
@@ -635,6 +674,39 @@ void Env::disable_use(const RegisterAccess& access) {
|
||||
}
|
||||
}
|
||||
|
||||
void Env::rebuild_stack_slot_use_def_info() {
|
||||
m_stack_slot_use_def_info.clear();
|
||||
|
||||
if (!func || !func->ir2.atomic_ops) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& ops = *func->ir2.atomic_ops;
|
||||
std::vector<int> op_to_block(ops.ops.size(), -1);
|
||||
for (int block_id = 0; block_id < (int)ops.block_id_to_first_atomic_op.size(); block_id++) {
|
||||
for (int op_id = ops.block_id_to_first_atomic_op.at(block_id);
|
||||
op_id < ops.block_id_to_end_atomic_op.at(block_id); op_id++) {
|
||||
op_to_block.at(op_id) = block_id;
|
||||
}
|
||||
}
|
||||
|
||||
for (int op_id = 0; op_id < (int)ops.ops.size(); op_id++) {
|
||||
const auto* op = ops.ops.at(op_id).get();
|
||||
if (auto* store = dynamic_cast<const StackSpillStoreOp*>(op)) {
|
||||
auto var_id = RegId(Register(Reg::GPR, Reg::SP), store->offset());
|
||||
auto& info = m_stack_slot_use_def_info[var_id];
|
||||
info.defs.push_back({op_id, op_to_block.at(op_id), AccessMode::WRITE, false});
|
||||
info.ssa_vars.insert(store->offset());
|
||||
}
|
||||
if (auto* load = dynamic_cast<const StackSpillLoadOp*>(op)) {
|
||||
auto var_id = RegId(Register(Reg::GPR, Reg::SP), load->offset());
|
||||
auto& info = m_stack_slot_use_def_info[var_id];
|
||||
info.uses.push_back({op_id, op_to_block.at(op_id), AccessMode::READ, false});
|
||||
info.ssa_vars.insert(load->offset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the stack hints. This must be done before type analysis.
|
||||
* This actually parses the types, so it should be done after the dts is set up.
|
||||
|
||||
@@ -146,6 +146,7 @@ class Env {
|
||||
void set_local_vars(const VariableNames& names) {
|
||||
m_var_names = names;
|
||||
m_has_local_vars = true;
|
||||
rebuild_stack_slot_use_def_info();
|
||||
}
|
||||
|
||||
void set_end_var(RegisterAccess var) { m_end_var = var; }
|
||||
@@ -248,6 +249,8 @@ class Env {
|
||||
bool pp_mapped_by_behavior() const { return m_pp_mapped_by_behavior; }
|
||||
|
||||
private:
|
||||
void rebuild_stack_slot_use_def_info();
|
||||
|
||||
RegisterAccess m_end_var;
|
||||
|
||||
bool m_has_reg_use = false;
|
||||
@@ -255,6 +258,7 @@ class Env {
|
||||
|
||||
bool m_has_local_vars = false;
|
||||
VariableNames m_var_names;
|
||||
std::unordered_map<RegId, UseDefInfo, RegId::hash> m_stack_slot_use_def_info;
|
||||
|
||||
bool m_has_types = false;
|
||||
bool m_pp_mapped_by_behavior = false;
|
||||
|
||||
@@ -1568,6 +1568,7 @@ class StackSpillValueElement : public FormElement {
|
||||
int stack_offset,
|
||||
bool is_signed,
|
||||
std::optional<TypeSpec> read_type = std::nullopt);
|
||||
int stack_offset() const { return m_stack_offset; }
|
||||
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;
|
||||
|
||||
@@ -87,6 +87,35 @@ Form* cast_stack_slot_var_if_needed(Form* in,
|
||||
return in;
|
||||
}
|
||||
|
||||
Form* cast_inlined_function_symbol_if_needed(Form* in,
|
||||
const TypeSpec& desired_type,
|
||||
FormPool& pool,
|
||||
const Env& env) {
|
||||
if (!env.has_type_analysis() || desired_type.base_type() != "function") {
|
||||
return in;
|
||||
}
|
||||
|
||||
auto existing_cast = in->try_as_element<CastElement>();
|
||||
if (existing_cast && existing_cast->type() == desired_type) {
|
||||
return in;
|
||||
}
|
||||
|
||||
auto obj = in->to_form(env);
|
||||
if (!obj.is_symbol()) {
|
||||
return in;
|
||||
}
|
||||
|
||||
try {
|
||||
auto symbol_type = env.dts->lookup_symbol_type(obj.as_symbol().name_ptr);
|
||||
if (symbol_type.base_type() == "function" && symbol_type != desired_type) {
|
||||
return pool.form<CastElement>(desired_type, in);
|
||||
}
|
||||
} catch (const std::runtime_error&) {
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
Form* strip_pcypld_64(Form* in) {
|
||||
auto m = match(Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::PCPYLD),
|
||||
{Matcher::integer(0), Matcher::any(0)}),
|
||||
@@ -402,19 +431,35 @@ void pop_helper(const std::vector<RegisterAccess>& vars,
|
||||
const std::optional<RegSet>& consumes = std::nullopt,
|
||||
const std::vector<int>& times_used = {}) {
|
||||
// to submit to stack to attempt popping
|
||||
std::vector<Register> submit_regs;
|
||||
// submit_reg[i] is for var submit_reg_to_var[i]
|
||||
std::vector<size_t> submit_reg_to_var;
|
||||
std::vector<RegisterAccess> submit_vars;
|
||||
// submit_vars[i] is for var submit_var_to_var[i]
|
||||
std::vector<size_t> submit_var_to_var;
|
||||
|
||||
// build submission for stack
|
||||
std::unordered_map<Register, int, Register::hash> reg_counts;
|
||||
std::unordered_map<std::string, int> stack_var_counts;
|
||||
for (auto& v : vars) {
|
||||
reg_counts[v.reg()]++;
|
||||
if (is_stack_slot_access(v)) {
|
||||
stack_var_counts[env.get_variable_name_name_only(v)]++;
|
||||
} else {
|
||||
reg_counts[v.reg()]++;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t var_idx = 0; var_idx < vars.size(); var_idx++) {
|
||||
const auto& var = vars.at(var_idx);
|
||||
if (is_stack_slot_access(var)) {
|
||||
int times = 1;
|
||||
if (!times_used.empty()) {
|
||||
times = times_used.at(var_idx);
|
||||
}
|
||||
|
||||
auto& use_def = env.get_use_def_info(var);
|
||||
if (stack_var_counts.at(env.get_variable_name_name_only(var)) == 1 &&
|
||||
use_def.use_count() == times && use_def.def_count() == 1) {
|
||||
submit_var_to_var.push_back(var_idx);
|
||||
submit_vars.push_back(var);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
auto& ri = env.reg_use().op.at(var.idx());
|
||||
@@ -430,8 +475,8 @@ void pop_helper(const std::vector<RegisterAccess>& vars,
|
||||
|
||||
auto& use_def = env.get_use_def_info(var);
|
||||
if (use_def.use_count() == times && use_def.def_count() == 1) {
|
||||
submit_reg_to_var.push_back(var_idx);
|
||||
submit_regs.push_back(var.reg());
|
||||
submit_var_to_var.push_back(var_idx);
|
||||
submit_vars.push_back(var);
|
||||
} else {
|
||||
// auto var_id = env.get_program_var_id(var);
|
||||
// lg::print(
|
||||
@@ -457,9 +502,9 @@ void pop_helper(const std::vector<RegisterAccess>& vars,
|
||||
// submit and get a result! If the stack has nothing to pop, the result here may be nullptr.
|
||||
std::vector<Form*> pop_result;
|
||||
// loop in reverse (later vals first)
|
||||
for (size_t i = submit_regs.size(); i-- > 0;) {
|
||||
for (size_t i = submit_vars.size(); i-- > 0;) {
|
||||
// figure out what var we are:
|
||||
auto var_idx = submit_reg_to_var.at(i);
|
||||
auto var_idx = submit_var_to_var.at(i);
|
||||
|
||||
// anything _less_ than this should be unmodified by the pop
|
||||
// it's fine to modify yourself in your pop.
|
||||
@@ -470,7 +515,7 @@ void pop_helper(const std::vector<RegisterAccess>& vars,
|
||||
|
||||
// do the pop, with the barrier to prevent out-of-sequence popping.
|
||||
pop_result.push_back(
|
||||
stack.pop_reg(submit_regs.at(i), pop_barrier_regs, env, allow_side_effects));
|
||||
stack.pop_reg(submit_vars.at(i), pop_barrier_regs, env, allow_side_effects));
|
||||
}
|
||||
// now flip back to the source order for making the final result
|
||||
std::reverse(pop_result.begin(), pop_result.end());
|
||||
@@ -480,9 +525,9 @@ void pop_helper(const std::vector<RegisterAccess>& vars,
|
||||
forms.resize(vars.size(), nullptr);
|
||||
if (!pop_result.empty()) {
|
||||
// success!
|
||||
for (size_t i = 0; i < submit_regs.size(); i++) {
|
||||
for (size_t i = 0; i < submit_vars.size(); i++) {
|
||||
// fill out vars from our submission
|
||||
forms.at(submit_reg_to_var.at(i)) = pop_result.at(i);
|
||||
forms.at(submit_var_to_var.at(i)) = pop_result.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2671,6 +2716,13 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
|
||||
// if we are a reg-reg move that consumes the original, push it without popping from stack.
|
||||
// it is the Stack's responsibility to untangle these later on.
|
||||
if (m_src->is_single_element()) {
|
||||
auto spill_value = dynamic_cast<StackSpillValueElement*>(m_src->back());
|
||||
if (spill_value) {
|
||||
stack.push_non_seq_reg_to_reg(m_dst, make_stack_slot_access(spill_value->stack_offset()),
|
||||
m_src, m_src_type, m_var_info);
|
||||
return;
|
||||
}
|
||||
|
||||
auto src_as_se = dynamic_cast<SimpleExpressionElement*>(m_src->back());
|
||||
if (src_as_se) {
|
||||
if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY &&
|
||||
@@ -2683,7 +2735,10 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
|
||||
|
||||
auto var = src_as_se->expr().get_arg(0).var();
|
||||
bool is_consumed_reg_move = false;
|
||||
if (!is_stack_slot_access(var)) {
|
||||
if (is_stack_slot_access(var)) {
|
||||
auto& use_def = env.get_use_def_info(var);
|
||||
is_consumed_reg_move = use_def.use_count() == 1 && use_def.def_count() == 1;
|
||||
} else {
|
||||
auto& info = env.reg_use().op.at(var.idx());
|
||||
is_consumed_reg_move = info.consumes.find(var.reg()) != info.consumes.end();
|
||||
}
|
||||
@@ -4023,7 +4078,9 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
}
|
||||
}
|
||||
|
||||
new_form = pool.alloc_element<GenericElement>(GenericOperator::make_function(unstacked.at(0)),
|
||||
auto called_function =
|
||||
cast_inlined_function_symbol_if_needed(unstacked.at(0), function_type, pool, env);
|
||||
new_form = pool.alloc_element<GenericElement>(GenericOperator::make_function(called_function),
|
||||
arg_forms);
|
||||
|
||||
{
|
||||
@@ -4085,7 +4142,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
if (match_result.matched) {
|
||||
auto& alloc = match_result.maps.strings.at(allocation);
|
||||
if (alloc != "global" && alloc != "debug" && alloc != "process" &&
|
||||
alloc != "loading-level") {
|
||||
alloc != "loading-level" && alloc != "process-level-heap") {
|
||||
throw std::runtime_error("Unrecognized heap symbol for new: " + alloc);
|
||||
}
|
||||
auto type_2 = match_result.maps.strings.at(type_for_arg);
|
||||
@@ -6987,6 +7044,40 @@ void StackSpillStoreElement::push_to_stack(const Env& env, FormPool& pool, FormS
|
||||
stack_type = *m_cast_type;
|
||||
}
|
||||
|
||||
auto src_as_generic = src->try_as_element<GenericElement>();
|
||||
if (src_as_generic && !src_as_generic->op().is_func()) {
|
||||
using InplaceOpInfo = std::pair<FixedOperatorKind, FixedOperatorKind>;
|
||||
const static std::array<InplaceOpInfo, 6> in_place_ops = {
|
||||
InplaceOpInfo{FixedOperatorKind::ADDITION, FixedOperatorKind::ADDITION_IN_PLACE},
|
||||
InplaceOpInfo{FixedOperatorKind::ADDITION_PTR, FixedOperatorKind::ADDITION_PTR_IN_PLACE},
|
||||
InplaceOpInfo{FixedOperatorKind::LOGAND, FixedOperatorKind::LOGAND_IN_PLACE},
|
||||
InplaceOpInfo{FixedOperatorKind::LOGIOR, FixedOperatorKind::LOGIOR_IN_PLACE},
|
||||
InplaceOpInfo{FixedOperatorKind::LOGCLEAR, FixedOperatorKind::LOGCLEAR_IN_PLACE},
|
||||
InplaceOpInfo{FixedOperatorKind::LOGXOR, FixedOperatorKind::LOGXOR_IN_PLACE},
|
||||
};
|
||||
|
||||
auto dst_var = make_stack_slot_access(m_stack_offset);
|
||||
auto dst_form = pool.form<SimpleAtomElement>(SimpleAtom::make_var(dst_var))->to_form(env);
|
||||
for (const auto& [kind, inplace_kind] : in_place_ops) {
|
||||
if (!src_as_generic->op().is_fixed(kind)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int inplace_arg : {0, 1}) {
|
||||
if (src_as_generic->elts().at(inplace_arg)->to_form(env) != dst_form) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inplace_arg != 0) {
|
||||
std::swap(src_as_generic->elts().at(0), src_as_generic->elts().at(1));
|
||||
}
|
||||
src_as_generic->op() = GenericOperator::make_fixed(inplace_kind);
|
||||
stack.push_form_element(src_as_generic, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stack.push_value_to_reg(make_stack_slot_access(m_stack_offset), src, true, stack_type);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,17 @@
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
|
||||
namespace decompiler {
|
||||
namespace {
|
||||
bool nonempty_intersection(const RegSet& a, const RegSet& b) {
|
||||
for (auto x : a) {
|
||||
if (b.find(x) != b.end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string FormStack::StackEntry::print(const Env& env) const {
|
||||
if (destination.has_value()) {
|
||||
ASSERT(source && !elt);
|
||||
@@ -116,19 +127,59 @@ Form* FormStack::pop_reg(const RegisterAccess& var,
|
||||
const Env& env,
|
||||
bool allow_side_effects,
|
||||
int begin_idx) {
|
||||
return pop_reg(var.reg(), barrier, env, allow_side_effects, begin_idx);
|
||||
}
|
||||
RegSet modified;
|
||||
size_t begin = m_stack.size();
|
||||
if (begin_idx >= 0) {
|
||||
begin = begin_idx;
|
||||
}
|
||||
for (size_t i = begin; i-- > 0;) {
|
||||
auto& entry = m_stack.at(i);
|
||||
if (entry.active) {
|
||||
if (entry.destination.has_value() && same_expression_var(*entry.destination, var)) {
|
||||
entry.source->get_modified_regs(modified);
|
||||
if (!allow_side_effects && entry.source->has_side_effects()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (nonempty_intersection(modified, barrier)) {
|
||||
return nullptr;
|
||||
}
|
||||
entry.active = false;
|
||||
ASSERT(entry.source);
|
||||
if (entry.non_seq_source.has_value()) {
|
||||
ASSERT(entry.sequence_point == false);
|
||||
auto result = pop_reg(*entry.non_seq_source, barrier, env, allow_side_effects, i);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool nonempty_intersection(const RegSet& a, const RegSet& b) {
|
||||
for (auto x : a) {
|
||||
if (b.find(x) != b.end()) {
|
||||
return true;
|
||||
return entry.source;
|
||||
} else {
|
||||
if (entry.sequence_point) {
|
||||
return nullptr;
|
||||
}
|
||||
if (entry.source) {
|
||||
ASSERT(!entry.elt);
|
||||
entry.source->get_modified_regs(modified);
|
||||
if (!allow_side_effects) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
ASSERT(entry.elt);
|
||||
entry.elt->get_modified_regs(modified);
|
||||
if (!allow_side_effects && entry.elt->has_side_effects()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (entry.destination.has_value() && same_expression_var(*entry.destination, var)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Form* FormStack::pop_reg(Register reg,
|
||||
const RegSet& barrier,
|
||||
|
||||
@@ -515,172 +515,143 @@ TEST_F(FormRegressionTestJak1, ExprDisasmVif) {
|
||||
" 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 (* arg1 4))\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 (-> *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"
|
||||
" (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 t1-1 48) 56)\n"
|
||||
" (shr (shl 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! s0-0 (the-as int (* (-> s1-0 imm) 16)))\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"
|
||||
" (+ (+ (* sv-48 16) 4) (the-as int arg0))\n"
|
||||
" (-> sv-32 (* sv-48 4))\n"
|
||||
" (-> sv-32 (+ (* sv-48 4) 1))\n"
|
||||
" (-> sv-32 (+ (* sv-48 4) 2))\n"
|
||||
" (-> sv-32 (+ (* sv-48 4) 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"
|
||||
" (+ (* (-> *vif-disasm-table* v1-0 val) (-> s1-0 num)) 3)\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 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 sv-64 48) 63)\n"
|
||||
" (shr (shl 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"
|
||||
" (set! v1-0 (-> *vif-disasm-table* length))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (+! gp-0 s0-0)\n"
|
||||
" (&+! arg0 s0-0)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (- gp-0 (* arg1 4))\n"
|
||||
" )";
|
||||
R"((let ((gp-0 0))
|
||||
(while (< gp-0 (* arg1 4))
|
||||
(let ((s0-0 4))
|
||||
(let ((s1-0 (-> arg0 0)))
|
||||
(format arg2 " #x~X:" arg0)
|
||||
(dotimes (v1-0 (-> *vif-disasm-table* length))
|
||||
(let ((sv-16 (-> s1-0 cmd)))
|
||||
(when (= (logand sv-16 (-> *vif-disasm-table* v1-0 mask)) (-> *vif-disasm-table* v1-0 tag))
|
||||
(let ((a0-12 (-> *vif-disasm-table* v1-0 print)))
|
||||
(cond
|
||||
((zero? a0-12)
|
||||
(format arg2 " (~s :irq ~D)~%" (-> *vif-disasm-table* v1-0 string1) (-> s1-0 irq))
|
||||
)
|
||||
((= a0-12 1)
|
||||
(format
|
||||
arg2
|
||||
" (~s :irq ~D :~s #x~X)~%"
|
||||
(-> *vif-disasm-table* v1-0 string1)
|
||||
(-> s1-0 irq)
|
||||
(-> *vif-disasm-table* v1-0 string2)
|
||||
(-> s1-0 imm)
|
||||
)
|
||||
)
|
||||
((= a0-12 2)
|
||||
(let ((t1-1 (-> s1-0 imm)))
|
||||
(format
|
||||
arg2
|
||||
" (~s :irq ~D :wl ~D :cl ~D)~%"
|
||||
(-> *vif-disasm-table* v1-0 string1)
|
||||
(-> s1-0 irq)
|
||||
(shr (shl t1-1 48) 56)
|
||||
(shr (shl t1-1 56) 56)
|
||||
)
|
||||
)
|
||||
)
|
||||
((= a0-12 3)
|
||||
(set! s0-0 8)
|
||||
(format
|
||||
arg2
|
||||
" (~s :irq ~D :~s #x~X)~%"
|
||||
(-> *vif-disasm-table* v1-0 string1)
|
||||
(-> s1-0 irq)
|
||||
(-> *vif-disasm-table* v1-0 string2)
|
||||
(-> arg0 1)
|
||||
)
|
||||
)
|
||||
((= a0-12 4)
|
||||
(set! s0-0 20)
|
||||
(format
|
||||
arg2
|
||||
" (~s :irq ~D :~s "
|
||||
(-> *vif-disasm-table* v1-0 string1)
|
||||
(-> s1-0 irq)
|
||||
(-> *vif-disasm-table* v1-0 string2)
|
||||
)
|
||||
(format arg2 "#x~X #x~X #x~X #x~X)~%" (-> arg0 1) (-> arg0 2) (-> arg0 3) (-> arg0 4))
|
||||
)
|
||||
((= a0-12 5)
|
||||
(format
|
||||
arg2
|
||||
" (~s :irq ~D :instructions #x~D :addr #x~X)~%"
|
||||
(-> *vif-disasm-table* v1-0 string1)
|
||||
(-> s1-0 irq)
|
||||
(-> s1-0 num)
|
||||
(-> s1-0 imm)
|
||||
)
|
||||
)
|
||||
((= a0-12 6)
|
||||
(if (-> s1-0 imm)
|
||||
(set! s0-0 #x100000)
|
||||
(set! s0-0 (the-as int (* (-> s1-0 imm) 16)))
|
||||
)
|
||||
(format arg2 " (~s :irq ~D :qwc #x~D)~%" (-> *vif-disasm-table* v1-0 string1) (-> s1-0 irq) (-> s1-0 imm))
|
||||
(let ((sv-32 (&-> arg0 1))
|
||||
(sv-48 0)
|
||||
)
|
||||
(while (< sv-48 (the-as int (-> s1-0 imm)))
|
||||
(format
|
||||
arg2
|
||||
" #x~X: #x~8x #x~8x #x~8x #x~8x~%"
|
||||
(+ (+ (* sv-48 16) 4) (the-as int arg0))
|
||||
(-> sv-32 (* sv-48 4))
|
||||
(-> sv-32 (+ (* sv-48 4) 1))
|
||||
(-> sv-32 (+ (* sv-48 4) 2))
|
||||
(-> sv-32 (+ (* sv-48 4) 3))
|
||||
)
|
||||
(+! sv-48 1)
|
||||
)
|
||||
)
|
||||
#f
|
||||
)
|
||||
((= a0-12 7)
|
||||
(set! s0-0 (the-as int (+ (logand -4 (+ (* (-> *vif-disasm-table* v1-0 val) (-> s1-0 num)) 3)) 4)))
|
||||
(let ((sv-64 (-> s1-0 imm)))
|
||||
(format
|
||||
arg2
|
||||
" (~s :irq ~D :num ~D :addr #x~X "
|
||||
(-> *vif-disasm-table* v1-0 string1)
|
||||
(-> s1-0 irq)
|
||||
(-> s1-0 num)
|
||||
(shr (shl sv-64 54) 54)
|
||||
)
|
||||
(format
|
||||
arg2
|
||||
":msk ~D :flg ~D :usn ~D [skip ~d])~%"
|
||||
(-> s1-0 msk)
|
||||
(shr (shl sv-64 48) 63)
|
||||
(shr (shl sv-64 49) 63)
|
||||
(the-as uint s0-0)
|
||||
)
|
||||
)
|
||||
(if arg3
|
||||
(disasm-vif-details
|
||||
arg2
|
||||
(the-as (pointer uint8) arg0)
|
||||
(logand sv-16 (vif-cmd cmd-mask))
|
||||
(the-as int (-> s1-0 num))
|
||||
)
|
||||
)
|
||||
)
|
||||
((= a0-12 8)
|
||||
(format arg2 " (*unknown* vif-tag #x~X)~%" (-> s1-0 cmd))
|
||||
)
|
||||
)
|
||||
)
|
||||
(set! v1-0 (-> *vif-disasm-table* length))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(+! gp-0 s0-0)
|
||||
(&+! arg0 s0-0)
|
||||
)
|
||||
)
|
||||
(- gp-0 (* arg1 4))
|
||||
)
|
||||
)";
|
||||
test_with_expr(func, type, expected, false, "",
|
||||
{{"L139", " #x~X:"},
|
||||
{"L138", " (~s :irq ~D)~%"},
|
||||
|
||||
Reference in New Issue
Block a user