[decompiler] support break (#516)

* support break

* finish implementation of break, decomp method 19 of res
This commit is contained in:
water111
2021-05-23 20:16:34 -04:00
committed by GitHub
parent a96710f0c5
commit d719bf5677
22 changed files with 1132 additions and 126 deletions
-1
View File
@@ -58,7 +58,6 @@ add_library(
VuDisasm/VuDisassembler.cpp
VuDisasm/VuInstruction.cpp
VuDisasm/VuProgram.cpp
config.cpp)
+66 -2
View File
@@ -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(),
+20 -1
View File
@@ -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;
+13
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+47
View File
@@ -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
+1
View File
@@ -144,6 +144,7 @@ enum class FixedOperatorKind {
PAIRP,
NONE,
PCPYLD,
SYMBOL_TO_STRING,
INVALID
};
+10 -6
View File
@@ -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());
-3
View File
@@ -1,3 +0,0 @@
#include "VuProgram.h"
+49 -2
View File
@@ -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;
+1 -1
View File
@@ -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"
}
}
}
+1 -1
View File
@@ -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)
)
+176
View File
@@ -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))
)
)
)
+2 -2
View File
@@ -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))))
)
+2 -2
View File
@@ -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"
" ]");
}