support anonymous functions (#323)

This commit is contained in:
water111 2021-03-14 19:06:51 -04:00 committed by GitHub
parent e93d97dd07
commit d60d9b639a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 259 additions and 36 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"));

View File

@ -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

View File

@ -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:

View File

@ -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);

View File

@ -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);

View File

@ -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...");

View 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

View 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);
}

View File

@ -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";

View File

@ -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

View File

@ -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": [

View File

@ -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)

View File

@ -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*
)
)

View File

@ -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)"