diff --git a/decompiler/Disasm/OpcodeInfo.cpp b/decompiler/Disasm/OpcodeInfo.cpp index 35a03e24f3..8646337513 100644 --- a/decompiler/Disasm/OpcodeInfo.cpp +++ b/decompiler/Disasm/OpcodeInfo.cpp @@ -359,6 +359,7 @@ void init_opcode_info() { .src_gpr(FT::RS); // Load Quadword to COP2 // COP2 + // NOTE: if adding more here, update AtomicOp.cpp AsmOp::update_register_info() cd_dvft_svfs(def(IK::VMOVE, "vmove")); // Transfer between Floating-Point Registers cd_dvft_svfs(def(IK::VFTOI0, "vftoi0")); // Conversion to Fixed Point cd_dvft_svfs(def(IK::VFTOI4, "vftoi4")); // Conversion to Fixed Point diff --git a/decompiler/Disasm/OpcodeInfo.h b/decompiler/Disasm/OpcodeInfo.h index b369fb830b..d941951506 100644 --- a/decompiler/Disasm/OpcodeInfo.h +++ b/decompiler/Disasm/OpcodeInfo.h @@ -208,7 +208,7 @@ enum class InstructionKind { RSQRTS, // COP2 - VMOVE, + VMOVE, // first cop2 macro instruction VFTOI0, VFTOI4, VFTOI12, @@ -231,6 +231,7 @@ enum class InstructionKind { VMUL_BC, VMULA_BC, VMADD_BC, + VADDA_BC, VMADDA_BC, VMSUBA_BC, @@ -267,11 +268,14 @@ enum class InstructionKind { VRNEXT, VNOP, VWAITQ, - VCALLMS, + VCALLMS, // last cop2 macro instruction EE_OP_MAX }; +constexpr InstructionKind FIRST_COP2_MACRO = InstructionKind::VMOVE; +constexpr InstructionKind LAST_COP2_MACRO = InstructionKind::VCALLMS; + enum class FieldType { RS, RT, diff --git a/decompiler/Disasm/Register.cpp b/decompiler/Disasm/Register.cpp index 05b9c8631b..5277648bbd 100644 --- a/decompiler/Disasm/Register.cpp +++ b/decompiler/Disasm/Register.cpp @@ -53,11 +53,13 @@ const static char* vf_names[32] = {"vf0", "vf1", "vf2", "vf3", "vf4", "vf5" const static char* vi_names[32] = { "vi0", "vi1", "vi2", "vi3", "vi4", "vi5", "vi6", "vi7", "vi8", "vi9", "vi10", "vi11", "vi12", "vi13", "vi14", "vi15", - "Status", "MAC", "Clipping", "INVALID3", "R", "I", "Q", "INVALID7", + "Status", "MAC", "Clipping", "INVALID3", "vi_R", "vi_I", "vi_Q", "INVALID7", "INVALID8", "INVALID9", "TPC", "CMSAR0", "FBRST", "VPU-STAT", "INVALID14", "CMSAR1"}; const static char* pcr_names[2] = {"pcr0", "pcr1"}; +const static char* cop2_macro_special[2] = {"Q", "ACC"}; + ///////////////////////////// // Register Names Conversion ///////////////////////////// @@ -92,6 +94,11 @@ const char* pcr_to_charp(uint32_t pcr) { assert(pcr < 2); return pcr_names[pcr]; } + +const char* cop2_macro_special_to_charp(uint32_t reg) { + assert(reg < 2); + return cop2_macro_special[reg]; +} } // namespace ///////////////////////////// @@ -120,6 +127,7 @@ Register::Register(Reg::RegisterKind kind, uint32_t num) { assert(num < 32); break; case Reg::PCR: + case Reg::COP2_MACRO_SPECIAL: assert(num < 2); break; default: @@ -164,6 +172,8 @@ const char* Register::to_charp() const { return cop0_to_charp(get_cop0()); case Reg::PCR: return pcr_to_charp(get_pcr()); + case Reg::COP2_MACRO_SPECIAL: + return cop2_macro_special_to_charp(get_cop2_macro_special()); default: throw std::runtime_error("Unsupported Register"); } @@ -245,6 +255,13 @@ uint32_t Register::get_pcr() const { return kind; } +Reg::Cop2MacroSpecial Register::get_cop2_macro_special() const { + assert(get_kind() == Reg::COP2_MACRO_SPECIAL); + uint16_t k = id & 0xff; + assert(k < 2); + return (Reg::Cop2MacroSpecial)k; +} + bool Register::operator==(const Register& other) const { return id == other.id; } diff --git a/decompiler/Disasm/Register.h b/decompiler/Disasm/Register.h index 5cd544666a..88c7e39a14 100644 --- a/decompiler/Disasm/Register.h +++ b/decompiler/Disasm/Register.h @@ -5,9 +5,6 @@ * Representation of an EE register. */ -#ifndef NEXT_REGISTER_H -#define NEXT_REGISTER_H - #include #include @@ -20,9 +17,10 @@ enum RegisterKind { VF = 2, // VU0 Floating point vector registers from EE, just called vf0 - vf31 VI = 3, // VU0 Integer registers from EE, the first 16 are vi00 - vi15, the rest are control regs. - COP0 = 4, // EE COP0 Control Registers: full of fancy names (there are 32 of them) - PCR = 5, // Performance Counter registers (PCR0, PCR1) - MAX_KIND = 6 + COP0 = 4, // EE COP0 Control Registers: full of fancy names (there are 32 of them) + PCR = 5, // Performance Counter registers (PCR0, PCR1) + COP2_MACRO_SPECIAL = 6, // COP2 Q, ACC accessed from macro mode instructions. + MAX_KIND = 7 }; // nicknames for GPRs @@ -122,6 +120,11 @@ enum Vi { MAX_COP2 = 32 }; +enum Cop2MacroSpecial { + MACRO_Q = 0, + MACRO_ACC = 1, +}; + const extern bool allowed_local_gprs[Reg::MAX_GPR]; } // namespace Reg @@ -141,6 +144,7 @@ class Register { uint32_t get_vi() const; Reg::Cop0 get_cop0() const; uint32_t get_pcr() const; + Reg::Cop2MacroSpecial get_cop2_macro_special() const; bool operator==(const Register& other) const; bool operator!=(const Register& other) const; @@ -154,4 +158,3 @@ class Register { uint16_t id = -1; }; } // namespace decompiler -#endif // NEXT_REGISTER_H diff --git a/decompiler/Function/CfgVtx.cpp b/decompiler/Function/CfgVtx.cpp index 79d096bd02..678a26b0d5 100644 --- a/decompiler/Function/CfgVtx.cpp +++ b/decompiler/Function/CfgVtx.cpp @@ -1892,7 +1892,7 @@ std::shared_ptr build_cfg(const LinkedObjectFile& file, int se } if (!cfg->is_fully_resolved()) { - func.warnings += ";; Failed to fully resolve CFG\n"; + func.warnings.cfg_build_warning("Could not fully resolve CFG"); } return cfg; diff --git a/decompiler/Function/Function.cpp b/decompiler/Function/Function.cpp index bbc6a300a5..111c0012d7 100644 --- a/decompiler/Function/Function.cpp +++ b/decompiler/Function/Function.cpp @@ -75,9 +75,11 @@ void Function::analyze_prologue(const LinkedObjectFile& file) { auto& instr = instructions.at(idx); // storing stack pointer on the stack is done by some ASM kernel functions if (instr.kind == InstructionKind::SW && instr.get_src(0).get_reg() == make_gpr(Reg::SP)) { - printf("[Warning] %s Suspected ASM function based on this instruction in prologue: %s\n", - guessed_name.to_string().c_str(), instr.to_string(file.labels).c_str()); - warnings += ";; Flagged as ASM function because of " + instr.to_string(file.labels) + "\n"; + lg::warn( + "Function {} was flagged as asm due to this instruction: {}. Consider flagging as asm " + "in config!", + guessed_name.to_string(), instr.to_string(file.labels)); + warnings.general_warning("Flagged as asm because of {}", instr.to_string(file.labels)); suspected_asm = true; return; } @@ -98,9 +100,11 @@ void Function::analyze_prologue(const LinkedObjectFile& file) { // storing s7 on the stack is done by interrupt handlers, which we probably don't want to // support if (instr.kind == InstructionKind::SD && instr.get_src(0).get_reg() == make_gpr(Reg::S7)) { - lg::warn("{} Suspected ASM function based on this instruction in prologue: {}\n", - guessed_name.to_string(), instr.to_string(file.labels)); - warnings += ";; Flagged as ASM function because of " + instr.to_string(file.labels) + "\n"; + lg::warn( + "Function {} was flagged as asm due to this instruction: {}. Consider flagging as asm " + "in config!", + guessed_name.to_string(), instr.to_string(file.labels)); + warnings.general_warning("Flagged as asm because of {}", instr.to_string(file.labels)); suspected_asm = true; return; } @@ -136,11 +140,9 @@ void Function::analyze_prologue(const LinkedObjectFile& file) { // sometimes stack memory is zeroed immediately after gpr backups, and this fools the previous // check. if (store_reg == make_gpr(Reg::R0)) { - printf( - "[Warning] %s Stack Zeroing Detected in Function::analyze_prologue, prologue may be " - "wrong\n", - guessed_name.to_string().c_str()); - warnings += ";; Stack Zeroing Detected, prologue may be wrong\n"; + lg::warn("Function {} has stack zeroing, manually check prologue!", + guessed_name.to_string()); + warnings.general_warning("Stack zeroing, check prologue!"); expect_nothing_after_gprs = true; break; } @@ -149,10 +151,8 @@ void Function::analyze_prologue(const LinkedObjectFile& file) { // avoid false positives here! if (store_reg == make_gpr(Reg::A0)) { suspected_asm = true; - printf( - "[Warning] %s Suspected ASM function because register $a0 was stored on the stack!\n", - guessed_name.to_string().c_str()); - warnings += ";; a0 on stack detected, flagging as asm\n"; + lg::warn("Function {} stores a0 on the stack, flagging as asm!", guessed_name.to_string()); + warnings.general_warning("Flagged as asm due to storing a0 on stack"); return; } @@ -168,11 +168,10 @@ void Function::analyze_prologue(const LinkedObjectFile& file) { assert(this_offset == prologue.gpr_backup_offset + 16 * i); if (this_reg != get_expected_gpr_backup(i, n_gpr_backups)) { suspected_asm = true; - printf("[Warning] %s Suspected asm function that isn't flagged due to stack store %s\n", - guessed_name.to_string().c_str(), - instructions.at(idx + i).to_string(file.labels).c_str()); - warnings += ";; Suspected asm function due to stack store: " + - instructions.at(idx + i).to_string(file.labels) + "\n"; + lg::warn("Function {} stores on the stack in a strange way ({}), flagging as asm!", + instructions.at(idx + i).to_string(file.labels), guessed_name.to_string()); + warnings.general_warning("Flagged as asm due to strange stack store: {}", + instructions.at(idx + i).to_string(file.labels)); return; } } @@ -198,11 +197,10 @@ void Function::analyze_prologue(const LinkedObjectFile& file) { assert(this_offset == prologue.fpr_backup_offset + 4 * i); if (this_reg != get_expected_fpr_backup(i, n_fpr_backups)) { suspected_asm = true; - printf("[Warning] %s Suspected asm function that isn't flagged due to stack store %s\n", - guessed_name.to_string().c_str(), - instructions.at(idx + i).to_string(file.labels).c_str()); - warnings += ";; Suspected asm function due to stack store: " + - instructions.at(idx + i).to_string(file.labels) + "\n"; + lg::warn("Function {} stores on the stack in a strange way ({}), flagging as asm!", + instructions.at(idx + i).to_string(file.labels), guessed_name.to_string()); + warnings.general_warning("Flagged as asm due to strange stack store: {}", + instructions.at(idx + i).to_string(file.labels)); return; } } @@ -358,11 +356,9 @@ void Function::check_epilogue(const LinkedObjectFile& file) { idx--; assert(is_jr_ra(instructions.at(idx))); idx--; - printf( - "[Warning] %s Double Return Epilogue Hack! This is probably an ASM function in " - "disguise\n", - guessed_name.to_string().c_str()); - warnings += ";; Double Return Epilogue - this is probably an ASM function\n"; + lg::warn("Function {} has a double return and is being flagged as asm.", + guessed_name.to_string()); + warnings.general_warning("Flagged as asm due to double return"); } // delay slot should be daddiu sp, sp, offset assert(is_gpr_2_imm_int(instructions.at(idx), InstructionKind::DADDIU, make_gpr(Reg::SP), diff --git a/decompiler/Function/Function.h b/decompiler/Function/Function.h index 39d3154c71..65bee8cd90 100644 --- a/decompiler/Function/Function.h +++ b/decompiler/Function/Function.h @@ -15,6 +15,7 @@ #include "CfgVtx.h" #include "common/type_system/TypeSpec.h" #include "decompiler/config.h" +#include "Warnings.h" namespace decompiler { class DecompilerTypeSystem; @@ -120,7 +121,8 @@ class Function { int epilogue_start = -1; int epilogue_end = -1; - std::string warnings; + DecompWarnings warnings; + bool contains_asm_ops = false; bool attempted_type_analysis = false; diff --git a/decompiler/Function/TypeAnalysis.cpp b/decompiler/Function/TypeAnalysis.cpp index a135311d9f..685bd2ce24 100644 --- a/decompiler/Function/TypeAnalysis.cpp +++ b/decompiler/Function/TypeAnalysis.cpp @@ -116,8 +116,8 @@ bool Function::run_type_analysis_ir2(const TypeSpec& my_type, try { op_types.at(op_id) = op->propagate_types(*init_types, ir2.env, dts); } catch (std::runtime_error& e) { - fmt::print("Type prop fail on {}: {}\n", guessed_name.to_string(), e.what()); - warnings += ";; Type prop attempted and failed.\n"; + lg::warn("Function {} failed type prop: {}", guessed_name.to_string(), e.what()); + warnings.type_prop_warning("{}", e.what()); ir2.env.set_types(block_init_types, op_types, *ir2.atomic_ops); return false; } @@ -147,8 +147,7 @@ bool Function::run_type_analysis_ir2(const TypeSpec& my_type, auto last_type = op_types.back().get(Register(Reg::GPR, Reg::V0)).typespec(); if (last_type != my_type.last_arg()) { - warnings += fmt::format(";; return type mismatch {} vs {}.\n", last_type.print(), - my_type.last_arg().print()); + warnings.info("Return type mismatch {} vs {}.", last_type.print(), my_type.last_arg().print()); } ir2.env.set_types(block_init_types, op_types, *ir2.atomic_ops); diff --git a/decompiler/Function/Warnings.h b/decompiler/Function/Warnings.h new file mode 100644 index 0000000000..b4eccf57ab --- /dev/null +++ b/decompiler/Function/Warnings.h @@ -0,0 +1,97 @@ +#pragma once +#include +#include +#include + +#include "third-party/fmt/core.h" + +class DecompWarnings { + public: + DecompWarnings() = default; + + template + void general_warning(const std::string& str, Args&&... args) { + warning(Warning::Kind::GENERAL, str, std::forward(args)...); + } + + template + void expression_build_warning(const std::string& str, Args&&... args) { + warning(Warning::Kind::EXPR_BUILD_FAILED, str, std::forward(args)...); + } + + template + void cfg_build_warning(const std::string& str, Args&&... args) { + warning(Warning::Kind::CFG_FAILED, str, std::forward(args)...); + } + + template + void type_prop_warning(const std::string& str, Args&&... args) { + warning(Warning::Kind::TYPE_PROP_FAILED, str, std::forward(args)...); + } + + template + void bad_vf_dependency(const std::string& str, Args&&... args) { + warning(Warning::Kind::BAD_VF_DEPENDENCY, str, std::forward(args)...); + } + + template + void info(const std::string& str, Args&&... args) { + warning(Warning::Kind::INFO, str, std::forward(args)...); + } + + bool has_warnings() const { return !m_warnings.empty(); } + + std::string get_warning_text(bool as_comment) const { + std::string result; + for (auto& w : m_warnings) { + if (as_comment) { + result += ";; "; + } + result += w.print(); + } + return result; + } + + private: + struct Warning { + enum class Kind { + GENERAL, + EXPR_BUILD_FAILED, + CFG_FAILED, + TYPE_PROP_FAILED, + INFO, + BAD_VF_DEPENDENCY + }; + Warning(Kind kind, std::string text) : warning_kind(kind), message(std::move(text)) {} + + std::string print() const { + switch (warning_kind) { + case Kind::GENERAL: + return fmt::format("WARN: {}\n", message); + case Kind::EXPR_BUILD_FAILED: + return fmt::format("WARN: Expression building failed: {}\n", message); + case Kind::CFG_FAILED: + return fmt::format("WARN: CFG building failed: {}\n", message); + case Kind::TYPE_PROP_FAILED: + return fmt::format("WARN: Type Propagation failed: {}\n", message); + case Kind::BAD_VF_DEPENDENCY: + return fmt::format("WARN: Bad vector register dependency: {}\n", message); + case Kind::INFO: + return fmt::format("INFO: {}\n", message); + default: + assert(false); + } + } + + Kind warning_kind; + std::string message; + }; + + template + void warning(Warning::Kind kind, const std::string& str, Args&&... args) { + Warning warn(kind, fmt::format(str, std::forward(args)...)); + m_warnings.push_back(warn); + } + + std::vector m_warnings; +}; \ No newline at end of file diff --git a/decompiler/IR/BasicOpBuilder.cpp b/decompiler/IR/BasicOpBuilder.cpp index 6027a434c9..36f04fdef6 100644 --- a/decompiler/IR/BasicOpBuilder.cpp +++ b/decompiler/IR/BasicOpBuilder.cpp @@ -2522,11 +2522,11 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec // everything failed if (!result) { // temp hack for debug: - printf("Instruction -> BasicOp failed on %s\n", i.to_string(file->labels).c_str()); + lg::error("Instruction -> BasicOp failed on {}", i.to_string(file->labels)); func->add_basic_op(std::make_shared(), instr, instr + 1); } else { if (!func->contains_asm_ops && dynamic_cast(result.get())) { - func->warnings += ";; Function contains asm op\n"; + func->warnings.info("Contains asm ops"); func->contains_asm_ops = true; } diff --git a/decompiler/IR2/AtomicOp.cpp b/decompiler/IR2/AtomicOp.cpp index e95c6275d4..c68eb413e5 100644 --- a/decompiler/IR2/AtomicOp.cpp +++ b/decompiler/IR2/AtomicOp.cpp @@ -422,7 +422,7 @@ AsmOp::AsmOp(Instruction instr, int my_idx) : AtomicOp(my_idx), m_instr(std::mov auto& dst = m_instr.get_dst(0); if (dst.is_reg()) { auto reg = dst.get_reg(); - if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR) { + if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR || reg.get_kind() == Reg::VF) { m_dst = Variable(VariableMode::WRITE, reg, my_idx, true); } } @@ -433,7 +433,7 @@ AsmOp::AsmOp(Instruction instr, int my_idx) : AtomicOp(my_idx), m_instr(std::mov auto& src = m_instr.get_src(i); if (src.is_reg()) { auto reg = src.get_reg(); - if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR) { + if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR || reg.get_kind() == Reg::VF) { m_src[i] = Variable(VariableMode::READ, reg, my_idx, true); } } @@ -497,6 +497,102 @@ void AsmOp::update_register_info() { m_read_regs.push_back(src->reg()); } } + + if (m_instr.kind >= FIRST_COP2_MACRO && m_instr.kind <= LAST_COP2_MACRO) { + switch (m_instr.kind) { + case InstructionKind::VMSUBQ: + m_read_regs.push_back(Register(Reg::COP2_MACRO_SPECIAL, Reg::MACRO_Q)); + m_read_regs.push_back(Register(Reg::COP2_MACRO_SPECIAL, Reg::MACRO_ACC)); + break; + + case InstructionKind::VMULAQ: + m_read_regs.push_back(Register(Reg::COP2_MACRO_SPECIAL, Reg::MACRO_Q)); + m_write_regs.push_back(Register(Reg::COP2_MACRO_SPECIAL, Reg::MACRO_ACC)); + break; + + // Read Q register + case InstructionKind::VADDQ: + case InstructionKind::VSUBQ: + case InstructionKind::VMULQ: + m_read_regs.push_back(Register(Reg::COP2_MACRO_SPECIAL, Reg::MACRO_Q)); + break; + + // Write ACC register + case InstructionKind::VADDA: + case InstructionKind::VADDA_BC: + case InstructionKind::VMULA: + case InstructionKind::VMULA_BC: + case InstructionKind::VOPMULA: + m_write_regs.push_back(Register(Reg::COP2_MACRO_SPECIAL, Reg::MACRO_ACC)); + break; + + // Write Q register + case InstructionKind::VDIV: + case InstructionKind::VSQRT: + case InstructionKind::VRSQRT: + m_write_regs.push_back(Register(Reg::COP2_MACRO_SPECIAL, Reg::MACRO_Q)); + break; + + // Read acc register + case InstructionKind::VMADD: + case InstructionKind::VMADD_BC: + case InstructionKind::VMSUB: + case InstructionKind::VMSUB_BC: + m_read_regs.push_back(Register(Reg::COP2_MACRO_SPECIAL, Reg::MACRO_ACC)); + break; + case InstructionKind::VOPMSUB: + m_read_regs.push_back(Register(Reg::COP2_MACRO_SPECIAL, Reg::MACRO_ACC)); + break; + + // Read/Write acc register + case InstructionKind::VMADDA: + case InstructionKind::VMADDA_BC: + case InstructionKind::VMSUBA_BC: + m_write_regs.push_back(Register(Reg::COP2_MACRO_SPECIAL, Reg::MACRO_ACC)); + m_read_regs.push_back(Register(Reg::COP2_MACRO_SPECIAL, Reg::MACRO_ACC)); + break; + + case InstructionKind::VMOVE: + case InstructionKind::VFTOI0: + case InstructionKind::VFTOI4: + case InstructionKind::VFTOI12: + case InstructionKind::VITOF0: + case InstructionKind::VITOF12: + case InstructionKind::VITOF15: + case InstructionKind::VABS: + case InstructionKind::VADD: + case InstructionKind::VADD_BC: + case InstructionKind::VSUB: + case InstructionKind::VSUB_BC: + case InstructionKind::VMUL: + case InstructionKind::VMUL_BC: + case InstructionKind::VMINI: + case InstructionKind::VMINI_BC: + case InstructionKind::VMAX: + case InstructionKind::VMAX_BC: + case InstructionKind::VCLIP: + case InstructionKind::VNOP: + + // anything using one of these should be manually analyzed. + case InstructionKind::VMTIR: + case InstructionKind::VIAND: + case InstructionKind::VLQI: + case InstructionKind::VIADDI: + case InstructionKind::VSQI: + case InstructionKind::VRGET: + case InstructionKind::VRXOR: + case InstructionKind::VRNEXT: + case InstructionKind::VWAITQ: // okay if following vsqrt/vrsqrt/vdiv + case InstructionKind::VCALLMS: + + // do not read/write acc/q + break; + + default: + assert(false); + break; + } + } } void AsmOp::collect_vars(VariableSet& vars) const { diff --git a/decompiler/IR2/Env.cpp b/decompiler/IR2/Env.cpp index d920ab0713..529200ab04 100644 --- a/decompiler/IR2/Env.cpp +++ b/decompiler/IR2/Env.cpp @@ -72,20 +72,24 @@ const std::string& Env::remapped_name(const std::string& name) const { } goos::Object Env::get_variable_name(Register reg, int atomic_idx, VariableMode mode) const { - std::string lookup_name = m_var_names.lookup(reg, atomic_idx, mode).name(); - auto remapped = m_var_remap.find(lookup_name); - if (remapped != m_var_remap.end()) { - lookup_name = remapped->second; - } - auto type_kv = m_typehints.find(atomic_idx); - if (type_kv != m_typehints.end()) { - for (auto& x : type_kv->second) { - if (x.reg == reg) { - return pretty_print::build_list("the-as", x.type_name, lookup_name); + if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR) { + std::string lookup_name = m_var_names.lookup(reg, atomic_idx, mode).name(); + auto remapped = m_var_remap.find(lookup_name); + if (remapped != m_var_remap.end()) { + lookup_name = remapped->second; + } + auto type_kv = m_typehints.find(atomic_idx); + if (type_kv != m_typehints.end()) { + for (auto& x : type_kv->second) { + if (x.reg == reg) { + return pretty_print::build_list("the-as", x.type_name, lookup_name); + } } } + return pretty_print::to_symbol(lookup_name); + } else { + return pretty_print::to_symbol(reg.to_charp()); } - return pretty_print::to_symbol(lookup_name); } /*! @@ -169,7 +173,9 @@ std::vector Env::extract_visible_variables( std::vector> vars; for (auto& x : var_set) { - vars.push_back(std::make_pair(get_ssa_var(x), x)); + if (x.reg().get_kind() == Reg::FPR || x.reg().get_kind() == Reg::GPR) { + vars.push_back(std::make_pair(get_ssa_var(x), x)); + } } std::sort(vars.begin(), vars.end(), diff --git a/decompiler/ObjectFile/LinkedObjectFile.cpp b/decompiler/ObjectFile/LinkedObjectFile.cpp index cac27f90d4..31439454db 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.cpp +++ b/decompiler/ObjectFile/LinkedObjectFile.cpp @@ -548,7 +548,7 @@ std::string LinkedObjectFile::to_asm_json(const std::string& obj_file_name) { f["name"] = fname; f["type"] = func.type.print(); f["segment"] = seg; - f["warnings"] = func.warnings; + f["warnings"] = func.warnings.get_warning_text(false); f["parent_object"] = obj_file_name; std::vector ops; @@ -601,8 +601,8 @@ std::string LinkedObjectFile::print_function_disassembly(Function& func, result += "; .function " + func.guessed_name.to_string() + " " + extra_name + "\n"; result += ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"; result += func.prologue.to_string(2) + "\n"; - if (!func.warnings.empty()) { - result += ";;Warnings:\n" + func.warnings + "\n"; + if (func.warnings.has_warnings()) { + result += ";; Warnings:\n" + func.warnings.get_warning_text(true) + "\n"; } // print each instruction in the function. diff --git a/decompiler/ObjectFile/ObjectFileDB.cpp b/decompiler/ObjectFile/ObjectFileDB.cpp index 3c20da85f3..6c515d911a 100644 --- a/decompiler/ObjectFile/ObjectFileDB.cpp +++ b/decompiler/ObjectFile/ObjectFileDB.cpp @@ -716,7 +716,7 @@ void ObjectFileDB::analyze_functions_ir1() { unique_names.insert(name); if (config.asm_functions_by_name.find(name) != config.asm_functions_by_name.end()) { - func.warnings += ";; flagged as asm by config\n"; + func.warnings.info("Flagged as asm by config"); func.suspected_asm = true; } } @@ -729,7 +729,7 @@ void ObjectFileDB::analyze_functions_ir1() { if (duplicated_functions.find(name) != duplicated_functions.end()) { duplicated_functions[name].insert(data.to_unique_name()); - func.warnings += ";; this function exists in multiple non-identical object files\n"; + func.warnings.info("Exists in multiple non-identical object files"); } }); @@ -791,7 +791,6 @@ void ObjectFileDB::analyze_functions_ir1() { } } else { asm_funcs++; - func.warnings.append(";; Assembly Function. Analysis passes were not attempted.\n"); } }); diff --git a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp index 67c783d21f..d8300e2e12 100644 --- a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp +++ b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp @@ -119,7 +119,7 @@ void ObjectFileDB::ir2_top_level_pass() { if (get_config().asm_functions_by_name.find(name) != get_config().asm_functions_by_name.end()) { - func.warnings += ";; flagged as asm by config\n"; + func.warnings.info("Flagged as asm by config"); func.suspected_asm = true; } } @@ -133,7 +133,7 @@ void ObjectFileDB::ir2_top_level_pass() { if (duplicated_functions.find(name) != duplicated_functions.end()) { duplicated_functions[name].insert(data.to_unique_name()); - func.warnings += ";; this function exists in multiple non-identical object files\n"; + func.warnings.info("this function exists in multiple non-identical object files"); } }); @@ -207,7 +207,7 @@ void ObjectFileDB::ir2_basic_block_pass() { } if (func.suspected_asm) { - func.warnings.append(";; Assembly Function\n"); + func.warnings.info("Assembly Function"); suspected_asm++; } }); @@ -248,7 +248,7 @@ void ObjectFileDB::ir2_atomic_op_pass() { } catch (std::exception& e) { lg::warn("Function {} from {} could not be converted to atomic ops: {}", func.guessed_name.to_string(), data.to_unique_name(), e.what()); - func.warnings.append(";; Failed to convert to atomic ops\n"); + func.warnings.general_warning("Failed to convert to atomic ops: {}", e.what()); } } }); @@ -286,11 +286,12 @@ void ObjectFileDB::ir2_type_analysis_pass() { if (func.run_type_analysis_ir2(ts, dts, data.linked_data, hints)) { successful_functions++; } else { - func.warnings.append(";; Type analysis failed\n"); + func.warnings.type_prop_warning("Type analysis failed"); } } else { // lg::warn("Function {} didn't know its type", func.guessed_name.to_string()); - func.warnings.append(";; Type of function is unknown\n"); + func.warnings.type_prop_warning("Function {} has unknown type", + func.guessed_name.to_string()); } } }); @@ -310,6 +311,19 @@ void ObjectFileDB::ir2_register_usage_pass() { if (!func.suspected_asm && func.ir2.atomic_ops_succeeded) { analyzed_funcs++; func.ir2.env.set_reg_use(analyze_ir2_register_usage(func)); + + auto block_0_start = func.ir2.env.reg_use().block.at(0).input; + for (auto x : block_0_start) { + if (x.get_kind() == Reg::VF && x.get_vf() != 0) { + lg::error("Bad vf dependency on {} in {}", x.to_charp(), func.guessed_name.to_string()); + func.warnings.bad_vf_dependency("{}", x.to_string()); + } + + if (x.get_kind() == Reg::COP2_MACRO_SPECIAL) { + lg::error("Bad vf dependency on {} in {}", x.to_charp(), func.guessed_name.to_string()); + func.warnings.bad_vf_dependency("{}", x.to_string()); + } + } } }); @@ -531,8 +545,8 @@ std::string ObjectFileDB::ir2_function_to_string(ObjectFileData& data, Function& result += "; .function " + func.guessed_name.to_string() + "\n"; result += ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"; result += func.prologue.to_string(2) + "\n"; - if (!func.warnings.empty()) { - result += ";;Warnings:\n" + func.warnings + "\n"; + if (func.warnings.has_warnings()) { + result += ";; Warnings:\n" + func.warnings.get_warning_text(true) + "\n"; } if (func.ir2.env.has_local_vars()) { diff --git a/decompiler/analysis/cfg_builder.cpp b/decompiler/analysis/cfg_builder.cpp index e941032162..6cbbb72e42 100644 --- a/decompiler/analysis/cfg_builder.cpp +++ b/decompiler/analysis/cfg_builder.cpp @@ -165,6 +165,7 @@ void clean_up_return_final(const Function& f, ReturnElement* ir) { if (!dead) { lg::error("failed to recognize dead code after return, got {}", ir->dead_code->to_string(f.ir2.env)); + throw std::runtime_error("failed to recognize dead code"); } assert(dead); auto src = dynamic_cast(dead->src()->try_as_single_element()); diff --git a/decompiler/analysis/expression_build.cpp b/decompiler/analysis/expression_build.cpp index 8a8e05f13f..ae2837a860 100644 --- a/decompiler/analysis/expression_build.cpp +++ b/decompiler/analysis/expression_build.cpp @@ -96,10 +96,7 @@ bool convert_to_expressions(Form* top_level_form, // fix up stuff } catch (std::exception& e) { - std::string warning = - fmt::format("Expression building failed in {}: {}", f.guessed_name.to_string(), e.what()); - lg::warn(warning); - f.warnings.append(";; " + warning); + f.warnings.expression_build_warning("In {}: {}", f.guessed_name.to_string(), e.what()); return false; } diff --git a/decompiler/analysis/final_output.cpp b/decompiler/analysis/final_output.cpp index 6aaf9f0b7a..c030377ae9 100644 --- a/decompiler/analysis/final_output.cpp +++ b/decompiler/analysis/final_output.cpp @@ -104,6 +104,12 @@ std::string careful_function_to_string( const DecompilerTypeSystem& dts, FunctionDefSpecials special_mode = FunctionDefSpecials::NONE) { auto& env = func->ir2.env; + + std::string result; + if (func->warnings.has_warnings()) { + result += func->warnings.get_warning_text(true); + } + if (!func->ir2.top_form) { return ";; ERROR: function was not converted to expressions. Cannot decompile.\n\n"; } @@ -119,7 +125,8 @@ std::string careful_function_to_string( return ";; ERROR: function has no register use analysis. Cannot decompile.\n\n"; } - return final_defun_out(*func, func->ir2.env, dts, special_mode) + "\n\n"; + result += final_defun_out(*func, func->ir2.env, dts, special_mode) + "\n\n"; + return result; } } // namespace diff --git a/decompiler/analysis/variable_naming.cpp b/decompiler/analysis/variable_naming.cpp index e3425b2c64..ef4a0de2e2 100644 --- a/decompiler/analysis/variable_naming.cpp +++ b/decompiler/analysis/variable_naming.cpp @@ -312,7 +312,9 @@ SSA make_rc_ssa(const Function& function, const RegUsageInfo& rui, const Functio const auto& start_op_op = ops.ops.at(start_op); auto init_regs = start_op_info.live; for (auto reg : start_op_op->read_regs()) { - init_regs.insert(reg); + if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR) { + init_regs.insert(reg); + } } for (auto reg : init_regs) { @@ -353,19 +355,23 @@ SSA make_rc_ssa(const Function& function, const RegUsageInfo& rui, const Functio assert(op->write_regs().size() <= 1); // reads: for (auto r : op->read_regs()) { - ssa_i.src.push_back(current_regs.at(r)); + if (r.get_kind() == Reg::FPR || r.get_kind() == Reg::GPR) { + ssa_i.src.push_back(current_regs.at(r)); + } } // writes: if (!op->write_regs().empty()) { auto w = op->write_regs().front(); - auto var = ssa.map.allocate(w); - ssa_i.dst = var; - // avoid operator[] again - auto it = current_regs.find(w); - if (it != current_regs.end()) { - it->second = var; - } else { - current_regs.insert(std::make_pair(w, var)); + if (w.get_kind() == Reg::FPR || w.get_kind() == Reg::GPR) { + auto var = ssa.map.allocate(w); + ssa_i.dst = var; + // avoid operator[] again + auto it = current_regs.find(w); + if (it != current_regs.end()) { + it->second = var; + } else { + current_regs.insert(std::make_pair(w, var)); + } } }