diff --git a/common/type_system/TypeFieldLookup.cpp b/common/type_system/TypeFieldLookup.cpp index c992bc0726..2ffff91d3c 100644 --- a/common/type_system/TypeFieldLookup.cpp +++ b/common/type_system/TypeFieldLookup.cpp @@ -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); } /*! diff --git a/decompiler/IR2/Env.h b/decompiler/IR2/Env.h index 34b792e1e1..cacc15b518 100644 --- a/decompiler/IR2/Env.h +++ b/decompiler/IR2/Env.h @@ -191,6 +191,9 @@ class Env { const std::unordered_map& var_remap_map() const { return m_var_remap; } + // hacks: + bool aggressively_reject_cond_to_value_rewrite = false; + private: RegisterAccess m_end_var; diff --git a/decompiler/IR2/Form.cpp b/decompiler/IR2/Form.cpp index e2a6315b2e..011e28e5b0 100644 --- a/decompiler/IR2/Form.cpp +++ b/decompiler/IR2/Form.cpp @@ -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 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& f) { f(this); m_src->apply(f); @@ -2439,14 +2447,13 @@ void GetSymbolStringPointer::get_modified_regs(RegSet& regs) const { // Utilities //////////////////////////////// -std::optional form_as_atom(const Form* f) { - auto as_single = f->try_as_single_element(); - auto as_atom = dynamic_cast(as_single); +std::optional form_element_as_atom(const FormElement* f) { + auto as_atom = dynamic_cast(f); if (as_atom) { return as_atom->atom(); } - auto as_se = dynamic_cast(as_single); + auto as_se = dynamic_cast(f); if (as_se && as_se->expr().is_identity()) { return as_se->expr().get_arg(0); } @@ -2454,6 +2461,11 @@ std::optional form_as_atom(const Form* f) { return {}; } +std::optional 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(elt); if (as_cast) { diff --git a/decompiler/IR2/Form.h b/decompiler/IR2/Form.h index 070b2245d0..f275490bdc 100644 --- a/decompiler/IR2/Form.h +++ b/decompiler/IR2/Form.h @@ -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 required_cast(const Env& env) const; + private: RegisterAccess m_dst; Form* m_src = nullptr; @@ -1636,6 +1638,7 @@ class FormPool { std::vector m_elements; }; +std::optional form_element_as_atom(const FormElement* f); std::optional 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); diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index d342c0487c..075ccb3a9c 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -1725,7 +1725,7 @@ void StoreArrayAccess::push_to_stack(const Env& env, FormPool& pool, FormStack& auto fr = pool.alloc_element( 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 dest_sets; std::vector 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(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()) { diff --git a/decompiler/IR2/FormStack.cpp b/decompiler/IR2/FormStack.cpp index bb18c66a1f..b279a6b11f 100644 --- a/decompiler/IR2/FormStack.cpp +++ b/decompiler/IR2/FormStack.cpp @@ -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 FormStack::rewrite(FormPool& pool, const Env& env) { +std::vector FormStack::rewrite(FormPool& pool, const Env& env) const { std::vector result; for (auto& e : m_stack) { @@ -340,7 +341,7 @@ std::vector FormStack::rewrite(FormPool& pool, const Env& env) { void rewrite_to_get_var(std::vector& 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& 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( + *cast, pool.alloc_sequence_form(nullptr, last_op_as_set->src()->elts()))}; + } else { + result = last_op_as_set->src()->elts(); + } } first = false; } diff --git a/decompiler/IR2/FormStack.h b/decompiler/IR2/FormStack.h index fc2b7c5239..1ee341aecd 100644 --- a/decompiler/IR2/FormStack.h +++ b/decompiler/IR2/FormStack.h @@ -42,7 +42,7 @@ class FormStack { int begin_idx = -1); FormElement* pop_back(FormPool& pool); bool is_single_expression(); - std::vector rewrite(FormPool& pool, const Env& env); + std::vector rewrite(FormPool& pool, const Env& env) const; std::string print(const Env& env); bool is_root() const { return m_is_root_stack; } diff --git a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp index c2ab4b3348..a781c107dd 100644 --- a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp +++ b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp @@ -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++; diff --git a/decompiler/analysis/atomic_op_builder.cpp b/decompiler/analysis/atomic_op_builder.cpp index 94d0cbae95..81c3d6e43c 100644 --- a/decompiler/analysis/atomic_op_builder.cpp +++ b/decompiler/analysis/atomic_op_builder.cpp @@ -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 //////////////////////// diff --git a/decompiler/analysis/expression_build.cpp b/decompiler/analysis/expression_build.cpp index 55bbd935f4..1167d070c8 100644 --- a/decompiler/analysis/expression_build.cpp +++ b/decompiler/analysis/expression_build.cpp @@ -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(); diff --git a/decompiler/analysis/final_output.cpp b/decompiler/analysis/final_output.cpp index 220c1124ad..0e306a0aea 100644 --- a/decompiler/analysis/final_output.cpp +++ b/decompiler/analysis/final_output.cpp @@ -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(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)); diff --git a/decompiler/analysis/variable_naming.cpp b/decompiler/analysis/variable_naming.cpp index 128d0234a7..763c3feefa 100644 --- a/decompiler/analysis/variable_naming.cpp +++ b/decompiler/analysis/variable_naming.cpp @@ -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)); } } diff --git a/decompiler/config.cpp b/decompiler/config.cpp index 07d72b5aff..d433308a46 100644 --- a/decompiler/config.cpp +++ b/decompiler/config.cpp @@ -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>(); config.hacks.types_with_bad_inspect_methods = hacks_json.at("types_with_bad_inspect_methods").get>(); + config.hacks.reject_cond_to_value = hacks_json.at("aggressively_reject_cond_to_value_rewrite") + .get>(); for (auto& entry : hacks_json.at("cond_with_else_max_lengths")) { auto func_name = entry.at(0).get(); diff --git a/decompiler/config.h b/decompiler/config.h index 04b20c6a6b..79fc90264f 100644 --- a/decompiler/config.h +++ b/decompiler/config.h @@ -53,6 +53,7 @@ struct DecompileHacks { std::unordered_set asm_functions_by_name; std::unordered_set pair_functions_by_name; std::unordered_map cond_with_else_len_by_func_name; + std::unordered_set reject_cond_to_value; }; struct Config { diff --git a/decompiler/config/all-types.gc b/decompiler/config/all-types.gc index cfe648281e..0d088fd15b 100644 --- a/decompiler/config/all-types.gc +++ b/decompiler/config/all-types.gc @@ -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 diff --git a/decompiler/config/jak1_ntsc_black_label/hacks.jsonc b/decompiler/config/jak1_ntsc_black_label/hacks.jsonc index 586af0873d..a701eb5cd5 100644 --- a/decompiler/config/jak1_ntsc_black_label/hacks.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/hacks.jsonc @@ -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": [ diff --git a/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc b/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc index 9344bb06d5..22638191d5 100644 --- a/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc @@ -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": [ diff --git a/goal_src/engine/anim/aligner-h.gc b/goal_src/engine/anim/aligner-h.gc index 85616a6ded..dab3b5023f 100644 --- a/goal_src/engine/anim/aligner-h.gc +++ b/goal_src/engine/anim/aligner-h.gc @@ -58,7 +58,7 @@ ) ) (set! (-> obj process) arg0) - (the-as align-control (the-as object obj)) + obj ) ) diff --git a/goal_src/engine/draw/drawable-h.gc b/goal_src/engine/draw/drawable-h.gc index 88b52c2065..fee5a0f0c0 100644 --- a/goal_src/engine/draw/drawable-h.gc +++ b/goal_src/engine/draw/drawable-h.gc @@ -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) diff --git a/test/decompiler/reference/engine/anim/aligner-h.gc b/test/decompiler/reference/engine/anim/aligner-h.gc new file mode 100644 index 0000000000..df74b8abdb --- /dev/null +++ b/test/decompiler/reference/engine/anim/aligner-h.gc @@ -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: #~%" (-> obj delta)) + (format #t "~Tlast-speed: (meters ~m)~%" (-> obj last-speed)) + (format #t "~Talign: #~%" (-> 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 + ) + ) diff --git a/test/decompiler/reference/engine/debug/memory-usage-h_REF.gc b/test/decompiler/reference/engine/debug/memory-usage-h_REF.gc index 1583a09f18..6a1ec0f50a 100644 --- a/test/decompiler/reference/engine/debug/memory-usage-h_REF.gc +++ b/test/decompiler/reference/engine/debug/memory-usage-h_REF.gc @@ -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) - ) - diff --git a/test/decompiler/reference/engine/game/game-info-h_REF.gc b/test/decompiler/reference/engine/game/game-info-h_REF.gc index 43d68e25ea..590d2e690b 100644 --- a/test/decompiler/reference/engine/game/game-info-h_REF.gc +++ b/test/decompiler/reference/engine/game/game-info-h_REF.gc @@ -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) diff --git a/test/decompiler/reference/engine/gfx/eye-h_REF.gc b/test/decompiler/reference/engine/gfx/eye-h_REF.gc index e20d43cf6c..b3db3fd1f8 100644 --- a/test/decompiler/reference/engine/gfx/eye-h_REF.gc +++ b/test/decompiler/reference/engine/gfx/eye-h_REF.gc @@ -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)) ) - - - - diff --git a/test/decompiler/reference/engine/gfx/sky/sky-h_REF.gc b/test/decompiler/reference/engine/gfx/sky/sky-h_REF.gc index fc8f1e3d76..6b689bb617 100644 --- a/test/decompiler/reference/engine/gfx/sky/sky-h_REF.gc +++ b/test/decompiler/reference/engine/gfx/sky/sky-h_REF.gc @@ -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)) ) - - - - diff --git a/test/decompiler/reference/engine/load/load-dgo_REF.gc b/test/decompiler/reference/engine/load/load-dgo_REF.gc index 552fb7b0ff..fd6a8c95c2 100644 --- a/test/decompiler/reference/engine/load/load-dgo_REF.gc +++ b/test/decompiler/reference/engine/load/load-dgo_REF.gc @@ -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 diff --git a/test/decompiler/reference/kernel/gcommon_REF.gc b/test/decompiler/reference/kernel/gcommon_REF.gc index 11e72ee0f8..67f8a2e55e 100644 --- a/test/decompiler/reference/kernel/gcommon_REF.gc +++ b/test/decompiler/reference/kernel/gcommon_REF.gc @@ -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 ) ) diff --git a/test/decompiler/reference/kernel/gkernel_REF.gc b/test/decompiler/reference/kernel/gkernel_REF.gc index 6d74dcc702..46dfe9b7a6 100644 --- a/test/decompiler/reference/kernel/gkernel_REF.gc +++ b/test/decompiler/reference/kernel/gkernel_REF.gc @@ -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) ) ) ) diff --git a/test/decompiler/reference/kernel/gstate_REF.gc b/test/decompiler/reference/kernel/gstate_REF.gc index cb808a4e44..3646b4ae74 100644 --- a/test/decompiler/reference/kernel/gstate_REF.gc +++ b/test/decompiler/reference/kernel/gstate_REF.gc @@ -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) diff --git a/test/decompiler/test_FormExpressionBuild.cpp b/test/decompiler/test_FormExpressionBuild.cpp index 59f735bb2f..d0387e8c6e 100644 --- a/test/decompiler/test_FormExpressionBuild.cpp +++ b/test/decompiler/test_FormExpressionBuild.cpp @@ -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" diff --git a/test/decompiler/test_gkernel_decomp.cpp b/test/decompiler/test_gkernel_decomp.cpp index 2e6fcd1212..3f2edcee77 100644 --- a/test/decompiler/test_gkernel_decomp.cpp +++ b/test/decompiler/test_gkernel_decomp.cpp @@ -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" diff --git a/test/offline/offline_test_main.cpp b/test/offline/offline_test_main.cpp index 431a0aff23..50418d5ff5 100644 --- a/test/offline/offline_test_main.cpp +++ b/test/offline/offline_test_main.cpp @@ -15,9 +15,8 @@ namespace { // list of object files to ignore during reference checks const std::unordered_set 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 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 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 diff --git a/test/test_type_system.cpp b/test/test_type_system.cpp index 3813197444..b98aa2b5ed 100644 --- a/test/test_type_system.cpp +++ b/test/test_type_system.cpp @@ -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();