From ddd60fca48e719d3b3295420864ff617ba5bfb9a Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sun, 16 Oct 2022 18:19:59 -0400 Subject: [PATCH] [decompiler] handle pointer to symbol value, clean up prints on offline test (#1978) - fix issue described in https://github.com/open-goal/jak-project/issues/1939 - fix `text`, which was manually patched with the wrong offset (was reading the symbol value off by one byte) - clean up some random useless prints - make the offline tests keep trying if there's a comparison error, clean up the output a bit so the diffs are all at the end. --- decompiler/Disasm/Instruction.cpp | 23 +- decompiler/Disasm/Instruction.h | 23 +- decompiler/Disasm/InstructionDecode.cpp | 11 + decompiler/Function/Function.cpp | 11 +- decompiler/IR2/AtomicOp.cpp | 9 + decompiler/IR2/AtomicOp.h | 5 +- decompiler/IR2/FormExpressionAnalysis.cpp | 3 +- decompiler/ObjectFile/LinkedObjectFile.cpp | 10 +- decompiler/ObjectFile/LinkedObjectFile.h | 5 +- .../ObjectFile/LinkedObjectFileCreation.cpp | 8 +- decompiler/ObjectFile/LinkedWord.h | 20 +- .../analysis/analyze_inspect_method.cpp | 7 - decompiler/analysis/atomic_op_builder.cpp | 10 +- decompiler/analysis/cfg_builder.cpp | 29 -- decompiler/config/jak2/all-types.gc | 4 +- decompiler/types2/ForwardProp.cpp | 11 + decompiler/util/sparticle_decompile.cpp | 4 + goal_src/jak2/engine/ui/text-h.gc | 2 +- goal_src/jak2/engine/ui/text.gc | 12 +- .../reference/jak2/engine/ui/text_REF.gc | 413 +++--------------- test/offline/offline_test_main.cpp | 180 ++++---- 21 files changed, 291 insertions(+), 509 deletions(-) diff --git a/decompiler/Disasm/Instruction.cpp b/decompiler/Disasm/Instruction.cpp index 3cb018d75b..e1e54655e3 100644 --- a/decompiler/Disasm/Instruction.cpp +++ b/decompiler/Disasm/Instruction.cpp @@ -30,6 +30,8 @@ std::string InstructionAtom::to_string(const std::vector& label return "Q"; case IMM_SYM: return sym; + case IMM_SYM_VAL_PTR: + return sym; case VF_FIELD: ASSERT(imm >= 0 && imm < 4); return fmt::format(".{}", "xyzw"[imm]); @@ -79,9 +81,17 @@ void InstructionAtom::set_vu_q() { /*! * Make this atom a symbol. */ -void InstructionAtom::set_sym(std::string _sym) { +void InstructionAtom::set_sym(const std::string& _sym) { kind = IMM_SYM; - sym = std::move(_sym); + sym = _sym; +} + +/*! + * Make this atom a symbol value pointer. + */ +void InstructionAtom::set_sym_val_ptr(const std::string& _sym) { + kind = IMM_SYM_VAL_PTR; + sym = _sym; } /*! @@ -129,17 +139,10 @@ int InstructionAtom::get_label() const { * Get as symbol, or error if not a symbol. */ std::string InstructionAtom::get_sym() const { - ASSERT(kind == IMM_SYM); + ASSERT(kind == IMM_SYM || kind == IMM_SYM_VAL_PTR); return sym; } -/*! - * True if this atom is some sort of constant that doesn't involve linking. - */ -bool InstructionAtom::is_link_or_label() const { - return kind == IMM_SYM || kind == LABEL; -} - bool InstructionAtom::operator==(const InstructionAtom& other) const { if (kind != other.kind) { return false; diff --git a/decompiler/Disasm/Instruction.h b/decompiler/Disasm/Instruction.h index 7a6bc85d2b..c0eccb1c90 100644 --- a/decompiler/Disasm/Instruction.h +++ b/decompiler/Disasm/Instruction.h @@ -20,13 +20,14 @@ constexpr int MAX_INTRUCTION_DEST = 1; // An "atom", representing a single register, immediate, etc... for use in an Instruction. struct InstructionAtom { enum AtomKind { - REGISTER, // An EE Register - IMM, // An immediate value (stored as int32) - IMM_SYM, // An immediate value (a symbolic link) - LABEL, // A label in a LinkedObjectFile - VU_ACC, // The VU0 Accumulator - VU_Q, // The VU0 Q Register - VF_FIELD, // Field specifier + REGISTER, // An EE Register + IMM, // An immediate value (stored as int32) + IMM_SYM, // An immediate value (a symbolic link) + IMM_SYM_VAL_PTR, // pointer to value of a symbol + LABEL, // A label in a LinkedObjectFile + VU_ACC, // The VU0 Accumulator + VU_Q, // The VU0 Q Register + VF_FIELD, // Field specifier INVALID } kind = INVALID; @@ -35,7 +36,8 @@ struct InstructionAtom { void set_label(int id); void set_vu_q(); void set_vu_acc(); - void set_sym(std::string _sym); + void set_sym(const std::string& _sym); + void set_sym_val_ptr(const std::string& _sym); void set_vf_field(uint32_t value); Register get_reg() const; @@ -46,15 +48,14 @@ struct InstructionAtom { std::string to_string(const std::vector& labels) const; - bool is_link_or_label() const; bool is_reg() const { return kind == REGISTER; } bool is_imm() const { return kind == IMM; } bool is_label() const { return kind == LABEL; } - bool is_sym() const { return kind == IMM_SYM; } + bool is_sym() const { return kind == IMM_SYM || kind == IMM_SYM_VAL_PTR; } bool is_reg(Register r) const { return kind == REGISTER && reg == r; } bool is_imm(int32_t i) const { return kind == IMM && imm == i; } - bool is_sym(const std::string& name) const { return kind == IMM_SYM && name == sym; } + bool is_sym(const std::string& name) const { return is_sym() && name == sym; } bool operator==(const InstructionAtom& other) const; bool operator!=(const InstructionAtom& other) const { return !((*this) == other); } diff --git a/decompiler/Disasm/InstructionDecode.cpp b/decompiler/Disasm/InstructionDecode.cpp index ecf3eaccca..7b455d8eb5 100644 --- a/decompiler/Disasm/InstructionDecode.cpp +++ b/decompiler/Disasm/InstructionDecode.cpp @@ -1193,6 +1193,17 @@ Instruction decode_instruction(LinkedWord& word, LinkedObjectFile& file, int seg ASSERT(fixed); } + if (word.kind() == LinkedWord::SYM_VAL_OFFSET) { + bool fixed = false; + for (int j = 0; j < i.n_src; j++) { + if (i.src[j].kind == InstructionAtom::IMM) { + fixed = true; + i.src[j].set_sym_val_ptr(word.symbol_name()); + } + } + ASSERT(fixed); + } + if (word.kind() == LinkedWord::HI_PTR) { ASSERT(i.kind == InstructionKind::LUI); bool fixed = false; diff --git a/decompiler/Function/Function.cpp b/decompiler/Function/Function.cpp index c64c9546f3..1b7d297f7f 100644 --- a/decompiler/Function/Function.cpp +++ b/decompiler/Function/Function.cpp @@ -504,8 +504,7 @@ void Function::find_method_defs(LinkedObjectFile& file, DecompilerTypeSystem& dt for (const auto& instr : instructions) { // look for lw t9, method-set!(s7) if (instr.kind == InstructionKind::LW && instr.get_dst(0).get_reg() == make_gpr(Reg::T9) && - instr.get_src(0).kind == InstructionAtom::IMM_SYM && - instr.get_src(0).get_sym() == "method-set!" && + instr.get_src(0).is_sym() && instr.get_src(0).get_sym() == "method-set!" && instr.get_src(1).get_reg() == make_gpr(Reg::S7)) { state = 1; continue; @@ -514,8 +513,7 @@ void Function::find_method_defs(LinkedObjectFile& file, DecompilerTypeSystem& dt if (state == 1) { // look for lw a0, type-name(s7) if (instr.kind == InstructionKind::LW && instr.get_dst(0).get_reg() == make_gpr(Reg::A0) && - instr.get_src(0).kind == InstructionAtom::IMM_SYM && - instr.get_src(1).get_reg() == make_gpr(Reg::S7)) { + instr.get_src(0).is_sym() && instr.get_src(1).get_reg() == make_gpr(Reg::S7)) { type_name = instr.get_src(0).get_sym(); state = 2; continue; @@ -587,7 +585,7 @@ void Function::find_type_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts) for (const auto& instr : instructions) { // look for lw xx, type(s7) - if (instr.kind == InstructionKind::LW && instr.get_src(0).kind == InstructionAtom::IMM_SYM && + if (instr.kind == InstructionKind::LW && instr.get_src(0).is_sym() && instr.get_src(0).get_sym() == "type" && instr.get_src(1).get_reg() == make_gpr(Reg::S7)) { state = 1; temp_reg = instr.get_dst(0).get_reg(); @@ -621,8 +619,7 @@ void Function::find_type_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts) if (state == 3) { // look for lw a1, parent-type(s7) if (instr.kind == InstructionKind::LW && instr.get_dst(0).get_reg() == make_gpr(Reg::A1) && - instr.get_src(0).kind == InstructionAtom::IMM_SYM && - instr.get_src(1).get_reg() == make_gpr(Reg::S7)) { + instr.get_src(0).is_sym() && instr.get_src(1).get_reg() == make_gpr(Reg::S7)) { state = 4; parent_type = instr.get_src(0).get_sym(); continue; diff --git a/decompiler/IR2/AtomicOp.cpp b/decompiler/IR2/AtomicOp.cpp index eed0998a77..d1463f02b7 100644 --- a/decompiler/IR2/AtomicOp.cpp +++ b/decompiler/IR2/AtomicOp.cpp @@ -122,6 +122,13 @@ SimpleAtom SimpleAtom::make_sym_val(const std::string& name) { return result; } +SimpleAtom SimpleAtom::make_sym_val_ptr(const std::string& name) { + SimpleAtom result; + result.m_kind = Kind::SYMBOL_VAL_PTR; + result.m_string = name; + return result; +} + SimpleAtom SimpleAtom::make_empty_list() { SimpleAtom result; result.m_kind = Kind::EMPTY_LIST; @@ -206,6 +213,8 @@ goos::Object SimpleAtom::to_form(const std::vector& labels, con return pretty_print::to_symbol("'()"); case Kind::STATIC_ADDRESS: return pretty_print::to_symbol(labels.at(m_int).name); + case Kind::SYMBOL_VAL_PTR: + return pretty_print::to_symbol(fmt::format("(&-> '{} value)", m_string)); default: ASSERT(false); return {}; diff --git a/decompiler/IR2/AtomicOp.h b/decompiler/IR2/AtomicOp.h index 3b3cf42acb..2bf81b5e74 100644 --- a/decompiler/IR2/AtomicOp.h +++ b/decompiler/IR2/AtomicOp.h @@ -131,6 +131,7 @@ class SimpleAtom { INTEGER_CONSTANT, SYMBOL_PTR, SYMBOL_VAL, + SYMBOL_VAL_PTR, EMPTY_LIST, STATIC_ADDRESS, INVALID @@ -140,6 +141,7 @@ class SimpleAtom { static SimpleAtom make_var(const RegisterAccess& var); static SimpleAtom make_sym_ptr(const std::string& name); static SimpleAtom make_sym_val(const std::string& name); + static SimpleAtom make_sym_val_ptr(const std::string& name); static SimpleAtom make_empty_list(); static SimpleAtom make_int_constant(s64 value); static SimpleAtom make_static_address(int static_label_id); @@ -174,6 +176,7 @@ class SimpleAtom { bool is_sym_val(const std::string& str) const { return m_kind == Kind::SYMBOL_VAL && m_string == str; } + bool is_sym_val_ptr() const { return m_kind == Kind::SYMBOL_VAL_PTR; }; bool is_empty_list() const { return m_kind == Kind::EMPTY_LIST; }; bool is_static_addr() const { return m_kind == Kind::STATIC_ADDRESS; }; Kind get_kind() const { return m_kind; } @@ -184,7 +187,7 @@ class SimpleAtom { SimpleExpression as_expr() const; TP_Type get_type(const TypeState& input, const Env& env, const DecompilerTypeSystem& dts) const; const std::string& get_str() const { - ASSERT(is_sym_ptr() || is_sym_val()); + ASSERT(is_sym_ptr() || is_sym_val() || is_sym_val_ptr()); return m_string; } void mark_as_float(); diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index 94cd15997e..0f61705bbb 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -753,7 +753,8 @@ void SimpleExpressionElement::update_from_stack_identity(const Env& env, } } - } else if (arg.is_sym_ptr() || arg.is_sym_val() || arg.is_int() || arg.is_empty_list()) { + } else if (arg.is_sym_ptr() || arg.is_sym_val() || arg.is_int() || arg.is_empty_list() || + arg.is_sym_val_ptr()) { result->push_back(this); return; } else { diff --git a/decompiler/ObjectFile/LinkedObjectFile.cpp b/decompiler/ObjectFile/LinkedObjectFile.cpp index 7454d247de..417b553f1c 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.cpp +++ b/decompiler/ObjectFile/LinkedObjectFile.cpp @@ -189,11 +189,14 @@ void LinkedObjectFile::symbol_link_word(int source_segment, * Add link information that a word's lower 16 bits are the offset of the given symbol relative to * the symbol table register. */ -void LinkedObjectFile::symbol_link_offset(int source_segment, int source_offset, const char* name) { +void LinkedObjectFile::symbol_link_offset(int source_segment, + int source_offset, + const char* name, + bool subtract_one) { ASSERT((source_offset % 4) == 0); auto& word = words_by_seg.at(source_segment).at(source_offset / 4); ASSERT(word.kind() == LinkedWord::PLAIN_DATA); - word.set_to_symbol(LinkedWord::SYM_OFFSET, name); + word.set_to_symbol(subtract_one ? LinkedWord::SYM_VAL_OFFSET : LinkedWord::SYM_OFFSET, name); } /*! @@ -312,6 +315,9 @@ void LinkedObjectFile::append_word_to_string(std::string& dest, const LinkedWord case LinkedWord::SYM_OFFSET: sprintf(buff, " .sym-off 0x%x %s\n", word.data >> 16, word.symbol_name().c_str()); break; + case LinkedWord::SYM_VAL_OFFSET: + sprintf(buff, " .sym-val-off 0x%x %s\n", word.data >> 16, word.symbol_name().c_str()); + break; default: throw std::runtime_error("nyi"); } diff --git a/decompiler/ObjectFile/LinkedObjectFile.h b/decompiler/ObjectFile/LinkedObjectFile.h index f592892a03..2a6e1b794a 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.h +++ b/decompiler/ObjectFile/LinkedObjectFile.h @@ -41,7 +41,10 @@ class LinkedObjectFile { int source_offset, const char* name, LinkedWord::Kind kind); - void symbol_link_offset(int source_segment, int source_offset, const char* name); + void symbol_link_offset(int source_segment, + int source_offset, + const char* name, + bool subtract_one); Function& get_function_at_label(int label_id); Function* try_get_function_at_label(int label_id); Function* try_get_function_at_label(const DecompilerLabel& label); diff --git a/decompiler/ObjectFile/LinkedObjectFileCreation.cpp b/decompiler/ObjectFile/LinkedObjectFileCreation.cpp index f7eaad5f95..fdd1f3f319 100644 --- a/decompiler/ObjectFile/LinkedObjectFileCreation.cpp +++ b/decompiler/ObjectFile/LinkedObjectFileCreation.cpp @@ -143,8 +143,8 @@ static uint32_t c_symlink2(LinkedObjectFile& f, ASSERT((code_value & 0xffff) == 0 || (code_value & 0xffff) == 0xffff); ASSERT(kind == SymbolLinkKind::SYMBOL); - // ASSERT(false); // this case does not occur in V2/V4. It does in V3. - f.symbol_link_offset(seg_id, code_ptr_offset - initial_offset, name); + f.symbol_link_offset(seg_id, code_ptr_offset - initial_offset, name, + (code_value & 0xffff) == 0xffff); } } while (data.at(link_ptr_offset)); @@ -197,9 +197,11 @@ static uint32_t c_symlink3(LinkedObjectFile& f, f.symbol_link_word(seg, code_ptr - initial_offset, name, word_kind); } else { + u16 lower = code_value & 0xffff; + ASSERT(lower == 0 || lower == 0xffff); f.stats.v3_symbol_link_offset++; ASSERT(kind == SymbolLinkKind::SYMBOL); - f.symbol_link_offset(seg, code_ptr - initial_offset, name); + f.symbol_link_offset(seg, code_ptr - initial_offset, name, lower == 0xffff); } } while (data.at(link_ptr)); diff --git a/decompiler/ObjectFile/LinkedWord.h b/decompiler/ObjectFile/LinkedWord.h index 8e334b979d..4029e0c164 100644 --- a/decompiler/ObjectFile/LinkedWord.h +++ b/decompiler/ObjectFile/LinkedWord.h @@ -24,14 +24,15 @@ namespace decompiler { class LinkedWord { public: enum Kind : u8 { - PLAIN_DATA, // just plain data - PTR, // pointer to a location - HI_PTR, // lower 16-bits of this data are the upper 16 bits of a pointer - LO_PTR, // lower 16-bits of this data are the lower 16 bits of a pointer - SYM_PTR, // this is a pointer to a symbol - EMPTY_PTR, // this is a pointer to the empty list - SYM_OFFSET, // this is an offset of a symbol in the symbol table - TYPE_PTR // this is a pointer to a type + PLAIN_DATA, // just plain data + PTR, // pointer to a location + HI_PTR, // lower 16-bits of this data are the upper 16 bits of a pointer + LO_PTR, // lower 16-bits of this data are the lower 16 bits of a pointer + SYM_PTR, // this is a pointer to a symbol + EMPTY_PTR, // this is a pointer to the empty list + SYM_OFFSET, // this is an offset of a symbol in the symbol table + SYM_VAL_OFFSET, // offset to the value of the symbol (different in jak 2) + TYPE_PTR // this is a pointer to a type }; private: @@ -46,7 +47,8 @@ class LinkedWord { explicit LinkedWord(uint32_t _data) : data(_data) {} bool holds_string() const { - return m_kind == SYM_PTR || m_kind == SYM_OFFSET || m_kind == TYPE_PTR; + return m_kind == SYM_PTR || m_kind == SYM_OFFSET || m_kind == TYPE_PTR || + m_kind == SYM_VAL_OFFSET; } LinkedWord(const LinkedWord& other) { diff --git a/decompiler/analysis/analyze_inspect_method.cpp b/decompiler/analysis/analyze_inspect_method.cpp index d3cfc652c0..c6fa7d4c72 100644 --- a/decompiler/analysis/analyze_inspect_method.cpp +++ b/decompiler/analysis/analyze_inspect_method.cpp @@ -1496,13 +1496,10 @@ void inspect_top_level_for_metadata(Function& top_level, continue; } - lg::print("got 1\n"); - // lwu t9, 16(v1) ;; [ 21] (set! t9-0 (l.wu (+ v1-10 16))) // ;; [v1: ] -> [t9: (function symbol type int type) const auto& aop_1 = top_level.ir2.atomic_ops->ops.at(i + 1); if (!is_set_reg_to_load(aop_1.get(), Register(Reg::GPR, Reg::T9), 16)) { - lg::print("fail1\n"); continue; } @@ -1510,7 +1507,6 @@ void inspect_top_level_for_metadata(Function& top_level, const auto& aop_2 = top_level.ir2.atomic_ops->ops.at(i + 2); auto type_name = get_set_reg_to_symbol_ptr(aop_2.get(), Register(Reg::GPR, Reg::A0)); if (!type_name) { - lg::print("fail2\n"); continue; } @@ -1518,7 +1514,6 @@ void inspect_top_level_for_metadata(Function& top_level, const auto& aop_3 = top_level.ir2.atomic_ops->ops.at(i + 3); auto parent_name = get_set_reg_to_symbol_value(aop_3.get(), Register(Reg::GPR, Reg::A1)); if (!parent_name) { - lg::print("fail3\n"); continue; } @@ -1526,14 +1521,12 @@ void inspect_top_level_for_metadata(Function& top_level, const auto& aop_4 = top_level.ir2.atomic_ops->ops.at(i + 4); auto flags = get_set_reg_to_u64_load(aop_4.get(), Register(Reg::GPR, Reg::A2), file); if (!flags) { - lg::print("fail3\n"); continue; } // jalr ra, t9 ;; [ 25] (call! a0-0 a1-0 a2-0) const auto& aop_5 = top_level.ir2.atomic_ops->ops.at(i + 5); if (!dynamic_cast(aop_5.get())) { - lg::print("fial4\n"); continue; } diff --git a/decompiler/analysis/atomic_op_builder.cpp b/decompiler/analysis/atomic_op_builder.cpp index b55fea1639..e12d96bbd8 100644 --- a/decompiler/analysis/atomic_op_builder.cpp +++ b/decompiler/analysis/atomic_op_builder.cpp @@ -575,8 +575,14 @@ std::unique_ptr convert_lw_1(const Instruction& i0, int idx) { std::unique_ptr convert_daddiu_1(const Instruction& i0, int idx, GameVersion version) { if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_sym()) { // get symbol pointer - return std::make_unique( - make_dst_var(i0, idx), SimpleAtom::make_sym_ptr(i0.get_src(1).get_sym()).as_expr(), idx); + if (i0.get_src(1).kind == InstructionAtom::IMM_SYM_VAL_PTR) { + return std::make_unique( + make_dst_var(i0, idx), SimpleAtom::make_sym_val_ptr(i0.get_src(1).get_sym()).as_expr(), + idx); + } else { + return std::make_unique( + make_dst_var(i0, idx), SimpleAtom::make_sym_ptr(i0.get_src(1).get_sym()).as_expr(), idx); + } } else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_imm(empty_pair_offset_from_s7(version))) { // get empty pair diff --git a/decompiler/analysis/cfg_builder.cpp b/decompiler/analysis/cfg_builder.cpp index 28d729e5c1..a4b2c7b233 100644 --- a/decompiler/analysis/cfg_builder.cpp +++ b/decompiler/analysis/cfg_builder.cpp @@ -1249,69 +1249,57 @@ Form* try_sc_as_type_of_jak2(FormPool& pool, Function& f, const ShortCircuit* vt // get the branch ir's auto b0_ptr = cfg_to_ir(pool, f, b0_c); // should be begin. if (b0_ptr->size() <= 2) { - lg::print("fail1\n"); return nullptr; } auto b1_ptr = cfg_to_ir(pool, f, b1_c); if (b1_ptr->size() <= 1) { - lg::print("fail2\n"); return nullptr; } auto b2_ptr = cfg_to_ir(pool, f, b2_c); if (b2_ptr->size() <= 1) { - lg::print("fail3\n"); return nullptr; } auto b3_ptr = cfg_to_ir(pool, f, b3_c); auto b3_ir = dynamic_cast(b3_ptr->try_as_single_element()); if (!b3_ir) { - lg::print("fail4\n"); return nullptr; } // identify the left shift auto set_shift_left = dynamic_cast(b0_ptr->at(b0_ptr->size() - 3)); if (!set_shift_left) { - lg::print("fail5\n"); return nullptr; } auto temp_reg0 = set_shift_left->dst(); auto shift_left = dynamic_cast(set_shift_left->src()->try_as_single_element()); if (!shift_left || shift_left->expr().kind() != SimpleExpression::Kind::LEFT_SHIFT) { - lg::print("fail6\n"); return nullptr; } auto src_reg = shift_left->expr().get_arg(0).var(); auto sa_left = shift_left->expr().get_arg(1); if (!sa_left.is_int() || sa_left.get_int() != 61) { - lg::print("fail7\n"); return nullptr; } // identify the right shift auto set_shift_right = dynamic_cast(b0_ptr->at(b0_ptr->size() - 2)); if (!set_shift_right) { - lg::print("fail8\n"); return nullptr; } if (set_shift_right->dst().reg() != set_shift_left->dst().reg()) { - lg::print("fail9\n"); return nullptr; } auto shift_right = dynamic_cast(set_shift_right->src()->try_as_single_element()); if (!shift_right || shift_right->expr().kind() != SimpleExpression::Kind::RIGHT_SHIFT_LOGIC) { - lg::print("fail10\n"); return nullptr; } if (temp_reg0.reg() != shift_right->expr().get_arg(0).var().reg()) { - lg::print("fail11\n"); return nullptr; } auto sa_right = shift_right->expr().get_arg(1); if (!sa_right.is_int() || sa_right.get_int() != 61) { - lg::print("fail12\n"); return nullptr; } @@ -1321,7 +1309,6 @@ Form* try_sc_as_type_of_jak2(FormPool& pool, Function& f, const ShortCircuit* vt if (!first_branch || !is_set_symbol_value(b0_delay_op, "binteger") || first_branch->op()->condition().kind() != IR2_Condition::Kind::ZERO || !first_branch->op()->likely()) { - lg::print("fail13\n"); return nullptr; } auto temp_reg = first_branch->op()->condition().src(0).var(); @@ -1330,19 +1317,16 @@ Form* try_sc_as_type_of_jak2(FormPool& pool, Function& f, const ShortCircuit* vt // branch 1 if (b1_ptr->size() != 2) { - lg::print("fail14\n"); return nullptr; } auto second_branch_pre_op = dynamic_cast(b1_ptr->at(0)); if (!second_branch_pre_op) { - lg::print("fail15\n"); return nullptr; } { auto pos = second_branch_pre_op->src(); auto pos_as_se = pos->try_as_element(); if (!pos_as_se || !pos_as_se->expr().is_identity() || !pos_as_se->expr().get_arg(0).is_int(4)) { - lg::print("fail16\n"); return nullptr; } } @@ -1358,30 +1342,24 @@ Form* try_sc_as_type_of_jak2(FormPool& pool, Function& f, const ShortCircuit* vt second_branch->op()->condition().src(0).var().reg() != temp_reg0.reg() || !second_branch->op()->condition().src(1).is_var() || second_branch->op()->condition().src(1).var().reg() != temp_reg1.reg()) { - lg::print("fail17\n"); return nullptr; } if (!b1_d) { - lg::print("fail18\n"); return nullptr; } auto b1_delay_op = get_delay_load_op(f, b1_d); if (b1_delay_op.kind() != LoadVarOp::Kind::UNSIGNED || b1_delay_op.size() != 4) { - lg::print("fail19\n"); return nullptr; } IR2_RegOffset ro; if (!get_as_reg_offset(b1_delay_op.src(), &ro)) { - lg::print("fail20\n"); return nullptr; } if (ro.offset != -4) { - lg::print("fail21\n"); return nullptr; } if (ro.reg != src_reg.reg() || b1_delay_op.get_set_destination().reg() != dst_reg.reg()) { - lg::print("fail22\n"); return nullptr; } @@ -1395,19 +1373,16 @@ Form* try_sc_as_type_of_jak2(FormPool& pool, Function& f, const ShortCircuit* vt lw a0, pair(s7) */ if (b2_ptr->size() != 2) { - lg::print("fail23\n"); return nullptr; } auto third_branch_pre_op = dynamic_cast(b2_ptr->at(0)); if (!third_branch_pre_op) { - lg::print("fail24\n"); return nullptr; } { auto pos = third_branch_pre_op->src(); auto pos_as_se = pos->try_as_element(); if (!pos_as_se || !pos_as_se->expr().is_identity() || !pos_as_se->expr().get_arg(0).is_int(2)) { - lg::print("fail25\n"); return nullptr; } } @@ -1418,22 +1393,18 @@ Form* try_sc_as_type_of_jak2(FormPool& pool, Function& f, const ShortCircuit* vt third_branch->op()->condition().src(0).var().reg() != temp_reg0.reg() || !third_branch->op()->condition().src(1).is_var() || third_branch->op()->condition().src(1).var().reg() != temp_reg2.reg()) { - lg::print("fail26\n"); return nullptr; } if (!b2_d) { - lg::print("fail27\n"); return nullptr; } auto b2_delay_op = get_delay_op(f, b2_d); if (!is_set_symbol_value(b2_delay_op, "pair") || b2_delay_op.dst().reg() != dst_reg.reg()) { - lg::print("fail28\n"); return nullptr; } if (!is_set_symbol_value(b3_ir, "symbol")) { - lg::print("fail29\n"); return nullptr; } diff --git a/decompiler/config/jak2/all-types.gc b/decompiler/config/jak2/all-types.gc index 1d4dd68463..1de0672bf3 100644 --- a/decompiler/config/jak2/all-types.gc +++ b/decompiler/config/jak2/all-types.gc @@ -26034,7 +26034,7 @@ (define-extern load-game-text-info "Load text, if needed. txt-name is the group name, curr-text is the _symbol_ for the game-text-info, and heap is the heap to load to. The heap will be cleared." - (function string symbol kheap int)) + (function string (pointer object) kheap int)) (define-extern load-level-text-files "Load the text files needed for level idx. This function made more sense back when text files were split up, but in the end they put everything @@ -29997,7 +29997,7 @@ (define-extern *hud-sprite-work* hud-sprite-work) (define-extern hud-create-icon (function none)) (define-extern hud-hidden (state hud)) ;; (state hud) -(define-extern hud-arriving (state hud)) ;; +(define-extern hud-arriving (state hud)) ;; (define-extern hud-in (state hud)) ;; (state hud) (define-extern hud-leaving (state int hud)) ;; (state int hud) (define-extern hud-init-by-other (function object :behavior hud)) diff --git a/decompiler/types2/ForwardProp.cpp b/decompiler/types2/ForwardProp.cpp index b266127423..21dede9c20 100644 --- a/decompiler/types2/ForwardProp.cpp +++ b/decompiler/types2/ForwardProp.cpp @@ -189,6 +189,13 @@ TP_Type get_type_symbol_ptr(const std::string& name) { } } +TP_Type get_type_symbol_val_ptr(const std::string& name, + const DecompilerTypeSystem& dts, + const Env& env) { + return TP_Type::make_from_ts( + TypeSpec("pointer", {get_type_symbol_val(name, dts, env).typespec()})); +} + /*! * Try to figure out the type of an atom. */ @@ -610,6 +617,10 @@ void types2_for_atom(types2::Type& type_out, auto type = get_type_symbol_ptr(atom.get_str()); type_out.type = type; } break; + case SimpleAtom::Kind::SYMBOL_VAL_PTR: { + auto type = get_type_symbol_val_ptr(atom.get_str(), dts, env); + type_out.type = type; + } break; case SimpleAtom::Kind::INTEGER_CONSTANT: { // auto type = TP_Type::make_from_integer(atom.get_int()); // type_out.type = type; diff --git a/decompiler/util/sparticle_decompile.cpp b/decompiler/util/sparticle_decompile.cpp index 2bc9574e27..c98741a31c 100644 --- a/decompiler/util/sparticle_decompile.cpp +++ b/decompiler/util/sparticle_decompile.cpp @@ -799,6 +799,10 @@ std::string debug_print(const LinkedWord& word) { return fmt::format("offset '{}", word.symbol_name()); case LinkedWord::SYM_PTR: return fmt::format("ptr '{}", word.symbol_name()); + case LinkedWord::SYM_VAL_OFFSET: + return fmt::format("val-ptr '{}", word.symbol_name()); + default: + ASSERT(false); } } diff --git a/goal_src/jak2/engine/ui/text-h.gc b/goal_src/jak2/engine/ui/text-h.gc index 95fff7d8d5..a7acf915c7 100644 --- a/goal_src/jak2/engine/ui/text-h.gc +++ b/goal_src/jak2/engine/ui/text-h.gc @@ -11,7 +11,7 @@ (define-extern load-game-text-info "Load text, if needed. txt-name is the group name, curr-text is the _symbol_ for the game-text-info, and heap is the heap to load to. The heap will be cleared." - (function string symbol kheap int)) + (function string (pointer object) kheap int)) (define-extern print-game-text-scaled "Print text, with a given scaling" (function string float font-context int none)) (define-extern print-game-text "Print text." (function string font-context symbol int int float)) diff --git a/goal_src/jak2/engine/ui/text.gc b/goal_src/jak2/engine/ui/text.gc index 461ef5ffcf..c907f9e6e3 100644 --- a/goal_src/jak2/engine/ui/text.gc +++ b/goal_src/jak2/engine/ui/text.gc @@ -226,11 +226,11 @@ (define text-is-loading #f) ;; WARN: Found some very strange gotos. Check result carefully, this is not well tested. -(defun load-game-text-info ((arg0 string) (arg1 symbol) (arg2 kheap)) +(defun load-game-text-info ((arg0 string) (arg1 (pointer object)) (arg2 kheap)) "Load text, if needed. txt-name is the group name, curr-text is the _symbol_ for the game-text-info, and heap is the heap to load to. The heap will be cleared." (local-vars (v0-3 int) (sv-16 game-text-info) (sv-24 int) (sv-32 int) (sv-40 int)) - (set! sv-16 (the-as game-text-info (-> arg1 value))) + (set! sv-16 (the-as game-text-info (-> arg1 0))) (set! sv-24 (the-as int (-> *setting-control* user-current language))) (set! sv-32 0) (set! sv-40 (&- (-> arg2 top) (the-as uint (-> arg2 base)))) @@ -290,11 +290,11 @@ (flush-cache 0) (let ((s3-1 link)) (format (clear *temp-string*) "~D~S.TXT" sv-24 arg0) - (set! (-> arg1 value) (s3-1 s2-1 (-> *temp-string* data) sv-32 arg2 0)) + (set! (-> arg1 0) (s3-1 s2-1 (-> *temp-string* data) sv-32 arg2 0)) ) ) - (if (<= (the-as int (-> arg1 value)) 0) - (set! (-> arg1 value) (the-as object #f)) + (if (<= (the-as int (-> arg1 0)) 0) + (set! (-> arg1 0) (the-as object #f)) ) ) (set! v0-3 0) @@ -307,7 +307,7 @@ This function made more sense back when text files were split up, but in the end they put everything in a single text group and file." (if (or *level-text-file-load-flag* (>= arg0 0)) - (load-game-text-info "common" '*common-text* *common-text-heap*) + (load-game-text-info "common" (&-> '*common-text* value) *common-text-heap*) ) 0 (none) diff --git a/test/decompiler/reference/jak2/engine/ui/text_REF.gc b/test/decompiler/reference/jak2/engine/ui/text_REF.gc index c80c5d6a38..0ec66b81e1 100644 --- a/test/decompiler/reference/jak2/engine/ui/text_REF.gc +++ b/test/decompiler/reference/jak2/engine/ui/text_REF.gc @@ -1,28 +1,19 @@ -;;-*-Lisp-*- (in-package goal) -;; failed to figure out what this is: (kmemopen global "text") -;; definition for symbol *expand-buf-number*, type int (define *expand-buf-number* 0) -;; definition for symbol *game-text-word*, type string (define *game-text-word* (new 'global 'string 256 (the-as string #f))) -;; definition for symbol *game-text-line*, type string (define *game-text-line* (new 'global 'string 1024 (the-as string #f))) -;; definition for symbol *expanded-text-line0*, type string (define *expanded-text-line0* (new 'global 'string 1024 (the-as string #f))) -;; definition for symbol *expanded-text-line1*, type string (define *expanded-text-line1* (new 'global 'string 1024 (the-as string #f))) -;; definition for symbol *level-text-file-load-flag*, type symbol (define *level-text-file-load-flag* #t) -;; failed to figure out what this is: (when (zero? (-> *common-text-heap* base)) (let ((gp-0 *common-text-heap*)) (set! (-> gp-0 base) (kmalloc global #x10000 (kmalloc-flags) "heap")) @@ -32,10 +23,8 @@ ) ) -;; failed to figure out what this is: (kmemclose) -;; definition for method 7 of type game-text-info (defmethod relocate game-text-info ((obj game-text-info) (arg0 int)) (let ((v1-1 (-> *level* loading-level))) (when v1-1 @@ -46,19 +35,14 @@ obj ) -;; definition for method 4 of type game-text-info (defmethod length game-text-info ((obj game-text-info)) (-> obj length) ) -;; definition for method 5 of type game-text-info -;; WARN: Return type mismatch uint vs int. (defmethod asize-of game-text-info ((obj game-text-info)) (the-as int (+ (-> obj type size) (* (-> obj length) 8))) ) -;; definition for method 3 of type game-text-info -;; INFO: this function exists in multiple non-identical object files (defmethod inspect game-text-info ((obj game-text-info)) (format #t "[~8x] ~A~%" obj (-> obj type)) (format #t "~Tlength: ~D~%" (-> obj length)) @@ -71,7 +55,6 @@ obj ) -;; definition for method 8 of type game-text-info (defmethod mem-usage game-text-info ((obj game-text-info) (arg0 memory-usage-block) (arg1 int)) (set! (-> arg0 length) (max 84 (-> arg0 length))) (set! (-> arg0 data 83 name) "string") @@ -92,7 +75,6 @@ obj ) -;; definition for function convert-korean-text (defun convert-korean-text ((arg0 string)) "Converts the provided [[string]] of [[game-text]] into korean. Returns a [[string]]" (local-vars (v1-21 int)) @@ -181,7 +163,6 @@ ) ) -;; definition for method 9 of type game-text-info (defmethod lookup-text! game-text-info ((obj game-text-info) (arg0 game-text-id) (arg1 symbol)) (cond ((= obj #f) @@ -234,7 +215,6 @@ ) ) -;; definition for method 23 of type level (defmethod lookup-text level ((obj level) (arg0 game-text-id) (arg1 symbol)) (let ((v1-0 *common-text*)) (dotimes (a3-0 (-> obj loaded-text-info-count)) @@ -246,281 +226,95 @@ ) ) -;; definition for symbol text-is-loading, type symbol (define text-is-loading #f) -;; definition for function load-game-text-info -;; WARN: Found some very strange gotos. Check result carefully, this is not well tested. -;; ERROR: failed type prop at 3: Could not figure out load: (set! v1 (l.wu gp)) -;; WARN: Return type mismatch none vs int. -(defun load-game-text-info ((a0-0 string) (a1-0 symbol) (a2-0 kheap)) +(defun load-game-text-info ((arg0 string) (arg1 (pointer object)) (arg2 kheap)) "Load text, if needed. txt-name is the group name, curr-text is the _symbol_ for the game-text-info, and heap is the heap to load to. The heap will be cleared." - (local-vars - (v0-0 none) - (v0-1 none) - (v0-2 none) - (v0-3 none) - (v0-4 none) - (v0-5 none) - (v0-6 none) - (v0-7 none) - (v0-8 none) - (v0-9 none) - (v0-10 none) - (v0-11 none) - (v0-12 none) - (v0-13 none) - (v1-0 game-text-info) - (v1-1 none) - (v1-2 none) - (v1-3 none) - (v1-4 none) - (v1-5 none) - (v1-6 none) - (v1-7 none) - (v1-9 none) - (v1-10 none) - (v1-11 none) - (v1-12 none) - (v1-13 none) - (v1-14 none) - (v1-16 none) - (v1-17 none) - (v1-18 none) - (v1-20 none) - (v1-21 none) - (v1-24 none) - (v1-25 none) - (v1-26 none) - (a0-1 none) - (a0-2 none) - (a0-3 none) - (a0-4 none) - (a0-5 none) - (a0-6 none) - (a0-7 none) - (a0-8 none) - (a0-10 none) - (a0-11 none) - (a0-12 none) - (a0-13 none) - (a0-14 none) - (a0-15 none) - (a0-17 none) - (a0-18 none) - (a0-19 none) - (a0-20 none) - (a0-21 none) - (a0-22 none) - (a1-2 none) - (a1-3 none) - (a1-4 none) - (a1-5 none) - (a1-6 none) - (a1-7 none) - (a1-8 none) - (a1-9 none) - (a2-1 none) - (a2-2 none) - (a2-3 none) - (a2-4 none) - (a2-5 none) - (a2-6 none) - (a3-1 none) - (a3-2 none) - (t0-0 none) - (s1-0 none) - (s2-0 none) - (s2-1 none) - (s3-0 none) - (s3-1 none) - (t9-0 none) - (t9-1 none) - (t9-2 none) - (t9-3 none) - (t9-4 none) - (t9-5 none) - (t9-6 none) - (t9-7 none) - (t9-8 none) - (t9-9 none) - (t9-10 none) - (t9-11 none) - (t9-12 none) - (sv-16 none) - (sv-24 none) - (sv-32 none) - (sv-40 none) - ) - (when (begin - (when (begin - (and (begin - (set! v1-0 (the-as game-text-info (l.wu a1-0))) - (set! sv-16 v1-0) - (set! v1-1 (the-as none *setting-control*)) - (set! v1-2 (the-as none (l.d (+ v1-1 28)))) - (set! sv-24 v1-2) - (set! sv-32 0) - (set! v1-3 (the-as none (-> a2-0 top))) - (set! a0-1 (the-as none (-> a2-0 base))) - (set! v1-4 (the-as none (- v1-3 a0-1))) - (set! sv-40 v1-4) - (set! t9-0 (the-as none scf-get-territory)) - (set! v0-0 (the-as none (call!))) - (set! v1-5 (the-as none (+ v0-0 -1))) - (set! a0-2 (the-as none (zero? v1-5))) - a0-2 - ) - (begin (set! v1-7 sv-24) (zero? v1-7)) - (begin (set! t9-1 (the-as none demo?)) (set! v0-1 (the-as none (call!))) (set! v1-6 (the-as none (not v0-1)))) - ) - v1-6 - ) - (set! v1-9 (the-as none 7)) - (set! sv-24 v1-9) - ) - (or (begin (set! v1-10 sv-16) (set! a0-3 (the-as none (= v1-10 #f))) a0-3) - (begin - (set! v1-12 sv-16) - (set! v1-13 (the-as none (l.w (+ v1-12 4)))) - (set! a0-4 sv-24) - (set! a0-5 (the-as none (!= v1-13 a0-4))) - a0-5 - ) - (begin - (set! t9-2 (the-as none string=)) - (set! v1-14 sv-16) - (set! a0-6 (the-as none (l.wu (+ v1-14 8)))) - (set! a1-1 (the-as none a0-0)) - (set! v0-2 (the-as none (call!))) - (set! v1-11 (the-as none (not v0-2))) - ) - ) - v1-11 - ) - (cond - ((begin - (set! v1-16 (the-as none a2-0)) - (set! a0-7 (the-as none (l.wu v1-16))) - (s.w! (+ v1-16 8) a0-7) - ((b! #t L50 (nop!)) (nop!)) - (label cfg-16) - (set! v0-3 (the-as none 0)) - ((b! #t L56 (nop!)) (nop!)) - (label cfg-17) - (set! s3-0 (the-as none str-load)) - (set! s2-0 (the-as none format)) - (set! t9-3 (the-as none clear)) - (set! a0-8 (the-as none *temp-string*)) - (call!) - (set! a0-9 (the-as none v0-4)) - (set! a1-2 (the-as none L100)) - (set! a2-1 sv-24) - (set! a3-0 (the-as none a0-0)) - (set! t9-4 (the-as none s2-0)) - (call!) - (set! a0-10 (the-as none *temp-string*)) - (set! a1-3 (the-as none -1)) - (set! v1-17 (the-as none -64)) - (set! a2-2 (the-as none (l.wu (+ a2-0 8)))) - (set! a2-3 (the-as none (+ a2-2 63))) - (set! a2-4 (the-as none (logand v1-17 a2-3))) - (set! v1-18 (the-as none (l.wu (+ a2-0 4)))) - (set! a3-1 (the-as none (l.wu (+ a2-0 8)))) - (set! a3-2 (the-as none (- v1-18 a3-1))) - (set! t9-5 (the-as none s3-0)) - (set! v0-6 (the-as none (call!))) - ((b! (not v0-6) L49 (nop!)) (nop!)) - (label cfg-19) - (set! t9-6 (the-as none str-load-status)) - (set! a0-11 (& sv-32)) - (set! v0-7 (the-as none (call!))) - (set! v1-20 (the-as none v0-7)) - (set! a0-12 (the-as none 'error)) - ((b! (!= v1-20 a0-12) L52 (set! a0-13 #f)) (empty-form)) - (set! t9-7 (the-as none format)) - (set! a0-14 (the-as none 0)) - (set! a1-4 (the-as none L99)) - (call!) - (set! v0-3 (the-as none 0)) - ((b! #t L56 (nop!)) (nop!)) - (set! v1-21 (the-as none 0)) - ((b! #t L54 (nop!)) (nop!)) - (label cfg-22) - (set! a0-15 sv-32) - (set! a1-5 sv-40) - (set! a1-6 (the-as none (+ a1-5 -300))) - (>=.si a0-15 a1-6) - ) - (return (begin - (set! t9-8 (the-as none format)) - (set! a0-17 (the-as none 0)) - (set! a1-7 (the-as none L98)) - (call!) - (set! v0-3 (the-as none 0)) - ) - ) - ) - ((begin (set! a0-18 (the-as none 'busy)) (= v1-20 a0-18)) - (begin (nop!) (nop!) (nop!) (nop!) (nop!) (nop!) (goto cfg-19)) - ) + (local-vars (v0-3 int) (sv-16 game-text-info) (sv-24 int) (sv-32 int) (sv-40 int)) + (set! sv-16 (the-as game-text-info (-> arg1 0))) + (set! sv-24 (the-as int (-> *setting-control* user-current language))) + (set! sv-32 0) + (set! sv-40 (&- (-> arg2 top) (the-as uint (-> arg2 base)))) + (if (and (= (scf-get-territory) 1) (= sv-24 (language-enum english)) (not (demo?))) + (set! sv-24 7) ) - (if (begin - (label cfg-27) - (set! v1-24 (the-as none -64)) - (set! a0-19 (the-as none (l.wu (+ a2-0 8)))) - (set! a0-20 (the-as none (+ a0-19 63))) - (set! s2-1 (the-as none (logand v1-24 a0-20))) - (set! t9-9 (the-as none flush-cache)) - (set! a0-21 (the-as none 0)) - (call!) - (set! s3-1 (the-as none link)) - (set! s1-0 (the-as none format)) - (set! t9-10 (the-as none clear)) - (set! a0-22 (the-as none *temp-string*)) - (call!) - (set! a0-23 (the-as none v0-11)) - (set! a1-8 (the-as none L100)) - (set! a2-5 sv-24) - (set! t9-11 (the-as none s1-0)) - (set! a3-3 (the-as none a0-0)) - (call!) - (set! v1-25 (the-as none *temp-string*)) - (set! a1-9 (the-as none (+ v1-25 4))) - (set! a2-6 sv-32) - (set! t0-0 (the-as none 0)) - (set! t9-12 (the-as none s3-1)) - (set! a0-24 (the-as none s2-1)) - (set! a3-4 (the-as none a2-0)) - (set! v0-13 (the-as none (call!))) - (s.w! a1-0 v0-13) - (set! v1-26 (the-as none (l.wu a1-0))) - (<=0.si v1-26) - ) - (s.w! a1-0 #f) + (when (or (= sv-16 #f) (!= (-> sv-16 language-id) sv-24) (not (string= (-> sv-16 group-name) arg0))) + (let ((v1-16 arg2)) + (set! (-> v1-16 current) (-> v1-16 base)) + ) + (b! #t cfg-17 :delay (nop!)) + (label cfg-16) + (set! v0-3 0) + (b! #t cfg-30 :delay (nop!)) + (label cfg-17) + (let ((s3-0 str-load)) + (format (clear *temp-string*) "~D~S.TXT" sv-24 arg0) + (b! + (not (s3-0 + *temp-string* + -1 + (logand -64 (&+ (-> arg2 current) 63)) + (&- (-> arg2 top) (the-as uint (-> arg2 current))) + ) + ) + cfg-16 + :delay (nop!) + ) + ) + (label cfg-19) + (let ((v1-20 (str-load-status (the-as (pointer int32) (& sv-32))))) + (b! (!= v1-20 'error) cfg-22 :delay (empty-form)) + (format 0 "Error loading text~%") + (set! v0-3 0) + (b! #t cfg-30 :delay (nop!)) + (the-as none 0) + (b! #t cfg-27 :delay (nop!)) + (label cfg-22) + (cond + ((>= sv-32 (+ sv-40 -300)) + (format 0 "Game text heap overrun!~%") + (return 0) + ) + ((= v1-20 'busy) + (nop!) + (nop!) + (nop!) + (nop!) + (nop!) + (nop!) + (goto cfg-19) + ) + ) + ) + (label cfg-27) + (let ((s2-1 (logand -64 (&+ (-> arg2 current) 63)))) + (flush-cache 0) + (let ((s3-1 link)) + (format (clear *temp-string*) "~D~S.TXT" sv-24 arg0) + (set! (-> arg1 0) (s3-1 s2-1 (-> *temp-string* data) sv-32 arg2 0)) + ) + ) + (if (<= (the-as int (-> arg1 0)) 0) + (set! (-> arg1 0) (the-as object #f)) ) ) - (set! v0-3 (the-as none 0)) + (set! v0-3 0) (label cfg-30) - (ret-value v0-3) + v0-3 ) -;; definition for function load-level-text-files -;; WARN: Return type mismatch int vs none. (defun load-level-text-files ((arg0 int)) "Load the text files needed for level idx. This function made more sense back when text files were split up, but in the end they put everything in a single text group and file." (if (or *level-text-file-load-flag* (>= arg0 0)) - (load-game-text-info "common" '*common-text* *common-text-heap*) + (load-game-text-info "common" (&-> '*common-text* value) *common-text-heap*) ) 0 (none) ) -;; definition for function draw-debug-text-box -;; WARN: Return type mismatch int vs none. (defun draw-debug-text-box ((arg0 font-context)) "Draws some lines" (when *cheat-mode* @@ -566,8 +360,6 @@ (none) ) -;; definition for function print-game-text-scaled -;; WARN: Return type mismatch int vs none. (defun print-game-text-scaled ((arg0 string) (arg1 float) (arg2 font-context) (arg3 int)) "Print text, with a given scaling" (let ((f26-0 (-> arg2 width)) @@ -602,69 +394,6 @@ (none) ) -;; definition for function print-game-text -;; WARN: Stack slot offset 48 signed mismatch -;; WARN: Stack slot offset 16 signed mismatch -;; WARN: Stack slot offset 20 signed mismatch -;; WARN: Stack slot offset 56 signed mismatch -;; WARN: Stack slot offset 52 signed mismatch -;; WARN: Stack slot offset 44 signed mismatch -;; WARN: Stack slot offset 36 signed mismatch -;; WARN: Stack slot offset 56 signed mismatch -;; WARN: Stack slot offset 40 signed mismatch -;; WARN: Stack slot offset 36 signed mismatch -;; WARN: Stack slot offset 48 signed mismatch -;; WARN: Stack slot offset 16 signed mismatch -;; WARN: Stack slot offset 20 signed mismatch -;; WARN: Stack slot offset 56 signed mismatch -;; WARN: Stack slot offset 52 signed mismatch -;; WARN: Stack slot offset 44 signed mismatch -;; WARN: Stack slot offset 36 signed mismatch -;; WARN: Stack slot offset 56 signed mismatch -;; WARN: Stack slot offset 40 signed mismatch -;; WARN: Stack slot offset 36 signed mismatch -;; WARN: Stack slot offset 48 signed mismatch -;; WARN: Stack slot offset 16 signed mismatch -;; WARN: Stack slot offset 20 signed mismatch -;; WARN: Stack slot offset 56 signed mismatch -;; WARN: Stack slot offset 52 signed mismatch -;; WARN: Stack slot offset 44 signed mismatch -;; WARN: Stack slot offset 36 signed mismatch -;; WARN: Stack slot offset 48 signed mismatch -;; WARN: Stack slot offset 16 signed mismatch -;; WARN: Stack slot offset 20 signed mismatch -;; WARN: Stack slot offset 56 signed mismatch -;; WARN: Stack slot offset 48 signed mismatch -;; WARN: Stack slot offset 16 signed mismatch -;; WARN: Stack slot offset 20 signed mismatch -;; WARN: Stack slot offset 56 signed mismatch -;; WARN: Stack slot offset 52 signed mismatch -;; WARN: Stack slot offset 44 signed mismatch -;; WARN: Stack slot offset 36 signed mismatch -;; WARN: Stack slot offset 56 signed mismatch -;; WARN: Stack slot offset 40 signed mismatch -;; WARN: Stack slot offset 36 signed mismatch -;; WARN: Stack slot offset 48 signed mismatch -;; WARN: Stack slot offset 16 signed mismatch -;; WARN: Stack slot offset 20 signed mismatch -;; WARN: Stack slot offset 56 signed mismatch -;; WARN: Stack slot offset 52 signed mismatch -;; WARN: Stack slot offset 44 signed mismatch -;; WARN: Stack slot offset 36 signed mismatch -;; WARN: Stack slot offset 56 signed mismatch -;; WARN: Stack slot offset 40 signed mismatch -;; WARN: Stack slot offset 36 signed mismatch -;; WARN: Stack slot offset 48 signed mismatch -;; WARN: Stack slot offset 16 signed mismatch -;; WARN: Stack slot offset 20 signed mismatch -;; WARN: Stack slot offset 56 signed mismatch -;; WARN: Stack slot offset 52 signed mismatch -;; WARN: Stack slot offset 44 signed mismatch -;; WARN: Stack slot offset 36 signed mismatch -;; WARN: Stack slot offset 48 signed mismatch -;; WARN: Stack slot offset 16 signed mismatch -;; WARN: Stack slot offset 20 signed mismatch -;; WARN: Stack slot offset 56 signed mismatch (defun print-game-text ((arg0 string) (arg1 font-context) (arg2 symbol) (arg3 int) (arg4 int)) "Print text." (local-vars @@ -859,8 +588,6 @@ ) ) -;; definition for function disable-level-text-file-loading -;; WARN: Return type mismatch int vs none. (defun disable-level-text-file-loading () "Disables [[*level-text-file-load-flag*]]" (set! *level-text-file-load-flag* #f) @@ -868,8 +595,6 @@ (none) ) -;; definition for function enable-level-text-file-loading -;; WARN: Return type mismatch int vs none. (defun enable-level-text-file-loading () "Disables [[*level-text-file-load-flag*]]" (set! *level-text-file-load-flag* #t) diff --git a/test/offline/offline_test_main.cpp b/test/offline/offline_test_main.cpp index 32ad9c2ec7..5b8aa5a7ac 100644 --- a/test/offline/offline_test_main.cpp +++ b/test/offline/offline_test_main.cpp @@ -174,12 +174,26 @@ int line_count(const std::string& str) { } struct CompareResult { - std::vector failing_files; + struct Fail { + std::string filename; + std::string diff; + }; + std::vector failing_files; int total_files = 0; int ok_files = 0; int total_lines = 0; - bool total_pass = true; + + void add(const CompareResult& other) { + failing_files.insert(failing_files.end(), other.failing_files.begin(), + other.failing_files.end()); + total_files += other.total_files; + ok_files += other.ok_files; + total_lines += other.total_lines; + if (!other.total_pass) { + total_pass = false; + } + } }; CompareResult compare(Decompiler& dc, const std::vector& refs, bool dump_mode) { @@ -192,11 +206,8 @@ CompareResult compare(Decompiler& dc, const std::vector& refs, b compare_result.total_files++; compare_result.total_lines += line_count(result); if (result != ref) { - compare_result.failing_files.push_back(file.unique_name); + compare_result.failing_files.push_back({file.unique_name, diff_strings(ref, result)}); compare_result.total_pass = false; - fmt::print("Reference test failure on {}:\n", file.unique_name); - fmt::print("{}\n", diff_strings(ref, result)); - if (dump_mode) { auto failure_dir = file_util::get_jak_project_dir() / "failures"; file_util::create_dir_if_needed(failure_dir); @@ -211,18 +222,35 @@ CompareResult compare(Decompiler& dc, const std::vector& refs, b return compare_result; } -bool compile(Decompiler& dc, - const std::vector& refs, - const OfflineTestConfig& config, - const std::string& game_name) { - fmt::print("Setting up compiler...\n"); +struct CompileResult { + bool ok = true; + struct Fail { + std::string filename; + std::string error; + }; + std::vector failing_files; + int num_lines = 0; + void add(const CompileResult& other) { + failing_files.insert(failing_files.end(), other.failing_files.begin(), + other.failing_files.end()); + num_lines += other.num_lines; + if (!other.ok) { + ok = false; + } + } +}; + +CompileResult compile(Decompiler& dc, + const std::vector& refs, + const OfflineTestConfig& config, + const std::string& game_name) { + CompileResult result; Compiler compiler(game_name_to_version(game_name)); compiler.run_front_end_on_file({"decompiler", "config", game_name_to_all_types[game_name]}); compiler.run_front_end_on_file( {"test", "decompiler", "reference", game_name, "decompiler-macros.gc"}); - Timer timer; int total_lines = 0; for (const auto& file : refs) { if (config.skip_compile_files.count(file.name_in_dgo)) { @@ -239,15 +267,13 @@ bool compile(Decompiler& dc, total_lines += line_count(src); compiler.run_full_compiler_on_string_no_save(src, file.name_in_dgo); } catch (const std::exception& e) { - fmt::print("Compiler exception: {}\n", e.what()); - return false; + result.ok = false; + result.failing_files.push_back({file.name_in_dgo, e.what()}); } } - auto time = timer.getSeconds(); - fmt::print("Total Lines Compiled: {}. Lines/second: {:.1f}\n", total_lines, - (float)total_lines / time); - return true; + result.num_lines = total_lines; + return result; } std::vector find_art_files(const std::string& game_name, @@ -411,10 +437,26 @@ std::optional parse_config(const std::string_view& game_name) /// @brief A simple struct to contain the reason for failure from a thread struct OfflineTestResult { - int exit_code; + int exit_code = 0; std::string reason; - OfflineTestResult(int _exit_code, std::string _reason) : exit_code(_exit_code), reason(_reason) {} + float time_spent_compiling = 0; + float time_spent_decompiling = 0; + float total_time = 0; + + CompareResult compare; + CompileResult compile; + + void add(const OfflineTestResult& other) { + if (other.exit_code) { + exit_code = other.exit_code; + } + time_spent_compiling += other.time_spent_compiling; + time_spent_decompiling += other.time_spent_decompiling; + total_time += other.total_time; + compare.add(other.compare); + compile.add(other.compile); + } }; int main(int argc, char* argv[]) { @@ -477,7 +519,7 @@ int main(int argc, char* argv[]) { } // First, prepare our batches of files to be processed std::vector> work_groups = {}; - for (int i = 0; i < num_threads; i++) { + for (size_t i = 0; i < num_threads; i++) { work_groups.push_back({}); } int total_added = 0; @@ -493,75 +535,67 @@ int main(int argc, char* argv[]) { decompiler::init_opcode_info(); for (const auto& work_group : work_groups) { threads.push_back(std::async(std::launch::async, [&]() { - std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); - lg::info("Setting up decompiler and loading files..."); + OfflineTestResult result; + Timer total_timer; + + Timer decompiler_timer; auto decompiler = setup_decompiler(work_group, art_files, fs::path(iso_data_path), config.value(), game_name); - lg::info("Took {}ms", std::chrono::duration_cast( - std::chrono::steady_clock::now() - begin) - .count()); - - begin = std::chrono::steady_clock::now(); - lg::info("Disassembling files..."); disassemble(decompiler); - lg::info("Took {}ms", std::chrono::duration_cast( - std::chrono::steady_clock::now() - begin) - .count()); - - begin = std::chrono::steady_clock::now(); - lg::info("Decompiling..."); decompile(decompiler, config.value()); // It's about 100ms per file to decompile on average // meaning that when we have all 900 files, a full offline test will take 1.5 minutes - lg::info("Took {}ms", std::chrono::duration_cast( - std::chrono::steady_clock::now() - begin) - .count()); + result.time_spent_decompiling = decompiler_timer.getSeconds(); - begin = std::chrono::steady_clock::now(); - lg::info("Comparing..."); - auto compare_result = compare(decompiler, work_group, dump_current_output); - lg::info("Took {}ms", std::chrono::duration_cast( - std::chrono::steady_clock::now() - begin) - .count()); - - lg::info("Compared {} lines. {}/{} files passed.", compare_result.total_lines, - compare_result.ok_files, compare_result.total_files); - lg::info("Dump? {}\n", dump_current_output); - - if (!compare_result.failing_files.empty()) { - lg::error("Failing files:"); - for (auto& f : compare_result.failing_files) { - lg::error("- {}", f); - } - lg::error("Comparison failed."); - // No point continuing to compile if the comparison has failed - return OfflineTestResult(1, "Comparison Failed"); + result.compare = compare(decompiler, work_group, dump_current_output); + if (!result.compare.total_pass) { + result.exit_code = 1; } - begin = std::chrono::steady_clock::now(); - lg::info("Compiling..."); - bool compile_result = compile(decompiler, work_group, config.value(), game_name); - // Compiling on the otherhand, is around 20ms per file - lg::info("Took {}ms", std::chrono::duration_cast( - std::chrono::steady_clock::now() - begin) - .count()); - - if (!compile_result) { - return OfflineTestResult(1, "Compilation Failed"); + Timer compile_timer; + result.compile = compile(decompiler, work_group, config.value(), game_name); + result.time_spent_compiling = compile_timer.getSeconds(); + if (!result.compile.ok) { + result.exit_code = 1; } + result.total_time = total_timer.getSeconds(); - return OfflineTestResult(0, ""); + return result; })); } - // Fail fast over any thread tripping over + // summarize results: + OfflineTestResult total; for (auto& thread : threads) { auto ret = thread.get(); - if (ret.exit_code != 0) { - lg::error(ret.reason); - return ret.exit_code; + total.add(ret); + } + + if (!total.compare.total_pass) { + lg::error("Comparison failed."); + for (auto& f : total.compare.failing_files) { + fmt::print("{}\n", f.diff); + } + lg::error("Failing files:"); + for (auto& f : total.compare.failing_files) { + lg::error("- {}", f.filename); } } - return 0; + if (!total.compile.ok) { + for (auto& f : total.compile.failing_files) { + lg::error("{}", f.filename); + fmt::print("{}\n", f.error); + } + } + + fmt::print("Compiled {} lines in {:.3f}s ({} lines/sec)\n", total.compile.num_lines, + total.time_spent_compiling, + (int)(total.compile.num_lines / total.time_spent_compiling)); + + if (!total.exit_code) { + fmt::print("pass!\n"); + } + + return total.exit_code; }