mirror of
https://github.com/open-goal/jak-project.git
synced 2025-02-01 11:22:12 +00:00
[decomp] Small fixes (#541)
* fix a few bugs * fix local vars missing in top level * more small fixes * support missing inline array access case * one more fix
This commit is contained in:
parent
c910a22c1b
commit
b1a76b2291
@ -289,15 +289,7 @@ bool TypeSystem::try_reverse_lookup_inline_array(const FieldReverseLookupInput&
|
||||
assert(di.can_deref);
|
||||
assert(!di.mem_deref); // if we make integer arrays allowed to be inline-array, this will break.
|
||||
|
||||
if (input.stride) {
|
||||
if (input.stride != di.stride) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input.offset >= di.stride) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input.stride && input.stride == di.stride && input.offset < di.stride) {
|
||||
// variable lookup.
|
||||
FieldReverseLookupOutput::Token token;
|
||||
token.kind = FieldReverseLookupOutput::Token::Kind::VAR_IDX;
|
||||
@ -315,40 +307,40 @@ bool TypeSystem::try_reverse_lookup_inline_array(const FieldReverseLookupInput&
|
||||
next_input.offset = input.offset;
|
||||
next_input.base_type = di.result_type;
|
||||
return try_reverse_lookup(next_input, path, addr_of, result_type);
|
||||
} else {
|
||||
// constant lookup, or accessing within the first one
|
||||
// which element we are in
|
||||
int elt_idx = input.offset / di.stride;
|
||||
// how many bytes into the element we look
|
||||
int offset_into_elt = input.offset - (elt_idx * di.stride);
|
||||
// the expected number of bytes into the element we would look to grab a ref to the elt.
|
||||
int expected_offset_into_elt = lookup_type(di.result_type)->get_offset();
|
||||
|
||||
FieldReverseLookupOutput::Token token;
|
||||
token.kind = FieldReverseLookupOutput::Token::Kind::CONSTANT_IDX;
|
||||
token.idx = elt_idx;
|
||||
|
||||
if (offset_into_elt == expected_offset_into_elt && !input.deref.has_value()) {
|
||||
// just get an element (possibly zero, and we want to include the 0 if so)
|
||||
// for the degenerate inline-array case, it seems more likely that we get the zeroth object
|
||||
// rather than the array? Either way, this code should be compatible with both approaches.
|
||||
path->push_back(token);
|
||||
*addr_of = false;
|
||||
*result_type = di.result_type;
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise access within the element
|
||||
path->push_back(token);
|
||||
|
||||
FieldReverseLookupInput next_input;
|
||||
next_input.deref = input.deref;
|
||||
next_input.stride = 0;
|
||||
// try_reverse_lookup expects "offset_into_field - boxed_offset"
|
||||
next_input.offset = offset_into_elt - expected_offset_into_elt;
|
||||
next_input.base_type = di.result_type;
|
||||
return try_reverse_lookup(next_input, path, addr_of, result_type);
|
||||
}
|
||||
|
||||
// constant lookup, or accessing within the first one
|
||||
// which element we are in
|
||||
int elt_idx = input.offset / di.stride;
|
||||
// how many bytes into the element we look
|
||||
int offset_into_elt = input.offset - (elt_idx * di.stride);
|
||||
// the expected number of bytes into the element we would look to grab a ref to the elt.
|
||||
int expected_offset_into_elt = lookup_type(di.result_type)->get_offset();
|
||||
|
||||
FieldReverseLookupOutput::Token token;
|
||||
token.kind = FieldReverseLookupOutput::Token::Kind::CONSTANT_IDX;
|
||||
token.idx = elt_idx;
|
||||
|
||||
if (offset_into_elt == expected_offset_into_elt && !input.deref.has_value()) {
|
||||
// just get an element (possibly zero, and we want to include the 0 if so)
|
||||
// for the degenerate inline-array case, it seems more likely that we get the zeroth object
|
||||
// rather than the array? Either way, this code should be compatible with both approaches.
|
||||
path->push_back(token);
|
||||
*addr_of = false;
|
||||
*result_type = di.result_type;
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise access within the element
|
||||
path->push_back(token);
|
||||
|
||||
FieldReverseLookupInput next_input;
|
||||
next_input.deref = input.deref;
|
||||
next_input.stride = input.stride;
|
||||
// try_reverse_lookup expects "offset_into_field - boxed_offset"
|
||||
next_input.offset = offset_into_elt - expected_offset_into_elt;
|
||||
next_input.base_type = di.result_type;
|
||||
return try_reverse_lookup(next_input, path, addr_of, result_type);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -191,6 +191,9 @@ class Env {
|
||||
|
||||
const std::unordered_map<std::string, std::string>& var_remap_map() const { return m_var_remap; }
|
||||
|
||||
// hacks:
|
||||
bool aggressively_reject_cond_to_value_rewrite = false;
|
||||
|
||||
private:
|
||||
RegisterAccess m_end_var;
|
||||
|
||||
|
@ -317,6 +317,14 @@ goos::Object SetVarElement::to_form_internal(const Env& env) const {
|
||||
return pretty_print::build_list("set!", m_dst.to_form(env), 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);
|
||||
@ -2439,14 +2447,13 @@ void GetSymbolStringPointer::get_modified_regs(RegSet& regs) const {
|
||||
// Utilities
|
||||
////////////////////////////////
|
||||
|
||||
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);
|
||||
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<SimpleExpressionElement*>(as_single);
|
||||
auto as_se = dynamic_cast<const SimpleExpressionElement*>(f);
|
||||
if (as_se && as_se->expr().is_identity()) {
|
||||
return as_se->expr().get_arg(0);
|
||||
}
|
||||
@ -2454,6 +2461,11 @@ std::optional<SimpleAtom> form_as_atom(const Form* f) {
|
||||
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) {
|
||||
|
@ -304,6 +304,8 @@ class SetVarElement : public FormElement {
|
||||
const SetVarInfo& info() const { return m_var_info; }
|
||||
const TypeSpec src_type() const { return m_src_type; }
|
||||
|
||||
std::optional<TypeSpec> required_cast(const Env& env) const;
|
||||
|
||||
private:
|
||||
RegisterAccess m_dst;
|
||||
Form* m_src = nullptr;
|
||||
@ -1636,6 +1638,7 @@ class FormPool {
|
||||
std::vector<FormElement*> m_elements;
|
||||
};
|
||||
|
||||
std::optional<SimpleAtom> form_element_as_atom(const FormElement* f);
|
||||
std::optional<SimpleAtom> form_as_atom(const Form* f);
|
||||
FormElement* make_cast_using_existing(Form* form, const TypeSpec& type, FormPool& pool);
|
||||
FormElement* make_cast_using_existing(FormElement* elt, const TypeSpec& type, FormPool& pool);
|
||||
|
@ -1725,7 +1725,7 @@ void StoreArrayAccess::push_to_stack(const Env& env, FormPool& pool, FormStack&
|
||||
auto fr = pool.alloc_element<SetFormFormElement>(
|
||||
form_out, make_optional_cast(m_src_cast_type, expr_form, pool, env));
|
||||
fr->mark_popped();
|
||||
stack.push_form_element(fr, true);
|
||||
fr->push_to_stack(env, pool, stack);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
@ -2178,6 +2178,8 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac
|
||||
// check all to see if they write the value.
|
||||
std::vector<SetVarElement*> dest_sets;
|
||||
std::vector<TypeSpec> source_types; // only explicit accesses that aren't move-eliminated
|
||||
|
||||
int empty_count = 0;
|
||||
for (auto form : write_output_forms) {
|
||||
auto last_in_body = dynamic_cast<SetVarElement*>(form->elts().back());
|
||||
if (last_in_body) {
|
||||
@ -2191,9 +2193,11 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac
|
||||
}
|
||||
last_var = last_in_body->dst();
|
||||
}
|
||||
// For now, I am fine with letting this fail. For example, if the set is eliminated by a
|
||||
// coloring move. If this makes really ugly code later on, we could use this to disable
|
||||
// write as set.
|
||||
empty_count++;
|
||||
}
|
||||
|
||||
if (empty_count > 0 && env.aggressively_reject_cond_to_value_rewrite) {
|
||||
rewrite_as_set = false;
|
||||
}
|
||||
|
||||
if (!last_var.has_value()) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "FormStack.h"
|
||||
#include "Form.h"
|
||||
#include "GenericElementMatcher.h"
|
||||
#include "decompiler/Function/Function.h"
|
||||
|
||||
namespace decompiler {
|
||||
std::string FormStack::StackEntry::print(const Env& env) const {
|
||||
@ -285,7 +286,7 @@ FormElement* try_rewrites_in_place(SetVarElement* in, const Env& env, FormPool&
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::vector<FormElement*> FormStack::rewrite(FormPool& pool, const Env& env) {
|
||||
std::vector<FormElement*> FormStack::rewrite(FormPool& pool, const Env& env) const {
|
||||
std::vector<FormElement*> result;
|
||||
|
||||
for (auto& e : m_stack) {
|
||||
@ -340,7 +341,7 @@ std::vector<FormElement*> FormStack::rewrite(FormPool& pool, const Env& env) {
|
||||
void rewrite_to_get_var(std::vector<FormElement*>& default_result,
|
||||
FormPool& pool,
|
||||
const RegisterAccess& var,
|
||||
const Env&) {
|
||||
const Env& env) {
|
||||
bool keep_going = true;
|
||||
RegisterAccess var_to_get = var;
|
||||
|
||||
@ -361,7 +362,18 @@ void rewrite_to_get_var(std::vector<FormElement*>& default_result,
|
||||
var_to_get = as_one->expr().var();
|
||||
}
|
||||
|
||||
result = last_op_as_set->src()->elts();
|
||||
auto cast = last_op_as_set->required_cast(env);
|
||||
if (cast && cast == TypeSpec("none")) {
|
||||
env.func->warnings.general_warning(
|
||||
"rewrite_to_get_var got a none typed variable. Is there unreachable code?");
|
||||
cast = std::nullopt;
|
||||
}
|
||||
if (cast) {
|
||||
result = {pool.alloc_element<CastElement>(
|
||||
*cast, pool.alloc_sequence_form(nullptr, last_op_as_set->src()->elts()))};
|
||||
} else {
|
||||
result = last_op_as_set->src()->elts();
|
||||
}
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class FormStack {
|
||||
int begin_idx = -1);
|
||||
FormElement* pop_back(FormPool& pool);
|
||||
bool is_single_expression();
|
||||
std::vector<FormElement*> rewrite(FormPool& pool, const Env& env);
|
||||
std::vector<FormElement*> rewrite(FormPool& pool, const Env& env) const;
|
||||
std::string print(const Env& env);
|
||||
bool is_root() const { return m_is_root_stack; }
|
||||
|
||||
|
@ -349,6 +349,11 @@ void ObjectFileDB::ir2_type_analysis_pass(const Config& config) {
|
||||
config.hacks.pair_functions_by_name.end()) {
|
||||
func.ir2.env.set_sloppy_pair_typing();
|
||||
}
|
||||
|
||||
if (config.hacks.reject_cond_to_value.find(func_name) !=
|
||||
config.hacks.reject_cond_to_value.end()) {
|
||||
func.ir2.env.aggressively_reject_cond_to_value_rewrite = true;
|
||||
}
|
||||
func.ir2.env.set_stack_var_hints(try_lookup(config.stack_var_hints_by_function, func_name));
|
||||
if (run_type_analysis_ir2(ts, dts, func)) {
|
||||
successful_functions++;
|
||||
|
@ -66,17 +66,20 @@ RegisterAccess make_dst_var(const Instruction& i, int idx) {
|
||||
// Atom Helpers
|
||||
////////////////////////
|
||||
|
||||
SimpleAtom false_sym() {
|
||||
return SimpleAtom::make_sym_val("#f");
|
||||
}
|
||||
|
||||
SimpleAtom make_src_atom(Register reg, int idx) {
|
||||
if (reg == Register(Reg::GPR, Reg::R0)) {
|
||||
return SimpleAtom::make_int_constant(0);
|
||||
}
|
||||
if (reg == Register(Reg::GPR, Reg::S7)) {
|
||||
return false_sym();
|
||||
}
|
||||
return SimpleAtom::make_var(make_src_var(reg, idx));
|
||||
}
|
||||
|
||||
SimpleAtom false_sym() {
|
||||
return SimpleAtom::make_sym_val("#f");
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
// Expression Helpers
|
||||
////////////////////////
|
||||
|
@ -67,9 +67,19 @@ bool convert_to_expressions(
|
||||
if (f.type.last_arg() != TypeSpec("none")) {
|
||||
auto return_var = f.ir2.atomic_ops->end_op().return_var();
|
||||
new_entries = rewrite_to_get_var(stack, pool, return_var, f.ir2.env);
|
||||
auto reg_return_type =
|
||||
f.ir2.env.get_types_after_op(f.ir2.atomic_ops->ops.size() - 1).get(return_var.reg());
|
||||
if (!dts.ts.tc(f.type.last_arg(), reg_return_type.typespec())) {
|
||||
TypeSpec return_type = f.ir2.env.get_types_after_op(f.ir2.atomic_ops->ops.size() - 1)
|
||||
.get(return_var.reg())
|
||||
.typespec();
|
||||
auto back_as_atom = form_element_as_atom(new_entries.back());
|
||||
if (back_as_atom && back_as_atom->is_var()) {
|
||||
return_type = f.ir2.env.get_variable_type(back_as_atom->var(), true);
|
||||
auto var_cast = f.ir2.env.get_variable_and_cast(back_as_atom->var());
|
||||
if (var_cast.cast) {
|
||||
return_type = *var_cast.cast;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dts.ts.tc(f.type.last_arg(), return_type)) {
|
||||
// we need to cast the final value.
|
||||
auto to_cast = new_entries.back();
|
||||
new_entries.pop_back();
|
||||
|
@ -173,6 +173,15 @@ std::string write_from_top_level(const Function& top_level,
|
||||
}
|
||||
|
||||
std::string result;
|
||||
// local vars:
|
||||
int var_count = 0;
|
||||
auto var_dec = env.local_var_type_list(top_level.ir2.top_form, 0, &var_count);
|
||||
if (var_count > 0) {
|
||||
result += pretty_print::to_string(var_dec);
|
||||
result += '\n';
|
||||
result += '\n';
|
||||
}
|
||||
|
||||
// look for the whole thing being in a (when *debug-segment* ....)
|
||||
bool in_debug_only_file = false;
|
||||
if (forms.size() == 1) {
|
||||
@ -341,6 +350,15 @@ std::string write_from_top_level(const Function& top_level,
|
||||
}
|
||||
}
|
||||
|
||||
if (!something_matched) {
|
||||
auto empty = dynamic_cast<EmptyElement*>(x);
|
||||
if (empty) {
|
||||
something_matched = true;
|
||||
} else if (!x->active()) {
|
||||
something_matched = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!something_matched) {
|
||||
result += ";; failed to figure out what this is:\n";
|
||||
result += pretty_print::to_string(x->to_form(env));
|
||||
|
@ -419,7 +419,7 @@ SSA make_rc_ssa(const Function& function, const RegUsageInfo& rui, const Functio
|
||||
if (succ != -1) {
|
||||
for (auto reg : end_op_info.live) {
|
||||
// only update phis for variables that are actually live at the next block.
|
||||
if (reg.get_kind() != Reg::VF) {
|
||||
if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR) {
|
||||
ssa.add_source_to_phi(succ, reg, current_regs.at(reg));
|
||||
}
|
||||
}
|
||||
|
@ -143,6 +143,8 @@ Config read_config_file(const std::string& path_to_config_file) {
|
||||
hacks_json.at("no_type_analysis_functions_by_name").get<std::unordered_set<std::string>>();
|
||||
config.hacks.types_with_bad_inspect_methods =
|
||||
hacks_json.at("types_with_bad_inspect_methods").get<std::unordered_set<std::string>>();
|
||||
config.hacks.reject_cond_to_value = hacks_json.at("aggressively_reject_cond_to_value_rewrite")
|
||||
.get<std::unordered_set<std::string>>();
|
||||
|
||||
for (auto& entry : hacks_json.at("cond_with_else_max_lengths")) {
|
||||
auto func_name = entry.at(0).get<std::string>();
|
||||
|
@ -53,6 +53,7 @@ struct DecompileHacks {
|
||||
std::unordered_set<std::string> asm_functions_by_name;
|
||||
std::unordered_set<std::string> pair_functions_by_name;
|
||||
std::unordered_map<std::string, CondWithElseLengthHack> cond_with_else_len_by_func_name;
|
||||
std::unordered_set<std::string> reject_cond_to_value;
|
||||
};
|
||||
|
||||
struct Config {
|
||||
|
@ -33697,7 +33697,7 @@
|
||||
;;(define-extern notice-top object) ;; unknown type
|
||||
;;(define-extern speed object) ;; unknown type
|
||||
;;(define-extern cam-notice-dist object) ;; unknown type
|
||||
(define-extern process-drawable-art-error symbol) ;; unknown type
|
||||
(define-extern process-drawable-art-error state)
|
||||
;;(define-extern notice-bottom object) ;; unknown type
|
||||
;;(define-extern eco-info object) ;; unknown type
|
||||
;;(define-extern cam-horz object) ;; unknown type
|
||||
|
@ -19,7 +19,16 @@
|
||||
["(method 20 res-lump)", "b0", 2]
|
||||
],
|
||||
|
||||
// if a cond with an else case is being used a value in a place where it looks wrong
|
||||
// you can add the function name to this list and it will more aggressively reject this rewrite.
|
||||
"aggressively_reject_cond_to_value_rewrite": [
|
||||
"(method 10 res-lump)",
|
||||
"(method 11 res-lump)",
|
||||
"(method 12 res-lump)"
|
||||
],
|
||||
|
||||
// this provides a hint to the decompiler that these functions will have a lot of inline assembly.
|
||||
// currently it just leaves pcpyld as an asm op.
|
||||
"hint_inline_assembly_functions": ["matrix-transpose!"],
|
||||
|
||||
"asm_functions_by_name": [
|
||||
|
@ -397,7 +397,7 @@
|
||||
|
||||
"(method 0 align-control)": [
|
||||
[[8, 13], "t9", "(function object object)"],
|
||||
[14, "v0", "align-control"]
|
||||
[[14,18], "v0", "align-control"]
|
||||
],
|
||||
|
||||
"str-load": [
|
||||
@ -405,7 +405,8 @@
|
||||
],
|
||||
|
||||
"str-load-status":[
|
||||
[[18, 28], "v1", "load-chunk-msg"]
|
||||
[[18, 22], "v1", "load-chunk-msg"],
|
||||
[26, "v1", "load-chunk-msg"]
|
||||
],
|
||||
|
||||
"str-play-async": [
|
||||
|
@ -58,7 +58,7 @@
|
||||
)
|
||||
)
|
||||
(set! (-> obj process) arg0)
|
||||
(the-as align-control (the-as object obj))
|
||||
obj
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -48,4 +48,4 @@
|
||||
|
||||
;; NOTE - I'm guessing there was a define-extern earlier in the build process
|
||||
;; This is actually set in process-drawable.gc, but it's used by files earlier in the process
|
||||
(define-extern process-drawable-art-error symbol)
|
||||
(define-extern process-drawable-art-error state)
|
||||
|
71
test/decompiler/reference/engine/anim/aligner-h.gc
Normal file
71
test/decompiler/reference/engine/anim/aligner-h.gc
Normal file
@ -0,0 +1,71 @@
|
||||
;;-*-Lisp-*-
|
||||
(in-package goal)
|
||||
|
||||
;; definition of type align-control
|
||||
(deftype align-control (basic)
|
||||
((flags uint32 :offset-assert 4)
|
||||
(process basic :offset-assert 8)
|
||||
(frame-group basic :offset-assert 12)
|
||||
(frame-num float :offset-assert 16)
|
||||
(matrix matrix 2 :inline :offset-assert 32)
|
||||
(transform transform 2 :inline :offset-assert 160)
|
||||
(delta transformq :inline :offset-assert 256)
|
||||
(last-speed float :offset-assert 304)
|
||||
(align transformq :inline :offset 160)
|
||||
)
|
||||
:method-count-assert 14
|
||||
:size-assert #x134
|
||||
:flag-assert #xe00000134
|
||||
(:methods
|
||||
(new (symbol type process) _type_ 0)
|
||||
(dummy-9 () none 9)
|
||||
(dummy-10 () none 10)
|
||||
(dummy-11 () none 11)
|
||||
(dummy-12 () none 12)
|
||||
(dummy-13 () none 13)
|
||||
)
|
||||
)
|
||||
|
||||
;; definition for method 3 of type align-control
|
||||
(defmethod inspect align-control ((obj align-control))
|
||||
(format #t "[~8x] ~A~%" obj (-> obj type))
|
||||
(format #t "~Tflags: #x~X~%" (-> obj flags))
|
||||
(format #t "~Tprocess: ~A~%" (-> obj process))
|
||||
(format #t "~Tframe-group: ~A~%" (-> obj frame-group))
|
||||
(format #t "~Tframe-num: ~f~%" (-> obj frame-num))
|
||||
(format #t "~Tmatrix[2] @ #x~X~%" (-> obj matrix))
|
||||
(format #t "~Ttransform[2] @ #x~X~%" (-> obj transform))
|
||||
(format #t "~Tdelta: #<transformq @ #x~X>~%" (-> obj delta))
|
||||
(format #t "~Tlast-speed: (meters ~m)~%" (-> obj last-speed))
|
||||
(format #t "~Talign: #<transformq @ #x~X>~%" (-> obj transform))
|
||||
obj
|
||||
)
|
||||
|
||||
;; definition for method 0 of type align-control
|
||||
;; INFO: Return type mismatch object vs align-control.
|
||||
(defmethod
|
||||
new
|
||||
align-control
|
||||
((allocation symbol) (type-to-make type) (arg0 process))
|
||||
(local-vars (pp process))
|
||||
(let
|
||||
((obj
|
||||
(object-new allocation type-to-make (the-as int (-> type-to-make size)))
|
||||
)
|
||||
)
|
||||
(if (zero? obj)
|
||||
(return (begin
|
||||
(let ((t9-1 (the-as (function object object) enter-state))
|
||||
(a0-1 "memory")
|
||||
)
|
||||
(set! (-> pp next-state) process-drawable-art-error)
|
||||
(t9-1 a0-1)
|
||||
)
|
||||
(the-as align-control 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(set! (-> obj process) arg0)
|
||||
obj
|
||||
)
|
||||
)
|
@ -59,8 +59,4 @@
|
||||
;; definition for symbol *temp-mem-usage*, type symbol
|
||||
(define *temp-mem-usage* #f)
|
||||
|
||||
;; failed to figure out what this is:
|
||||
(empty-form)
|
||||
|
||||
)
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
;;-*-Lisp-*-
|
||||
(in-package goal)
|
||||
|
||||
(local-vars (gp-0 game-info))
|
||||
|
||||
;; definition of type game-bank
|
||||
(deftype game-bank (basic)
|
||||
((life-max-default float :offset-assert 4)
|
||||
|
@ -106,16 +106,6 @@
|
||||
(set! (-> *eye-control-array* data v1-5 blink) 0.0)
|
||||
)
|
||||
|
||||
;; failed to figure out what this is:
|
||||
(empty-form)
|
||||
|
||||
;; failed to figure out what this is:
|
||||
(empty-form)
|
||||
|
||||
;; failed to figure out what this is:
|
||||
(let ((v0-4 0))
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -224,12 +224,6 @@
|
||||
(set! (-> *sky-upload-data* circle data gp-0 w) 0.0)
|
||||
)
|
||||
|
||||
;; failed to figure out what this is:
|
||||
(empty-form)
|
||||
|
||||
;; failed to figure out what this is:
|
||||
(empty-form)
|
||||
|
||||
;; definition of type sky-tng-data
|
||||
(deftype sky-tng-data (basic)
|
||||
((giftag-base qword :inline :offset-assert 16)
|
||||
@ -317,7 +311,3 @@
|
||||
;; failed to figure out what this is:
|
||||
(let ((v0-15 0))
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -116,7 +116,6 @@
|
||||
)
|
||||
|
||||
;; definition for function str-load-status
|
||||
;; INFO: Return type mismatch structure vs symbol.
|
||||
(defun str-load-status ((length-out (pointer int32)))
|
||||
(if (check-busy *load-str-rpc*)
|
||||
(return 'busy)
|
||||
@ -127,12 +126,9 @@
|
||||
(if (= (-> response result) (load-msg-result error))
|
||||
(return 'error)
|
||||
)
|
||||
(set!
|
||||
(-> length-out 0)
|
||||
(the-as int (the-as load-chunk-msg (-> response maxlen)))
|
||||
)
|
||||
(set! (-> length-out 0) (the-as int (-> response maxlen)))
|
||||
)
|
||||
(the-as symbol 'complete)
|
||||
'complete
|
||||
)
|
||||
|
||||
;; definition for function str-load-cancel
|
||||
|
@ -537,7 +537,7 @@
|
||||
(+
|
||||
(-> type-to-make size)
|
||||
(the-as uint (* len (if (type-type? content-type number)
|
||||
(-> content-type size)
|
||||
(the-as int (-> content-type size))
|
||||
4
|
||||
)
|
||||
)
|
||||
@ -801,7 +801,7 @@
|
||||
(the-as
|
||||
uint
|
||||
(* (-> obj allocated-length) (if (type-type? (-> obj content-type) number)
|
||||
(-> obj content-type size)
|
||||
(the-as int (-> obj content-type size))
|
||||
4
|
||||
)
|
||||
)
|
||||
|
@ -159,7 +159,7 @@
|
||||
)
|
||||
(let ((obj (the-as cpu-thread (cond
|
||||
((-> arg0 top-thread)
|
||||
(&+ arg3 -7164)
|
||||
(the-as cpu-thread (&+ arg3 -7164))
|
||||
)
|
||||
(else
|
||||
(let
|
||||
@ -183,7 +183,7 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(+ v1-2 4)
|
||||
(the-as cpu-thread (+ v1-2 4))
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -627,7 +627,7 @@
|
||||
)
|
||||
(-> obj name)
|
||||
)
|
||||
#f
|
||||
(the-as process #f)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -1348,7 +1348,7 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(the-as process #f)
|
||||
(the-as process (the-as process-tree #f))
|
||||
)
|
||||
|
||||
;; definition for function kernel-dispatcher
|
||||
@ -1553,7 +1553,7 @@
|
||||
(when parent
|
||||
(let ((child (-> parent 0 child)))
|
||||
(if (= child proc)
|
||||
(return #f)
|
||||
(return (the-as (pointer process-tree) #f))
|
||||
)
|
||||
(while child
|
||||
(if (= (-> child 0 brother) proc)
|
||||
@ -1562,7 +1562,7 @@
|
||||
(set! child (-> child 0 brother))
|
||||
)
|
||||
)
|
||||
#f
|
||||
(the-as (pointer process-tree) #f)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -171,6 +171,7 @@
|
||||
|
||||
;; definition for function looping-code
|
||||
;; INFO: Return type mismatch none vs symbol.
|
||||
;; WARN: rewrite_to_get_var got a none typed variable. Is there unreachable code?
|
||||
(defun looping-code ()
|
||||
(while #t
|
||||
(suspend)
|
||||
|
@ -1739,7 +1739,7 @@ TEST_F(FormRegressionTest, ExprArrayMethod0) {
|
||||
" int\n"
|
||||
" (+\n"
|
||||
" (-> arg1 size)\n"
|
||||
" (the-as uint (* arg3 (if (type-type? arg2 number) (-> arg2 size) 4)))\n"
|
||||
" (the-as uint (* arg3 (if (type-type? arg2 number) (the-as int (-> arg2 size)) 4)))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
@ -1817,7 +1817,7 @@ TEST_F(FormRegressionTest, ExprArrayMethod5) {
|
||||
" (-> arg0 allocated-length)\n"
|
||||
" (if\n"
|
||||
" (type-type? (-> arg0 content-type) number)\n"
|
||||
" (-> arg0 content-type size)\n"
|
||||
" (the-as int (-> arg0 content-type size))\n"
|
||||
" 4\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
|
@ -337,7 +337,7 @@ TEST_F(FormRegressionTest, ExprMethod0Thread) {
|
||||
std::string expected =
|
||||
"(let ((obj (the-as cpu-thread (cond\n"
|
||||
" ((-> arg2 top-thread)\n"
|
||||
" (&+ arg5 -7164)\n"
|
||||
" (the-as cpu-thread (&+ arg5 -7164))\n"
|
||||
" )\n"
|
||||
" (else\n"
|
||||
" (let\n"
|
||||
@ -355,7 +355,7 @@ TEST_F(FormRegressionTest, ExprMethod0Thread) {
|
||||
" (+ (+ v1-2 (the-as int (-> arg1 size))) arg4)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (+ v1-2 4)\n"
|
||||
" (the-as cpu-thread (+ v1-2 4))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
@ -1114,7 +1114,7 @@ TEST_F(FormRegressionTest, ExprMethod14DeadPool) {
|
||||
" )\n"
|
||||
" (-> arg0 name)\n"
|
||||
" )\n"
|
||||
" #f\n"
|
||||
" (the-as process #f)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
|
@ -15,9 +15,8 @@ namespace {
|
||||
|
||||
// list of object files to ignore during reference checks
|
||||
const std::unordered_set<std::string> g_files_to_skip_compiling = {
|
||||
"timer", // accessing timer regs
|
||||
"display", // interrupt handlers
|
||||
"game-info-h", // variable scoped at object file top-level issue.
|
||||
"timer", // accessing timer regs
|
||||
"display", // interrupt handlers
|
||||
};
|
||||
|
||||
// the functions we expect the decompiler to skip
|
||||
@ -107,7 +106,6 @@ const std::unordered_set<std::string> g_functions_to_skip_compiling = {
|
||||
"rand-vu-init",
|
||||
"rand-vu",
|
||||
"rand-vu-nostep", // random hardware
|
||||
"log2", // weird tricky int-as-float stuff
|
||||
|
||||
// trig
|
||||
"sin-rad", // fpu acc
|
||||
@ -148,9 +146,6 @@ const std::unordered_set<std::string> g_functions_to_skip_compiling = {
|
||||
// asm
|
||||
"invalidate-cache-line",
|
||||
|
||||
// capture
|
||||
"(method 3 gs-store-image-packet)", // print giftag weirdness
|
||||
|
||||
// sync-info
|
||||
"(method 15 sync-info)", // needs display stuff first
|
||||
"(method 15 sync-info-eased)", // needs display stuff first
|
||||
|
@ -40,6 +40,71 @@ TEST(TypeSystem, DefaultMethods) {
|
||||
ts.assert_method_id("function", "mem-usage", GOAL_MEMUSAGE_METHOD);
|
||||
}
|
||||
|
||||
TEST(TypeSystemReverse, NestedInlineWeird) {
|
||||
// tests the case where we're accessing nested inline arrays, with a dynamic inner access
|
||||
// and constant outer access, which will be constant propagated by the GOAL compiler.
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
goos::Reader reader;
|
||||
auto add_type = [&](const std::string& str) {
|
||||
auto in = reader.read_from_string(str).as_pair()->cdr.as_pair()->car.as_pair()->cdr;
|
||||
parse_deftype(in, &ts);
|
||||
};
|
||||
|
||||
add_type(
|
||||
"(deftype rgba (uint32)\n"
|
||||
" ((r uint8 :offset 0)\n"
|
||||
" (g uint8 :offset 8)\n"
|
||||
" (b uint8 :offset 16)\n"
|
||||
" (a uint8 :offset 24)\n"
|
||||
" )\n"
|
||||
" :flag-assert #x900000004\n"
|
||||
" )");
|
||||
|
||||
add_type(
|
||||
"(deftype char-color (structure)\n"
|
||||
" ((color rgba 4 :offset-assert 0)\n"
|
||||
" )\n"
|
||||
" :method-count-assert 9\n"
|
||||
" :size-assert #x10\n"
|
||||
" :flag-assert #x900000010\n"
|
||||
" )");
|
||||
|
||||
add_type(
|
||||
"(deftype font-work (structure)\n"
|
||||
" (\n"
|
||||
" (color-table char-color 64 :inline :offset 1984)\n"
|
||||
" (last-color uint64 :offset-assert 3008)\n"
|
||||
" (save-last-color uint64 :offset-assert 3016)\n"
|
||||
" (buf basic :offset-assert 3024)\n"
|
||||
" (str-ptr uint32 :offset-assert 3028)\n"
|
||||
" (flags uint32 :offset-assert 3032)\n"
|
||||
" (reg-save uint32 5 :offset-assert 3036)\n"
|
||||
" )\n"
|
||||
" :method-count-assert 9\n"
|
||||
" :size-assert #xbf0\n"
|
||||
" :flag-assert #x900000bf0\n"
|
||||
" )");
|
||||
|
||||
FieldReverseLookupInput input;
|
||||
input.stride = 4;
|
||||
input.base_type = ts.make_typespec("font-work");
|
||||
input.offset = 2496;
|
||||
DerefKind dk;
|
||||
dk.size = 4;
|
||||
dk.sign_extend = false;
|
||||
dk.is_store = false;
|
||||
dk.reg_kind = RegClass::GPR_64;
|
||||
input.deref = dk;
|
||||
auto result = ts.reverse_field_lookup(input);
|
||||
EXPECT_TRUE(result.success);
|
||||
|
||||
EXPECT_EQ(result.tokens.at(0).print(), "color-table");
|
||||
EXPECT_EQ(result.tokens.at(1).print(), "32"); // 32 * 16 + 1984 = 2496
|
||||
EXPECT_EQ(result.tokens.at(2).print(), "color");
|
||||
EXPECT_EQ(result.tokens.at(3).kind, FieldReverseLookupOutput::Token::Kind::VAR_IDX);
|
||||
}
|
||||
|
||||
TEST(TypeSystem, TypeSpec) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
|
Loading…
x
Reference in New Issue
Block a user