mirror of
https://github.com/open-goal/jak-project
synced 2026-05-23 06:54:31 -04:00
d3cc739e43
This attempts to get into master whatever work was done in this PR / it's earlier PR https://github.com/open-goal/jak-project/pull/3965 I don't want this work to be lost / floating around in massive PRs. However the changes are: - switch to ntsc_v1 instead of PAL as the development target, as we have done for all other games - remove most of the copied-from-jak2/3 changes as they need to be confirmed during the decompilation process not just assumed - avoids committing any changes to `game/kernel/common` as it was not clear to me if these were changes made in jak x's kernel that were not properly broken out into it's own functions. We don't want to accidentally introduce bugs into jak1-3's kernel code. - in other words, if the change in the kernel only happens in jak x...it should likely be specific to jak x's kernel, not common. --------- Co-authored-by: VodBox <dillon@vodbox.io> Co-authored-by: yodah <greenboyyodah@gmail.com>
3660 lines
109 KiB
C++
3660 lines
109 KiB
C++
#include "Form.h"
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
#include "common/goos/PrettyPrinter.h"
|
|
#include "common/type_system/TypeSystem.h"
|
|
#include "common/util/print_float.h"
|
|
|
|
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
|
#include "decompiler/util/DecompilerTypeSystem.h"
|
|
#include "decompiler/util/data_decompile.h"
|
|
#include "decompiler/util/sparticle_decompile.h"
|
|
|
|
#include "fmt/ranges.h"
|
|
|
|
namespace decompiler {
|
|
|
|
///////////////////
|
|
// FormPool
|
|
///////////////////
|
|
|
|
FormPool::~FormPool() {
|
|
for (auto& x : m_forms) {
|
|
delete x;
|
|
}
|
|
|
|
for (auto& x : m_elements) {
|
|
delete x;
|
|
}
|
|
}
|
|
|
|
///////////////////
|
|
// FormElement
|
|
///////////////////
|
|
|
|
std::string FormElement::to_string(const Env& env) const {
|
|
return to_form(env).print();
|
|
}
|
|
|
|
void FormElement::push_to_stack(const Env& env, FormPool&, FormStack&) {
|
|
throw std::runtime_error(fmt::format("push_to_stack not implemented for {}: {}", to_string(env),
|
|
typeid(*this).name()));
|
|
}
|
|
|
|
goos::Object FormElement::to_form_as_condition_internal(const Env& env) const {
|
|
return to_form(env);
|
|
}
|
|
|
|
bool FormElement::active() const {
|
|
return true;
|
|
}
|
|
|
|
goos::Object FormElement::to_form(const Env& env) const {
|
|
if (active()) {
|
|
return to_form_internal(env);
|
|
} else {
|
|
return pretty_print::build_list("empty-form");
|
|
}
|
|
}
|
|
|
|
///////////////////
|
|
// Form
|
|
//////////////////
|
|
|
|
goos::Object Form::to_form(const Env& env) const {
|
|
ASSERT(!m_elements.empty());
|
|
if (m_elements.size() == 1) {
|
|
return m_elements.front()->to_form(env);
|
|
} else {
|
|
std::vector<goos::Object> forms;
|
|
forms.push_back(pretty_print::to_symbol("begin"));
|
|
for (auto& x : m_elements) {
|
|
if (x->active()) {
|
|
forms.push_back(x->to_form_internal(env));
|
|
}
|
|
}
|
|
|
|
if (forms.size() == 2) {
|
|
return forms.at(1);
|
|
}
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
}
|
|
|
|
goos::Object Form::to_form_as_condition(const Env& env) const {
|
|
ASSERT(!m_elements.empty());
|
|
if (m_elements.size() == 1) {
|
|
return m_elements.front()->to_form_as_condition_internal(env);
|
|
} else {
|
|
std::vector<goos::Object> forms;
|
|
forms.push_back(pretty_print::to_symbol("begin"));
|
|
for (size_t i = 0; i < m_elements.size(); i++) {
|
|
const auto& x = m_elements.at(i);
|
|
if (i == m_elements.size() - 1) {
|
|
forms.push_back(x->to_form_as_condition_internal(env));
|
|
} else {
|
|
if (x->active()) {
|
|
forms.push_back(x->to_form_internal(env));
|
|
}
|
|
}
|
|
}
|
|
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
}
|
|
|
|
std::string Form::to_string(const Env& env) const {
|
|
return to_form(env).print();
|
|
}
|
|
|
|
void Form::inline_forms(std::vector<goos::Object>& forms, const Env& env) const {
|
|
for (auto& x : m_elements) {
|
|
if (x->active()) {
|
|
forms.push_back(x->to_form_internal(env));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Form::apply(const std::function<void(FormElement*)>& f) {
|
|
for (size_t i = 0; i < m_elements.size(); i++) {
|
|
m_elements.at(i)->apply(f);
|
|
}
|
|
}
|
|
|
|
void Form::apply_form(const std::function<void(Form*)>& f) {
|
|
f(this);
|
|
for (auto& x : m_elements) {
|
|
x->apply_form(f);
|
|
}
|
|
}
|
|
|
|
void Form::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
for (auto e : m_elements) {
|
|
e->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void Form::get_modified_regs(RegSet& regs) const {
|
|
for (auto e : m_elements) {
|
|
e->get_modified_regs(regs);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// SimpleExpressionElement
|
|
/////////////////////////////
|
|
|
|
SimpleExpressionElement::SimpleExpressionElement(SimpleExpression expr, int my_idx)
|
|
: m_expr(std::move(expr)), m_my_idx(my_idx) {}
|
|
|
|
goos::Object SimpleExpressionElement::to_form_internal(const Env& env) const {
|
|
return m_expr.to_form(env.file->labels, env);
|
|
}
|
|
|
|
void SimpleExpressionElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void SimpleExpressionElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
bool SimpleExpressionElement::is_sequence_point() const {
|
|
throw std::runtime_error("Should not check if a SimpleExpressionElement is a sequence point");
|
|
}
|
|
|
|
void SimpleExpressionElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
m_expr.collect_vars(vars);
|
|
}
|
|
|
|
void SimpleExpressionElement::get_modified_regs(RegSet& regs) const {
|
|
(void)regs;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// StoreElement
|
|
/////////////////////////////
|
|
|
|
StoreElement::StoreElement(const StoreOp* op) : m_op(op) {}
|
|
|
|
goos::Object StoreElement::to_form_internal(const Env& env) const {
|
|
return m_op->to_form(env.file->labels, env);
|
|
}
|
|
|
|
void StoreElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void StoreElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void StoreElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
return m_op->collect_vars(vars);
|
|
}
|
|
|
|
void StoreElement::get_modified_regs(RegSet& regs) const {
|
|
(void)regs;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// LoadSourceElement
|
|
/////////////////////////////
|
|
|
|
LoadSourceElement::LoadSourceElement(Form* addr,
|
|
int size,
|
|
LoadVarOp::Kind kind,
|
|
const std::optional<IR2_RegOffset>& load_source_ro,
|
|
const TP_Type& ro_reg_type)
|
|
: m_addr(addr),
|
|
m_size(size),
|
|
m_kind(kind),
|
|
m_load_source_ro(load_source_ro),
|
|
m_ro_reg_type(ro_reg_type) {
|
|
m_addr->parent_element = this;
|
|
}
|
|
|
|
goos::Object LoadSourceElement::to_form_internal(const Env& env) const {
|
|
switch (m_kind) {
|
|
case LoadVarOp::Kind::FLOAT:
|
|
ASSERT(m_size == 4);
|
|
return pretty_print::build_list("l.f", m_addr->to_form(env));
|
|
case LoadVarOp::Kind::UNSIGNED:
|
|
switch (m_size) {
|
|
case 1:
|
|
return pretty_print::build_list("l.bu", m_addr->to_form(env));
|
|
case 2:
|
|
return pretty_print::build_list("l.hu", m_addr->to_form(env));
|
|
case 4:
|
|
return pretty_print::build_list("l.wu", m_addr->to_form(env));
|
|
case 8:
|
|
return pretty_print::build_list("l.d", m_addr->to_form(env));
|
|
case 16:
|
|
return pretty_print::build_list("l.q", m_addr->to_form(env));
|
|
default:
|
|
ASSERT(false);
|
|
return {};
|
|
}
|
|
break;
|
|
case LoadVarOp::Kind::SIGNED:
|
|
switch (m_size) {
|
|
case 1:
|
|
return pretty_print::build_list("l.b", m_addr->to_form(env));
|
|
case 2:
|
|
return pretty_print::build_list("l.h", m_addr->to_form(env));
|
|
case 4:
|
|
return pretty_print::build_list("l.w", m_addr->to_form(env));
|
|
default:
|
|
ASSERT(false);
|
|
return {};
|
|
}
|
|
break;
|
|
case LoadVarOp::Kind::VECTOR_FLOAT:
|
|
ASSERT(m_size == 16);
|
|
return pretty_print::build_list("l.vf", m_addr->to_form(env));
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
return {};
|
|
}
|
|
}
|
|
|
|
void LoadSourceElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_addr->apply(f);
|
|
}
|
|
|
|
void LoadSourceElement::apply_form(const std::function<void(Form*)>& f) {
|
|
m_addr->apply_form(f);
|
|
}
|
|
|
|
void LoadSourceElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
m_addr->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void LoadSourceElement::get_modified_regs(RegSet& regs) const {
|
|
m_addr->get_modified_regs(regs);
|
|
}
|
|
|
|
/////////////////////////////
|
|
// SimpleAtomElement
|
|
/////////////////////////////
|
|
|
|
SimpleAtomElement::SimpleAtomElement(const SimpleAtom& atom, bool omit_var_cast)
|
|
: m_atom(atom), m_omit_var_cast(omit_var_cast), m_no_hex(false) {
|
|
if (m_omit_var_cast) {
|
|
ASSERT(atom.is_var());
|
|
}
|
|
}
|
|
|
|
SimpleAtomElement::SimpleAtomElement(int int_val, bool no_hex)
|
|
: m_atom(SimpleAtom::make_int_constant(int_val)), m_omit_var_cast(false), m_no_hex(no_hex) {
|
|
if (m_no_hex) {
|
|
m_atom.mark_as_no_hex();
|
|
}
|
|
}
|
|
|
|
goos::Object SimpleAtomElement::to_form_internal(const Env& env) const {
|
|
if (m_omit_var_cast) {
|
|
return m_atom.var().to_form(env, RegisterAccess::Print::AS_VARIABLE_NO_CAST);
|
|
}
|
|
return m_atom.to_form(env.file->labels, env);
|
|
}
|
|
|
|
void SimpleAtomElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void SimpleAtomElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void SimpleAtomElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
return m_atom.collect_vars(vars);
|
|
}
|
|
|
|
void SimpleAtomElement::get_modified_regs(RegSet& regs) const {
|
|
(void)regs;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// SetVarElement
|
|
/////////////////////////////
|
|
|
|
SetVarElement::SetVarElement(const RegisterAccess& var,
|
|
Form* value,
|
|
bool is_sequence_point,
|
|
TypeSpec src_type,
|
|
const SetVarInfo& info)
|
|
: m_dst(var),
|
|
m_src(value),
|
|
m_is_sequence_point(is_sequence_point),
|
|
m_src_type(std::move(src_type)),
|
|
m_var_info(info) {
|
|
value->parent_element = this;
|
|
}
|
|
|
|
goos::Object SetVarElement::to_form_internal(const Env& env) const {
|
|
ASSERT(active());
|
|
auto reg_kind = m_dst.reg().get_kind();
|
|
if ((reg_kind == Reg::FPR || reg_kind == Reg::GPR) && env.has_type_analysis() &&
|
|
env.has_local_vars()) {
|
|
auto expected_type = env.get_variable_type(m_dst, true);
|
|
if (!env.dts->ts.tc(expected_type, m_src_type)) {
|
|
return pretty_print::build_list(
|
|
"set!", m_dst.to_form(env, RegisterAccess::Print::AS_VARIABLE_NO_CAST),
|
|
pretty_print::build_list("the-as", expected_type.print(), m_src->to_form(env)));
|
|
}
|
|
}
|
|
|
|
return pretty_print::build_list(
|
|
"set!", m_dst.to_form(env, RegisterAccess::Print::AS_VARIABLE_NO_CAST), m_src->to_form(env));
|
|
}
|
|
|
|
std::optional<TypeSpec> SetVarElement::required_cast(const Env& env) const {
|
|
auto expected_type = env.get_variable_type(m_dst, true);
|
|
if (!env.dts->ts.tc(expected_type, m_src_type)) {
|
|
return expected_type;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
void SetVarElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_src->apply(f);
|
|
}
|
|
|
|
void SetVarElement::apply_form(const std::function<void(Form*)>& f) {
|
|
m_src->apply_form(f);
|
|
}
|
|
|
|
bool SetVarElement::is_sequence_point() const {
|
|
return m_is_sequence_point;
|
|
}
|
|
|
|
void SetVarElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (m_var_info.is_dead_set || m_var_info.is_dead_false) {
|
|
return;
|
|
}
|
|
vars.insert(m_dst);
|
|
if (recursive) {
|
|
m_src->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void SetVarElement::get_modified_regs(RegSet& regs) const {
|
|
regs.insert(m_dst.reg());
|
|
m_src->get_modified_regs(regs);
|
|
}
|
|
|
|
bool SetVarElement::active() const {
|
|
if (is_eliminated_coloring_move() || is_dead_false_set()) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
StoreInSymbolElement::StoreInSymbolElement(std::string sym_name,
|
|
SimpleExpression value,
|
|
std::optional<TypeSpec> cast_for_set,
|
|
std::optional<TypeSpec> cast_for_define,
|
|
int my_idx)
|
|
: m_sym_name(std::move(sym_name)),
|
|
m_value(std::move(value)),
|
|
m_cast_for_set(std::move(cast_for_set)),
|
|
m_cast_for_define(std::move(cast_for_define)),
|
|
m_my_idx(my_idx) {}
|
|
|
|
goos::Object StoreInSymbolElement::to_form_internal(const Env& env) const {
|
|
if (m_cast_for_set) {
|
|
return pretty_print::build_list(
|
|
"set!", m_sym_name,
|
|
pretty_print::build_list(fmt::format("the-as {}", m_cast_for_set->print()),
|
|
m_value.to_form(env.file->labels, env)));
|
|
} else {
|
|
return pretty_print::build_list("set!", m_sym_name, m_value.to_form(env.file->labels, env));
|
|
}
|
|
}
|
|
|
|
void StoreInSymbolElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void StoreInSymbolElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void StoreInSymbolElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
m_value.collect_vars(vars);
|
|
}
|
|
|
|
void StoreInSymbolElement::get_modified_regs(RegSet&) const {}
|
|
|
|
StoreInPairElement::StoreInPairElement(bool is_car,
|
|
RegisterAccess pair,
|
|
SimpleExpression value,
|
|
int my_idx)
|
|
: m_is_car(is_car), m_pair(pair), m_value(value), m_my_idx(my_idx) {}
|
|
|
|
goos::Object StoreInPairElement::to_form_internal(const Env& env) const {
|
|
return pretty_print::build_list(
|
|
"set!", pretty_print::build_list(m_is_car ? "car" : "cdr", m_pair.to_form(env)),
|
|
m_value.to_form(env.file->labels, env));
|
|
}
|
|
|
|
void StoreInPairElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void StoreInPairElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void StoreInPairElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
m_value.collect_vars(vars);
|
|
vars.insert(m_pair);
|
|
}
|
|
|
|
void StoreInPairElement::get_modified_regs(RegSet&) const {}
|
|
|
|
/////////////////////////////
|
|
// SetFormFormElement
|
|
/////////////////////////////
|
|
|
|
SetFormFormElement::SetFormFormElement(Form* dst,
|
|
Form* src,
|
|
std::optional<TypeSpec> cast_for_set,
|
|
std::optional<TypeSpec> cast_for_define)
|
|
: m_dst(dst),
|
|
m_src(src),
|
|
m_cast_for_set(std::move(cast_for_set)),
|
|
m_cast_for_define(std::move(cast_for_define)) {
|
|
m_dst->parent_element = this;
|
|
m_src->parent_element = this;
|
|
}
|
|
|
|
goos::Object SetFormFormElement::to_form_internal(const Env& env) const {
|
|
if (m_cast_for_set) {
|
|
return pretty_print::build_list(
|
|
fmt::format("set!"), m_dst->to_form(env),
|
|
pretty_print::build_list(fmt::format("the-as {}", m_cast_for_set->print()),
|
|
m_src->to_form(env)));
|
|
} else {
|
|
std::vector<goos::Object> forms = {pretty_print::to_symbol("set!"), m_dst->to_form(env),
|
|
m_src->to_form(env)};
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
}
|
|
|
|
goos::Object SetFormFormElement::to_form_for_define(
|
|
const Env& env,
|
|
const std::optional<std::string>& docstring) const {
|
|
std::vector<goos::Object> forms = {pretty_print::to_symbol("define"), m_dst->to_form(env)};
|
|
if (docstring) {
|
|
forms.push_back(pretty_print::to_symbol(fmt::format("\"{}\"", docstring.value())));
|
|
}
|
|
if (m_cast_for_define) {
|
|
// for vu-function, we just put a 0. These aren't supported
|
|
if (*m_cast_for_define == TypeSpec("vu-function")) {
|
|
forms.push_back(pretty_print::build_list(fmt::format("the-as {}", m_cast_for_define->print()),
|
|
pretty_print::to_symbol("0")));
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
forms.push_back(pretty_print::build_list(fmt::format("the-as {}", m_cast_for_define->print()),
|
|
m_src->to_form(env)));
|
|
return pretty_print::build_list(forms);
|
|
} else {
|
|
forms.push_back(m_src->to_form(env));
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
}
|
|
|
|
void SetFormFormElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_src->apply(f);
|
|
m_dst->apply(f);
|
|
}
|
|
|
|
void SetFormFormElement::apply_form(const std::function<void(Form*)>& f) {
|
|
m_src->apply_form(f);
|
|
m_dst->apply_form(f);
|
|
}
|
|
|
|
bool SetFormFormElement::is_sequence_point() const {
|
|
return true;
|
|
}
|
|
|
|
void SetFormFormElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
m_src->collect_vars(vars, recursive);
|
|
m_dst->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void SetFormFormElement::get_modified_regs(RegSet& regs) const {
|
|
(void)regs;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// AtomicOpElement
|
|
/////////////////////////////
|
|
|
|
AtomicOpElement::AtomicOpElement(AtomicOp* op) : m_op(op) {}
|
|
|
|
goos::Object AtomicOpElement::to_form_internal(const Env& env) const {
|
|
return m_op->to_form(env.file->labels, env);
|
|
}
|
|
|
|
void AtomicOpElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void AtomicOpElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void AtomicOpElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
m_op->collect_vars(vars);
|
|
}
|
|
|
|
void AtomicOpElement::get_modified_regs(RegSet& regs) const {
|
|
for (auto r : m_op->write_regs()) {
|
|
regs.insert(r);
|
|
}
|
|
|
|
for (auto r : m_op->clobber_regs()) {
|
|
regs.insert(r);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// AsmBranchElement
|
|
/////////////////////////////
|
|
|
|
AsmBranchElement::AsmBranchElement(AsmBranchOp* branch_op, Form* branch_delay, bool likely)
|
|
: m_branch_op(branch_op), m_branch_delay(branch_delay), m_likely(likely) {
|
|
m_branch_delay->parent_element = this;
|
|
for (auto& elt : m_branch_delay->elts()) {
|
|
ASSERT(elt->parent_form == m_branch_delay);
|
|
}
|
|
}
|
|
|
|
goos::Object AsmBranchElement::to_form_internal(const Env& env) const {
|
|
auto f = m_branch_op->to_form(env.file->labels, env);
|
|
return pretty_print::build_list(f, m_branch_delay->to_form(env)); // temp hack
|
|
}
|
|
|
|
void AsmBranchElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_branch_delay->apply(f);
|
|
}
|
|
|
|
void AsmBranchElement::apply_form(const std::function<void(Form*)>& f) {
|
|
m_branch_delay->apply_form(f);
|
|
}
|
|
|
|
void AsmBranchElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
m_branch_delay->collect_vars(vars, recursive);
|
|
}
|
|
m_branch_op->collect_vars(vars);
|
|
}
|
|
|
|
void AsmBranchElement::get_modified_regs(RegSet& regs) const {
|
|
m_branch_delay->get_modified_regs(regs);
|
|
for (auto r : m_branch_op->write_regs()) {
|
|
regs.insert(r);
|
|
}
|
|
|
|
for (auto r : m_branch_op->clobber_regs()) {
|
|
regs.insert(r);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// TranslatedAsmBranch
|
|
/////////////////////////////
|
|
|
|
TranslatedAsmBranch::TranslatedAsmBranch(Form* branch_condition,
|
|
Form* branch_delay,
|
|
int label_id,
|
|
bool likely)
|
|
: m_branch_condition(branch_condition),
|
|
m_branch_delay(branch_delay),
|
|
m_label_id(label_id),
|
|
m_likely(likely) {
|
|
if (m_branch_delay) {
|
|
m_branch_delay->parent_element = this;
|
|
}
|
|
|
|
m_branch_condition->parent_element = this;
|
|
}
|
|
|
|
goos::Object TranslatedAsmBranch::to_form_internal(const Env& env) const {
|
|
// auto& cfg = env.func->cfg;
|
|
auto& label = env.file->labels.at(m_label_id);
|
|
int instr_in_function = (label.offset / 4 - env.func->start_word);
|
|
|
|
int block_id = -20;
|
|
if (instr_in_function == env.func->basic_blocks.back().end_word) {
|
|
block_id = env.func->basic_blocks.size() - 1;
|
|
} else {
|
|
int atomic_op_in_function =
|
|
env.func->ir2.atomic_ops->instruction_to_atomic_op.at(instr_in_function);
|
|
auto& ao = env.func->ir2.atomic_ops;
|
|
|
|
for (int i = 0; i < (int)ao->block_id_to_first_atomic_op.size(); i++) {
|
|
if (ao->block_id_to_first_atomic_op.at(i) == atomic_op_in_function) {
|
|
block_id = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(block_id >= 0);
|
|
|
|
if (m_branch_delay) {
|
|
if (m_branch_delay->parent_element != this) {
|
|
ASSERT_MSG(false, fmt::format("bad ptr. Parent is {}",
|
|
m_branch_delay->parent_element->to_string(env)));
|
|
}
|
|
|
|
ASSERT(m_branch_delay->parent_element->parent_form);
|
|
std::vector<goos::Object> list = {
|
|
pretty_print::to_symbol("b!"), m_branch_condition->to_form(env),
|
|
pretty_print::to_symbol(fmt::format("cfg-{}", block_id)),
|
|
pretty_print::to_symbol(m_likely ? ":likely-delay" : ":delay"),
|
|
m_branch_delay->to_form(env)};
|
|
|
|
return pretty_print::build_list(list);
|
|
} else {
|
|
std::vector<goos::Object> list = {pretty_print::to_symbol("b!"),
|
|
m_branch_condition->to_form(env),
|
|
pretty_print::to_symbol(fmt::format("cfg-{}", block_id))};
|
|
|
|
return pretty_print::build_list(list);
|
|
}
|
|
}
|
|
|
|
void TranslatedAsmBranch::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_branch_condition->apply(f);
|
|
if (m_branch_delay) {
|
|
m_branch_delay->apply(f);
|
|
}
|
|
}
|
|
|
|
void TranslatedAsmBranch::apply_form(const std::function<void(Form*)>& f) {
|
|
m_branch_condition->apply_form(f);
|
|
if (m_branch_delay) {
|
|
m_branch_delay->apply_form(f);
|
|
}
|
|
}
|
|
|
|
void TranslatedAsmBranch::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
m_branch_condition->collect_vars(vars, recursive);
|
|
if (m_branch_delay) {
|
|
if (m_branch_delay->parent_element != this) {
|
|
ASSERT_MSG(false,
|
|
fmt::format("bad ptr. Parent is {}", (void*)m_branch_delay->parent_element));
|
|
}
|
|
|
|
for (auto& elt : m_branch_delay->elts()) {
|
|
ASSERT(elt->parent_form == m_branch_delay);
|
|
}
|
|
|
|
m_branch_delay->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TranslatedAsmBranch::get_modified_regs(RegSet& regs) const {
|
|
m_branch_condition->get_modified_regs(regs);
|
|
if (m_branch_delay) {
|
|
m_branch_delay->get_modified_regs(regs);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// AsmOpElement
|
|
/////////////////////////////
|
|
|
|
AsmOpElement::AsmOpElement(const AsmOp* op) : m_op(op) {}
|
|
|
|
goos::Object AsmOpElement::to_form_internal(const Env& env) const {
|
|
return m_op->to_form(env.file->labels, env);
|
|
}
|
|
|
|
void AsmOpElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void AsmOpElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void AsmOpElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
m_op->collect_vars(vars);
|
|
}
|
|
|
|
void AsmOpElement::get_modified_regs(RegSet& regs) const {
|
|
for (auto r : m_op->write_regs()) {
|
|
regs.insert(r);
|
|
}
|
|
|
|
for (auto r : m_op->clobber_regs()) {
|
|
regs.insert(r);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// OpenGoalAsmOpElement
|
|
/////////////////////////////
|
|
|
|
OpenGoalAsmOpElement::OpenGoalAsmOpElement(const AsmOp* op) : m_op(op) {}
|
|
|
|
goos::Object OpenGoalAsmOpElement::to_form_internal(const Env& env) const {
|
|
return m_op->to_open_goal_form(env.file->labels, env);
|
|
}
|
|
|
|
void OpenGoalAsmOpElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void OpenGoalAsmOpElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void OpenGoalAsmOpElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
m_op->collect_vars(vars);
|
|
}
|
|
|
|
void OpenGoalAsmOpElement::collect_vf_regs(RegSet& regs) const {
|
|
for (auto r : m_op->read_regs()) {
|
|
if (r.is_vu_float()) {
|
|
regs.insert(r);
|
|
}
|
|
}
|
|
|
|
for (auto r : m_op->write_regs()) {
|
|
if (r.is_vu_float()) {
|
|
regs.insert(r);
|
|
}
|
|
}
|
|
|
|
for (auto r : m_op->clobber_regs()) {
|
|
if (r.is_vu_float()) {
|
|
regs.insert(r);
|
|
}
|
|
}
|
|
}
|
|
|
|
void OpenGoalAsmOpElement::get_modified_regs(RegSet& regs) const {
|
|
for (auto r : m_op->write_regs()) {
|
|
regs.insert(r);
|
|
}
|
|
|
|
for (auto r : m_op->clobber_regs()) {
|
|
regs.insert(r);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// ConditionElement
|
|
/////////////////////////////
|
|
|
|
ConditionElement::ConditionElement(IR2_Condition::Kind kind,
|
|
std::optional<SimpleAtom> src0,
|
|
std::optional<SimpleAtom> src1,
|
|
RegSet consumed,
|
|
bool flipped)
|
|
: m_kind(kind), m_consumed(std::move(consumed)), m_flipped(flipped) {
|
|
m_src[0] = src0;
|
|
m_src[1] = src1;
|
|
}
|
|
|
|
goos::Object ConditionElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> forms;
|
|
forms.push_back(pretty_print::to_symbol(get_condition_kind_name(m_kind)));
|
|
for (int i = 0; i < get_condition_num_args(m_kind); i++) {
|
|
forms.push_back(m_src[i]->to_form(env.file->labels, env));
|
|
}
|
|
if (forms.size() > 1) {
|
|
return pretty_print::build_list(forms);
|
|
} else {
|
|
return forms.front();
|
|
}
|
|
}
|
|
|
|
goos::Object ConditionElement::to_form_as_condition_internal(const Env& env) const {
|
|
if (m_kind == IR2_Condition::Kind::TRUTHY) {
|
|
return m_src[0]->to_form(env.file->labels, env);
|
|
} else {
|
|
return to_form_internal(env);
|
|
}
|
|
}
|
|
|
|
void ConditionElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void ConditionElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void ConditionElement::invert() {
|
|
m_kind = get_condition_opposite(m_kind);
|
|
}
|
|
|
|
void ConditionElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
for (auto src : m_src) {
|
|
if (src.has_value() && src->is_var()) {
|
|
vars.insert(src->var());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConditionElement::get_modified_regs(RegSet& regs) const {
|
|
(void)regs;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// FunctionCallElement
|
|
/////////////////////////////
|
|
|
|
FunctionCallElement::FunctionCallElement(const CallOp* op) : m_op(op) {}
|
|
|
|
goos::Object FunctionCallElement::to_form_internal(const Env& env) const {
|
|
return m_op->to_form(env.file->labels, env);
|
|
}
|
|
|
|
void FunctionCallElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void FunctionCallElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void FunctionCallElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
return m_op->collect_vars(vars);
|
|
}
|
|
|
|
void FunctionCallElement::get_modified_regs(RegSet& regs) const {
|
|
for (auto r : m_op->write_regs()) {
|
|
regs.insert(r);
|
|
}
|
|
|
|
for (auto r : m_op->clobber_regs()) {
|
|
regs.insert(r);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// BranchElement
|
|
/////////////////////////////
|
|
|
|
BranchElement::BranchElement(const BranchOp* op) : m_op(op) {}
|
|
|
|
goos::Object BranchElement::to_form_internal(const Env& env) const {
|
|
return m_op->to_form(env.file->labels, env);
|
|
}
|
|
|
|
void BranchElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void BranchElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void BranchElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
return m_op->collect_vars(vars);
|
|
}
|
|
|
|
void BranchElement::get_modified_regs(RegSet& regs) const {
|
|
for (auto r : m_op->write_regs()) {
|
|
regs.insert(r);
|
|
}
|
|
|
|
for (auto r : m_op->clobber_regs()) {
|
|
regs.insert(r);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// ReturnElement
|
|
/////////////////////////////
|
|
|
|
ReturnElement::ReturnElement(Form* _return_code, Form* _dead_code)
|
|
: return_code(_return_code), dead_code(_dead_code) {
|
|
return_code->parent_element = this;
|
|
if (dead_code) {
|
|
dead_code->parent_element = this;
|
|
}
|
|
}
|
|
|
|
goos::Object ReturnElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> forms;
|
|
forms.push_back(pretty_print::to_symbol("return"));
|
|
forms.push_back(return_code->to_form(env));
|
|
if (dead_code) {
|
|
forms.push_back(dead_code->to_form(env));
|
|
}
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
|
|
void ReturnElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
return_code->apply(f);
|
|
if (dead_code) {
|
|
dead_code->apply(f);
|
|
}
|
|
}
|
|
|
|
void ReturnElement::apply_form(const std::function<void(Form*)>& f) {
|
|
return_code->apply_form(f);
|
|
if (dead_code) {
|
|
dead_code->apply_form(f);
|
|
}
|
|
}
|
|
|
|
void ReturnElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
return_code->collect_vars(vars, recursive);
|
|
if (dead_code) {
|
|
dead_code->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReturnElement::get_modified_regs(RegSet& regs) const {
|
|
return_code->get_modified_regs(regs);
|
|
if (dead_code) {
|
|
dead_code->get_modified_regs(regs);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// BreakElement
|
|
/////////////////////////////
|
|
|
|
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;
|
|
if (dead_code) {
|
|
dead_code->parent_element = this;
|
|
}
|
|
}
|
|
|
|
goos::Object BreakElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> forms;
|
|
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 {
|
|
if (return_code->try_as_element<EmptyElement>()) {
|
|
return pretty_print::build_list(fmt::format("goto cfg-{}", lid));
|
|
} 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);
|
|
if (dead_code) {
|
|
dead_code->apply(f);
|
|
}
|
|
}
|
|
|
|
void BreakElement::apply_form(const std::function<void(Form*)>& f) {
|
|
return_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);
|
|
if (dead_code) {
|
|
dead_code->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BreakElement::get_modified_regs(RegSet& regs) const {
|
|
return_code->get_modified_regs(regs);
|
|
if (dead_code) {
|
|
dead_code->get_modified_regs(regs);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// CondWithElseElement
|
|
/////////////////////////////
|
|
|
|
CondWithElseElement::CondWithElseElement(std::vector<Entry> _entries, Form* _else_ir)
|
|
: entries(std::move(_entries)), else_ir(_else_ir) {
|
|
for (auto& e : entries) {
|
|
e.condition->parent_element = this;
|
|
e.body->parent_element = this;
|
|
}
|
|
else_ir->parent_element = this;
|
|
}
|
|
|
|
goos::Object CondWithElseElement::to_form_internal(const Env& env) const {
|
|
// for now we only turn it into an if statement if both cases won't require a begin at the top
|
|
// level. I think it is more common to write these as a two-case cond instead of an if with begin.
|
|
if (entries.size() == 1 && entries.front().body->is_reasonable_for_if() &&
|
|
else_ir->is_reasonable_for_if()) {
|
|
std::vector<goos::Object> list;
|
|
list.push_back(pretty_print::to_symbol("if"));
|
|
list.push_back(entries.front().condition->to_form_as_condition(env));
|
|
list.push_back(entries.front().body->to_form(env));
|
|
list.push_back(else_ir->to_form(env));
|
|
return pretty_print::build_list(list);
|
|
} else {
|
|
std::vector<goos::Object> list;
|
|
list.push_back(pretty_print::to_symbol("cond"));
|
|
for (auto& e : entries) {
|
|
std::vector<goos::Object> entry;
|
|
entry.push_back(e.condition->to_form_as_condition(env));
|
|
e.body->inline_forms(entry, env);
|
|
list.push_back(pretty_print::build_list(entry));
|
|
}
|
|
std::vector<goos::Object> else_form;
|
|
else_form.push_back(pretty_print::to_symbol("else"));
|
|
else_ir->inline_forms(else_form, env);
|
|
list.push_back(pretty_print::build_list(else_form));
|
|
return pretty_print::build_list(list);
|
|
}
|
|
}
|
|
|
|
void CondWithElseElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
for (auto& entry : entries) {
|
|
entry.condition->apply(f);
|
|
entry.body->apply(f);
|
|
}
|
|
else_ir->apply(f);
|
|
}
|
|
|
|
void CondWithElseElement::apply_form(const std::function<void(Form*)>& f) {
|
|
for (auto& entry : entries) {
|
|
entry.condition->apply_form(f);
|
|
entry.body->apply_form(f);
|
|
}
|
|
else_ir->apply_form(f);
|
|
}
|
|
|
|
void CondWithElseElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
for (auto& entry : entries) {
|
|
entry.condition->collect_vars(vars, recursive);
|
|
entry.body->collect_vars(vars, recursive);
|
|
}
|
|
else_ir->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void CondWithElseElement::get_modified_regs(RegSet& regs) const {
|
|
for (auto& e : entries) {
|
|
e.condition->get_modified_regs(regs);
|
|
e.body->get_modified_regs(regs);
|
|
}
|
|
else_ir->get_modified_regs(regs);
|
|
}
|
|
|
|
/////////////////////////////
|
|
// EmptyElement
|
|
/////////////////////////////
|
|
|
|
goos::Object EmptyElement::to_form_internal(const Env&) const {
|
|
return pretty_print::build_list("empty");
|
|
}
|
|
|
|
void EmptyElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void EmptyElement::apply_form(const std::function<void(Form*)>&) {}
|
|
void EmptyElement::collect_vars(RegAccessSet&, bool) const {}
|
|
void EmptyElement::get_modified_regs(RegSet&) const {}
|
|
|
|
/////////////////////////////
|
|
// RLetElement
|
|
/////////////////////////////
|
|
|
|
bool cmp(Register x, Register y) {
|
|
int comparison = x.to_string() > y.to_string();
|
|
if (comparison <= 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
RLetElement::RLetElement(Form* _body, RegSet _regs) : body(_body) {
|
|
for (auto& reg : _regs) {
|
|
sorted_regs.push_back(reg);
|
|
}
|
|
std::stable_sort(sorted_regs.begin(), sorted_regs.end(), cmp);
|
|
}
|
|
|
|
void RLetElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
body->apply(f);
|
|
}
|
|
|
|
goos::Object RLetElement::reg_list() const {
|
|
std::vector<goos::Object> regs;
|
|
for (auto& reg : sorted_regs) {
|
|
if (reg.is_vu_float()) {
|
|
std::string reg_name = reg.to_string() == "ACC" ? "acc" : reg.to_string();
|
|
regs.push_back(
|
|
pretty_print::build_list(pretty_print::to_symbol(fmt::format("{} :class vf", reg_name))));
|
|
}
|
|
}
|
|
return pretty_print::build_list(regs);
|
|
}
|
|
|
|
bool RLetElement::needs_vf0_init() const {
|
|
for (auto& reg : sorted_regs) {
|
|
if (reg.get_kind() == Reg::RegisterKind::VF && reg.to_string() == "vf0") {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
goos::Object RLetElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> rletForm;
|
|
rletForm.push_back(pretty_print::to_symbol("rlet"));
|
|
rletForm.push_back(reg_list());
|
|
|
|
// NOTE - initialize any relevant registers in the body first
|
|
if (needs_vf0_init()) {
|
|
rletForm.push_back(pretty_print::to_symbol("(init-vf0-vector)")); // Defined in vector-h.gc
|
|
}
|
|
|
|
body->inline_forms(rletForm, env);
|
|
return pretty_print::build_list(rletForm);
|
|
}
|
|
|
|
void RLetElement::apply_form(const std::function<void(Form*)>& f) {
|
|
body->apply_form(f);
|
|
}
|
|
|
|
void RLetElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
body->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void RLetElement::get_modified_regs(RegSet& regs) const {
|
|
body->get_modified_regs(regs);
|
|
}
|
|
|
|
/////////////////////////////
|
|
// WhileElement
|
|
/////////////////////////////
|
|
|
|
WhileElement::WhileElement(Form* _condition, Form* _body) : condition(_condition), body(_body) {
|
|
condition->parent_element = this;
|
|
body->parent_element = this;
|
|
}
|
|
|
|
void WhileElement::apply(const std::function<void(FormElement*)>& f) {
|
|
// note - this is done in program order, rather than print order. Not sure if this makes sense.
|
|
f(this);
|
|
body->apply(f);
|
|
condition->apply(f);
|
|
}
|
|
|
|
goos::Object WhileElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> list;
|
|
auto cond = condition->to_form_as_condition(env);
|
|
if (cond == pretty_print::to_symbol("#t")) {
|
|
list.push_back(pretty_print::to_symbol("loop"));
|
|
} else {
|
|
list.push_back(pretty_print::to_symbol("while"));
|
|
list.push_back(condition->to_form_as_condition(env));
|
|
}
|
|
body->inline_forms(list, env);
|
|
return pretty_print::build_list(list);
|
|
}
|
|
|
|
void WhileElement::apply_form(const std::function<void(Form*)>& f) {
|
|
body->apply_form(f);
|
|
condition->apply_form(f);
|
|
}
|
|
|
|
void WhileElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
body->collect_vars(vars, recursive);
|
|
condition->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void WhileElement::get_modified_regs(RegSet& regs) const {
|
|
condition->get_modified_regs(regs);
|
|
body->get_modified_regs(regs);
|
|
}
|
|
|
|
/////////////////////////////
|
|
// UntilElement
|
|
/////////////////////////////
|
|
|
|
UntilElement::UntilElement(Form* _condition, Form* _body) : condition(_condition), body(_body) {
|
|
condition->parent_element = this;
|
|
body->parent_element = this;
|
|
}
|
|
|
|
void UntilElement::apply(const std::function<void(FormElement*)>& f) {
|
|
// note - this is done in program order, rather than print order. Not sure if this makes sense.
|
|
f(this);
|
|
body->apply(f);
|
|
condition->apply(f);
|
|
}
|
|
|
|
goos::Object UntilElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> list;
|
|
list.push_back(pretty_print::to_symbol("until"));
|
|
list.push_back(condition->to_form_as_condition(env));
|
|
body->inline_forms(list, env);
|
|
return pretty_print::build_list(list);
|
|
}
|
|
|
|
void UntilElement::apply_form(const std::function<void(Form*)>& f) {
|
|
body->apply_form(f);
|
|
condition->apply_form(f);
|
|
}
|
|
|
|
void UntilElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
body->collect_vars(vars, recursive);
|
|
condition->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void UntilElement::get_modified_regs(RegSet& regs) const {
|
|
condition->get_modified_regs(regs);
|
|
body->get_modified_regs(regs);
|
|
}
|
|
|
|
/////////////////////////////
|
|
// ShortCircuitElement
|
|
/////////////////////////////
|
|
|
|
ShortCircuitElement::ShortCircuitElement(std::vector<Entry> _entries)
|
|
: entries(std::move(_entries)) {
|
|
for (auto& entry : entries) {
|
|
entry.condition->parent_element = this;
|
|
}
|
|
}
|
|
|
|
void ShortCircuitElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
for (auto& x : entries) {
|
|
x.condition->apply(f);
|
|
// if (x.output) {
|
|
// // not sure about this...
|
|
// x.output->apply(f);
|
|
// }
|
|
}
|
|
}
|
|
|
|
void ShortCircuitElement::apply_form(const std::function<void(Form*)>& f) {
|
|
for (auto& x : entries) {
|
|
x.condition->apply_form(f);
|
|
// if (x.output) {
|
|
// // not sure about this...
|
|
// x.output->apply(f);
|
|
// }
|
|
}
|
|
}
|
|
|
|
goos::Object ShortCircuitElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> forms;
|
|
switch (kind) {
|
|
case UNKNOWN:
|
|
forms.push_back(pretty_print::to_symbol("unknown-sc"));
|
|
break;
|
|
case AND:
|
|
forms.push_back(pretty_print::to_symbol("and"));
|
|
break;
|
|
case OR:
|
|
forms.push_back(pretty_print::to_symbol("or"));
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
for (auto& x : entries) {
|
|
forms.push_back(x.condition->to_form_as_condition(env));
|
|
}
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
|
|
void ShortCircuitElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
// vars.insert(final_result); // todo - this might be unused.
|
|
if (recursive) {
|
|
for (auto& entry : entries) {
|
|
entry.condition->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShortCircuitElement::get_modified_regs(RegSet& regs) const {
|
|
for (auto& e : entries) {
|
|
e.condition->get_modified_regs(regs);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// CondNoElseElement
|
|
/////////////////////////////
|
|
|
|
CondNoElseElement::CondNoElseElement(std::vector<Entry> _entries) : entries(std::move(_entries)) {
|
|
for (auto& entry : entries) {
|
|
entry.condition->parent_element = this;
|
|
entry.body->parent_element = this;
|
|
}
|
|
}
|
|
|
|
goos::Object CondNoElseElement::to_form_internal(const Env& env) const {
|
|
if (entries.size() == 1 && entries.front().body->is_reasonable_for_if()) {
|
|
// print as an if statement if we can put the body in a single form.
|
|
std::vector<goos::Object> list;
|
|
list.push_back(pretty_print::to_symbol("if"));
|
|
list.push_back(entries.front().condition->to_form_as_condition(env));
|
|
list.push_back(entries.front().body->to_form(env));
|
|
return pretty_print::build_list(list);
|
|
} else if (entries.size() == 1) {
|
|
// turn into a when if the body requires multiple forms
|
|
// todo check to see if the condition starts with a NOT and this can be simplified to an
|
|
// unless.
|
|
std::vector<goos::Object> list;
|
|
list.push_back(pretty_print::to_symbol("when"));
|
|
list.push_back(entries.front().condition->to_form_as_condition(env));
|
|
entries.front().body->inline_forms(list, env);
|
|
return pretty_print::build_list(list);
|
|
} else {
|
|
std::vector<goos::Object> list;
|
|
list.push_back(pretty_print::to_symbol("cond"));
|
|
for (auto& e : entries) {
|
|
std::vector<goos::Object> entry;
|
|
entry.push_back(e.condition->to_form_as_condition(env));
|
|
e.body->inline_forms(entry, env);
|
|
list.push_back(pretty_print::build_list(entry));
|
|
}
|
|
return pretty_print::build_list(list);
|
|
}
|
|
}
|
|
|
|
void CondNoElseElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
for (auto& e : entries) {
|
|
e.condition->apply(f);
|
|
e.body->apply(f);
|
|
}
|
|
}
|
|
|
|
void CondNoElseElement::apply_form(const std::function<void(Form*)>& f) {
|
|
for (auto& e : entries) {
|
|
e.condition->apply_form(f);
|
|
e.body->apply_form(f);
|
|
}
|
|
}
|
|
|
|
void CondNoElseElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
for (auto& e : entries) {
|
|
e.condition->collect_vars(vars, recursive);
|
|
e.body->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CondNoElseElement::get_modified_regs(RegSet& regs) const {
|
|
for (auto& e : entries) {
|
|
e.condition->get_modified_regs(regs);
|
|
e.body->get_modified_regs(regs);
|
|
}
|
|
}
|
|
|
|
CaseElement::CaseElement(Form* value, const std::vector<Entry>& entries, Form* else_body)
|
|
: m_value(value), m_entries(entries), m_else_body(else_body) {
|
|
m_value->parent_element = this;
|
|
for (auto& entry : m_entries) {
|
|
for (auto& val : entry.vals) {
|
|
val->parent_element = this;
|
|
}
|
|
entry.body->parent_element = this;
|
|
}
|
|
if (m_else_body) {
|
|
m_else_body->parent_element = this;
|
|
}
|
|
}
|
|
|
|
goos::Object CaseElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> list;
|
|
list.push_back(pretty_print::to_symbol("case"));
|
|
list.push_back(m_value->to_form(env));
|
|
for (auto& e : m_entries) {
|
|
std::vector<goos::Object> entry;
|
|
|
|
// cases
|
|
std::vector<goos::Object> cases;
|
|
for (auto& val : e.vals) {
|
|
cases.push_back(val->to_form(env));
|
|
}
|
|
entry.push_back(pretty_print::build_list(cases));
|
|
|
|
// body
|
|
e.body->inline_forms(entry, env);
|
|
list.push_back(pretty_print::build_list(entry));
|
|
}
|
|
|
|
if (m_else_body) {
|
|
std::vector<goos::Object> entry;
|
|
entry.push_back(pretty_print::to_symbol("else"));
|
|
m_else_body->inline_forms(entry, env);
|
|
list.push_back(pretty_print::build_list(entry));
|
|
}
|
|
return pretty_print::build_list(list);
|
|
}
|
|
|
|
void CaseElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_value->apply(f);
|
|
for (auto& e : m_entries) {
|
|
for (auto& val : e.vals) {
|
|
val->apply(f);
|
|
}
|
|
e.body->apply(f);
|
|
}
|
|
|
|
if (m_else_body) {
|
|
m_else_body->apply(f);
|
|
}
|
|
}
|
|
|
|
void CaseElement::apply_form(const std::function<void(Form*)>& f) {
|
|
m_value->apply_form(f);
|
|
for (auto& e : m_entries) {
|
|
for (auto& val : e.vals) {
|
|
val->apply_form(f);
|
|
}
|
|
e.body->apply_form(f);
|
|
}
|
|
|
|
if (m_else_body) {
|
|
m_else_body->apply_form(f);
|
|
}
|
|
}
|
|
|
|
void CaseElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
m_value->collect_vars(vars, recursive);
|
|
for (auto& e : m_entries) {
|
|
for (auto& val : e.vals) {
|
|
val->collect_vars(vars, recursive);
|
|
}
|
|
e.body->collect_vars(vars, recursive);
|
|
}
|
|
|
|
if (m_else_body) {
|
|
m_else_body->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CaseElement::get_modified_regs(RegSet& regs) const {
|
|
m_value->get_modified_regs(regs);
|
|
for (auto& e : m_entries) {
|
|
for (auto& val : e.vals) {
|
|
val->get_modified_regs(regs);
|
|
}
|
|
e.body->get_modified_regs(regs);
|
|
}
|
|
|
|
if (m_else_body) {
|
|
m_else_body->get_modified_regs(regs);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// AbsElement
|
|
/////////////////////////////
|
|
|
|
AbsElement::AbsElement(RegisterAccess _source, RegSet _consumed)
|
|
: source(_source), consumed(std::move(_consumed)) {}
|
|
|
|
goos::Object AbsElement::to_form_internal(const Env& env) const {
|
|
return pretty_print::build_list("abs", source.to_form(env));
|
|
}
|
|
|
|
void AbsElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void AbsElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void AbsElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
vars.insert(source);
|
|
}
|
|
|
|
void AbsElement::get_modified_regs(RegSet&) const {}
|
|
|
|
/////////////////////////////
|
|
// AshElement
|
|
/////////////////////////////
|
|
|
|
AshElement::AshElement(RegisterAccess _shift_amount,
|
|
RegisterAccess _value,
|
|
std::optional<RegisterAccess> _clobber,
|
|
bool _is_signed,
|
|
RegSet _consumed)
|
|
: shift_amount(_shift_amount),
|
|
value(_value),
|
|
clobber(_clobber),
|
|
is_signed(_is_signed),
|
|
consumed(_consumed) {}
|
|
|
|
goos::Object AshElement::to_form_internal(const Env& env) const {
|
|
return pretty_print::build_list(pretty_print::to_symbol(is_signed ? "ash.si" : "ash.ui"),
|
|
value.to_form(env), shift_amount.to_form(env));
|
|
}
|
|
|
|
void AshElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void AshElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void AshElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
vars.insert(value);
|
|
vars.insert(shift_amount);
|
|
}
|
|
|
|
void AshElement::get_modified_regs(RegSet&) const {}
|
|
|
|
/////////////////////////////
|
|
// TypeOfElement
|
|
/////////////////////////////
|
|
|
|
TypeOfElement::TypeOfElement(Form* _value) : value(_value) {
|
|
value->parent_element = this;
|
|
}
|
|
|
|
goos::Object TypeOfElement::to_form_internal(const Env& env) const {
|
|
return pretty_print::build_list("rtype-of", value->to_form(env));
|
|
}
|
|
|
|
void TypeOfElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
value->apply(f);
|
|
}
|
|
|
|
void TypeOfElement::apply_form(const std::function<void(Form*)>& f) {
|
|
value->apply_form(f);
|
|
}
|
|
|
|
void TypeOfElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
value->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void TypeOfElement::get_modified_regs(RegSet&) const {}
|
|
|
|
/////////////////////////////
|
|
// ConditionalMoveFalseElement
|
|
/////////////////////////////
|
|
|
|
ConditionalMoveFalseElement::ConditionalMoveFalseElement(RegisterAccess _dest,
|
|
RegisterAccess _old_value,
|
|
RegisterAccess _source,
|
|
bool _on_zero)
|
|
: dest(_dest), old_value(_old_value), source(_source), on_zero(_on_zero) {}
|
|
|
|
goos::Object ConditionalMoveFalseElement::to_form_internal(const Env& env) const {
|
|
return pretty_print::build_list(on_zero ? "cmove-#f-zero" : "cmove-#f-nonzero", dest.to_form(env),
|
|
source.to_form(env), old_value.to_form(env));
|
|
}
|
|
|
|
void ConditionalMoveFalseElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void ConditionalMoveFalseElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void ConditionalMoveFalseElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
vars.insert(dest);
|
|
vars.insert(old_value);
|
|
vars.insert(source);
|
|
}
|
|
|
|
void ConditionalMoveFalseElement::get_modified_regs(RegSet& regs) const {
|
|
regs.insert(dest.reg());
|
|
}
|
|
|
|
/////////////////////////////
|
|
// GenericElement
|
|
/////////////////////////////
|
|
|
|
GenericOperator GenericOperator::make_fixed(FixedOperatorKind kind) {
|
|
GenericOperator op;
|
|
op.m_kind = Kind::FIXED_OPERATOR;
|
|
op.m_fixed_kind = kind;
|
|
return op;
|
|
}
|
|
|
|
GenericOperator GenericOperator::make_function(Form* value) {
|
|
GenericOperator op;
|
|
op.m_kind = Kind::FUNCTION_EXPR;
|
|
op.m_function = value;
|
|
return op;
|
|
}
|
|
|
|
GenericOperator GenericOperator::make_compare(IR2_Condition::Kind kind) {
|
|
GenericOperator op;
|
|
op.m_kind = Kind::CONDITION_OPERATOR;
|
|
op.m_condition_kind = kind;
|
|
return op;
|
|
}
|
|
|
|
void GenericOperator::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
switch (m_kind) {
|
|
case Kind::FIXED_OPERATOR:
|
|
case Kind::CONDITION_OPERATOR:
|
|
return;
|
|
case Kind::FUNCTION_EXPR:
|
|
if (recursive) {
|
|
m_function->collect_vars(vars, recursive);
|
|
}
|
|
return;
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
}
|
|
|
|
goos::Object GenericOperator::to_form(const Env& env) const {
|
|
switch (m_kind) {
|
|
case Kind::FIXED_OPERATOR:
|
|
return pretty_print::to_symbol(fixed_operator_to_string(m_fixed_kind));
|
|
case Kind::CONDITION_OPERATOR:
|
|
return pretty_print::to_symbol(get_condition_kind_name(m_condition_kind));
|
|
case Kind::FUNCTION_EXPR:
|
|
return m_function->to_form(env);
|
|
default:
|
|
ASSERT(false);
|
|
return {};
|
|
}
|
|
}
|
|
|
|
void GenericOperator::apply(const std::function<void(FormElement*)>& f) {
|
|
switch (m_kind) {
|
|
case Kind::FIXED_OPERATOR:
|
|
case Kind::CONDITION_OPERATOR:
|
|
break;
|
|
case Kind::FUNCTION_EXPR:
|
|
m_function->apply(f);
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
}
|
|
|
|
void GenericOperator::apply_form(const std::function<void(Form*)>& f) {
|
|
switch (m_kind) {
|
|
case Kind::FIXED_OPERATOR:
|
|
case Kind::CONDITION_OPERATOR:
|
|
break;
|
|
case Kind::FUNCTION_EXPR:
|
|
m_function->apply_form(f);
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
}
|
|
|
|
bool GenericOperator::operator==(const GenericOperator& other) const {
|
|
if (m_kind != other.m_kind) {
|
|
return false;
|
|
}
|
|
switch (m_kind) {
|
|
case Kind::FIXED_OPERATOR:
|
|
return m_fixed_kind == other.m_fixed_kind;
|
|
case Kind::CONDITION_OPERATOR:
|
|
return m_condition_kind == other.m_condition_kind;
|
|
case Kind::FUNCTION_EXPR:
|
|
return false;
|
|
default:
|
|
ASSERT(false);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool GenericOperator::operator!=(const GenericOperator& other) const {
|
|
return !((*this) == other);
|
|
}
|
|
|
|
void GenericOperator::get_modified_regs(RegSet& regs) const {
|
|
switch (m_kind) {
|
|
case Kind::FIXED_OPERATOR:
|
|
case Kind::CONDITION_OPERATOR:
|
|
break;
|
|
case Kind::FUNCTION_EXPR:
|
|
m_function->get_modified_regs(regs);
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
}
|
|
|
|
std::string fixed_operator_to_string(FixedOperatorKind kind) {
|
|
switch (kind) {
|
|
case FixedOperatorKind::GPR_TO_FPR:
|
|
return "gpr->fpr";
|
|
case FixedOperatorKind::DIVISION:
|
|
return "/";
|
|
case FixedOperatorKind::ADDITION:
|
|
return "+";
|
|
case FixedOperatorKind::ADDITION_IN_PLACE:
|
|
return "+!";
|
|
case FixedOperatorKind::ADDITION_PTR:
|
|
return "&+";
|
|
case FixedOperatorKind::ADDITION_PTR_IN_PLACE:
|
|
return "&+!";
|
|
case FixedOperatorKind::SUBTRACTION:
|
|
return "-";
|
|
case FixedOperatorKind::SUBTRACTION_PTR:
|
|
return "&-";
|
|
case FixedOperatorKind::MULTIPLICATION:
|
|
return "*";
|
|
case FixedOperatorKind::SQRT:
|
|
return "sqrtf";
|
|
case FixedOperatorKind::ARITH_SHIFT:
|
|
return "ash";
|
|
case FixedOperatorKind::MOD:
|
|
return "mod";
|
|
case FixedOperatorKind::ABS:
|
|
return "abs";
|
|
case FixedOperatorKind::MIN:
|
|
return "min";
|
|
case FixedOperatorKind::MAX:
|
|
return "max";
|
|
case FixedOperatorKind::FABS:
|
|
return "fabs";
|
|
case FixedOperatorKind::FMIN:
|
|
return "fmin";
|
|
case FixedOperatorKind::FMAX:
|
|
return "fmax";
|
|
case FixedOperatorKind::LOGAND:
|
|
return "logand";
|
|
case FixedOperatorKind::LOGAND_IN_PLACE:
|
|
return "logand!";
|
|
case FixedOperatorKind::LOGIOR:
|
|
return "logior";
|
|
case FixedOperatorKind::LOGIOR_IN_PLACE:
|
|
return "logior!";
|
|
case FixedOperatorKind::LOGXOR:
|
|
return "logxor";
|
|
case FixedOperatorKind::LOGXOR_IN_PLACE:
|
|
return "logxor!";
|
|
case FixedOperatorKind::LOGNOR:
|
|
return "lognor";
|
|
case FixedOperatorKind::LOGNOT:
|
|
return "lognot";
|
|
case FixedOperatorKind::LOGCLEAR:
|
|
return "logclear";
|
|
case FixedOperatorKind::LOGCLEAR_IN_PLACE:
|
|
return "logclear!";
|
|
case FixedOperatorKind::LOGTEST:
|
|
return "logtest?";
|
|
case FixedOperatorKind::LOGTESTA:
|
|
return "logtesta?";
|
|
case FixedOperatorKind::SHL:
|
|
return "shl";
|
|
case FixedOperatorKind::SHR:
|
|
return "shr";
|
|
case FixedOperatorKind::SAR:
|
|
return "sar";
|
|
case FixedOperatorKind::CAR:
|
|
return "car";
|
|
case FixedOperatorKind::CDR:
|
|
return "cdr";
|
|
case FixedOperatorKind::NEW:
|
|
return "new";
|
|
case FixedOperatorKind::OBJECT_NEW:
|
|
return "object-new";
|
|
case FixedOperatorKind::TYPE_NEW:
|
|
return "type-new";
|
|
case FixedOperatorKind::CONS:
|
|
return "cons";
|
|
case FixedOperatorKind::LT:
|
|
return "<";
|
|
case FixedOperatorKind::GT:
|
|
return ">";
|
|
case FixedOperatorKind::LEQ:
|
|
return "<=";
|
|
case FixedOperatorKind::GEQ:
|
|
return ">=";
|
|
case FixedOperatorKind::EQ:
|
|
return "=";
|
|
case FixedOperatorKind::NEQ:
|
|
return "!=";
|
|
case FixedOperatorKind::METHOD_OF_OBJECT:
|
|
return "method-of-object";
|
|
case FixedOperatorKind::METHOD_OF_TYPE:
|
|
return "method-of-type";
|
|
case FixedOperatorKind::NULLP:
|
|
return "null?";
|
|
case FixedOperatorKind::PAIRP:
|
|
return "pair?";
|
|
case FixedOperatorKind::NONE:
|
|
return "none";
|
|
case FixedOperatorKind::PCPYLD:
|
|
return "make-u128";
|
|
case FixedOperatorKind::SYMBOL_TO_STRING:
|
|
return "symbol->string";
|
|
case FixedOperatorKind::ADDRESS_OF:
|
|
return "&";
|
|
case FixedOperatorKind::ASM_SLLV_R0:
|
|
return ".asm.sllv.r0";
|
|
case FixedOperatorKind::ASM_MADDS:
|
|
return ".asm.madd.s";
|
|
case FixedOperatorKind::VECTOR_MINUS:
|
|
return "vector-!";
|
|
case FixedOperatorKind::VECTOR_PLUS:
|
|
return "vector+!";
|
|
case FixedOperatorKind::VECTOR_XYZ_PRODUCT:
|
|
return "vector*!";
|
|
case FixedOperatorKind::VECTOR_CROSS:
|
|
return "vector-cross!";
|
|
case FixedOperatorKind::VECTOR_FLOAT_PRODUCT:
|
|
return "vector-float*!";
|
|
case FixedOperatorKind::L32_NOT_FALSE_CBOOL:
|
|
return "l32-false-check";
|
|
case FixedOperatorKind::VECTOR_3_DOT:
|
|
return "vector-dot";
|
|
case FixedOperatorKind::VECTOR_4_DOT:
|
|
return "vector4-dot";
|
|
case FixedOperatorKind::PROCESS_TO_PPOINTER:
|
|
return "process->ppointer";
|
|
case FixedOperatorKind::PPOINTER_TO_HANDLE:
|
|
return "ppointer->handle";
|
|
case FixedOperatorKind::PROCESS_TO_HANDLE:
|
|
return "process->handle";
|
|
case FixedOperatorKind::PPOINTER_TO_PROCESS:
|
|
return "ppointer->process";
|
|
case FixedOperatorKind::SEND_EVENT:
|
|
return "send-event";
|
|
case FixedOperatorKind::CPAD_PRESSED_P:
|
|
return "cpad-pressed?";
|
|
case FixedOperatorKind::CPAD_HOLD_P:
|
|
return "cpad-hold?";
|
|
case FixedOperatorKind::MOUSE_PRESSED_P:
|
|
return "mouse-pressed?";
|
|
case FixedOperatorKind::MOUSE_HOLD_P:
|
|
return "mouse-hold?";
|
|
case FixedOperatorKind::VECTOR_LENGTH:
|
|
return "vector-length";
|
|
case FixedOperatorKind::VECTOR_LENGTH_SQUARED:
|
|
return "vector-length-squared";
|
|
case FixedOperatorKind::VECTOR_PLUS_FLOAT_TIMES:
|
|
return "vector+float*!";
|
|
case FixedOperatorKind::VECTOR_PLUS_TIMES:
|
|
return "vector+*!";
|
|
case FixedOperatorKind::FOCUS_TEST:
|
|
return "focus-test?";
|
|
default:
|
|
ASSERT(false);
|
|
return "";
|
|
}
|
|
}
|
|
|
|
GenericElement::GenericElement(GenericOperator op) : m_head(op) {
|
|
if (op.kind() == GenericOperator::Kind::FUNCTION_EXPR) {
|
|
op.m_function->parent_element = this;
|
|
}
|
|
}
|
|
|
|
GenericElement::GenericElement(GenericOperator op, Form* arg) : m_head(op), m_elts({arg}) {
|
|
if (op.kind() == GenericOperator::Kind::FUNCTION_EXPR) {
|
|
op.m_function->parent_element = this;
|
|
}
|
|
for (auto x : m_elts) {
|
|
x->parent_element = this;
|
|
}
|
|
}
|
|
|
|
GenericElement::GenericElement(GenericOperator op, Form* arg0, Form* arg1)
|
|
: m_head(op), m_elts({arg0, arg1}) {
|
|
if (op.kind() == GenericOperator::Kind::FUNCTION_EXPR) {
|
|
op.m_function->parent_element = this;
|
|
}
|
|
for (auto x : m_elts) {
|
|
x->parent_element = this;
|
|
}
|
|
}
|
|
|
|
GenericElement::GenericElement(GenericOperator op, std::vector<Form*> forms)
|
|
: m_head(op), m_elts(std::move(forms)) {
|
|
if (op.kind() == GenericOperator::Kind::FUNCTION_EXPR) {
|
|
op.m_function->parent_element = this;
|
|
}
|
|
for (auto x : m_elts) {
|
|
x->parent_element = this;
|
|
}
|
|
}
|
|
|
|
goos::Object GenericElement::to_form_internal(const Env& env) const {
|
|
if (m_head.kind() == GenericOperator::Kind::CONDITION_OPERATOR &&
|
|
m_head.condition_kind() == IR2_Condition::Kind::TRUTHY) {
|
|
ASSERT(m_elts.size() == 1);
|
|
return m_elts.front()->to_form_as_condition(env);
|
|
} else if (m_head.kind() == GenericOperator::Kind::CONDITION_OPERATOR &&
|
|
m_head.condition_kind() == IR2_Condition::Kind::ALWAYS) {
|
|
return pretty_print::to_symbol("#t");
|
|
} else {
|
|
std::vector<goos::Object> result;
|
|
result.push_back(m_head.to_form(env));
|
|
for (auto x : m_elts) {
|
|
ASSERT(x->parent_element);
|
|
result.push_back(x->to_form(env));
|
|
}
|
|
return pretty_print::build_list(result);
|
|
}
|
|
}
|
|
|
|
void GenericElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_head.apply(f);
|
|
for (auto x : m_elts) {
|
|
x->apply(f);
|
|
}
|
|
}
|
|
|
|
void GenericElement::apply_form(const std::function<void(Form*)>& f) {
|
|
m_head.apply_form(f);
|
|
for (auto x : m_elts) {
|
|
x->apply_form(f);
|
|
}
|
|
}
|
|
|
|
void GenericElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
m_head.collect_vars(vars, recursive);
|
|
for (auto x : m_elts) {
|
|
x->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GenericElement::get_modified_regs(RegSet& regs) const {
|
|
m_head.get_modified_regs(regs);
|
|
for (auto x : m_elts) {
|
|
x->get_modified_regs(regs);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// CastElement
|
|
/////////////////////////////
|
|
|
|
CastElement::CastElement(TypeSpec type, Form* source, bool numeric)
|
|
: m_type(std::move(type)), m_source(source), m_numeric(numeric) {
|
|
m_source->parent_element = this;
|
|
}
|
|
|
|
goos::Object CastElement::to_form_internal(const Env& env) const {
|
|
// ASSERT(m_source->parent_element == this);
|
|
auto atom = form_as_atom(m_source);
|
|
if (atom && atom->is_var()) {
|
|
return pretty_print::build_list(
|
|
m_numeric ? "the" : "the-as", m_type.print(),
|
|
atom->var().to_form(env, RegisterAccess::Print::AS_VARIABLE_NO_CAST));
|
|
}
|
|
return pretty_print::build_list(m_numeric ? "the" : "the-as", m_type.print(),
|
|
m_source->to_form(env));
|
|
}
|
|
|
|
void CastElement::apply(const std::function<void(FormElement*)>& f) {
|
|
// ASSERT(m_source->parent_element == this);
|
|
f(this);
|
|
m_source->apply(f);
|
|
}
|
|
|
|
void CastElement::apply_form(const std::function<void(Form*)>& f) {
|
|
// ASSERT(m_source->parent_element == this);
|
|
m_source->apply_form(f);
|
|
}
|
|
|
|
void CastElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
// ASSERT(m_source->parent_element == this);
|
|
if (recursive) {
|
|
m_source->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void CastElement::get_modified_regs(RegSet& regs) const {
|
|
ASSERT(m_source->parent_element == this);
|
|
m_source->get_modified_regs(regs);
|
|
}
|
|
|
|
/////////////////////////////
|
|
// DerefElement
|
|
/////////////////////////////
|
|
|
|
DerefToken DerefToken::make_int_constant(s64 int_constant) {
|
|
DerefToken x;
|
|
x.m_kind = Kind::INTEGER_CONSTANT;
|
|
x.m_int_constant = int_constant;
|
|
return x;
|
|
}
|
|
|
|
DerefToken DerefToken::make_int_expr(Form* expr) {
|
|
DerefToken x;
|
|
x.m_kind = Kind::INTEGER_EXPRESSION;
|
|
x.m_expr = expr;
|
|
return x;
|
|
}
|
|
|
|
DerefToken DerefToken::make_field_name(const std::string& name) {
|
|
DerefToken x;
|
|
x.m_kind = Kind::FIELD_NAME;
|
|
x.m_name = name;
|
|
return x;
|
|
}
|
|
|
|
DerefToken DerefToken::make_expr_placeholder() {
|
|
DerefToken x;
|
|
x.m_kind = Kind::EXPRESSION_PLACEHOLDER;
|
|
return x;
|
|
}
|
|
|
|
void DerefToken::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
switch (m_kind) {
|
|
case Kind::INTEGER_CONSTANT:
|
|
case Kind::FIELD_NAME:
|
|
case Kind::EXPRESSION_PLACEHOLDER:
|
|
break;
|
|
case Kind::INTEGER_EXPRESSION:
|
|
if (recursive) {
|
|
m_expr->collect_vars(vars, recursive);
|
|
}
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
}
|
|
|
|
goos::Object DerefToken::to_form(const Env& env) const {
|
|
switch (m_kind) {
|
|
case Kind::INTEGER_CONSTANT:
|
|
return pretty_print::to_symbol(fmt::format("{}", m_int_constant));
|
|
case Kind::INTEGER_EXPRESSION:
|
|
return m_expr->to_form(env);
|
|
case Kind::FIELD_NAME:
|
|
return pretty_print::to_symbol(m_name);
|
|
case Kind::EXPRESSION_PLACEHOLDER:
|
|
return pretty_print::to_symbol("PLACEHOLDER");
|
|
default:
|
|
ASSERT(false);
|
|
return {};
|
|
}
|
|
}
|
|
|
|
void DerefToken::apply(const std::function<void(FormElement*)>& f) {
|
|
switch (m_kind) {
|
|
case Kind::INTEGER_CONSTANT:
|
|
case Kind::FIELD_NAME:
|
|
case Kind::EXPRESSION_PLACEHOLDER:
|
|
break;
|
|
case Kind::INTEGER_EXPRESSION:
|
|
m_expr->apply(f);
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
}
|
|
|
|
void DerefToken::apply_form(const std::function<void(Form*)>& f) {
|
|
switch (m_kind) {
|
|
case Kind::INTEGER_CONSTANT:
|
|
case Kind::FIELD_NAME:
|
|
case Kind::EXPRESSION_PLACEHOLDER:
|
|
break;
|
|
case Kind::INTEGER_EXPRESSION:
|
|
m_expr->apply_form(f);
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
}
|
|
|
|
void DerefToken::get_modified_regs(RegSet& regs) const {
|
|
switch (m_kind) {
|
|
case Kind::INTEGER_CONSTANT:
|
|
case Kind::FIELD_NAME:
|
|
case Kind::EXPRESSION_PLACEHOLDER:
|
|
break;
|
|
case Kind::INTEGER_EXPRESSION:
|
|
m_expr->get_modified_regs(regs);
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
}
|
|
|
|
DerefToken to_token(const FieldReverseLookupOutput::Token& in) {
|
|
switch (in.kind) {
|
|
case FieldReverseLookupOutput::Token::Kind::FIELD:
|
|
return DerefToken::make_field_name(in.name);
|
|
case FieldReverseLookupOutput::Token::Kind::CONSTANT_IDX:
|
|
return DerefToken::make_int_constant(in.idx);
|
|
case FieldReverseLookupOutput::Token::Kind::VAR_IDX:
|
|
return DerefToken::make_expr_placeholder();
|
|
default:
|
|
// temp
|
|
throw std::runtime_error("Cannot convert rd lookup token to deref token");
|
|
}
|
|
}
|
|
|
|
std::vector<DerefToken> to_tokens(const std::vector<FieldReverseLookupOutput::Token>& in) {
|
|
std::vector<DerefToken> ret;
|
|
ret.reserve(in.size());
|
|
for (auto& x : in) {
|
|
ret.push_back(to_token(x));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
DerefElement::DerefElement(Form* base, bool is_addr_of, DerefToken token)
|
|
: m_base(base), m_is_addr_of(is_addr_of), m_tokens({std::move(token)}) {
|
|
m_base->parent_element = this;
|
|
for (auto& x : m_tokens) {
|
|
if (x.kind() == DerefToken::Kind::INTEGER_EXPRESSION) {
|
|
x.expr()->parent_element = this;
|
|
}
|
|
}
|
|
inline_nested();
|
|
}
|
|
|
|
DerefElement::DerefElement(Form* base, bool is_addr_of, std::vector<DerefToken> tokens)
|
|
: m_base(base), m_is_addr_of(is_addr_of), m_tokens(std::move(tokens)) {
|
|
m_base->parent_element = this;
|
|
for (auto& x : m_tokens) {
|
|
if (x.kind() == DerefToken::Kind::INTEGER_EXPRESSION) {
|
|
x.expr()->parent_element = this;
|
|
}
|
|
}
|
|
inline_nested();
|
|
}
|
|
|
|
goos::Object DerefElement::to_form_internal(const Env& env) const {
|
|
ASSERT(m_base->parent_element);
|
|
std::vector<goos::Object> forms = {pretty_print::to_symbol(m_is_addr_of ? "&->" : "->"),
|
|
m_base->to_form(env)};
|
|
for (auto& tok : m_tokens) {
|
|
forms.push_back(tok.to_form(env));
|
|
}
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
|
|
void DerefElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_base->apply(f);
|
|
for (auto& tok : m_tokens) {
|
|
tok.apply(f);
|
|
}
|
|
}
|
|
|
|
void DerefElement::apply_form(const std::function<void(Form*)>& f) {
|
|
m_base->apply_form(f);
|
|
for (auto& tok : m_tokens) {
|
|
tok.apply_form(f);
|
|
}
|
|
}
|
|
|
|
void DerefElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
m_base->collect_vars(vars, recursive);
|
|
for (auto& tok : m_tokens) {
|
|
tok.collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DerefElement::get_modified_regs(RegSet& regs) const {
|
|
m_base->get_modified_regs(regs);
|
|
for (auto& tok : m_tokens) {
|
|
tok.get_modified_regs(regs);
|
|
}
|
|
}
|
|
|
|
void DerefElement::set_base(Form* new_base) {
|
|
m_base = new_base;
|
|
m_base->parent_element = this;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// DynamicMethodAccess
|
|
/////////////////////////////
|
|
|
|
DynamicMethodAccess::DynamicMethodAccess(RegisterAccess source) : m_source(source) {}
|
|
|
|
goos::Object DynamicMethodAccess::to_form_internal(const Env& env) const {
|
|
return pretty_print::build_list("dyn-method-access", m_source.to_form(env));
|
|
}
|
|
|
|
void DynamicMethodAccess::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void DynamicMethodAccess::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void DynamicMethodAccess::collect_vars(RegAccessSet& vars, bool) const {
|
|
vars.insert(m_source);
|
|
}
|
|
|
|
void DynamicMethodAccess::get_modified_regs(RegSet&) const {}
|
|
|
|
/////////////////////////////
|
|
// ArrayFieldAccess
|
|
/////////////////////////////
|
|
ArrayFieldAccess::ArrayFieldAccess(RegisterAccess source,
|
|
const std::vector<DerefToken>& deref_tokens,
|
|
int expected_stride,
|
|
int constant_offset,
|
|
bool flipped)
|
|
: m_source(source),
|
|
m_deref_tokens(deref_tokens),
|
|
m_expected_stride(expected_stride),
|
|
m_constant_offset(constant_offset),
|
|
m_flipped(flipped) {
|
|
for (auto& token : m_deref_tokens) {
|
|
if (token.kind() == DerefToken::Kind::INTEGER_EXPRESSION) {
|
|
token.expr()->parent_element = this;
|
|
}
|
|
}
|
|
}
|
|
|
|
goos::Object ArrayFieldAccess::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> elts;
|
|
elts.push_back(pretty_print::to_symbol("dynamic-array-field-access"));
|
|
elts.push_back(m_source.to_form(env));
|
|
for (auto& tok : m_deref_tokens) {
|
|
elts.push_back(tok.to_form(env));
|
|
}
|
|
return pretty_print::build_list(elts);
|
|
}
|
|
|
|
void ArrayFieldAccess::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
for (auto& tok : m_deref_tokens) {
|
|
tok.apply(f);
|
|
}
|
|
}
|
|
|
|
void ArrayFieldAccess::apply_form(const std::function<void(Form*)>& f) {
|
|
for (auto& tok : m_deref_tokens) {
|
|
tok.apply_form(f);
|
|
}
|
|
}
|
|
|
|
void ArrayFieldAccess::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
vars.insert(m_source);
|
|
if (recursive) {
|
|
for (auto& tok : m_deref_tokens) {
|
|
tok.collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ArrayFieldAccess::get_modified_regs(RegSet& regs) const {
|
|
for (auto& tok : m_deref_tokens) {
|
|
tok.get_modified_regs(regs);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// GetMethodElement
|
|
/////////////////////////////
|
|
|
|
GetMethodElement::GetMethodElement(Form* in, std::string name, bool is_object)
|
|
: m_in(in), m_name(std::move(name)), m_is_object(is_object) {
|
|
in->parent_element = this;
|
|
}
|
|
|
|
goos::Object GetMethodElement::to_form_internal(const Env& env) const {
|
|
return pretty_print::build_list(m_is_object ? "method-of-object" : "method-of-type",
|
|
m_in->to_form(env), m_name);
|
|
}
|
|
|
|
void GetMethodElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_in->apply(f);
|
|
}
|
|
|
|
void GetMethodElement::apply_form(const std::function<void(Form*)>& f) {
|
|
m_in->apply_form(f);
|
|
}
|
|
|
|
void GetMethodElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
m_in->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void GetMethodElement::get_modified_regs(RegSet& regs) const {
|
|
m_in->get_modified_regs(regs);
|
|
}
|
|
|
|
/////////////////////////////
|
|
// StringConstantElement
|
|
/////////////////////////////
|
|
|
|
StringConstantElement::StringConstantElement(const std::string& value) : m_value(value) {}
|
|
|
|
goos::Object StringConstantElement::to_form_internal(const Env&) const {
|
|
return goos::StringObject::make_new(m_value);
|
|
}
|
|
|
|
void StringConstantElement::apply(const std::function<void(FormElement*)>&) {}
|
|
void StringConstantElement::apply_form(const std::function<void(Form*)>&) {}
|
|
void StringConstantElement::collect_vars(RegAccessSet&, bool) const {}
|
|
void StringConstantElement::get_modified_regs(RegSet&) const {}
|
|
|
|
/////////////////////////////
|
|
// ConstantTokenElement
|
|
/////////////////////////////
|
|
ConstantTokenElement::ConstantTokenElement(const std::string& value) : m_value(value) {}
|
|
|
|
goos::Object ConstantTokenElement::to_form_internal(const Env&) const {
|
|
return pretty_print::to_symbol(m_value);
|
|
}
|
|
|
|
void ConstantTokenElement::apply(const std::function<void(FormElement*)>&) {}
|
|
void ConstantTokenElement::apply_form(const std::function<void(Form*)>&) {}
|
|
void ConstantTokenElement::collect_vars(RegAccessSet&, bool) const {}
|
|
void ConstantTokenElement::get_modified_regs(RegSet&) const {}
|
|
|
|
/////////////////////////////
|
|
// ConstantFloatElement
|
|
/////////////////////////////
|
|
|
|
ConstantFloatElement::ConstantFloatElement(float value) : m_value(value) {}
|
|
|
|
void ConstantFloatElement::apply(const std::function<void(FormElement*)>&) {}
|
|
void ConstantFloatElement::apply_form(const std::function<void(Form*)>&) {}
|
|
void ConstantFloatElement::collect_vars(RegAccessSet&, bool) const {}
|
|
void ConstantFloatElement::get_modified_regs(RegSet&) const {}
|
|
|
|
goos::Object ConstantFloatElement::to_form_internal(const Env&) const {
|
|
return pretty_print::float_representation(m_value);
|
|
}
|
|
|
|
/////////////////////////////
|
|
// StorePlainDeref
|
|
/////////////////////////////
|
|
|
|
StorePlainDeref::StorePlainDeref(Form* dst,
|
|
SimpleExpression expr,
|
|
int my_idx,
|
|
RegisterAccess base_var,
|
|
std::optional<TypeSpec> dst_cast_type,
|
|
std::optional<TypeSpec> src_cast_type,
|
|
int size)
|
|
: m_dst(dst),
|
|
m_expr(std::move(expr)),
|
|
m_my_idx(my_idx),
|
|
m_base_var(base_var),
|
|
m_dst_cast_type(std::move(dst_cast_type)),
|
|
m_src_cast_type(std::move(src_cast_type)),
|
|
m_size(size) {
|
|
m_dst->parent_element = this;
|
|
}
|
|
|
|
goos::Object StorePlainDeref::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> lst = {pretty_print::to_symbol("set!")};
|
|
|
|
if (m_dst_cast_type) {
|
|
lst.push_back(
|
|
pretty_print::build_list("the-as", m_dst_cast_type->print(), m_dst->to_form(env)));
|
|
} else {
|
|
lst.push_back(m_dst->to_form(env));
|
|
}
|
|
|
|
if (m_src_cast_type) {
|
|
lst.push_back(pretty_print::build_list("the-as", m_src_cast_type->print(),
|
|
m_expr.to_form(env.file->labels, env)));
|
|
} else {
|
|
lst.push_back(m_expr.to_form(env.file->labels, env));
|
|
}
|
|
|
|
return pretty_print::build_list(lst);
|
|
}
|
|
|
|
void StorePlainDeref::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_dst->apply(f);
|
|
}
|
|
|
|
void StorePlainDeref::apply_form(const std::function<void(Form*)>& f) {
|
|
m_dst->apply_form(f);
|
|
}
|
|
|
|
void StorePlainDeref::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
m_expr.collect_vars(vars);
|
|
m_dst->collect_vars(vars, recursive);
|
|
}
|
|
|
|
void StorePlainDeref::get_modified_regs(RegSet& regs) const {
|
|
m_dst->get_modified_regs(regs);
|
|
}
|
|
|
|
/////////////////////////////
|
|
// StoreArrayAccess
|
|
/////////////////////////////
|
|
|
|
StoreArrayAccess::StoreArrayAccess(ArrayFieldAccess* dst,
|
|
SimpleExpression expr,
|
|
int my_idx,
|
|
RegisterAccess array_src,
|
|
std::optional<TypeSpec> src_cast_type)
|
|
: m_dst(dst),
|
|
m_expr(expr),
|
|
m_my_idx(my_idx),
|
|
m_base_var(array_src),
|
|
m_src_cast_type(src_cast_type) {}
|
|
|
|
goos::Object StoreArrayAccess::to_form_internal(const Env& env) const {
|
|
return pretty_print::build_list("set!", m_dst->to_form(env),
|
|
m_expr.to_form(env.file->labels, env));
|
|
}
|
|
|
|
void StoreArrayAccess::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_dst->apply(f);
|
|
}
|
|
|
|
void StoreArrayAccess::apply_form(const std::function<void(Form*)>& f) {
|
|
m_dst->apply_form(f);
|
|
}
|
|
|
|
void StoreArrayAccess::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
m_expr.collect_vars(vars);
|
|
m_dst->collect_vars(vars, recursive);
|
|
}
|
|
|
|
void StoreArrayAccess::get_modified_regs(RegSet& regs) const {
|
|
m_dst->get_modified_regs(regs);
|
|
}
|
|
|
|
/////////////////////////////
|
|
// DecompiledDataElement
|
|
/////////////////////////////
|
|
|
|
DecompiledDataElement::DecompiledDataElement(const DecompilerLabel& label,
|
|
const std::optional<LabelInfo>& label_info)
|
|
: m_label(label), m_label_info(label_info) {}
|
|
|
|
goos::Object DecompiledDataElement::to_form_internal(const Env&) const {
|
|
if (m_decompiled) {
|
|
return m_description;
|
|
} else {
|
|
return pretty_print::to_symbol(fmt::format("<static-data {}>", m_label.name));
|
|
}
|
|
}
|
|
|
|
void DecompiledDataElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void DecompiledDataElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void DecompiledDataElement::collect_vars(RegAccessSet&, bool) const {}
|
|
|
|
void DecompiledDataElement::get_modified_regs(RegSet&) const {}
|
|
|
|
void DecompiledDataElement::do_decomp(const Env& env, const LinkedObjectFile* file) {
|
|
if (m_label_info) {
|
|
m_description =
|
|
decompile_at_label_with_hint(*m_label_info, m_label, env.file->labels,
|
|
env.file->words_by_seg, env.dts->ts, file, env.version);
|
|
} else {
|
|
m_description = decompile_at_label_guess_type(m_label, env.file->labels, env.file->words_by_seg,
|
|
env.dts->ts, file, env.version);
|
|
}
|
|
m_decompiled = true;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// LetElement
|
|
/////////////////////////////
|
|
|
|
LetElement::LetElement(Form* body, bool star) : m_body(body), m_star(star) {
|
|
m_body->parent_element = this;
|
|
}
|
|
|
|
void LetElement::add_def(RegisterAccess dst, Form* value) {
|
|
value->parent_element = this;
|
|
m_entries.push_back({dst, value});
|
|
}
|
|
|
|
void LetElement::make_let_star() {
|
|
m_star = true;
|
|
}
|
|
|
|
void LetElement::clear_let_star() {
|
|
m_star = false;
|
|
}
|
|
|
|
goos::Object LetElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> outer = {pretty_print::to_symbol(m_star ? "let*" : "let")};
|
|
|
|
std::vector<goos::Object> def_list;
|
|
|
|
for (auto& entry : m_entries) {
|
|
def_list.push_back(pretty_print::build_list(
|
|
entry.dest.to_form(env, RegisterAccess::Print::AS_VARIABLE_NO_CAST),
|
|
entry.src->to_form(env)));
|
|
}
|
|
|
|
outer.push_back(pretty_print::build_list(def_list));
|
|
m_body->inline_forms(outer, env);
|
|
return pretty_print::build_list(outer);
|
|
}
|
|
|
|
void LetElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
for (auto& entry : m_entries) {
|
|
entry.src->apply(f);
|
|
}
|
|
m_body->apply(f);
|
|
}
|
|
|
|
void LetElement::apply_form(const std::function<void(Form*)>& f) {
|
|
for (auto& entry : m_entries) {
|
|
entry.src->apply_form(f);
|
|
}
|
|
m_body->apply_form(f);
|
|
}
|
|
|
|
void LetElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
for (auto& entry : m_entries) {
|
|
vars.insert(entry.dest);
|
|
if (recursive) {
|
|
entry.src->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
m_body->collect_vars(vars, recursive);
|
|
}
|
|
|
|
void LetElement::get_modified_regs(RegSet& regs) const {
|
|
for (auto& entry : m_entries) {
|
|
regs.insert(entry.dest.reg());
|
|
}
|
|
m_body->get_modified_regs(regs);
|
|
}
|
|
|
|
void LetElement::add_entry(const Entry& e) {
|
|
e.src->parent_element = this;
|
|
m_entries.push_back(e);
|
|
}
|
|
|
|
void LetElement::set_body(Form* new_body) {
|
|
m_body = new_body;
|
|
m_body->parent_element = this;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// CounterLoopElement
|
|
/////////////////////////////
|
|
|
|
CounterLoopElement::CounterLoopElement(Kind kind,
|
|
RegisterAccess var_init,
|
|
RegisterAccess var_check,
|
|
RegisterAccess var_inc,
|
|
Form* check_value,
|
|
Form* body)
|
|
: m_var_init(var_init),
|
|
m_var_check(var_check),
|
|
m_var_inc(var_inc),
|
|
m_check_value(check_value),
|
|
m_body(body),
|
|
m_kind(kind) {
|
|
m_body->parent_element = this;
|
|
m_check_value->parent_element = this;
|
|
ASSERT(m_var_inc.reg() == m_var_check.reg());
|
|
ASSERT(m_var_init.reg() == m_var_inc.reg());
|
|
}
|
|
|
|
goos::Object CounterLoopElement::to_form_internal(const Env& env) const {
|
|
std::string loop_name;
|
|
switch (m_kind) {
|
|
case Kind::DOTIMES:
|
|
loop_name = "dotimes";
|
|
break;
|
|
case Kind::COUNTDOWN:
|
|
loop_name = "countdown";
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
std::vector<goos::Object> outer = {
|
|
pretty_print::to_symbol(loop_name),
|
|
pretty_print::build_list(m_var_init.to_form(env), m_check_value->to_form(env))};
|
|
m_body->inline_forms(outer, env);
|
|
return pretty_print::build_list(outer);
|
|
}
|
|
|
|
void CounterLoopElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_check_value->apply(f);
|
|
m_body->apply(f);
|
|
}
|
|
|
|
void CounterLoopElement::apply_form(const std::function<void(Form*)>& f) {
|
|
m_check_value->apply_form(f);
|
|
m_body->apply_form(f);
|
|
}
|
|
|
|
void CounterLoopElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
vars.insert(m_var_init);
|
|
vars.insert(m_var_check);
|
|
vars.insert(m_var_inc);
|
|
if (recursive) {
|
|
m_body->collect_vars(vars, recursive);
|
|
m_check_value->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void CounterLoopElement::get_modified_regs(RegSet& regs) const {
|
|
regs.insert(m_var_inc.reg());
|
|
m_body->get_modified_regs(regs);
|
|
m_check_value->get_modified_regs(regs);
|
|
}
|
|
|
|
/////////////////////////////
|
|
// LambdaDefinitionElement
|
|
/////////////////////////////
|
|
|
|
LambdaDefinitionElement::LambdaDefinitionElement(const goos::Object& def) : m_def(def) {}
|
|
|
|
goos::Object LambdaDefinitionElement::to_form_internal(const Env&) const {
|
|
return m_def;
|
|
}
|
|
|
|
void LambdaDefinitionElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void LambdaDefinitionElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void LambdaDefinitionElement::collect_vars(RegAccessSet&, bool) const {}
|
|
|
|
void LambdaDefinitionElement::get_modified_regs(RegSet&) const {}
|
|
|
|
/////////////////////////////
|
|
// StackVarDefElement
|
|
/////////////////////////////
|
|
|
|
StackStructureDefElement::StackStructureDefElement(const StackStructureEntry& entry)
|
|
: m_entry(entry) {}
|
|
|
|
goos::Object StackStructureDefElement::to_form_internal(const Env&) const {
|
|
switch (m_entry.hint.container_type) {
|
|
case StackStructureHint::ContainerType::NONE:
|
|
return pretty_print::build_list(
|
|
fmt::format("new 'stack-no-clear '{}", m_entry.ref_type.print()));
|
|
case StackStructureHint::ContainerType::INLINE_ARRAY:
|
|
return pretty_print::build_list(fmt::format("new 'stack-no-clear 'inline-array '{} {}",
|
|
m_entry.ref_type.get_single_arg().print(),
|
|
m_entry.hint.container_size));
|
|
case StackStructureHint::ContainerType::ARRAY:
|
|
return pretty_print::build_list(fmt::format("new 'stack-no-clear 'array '{} {}",
|
|
m_entry.ref_type.get_single_arg().print(),
|
|
m_entry.hint.container_size));
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
}
|
|
|
|
void StackStructureDefElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void StackStructureDefElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void StackStructureDefElement::collect_vars(RegAccessSet&, bool) const {}
|
|
|
|
void StackStructureDefElement::get_modified_regs(RegSet&) const {}
|
|
|
|
////////////////////////////////
|
|
// VectorFloatLoadStoreElement
|
|
////////////////////////////////
|
|
|
|
VectorFloatLoadStoreElement::VectorFloatLoadStoreElement(Register vf_reg,
|
|
Form* location,
|
|
bool is_load,
|
|
int my_idx)
|
|
: m_vf_reg(vf_reg), m_location(location), m_is_load(is_load), m_my_idx(my_idx) {
|
|
location->parent_element = this;
|
|
}
|
|
|
|
goos::Object VectorFloatLoadStoreElement::to_form_internal(const Env& env) const {
|
|
if (m_is_load) {
|
|
return pretty_print::build_list(".lvf", pretty_print::to_symbol(m_vf_reg.to_charp()),
|
|
m_location->to_form(env));
|
|
} else {
|
|
return pretty_print::build_list(".svf", m_location->to_form(env),
|
|
pretty_print::to_symbol(m_vf_reg.to_charp()));
|
|
}
|
|
}
|
|
|
|
void VectorFloatLoadStoreElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_location->apply(f);
|
|
}
|
|
|
|
void VectorFloatLoadStoreElement::apply_form(const std::function<void(Form*)>& f) {
|
|
m_location->apply_form(f);
|
|
}
|
|
|
|
void VectorFloatLoadStoreElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
m_location->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
|
|
void VectorFloatLoadStoreElement::get_modified_regs(RegSet&) const {
|
|
// vf's dont count
|
|
}
|
|
|
|
void VectorFloatLoadStoreElement::collect_vf_regs(RegSet& regs) const {
|
|
regs.insert(m_vf_reg);
|
|
}
|
|
|
|
////////////////////////////////
|
|
// StackSpillStoreElement
|
|
////////////////////////////////
|
|
|
|
StackSpillStoreElement::StackSpillStoreElement(SimpleAtom value,
|
|
int size,
|
|
int stack_offset,
|
|
const std::optional<TypeSpec>& cast_type)
|
|
: m_value(value), m_size(size), m_stack_offset(stack_offset), m_cast_type(cast_type) {}
|
|
|
|
goos::Object StackSpillStoreElement::to_form_internal(const Env& env) const {
|
|
return pretty_print::build_list(
|
|
fmt::format("set! {}", env.get_spill_slot_var_name(m_stack_offset)), m_value.to_form(env));
|
|
}
|
|
|
|
void StackSpillStoreElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void StackSpillStoreElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void StackSpillStoreElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
if (m_value.is_var()) {
|
|
vars.insert(m_value.var());
|
|
}
|
|
}
|
|
|
|
void StackSpillStoreElement::get_modified_regs(RegSet&) const {}
|
|
|
|
////////////////////////////////
|
|
// StackSpillValueElement
|
|
////////////////////////////////
|
|
|
|
StackSpillValueElement::StackSpillValueElement(int size, int stack_offset, bool is_signed)
|
|
: m_size(size), m_stack_offset(stack_offset), m_is_signed(is_signed) {}
|
|
|
|
goos::Object StackSpillValueElement::to_form_internal(const Env& env) const {
|
|
return pretty_print::to_symbol(env.get_spill_slot_var_name(m_stack_offset));
|
|
}
|
|
|
|
void StackSpillValueElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void StackSpillValueElement::apply_form(const std::function<void(Form*)>&) {}
|
|
void StackSpillValueElement::collect_vars(RegAccessSet&, bool) const {}
|
|
void StackSpillValueElement::get_modified_regs(RegSet&) const {}
|
|
|
|
////////////////////////////////
|
|
// MethodOfTypeElement
|
|
///////////////////////////////
|
|
|
|
MethodOfTypeElement::MethodOfTypeElement(RegisterAccess type_reg,
|
|
const TypeSpec& type_at_decompile,
|
|
const MethodInfo& method_info)
|
|
: m_type_reg(type_reg), m_type_at_decompile(type_at_decompile), m_method_info(method_info) {}
|
|
|
|
goos::Object MethodOfTypeElement::to_form_internal(const Env& env) const {
|
|
return pretty_print::build_list("method-of-type", m_type_reg.to_form(env), m_method_info.name);
|
|
}
|
|
|
|
void MethodOfTypeElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void MethodOfTypeElement::apply_form(const std::function<void(Form*)>&) {}
|
|
|
|
void MethodOfTypeElement::collect_vars(RegAccessSet& vars, bool) const {
|
|
vars.insert(m_type_reg);
|
|
}
|
|
|
|
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 {}
|
|
|
|
////////////////////////////////
|
|
// LabelDerefElement
|
|
///////////////////////////////
|
|
|
|
LabelDerefElement::LabelDerefElement(int lid,
|
|
int size,
|
|
LoadVarOp::Kind load_kind,
|
|
RegisterAccess var)
|
|
: m_lid(lid), m_size(size), m_load_kind(load_kind), m_var(var) {}
|
|
|
|
goos::Object LabelDerefElement::to_form_internal(const Env& env) const {
|
|
return pretty_print::build_list(fmt::format("label-deref {} :label {} :size {} :kind {}",
|
|
m_var.to_string(env), env.file->labels.at(m_lid).name,
|
|
m_size, load_kind_to_string(m_load_kind)));
|
|
}
|
|
|
|
void LabelDerefElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void LabelDerefElement::apply_form(const std::function<void(Form*)>&) {}
|
|
void LabelDerefElement::collect_vars(RegAccessSet& regs, bool) const {
|
|
regs.insert(m_var);
|
|
}
|
|
void LabelDerefElement::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);
|
|
}
|
|
|
|
////////////////////////////////
|
|
// DefstateElement
|
|
////////////////////////////////
|
|
|
|
DefstateElement::DefstateElement(const std::string& process_type,
|
|
const std::string& state_name,
|
|
const std::string& parent_name,
|
|
const std::vector<Entry>& entries,
|
|
bool is_virtual,
|
|
bool is_override)
|
|
: m_process_type(process_type),
|
|
m_state_name(state_name),
|
|
m_parent_name(parent_name),
|
|
m_entries(entries),
|
|
m_is_virtual(is_virtual),
|
|
m_is_override(is_override) {
|
|
for (auto& e : m_entries) {
|
|
e.val->parent_element = this;
|
|
}
|
|
}
|
|
|
|
void DefstateElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
for (auto& e : m_entries) {
|
|
e.val->apply(f);
|
|
}
|
|
}
|
|
|
|
void DefstateElement::apply_form(const std::function<void(Form*)>& f) {
|
|
for (auto& e : m_entries) {
|
|
e.val->apply_form(f);
|
|
}
|
|
}
|
|
|
|
void DefstateElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
for (auto& e : m_entries) {
|
|
e.val->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DefstateElement::get_modified_regs(RegSet& regs) const {
|
|
for (auto& e : m_entries) {
|
|
e.val->get_modified_regs(regs);
|
|
}
|
|
}
|
|
|
|
goos::Object DefstateElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> forms;
|
|
forms.push_back(pretty_print::to_symbol("defstate"));
|
|
forms.push_back(pretty_print::to_symbol(m_state_name));
|
|
forms.push_back(pretty_print::build_list(m_process_type));
|
|
|
|
if (m_is_virtual) {
|
|
if (!m_is_override) {
|
|
forms.push_back(pretty_print::to_symbol(":virtual #t"));
|
|
} else {
|
|
forms.push_back(pretty_print::to_symbol(":virtual override"));
|
|
}
|
|
}
|
|
|
|
if (!m_parent_name.empty()) {
|
|
forms.push_back(pretty_print::to_symbol(fmt::format(":parent {}", m_parent_name)));
|
|
}
|
|
|
|
for (const auto& e : m_entries) {
|
|
forms.push_back(pretty_print::to_symbol(fmt::format(":{}", handler_kind_to_name(e.kind))));
|
|
auto to_print = e.val;
|
|
forms.push_back(to_print->to_form(env));
|
|
}
|
|
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
|
|
////////////////////////////////
|
|
// WithDmaBufferAddBucketElement
|
|
////////////////////////////////
|
|
|
|
WithDmaBufferAddBucketElement::WithDmaBufferAddBucketElement(RegisterAccess dma_buf,
|
|
Form* dma_buf_val,
|
|
Form* bucket,
|
|
Form* body)
|
|
: m_dma_buf(dma_buf), m_dma_buf_val(dma_buf_val), m_bucket(bucket), m_body(body) {
|
|
m_dma_buf_val->parent_element = this;
|
|
m_bucket->parent_element = this;
|
|
m_body->parent_element = this;
|
|
}
|
|
|
|
void WithDmaBufferAddBucketElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_dma_buf_val->apply(f);
|
|
m_bucket->apply(f);
|
|
m_body->apply(f);
|
|
}
|
|
|
|
void WithDmaBufferAddBucketElement::apply_form(const std::function<void(Form*)>& f) {
|
|
m_dma_buf_val->apply_form(f);
|
|
m_bucket->apply_form(f);
|
|
m_body->apply_form(f);
|
|
}
|
|
|
|
void WithDmaBufferAddBucketElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
m_dma_buf_val->collect_vars(vars, recursive);
|
|
m_bucket->collect_vars(vars, recursive);
|
|
m_body->collect_vars(vars, recursive);
|
|
}
|
|
|
|
void WithDmaBufferAddBucketElement::get_modified_regs(RegSet& regs) const {
|
|
m_dma_buf_val->get_modified_regs(regs);
|
|
m_bucket->get_modified_regs(regs);
|
|
m_body->get_modified_regs(regs);
|
|
}
|
|
|
|
goos::Object WithDmaBufferAddBucketElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> forms;
|
|
forms.push_back(pretty_print::to_symbol("with-dma-buffer-add-bucket"));
|
|
forms.push_back(pretty_print::build_list(
|
|
{pretty_print::build_list({pretty_print::to_symbol(env.get_variable_name(m_dma_buf)),
|
|
m_dma_buf_val->to_form(env)}),
|
|
m_bucket->to_form(env)}));
|
|
m_body->inline_forms(forms, env);
|
|
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
|
|
////////////////////////////////
|
|
// DefskelgroupElement
|
|
////////////////////////////////
|
|
|
|
DefskelgroupElement::DefskelgroupElement(const std::string& name,
|
|
const DefskelgroupElement::Info& info,
|
|
const StaticInfo& data)
|
|
: m_name(name), m_static_info(data), m_info(info) {
|
|
for (auto& e : m_info.lods) {
|
|
e.mgeo->parent_element = this;
|
|
e.lod_dist->parent_element = this;
|
|
}
|
|
m_info.janim->parent_element = this;
|
|
m_info.jgeo->parent_element = this;
|
|
}
|
|
|
|
void DefskelgroupElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
for (auto& e : m_info.lods) {
|
|
e.mgeo->apply(f);
|
|
e.lod_dist->apply(f);
|
|
}
|
|
m_info.janim->apply(f);
|
|
m_info.jgeo->apply(f);
|
|
}
|
|
|
|
void DefskelgroupElement::apply_form(const std::function<void(Form*)>& f) {
|
|
for (auto& e : m_info.lods) {
|
|
e.mgeo->apply_form(f);
|
|
e.lod_dist->apply_form(f);
|
|
}
|
|
m_info.janim->apply_form(f);
|
|
m_info.jgeo->apply_form(f);
|
|
}
|
|
|
|
void DefskelgroupElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
for (auto& e : m_info.lods) {
|
|
e.mgeo->collect_vars(vars, recursive);
|
|
e.lod_dist->collect_vars(vars, recursive);
|
|
}
|
|
m_info.janim->collect_vars(vars, recursive);
|
|
m_info.jgeo->collect_vars(vars, recursive);
|
|
}
|
|
|
|
void DefskelgroupElement::get_modified_regs(RegSet& regs) const {
|
|
for (auto& e : m_info.lods) {
|
|
e.mgeo->get_modified_regs(regs);
|
|
e.lod_dist->get_modified_regs(regs);
|
|
}
|
|
m_info.janim->get_modified_regs(regs);
|
|
m_info.jgeo->get_modified_regs(regs);
|
|
}
|
|
|
|
goos::Object DefskelgroupElement::ClothParams::to_list(const std::string& ag_name,
|
|
const Env& env) const {
|
|
std::vector<goos::Object> result;
|
|
if (mesh != 0) {
|
|
const auto& art = env.dts->art_group_info;
|
|
if (art.find(ag_name) != art.end() && art.at(ag_name).find(mesh) != art.at(ag_name).end()) {
|
|
auto name = art.at(ag_name).at(mesh);
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("mesh"), pretty_print::to_symbol(name)}));
|
|
} else {
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("mesh"), pretty_print::to_symbol(std::to_string(mesh))}));
|
|
}
|
|
}
|
|
if (gravity != 0.0f) {
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("gravity-constant"),
|
|
pretty_print::to_symbol(fmt::format("(meters {})", meters_to_string(gravity)))}));
|
|
}
|
|
if (wind != 0.0f) {
|
|
result.push_back(pretty_print::build_list({pretty_print::to_symbol("wind-constant"),
|
|
pretty_print::to_symbol(float_to_string(wind))}));
|
|
}
|
|
if (width != 0) {
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("cloth-width"), pretty_print::to_symbol(std::to_string(width))}));
|
|
}
|
|
if (sphere_constraints != 0) {
|
|
result.push_back(
|
|
pretty_print::build_list({pretty_print::to_symbol("num-sphere-constraints"),
|
|
pretty_print::to_symbol(std::to_string(sphere_constraints))}));
|
|
}
|
|
if (disc_constraints != 0) {
|
|
result.push_back(
|
|
pretty_print::build_list({pretty_print::to_symbol("num-disc-constraints"),
|
|
pretty_print::to_symbol(std::to_string(disc_constraints))}));
|
|
}
|
|
if (anchor_points != 0) {
|
|
result.push_back(
|
|
pretty_print::build_list({pretty_print::to_symbol("num-anchor-points"),
|
|
pretty_print::to_symbol(std::to_string(anchor_points))}));
|
|
}
|
|
if (flags != 0) {
|
|
auto bits = decompile_bitfield_enum_from_int(TypeSpec("cloth-flag"), env.dts->ts, flags);
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("flags"),
|
|
pretty_print::to_symbol(fmt::format("(cloth-flag {})", fmt::join(bits, " ")))}));
|
|
}
|
|
if (!tex_name.empty()) {
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("tex-name"), pretty_print::new_string(tex_name)}));
|
|
}
|
|
if (!tex_name2.empty()) {
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("tex-name2"), pretty_print::new_string(tex_name2)}));
|
|
}
|
|
if (!tex_name3.empty()) {
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("tex-name3"), pretty_print::new_string(tex_name3)}));
|
|
}
|
|
if (!alt_tex_name.empty()) {
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("alt-tex-name"), pretty_print::new_string(alt_tex_name)}));
|
|
}
|
|
if (!alt_tex_name2.empty()) {
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("alt-tex-name2"), pretty_print::new_string(alt_tex_name2)}));
|
|
}
|
|
if (!alt_tex_name3.empty()) {
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("alt-tex-name3"), pretty_print::new_string(alt_tex_name3)}));
|
|
}
|
|
if (thickness != 0.0f) {
|
|
result.push_back(
|
|
pretty_print::build_list({pretty_print::to_symbol("cloth-thickness"),
|
|
pretty_print::to_symbol(float_to_string(thickness))}));
|
|
}
|
|
if (xform != 0) {
|
|
result.push_back(pretty_print::build_list({pretty_print::to_symbol("initial-xform"),
|
|
pretty_print::to_symbol(std::to_string(xform))}));
|
|
}
|
|
if (drag != 0.0f) {
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("drag"), pretty_print::to_symbol(float_to_string(drag))}));
|
|
}
|
|
if (ball_collision_radius != 0.0f) {
|
|
result.push_back(
|
|
pretty_print::build_list({pretty_print::to_symbol("ball-collision-radius"),
|
|
pretty_print::to_symbol(fmt::format(
|
|
"(meters {})", meters_to_string(ball_collision_radius)))}));
|
|
}
|
|
if (iterations != 0) {
|
|
result.push_back(
|
|
pretty_print::build_list({pretty_print::to_symbol("num-iterations"),
|
|
pretty_print::to_symbol(std::to_string(iterations))}));
|
|
}
|
|
if (timestep_freq != 0) {
|
|
result.push_back(
|
|
pretty_print::build_list({pretty_print::to_symbol("timestep-frequency"),
|
|
pretty_print::to_symbol(std::to_string(timestep_freq))}));
|
|
}
|
|
if (secret != 0) {
|
|
auto bits = decompile_bitfield_enum_from_int(TypeSpec("game-secrets"), env.dts->ts, secret);
|
|
result.push_back(pretty_print::build_list(
|
|
{pretty_print::to_symbol("secret-disable"),
|
|
pretty_print::to_symbol(fmt::format("(game-secrets {})", fmt::join(bits, " ")))}));
|
|
}
|
|
return pretty_print::build_list(result);
|
|
}
|
|
|
|
goos::Object DefskelgroupElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> forms;
|
|
forms.push_back(pretty_print::to_symbol("defskelgroup"));
|
|
forms.push_back(pretty_print::to_symbol(m_name));
|
|
forms.push_back(pretty_print::to_symbol(m_static_info.art_group_name));
|
|
const auto& art = env.dts->art_group_info.find(m_static_info.art_group_name + "-ag");
|
|
bool has_art = art != env.dts->art_group_info.end();
|
|
auto jg = m_info.jgeo->to_form(env);
|
|
if (jg.is_int() && has_art && art->second.count(jg.as_int())) {
|
|
forms.push_back(pretty_print::to_symbol(art->second.at(jg.as_int())));
|
|
} else {
|
|
forms.push_back(jg);
|
|
}
|
|
auto ja = m_info.janim->to_form(env);
|
|
if (ja.is_int() && has_art && art->second.count(ja.as_int())) {
|
|
forms.push_back(pretty_print::to_symbol(art->second.at(ja.as_int())));
|
|
} else {
|
|
forms.push_back(ja);
|
|
}
|
|
|
|
std::vector<goos::Object> lod_forms;
|
|
for (const auto& e : m_info.lods) {
|
|
auto f_dist = pretty_print::to_symbol(
|
|
fmt::format("(meters {})", meters_to_string(e.lod_dist->to_form(env).as_float())));
|
|
auto mg = e.mgeo->to_form(env);
|
|
if (mg.is_int() && has_art && art->second.count(mg.as_int())) {
|
|
lod_forms.push_back(
|
|
pretty_print::build_list(pretty_print::to_symbol(art->second.at(mg.as_int())), f_dist));
|
|
} else {
|
|
lod_forms.push_back(pretty_print::build_list(mg, f_dist));
|
|
}
|
|
}
|
|
forms.push_back(pretty_print::build_list(lod_forms));
|
|
|
|
forms.push_back(pretty_print::to_symbol(fmt::format(
|
|
":bounds (static-spherem {} {} {} {})", meters_to_string(m_static_info.bounds.x()),
|
|
meters_to_string(m_static_info.bounds.y()), meters_to_string(m_static_info.bounds.z()),
|
|
meters_to_string(m_static_info.bounds.w()))));
|
|
|
|
if (m_static_info.longest_edge != 0) {
|
|
forms.push_back(pretty_print::to_symbol(
|
|
fmt::format(":longest-edge (meters {})", meters_to_string(m_static_info.longest_edge))));
|
|
}
|
|
|
|
if (m_static_info.shadow != 0) {
|
|
if (has_art && art->second.count(m_static_info.shadow)) {
|
|
forms.push_back(
|
|
pretty_print::to_symbol(fmt::format(":shadow {}", art->second.at(m_static_info.shadow))));
|
|
} else {
|
|
forms.push_back(pretty_print::to_symbol(fmt::format(":shadow {}", m_static_info.shadow)));
|
|
}
|
|
}
|
|
if (m_static_info.tex_level != 0) {
|
|
forms.push_back(
|
|
pretty_print::to_symbol(fmt::format(":texture-level {}", m_static_info.tex_level)));
|
|
}
|
|
if (m_static_info.sort != 0) {
|
|
forms.push_back(pretty_print::to_symbol(fmt::format(":sort {}", m_static_info.sort)));
|
|
}
|
|
switch (env.version) {
|
|
case GameVersion::Jak1:
|
|
if (m_static_info.version != 6) {
|
|
forms.push_back(pretty_print::to_symbol(fmt::format(":version {}", m_static_info.version)));
|
|
}
|
|
break;
|
|
case GameVersion::Jak2:
|
|
if (m_static_info.version != 7) {
|
|
forms.push_back(pretty_print::to_symbol(fmt::format(":version {}", m_static_info.version)));
|
|
}
|
|
break;
|
|
case GameVersion::Jak3:
|
|
case GameVersion::JakX:
|
|
if (m_static_info.version != 8) {
|
|
forms.push_back(pretty_print::to_symbol(fmt::format(":version {}", m_static_info.version)));
|
|
}
|
|
}
|
|
if (env.version != GameVersion::Jak1) {
|
|
if (m_static_info.origin_joint_index != 0) {
|
|
forms.push_back(pretty_print::to_symbol(
|
|
fmt::format(":origin-joint-index {}", m_static_info.origin_joint_index)));
|
|
}
|
|
if (m_static_info.shadow_joint_index != 0) {
|
|
forms.push_back(pretty_print::to_symbol(
|
|
fmt::format(":shadow-joint-index {}", m_static_info.shadow_joint_index)));
|
|
}
|
|
if (m_static_info.light_index != 0) {
|
|
forms.push_back(
|
|
pretty_print::to_symbol(fmt::format(":light-index {}", m_static_info.light_index)));
|
|
}
|
|
if (env.version != GameVersion::Jak2) {
|
|
if (m_static_info.global_effects != 0) {
|
|
forms.push_back(pretty_print::to_symbol(
|
|
fmt::format(":global-effects {}", m_static_info.global_effects)));
|
|
}
|
|
if (!m_static_info.clothing.empty()) {
|
|
std::vector<goos::Object> cloth_list;
|
|
forms.push_back(pretty_print::to_symbol(":clothing"));
|
|
for (const auto& p : m_static_info.clothing) {
|
|
auto macro = p.to_list(m_static_info.art_group_name + "-ag", env);
|
|
cloth_list.push_back(macro);
|
|
}
|
|
forms.push_back(pretty_print::build_list(cloth_list));
|
|
}
|
|
}
|
|
}
|
|
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
|
|
////////////////////////////////
|
|
// DefpartgroupElement
|
|
////////////////////////////////
|
|
|
|
DefpartgroupElement::DefpartgroupElement(const StaticInfo& data, int group_id)
|
|
: m_static_info(data), m_group_id(group_id) {}
|
|
|
|
void DefpartgroupElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void DefpartgroupElement::apply_form(const std::function<void(Form*)>&) {}
|
|
void DefpartgroupElement::collect_vars(RegAccessSet&, bool) const {}
|
|
void DefpartgroupElement::get_modified_regs(RegSet&) const {}
|
|
|
|
goos::Object DefpartgroupElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> forms;
|
|
forms.push_back(pretty_print::to_symbol(fmt::format("defpartgroup {}", name())));
|
|
forms.push_back(pretty_print::to_symbol(fmt::format(":id {}", m_group_id)));
|
|
if (m_static_info.duration != 3000) {
|
|
forms.push_back(pretty_print::to_symbol(
|
|
fmt::format(":duration (seconds {})", seconds_to_string(m_static_info.duration))));
|
|
}
|
|
if (m_static_info.linger != 1500) {
|
|
// 5 seconds is default
|
|
forms.push_back(pretty_print::to_symbol(
|
|
fmt::format(":linger-duration (seconds {})", seconds_to_string(m_static_info.linger))));
|
|
}
|
|
if (m_static_info.flags != 0) {
|
|
auto things = decompile_bitfield_enum_from_int(TypeSpec("sp-group-flag"), env.dts->ts,
|
|
m_static_info.flags);
|
|
std::string result = ":flags (";
|
|
for (auto& thing : things) {
|
|
result += thing;
|
|
result += ' ';
|
|
}
|
|
result.pop_back();
|
|
result += ')';
|
|
forms.push_back(pretty_print::to_symbol(result));
|
|
}
|
|
forms.push_back(pretty_print::to_symbol(fmt::format(
|
|
":bounds (static-bspherem {} {} {} {})", meters_to_string(m_static_info.bounds.x()),
|
|
meters_to_string(m_static_info.bounds.y()), meters_to_string(m_static_info.bounds.z()),
|
|
meters_to_string(m_static_info.bounds.w()))));
|
|
|
|
if (env.version != GameVersion::Jak1) {
|
|
// jak 2 stuff.
|
|
if (m_static_info.rot != 0) {
|
|
forms.push_back(pretty_print::to_symbol(fmt::format(
|
|
":rotate ((degrees {}) (degrees {}) (degrees {}))",
|
|
degrees_to_string(m_static_info.rot.x()), degrees_to_string(m_static_info.rot.y()),
|
|
degrees_to_string(m_static_info.rot.z()))));
|
|
}
|
|
if (m_static_info.scale != 1) {
|
|
forms.push_back(pretty_print::to_symbol(fmt::format(
|
|
":scale ({} {} {})", float_to_string(m_static_info.scale.x()),
|
|
float_to_string(m_static_info.scale.y()), float_to_string(m_static_info.scale.z()))));
|
|
}
|
|
}
|
|
|
|
std::vector<goos::Object> item_forms;
|
|
for (const auto& e : m_static_info.elts) {
|
|
s32 launcher = e.part_id;
|
|
u16 flags = e.flags;
|
|
u16 period = e.period;
|
|
u16 length = e.length;
|
|
u16 offset = e.offset;
|
|
u32 hour_mask = e.hour_mask;
|
|
u32 binding = e.binding;
|
|
|
|
std::string result =
|
|
fmt::format("(sp-item {}", launcher); // use decimal, so it matches array idx
|
|
|
|
if (e.fade != 0.0) {
|
|
result += fmt::format(" :fade-after (meters {})", meters_to_string(e.fade));
|
|
}
|
|
|
|
if (e.falloff != 0.0) {
|
|
result += fmt::format(" :falloff-to (meters {})", meters_to_string(e.falloff));
|
|
}
|
|
|
|
if (flags) {
|
|
auto things =
|
|
decompile_bitfield_enum_from_int(TypeSpec("sp-group-item-flag"), env.dts->ts, flags);
|
|
result += " :flags (";
|
|
for (auto& thing : things) {
|
|
result += thing;
|
|
result += ' ';
|
|
}
|
|
result.pop_back();
|
|
result += ')';
|
|
}
|
|
|
|
if (period) {
|
|
result += fmt::format(" :period (seconds {})", seconds_to_string(period));
|
|
}
|
|
|
|
if (length) {
|
|
result += fmt::format(" :length (seconds {})", seconds_to_string(length));
|
|
}
|
|
|
|
if (offset) {
|
|
// jak2 has switched this field to a signed 16 bit number
|
|
if (env.version >= GameVersion::Jak2) {
|
|
result += fmt::format(" :offset {}", (s16)offset);
|
|
} else {
|
|
result += fmt::format(" :offset {}", offset);
|
|
}
|
|
}
|
|
|
|
if (hour_mask) {
|
|
result += fmt::format(" :hour-mask #b{:b}", hour_mask);
|
|
}
|
|
|
|
if (binding) {
|
|
result += fmt::format(" :binding {}", binding);
|
|
}
|
|
|
|
result += ')';
|
|
|
|
item_forms.push_back(pretty_print::to_symbol(result));
|
|
}
|
|
if (!item_forms.empty()) {
|
|
forms.push_back(pretty_print::to_symbol(":parts"));
|
|
forms.push_back(pretty_print::build_list(item_forms));
|
|
}
|
|
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
|
|
////////////////////////////////
|
|
// DefpartElement
|
|
////////////////////////////////
|
|
|
|
DefpartElement::DefpartElement(const StaticInfo& data, int id) : m_static_info(data), m_id(id) {}
|
|
|
|
void DefpartElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
}
|
|
|
|
void DefpartElement::apply_form(const std::function<void(Form*)>&) {}
|
|
void DefpartElement::collect_vars(RegAccessSet&, bool) const {}
|
|
void DefpartElement::get_modified_regs(RegSet&) const {}
|
|
|
|
goos::Object DefpartElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> forms;
|
|
forms.push_back(pretty_print::to_symbol("defpart"));
|
|
forms.push_back(pretty_print::to_symbol(fmt::format("{}", m_id)));
|
|
|
|
std::vector<goos::Object> item_forms;
|
|
for (const auto& e : m_static_info.fields) {
|
|
if (e.is_sp_end(env.version)) {
|
|
// sp-end
|
|
break;
|
|
}
|
|
item_forms.push_back(decompile_sparticle_field_init(e.data, e.field_id, e.flags, e.sound_spec,
|
|
e.userdata, env.dts->ts, env.version, env));
|
|
}
|
|
if (!item_forms.empty()) {
|
|
forms.push_back(pretty_print::to_symbol(":init-specs"));
|
|
forms.push_back(pretty_print::build_list(item_forms));
|
|
}
|
|
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
|
|
////////////////////////////////
|
|
// ResLumpMacroElement
|
|
////////////////////////////////
|
|
|
|
ResLumpMacroElement::ResLumpMacroElement(Kind kind,
|
|
Form* lump_object,
|
|
Form* property_name,
|
|
Form* default_arg,
|
|
Form* tag_ptr,
|
|
Form* time,
|
|
const TypeSpec& result_type)
|
|
: m_kind(kind),
|
|
m_lump_object(lump_object),
|
|
m_property_name(property_name),
|
|
m_default_arg(default_arg),
|
|
m_tag_ptr(tag_ptr),
|
|
m_time(time),
|
|
m_result_type(result_type) {
|
|
m_lump_object->parent_element = this;
|
|
m_property_name->parent_element = this;
|
|
if (m_default_arg) {
|
|
m_default_arg->parent_element = this;
|
|
}
|
|
if (m_tag_ptr) {
|
|
m_tag_ptr->parent_element = this;
|
|
}
|
|
if (m_time) {
|
|
m_time->parent_element = this;
|
|
}
|
|
}
|
|
|
|
void ResLumpMacroElement::apply(const std::function<void(FormElement*)>& f) {
|
|
f(this);
|
|
m_lump_object->apply(f);
|
|
m_property_name->apply(f);
|
|
if (m_default_arg) {
|
|
m_default_arg->apply(f);
|
|
}
|
|
if (m_tag_ptr) {
|
|
m_tag_ptr->apply(f);
|
|
}
|
|
if (m_time) {
|
|
m_time->apply(f);
|
|
}
|
|
}
|
|
|
|
void ResLumpMacroElement::apply_form(const std::function<void(Form*)>& f) {
|
|
m_lump_object->apply_form(f);
|
|
m_property_name->apply_form(f);
|
|
if (m_default_arg) {
|
|
m_default_arg->apply_form(f);
|
|
}
|
|
if (m_tag_ptr) {
|
|
m_tag_ptr->apply_form(f);
|
|
}
|
|
if (m_time) {
|
|
m_time->apply_form(f);
|
|
}
|
|
}
|
|
|
|
void ResLumpMacroElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
|
if (recursive) {
|
|
m_lump_object->collect_vars(vars, recursive);
|
|
m_property_name->collect_vars(vars, recursive);
|
|
if (m_default_arg) {
|
|
m_default_arg->collect_vars(vars, recursive);
|
|
}
|
|
if (m_tag_ptr) {
|
|
m_tag_ptr->collect_vars(vars, recursive);
|
|
}
|
|
if (m_time) {
|
|
m_time->collect_vars(vars, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ResLumpMacroElement::get_modified_regs(RegSet& regs) const {
|
|
m_lump_object->get_modified_regs(regs);
|
|
m_property_name->get_modified_regs(regs);
|
|
if (m_default_arg) {
|
|
m_default_arg->get_modified_regs(regs);
|
|
}
|
|
if (m_tag_ptr) {
|
|
m_tag_ptr->get_modified_regs(regs);
|
|
}
|
|
if (m_time) {
|
|
m_time->get_modified_regs(regs);
|
|
}
|
|
}
|
|
|
|
goos::Object ResLumpMacroElement::to_form_internal(const Env& env) const {
|
|
std::vector<goos::Object> forms;
|
|
|
|
switch (m_kind) {
|
|
case Kind::DATA:
|
|
forms.push_back(pretty_print::to_symbol("res-lump-data"));
|
|
break;
|
|
case Kind::STRUCT:
|
|
forms.push_back(pretty_print::to_symbol("res-lump-struct"));
|
|
break;
|
|
case Kind::VALUE:
|
|
forms.push_back(pretty_print::to_symbol("res-lump-value"));
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
|
|
forms.push_back(m_lump_object->to_form(env));
|
|
forms.push_back(m_property_name->to_form(env));
|
|
|
|
forms.push_back(pretty_print::to_symbol(m_result_type.print()));
|
|
|
|
if (m_default_arg) {
|
|
forms.push_back(pretty_print::to_symbol(":default"));
|
|
forms.push_back(m_default_arg->to_form(env));
|
|
}
|
|
|
|
if (m_tag_ptr) {
|
|
forms.push_back(pretty_print::to_symbol(":tag-ptr"));
|
|
forms.push_back(m_tag_ptr->to_form(env));
|
|
}
|
|
|
|
if (m_time) {
|
|
forms.push_back(pretty_print::to_symbol(":time"));
|
|
forms.push_back(m_time->to_form(env));
|
|
}
|
|
|
|
return pretty_print::build_list(forms);
|
|
}
|
|
////////////////////////////////
|
|
// Utilities
|
|
////////////////////////////////
|
|
|
|
std::optional<SimpleAtom> form_element_as_atom(const FormElement* f) {
|
|
auto as_atom = dynamic_cast<const SimpleAtomElement*>(f);
|
|
if (as_atom) {
|
|
return as_atom->atom();
|
|
}
|
|
|
|
auto as_se = dynamic_cast<const SimpleExpressionElement*>(f);
|
|
if (as_se && as_se->expr().is_identity()) {
|
|
return as_se->expr().get_arg(0);
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
std::optional<SimpleAtom> form_as_atom(const Form* f) {
|
|
auto as_single = f->try_as_single_element();
|
|
return form_element_as_atom(as_single);
|
|
}
|
|
|
|
FormElement* make_cast_using_existing(FormElement* elt, const TypeSpec& type, FormPool& pool) {
|
|
auto as_cast = dynamic_cast<CastElement*>(elt);
|
|
if (as_cast) {
|
|
if (as_cast->type() != type) {
|
|
as_cast->set_type(type);
|
|
}
|
|
return as_cast;
|
|
} else {
|
|
return pool.alloc_element<CastElement>(type, pool.alloc_single_form(nullptr, elt));
|
|
}
|
|
}
|
|
|
|
GenericElement* alloc_generic_token_op(const std::string& name,
|
|
const std::vector<Form*>& args,
|
|
FormPool& pool) {
|
|
auto op = GenericOperator::make_function(
|
|
pool.alloc_single_element_form<ConstantTokenElement>(nullptr, name));
|
|
return pool.alloc_element<GenericElement>(op, args);
|
|
}
|
|
|
|
Form* alloc_var_form(const RegisterAccess& var, FormPool& pool) {
|
|
return pool.alloc_single_element_form<SimpleAtomElement>(nullptr, SimpleAtom::make_var(var));
|
|
}
|
|
|
|
} // namespace decompiler
|