mirror of
https://github.com/open-goal/jak-project
synced 2026-08-21 14:54:53 -04:00
[decompiler] support break (#516)
* support break * finish implementation of break, decomp method 19 of res
This commit is contained in:
@@ -58,7 +58,6 @@ add_library(
|
||||
|
||||
VuDisasm/VuDisassembler.cpp
|
||||
VuDisasm/VuInstruction.cpp
|
||||
VuDisasm/VuProgram.cpp
|
||||
|
||||
config.cpp)
|
||||
|
||||
|
||||
@@ -146,6 +146,10 @@ goos::Object BlockVtx::to_form() const {
|
||||
return pretty_print::to_symbol("b" + std::to_string(block_id));
|
||||
}
|
||||
|
||||
int BlockVtx::get_first_block_id() const {
|
||||
return block_id;
|
||||
}
|
||||
|
||||
std::string SequenceVtx::to_string() const {
|
||||
assert(!seq.empty());
|
||||
// todo - this is not a great way to print it. Maybe sequences should have an ID or name?
|
||||
@@ -163,6 +167,10 @@ goos::Object SequenceVtx::to_form() const {
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
int SequenceVtx::get_first_block_id() const {
|
||||
return seq.at(0)->get_first_block_id();
|
||||
}
|
||||
|
||||
std::string EntryVtx::to_string() const {
|
||||
return "ENTRY";
|
||||
}
|
||||
@@ -171,6 +179,11 @@ goos::Object EntryVtx::to_form() const {
|
||||
return pretty_print::to_symbol("entry");
|
||||
}
|
||||
|
||||
int EntryVtx::get_first_block_id() const {
|
||||
assert(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string ExitVtx::to_string() const {
|
||||
return "EXIT";
|
||||
}
|
||||
@@ -179,6 +192,11 @@ goos::Object ExitVtx::to_form() const {
|
||||
return pretty_print::to_symbol("exit");
|
||||
}
|
||||
|
||||
int ExitVtx::get_first_block_id() const {
|
||||
assert(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string CondWithElse::to_string() const {
|
||||
return "CONDWE" + std::to_string(uid);
|
||||
}
|
||||
@@ -195,6 +213,10 @@ goos::Object CondWithElse::to_form() const {
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
int CondWithElse::get_first_block_id() const {
|
||||
return entries.at(0).condition->get_first_block_id();
|
||||
}
|
||||
|
||||
std::string CondNoElse::to_string() const {
|
||||
return "CONDNE" + std::to_string(uid);
|
||||
}
|
||||
@@ -209,6 +231,10 @@ goos::Object CondNoElse::to_form() const {
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
int CondNoElse::get_first_block_id() const {
|
||||
return entries.at(0).condition->get_first_block_id();
|
||||
}
|
||||
|
||||
std::string WhileLoop::to_string() const {
|
||||
return "WHL" + std::to_string(uid);
|
||||
}
|
||||
@@ -219,6 +245,11 @@ goos::Object WhileLoop::to_form() const {
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
int WhileLoop::get_first_block_id() const {
|
||||
assert(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string UntilLoop::to_string() const {
|
||||
return "UNTL" + std::to_string(uid);
|
||||
}
|
||||
@@ -229,6 +260,10 @@ goos::Object UntilLoop::to_form() const {
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
int UntilLoop::get_first_block_id() const {
|
||||
return condition->get_first_block_id();
|
||||
}
|
||||
|
||||
std::string UntilLoop_single::to_string() const {
|
||||
return "UNTLS" + std::to_string(uid);
|
||||
}
|
||||
@@ -238,6 +273,11 @@ goos::Object UntilLoop_single::to_form() const {
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
int UntilLoop_single::get_first_block_id() const {
|
||||
assert(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string InfiniteLoopBlock::to_string() const {
|
||||
return "INFL" + std::to_string(uid);
|
||||
}
|
||||
@@ -247,6 +287,11 @@ goos::Object InfiniteLoopBlock::to_form() const {
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
int InfiniteLoopBlock::get_first_block_id() const {
|
||||
assert(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string ShortCircuit::to_string() const {
|
||||
return "SC" + std::to_string(uid);
|
||||
}
|
||||
@@ -264,6 +309,10 @@ goos::Object ShortCircuit::to_form() const {
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
int ShortCircuit::get_first_block_id() const {
|
||||
return entries.at(0).condition->get_first_block_id();
|
||||
}
|
||||
|
||||
std::string GotoEnd::to_string() const {
|
||||
return "goto_end" + std::to_string(uid);
|
||||
}
|
||||
@@ -274,17 +323,25 @@ goos::Object GotoEnd::to_form() const {
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
int GotoEnd::get_first_block_id() const {
|
||||
return body->get_first_block_id();
|
||||
}
|
||||
|
||||
std::string Break::to_string() const {
|
||||
return "goto" + std::to_string(uid);
|
||||
}
|
||||
|
||||
goos::Object Break::to_form() const {
|
||||
std::vector<goos::Object> forms = {pretty_print::to_symbol("break"),
|
||||
pretty_print::to_symbol(std::to_string(dest_block)),
|
||||
pretty_print::to_symbol(std::to_string(dest_block_id)),
|
||||
body->to_form(), unreachable_block->to_form()};
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
int Break::get_first_block_id() const {
|
||||
return body->get_first_block_id();
|
||||
}
|
||||
|
||||
std::string EmptyVtx::to_string() const {
|
||||
return "empty";
|
||||
}
|
||||
@@ -293,6 +350,11 @@ goos::Object EmptyVtx::to_form() const {
|
||||
return pretty_print::build_list("empty");
|
||||
}
|
||||
|
||||
int EmptyVtx::get_first_block_id() const {
|
||||
assert(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ControlFlowGraph::ControlFlowGraph() {
|
||||
// allocate the entry and exit vertices.
|
||||
m_entry = alloc<EntryVtx>();
|
||||
@@ -796,9 +858,11 @@ bool ControlFlowGraph::find_goto_not_end() {
|
||||
replaced = true;
|
||||
|
||||
auto* new_goto = alloc<Break>();
|
||||
m_has_break = true;
|
||||
new_goto->body = b0;
|
||||
new_goto->unreachable_block = b1;
|
||||
// todo set block number
|
||||
new_goto->dest_block_id = b0->succ_branch->get_first_block_id();
|
||||
m_blocks.at(new_goto->dest_block_id)->needs_label = true;
|
||||
|
||||
for (auto* new_pred : b0->pred) {
|
||||
// printf("fix up pred %s of %s\n", new_pred->to_string().c_str(),
|
||||
|
||||
@@ -67,6 +67,7 @@ class CfgVtx {
|
||||
public:
|
||||
virtual std::string to_string() const = 0; // convert to a single line string for debugging
|
||||
virtual goos::Object to_form() const = 0; // recursive print as LISP form.
|
||||
virtual int get_first_block_id() const = 0;
|
||||
virtual ~CfgVtx() = default;
|
||||
|
||||
CfgVtx* parent = nullptr; // parent structure, or nullptr if top level
|
||||
@@ -77,6 +78,8 @@ class CfgVtx {
|
||||
std::vector<CfgVtx*> pred; // all vertices which have us as succ_branch or succ_ft
|
||||
int uid = -1;
|
||||
|
||||
bool needs_label = false;
|
||||
|
||||
enum class DelaySlotKind { NO_BRANCH, SET_REG_FALSE, SET_REG_TRUE, NOP, OTHER, NO_DELAY };
|
||||
|
||||
struct {
|
||||
@@ -134,6 +137,7 @@ class EntryVtx : public CfgVtx {
|
||||
EntryVtx() = default;
|
||||
goos::Object to_form() const override;
|
||||
std::string to_string() const override;
|
||||
int get_first_block_id() const override;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -143,6 +147,7 @@ class ExitVtx : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int get_first_block_id() const override;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -153,6 +158,7 @@ class BlockVtx : public CfgVtx {
|
||||
explicit BlockVtx(int id) : block_id(id) {}
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int get_first_block_id() const override;
|
||||
int block_id = -1; // which block are we?
|
||||
bool is_early_exit_block = false; // are we an empty block at the end for early exits to jump to?
|
||||
};
|
||||
@@ -165,6 +171,7 @@ class SequenceVtx : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int get_first_block_id() const override;
|
||||
std::vector<CfgVtx*> seq;
|
||||
};
|
||||
|
||||
@@ -177,6 +184,7 @@ class CondWithElse : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int get_first_block_id() const override;
|
||||
|
||||
struct Entry {
|
||||
Entry() = default;
|
||||
@@ -198,6 +206,7 @@ class CondNoElse : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int get_first_block_id() const override;
|
||||
|
||||
struct Entry {
|
||||
Entry() = default;
|
||||
@@ -213,6 +222,7 @@ class WhileLoop : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int get_first_block_id() const override;
|
||||
|
||||
CfgVtx* condition = nullptr;
|
||||
CfgVtx* body = nullptr;
|
||||
@@ -222,6 +232,7 @@ class UntilLoop : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int get_first_block_id() const override;
|
||||
|
||||
CfgVtx* condition = nullptr;
|
||||
CfgVtx* body = nullptr;
|
||||
@@ -231,6 +242,7 @@ class UntilLoop_single : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int get_first_block_id() const override;
|
||||
|
||||
CfgVtx* block = nullptr;
|
||||
};
|
||||
@@ -239,6 +251,7 @@ class ShortCircuit : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int get_first_block_id() const override;
|
||||
struct Entry {
|
||||
CfgVtx* condition = nullptr;
|
||||
CfgVtx* likely_delay = nullptr; // will be nullptr on last case
|
||||
@@ -250,6 +263,7 @@ class InfiniteLoopBlock : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int get_first_block_id() const override;
|
||||
CfgVtx* block;
|
||||
};
|
||||
|
||||
@@ -257,6 +271,7 @@ class GotoEnd : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int get_first_block_id() const override;
|
||||
CfgVtx* body = nullptr;
|
||||
CfgVtx* unreachable_block = nullptr;
|
||||
};
|
||||
@@ -265,15 +280,17 @@ class Break : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int dest_block = -1;
|
||||
int get_first_block_id() const override;
|
||||
CfgVtx* body = nullptr;
|
||||
CfgVtx* unreachable_block = nullptr;
|
||||
int dest_block_id = -1;
|
||||
};
|
||||
|
||||
class EmptyVtx : public CfgVtx {
|
||||
public:
|
||||
std::string to_string() const override;
|
||||
goos::Object to_form() const override;
|
||||
int get_first_block_id() const override;
|
||||
};
|
||||
|
||||
struct BasicBlock;
|
||||
@@ -292,6 +309,7 @@ class ControlFlowGraph {
|
||||
int get_top_level_vertices_count();
|
||||
bool is_fully_resolved();
|
||||
CfgVtx* get_single_top_level();
|
||||
bool contains_break() const { return m_has_break; }
|
||||
|
||||
void flag_early_exit(const std::vector<BasicBlock>& blocks);
|
||||
|
||||
@@ -359,6 +377,7 @@ class ControlFlowGraph {
|
||||
EntryVtx* m_entry; // the entry vertex
|
||||
ExitVtx* m_exit; // the exit vertex
|
||||
int m_uid = 0;
|
||||
bool m_has_break = false;
|
||||
};
|
||||
|
||||
class LinkedObjectFile;
|
||||
|
||||
@@ -308,6 +308,19 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
|
||||
case Kind::RIGHT_SHIFT_ARITH:
|
||||
case Kind::RIGHT_SHIFT_LOGIC: {
|
||||
bool is_unsigned = m_kind == Kind::RIGHT_SHIFT_LOGIC;
|
||||
|
||||
if (m_args[1].is_int()) {
|
||||
auto bf = dynamic_cast<BitFieldType*>(dts.ts.lookup_type(arg0_type.typespec()));
|
||||
if (bf) {
|
||||
int shift_size = 64;
|
||||
int size = shift_size - m_args[1].get_int();
|
||||
int start_bit = shift_size - size;
|
||||
auto field = find_field(dts.ts, bf, start_bit, size, is_unsigned);
|
||||
return TP_Type::make_from_ts(field.type());
|
||||
// auto field = find_field(arg0_type.typespec(), bf, 64)
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
+77
-11
@@ -767,41 +767,56 @@ void ReturnElement::get_modified_regs(RegSet& regs) const {
|
||||
// BreakElement
|
||||
/////////////////////////////
|
||||
|
||||
BreakElement::BreakElement(Form* _return_code, Form* _dead_code)
|
||||
: return_code(_return_code), dead_code(_dead_code) {
|
||||
BreakElement::BreakElement(Form* _return_code, Form* _dead_code, int _lid)
|
||||
: return_code(_return_code), dead_code(_dead_code), lid(_lid) {
|
||||
return_code->parent_element = this;
|
||||
dead_code->parent_element = this;
|
||||
if (dead_code) {
|
||||
dead_code->parent_element = this;
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object BreakElement::to_form_internal(const Env& env) const {
|
||||
std::vector<goos::Object> forms;
|
||||
forms.push_back(pretty_print::to_symbol("break"));
|
||||
forms.push_back(pretty_print::build_list(return_code->to_form(env)));
|
||||
forms.push_back(pretty_print::build_list(dead_code->to_form(env)));
|
||||
if (dead_code) {
|
||||
forms.push_back(pretty_print::to_symbol("break"));
|
||||
forms.push_back(pretty_print::build_list(return_code->to_form(env)));
|
||||
forms.push_back(pretty_print::build_list(dead_code->to_form(env)));
|
||||
} else {
|
||||
forms.push_back(pretty_print::to_symbol("begin"));
|
||||
return_code->inline_forms(forms, env);
|
||||
forms.push_back(pretty_print::build_list(fmt::format("goto cfg-{}", lid)));
|
||||
}
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
void BreakElement::apply(const std::function<void(FormElement*)>& f) {
|
||||
f(this);
|
||||
return_code->apply(f);
|
||||
dead_code->apply(f);
|
||||
if (dead_code) {
|
||||
dead_code->apply(f);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakElement::apply_form(const std::function<void(Form*)>& f) {
|
||||
return_code->apply_form(f);
|
||||
dead_code->apply_form(f);
|
||||
if (dead_code) {
|
||||
dead_code->apply_form(f);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
||||
if (recursive) {
|
||||
return_code->collect_vars(vars, recursive);
|
||||
dead_code->collect_vars(vars, recursive);
|
||||
if (dead_code) {
|
||||
dead_code->collect_vars(vars, recursive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BreakElement::get_modified_regs(RegSet& regs) const {
|
||||
for (auto x : {return_code, dead_code}) {
|
||||
x->get_modified_regs(regs);
|
||||
return_code->get_modified_regs(regs);
|
||||
if (dead_code) {
|
||||
dead_code->get_modified_regs(regs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1503,6 +1518,8 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
|
||||
return "none";
|
||||
case FixedOperatorKind::PCPYLD:
|
||||
return "make-u128";
|
||||
case FixedOperatorKind::SYMBOL_TO_STRING:
|
||||
return "symbol->string";
|
||||
default:
|
||||
assert(false);
|
||||
return "";
|
||||
@@ -2369,6 +2386,55 @@ void MethodOfTypeElement::collect_vars(RegAccessSet& vars, bool) const {
|
||||
|
||||
void MethodOfTypeElement::get_modified_regs(RegSet&) const {}
|
||||
|
||||
////////////////////////////////
|
||||
// LabelElement
|
||||
///////////////////////////////
|
||||
|
||||
LabelElement::LabelElement(int lid) : m_lid(lid) {}
|
||||
|
||||
goos::Object LabelElement::to_form_internal(const Env&) const {
|
||||
return pretty_print::build_list(fmt::format("label cfg-{}", m_lid));
|
||||
}
|
||||
|
||||
void LabelElement::apply(const std::function<void(FormElement*)>& f) {
|
||||
f(this);
|
||||
}
|
||||
|
||||
void LabelElement::apply_form(const std::function<void(Form*)>&) {}
|
||||
void LabelElement::collect_vars(RegAccessSet&, bool) const {}
|
||||
void LabelElement::get_modified_regs(RegSet&) const {}
|
||||
|
||||
////////////////////////////////
|
||||
// GetSymbolStringPointer
|
||||
//////////////////////////////
|
||||
|
||||
GetSymbolStringPointer::GetSymbolStringPointer(Form* src) : m_src(src) {
|
||||
m_src->parent_element = this;
|
||||
}
|
||||
|
||||
goos::Object GetSymbolStringPointer::to_form_internal(const Env& env) const {
|
||||
return pretty_print::build_list("sym->str-ptr", m_src->to_form(env));
|
||||
}
|
||||
|
||||
void GetSymbolStringPointer::apply(const std::function<void(FormElement*)>& f) {
|
||||
f(this);
|
||||
m_src->apply(f);
|
||||
}
|
||||
|
||||
void GetSymbolStringPointer::apply_form(const std::function<void(Form*)>& f) {
|
||||
m_src->apply_form(f);
|
||||
}
|
||||
|
||||
void GetSymbolStringPointer::collect_vars(RegAccessSet& vars, bool recursive) const {
|
||||
if (recursive) {
|
||||
m_src->collect_vars(vars, recursive);
|
||||
}
|
||||
}
|
||||
|
||||
void GetSymbolStringPointer::get_modified_regs(RegSet& regs) const {
|
||||
return m_src->get_modified_regs(regs);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Utilities
|
||||
////////////////////////////////
|
||||
|
||||
+38
-1
@@ -590,12 +590,14 @@ class BreakElement : public FormElement {
|
||||
public:
|
||||
Form* return_code = nullptr;
|
||||
Form* dead_code = nullptr;
|
||||
BreakElement(Form* _return_code, Form* _dead_code);
|
||||
int lid = -1;
|
||||
BreakElement(Form* _return_code, Form* _dead_code, int _lid);
|
||||
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;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -1015,6 +1017,8 @@ class DerefToken {
|
||||
return m_kind == Kind::FIELD_NAME && m_name == name;
|
||||
}
|
||||
|
||||
bool is_int(int x) const { return m_kind == Kind::INTEGER_CONSTANT && m_int_constant == x; }
|
||||
|
||||
Kind kind() const { return m_kind; }
|
||||
const std::string& field_name() const {
|
||||
assert(m_kind == Kind::FIELD_NAME);
|
||||
@@ -1409,6 +1413,39 @@ class MethodOfTypeElement : public FormElement {
|
||||
MethodInfo m_method_info;
|
||||
};
|
||||
|
||||
class LabelElement : public FormElement {
|
||||
public:
|
||||
LabelElement(int lid);
|
||||
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;
|
||||
|
||||
private:
|
||||
int m_lid = -1;
|
||||
};
|
||||
|
||||
class GetSymbolStringPointer : public FormElement {
|
||||
public:
|
||||
GetSymbolStringPointer(Form* src);
|
||||
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;
|
||||
Form* src() { return m_src; }
|
||||
|
||||
private:
|
||||
Form* m_src = nullptr;
|
||||
};
|
||||
|
||||
/*!
|
||||
* A Form is a wrapper around one or more FormElements.
|
||||
* This is done for two reasons:
|
||||
|
||||
@@ -629,6 +629,15 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
|
||||
// lookup types.
|
||||
auto arg1_type = env.get_types_before_op(m_my_idx).get(m_expr.get_arg(1).var().reg());
|
||||
auto arg0_type = env.get_types_before_op(m_my_idx).get(m_expr.get_arg(0).var().reg());
|
||||
|
||||
// try to find symbol to string stuff
|
||||
auto arg0_int = get_goal_integer_constant(args.at(0), env);
|
||||
if (arg0_int && (*arg0_int == SYM_INFO_OFFSET + 4) &&
|
||||
arg1_type.typespec() == TypeSpec("symbol")) {
|
||||
result->push_back(pool.alloc_element<GetSymbolStringPointer>(args.at(1)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (arg0_type.kind == TP_Type::Kind::INTEGER_CONSTANT_PLUS_VAR) {
|
||||
// try to see if this is valid, from the type system.
|
||||
FieldReverseLookupInput input;
|
||||
@@ -1981,6 +1990,14 @@ void DerefElement::update_from_stack(const Env& env,
|
||||
// todo - update var tokens from stack?
|
||||
m_base->update_children_from_stack(env, pool, stack, allow_side_effects);
|
||||
|
||||
// look for sym->str-ptr
|
||||
auto sym_str = m_base->try_as_element<GetSymbolStringPointer>();
|
||||
if (sym_str && m_tokens.size() == 1 && m_tokens.at(0).is_int(0)) {
|
||||
result->push_back(pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::SYMBOL_TO_STRING), sym_str->src()));
|
||||
return;
|
||||
}
|
||||
|
||||
// merge nested ->'s
|
||||
inline_nested();
|
||||
|
||||
@@ -3470,4 +3487,34 @@ void StackSpillValueElement::update_from_stack(const Env&,
|
||||
result->push_back(this);
|
||||
}
|
||||
|
||||
void GetSymbolStringPointer::update_from_stack(const Env&,
|
||||
FormPool&,
|
||||
FormStack&,
|
||||
std::vector<FormElement*>* result,
|
||||
bool) {
|
||||
mark_popped();
|
||||
result->push_back(this);
|
||||
}
|
||||
|
||||
void LabelElement::push_to_stack(const Env&, FormPool&, FormStack& stack) {
|
||||
mark_popped();
|
||||
stack.push_form_element(this, true);
|
||||
}
|
||||
|
||||
void BreakElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) {
|
||||
mark_popped();
|
||||
|
||||
FormStack temp_stack(false);
|
||||
for (auto& elt : return_code->elts()) {
|
||||
elt->push_to_stack(env, pool, temp_stack);
|
||||
}
|
||||
|
||||
auto new_entries = temp_stack.rewrite(pool, env);
|
||||
return_code->clear();
|
||||
for (auto e : new_entries) {
|
||||
return_code->push_back(e);
|
||||
}
|
||||
stack.push_form_element(this, true);
|
||||
}
|
||||
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -144,6 +144,7 @@ enum class FixedOperatorKind {
|
||||
PAIRP,
|
||||
NONE,
|
||||
PCPYLD,
|
||||
SYMBOL_TO_STRING,
|
||||
INVALID
|
||||
};
|
||||
|
||||
|
||||
@@ -242,13 +242,17 @@ void ObjectFileDB::ir2_stack_spill_slot_pass() {
|
||||
if (!func.cfg_ok) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
try {
|
||||
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);
|
||||
} catch (std::exception& e) {
|
||||
func.warnings.general_warning("stack spill failed: {}", e.what());
|
||||
}
|
||||
func.ir2.env.set_stack_spills(spill_map);
|
||||
});
|
||||
lg::info("Analyzed stack spills: found {} functions with spills (total {} vars), took {:.2f} ms",
|
||||
functions_with_spills, total_slots, timer.getMs());
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
|
||||
#include "VuProgram.h"
|
||||
@@ -192,6 +192,32 @@ void clean_up_break(FormPool& pool, BreakElement* ir) {
|
||||
}
|
||||
}
|
||||
|
||||
void clean_up_break_final(const Function& f, BreakElement* ir) {
|
||||
SetVarElement* dead = dynamic_cast<SetVarElement*>(ir->dead_code->try_as_single_element());
|
||||
if (!dead) {
|
||||
dead = dynamic_cast<SetVarElement*>(ir->dead_code->elts().front());
|
||||
for (int i = 1; i < ir->dead_code->size(); i++) {
|
||||
if (!dynamic_cast<EmptyElement*>(ir->dead_code->at(i))) {
|
||||
dead = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dead) {
|
||||
lg::error("failed to recognize dead code after break, got {}",
|
||||
ir->dead_code->to_string(f.ir2.env));
|
||||
throw std::runtime_error("failed to recognize dead code");
|
||||
}
|
||||
assert(dead);
|
||||
auto src = dynamic_cast<SimpleExpressionElement*>(dead->src()->try_as_single_element());
|
||||
assert(src);
|
||||
if (src->expr().is_identity() && src->expr().get_arg(0).is_int() &&
|
||||
src->expr().get_arg(0).get_int() == 0) {
|
||||
ir->dead_code = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Does the instruction in the delay slot set a register to false?
|
||||
* Note. a beql s7, x followed by a or y, x, r0 will count as this. I don't know why but
|
||||
@@ -1412,10 +1438,16 @@ void insert_cfg_into_list(FormPool& pool,
|
||||
auto as_block = dynamic_cast<const BlockVtx*>(vtx);
|
||||
if (as_sequence) {
|
||||
// inline the sequence.
|
||||
if (as_sequence->needs_label) {
|
||||
output->push_back(pool.alloc_element<LabelElement>(vtx->get_first_block_id()));
|
||||
}
|
||||
for (auto& x : as_sequence->seq) {
|
||||
insert_cfg_into_list(pool, f, x, output);
|
||||
}
|
||||
} else if (as_block) {
|
||||
if (as_block->needs_label) {
|
||||
output->push_back(pool.alloc_element<LabelElement>(vtx->get_first_block_id()));
|
||||
}
|
||||
convert_and_inline(pool, f, as_block, output);
|
||||
} else {
|
||||
auto ir = cfg_to_ir(pool, f, vtx);
|
||||
@@ -1425,7 +1457,7 @@ void insert_cfg_into_list(FormPool& pool,
|
||||
}
|
||||
}
|
||||
|
||||
Form* cfg_to_ir(FormPool& pool, Function& f, const CfgVtx* vtx) {
|
||||
Form* cfg_to_ir_helper(FormPool& pool, Function& f, const CfgVtx* vtx) {
|
||||
if (dynamic_cast<const BlockVtx*>(vtx)) {
|
||||
auto* bv = dynamic_cast<const BlockVtx*>(vtx);
|
||||
Form* output = pool.alloc_empty_form();
|
||||
@@ -1588,7 +1620,8 @@ Form* cfg_to_ir(FormPool& pool, Function& f, const CfgVtx* vtx) {
|
||||
} else if (dynamic_cast<const Break*>(vtx)) {
|
||||
auto* cvtx = dynamic_cast<const Break*>(vtx);
|
||||
auto result = pool.alloc_single_element_form<BreakElement>(
|
||||
nullptr, cfg_to_ir(pool, f, cvtx->body), cfg_to_ir(pool, f, cvtx->unreachable_block));
|
||||
nullptr, cfg_to_ir(pool, f, cvtx->body), cfg_to_ir(pool, f, cvtx->unreachable_block),
|
||||
cvtx->dest_block_id);
|
||||
clean_up_break(pool, dynamic_cast<BreakElement*>(result->try_as_single_element()));
|
||||
return result;
|
||||
} else if (dynamic_cast<const EmptyVtx*>(vtx)) {
|
||||
@@ -1599,6 +1632,15 @@ Form* cfg_to_ir(FormPool& pool, Function& f, const CfgVtx* vtx) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Form* cfg_to_ir(FormPool& pool, Function& f, const CfgVtx* vtx) {
|
||||
Form* result = cfg_to_ir_helper(pool, f, vtx);
|
||||
if (vtx->needs_label) {
|
||||
result->elts().insert(result->elts().begin(),
|
||||
pool.alloc_element<LabelElement>(vtx->get_first_block_id()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Post processing pass to clean up while loops - annoyingly the block before a while loop
|
||||
* has a jump to the condition branch that we need to remove. This currently happens after all
|
||||
@@ -1666,6 +1708,11 @@ void build_initial_forms(Function& function) {
|
||||
if (as_return) {
|
||||
clean_up_return_final(function, as_return);
|
||||
}
|
||||
|
||||
auto as_break = dynamic_cast<BreakElement*>(form);
|
||||
if (as_break) {
|
||||
clean_up_break_final(function, as_break);
|
||||
}
|
||||
});
|
||||
|
||||
function.ir2.top_form = result;
|
||||
|
||||
@@ -8562,7 +8562,7 @@
|
||||
(dummy-16 (_type_ int int int int) none 16)
|
||||
(dummy-17 (_type_ int int) res-lump 17)
|
||||
(dummy-18 (_type_ int int) none 18)
|
||||
(dummy-19 (_type_ symbol symbol int) int 19)
|
||||
(lookup-tag-idx (_type_ symbol symbol float) int 19)
|
||||
(dummy-20 (_type_ int int) none 20)
|
||||
(dummy-21 (_type_ int int int int int) none 21)
|
||||
)
|
||||
|
||||
@@ -440,5 +440,9 @@
|
||||
|
||||
"trajectory": [
|
||||
["L18", "uint64", true]
|
||||
],
|
||||
|
||||
"res": [
|
||||
["L150", "uint64", true]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -98,8 +98,8 @@
|
||||
|
||||
// GSTRING
|
||||
"name=": [
|
||||
[24, "a1", "symbol"],
|
||||
[39, "a0", "symbol"]
|
||||
[26, "a1", "symbol"],
|
||||
[42, "a0", "symbol"]
|
||||
],
|
||||
|
||||
"string-cat-to-last-char": [
|
||||
|
||||
@@ -849,7 +849,7 @@
|
||||
"args": ["allocation", "type-to-make", "psm", "w", "h", "ztest", "zpsm"],
|
||||
"vars": { "gp-0": "obj" }
|
||||
},
|
||||
|
||||
|
||||
"(method 0 ripple-control)": {
|
||||
"vars": { "v0-0": "obj" }
|
||||
},
|
||||
@@ -1200,87 +1200,87 @@
|
||||
"s0-0": "upload-chunk-idx",
|
||||
"sv-52": "current-dest-chunk",
|
||||
"sv-56": "allow-cached",
|
||||
"gp-0":"total-upload-size",
|
||||
"a0-21":["dma", "dma-packet"],
|
||||
"a0-23":["gif", "gs-gif-tag"],
|
||||
"v1-55":["dma-end", "dma-packet"]
|
||||
"gp-0": "total-upload-size",
|
||||
"a0-21": ["dma", "dma-packet"],
|
||||
"a0-23": ["gif", "gs-gif-tag"],
|
||||
"v1-55": ["dma-end", "dma-packet"]
|
||||
}
|
||||
},
|
||||
|
||||
"texture-page-near-allocate-0": {
|
||||
"args": ["pool", "page", "heap", "mode"],
|
||||
"vars": {
|
||||
"s3-0":"common-dest",
|
||||
"s2-0":"page-seg-idx",
|
||||
"a1-5":"page-seg-2-size",
|
||||
"v1-15":"after-seg-2-data",
|
||||
"a0-8":"seg-2-data"
|
||||
"s3-0": "common-dest",
|
||||
"s2-0": "page-seg-idx",
|
||||
"a1-5": "page-seg-2-size",
|
||||
"v1-15": "after-seg-2-data",
|
||||
"a0-8": "seg-2-data"
|
||||
}
|
||||
},
|
||||
|
||||
"texture-page-near-allocate-1": {
|
||||
"args": ["pool", "page", "heap", "mode"],
|
||||
"vars": {
|
||||
"s4-0":"seg2-size",
|
||||
"a1-1":"seg2-dest",
|
||||
"s2-0":"common-dest",
|
||||
"s1-0":"page-seg-idx"
|
||||
"s4-0": "seg2-size",
|
||||
"a1-1": "seg2-dest",
|
||||
"s2-0": "common-dest",
|
||||
"s1-0": "page-seg-idx"
|
||||
}
|
||||
},
|
||||
|
||||
"texture-page-level-allocate": {
|
||||
"args": ["pool", "page", "heap", "mode"],
|
||||
"vars": {
|
||||
"s2-0":"common-id",
|
||||
"v1-6":"level-idx"
|
||||
"s2-0": "common-id",
|
||||
"v1-6": "level-idx"
|
||||
}
|
||||
},
|
||||
|
||||
"texture-page-size-check": {
|
||||
"args": ["pool", "level", "hide-prints"],
|
||||
"vars": {
|
||||
"gp-0":"oversize",
|
||||
"s3-0":"tfrag-page",
|
||||
"v1-0":"tfrag-mip0-size",
|
||||
"v1-3":"pris-page",
|
||||
"v1-5":"shrub-page",
|
||||
"v1-7":"alpha-page",
|
||||
"v1-9":"water-page"
|
||||
"gp-0": "oversize",
|
||||
"s3-0": "tfrag-page",
|
||||
"v1-0": "tfrag-mip0-size",
|
||||
"v1-3": "pris-page",
|
||||
"v1-5": "shrub-page",
|
||||
"v1-7": "alpha-page",
|
||||
"v1-9": "water-page"
|
||||
}
|
||||
},
|
||||
|
||||
"(method 13 texture-pool)": {
|
||||
"args":["obj", "level", "max-page-kind", "id-array"],
|
||||
"args": ["obj", "level", "max-page-kind", "id-array"],
|
||||
"vars": {
|
||||
"v1-0":"page-idx",
|
||||
"v1-5":"tfrag-dir-entry",
|
||||
"v1-7":"pris-dir-entry",
|
||||
"v1-9":"shrub-dir-entry",
|
||||
"v1-11":"alpha-dir-entry",
|
||||
"v1-13":"water-dir-entry",
|
||||
"a2-7":"overflow-bits"
|
||||
"v1-0": "page-idx",
|
||||
"v1-5": "tfrag-dir-entry",
|
||||
"v1-7": "pris-dir-entry",
|
||||
"v1-9": "shrub-dir-entry",
|
||||
"v1-11": "alpha-dir-entry",
|
||||
"v1-13": "water-dir-entry",
|
||||
"a2-7": "overflow-bits"
|
||||
}
|
||||
},
|
||||
|
||||
"(method 14 texture-pool)": {
|
||||
"args":["obj", "level", "tex-page-kind"],
|
||||
"args": ["obj", "level", "tex-page-kind"],
|
||||
"vars": {
|
||||
"s3-0":"tfrag-page",
|
||||
"s2-0":"tfrag-bucket",
|
||||
"f30-0":"distance",
|
||||
"a2-4":"pris-page",
|
||||
"a3-3":"pris-bucket",
|
||||
"a2-5":"shrub-page",
|
||||
"f0-5":"shrub-closest",
|
||||
"t0-4":"shrub-bucket",
|
||||
"a3-4":"shrub-mode",
|
||||
"s3-1":"alpha-page",
|
||||
"f0-6":"alpha-closest",
|
||||
"s2-1":"alpha-bucket",
|
||||
"s1-3":"alpha-mode",
|
||||
"s0-0":"alpha-dest-chunk",
|
||||
"a2-7":"water-page",
|
||||
"a3-6":"water-bucket"
|
||||
"s3-0": "tfrag-page",
|
||||
"s2-0": "tfrag-bucket",
|
||||
"f30-0": "distance",
|
||||
"a2-4": "pris-page",
|
||||
"a3-3": "pris-bucket",
|
||||
"a2-5": "shrub-page",
|
||||
"f0-5": "shrub-closest",
|
||||
"t0-4": "shrub-bucket",
|
||||
"a3-4": "shrub-mode",
|
||||
"s3-1": "alpha-page",
|
||||
"f0-6": "alpha-closest",
|
||||
"s2-1": "alpha-bucket",
|
||||
"s1-3": "alpha-mode",
|
||||
"s0-0": "alpha-dest-chunk",
|
||||
"a2-7": "water-page",
|
||||
"a3-6": "water-bucket"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1377,11 +1377,11 @@
|
||||
},
|
||||
|
||||
"move-target-from-pad": {
|
||||
"args":["trans", "pad-idx"],
|
||||
"args": ["trans", "pad-idx"],
|
||||
"vars": {
|
||||
"s4-0":"local-trans",
|
||||
"a0-5":"inv-cam-rot",
|
||||
"s3-0":"cam-rot-mat"
|
||||
"s4-0": "local-trans",
|
||||
"a0-5": "inv-cam-rot",
|
||||
"s3-0": "cam-rot-mat"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1565,10 +1565,18 @@
|
||||
"args": ["obj", "name", "cmd-idx"],
|
||||
"vars": { "v1-1": "cmd-lst" }
|
||||
},
|
||||
|
||||
|
||||
// SHADOW-CPU-H
|
||||
"(method 0 shadow-control)": {
|
||||
"args": ["allocation", "type-to-make", "bottom-offset", "top-offset", "dir", "center", "fade"],
|
||||
"args": [
|
||||
"allocation",
|
||||
"type-to-make",
|
||||
"bottom-offset",
|
||||
"top-offset",
|
||||
"dir",
|
||||
"center",
|
||||
"fade"
|
||||
],
|
||||
"vars": { "v0-0": "obj" }
|
||||
},
|
||||
|
||||
@@ -1584,85 +1592,104 @@
|
||||
},
|
||||
|
||||
"(method 0 align-control)": {
|
||||
"vars":{"v0-0": ["obj", "align-control"]}
|
||||
"vars": { "v0-0": ["obj", "align-control"] }
|
||||
},
|
||||
|
||||
"str-load": {
|
||||
"args":["name", "chunk-id", "address", "len"],
|
||||
"vars":{"s2-0":["cmd", "load-chunk-msg"]}
|
||||
"args": ["name", "chunk-id", "address", "len"],
|
||||
"vars": { "s2-0": ["cmd", "load-chunk-msg"] }
|
||||
},
|
||||
|
||||
"str-load-status": {
|
||||
"args":["length-out"],
|
||||
"vars":{"v1-7":"response"}
|
||||
"args": ["length-out"],
|
||||
"vars": { "v1-7": "response" }
|
||||
},
|
||||
|
||||
"str-play-async": {
|
||||
"args": ["name", "addr"],
|
||||
"vars":{"s4-0":"cmd"}
|
||||
"vars": { "s4-0": "cmd" }
|
||||
},
|
||||
|
||||
"str-play-stop": {
|
||||
"args": ["name"],
|
||||
"vars":{"s5-0":"cmd"}
|
||||
"vars": { "s5-0": "cmd" }
|
||||
},
|
||||
|
||||
"str-play-queue": {
|
||||
"args": ["name"],
|
||||
"vars":{"s5-0":"cmd"}
|
||||
"vars": { "s5-0": "cmd" }
|
||||
},
|
||||
|
||||
"str-ambient-play": {
|
||||
"args": ["name"],
|
||||
"vars":{"s5-0":"cmd"}
|
||||
"vars": { "s5-0": "cmd" }
|
||||
},
|
||||
|
||||
"str-ambient-stop": {
|
||||
"args": ["name"],
|
||||
"vars":{"s5-0":"cmd"}
|
||||
"vars": { "s5-0": "cmd" }
|
||||
},
|
||||
|
||||
"string->sound-name": {
|
||||
"args":["str"],
|
||||
"vars":{"v1-0":"snd-name", "a1-0":["out-ptr", "(pointer uint8)"], "a2-0":"in-ptr"}
|
||||
"args": ["str"],
|
||||
"vars": {
|
||||
"v1-0": "snd-name",
|
||||
"a1-0": ["out-ptr", "(pointer uint8)"],
|
||||
"a2-0": "in-ptr"
|
||||
}
|
||||
},
|
||||
"dgo-load-begin": {
|
||||
"args":["name", "buffer1", "buffer2", "current-heap"],
|
||||
"vars":{"s2-0":"cmd"}
|
||||
"args": ["name", "buffer1", "buffer2", "current-heap"],
|
||||
"vars": { "s2-0": "cmd" }
|
||||
},
|
||||
|
||||
"dgo-load-get-next": {
|
||||
"args":["last-object"],
|
||||
"vars":{"gp-0":["load-location", "pointer"], "v1-5":"response"}
|
||||
"args": ["last-object"],
|
||||
"vars": { "gp-0": ["load-location", "pointer"], "v1-5": "response" }
|
||||
},
|
||||
|
||||
"dgo-load-continue": {
|
||||
"args":["current-heap"],
|
||||
"vars":{"gp-0":"cmd"}
|
||||
"args": ["current-heap"],
|
||||
"vars": { "gp-0": "cmd" }
|
||||
},
|
||||
"dgo-load-cancel": {
|
||||
"vars":{"a2-0":"cmd"}
|
||||
"vars": { "a2-0": "cmd" }
|
||||
},
|
||||
|
||||
"find-temp-buffer": {
|
||||
"args":["size"],
|
||||
"vars":{"gp-0":"qwc"}
|
||||
"args": ["size"],
|
||||
"vars": { "gp-0": "qwc" }
|
||||
},
|
||||
|
||||
"dgo-load-link": {
|
||||
"args":["obj-file", "heap", "print-login", "last-object"],
|
||||
"vars":{"s4-0":"obj-data"}
|
||||
"args": ["obj-file", "heap", "print-login", "last-object"],
|
||||
"vars": { "s4-0": "obj-data" }
|
||||
},
|
||||
|
||||
"ramdisk-load": {
|
||||
"args":["file-id", "offset", "length", "buffer"],
|
||||
"vars":{"v1-1":"cmd"}
|
||||
"args": ["file-id", "offset", "length", "buffer"],
|
||||
"vars": { "v1-1": "cmd" }
|
||||
},
|
||||
|
||||
"show-mc-info": {
|
||||
"args":["dma-buf"],
|
||||
"vars":{"s5-0":"info", "s4-0":"slot-idx"}
|
||||
}
|
||||
"args": ["dma-buf"],
|
||||
"vars": { "s5-0": "info", "s4-0": "slot-idx" }
|
||||
},
|
||||
|
||||
|
||||
"(method 19 res-lump)": {
|
||||
"args": ["obj", "name-sym", "mode", "time"],
|
||||
"vars": {
|
||||
"t2-4": "type-chars",
|
||||
"t3-1": "max-search",
|
||||
"t4-0": "min-search",
|
||||
"t6-5": "diff",
|
||||
"t5-2": "check-idx",
|
||||
"t4-1": "tag-idx",
|
||||
"t3-13": "interp-tag-idx",
|
||||
"t4-4": "tag-ptr",
|
||||
"t0-6": "lo-tag-idx-out",
|
||||
"v1-14": "hi-tag-idx-out",
|
||||
"t1-0": "most-recent-invalid-time-idx"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
(dummy-16 (_type_ int int int int) none 16)
|
||||
(dummy-17 (_type_ int int) res-lump 17)
|
||||
(dummy-18 (_type_ int int) none 18)
|
||||
(dummy-19 (_type_ symbol symbol int) int 19)
|
||||
(lookup-tag-idx (_type_ symbol symbol float) int 19)
|
||||
(dummy-20 (_type_ int int) none 20)
|
||||
(dummy-21 (_type_ int int int int int) none 21)
|
||||
)
|
||||
|
||||
@@ -121,3 +121,179 @@
|
||||
(-> obj data-size)))
|
||||
)
|
||||
|
||||
(defmethod lookup-tag-idx res-lump ((obj res-lump) (name-sym symbol) (mode symbol) (time float))
|
||||
"Look up the index of the tag containing with the given name and timestamp.
|
||||
This will actually return two tags: one in the lower 32 bits and one in the upper 32 bits.
|
||||
Depending on the mode, they may be the same, or they may be two tags that you should interpolate
|
||||
between, if the exact time was not found.
|
||||
|
||||
name-sym should be the name of the thing you want.
|
||||
time is for the timestamp you want.
|
||||
If mode = 'base, then both the indices are the same and the timestamp is ignored.
|
||||
If mode = 'interp, then it tries to get closest below/closest above (or both the same, if exact match found)
|
||||
If mode = 'exact, then it requires an exact timestamp match and both indices are the same.
|
||||
If things go wrong, returns a negative number"
|
||||
(local-vars (tag-idx int))
|
||||
;; for some unknown reason, these names cannot be looked up with this method.
|
||||
(when (or
|
||||
(= name-sym 'id)
|
||||
(= name-sym 'aid)
|
||||
(= name-sym 'trans)
|
||||
(= name-sym 'rot)
|
||||
(= name-sym 'nav-mesh)
|
||||
(= name-sym 'process-type)
|
||||
(= name-sym 'task)
|
||||
)
|
||||
(crash!)
|
||||
)
|
||||
|
||||
;; check that we are valid.
|
||||
(if (or (not obj) (zero? obj) (<= (-> obj length) 0))
|
||||
(return -1)
|
||||
)
|
||||
|
||||
;; these are the outputs of the function.
|
||||
(let ((hi-tag-idx-out -1)
|
||||
(lo-tag-idx-out -1)
|
||||
)
|
||||
;; this value is the index of the most recently passed tag with an "invalid" timestamp
|
||||
(let ((most-recent-invalid-time-idx -1)
|
||||
;; read 8 chars of the name we want
|
||||
(type-chars (-> (the-as (pointer uint64) (-> (symbol->string name-sym) data)) 0))
|
||||
)
|
||||
|
||||
;; now we will do a binary search. The names are stored in ascending order if you
|
||||
;; treat the first 8 chars as an integer
|
||||
;; min/max are inclusive.
|
||||
(let ((max-search (+ (-> obj length) -1))
|
||||
(min-search 0)
|
||||
)
|
||||
;; inclusive, so >= is correct
|
||||
(while (>= max-search min-search)
|
||||
;; check in the middle of the range to bisect it.
|
||||
(let* ((check-idx (+ min-search (/ (- max-search min-search) 2)))
|
||||
;; subtract the two words. The sign of this tells us if we are too high or too low
|
||||
(diff (- type-chars
|
||||
(-> (the-as (pointer uint64) (-> (symbol->string (-> (-> obj tag) check-idx name)) data)) 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(cond
|
||||
((zero? diff)
|
||||
;; perfect match! we are done, set the tag-idx and get out of here.
|
||||
(begin
|
||||
(set! tag-idx check-idx)
|
||||
(goto cfg-32)
|
||||
)
|
||||
)
|
||||
(else
|
||||
;; didn't match. pick the appropriate half of the remaining tags
|
||||
(if (< (the-as int diff) 0)
|
||||
(set! max-search (+ check-idx -1))
|
||||
(set! min-search (+ check-idx 1))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
;; got to the end of the loop without finding the answer. Set a negative tag
|
||||
(set! tag-idx -1)
|
||||
|
||||
(label cfg-32)
|
||||
(if (< tag-idx 0)
|
||||
(return tag-idx)
|
||||
)
|
||||
|
||||
;; if there are multiple tags with the same name and different timesteps, we can't be sure which we ended on.
|
||||
;; this loop brings us to the first tag with the correct name.
|
||||
(while (and (> tag-idx 0)
|
||||
(= type-chars
|
||||
(-> (the-as (pointer uint64) (-> (symbol->string (-> (-> obj tag) (+ tag-idx -1) name)) data)) 0)
|
||||
)
|
||||
)
|
||||
(+! tag-idx -1)
|
||||
)
|
||||
|
||||
;; in 'base mode, we just want the earliest tag with the right name.
|
||||
;; tags are in increasing timestamps, so we found it!
|
||||
(if (= mode 'base)
|
||||
(begin
|
||||
;; both lo and hi are the same
|
||||
(set! lo-tag-idx-out tag-idx)
|
||||
(set! hi-tag-idx-out tag-idx)
|
||||
(goto cfg-73)
|
||||
)
|
||||
)
|
||||
|
||||
;; next we will iterate through tags with the right name and find the one(s) with the right timestamp
|
||||
;; interp-tag-idx is the index of tag-ptr always
|
||||
;; (the fact that they keep a pointer to a tag and not actually load the tag makes it seem like
|
||||
;; they ran into some 128-bit integer issues here...)
|
||||
(let ((interp-tag-idx tag-idx)
|
||||
(tag-ptr (&-> (-> obj tag) tag-idx))
|
||||
)
|
||||
;; loop, until we reach another name or the end of the table
|
||||
(while (not (or
|
||||
(>= interp-tag-idx (-> obj length))
|
||||
;; < is correct here because we are incrementing and names get larger
|
||||
(< type-chars
|
||||
(-> (the-as (pointer uint64) (-> (symbol->string (-> tag-ptr 0 name)) data)) 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(cond
|
||||
;; The checks above only made sure that the first 8 chars were correct.
|
||||
;; This skips items with matching first 8 chars, but differences after that.
|
||||
((!= name-sym (-> tag-ptr 0 name))
|
||||
)
|
||||
|
||||
;; check for exact match
|
||||
((= (-> tag-ptr 0 key-frame) time)
|
||||
;; in all cases, just return the exact match.
|
||||
(begin
|
||||
(set! lo-tag-idx-out interp-tag-idx)
|
||||
(set! hi-tag-idx-out interp-tag-idx)
|
||||
(goto cfg-73)
|
||||
)
|
||||
)
|
||||
|
||||
;; check for being not far enough
|
||||
((and (>= time (-> tag-ptr 0 key-frame)) (!= mode 'exact))
|
||||
;; in all cases, except for exact, we'll want to remember this.
|
||||
;; just in case there are no more tags
|
||||
(set! lo-tag-idx-out interp-tag-idx)
|
||||
(set! hi-tag-idx-out interp-tag-idx)
|
||||
;; also remember if we hit an invalid one
|
||||
(if (= (-> tag-ptr 0 key-frame) -1000000000.0)
|
||||
(set! most-recent-invalid-time-idx interp-tag-idx)
|
||||
)
|
||||
)
|
||||
|
||||
;; check for being too far (passed the time)
|
||||
((< time (-> tag-ptr 0 key-frame))
|
||||
(begin
|
||||
;; if we're interpolation mode and valid, set the high one.
|
||||
;; not sure what the invalid time thing is about.
|
||||
(if (and (!= lo-tag-idx-out most-recent-invalid-time-idx) (= mode 'interp))
|
||||
(set! hi-tag-idx-out interp-tag-idx)
|
||||
)
|
||||
(goto cfg-73)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; advance to next tag
|
||||
(+! interp-tag-idx 1)
|
||||
(set! tag-ptr (&-> tag-ptr 1))
|
||||
)
|
||||
)
|
||||
)
|
||||
(label cfg-73)
|
||||
;; end: return the tags.
|
||||
(logior
|
||||
(logand #xffffffff (the-as uint lo-tag-idx-out))
|
||||
(the-as uint (shl hi-tag-idx-out 32))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
(defconstant PAIR_OFFSET 2)
|
||||
(defconstant BASIC_OFFSET 4)
|
||||
|
||||
(defmacro symbol-to-string (sym)
|
||||
;; "Convert a symbol to a goal string."
|
||||
(defmacro symbol->string (sym)
|
||||
"Convert a symbol to a goal string."
|
||||
`(-> (the-as (pointer string) (+ SYM_TO_STRING_OFFSET (the-as int ,sym))))
|
||||
)
|
||||
|
||||
|
||||
@@ -106,10 +106,10 @@
|
||||
(string= (the-as string arg0) (the-as string arg1))
|
||||
)
|
||||
((and (= (-> arg0 type) string) (= (-> arg1 type) symbol))
|
||||
(string= (the-as string arg0) (symbol-to-string arg1))
|
||||
(string= (the-as string arg0) (symbol->string arg1))
|
||||
)
|
||||
((and (= (-> arg1 type) string) (= (-> arg0 type) symbol))
|
||||
(string= (the-as string arg1) (symbol-to-string arg0))
|
||||
(string= (the-as string arg1) (symbol->string arg0))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -659,3 +659,8 @@
|
||||
)
|
||||
(define-extern add-debug-line (function symbol int vector vector rgba symbol int int))
|
||||
|
||||
(defconstant SYM_TO_STRING_OFFSET #xff38)
|
||||
(defmacro symbol->string (sym)
|
||||
"Convert a symbol to a goal string."
|
||||
`(-> (the-as (pointer string) (+ SYM_TO_STRING_OFFSET (the-as int ,sym))))
|
||||
)
|
||||
|
||||
@@ -114,16 +114,10 @@
|
||||
(string= (the-as string arg0) (the-as string arg1))
|
||||
)
|
||||
((and (= (-> arg0 type) string) (= (-> arg1 type) symbol))
|
||||
(string=
|
||||
(the-as string arg0)
|
||||
(the-as string (-> (the-as (pointer uint32) (+ #xff38 (the-as int arg1)))))
|
||||
)
|
||||
(string= (the-as string arg0) (symbol->string (the-as symbol arg1)))
|
||||
)
|
||||
((and (= (-> arg1 type) string) (= (-> arg0 type) symbol))
|
||||
(string=
|
||||
(the-as string arg1)
|
||||
(the-as string (-> (the-as (pointer uint32) (+ #xff38 (the-as int arg0)))))
|
||||
)
|
||||
(string= (the-as string arg1) (symbol->string (the-as symbol arg0)))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -2441,3 +2441,509 @@ TEST_F(FormRegressionTest, ExprStringToInt) {
|
||||
" )";
|
||||
test_final_function(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, Method19ResTag) {
|
||||
std::string func =
|
||||
"sll r0, r0, 0\n"
|
||||
"L101:\n"
|
||||
" daddiu sp, sp, -16\n"
|
||||
" sd fp, 8(sp)\n"
|
||||
" or fp, t9, r0\n"
|
||||
" daddiu v1, s7, id\n"
|
||||
" dsubu v1, a1, v1\n"
|
||||
" daddiu t0, s7, 8\n"
|
||||
" movn t0, s7, v1\n"
|
||||
" bnel s7, t0, L102\n"
|
||||
" or v1, t0, r0\n"
|
||||
"\n"
|
||||
" daddiu v1, s7, aid\n"
|
||||
" dsubu v1, a1, v1\n"
|
||||
" daddiu t0, s7, 8\n"
|
||||
" movn t0, s7, v1\n"
|
||||
" bnel s7, t0, L102\n"
|
||||
" or v1, t0, r0\n"
|
||||
"\n"
|
||||
" daddiu v1, s7, trans\n"
|
||||
" dsubu v1, a1, v1\n"
|
||||
" daddiu t0, s7, 8\n"
|
||||
" movn t0, s7, v1\n"
|
||||
" bnel s7, t0, L102\n"
|
||||
" or v1, t0, r0\n"
|
||||
"\n"
|
||||
" daddiu v1, s7, rot\n"
|
||||
" dsubu v1, a1, v1\n"
|
||||
" daddiu t0, s7, 8\n"
|
||||
" movn t0, s7, v1\n"
|
||||
" bnel s7, t0, L102\n"
|
||||
" or v1, t0, r0\n"
|
||||
"\n"
|
||||
" daddiu v1, s7, nav-mesh\n"
|
||||
" dsubu v1, a1, v1\n"
|
||||
" daddiu t0, s7, 8\n"
|
||||
" movn t0, s7, v1\n"
|
||||
" bnel s7, t0, L102\n"
|
||||
" or v1, t0, r0\n"
|
||||
"\n"
|
||||
" daddiu v1, s7, process-type\n"
|
||||
" dsubu v1, a1, v1\n"
|
||||
" daddiu t0, s7, 8\n"
|
||||
" movn t0, s7, v1\n"
|
||||
" bnel s7, t0, L102\n"
|
||||
" or v1, t0, r0\n"
|
||||
"\n"
|
||||
" daddiu v1, s7, task\n"
|
||||
" dsubu t0, a1, v1\n"
|
||||
" daddiu v1, s7, 8\n"
|
||||
" movn v1, s7, t0\n"
|
||||
"L102:\n"
|
||||
" beq s7, v1, L103\n"
|
||||
" or v1, s7, r0\n"
|
||||
"\n"
|
||||
" sd r0, 2(r0)\n"
|
||||
" or v1, r0, r0\n"
|
||||
"L103:\n"
|
||||
" beql s7, a0, L104\n"
|
||||
" daddiu v1, s7, 8\n"
|
||||
"\n"
|
||||
" beql a0, r0, L104\n"
|
||||
" daddiu v1, s7, 8\n"
|
||||
"\n"
|
||||
" lw v1, 0(a0)\n"
|
||||
" slt t0, r0, v1\n"
|
||||
" daddiu v1, s7, 8\n"
|
||||
" movn v1, s7, t0\n"
|
||||
"L104:\n"
|
||||
" beq s7, v1, L105\n"
|
||||
" or v1, s7, r0\n"
|
||||
"\n"
|
||||
" addiu v0, r0, -1\n"
|
||||
" beq r0, r0, L129\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
" or v1, r0, r0\n"
|
||||
"L105:\n"
|
||||
" addiu v1, r0, -1\n"
|
||||
" addiu t0, r0, -1\n"
|
||||
" addiu t1, r0, -1\n"
|
||||
" ori t2, r0, 65336\n"
|
||||
" daddu t2, t2, a1\n"
|
||||
"\n"
|
||||
" lwu t2, 0(t2)\n"
|
||||
" daddiu t2, t2, 4\n"
|
||||
" ld t2, 0(t2)\n"
|
||||
"\n"
|
||||
" lw t3, 0(a0)\n"
|
||||
" daddiu t3, t3, -1\n"
|
||||
" addiu t4, r0, 0\n"
|
||||
" beq r0, r0, L109\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
"L106:\n"
|
||||
" dsubu t5, t3, t4\n"
|
||||
"\n"
|
||||
" dsra t5, t5, 1\n"
|
||||
" daddu t5, t4, t5\n"
|
||||
" ori t6, r0, 65336\n"
|
||||
" lwu t7, 24(a0)\n"
|
||||
" dsll t8, t5, 4\n"
|
||||
" daddu t7, t7, t8\n"
|
||||
"\n"
|
||||
" lq t7, 0(t7)\n"
|
||||
" dsll32 t7, t7, 0\n"
|
||||
" dsrl32 t7, t7, 0\n"
|
||||
" daddu t6, t6, t7\n"
|
||||
"\n"
|
||||
" lwu t6, 0(t6)\n"
|
||||
" daddiu t6, t6, 4\n"
|
||||
" ld t6, 0(t6)\n"
|
||||
"\n"
|
||||
" dsubu t6, t2, t6\n"
|
||||
" bne t6, r0, L107\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
" or t4, t5, r0\n"
|
||||
" beq r0, r0, L110\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
" or v1, r0, r0\n"
|
||||
" beq r0, r0, L109\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
"L107:\n"
|
||||
" slt t6, t6, r0\n"
|
||||
" beq t6, r0, L108\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
" daddiu t3, t5, -1\n"
|
||||
" or t5, t3, r0\n"
|
||||
" beq r0, r0, L109\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
"L108:\n"
|
||||
" daddiu t4, t5, 1\n"
|
||||
" or t5, t4, r0\n"
|
||||
"L109:\n"
|
||||
" slt t5, t3, t4\n"
|
||||
"\n"
|
||||
" beq t5, r0, L106\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
" or t3, s7, r0\n"
|
||||
" addiu t4, r0, -1\n"
|
||||
"L110:\n"
|
||||
" slt t3, t4, r0\n"
|
||||
" beq t3, r0, L111\n"
|
||||
" or t3, s7, r0\n"
|
||||
"\n"
|
||||
" or v0, t4, r0\n"
|
||||
" beq r0, r0, L129\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
" or v1, r0, r0\n"
|
||||
"L111:\n"
|
||||
" beq r0, r0, L113\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
"L112:\n"
|
||||
" daddiu t4, t4, -1\n"
|
||||
"L113:\n"
|
||||
" slt t3, r0, t4\n"
|
||||
" daddiu t5, s7, 8\n"
|
||||
" movz t5, s7, t3\n"
|
||||
" beql s7, t5, L114\n"
|
||||
" or t3, t5, r0\n"
|
||||
"\n"
|
||||
" ori t3, r0, 65336\n"
|
||||
" lwu t5, 24(a0)\n"
|
||||
" daddiu t6, t4, -1\n"
|
||||
" dsll t6, t6, 4\n"
|
||||
" daddu t5, t5, t6\n"
|
||||
"\n"
|
||||
" lq t5, 0(t5)\n"
|
||||
" dsll32 t5, t5, 0\n"
|
||||
" dsrl32 t5, t5, 0\n"
|
||||
" daddu t3, t3, t5\n"
|
||||
"\n"
|
||||
" lwu t3, 0(t3)\n"
|
||||
" daddiu t3, t3, 4\n"
|
||||
" ld t3, 0(t3)\n"
|
||||
"\n"
|
||||
" dsubu t5, t2, t3\n"
|
||||
" daddiu t3, s7, 8\n"
|
||||
" movn t3, s7, t5\n"
|
||||
"L114:\n"
|
||||
" bne s7, t3, L112\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
" or t3, s7, r0\n"
|
||||
" daddiu t3, s7, base\n"
|
||||
" bne a2, t3, L115\n"
|
||||
" or t3, s7, r0\n"
|
||||
"\n"
|
||||
" or t0, t4, r0\n"
|
||||
" or v1, t4, r0\n"
|
||||
" or a0, s7, r0\n"
|
||||
" beq r0, r0, L128\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
" or v1, r0, r0\n"
|
||||
"L115:\n"
|
||||
" or t3, t4, r0\n"
|
||||
" dsll t4, t4, 4\n"
|
||||
" daddu t4, r0, t4\n"
|
||||
" lwu t5, 24(a0)\n"
|
||||
" daddu t4, t4, t5\n"
|
||||
"\n"
|
||||
" beq r0, r0, L126\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
"L116:\n"
|
||||
" lq t5, 0(t4)\n"
|
||||
" dsll32 t5, t5, 0\n"
|
||||
" dsrl32 t5, t5, 0\n"
|
||||
" beq a1, t5, L117\n"
|
||||
" or t5, s7, r0\n"
|
||||
"\n"
|
||||
" or t5, s7, r0\n"
|
||||
" beq r0, r0, L125\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
"L117:\n"
|
||||
" lq t5, 0(t4)\n"
|
||||
" dsra32 t5, t5, 0\n"
|
||||
" mtc1 f0, t5\n"
|
||||
" mtc1 f1, a3\n"
|
||||
" c.eq.s f0, f1\n"
|
||||
" bc1f L118\n"
|
||||
" or t5, s7, r0\n"
|
||||
"\n"
|
||||
" or t0, t3, r0\n"
|
||||
" or v1, t3, r0\n"
|
||||
" or a0, s7, r0\n"
|
||||
" beq r0, r0, L128\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
" or v1, r0, r0\n"
|
||||
" beq r0, r0, L125\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
"L118:\n"
|
||||
" mtc1 f0, a3\n"
|
||||
" lq t5, 0(t4)\n"
|
||||
" dsra32 t5, t5, 0\n"
|
||||
" mtc1 f1, t5\n"
|
||||
" c.lt.s f0, f1\n"
|
||||
" bc1f L119\n"
|
||||
" daddiu t5, s7, 8\n"
|
||||
"\n"
|
||||
" or t5, s7, r0\n"
|
||||
"L119:\n"
|
||||
" beql s7, t5, L120\n"
|
||||
" or t5, t5, r0\n"
|
||||
"\n"
|
||||
" daddiu t5, s7, exact\n"
|
||||
" dsubu t6, a2, t5\n"
|
||||
" daddiu t5, s7, 8\n"
|
||||
" movz t5, s7, t6\n"
|
||||
"L120:\n"
|
||||
" beq s7, t5, L122\n"
|
||||
" or t5, s7, r0\n"
|
||||
"\n"
|
||||
" or t0, t3, r0\n"
|
||||
" or v1, t3, r0\n"
|
||||
" lq t5, 0(t4)\n"
|
||||
" dsra32 t5, t5, 0\n"
|
||||
" mtc1 f0, t5\n"
|
||||
//" lwc1 f1, L147(fp)\n"
|
||||
" mtc1 f1, r0\n"
|
||||
" c.eq.s f0, f1\n"
|
||||
" bc1f L121\n"
|
||||
" or t5, s7, r0\n"
|
||||
"\n"
|
||||
" or t1, t3, r0\n"
|
||||
" or t5, t1, r0\n"
|
||||
"L121:\n"
|
||||
" beq r0, r0, L125\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
"L122:\n"
|
||||
" mtc1 f0, a3\n"
|
||||
" lq t5, 0(t4)\n"
|
||||
" dsra32 t5, t5, 0\n"
|
||||
" mtc1 f1, t5\n"
|
||||
" c.lt.s f0, f1\n"
|
||||
" bc1f L125\n"
|
||||
" or t5, s7, r0\n"
|
||||
"\n"
|
||||
" dsubu a0, t0, t1\n"
|
||||
"\n"
|
||||
" daddiu a1, s7, 8\n"
|
||||
" movz a1, s7, a0\n"
|
||||
" beql s7, a1, L123\n"
|
||||
" or a0, a1, r0\n"
|
||||
"\n"
|
||||
" daddiu a0, s7, interp\n"
|
||||
" dsubu a1, a2, a0\n"
|
||||
" daddiu a0, s7, 8\n"
|
||||
" movn a0, s7, a1\n"
|
||||
"L123:\n"
|
||||
" beq s7, a0, L124\n"
|
||||
" or a0, s7, r0\n"
|
||||
"\n"
|
||||
" or v1, t3, r0\n"
|
||||
" or a0, v1, r0\n"
|
||||
"L124:\n"
|
||||
" or a0, s7, r0\n"
|
||||
" beq r0, r0, L128\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
" or v1, r0, r0\n"
|
||||
"L125:\n"
|
||||
" daddiu t3, t3, 1\n"
|
||||
" daddiu t4, t4, 16\n"
|
||||
"L126:\n"
|
||||
" lw t5, 0(a0)\n"
|
||||
" slt t5, t3, t5\n"
|
||||
" daddiu t6, s7, 8\n"
|
||||
" movn t6, s7, t5\n"
|
||||
" bnel s7, t6, L127\n"
|
||||
" or t5, t6, r0\n"
|
||||
"\n"
|
||||
" ori t5, r0, 65336\n"
|
||||
" lq t6, 0(t4)\n"
|
||||
" dsll32 t6, t6, 0\n"
|
||||
" dsrl32 t6, t6, 0\n"
|
||||
" daddu t5, t5, t6\n"
|
||||
"\n"
|
||||
" lwu t5, 0(t5)\n"
|
||||
" daddiu t5, t5, 4\n"
|
||||
" ld t5, 0(t5)\n"
|
||||
"\n"
|
||||
" sltu t6, t2, t5\n"
|
||||
" daddiu t5, s7, 8\n"
|
||||
" movz t5, s7, t6\n"
|
||||
"L127:\n"
|
||||
" beq s7, t5, L116\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"\n"
|
||||
" or a0, s7, r0\n"
|
||||
" or a0, s7, r0\n"
|
||||
"L128:\n"
|
||||
//" ld a0, L150(fp)\n"
|
||||
" or a0, r0, r0\n"
|
||||
" and a0, a0, t0\n"
|
||||
" dsll32 v1, v1, 0\n"
|
||||
" or v0, a0, v1\n"
|
||||
"\n"
|
||||
"L129:\n"
|
||||
" ld fp, 8(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 16";
|
||||
std::string type = "(function res-lump symbol symbol float int)";
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (when\n"
|
||||
" (or\n"
|
||||
" (= arg1 'id)\n"
|
||||
" (= arg1 'aid)\n"
|
||||
" (= arg1 'trans)\n"
|
||||
" (= arg1 'rot)\n"
|
||||
" (= arg1 'nav-mesh)\n"
|
||||
" (= arg1 'process-type)\n"
|
||||
" (= arg1 'task)\n"
|
||||
" )\n"
|
||||
" (crash!)\n"
|
||||
" (let ((v1-9 0))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (if (or (not arg0) (zero? arg0) (<= (-> arg0 length) 0))\n"
|
||||
" (return -1)\n"
|
||||
" )\n"
|
||||
" (let ((v1-14 -1)\n"
|
||||
" (t0-6 -1)\n"
|
||||
" )\n"
|
||||
" (let ((t1-0 -1)\n"
|
||||
" (t2-4 (-> (the-as (pointer uint64) (-> (symbol->string arg1) data)) 0))\n"
|
||||
" )\n"
|
||||
" (let ((t3-1 (+ (-> arg0 length) -1))\n"
|
||||
" (t4-0 0)\n"
|
||||
" )\n"
|
||||
" (while (>= t3-1 t4-0)\n"
|
||||
" (let* ((t5-2 (+ t4-0 (/ (- t3-1 t4-0) 2)))\n"
|
||||
" (t6-5\n"
|
||||
" (-\n"
|
||||
" t2-4\n"
|
||||
" (->\n"
|
||||
" (the-as\n"
|
||||
" (pointer uint64)\n"
|
||||
" (-> (symbol->string (-> (-> arg0 tag) t5-2 name)) data)\n"
|
||||
" )\n"
|
||||
" 0\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (cond\n"
|
||||
" ((zero? t6-5)\n"
|
||||
" (begin\n"
|
||||
" (set! t4-1 t5-2)\n"
|
||||
" (goto cfg-32)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (else\n"
|
||||
" (if (< (the-as int t6-5) 0)\n"
|
||||
" (set! t3-1 (+ t5-2 -1))\n"
|
||||
" (set! t4-0 (+ t5-2 1))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! t4-1 -1)\n"
|
||||
" (label cfg-32)\n"
|
||||
" (if (< t4-1 0)\n"
|
||||
" (return t4-1)\n"
|
||||
" )\n"
|
||||
" (while\n"
|
||||
" (and\n"
|
||||
" (> t4-1 0)\n"
|
||||
" (=\n"
|
||||
" t2-4\n"
|
||||
" (->\n"
|
||||
" (the-as\n"
|
||||
" (pointer uint64)\n"
|
||||
" (-> (symbol->string (-> (-> arg0 tag) (+ t4-1 -1) name)) data)\n"
|
||||
" )\n"
|
||||
" 0\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (+! t4-1 -1)\n"
|
||||
" )\n"
|
||||
" (if (= arg2 'base)\n"
|
||||
" (begin\n"
|
||||
" (set! t0-6 t4-1)\n"
|
||||
" (set! v1-14 t4-1)\n"
|
||||
" (goto cfg-73)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (let ((t3-13 t4-1)\n"
|
||||
" (t4-4 (&-> (-> arg0 tag) t4-1))\n"
|
||||
" )\n"
|
||||
" (while\n"
|
||||
" (not\n"
|
||||
" (or\n"
|
||||
" (>= t3-13 (-> arg0 length))\n"
|
||||
" (<\n"
|
||||
" t2-4\n"
|
||||
" (->\n"
|
||||
" (the-as (pointer uint64) (-> (symbol->string (-> t4-4 0 name)) data))\n"
|
||||
" 0\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (cond\n"
|
||||
" ((!= arg1 (-> t4-4 0 name))\n"
|
||||
" )\n"
|
||||
" ((= (-> t4-4 0 key-frame) arg3)\n"
|
||||
" (begin\n"
|
||||
" (set! t0-6 t3-13)\n"
|
||||
" (set! v1-14 t3-13)\n"
|
||||
" (goto cfg-73)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" ((and (>= arg3 (-> t4-4 0 key-frame)) (!= arg2 'exact))\n"
|
||||
" (set! t0-6 t3-13)\n"
|
||||
" (set! v1-14 t3-13)\n"
|
||||
" (if (= (-> t4-4 0 key-frame) 0.0)\n"
|
||||
" (set! t1-0 t3-13)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" ((< arg3 (-> t4-4 0 key-frame))\n"
|
||||
" (begin\n"
|
||||
" (if (and (!= t0-6 t1-0) (= arg2 'interp))\n"
|
||||
" (set! v1-14 t3-13)\n"
|
||||
" )\n"
|
||||
" (goto cfg-73)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (+! t3-13 1)\n"
|
||||
" (set! t4-4 (&-> t4-4 1))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (label cfg-73)\n"
|
||||
" (logior (logand 0 t0-6) (shl v1-14 32))\n"
|
||||
" )\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "", {},
|
||||
"[\n"
|
||||
" [46, \"t2\", \"(pointer uint64)\"],\n"
|
||||
" [100, \"t3\", \"(pointer uint64)\"],\n"
|
||||
" [184, \"t5\", \"(pointer uint64)\"],\n"
|
||||
" [64, \"t6\", \"(pointer uint64)\"]\n"
|
||||
" ]");
|
||||
}
|
||||
Reference in New Issue
Block a user