mirror of
https://github.com/open-goal/jak-project
synced 2026-08-22 07:04:29 -04:00
[Decomp] Decompile gcommon, gstring-h, and gkernel-h (#249)
* begin decomp * untabify * finish decomp of gcommon * warning with function name * clean up gkernel-h * fix all types
This commit is contained in:
@@ -474,7 +474,7 @@ void insertSpecialBreaks(NodePool& pool, PrettyPrinterNode* node) {
|
||||
}
|
||||
}
|
||||
|
||||
if (name == "defun" || name == "defmethod") {
|
||||
if (name == "defun" || name == "defmethod" || name == "defun-debug") {
|
||||
auto* parent_type_dec = getNextListOnLine(node);
|
||||
if (parent_type_dec) {
|
||||
insertNewlineAfter(pool, parent_type_dec->paren, 0);
|
||||
|
||||
@@ -91,6 +91,10 @@ TP_Type SimpleAtom::get_type(const TypeState& input,
|
||||
return TP_Type::make_type_object(TypeSpec(m_string));
|
||||
}
|
||||
|
||||
if (type->second == TypeSpec("function")) {
|
||||
lg::warn("Function {} has unknown type", m_string);
|
||||
}
|
||||
|
||||
// otherwise, just return a normal typespec
|
||||
return TP_Type::make_from_ts(type->second);
|
||||
}
|
||||
|
||||
@@ -704,12 +704,12 @@ void SimpleExpressionElement::update_from_stack(const Env& env,
|
||||
allow_side_effects);
|
||||
break;
|
||||
case SimpleExpression::Kind::RIGHT_SHIFT_LOGIC:
|
||||
update_from_stack_force_ui_2(env, FixedOperatorKind::SHR, pool, stack, result,
|
||||
allow_side_effects);
|
||||
update_from_stack_copy_first_int_2(env, FixedOperatorKind::SHR, pool, stack, result,
|
||||
allow_side_effects);
|
||||
break;
|
||||
case SimpleExpression::Kind::RIGHT_SHIFT_ARITH:
|
||||
update_from_stack_force_si_2(env, FixedOperatorKind::SAR, pool, stack, result,
|
||||
allow_side_effects);
|
||||
update_from_stack_copy_first_int_2(env, FixedOperatorKind::SAR, pool, stack, result,
|
||||
allow_side_effects);
|
||||
break;
|
||||
case SimpleExpression::Kind::MUL_UNSIGNED:
|
||||
update_from_stack_force_ui_2(env, FixedOperatorKind::MULTIPLICATION, pool, stack, result,
|
||||
|
||||
@@ -89,6 +89,15 @@ Matcher Matcher::symbol(const std::string& name) {
|
||||
return m;
|
||||
}
|
||||
|
||||
Matcher Matcher::if_with_else(const Matcher& condition,
|
||||
const Matcher& true_case,
|
||||
const Matcher& false_case) {
|
||||
Matcher m;
|
||||
m.m_kind = Kind::IF_WITH_ELSE;
|
||||
m.m_sub_matchers = {condition, true_case, false_case};
|
||||
return m;
|
||||
}
|
||||
|
||||
Matcher Matcher::deref(const Matcher& root,
|
||||
bool is_addr_of,
|
||||
const std::vector<DerefTokenMatcher>& tokens) {
|
||||
@@ -367,6 +376,30 @@ bool Matcher::do_match(Form* input, MatchResult::Maps* maps_out) const {
|
||||
}
|
||||
} break;
|
||||
|
||||
case Kind::IF_WITH_ELSE: {
|
||||
auto as_cond = dynamic_cast<CondWithElseElement*>(input->try_as_single_element());
|
||||
if (!as_cond) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (as_cond->entries.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_sub_matchers.at(0).do_match(as_cond->entries.front().condition, maps_out)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_sub_matchers.at(1).do_match(as_cond->entries.front().body, maps_out)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_sub_matchers.at(2).do_match(as_cond->else_ir, maps_out)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
return false;
|
||||
@@ -429,6 +462,13 @@ GenericOpMatcher GenericOpMatcher::func(const Matcher& func_matcher) {
|
||||
return m;
|
||||
}
|
||||
|
||||
GenericOpMatcher GenericOpMatcher::condition(IR2_Condition::Kind condition) {
|
||||
GenericOpMatcher m;
|
||||
m.m_kind = Kind::CONDITION;
|
||||
m.m_condition_kind = condition;
|
||||
return m;
|
||||
}
|
||||
|
||||
bool GenericOpMatcher::do_match(GenericOperator& input, MatchResult::Maps* maps_out) const {
|
||||
switch (m_kind) {
|
||||
case Kind::FIXED:
|
||||
@@ -441,8 +481,14 @@ bool GenericOpMatcher::do_match(GenericOperator& input, MatchResult::Maps* maps_
|
||||
return m_func_matcher.do_match(input.func(), maps_out);
|
||||
}
|
||||
return false;
|
||||
case Kind::CONDITION:
|
||||
if (input.kind() == GenericOperator::Kind::CONDITION_OPERATOR) {
|
||||
return input.condition_kind() == m_condition_kind;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace decompiler
|
||||
@@ -40,6 +40,9 @@ class Matcher {
|
||||
static Matcher deref(const Matcher& root,
|
||||
bool is_addr_of,
|
||||
const std::vector<DerefTokenMatcher>& tokens);
|
||||
static Matcher if_with_else(const Matcher& condition,
|
||||
const Matcher& true_case,
|
||||
const Matcher& false_case);
|
||||
|
||||
enum class Kind {
|
||||
ANY_REG, // matching any register
|
||||
@@ -55,6 +58,7 @@ class Matcher {
|
||||
SET,
|
||||
ANY_LABEL,
|
||||
SYMBOL,
|
||||
IF_WITH_ELSE,
|
||||
INVALID
|
||||
};
|
||||
|
||||
@@ -95,14 +99,16 @@ class GenericOpMatcher {
|
||||
public:
|
||||
static GenericOpMatcher fixed(FixedOperatorKind kind);
|
||||
static GenericOpMatcher func(const Matcher& func_matcher);
|
||||
static GenericOpMatcher condition(IR2_Condition::Kind condition);
|
||||
|
||||
enum class Kind { FIXED, FUNC, INVALID };
|
||||
enum class Kind { FIXED, FUNC, CONDITION, INVALID };
|
||||
|
||||
bool do_match(GenericOperator& input, MatchResult::Maps* maps_out) const;
|
||||
|
||||
private:
|
||||
Kind m_kind = Kind::INVALID;
|
||||
FixedOperatorKind m_fixed_kind = FixedOperatorKind::INVALID;
|
||||
IR2_Condition::Kind m_condition_kind = IR2_Condition::Kind::INVALID;
|
||||
Matcher m_func_matcher;
|
||||
};
|
||||
|
||||
|
||||
@@ -199,6 +199,11 @@ bool convert_to_expressions(Form* top_level_form,
|
||||
map2 = var_map->second;
|
||||
}
|
||||
f.ir2.env.map_args_from_config(config_map->second, map2);
|
||||
} else {
|
||||
auto var_map = get_config().function_var_names.find(f.guessed_name.to_string());
|
||||
if (var_map != get_config().function_var_names.end()) {
|
||||
f.ir2.env.map_args_from_config({}, var_map->second);
|
||||
}
|
||||
}
|
||||
|
||||
// strip out coloring moves
|
||||
|
||||
@@ -21,7 +21,10 @@ void append(goos::Object& _in, const goos::Object& add) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string final_defun_out(const Function& func, const Env& env, const DecompilerTypeSystem& dts) {
|
||||
std::string final_defun_out(const Function& func,
|
||||
const Env& env,
|
||||
const DecompilerTypeSystem& dts,
|
||||
FunctionDefSpecials special_mode) {
|
||||
std::vector<goos::Object> inline_body;
|
||||
func.ir2.top_form->inline_forms(inline_body, env);
|
||||
|
||||
@@ -39,8 +42,14 @@ std::string final_defun_out(const Function& func, const Env& env, const Decompil
|
||||
auto arguments = pretty_print::build_list(argument_elts);
|
||||
|
||||
if (func.guessed_name.kind == FunctionName::FunctionKind::GLOBAL) {
|
||||
std::string def_name = "defun";
|
||||
if (special_mode == FunctionDefSpecials::DEFUN_DEBUG) {
|
||||
def_name = "defun-debug";
|
||||
} else {
|
||||
assert(special_mode == FunctionDefSpecials::NONE);
|
||||
}
|
||||
std::vector<goos::Object> top;
|
||||
top.push_back(pretty_print::to_symbol("defun"));
|
||||
top.push_back(pretty_print::to_symbol(def_name));
|
||||
top.push_back(pretty_print::to_symbol(func.guessed_name.to_string()));
|
||||
top.push_back(arguments);
|
||||
auto top_form = pretty_print::build_list(top);
|
||||
@@ -54,6 +63,7 @@ std::string final_defun_out(const Function& func, const Env& env, const Decompil
|
||||
}
|
||||
|
||||
if (func.guessed_name.kind == FunctionName::FunctionKind::METHOD) {
|
||||
assert(special_mode == FunctionDefSpecials::NONE);
|
||||
std::vector<goos::Object> top;
|
||||
top.push_back(pretty_print::to_symbol("defmethod"));
|
||||
auto method_info =
|
||||
@@ -72,6 +82,7 @@ std::string final_defun_out(const Function& func, const Env& env, const Decompil
|
||||
}
|
||||
|
||||
if (func.guessed_name.kind == FunctionName::FunctionKind::TOP_LEVEL_INIT) {
|
||||
assert(special_mode == FunctionDefSpecials::NONE);
|
||||
std::vector<goos::Object> top;
|
||||
top.push_back(pretty_print::to_symbol("top-level-function"));
|
||||
top.push_back(arguments);
|
||||
@@ -88,7 +99,10 @@ std::string final_defun_out(const Function& func, const Env& env, const Decompil
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string careful_function_to_string(const Function* func, const DecompilerTypeSystem& dts) {
|
||||
std::string careful_function_to_string(
|
||||
const Function* func,
|
||||
const DecompilerTypeSystem& dts,
|
||||
FunctionDefSpecials special_mode = FunctionDefSpecials::NONE) {
|
||||
auto& env = func->ir2.env;
|
||||
if (!func->ir2.top_form) {
|
||||
return ";; ERROR: function was not converted to expressions. Cannot decompile.\n\n";
|
||||
@@ -105,7 +119,7 @@ std::string careful_function_to_string(const Function* func, const DecompilerTyp
|
||||
return ";; ERROR: function has no register use analysis. Cannot decompile.\n\n";
|
||||
}
|
||||
|
||||
return final_defun_out(*func, func->ir2.env, dts) + "\n\n";
|
||||
return final_defun_out(*func, func->ir2.env, dts, special_mode) + "\n\n";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -151,6 +165,14 @@ std::string write_from_top_level(const Function& top_level,
|
||||
Matcher::op_with_rest(GenericOpMatcher::fixed(FixedOperatorKind::TYPE_NEW),
|
||||
{Matcher::any_quoted_symbol(type_name)});
|
||||
|
||||
// (if *debug-segment* (set! mem-print L347) (set! mem-print nothing))
|
||||
auto debug_seg_matcher = Matcher::op(GenericOpMatcher::condition(IR2_Condition::Kind::TRUTHY),
|
||||
{Matcher::symbol("*debug-segment*")});
|
||||
auto debug_def_matcher = Matcher::set(Matcher::any_symbol(0), Matcher::any_label(1));
|
||||
auto non_debug_def_matcher = Matcher::set(Matcher::any_symbol(2), Matcher::symbol("nothing"));
|
||||
auto defun_debug_matcher =
|
||||
Matcher::if_with_else(debug_seg_matcher, debug_def_matcher, non_debug_def_matcher);
|
||||
|
||||
for (auto& x : top_form->elts()) {
|
||||
bool something_matched = false;
|
||||
Form f;
|
||||
@@ -190,6 +212,23 @@ std::string write_from_top_level(const Function& top_level,
|
||||
}
|
||||
}
|
||||
|
||||
if (!something_matched) {
|
||||
auto debug_match_result = match(defun_debug_matcher, &f);
|
||||
if (debug_match_result.matched) {
|
||||
auto first_name = debug_match_result.maps.strings.at(0);
|
||||
auto second_name = debug_match_result.maps.strings.at(2);
|
||||
if (first_name == second_name) {
|
||||
auto func = file.try_get_function_at_label(debug_match_result.maps.label.at(1));
|
||||
if (func) {
|
||||
something_matched = true;
|
||||
result += fmt::format(";; definition (debug) for function {}\n",
|
||||
debug_match_result.maps.strings.at(0));
|
||||
result += careful_function_to_string(func, dts, FunctionDefSpecials::DEFUN_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!something_matched) {
|
||||
result += ";; failed to figure out what this is:\n";
|
||||
result += pretty_print::to_string(x->to_form(env));
|
||||
|
||||
@@ -3,7 +3,13 @@
|
||||
#include "decompiler/Function/Function.h"
|
||||
|
||||
namespace decompiler {
|
||||
std::string final_defun_out(const Function& func, const Env& env, const DecompilerTypeSystem& dts);
|
||||
|
||||
enum class FunctionDefSpecials { NONE, DEFUN_DEBUG };
|
||||
|
||||
std::string final_defun_out(const Function& func,
|
||||
const Env& env,
|
||||
const DecompilerTypeSystem& dts,
|
||||
FunctionDefSpecials special_mode = FunctionDefSpecials::NONE);
|
||||
std::string write_from_top_level(const Function& top_level,
|
||||
const DecompilerTypeSystem& dts,
|
||||
const LinkedObjectFile& file);
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
(define-extern valid? (function object type basic basic object symbol))
|
||||
|
||||
;; has issues:
|
||||
(define-extern breakpoint-range-set! function)
|
||||
(define-extern breakpoint-range-set! (function uint uint uint int))
|
||||
|
||||
|
||||
|
||||
@@ -224,8 +224,12 @@
|
||||
|
||||
;; gkernel-h
|
||||
(deftype cpu-thread (thread)
|
||||
((rreg uint64 8 :offset-assert 40)
|
||||
(freg float 6 :offset-assert 104)
|
||||
(
|
||||
;;(rreg uint64 8 :offset-assert 40)
|
||||
;;(freg float 6 :offset-assert 104)
|
||||
;; changed from GOAL, see gkernel-h.gc
|
||||
(rreg uint64 7 :offset-assert 40)
|
||||
(freg float 8)
|
||||
(stack uint8 :dynamic :offset-assert 128)
|
||||
)
|
||||
|
||||
@@ -378,8 +382,11 @@
|
||||
(deftype catch-frame (stack-frame)
|
||||
((sp int32 :offset-assert 12)
|
||||
(ra int32 :offset-assert 16)
|
||||
(freg float 6 :offset-assert 20)
|
||||
(rreg uint128 8 :offset-assert 48)
|
||||
;; changed from GOAL, see gkernel-h.gc
|
||||
(freg float 10 :offset-assert 20)
|
||||
(rreg uint128 7)
|
||||
;;(freg float 6 :offset-assert 20)
|
||||
;;(rreg uint128 8 :offset-assert 48)
|
||||
)
|
||||
|
||||
(:methods
|
||||
|
||||
@@ -93,10 +93,6 @@
|
||||
"part-tracker"],
|
||||
|
||||
"no_type_analysis_functions_by_name":[
|
||||
"(method 2 vec4s)", // 128-bit bitfield.
|
||||
"(method 3 vec4s)", // 128-bit bitfield
|
||||
"qmem-copy<-!", // 128-bit loads and stores
|
||||
"qmem-copy->!", // 128-bit loads and stores
|
||||
"breakpoint-range-set!", // messing with COP0 registers
|
||||
"(method 0 catch-frame)", // kernel asm
|
||||
"throw-dispatch", // kernel asm
|
||||
|
||||
@@ -32,9 +32,10 @@
|
||||
],
|
||||
|
||||
"(method 2 handle)":[
|
||||
[10, ["a3", "process"]],
|
||||
[11, ["v1", "int"]],
|
||||
[15, ["gp", "int"]]
|
||||
[10, ["a2", "(pointer process)"]],
|
||||
[11, ["a3", "process"]],
|
||||
[12, ["v1", "int"]],
|
||||
[16, ["gp", "int"]]
|
||||
],
|
||||
|
||||
"(method 3 handle)":[
|
||||
|
||||
@@ -83,74 +83,115 @@
|
||||
},
|
||||
|
||||
"ref":{
|
||||
"args":["lst", "index"]
|
||||
"args":["lst", "index"],
|
||||
"vars":{"v1-0":"count"}
|
||||
},
|
||||
|
||||
"(method 4 pair)": {
|
||||
"vars":{"v0-0":"result", "v1-1":"iter"}
|
||||
},
|
||||
|
||||
"last":{
|
||||
"args":["lst"]
|
||||
"args":["lst"],
|
||||
"vars":{"v0-0":"iter"}
|
||||
},
|
||||
"member":{
|
||||
"args":["obj", "lst"]
|
||||
"args":["obj", "lst"],
|
||||
"vars":{"v1-0":"iter"}
|
||||
},
|
||||
"nmember":{
|
||||
"args":["obj", "lst"]
|
||||
},
|
||||
"assoc":{
|
||||
"args":["item", "alist"]
|
||||
"args":["item", "alist"],
|
||||
"vars":{"v1-0":"iter"}
|
||||
},
|
||||
"assoce":{
|
||||
"args":["item", "alist"]
|
||||
"args":["item", "alist"],
|
||||
"vars":{"v1-0":"iter"}
|
||||
},
|
||||
"nassoc":{
|
||||
"args":["item-name", "alist"]
|
||||
"args":["item-name", "alist"],
|
||||
"vars":{"a1-1":"key"}
|
||||
},
|
||||
"nassoce":{
|
||||
"args":["item-name", "alist"]
|
||||
"args":["item-name", "alist"],
|
||||
"vars":{"s4-0":"key"}
|
||||
},
|
||||
"append!":{
|
||||
"args":["front", "back"]
|
||||
"args":["front", "back"],
|
||||
"vars":{"v1-1":"iter"}
|
||||
},
|
||||
"delete!":{
|
||||
"args":["item", "lst"]
|
||||
"args":["item", "lst"],
|
||||
"vars":{"a2-0":"iter", "v1-1":"iter-prev"}
|
||||
},
|
||||
"delete-car!":{
|
||||
"args":["item", "lst"]
|
||||
"args":["item", "lst"],
|
||||
"vars":{"a2-0":"iter", "v1-2":"iter-prev"}
|
||||
},
|
||||
"insert-cons!":{
|
||||
"args":["kv", "alist"]
|
||||
"args":["kv", "alist"],
|
||||
"vars":{"a3-0":"updated-list"}
|
||||
},
|
||||
"sort":{
|
||||
"args":["lst", "compare-func"]
|
||||
"args":["lst", "compare-func"],
|
||||
"vars":{"s4-0":"unsorted-count", "s3-0":"iter", "s2-0":"first-elt", "s1-0":"seoncd-elt", "v1-1":"compare-result"}
|
||||
},
|
||||
"(method 0 inline-array)":{
|
||||
"args":["allocation", "type-to-make", "size"]
|
||||
"(method 0 inline-array-class)":{
|
||||
"args":["allocation", "type-to-make", "size"],
|
||||
"vars":{"v0-0":"obj"}
|
||||
},
|
||||
"(method 0 array)":{
|
||||
"args":["allocation", "type-to-make", "content-type", "size"]
|
||||
"args":["allocation", "type-to-make", "content-type", "len"],
|
||||
"vars":{"v0-1":"obj"}
|
||||
},
|
||||
|
||||
"(method 2 array)":{
|
||||
"vars":{"v1-1":"content-type-sym","s5-0":"i","s5-1":"i","s5-2":"i"
|
||||
,"s5-3":"i","s5-4":"i","s5-5":"i","s5-6":"i","s5-7":"i","s5-8":"i",
|
||||
"s5-9":"i","s5-10":"i","s5-11":"i"}
|
||||
},
|
||||
|
||||
"(method 3 array)":{
|
||||
"vars":{"v1-1":"content-type-sym","s5-0":"i","s5-1":"i","s5-2":"i"
|
||||
,"s5-3":"i","s5-4":"i","s5-5":"i","s5-6":"i","s5-7":"i","s5-8":"i","s5-9":"i","s5-10":"i","s5-11":"i"}
|
||||
},
|
||||
|
||||
"mem-copy!":{
|
||||
"args":["dst", "src", "size"]
|
||||
"args":["dst", "src", "size"],
|
||||
"vars":{"v0-0":"result", "v1-0":"i"}
|
||||
},
|
||||
"qmem-copy<-!":{
|
||||
"args":["dst", "src", "size"]
|
||||
"args":["dst", "src", "size"],
|
||||
"vars":{"v0-0":"result", "v1-1":"qwc", "a2-1":"value"}
|
||||
},
|
||||
"qmem-copy->!":{
|
||||
"args":["dst", "src", "size"]
|
||||
"args":["dst", "src", "size"],
|
||||
"vars":{"v0-0":"result", "v1-1":"qwc", "a0-1":"src-ptr", "a1-1":"dst-ptr", "a2-3":"value"}
|
||||
},
|
||||
"mem-set32!":{
|
||||
"args":["dst", "size", "value"]
|
||||
"args":["dst", "size", "value"],
|
||||
"vars":{"v0-0":"result", "v1-0":"i"}
|
||||
},
|
||||
"mem-or!":{
|
||||
"args":["dst", "src", "size"]
|
||||
"args":["dst", "src", "size"],
|
||||
"vars":{"v0-0":"result", "v1-0":"i"}
|
||||
},
|
||||
"fact":{
|
||||
"args":["x"]
|
||||
},
|
||||
"mem-print":{
|
||||
"args":["data", "word-count"],
|
||||
"vars":{"s4-0":"current-qword"}
|
||||
},
|
||||
"print-tree-bitmask":{
|
||||
"args":["bits", "count"]
|
||||
"args":["bits", "count"],
|
||||
"vars":{"s4-0":"i"}
|
||||
},
|
||||
"valid?":{
|
||||
"args":["obj", "expected-type", "name", "allow-false", "print-dest"]
|
||||
"args":["obj", "expected-type", "name", "allow-false", "print-dest"],
|
||||
"vars":{"v1-1":"in-goal-mem"}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -396,7 +396,12 @@
|
||||
|
||||
(defmacro pair? (obj)
|
||||
;; todo, make this more efficient
|
||||
`(= 2 (logand (the integer ,obj) #b111))
|
||||
;`(= 2 (logand (the integer ,obj) #b111))
|
||||
`(< (shl (the-as int ,obj) 62) 0)
|
||||
)
|
||||
|
||||
(defmacro not-pair? (obj)
|
||||
`(>= (shl (the-as int ,obj) 62) 0)
|
||||
)
|
||||
|
||||
(defmacro binteger? (obj)
|
||||
|
||||
+1060
-358
File diff suppressed because it is too large
Load Diff
+135
-109
@@ -54,34 +54,35 @@
|
||||
;; ENUMS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
; bitfield enum to indicate proprties about a process-tree
|
||||
(defenum process-mask :bitfield #t :type int32
|
||||
(execute 0) ; 1
|
||||
(draw 1) ; 2
|
||||
(pause 2) ; 4
|
||||
(menu 3) ; 8
|
||||
(progress 4) ; 16
|
||||
(actor-pause 5) ; 32
|
||||
(sleep 6) ; 64
|
||||
(sleep-code 7) ; 128
|
||||
(process-tree 8) ; 256 ; not an actual process, just a "tree node" for organization
|
||||
(heap-shrunk 9) ; 512
|
||||
(going 10) ; 1024
|
||||
(movie 11) ; 2048
|
||||
(movie-subject 12) ; 4096
|
||||
(target 13) ; 8192
|
||||
(sidekick 14) ; 16384
|
||||
(crate 15) ; 32768
|
||||
(collectable 16) ; 65536
|
||||
(enemy 17) ; 131072
|
||||
(camera 18) ; 262144
|
||||
(platform 19) ; 524288
|
||||
(ambient 20) ; 1048576
|
||||
(entity 21) ; 2097152
|
||||
(projectile 22) ; 4194304
|
||||
(attackable 23) ; 8388608
|
||||
(death 24) ; 16777216
|
||||
)
|
||||
;; bitfield enum to indicate proprties about a process-tree
|
||||
(defenum process-mask
|
||||
:bitfield #t :type int32
|
||||
(execute 0) ;; 1
|
||||
(draw 1) ;; 2
|
||||
(pause 2) ;; 4
|
||||
(menu 3) ;; 8
|
||||
(progress 4) ;; 16
|
||||
(actor-pause 5) ;; 32
|
||||
(sleep 6) ;; 64
|
||||
(sleep-code 7) ;; 128
|
||||
(process-tree 8) ;; 256 not an actual process, just a "tree node" for organization
|
||||
(heap-shrunk 9) ;; 512
|
||||
(going 10) ;; 1024
|
||||
(movie 11) ;; 2048
|
||||
(movie-subject 12) ;; 4096
|
||||
(target 13) ;; 8192
|
||||
(sidekick 14) ;; 16384
|
||||
(crate 15) ;; 32768
|
||||
(collectable 16) ;; 65536
|
||||
(enemy 17) ;; 131072
|
||||
(camera 18) ;; 262144
|
||||
(platform 19) ;; 524288
|
||||
(ambient 20) ;; 1048576
|
||||
(entity 21) ;; 2097152
|
||||
(projectile 22) ;; 4194304
|
||||
(attackable 23) ;; 8388608
|
||||
(death 24) ;; 16777216
|
||||
)
|
||||
|
||||
;; -961
|
||||
(defconstant PROCESS_CLEAR_MASK
|
||||
@@ -96,10 +97,8 @@
|
||||
`(/ 0 0)
|
||||
)
|
||||
|
||||
;; todo, process check and set
|
||||
|
||||
(defmacro msg-err (&rest args)
|
||||
;"Print a message to stdout immediately. This won't appear in the compiler."
|
||||
;; "Print a message to stdout immediately. This won't appear in the compiler."
|
||||
`(format 0 ,@args)
|
||||
)
|
||||
|
||||
@@ -107,12 +106,6 @@
|
||||
`(format 0 ,@args)
|
||||
)
|
||||
|
||||
;; todo process pointer
|
||||
;; todo process memory usage
|
||||
;; with pp
|
||||
;; todo suspend
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TYPES
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -123,7 +116,7 @@
|
||||
(require-for-run uint32 :offset-assert 8)
|
||||
(allow-to-run uint32 :offset-assert 12)
|
||||
(next-pid int32 :offset-assert 16)
|
||||
(fast-stack-top pointer :offset-assert 20)
|
||||
(fast-stack-top pointer :offset-assert 20)
|
||||
(current-process basic :offset-assert 24)
|
||||
(relocating-process basic :offset-assert 28)
|
||||
(relocating-min int32 :offset-assert 32)
|
||||
@@ -151,23 +144,23 @@
|
||||
|
||||
; DANGER - this type is created in kscheme.cpp. It has room for 12 methods and size 0x28 bytes.
|
||||
(deftype thread (basic)
|
||||
((name basic :offset-assert 4) ;; name of the thread (usually a symbol?)
|
||||
(process process :offset-assert 8) ;; process that the thread belongs to
|
||||
(previous thread :offset-assert 12) ;; previous thread that was running in the process
|
||||
(suspend-hook (function cpu-thread none) :offset-assert 16) ;; function to suspend this thread
|
||||
(resume-hook (function cpu-thread none) :offset-assert 20) ;; function to resume this thread
|
||||
(pc pointer :offset-assert 24) ;; program counter of the thread
|
||||
(sp pointer :offset-assert 28) ;; stack pointer of the thread (actual stack)
|
||||
(stack-top pointer :offset-assert 32) ;; top of the thread's stack (actual stack)
|
||||
(stack-size int32 :offset-assert 36) ;; size of the thread's stack (backup stack)
|
||||
((name basic :offset-assert 4) ;; name of the thread (usually a symbol?)
|
||||
(process process :offset-assert 8) ;; process that the thread belongs to
|
||||
(previous thread :offset-assert 12) ;; previous thread that was running in the process
|
||||
(suspend-hook (function cpu-thread none) :offset-assert 16) ;; function to suspend this thread
|
||||
(resume-hook (function cpu-thread none) :offset-assert 20) ;; function to resume this thread
|
||||
(pc pointer :offset-assert 24) ;; program counter of the thread
|
||||
(sp pointer :offset-assert 28) ;; stack pointer of the thread (actual stack)
|
||||
(stack-top pointer :offset-assert 32) ;; top of the thread's stack (actual stack)
|
||||
(stack-size int32 :offset-assert 36) ;; size of the thread's stack (backup stack)
|
||||
)
|
||||
|
||||
(:methods
|
||||
;; todo, triple check these method numbers.
|
||||
(stack-size-set! ((this thread) (stack-size int)) none 9)
|
||||
(thread-suspend ((this _type_)) none 10) ;; only safe on a cpu-thread, but slot exists for thread
|
||||
(thread-resume ((to-resume _type_)) none 11) ;; only safe on a cpu-thread, but slot exists for thread
|
||||
)
|
||||
;; todo, triple check these method numbers.
|
||||
(stack-size-set! ((this thread) (stack-size int)) none 9)
|
||||
(thread-suspend ((this _type_)) none 10) ;; only safe on a cpu-thread, but slot exists for thread
|
||||
(thread-resume ((to-resume _type_)) none 11) ;; only safe on a cpu-thread, but slot exists for thread
|
||||
)
|
||||
|
||||
:size-assert #x28
|
||||
:method-count-assert 12
|
||||
@@ -177,16 +170,24 @@
|
||||
|
||||
;; A CPU thread is a thread which has some memory to save registers and a stack
|
||||
(deftype cpu-thread (thread)
|
||||
((rreg uint64 8 :offset-assert 40) ;; general purpose saved registers
|
||||
(freg float 6 :offset-assert 104) ;; floating point registers
|
||||
(stack uint8 :dynamic :offset-assert 128) ;; stack memory (dynamic array)
|
||||
(
|
||||
;; This is what GOAL did:
|
||||
;; (rreg uint64 8 :offset-assert 40) ;; general purpose saved registers
|
||||
;; (freg float 6 :offset-assert 104) ;; floating point registers
|
||||
|
||||
;; OpenGOAL has only 5 saved registers but 8 fregs, so we swap a rreg for 2 fregs.
|
||||
(rreg uint64 7 :offset-assert 40)
|
||||
(freg float 8)
|
||||
|
||||
;; This is the same between GOAL and OpenGOAL
|
||||
(stack uint8 :dynamic :offset-assert 128) ;; stack memory (dynamic array)
|
||||
)
|
||||
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (parent-process process) (name symbol) (stack-size int) (stack-top pointer)) _type_ 0)
|
||||
(thread-suspend ((this _type_)) none 10)
|
||||
(thread-resume ((to-resume _type_)) none 11)
|
||||
)
|
||||
(new ((allocation symbol) (type-to-make type) (parent-process process) (name symbol) (stack-size int) (stack-top pointer)) _type_ 0)
|
||||
(thread-suspend ((this _type_)) none 10)
|
||||
(thread-resume ((to-resume _type_)) none 11)
|
||||
)
|
||||
|
||||
:size-assert #x80
|
||||
:method-count-assert 12
|
||||
@@ -207,13 +208,13 @@
|
||||
)
|
||||
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (name basic)) _type_ 0)
|
||||
(activate ((obj _type_) (dest process-tree) (name basic) (stack-top pointer)) process-tree 9)
|
||||
(deactivate ((obj _type_)) none 10)
|
||||
(dummy-method-11 () none 11)
|
||||
(run-logic? ((obj _type_)) symbol 12)
|
||||
(dummy-method () none 13)
|
||||
)
|
||||
(new ((allocation symbol) (type-to-make type) (name basic)) _type_ 0)
|
||||
(activate ((obj _type_) (dest process-tree) (name basic) (stack-top pointer)) process-tree 9)
|
||||
(deactivate ((obj _type_)) none 10)
|
||||
(dummy-method-11 () none 11)
|
||||
(run-logic? ((obj _type_)) symbol 12)
|
||||
(dummy-method () none 13)
|
||||
)
|
||||
|
||||
:size-assert #x20
|
||||
:method-count-assert 14
|
||||
@@ -244,13 +245,13 @@
|
||||
)
|
||||
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (name basic) (stack-size int)) _type_ 0)
|
||||
(activate ((obj _type_) (dest process-tree) (name basic) (stack-top pointer)) process-tree 9)
|
||||
(deactivate ((obj process)) none 10)
|
||||
(dummy-method-11 () none 11)
|
||||
(run-logic? ((obj process)) symbol 12)
|
||||
(dummy-method () none 13)
|
||||
)
|
||||
(new ((allocation symbol) (type-to-make type) (name basic) (stack-size int)) _type_ 0)
|
||||
(activate ((obj _type_) (dest process-tree) (name basic) (stack-top pointer)) process-tree 9)
|
||||
(deactivate ((obj process)) none 10)
|
||||
(dummy-method-11 () none 11)
|
||||
(run-logic? ((obj process)) symbol 12)
|
||||
(dummy-method () none 13)
|
||||
)
|
||||
|
||||
:size-assert #x70
|
||||
:method-count-assert 14
|
||||
@@ -264,10 +265,10 @@
|
||||
;; nothing new!
|
||||
)
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (count int) (stack-size int) (name basic)) _type_ 0)
|
||||
(get-process ((pool _type_) (type-to-make type) (stack-size int)) process 14)
|
||||
(return-process ((pool _type_) (proc process)) none 15)
|
||||
)
|
||||
(new ((allocation symbol) (type-to-make type) (count int) (stack-size int) (name basic)) _type_ 0)
|
||||
(get-process ((pool _type_) (type-to-make type) (stack-size int)) process 14)
|
||||
(return-process ((pool _type_) (proc process)) none 15)
|
||||
)
|
||||
:size-assert #x20
|
||||
:method-count-assert 16
|
||||
:flag-assert #x1000000020
|
||||
@@ -299,28 +300,28 @@
|
||||
(fill-percent float :offset-assert #x30) ;; ??
|
||||
(first-gap dead-pool-heap-rec :offset-assert #x34) ;; ??
|
||||
(first-shrink dead-pool-heap-rec :offset-assert #x38) ;; ??
|
||||
(heap kheap :inline :offset-assert 64) ;; ??
|
||||
(alive-list dead-pool-heap-rec :inline :offset-assert 80) ;; ??
|
||||
(heap kheap :inline :offset-assert 64) ;; ??
|
||||
(alive-list dead-pool-heap-rec :inline :offset-assert 80) ;; ??
|
||||
(last dead-pool-heap-rec :offset #x54 :offset-assert #x54) ;; overlay of (-> alive-list prev)
|
||||
;; note - the placement of dead-list at 92 here is used to determine the packing behavior.
|
||||
;; see TypeSystem::get_size_in_type().
|
||||
(dead-list dead-pool-heap-rec :inline :offset-assert 92) ;; ??
|
||||
(dead-list dead-pool-heap-rec :inline :offset-assert 92) ;; ??
|
||||
(process-list dead-pool-heap-rec :inline :dynamic :offset-assert 104)
|
||||
)
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (name basic) (allocated-length int) (heap-size int)) _type_ 0)
|
||||
(compact ((this dead-pool-heap) (count int)) none 16)
|
||||
(shrink-heap ((this dead-pool-heap) (proc process)) dead-pool-heap 17)
|
||||
(churn ((this dead-pool-heap) (count int)) none 18)
|
||||
(memory-used ((this dead-pool-heap)) int 19)
|
||||
(memory-total ((this dead-pool-heap)) int 20)
|
||||
(gap-size ((this dead-pool-heap) (rec dead-pool-heap-rec)) int 21)
|
||||
(gap-location ((this dead-pool-heap) (rec dead-pool-heap-rec)) pointer 22)
|
||||
(find-gap ((this dead-pool-heap) (rec dead-pool-heap-rec)) dead-pool-heap-rec 23)
|
||||
(find-gap-by-size ((this dead-pool-heap) (size int)) dead-pool-heap-rec 24)
|
||||
(memory-free ((this dead-pool-heap)) int 25)
|
||||
(compact-time ((this dead-pool-heap)) uint 26)
|
||||
)
|
||||
(new ((allocation symbol) (type-to-make type) (name basic) (allocated-length int) (heap-size int)) _type_ 0)
|
||||
(compact ((this dead-pool-heap) (count int)) none 16)
|
||||
(shrink-heap ((this dead-pool-heap) (proc process)) dead-pool-heap 17)
|
||||
(churn ((this dead-pool-heap) (count int)) none 18)
|
||||
(memory-used ((this dead-pool-heap)) int 19)
|
||||
(memory-total ((this dead-pool-heap)) int 20)
|
||||
(gap-size ((this dead-pool-heap) (rec dead-pool-heap-rec)) int 21)
|
||||
(gap-location ((this dead-pool-heap) (rec dead-pool-heap-rec)) pointer 22)
|
||||
(find-gap ((this dead-pool-heap) (rec dead-pool-heap-rec)) dead-pool-heap-rec 23)
|
||||
(find-gap-by-size ((this dead-pool-heap) (size int)) dead-pool-heap-rec 24)
|
||||
(memory-free ((this dead-pool-heap)) int 25)
|
||||
(compact-time ((this dead-pool-heap)) uint 26)
|
||||
)
|
||||
|
||||
:size-assert #x68
|
||||
:method-count-assert #x1b
|
||||
@@ -338,23 +339,28 @@
|
||||
:size-assert #xc
|
||||
:method-count-assert 9
|
||||
:flag-assert #x90000000c
|
||||
:no-runtime-type ;; already constructed, don't do it again.
|
||||
)
|
||||
|
||||
|
||||
;; A catch frame is a frame you can "throw" to, by name.
|
||||
;; You can "throw" out of a function and into another function.
|
||||
(deftype catch-frame (stack-frame)
|
||||
((sp int32 :offset 12) ;; where to reset the stack when throwing.
|
||||
(ra int32 :offset 16) ;; where to jump when throwing
|
||||
((sp int32 :offset 12) ;; where to reset the stack when throwing.
|
||||
(ra int32 :offset 16) ;; where to jump when throwing
|
||||
|
||||
;; todo - rework for x86-64.
|
||||
(freg float 6 :offset-assert 20) ;; saved floating point registers from "catch" statement
|
||||
(rreg uint128 8 :offset-assert 48) ;; saved GPRs from "catch" statement (ugh they are 128s)
|
||||
;; In GOAL
|
||||
;; (freg float 6 :offset-assert 20) ;; saved floating point registers from "catch" statement
|
||||
;; (rreg uint128 8 :offset-assert 48) ;; saved GPRs from "catch" statement (ugh they are 128s)
|
||||
|
||||
;; In OpenGOAL, we swap a rreg for 4 more fregs.
|
||||
(freg float 10 :offset-assert 20) ;; only use 8
|
||||
(rreg uint128 7) ;; only use 5
|
||||
)
|
||||
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (name symbol) (func function) (params (pointer uint64))) object 0)
|
||||
)
|
||||
(new ((allocation symbol) (type-to-make type) (name symbol) (func function) (params (pointer uint64))) object 0)
|
||||
)
|
||||
:size-assert #xb0
|
||||
:method-count-assert 9
|
||||
:flag-assert #x9000000b0
|
||||
@@ -365,15 +371,23 @@
|
||||
((exit (function object) :offset-assert 12)) ;; function to call to clean up
|
||||
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (func (function object))) protect-frame)
|
||||
)
|
||||
(new ((allocation symbol) (type-to-make type) (func (function object))) protect-frame)
|
||||
)
|
||||
:size-assert 16
|
||||
:method-count-assert 9
|
||||
:flag-assert #x900000010
|
||||
)
|
||||
|
||||
;; A handle is a reference to a _specific_ process.
|
||||
;; There are two tricks here:
|
||||
;; 1). A process can be relocated in memory, so we can't just store a process.
|
||||
;; Instead, we use a (pointer process) that points to a non-moving record.
|
||||
;; dead-pool-heap takes care of maintaining these.
|
||||
;; 2). Process memory can be reused. We don't want to get confused by this.
|
||||
;; So we also store a unique PID to the specific activation of a process.
|
||||
;; This way we can check the handle's PID against the PID in the process.
|
||||
(deftype handle (uint64)
|
||||
((process (pointer process) :offset 0)
|
||||
((process (pointer process) :offset 0) ;; set to #f for null.
|
||||
(pid int32 :offset 32)
|
||||
(u64 uint64 :offset 0)
|
||||
)
|
||||
@@ -390,10 +404,10 @@
|
||||
|
||||
(defmacro get-process-from-handle (handle)
|
||||
;; the actual implementation is more clever than this.
|
||||
;; Checks PID.
|
||||
`(if (-> ,handle process)
|
||||
(let ((proc (-> (-> ,handle process))))
|
||||
(if (= (-> ,handle pid)
|
||||
(-> proc pid))
|
||||
(if (= (-> ,handle pid) (-> proc pid))
|
||||
proc
|
||||
)
|
||||
)
|
||||
@@ -421,9 +435,9 @@
|
||||
(event basic :offset-assert 32)
|
||||
)
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (name basic) (code function)
|
||||
(trans function) (enter function) (exit (function object)) (event function)) _type_ 0)
|
||||
)
|
||||
(new ((allocation symbol) (type-to-make type) (name basic) (code function)
|
||||
(trans function) (enter function) (exit (function object)) (event function)) _type_ 0)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x24
|
||||
:flag-assert #x900000024
|
||||
@@ -445,6 +459,10 @@
|
||||
)
|
||||
|
||||
(defmacro as-process (ppointer)
|
||||
;; convert a (pointer process) to a process.
|
||||
;; this uses the self field, which seems to always just get set to the object.
|
||||
;; perhaps when deleting a process you could have it set self to #f?
|
||||
;; I don't see this happen anywhere though, so it's not clear.
|
||||
`(if ,ppointer
|
||||
(-> (-> ,ppointer) self)
|
||||
)
|
||||
@@ -488,20 +506,28 @@
|
||||
)
|
||||
|
||||
(defmacro process-mask-set! (mask enum-value)
|
||||
;; sets the given bits in the process mask (with or)
|
||||
`(set! ,mask (logior ,mask (process-mask ,enum-value)))
|
||||
)
|
||||
|
||||
(defmacro suspend ()
|
||||
;; suspend the current process.
|
||||
`(rlet ((pp :reg r13 :reset-here #t))
|
||||
;; we pass the current thread to the kernel with the pp register.
|
||||
;; so it should be backed up on the stack here.
|
||||
(.push pp)
|
||||
;; set to the current thread
|
||||
(set! pp (-> (the process pp) top-thread))
|
||||
;; call the suspend hook (put nothing as the argument)
|
||||
((-> (the cpu-thread pp) suspend-hook) (the cpu-thread 0))
|
||||
;; now we've been resumed, restore pp.
|
||||
(.pop pp)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro process-deactivate ()
|
||||
;; deactivate the current process
|
||||
`(rlet ((pp :reg r13 :reset-here #t :type process))
|
||||
(deactivate pp)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -479,7 +479,7 @@ Val* Compiler::compile_deref(const goos::Object& form, const goos::Object& _rest
|
||||
// array-indexable and a structure, we treat it like a structure only if the
|
||||
// deref thing is one of the field names. Otherwise, array.
|
||||
if (field_name == "content-type" || field_name == "length" ||
|
||||
field_name == "allocated-length" || field_name == "type") {
|
||||
field_name == "allocated-length" || field_name == "type" || field_name == "data") {
|
||||
result = get_field_of_structure(struct_type, result, field_name, env);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2207,7 +2207,7 @@ TEST_F(FormRegressionTest, ExprPrintTreeBitmask) {
|
||||
" (format (quote #t) \" \")\n"
|
||||
" (format (quote #t) \"| \")\n"
|
||||
" )\n"
|
||||
" (set! arg0 (shr (the-as uint arg0) 1))\n"
|
||||
" (set! arg0 (shr arg0 1))\n"
|
||||
" (set! s4-0 (+ s4-0 1))\n"
|
||||
" )\n"
|
||||
" (set! v1-3 (quote #f))\n"
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
(format #t "TEST FAIL~%~%")
|
||||
)
|
||||
|
||||
(print test-result)
|
||||
(printl test-result)
|
||||
)
|
||||
0
|
||||
@@ -13,17 +13,17 @@ TEST(GameNoDebugSegment, Init) {
|
||||
compiler.run_test_from_string("(inspect *kernel-context*)");
|
||||
|
||||
// these should be equal, both the fallback inspect method
|
||||
EXPECT_TRUE(compiler.run_test_from_string("(print (eq? (method-of-type kernel-context inspect) "
|
||||
EXPECT_TRUE(compiler.run_test_from_string("(printl (eq? (method-of-type kernel-context inspect) "
|
||||
"(method-of-type cpu-thread inspect))) 0") ==
|
||||
std::vector<std::string>{"#t\n0\n"});
|
||||
|
||||
// should be below the debug heap.
|
||||
EXPECT_TRUE(compiler.run_test_from_string("(print (< (the uint (method-of-type kernel-context "
|
||||
EXPECT_TRUE(compiler.run_test_from_string("(printl (< (the uint (method-of-type kernel-context "
|
||||
"inspect)) (the uint (-> debug base)))) 0") ==
|
||||
std::vector<std::string>{"#t\n0\n"});
|
||||
|
||||
// debug segment flag should be disabled.
|
||||
EXPECT_TRUE(compiler.run_test_from_string("(print *debug-segment*) 0") ==
|
||||
EXPECT_TRUE(compiler.run_test_from_string("(printl *debug-segment*) 0") ==
|
||||
std::vector<std::string>{"#f\n0\n"});
|
||||
|
||||
compiler.shutdown_target();
|
||||
|
||||
Reference in New Issue
Block a user