mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-23 14:20:07 +00:00
support anonymous functions (#323)
This commit is contained in:
parent
e93d97dd07
commit
d60d9b639a
@ -730,4 +730,16 @@ goos::Object build_list(const std::vector<std::string>& symbols) {
|
||||
}
|
||||
return build_list(f.data(), f.size());
|
||||
}
|
||||
|
||||
void append(goos::Object& _in, const goos::Object& add) {
|
||||
auto* in = &_in;
|
||||
while (in->is_pair() && !in->as_pair()->cdr.is_empty_list()) {
|
||||
in = &in->as_pair()->cdr;
|
||||
}
|
||||
|
||||
if (!in->is_pair()) {
|
||||
assert(false); // invalid list
|
||||
}
|
||||
in->as_pair()->cdr = add;
|
||||
}
|
||||
} // namespace pretty_print
|
||||
|
@ -53,4 +53,6 @@ goos::Reader& get_pretty_printer_reader();
|
||||
|
||||
goos::Object float_representation(float value);
|
||||
|
||||
void append(goos::Object& _in, const goos::Object& add);
|
||||
|
||||
} // namespace pretty_print
|
||||
|
@ -2,6 +2,7 @@ add_library(
|
||||
decomp
|
||||
SHARED
|
||||
|
||||
analysis/anonymous_function_def.cpp
|
||||
analysis/atomic_op_builder.cpp
|
||||
analysis/cfg_builder.cpp
|
||||
analysis/expression_build.cpp
|
||||
|
@ -109,10 +109,18 @@ TP_Type SimpleAtom::get_type(const TypeState& input,
|
||||
if (word.kind == LinkedWord::TYPE_PTR) {
|
||||
if (word.symbol_name == "string") {
|
||||
return TP_Type::make_from_string(env.file->get_goal_string_by_label(label));
|
||||
} else {
|
||||
// otherwise, some other static basic.
|
||||
return TP_Type::make_from_ts(TypeSpec(word.symbol_name));
|
||||
} else if (word.symbol_name == "function") {
|
||||
// let's see if the user marked this as a lambda and if we can get a more specific type.
|
||||
auto hint_kv = env.label_types().find(label.name);
|
||||
if (hint_kv != env.label_types().end() && hint_kv->second.type_name == "_lambda_") {
|
||||
auto func = env.file->try_get_function_at_label(m_int);
|
||||
if (func) {
|
||||
return TP_Type::make_from_ts(func->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
// otherwise, some other static basic.
|
||||
return TP_Type::make_from_ts(TypeSpec(word.symbol_name));
|
||||
}
|
||||
} else if ((label.offset & 7) == PAIR_OFFSET) {
|
||||
return TP_Type::make_from_ts(TypeSpec("pair"));
|
||||
|
@ -2178,6 +2178,26 @@ void DoTimesElement::get_modified_regs(RegSet& regs) const {
|
||||
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 {}
|
||||
|
||||
std::optional<SimpleAtom> form_as_atom(const Form* f) {
|
||||
auto as_single = f->try_as_single_element();
|
||||
auto as_atom = dynamic_cast<SimpleAtomElement*>(as_single);
|
||||
@ -2192,5 +2212,4 @@ std::optional<SimpleAtom> form_as_atom(const Form* f) {
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace decompiler
|
||||
|
@ -1234,6 +1234,19 @@ class DoTimesElement : public FormElement {
|
||||
Form* m_body = nullptr;
|
||||
};
|
||||
|
||||
class LambdaDefinitionElement : public FormElement {
|
||||
public:
|
||||
LambdaDefinitionElement(const goos::Object& def);
|
||||
goos::Object to_form_internal(const Env& env) const override;
|
||||
void apply(const std::function<void(FormElement*)>& f) override;
|
||||
void apply_form(const std::function<void(Form*)>& f) override;
|
||||
void collect_vars(RegAccessSet& vars, bool recursive) const override;
|
||||
void get_modified_regs(RegSet& regs) const override;
|
||||
|
||||
private:
|
||||
goos::Object m_def;
|
||||
};
|
||||
|
||||
/*!
|
||||
* A Form is a wrapper around one or more FormElements.
|
||||
* This is done for two reasons:
|
||||
|
@ -393,6 +393,8 @@ void SimpleExpressionElement::update_from_stack_identity(const Env& env,
|
||||
auto decompiled_data = decompile_at_label_guess_type(lab, env.file->labels,
|
||||
env.file->words_by_seg, env.dts->ts);
|
||||
result->push_back(pool.alloc_element<DecompiledDataElement>(decompiled_data));
|
||||
} else if (type_name == "_lambda_") {
|
||||
result->push_back(this);
|
||||
} else {
|
||||
auto decompiled_data = decompile_at_label_with_hint(kv->second, lab, env.file->labels,
|
||||
env.file->words_by_seg, *env.dts);
|
||||
|
@ -77,6 +77,7 @@ class ObjectFileDB {
|
||||
void ir2_build_expressions();
|
||||
void ir2_insert_lets();
|
||||
void ir2_rewrite_inline_asm_instructions();
|
||||
void ir2_insert_anonymous_functions();
|
||||
void ir2_write_results(const std::string& output_dir);
|
||||
std::string ir2_to_file(ObjectFileData& data);
|
||||
std::string ir2_function_to_string(ObjectFileData& data, Function& function, int seg);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "decompiler/analysis/final_output.h"
|
||||
#include "decompiler/analysis/expression_build.h"
|
||||
#include "decompiler/analysis/inline_asm_rewrite.h"
|
||||
#include "decompiler/analysis/anonymous_function_def.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
#include "decompiler/IR2/Form.h"
|
||||
|
||||
@ -53,6 +54,8 @@ void ObjectFileDB::analyze_functions_ir2(const std::string& output_dir) {
|
||||
lg::info("Inserting lets...");
|
||||
ir2_insert_lets();
|
||||
}
|
||||
lg::info("Inserting anonymous function definitions...");
|
||||
ir2_insert_anonymous_functions();
|
||||
}
|
||||
|
||||
if (!output_dir.empty()) {
|
||||
@ -480,6 +483,20 @@ void ObjectFileDB::ir2_rewrite_inline_asm_instructions() {
|
||||
timer.getMs());
|
||||
}
|
||||
|
||||
void ObjectFileDB::ir2_insert_anonymous_functions() {
|
||||
Timer timer;
|
||||
int total = 0;
|
||||
for_each_function_def_order([&](Function& func, int segment_id, ObjectFileData& data) {
|
||||
(void)segment_id;
|
||||
(void)data;
|
||||
if (func.ir2.top_form && func.ir2.env.has_type_analysis()) {
|
||||
total += insert_anonymous_functions(func.ir2.top_form, *func.ir2.form_pool, func, dts);
|
||||
}
|
||||
});
|
||||
|
||||
lg::info("Inserted {} anonymous functions in {:.2f} ms\n", total, timer.getMs());
|
||||
}
|
||||
|
||||
void ObjectFileDB::ir2_write_results(const std::string& output_dir) {
|
||||
Timer timer;
|
||||
lg::info("Writing IR2 results to file...");
|
||||
|
39
decompiler/analysis/anonymous_function_def.cpp
Normal file
39
decompiler/analysis/anonymous_function_def.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "anonymous_function_def.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
#include "decompiler/Function/Function.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
#include "decompiler/analysis/final_output.h"
|
||||
|
||||
namespace decompiler {
|
||||
int insert_anonymous_functions(Form* top_level_form,
|
||||
FormPool& pool,
|
||||
const Function& function,
|
||||
const DecompilerTypeSystem&) {
|
||||
int replaced = 0;
|
||||
top_level_form->apply_form([&](Form* f) {
|
||||
auto atom = form_as_atom(f);
|
||||
if (atom && atom->is_static_addr()) {
|
||||
auto lab = function.ir2.env.file->labels.at(atom->label());
|
||||
auto& env = function.ir2.env;
|
||||
auto label_kv = env.label_types().find(lab.name);
|
||||
if (label_kv != env.label_types().end()) {
|
||||
if (label_kv->second.type_name == "_lambda_") {
|
||||
auto& file = env.file;
|
||||
auto other_func = file->try_get_function_at_label(atom->label());
|
||||
if (other_func) {
|
||||
std::vector<goos::Object> inline_body;
|
||||
other_func->ir2.top_form->inline_forms(inline_body, other_func->ir2.env);
|
||||
auto result = pretty_print::build_list(
|
||||
"lambda", get_arg_list_for_function(*other_func, other_func->ir2.env));
|
||||
pretty_print::append(result, pretty_print::build_list(inline_body));
|
||||
|
||||
f->clear();
|
||||
f->push_back(pool.alloc_element<LambdaDefinitionElement>(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return replaced;
|
||||
}
|
||||
} // namespace decompiler
|
10
decompiler/analysis/anonymous_function_def.h
Normal file
10
decompiler/analysis/anonymous_function_def.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "decompiler/IR2/Form.h"
|
||||
|
||||
namespace decompiler {
|
||||
int insert_anonymous_functions(Form* top_level_form,
|
||||
FormPool& pool,
|
||||
const Function& function,
|
||||
const DecompilerTypeSystem& dts);
|
||||
}
|
@ -7,24 +7,23 @@
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
namespace {
|
||||
void append(goos::Object& _in, const goos::Object& add) {
|
||||
auto* in = &_in;
|
||||
while (in->is_pair() && !in->as_pair()->cdr.is_empty_list()) {
|
||||
in = &in->as_pair()->cdr;
|
||||
goos::Object get_arg_list_for_function(const Function& func, const Env& env) {
|
||||
std::vector<goos::Object> argument_elts;
|
||||
assert(func.type.arg_count() >= 1);
|
||||
for (size_t i = 0; i < func.type.arg_count() - 1; i++) {
|
||||
auto reg = Register(Reg::GPR, Reg::A0 + i);
|
||||
auto name = fmt::format("{}-0", reg.to_charp());
|
||||
argument_elts.push_back(
|
||||
pretty_print::build_list(env.remapped_name(name), func.type.get_arg(i).print()));
|
||||
}
|
||||
|
||||
if (!in->is_pair()) {
|
||||
assert(false); // invalid list
|
||||
}
|
||||
in->as_pair()->cdr = add;
|
||||
return pretty_print::build_list(argument_elts);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string final_defun_out(const Function& func,
|
||||
const Env& env,
|
||||
const DecompilerTypeSystem& dts,
|
||||
FunctionDefSpecials special_mode) {
|
||||
using pretty_print::append;
|
||||
std::vector<goos::Object> inline_body;
|
||||
try {
|
||||
func.ir2.top_form->inline_forms(inline_body, env);
|
||||
@ -34,16 +33,7 @@ std::string final_defun_out(const Function& func,
|
||||
|
||||
int var_count = 0;
|
||||
auto var_dec = env.local_var_type_list(func.ir2.top_form, func.type.arg_count() - 1, &var_count);
|
||||
|
||||
std::vector<goos::Object> argument_elts;
|
||||
assert(func.type.arg_count() >= 1);
|
||||
for (size_t i = 0; i < func.type.arg_count() - 1; i++) {
|
||||
auto reg = Register(Reg::GPR, Reg::A0 + i);
|
||||
auto name = fmt::format("{}-0", reg.to_charp());
|
||||
argument_elts.push_back(
|
||||
pretty_print::build_list(env.remapped_name(name), func.type.get_arg(i).print()));
|
||||
}
|
||||
auto arguments = pretty_print::build_list(argument_elts);
|
||||
auto arguments = get_arg_list_for_function(func, env);
|
||||
|
||||
if (func.guessed_name.kind == FunctionName::FunctionKind::GLOBAL) {
|
||||
std::string def_name = "defun";
|
||||
|
@ -14,4 +14,6 @@ std::string write_from_top_level(const Function& top_level,
|
||||
const DecompilerTypeSystem& dts,
|
||||
const LinkedObjectFile& file,
|
||||
const std::unordered_set<std::string>& skip_functions = {});
|
||||
|
||||
goos::Object get_arg_list_for_function(const Function& func, const Env& env);
|
||||
} // namespace decompiler
|
||||
|
@ -11,7 +11,13 @@
|
||||
["L347", "float", true],
|
||||
["L348", "float", true],
|
||||
["L289", "_auto_", true],
|
||||
["L282", "_auto_", true]
|
||||
["L282", "_auto_", true],
|
||||
["L122", "_lambda_", true],
|
||||
["L129", "_lambda_", true],
|
||||
["L134", "_lambda_", true],
|
||||
["L136", "_lambda_", true],
|
||||
["L139", "_lambda_", true],
|
||||
["L86", "_lambda_", true]
|
||||
],
|
||||
|
||||
"math": [
|
||||
|
@ -68,4 +68,7 @@
|
||||
(define-extern *camera-pool* process-tree)
|
||||
(define-extern *target-pool* process-tree)
|
||||
(define-extern *entity-pool* process-tree)
|
||||
(define-extern *default-pool* process-tree)
|
||||
(define-extern *default-pool* process-tree)
|
||||
(define-extern *stdcon0* string)
|
||||
(define-extern *stdcon1* string)
|
||||
(define-extern *debug-draw-pauseable* symbol)
|
@ -1160,13 +1160,19 @@
|
||||
;; definition for function process-by-name
|
||||
(defun process-by-name ((arg0 object) (arg1 process-tree))
|
||||
(set! *global-search-name* (the-as basic arg0))
|
||||
(search-process-tree arg1 (the-as (function process-tree object) L139))
|
||||
(search-process-tree
|
||||
arg1
|
||||
(lambda ((a0-0 process)) (name= (-> a0-0 name) *global-search-name*))
|
||||
)
|
||||
)
|
||||
|
||||
;; definition for function process-not-name
|
||||
(defun process-not-name ((arg0 object) (arg1 process-tree))
|
||||
(set! *global-search-name* (the-as basic arg0))
|
||||
(search-process-tree arg1 (the-as (function process-tree object) L136))
|
||||
(search-process-tree
|
||||
arg1
|
||||
(lambda ((a0-0 process)) (not (name= (-> a0-0 name) *global-search-name*)))
|
||||
)
|
||||
)
|
||||
|
||||
;; definition for function process-count
|
||||
@ -1174,7 +1180,11 @@
|
||||
(set! *global-search-count* 0)
|
||||
(iterate-process-tree
|
||||
arg0
|
||||
(the-as (function object object) L134)
|
||||
(lambda
|
||||
((a0-0 process))
|
||||
(set! *global-search-count* (+ *global-search-count* 1))
|
||||
#t
|
||||
)
|
||||
*null-kernel-context*
|
||||
)
|
||||
*global-search-count*
|
||||
@ -1203,7 +1213,7 @@
|
||||
((v0-0
|
||||
(search-process-tree
|
||||
arg1
|
||||
(the-as (function process-tree object) L129)
|
||||
(lambda ((a0-0 process)) (= (-> a0-0 type) *global-search-name*))
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -1239,7 +1249,7 @@
|
||||
((v0-0
|
||||
(search-process-tree
|
||||
arg1
|
||||
(the-as (function process-tree object) L122)
|
||||
(lambda ((a0-0 process)) (!= (-> a0-0 type) *global-search-name*))
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -1362,7 +1372,98 @@
|
||||
)
|
||||
(execute-process-tree
|
||||
*active-pool*
|
||||
(the-as (function object object) L86)
|
||||
(lambda ((a0-0 process)) (let ((s5-0 *kernel-context*)
|
||||
(v1-0 (-> a0-0 status))
|
||||
)
|
||||
(cond
|
||||
((or (= v1-0 'waiting-to-run) (= v1-0 'suspended))
|
||||
(set! (-> s5-0 current-process) a0-0)
|
||||
(cond
|
||||
((nonzero? (logand (-> a0-0 mask) 4))
|
||||
(set! *stdcon* *stdcon1*)
|
||||
(set! *debug-draw-pauseable* #t)
|
||||
)
|
||||
(else
|
||||
(set! *stdcon* *stdcon0*)
|
||||
(set! *debug-draw-pauseable* #f)
|
||||
)
|
||||
)
|
||||
(when (-> a0-0 trans-hook)
|
||||
(let*
|
||||
((s4-0
|
||||
(new
|
||||
'process
|
||||
'cpu-thread
|
||||
a0-0
|
||||
'trans
|
||||
256
|
||||
(-> a0-0 main-thread stack-top)
|
||||
)
|
||||
)
|
||||
(v0-1
|
||||
(reset-and-call s4-0 (-> a0-0 trans-hook))
|
||||
)
|
||||
)
|
||||
(delete s4-0)
|
||||
(let ((v1-12 v0-1))
|
||||
)
|
||||
)
|
||||
(if (= (-> a0-0 status) 'dead)
|
||||
(return (begin
|
||||
(set! (-> s5-0 current-process) #f)
|
||||
'dead
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(if (nonzero? (logand (-> a0-0 mask) 128))
|
||||
(set! (-> a0-0 status) 'suspended)
|
||||
((-> a0-0 main-thread resume-hook)
|
||||
(-> a0-0 main-thread)
|
||||
)
|
||||
)
|
||||
(cond
|
||||
((= (-> a0-0 status) 'dead)
|
||||
(set! (-> s5-0 current-process) #f)
|
||||
'dead
|
||||
)
|
||||
(else
|
||||
(when (-> a0-0 post-hook)
|
||||
(let
|
||||
((s4-1
|
||||
(new
|
||||
'process
|
||||
'cpu-thread
|
||||
a0-0
|
||||
'post
|
||||
256
|
||||
(&-> *dram-stack* 14336)
|
||||
)
|
||||
)
|
||||
)
|
||||
(reset-and-call s4-1 (-> a0-0 post-hook))
|
||||
(delete s4-1)
|
||||
)
|
||||
(if (= (-> a0-0 status) 'dead)
|
||||
(return (begin
|
||||
(set! (-> s5-0 current-process) #f)
|
||||
'dead
|
||||
)
|
||||
)
|
||||
)
|
||||
(set! (-> a0-0 status) 'suspended)
|
||||
)
|
||||
(set! (-> s5-0 current-process) #f)
|
||||
#f
|
||||
)
|
||||
)
|
||||
)
|
||||
((= v1-0 'dead)
|
||||
'dead
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
*kernel-context*
|
||||
)
|
||||
)
|
||||
|
@ -67,9 +67,6 @@ const std::unordered_set<std::string> skip_in_compiling = {
|
||||
//////////////////////
|
||||
// GKERNEL
|
||||
//////////////////////
|
||||
// these refer to anonymous functions, which aren't yet implemented.
|
||||
"process-by-name", "process-not-name", "process-count", "kill-by-type", "kill-not-type",
|
||||
"kill-by-name", "kill-not-name", "kernel-dispatcher",
|
||||
|
||||
// asm
|
||||
"(method 10 process)"
|
||||
|
Loading…
Reference in New Issue
Block a user