[Decompiler] Flag uses of vf registers for values outside of functions (#263)

* clean up warning system

* add bad dependency warning

* add q and acc
This commit is contained in:
water111
2021-02-15 11:55:10 -05:00
committed by GitHub
parent af1691a0bc
commit aa9bcd07f4
19 changed files with 336 additions and 91 deletions
+1
View File
@@ -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
+6 -2
View File
@@ -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,
+18 -1
View File
@@ -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;
}
+10 -7
View File
@@ -5,9 +5,6 @@
* Representation of an EE register.
*/
#ifndef NEXT_REGISTER_H
#define NEXT_REGISTER_H
#include <cstdint>
#include <string>
@@ -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
+1 -1
View File
@@ -1892,7 +1892,7 @@ std::shared_ptr<ControlFlowGraph> 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;
+26 -30
View File
@@ -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),
+3 -1
View File
@@ -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;
+3 -4
View File
@@ -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);
+97
View File
@@ -0,0 +1,97 @@
#pragma once
#include <string>
#include <vector>
#include <cassert>
#include "third-party/fmt/core.h"
class DecompWarnings {
public:
DecompWarnings() = default;
template <typename... Args>
void general_warning(const std::string& str, Args&&... args) {
warning(Warning::Kind::GENERAL, str, std::forward<Args>(args)...);
}
template <typename... Args>
void expression_build_warning(const std::string& str, Args&&... args) {
warning(Warning::Kind::EXPR_BUILD_FAILED, str, std::forward<Args>(args)...);
}
template <typename... Args>
void cfg_build_warning(const std::string& str, Args&&... args) {
warning(Warning::Kind::CFG_FAILED, str, std::forward<Args>(args)...);
}
template <typename... Args>
void type_prop_warning(const std::string& str, Args&&... args) {
warning(Warning::Kind::TYPE_PROP_FAILED, str, std::forward<Args>(args)...);
}
template <typename... Args>
void bad_vf_dependency(const std::string& str, Args&&... args) {
warning(Warning::Kind::BAD_VF_DEPENDENCY, str, std::forward<Args>(args)...);
}
template <typename... Args>
void info(const std::string& str, Args&&... args) {
warning(Warning::Kind::INFO, str, std::forward<Args>(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 <typename... Args>
void warning(Warning::Kind kind, const std::string& str, Args&&... args) {
Warning warn(kind, fmt::format(str, std::forward<Args>(args)...));
m_warnings.push_back(warn);
}
std::vector<Warning> m_warnings;
};
+2 -2
View File
@@ -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<IR_Failed_Atomic>(), instr, instr + 1);
} else {
if (!func->contains_asm_ops && dynamic_cast<IR_AsmOp*>(result.get())) {
func->warnings += ";; Function contains asm op\n";
func->warnings.info("Contains asm ops");
func->contains_asm_ops = true;
}
+98 -2
View File
@@ -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 {
+18 -12
View File
@@ -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<VariableNames::VarInfo> Env::extract_visible_variables(
std::vector<std::pair<RegId, Variable>> 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(),
+3 -3
View File
@@ -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<nlohmann::json::object_t> 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.
+2 -3
View File
@@ -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");
}
});
+22 -8
View File
@@ -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()) {
+1
View File
@@ -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<SimpleExpressionElement*>(dead->src()->try_as_single_element());
+1 -4
View File
@@ -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;
}
+8 -1
View File
@@ -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
+16 -10
View File
@@ -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));
}
}
}