mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
Add decompiler IR, basic operations, all-types file (#57)
* framework for basic op * started IR framework * check in type info file * add some basic operations to the first pass ir conversion * use a single condition system * add more basic op decoding * more ir
This commit is contained in:
parent
c9b53d51ff
commit
376c273845
|
@ -78,24 +78,6 @@ int64_t get_int(const goos::Object& obj) {
|
|||
throw std::runtime_error(obj.print() + " was supposed to be an integer, but isn't");
|
||||
}
|
||||
|
||||
TypeSpec parse_typespec(TypeSystem* type_system, const goos::Object& src) {
|
||||
if (src.is_symbol()) {
|
||||
return type_system->make_typespec(symbol_string(src));
|
||||
} else if (src.is_pair()) {
|
||||
TypeSpec ts = type_system->make_typespec(symbol_string(car(&src)));
|
||||
const auto& rest = *cdr(&src);
|
||||
|
||||
for_each_in_list(rest,
|
||||
[&](const goos::Object& o) { ts.add_arg(parse_typespec(type_system, o)); });
|
||||
|
||||
return ts;
|
||||
} else {
|
||||
throw std::runtime_error("invalid typespec: " + src.print());
|
||||
}
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
void add_field(StructureType* structure, TypeSystem* ts, const goos::Object& def) {
|
||||
auto rest = &def;
|
||||
|
||||
|
@ -278,6 +260,24 @@ TypeFlags parse_structure_def(StructureType* type,
|
|||
|
||||
} // namespace
|
||||
|
||||
TypeSpec parse_typespec(TypeSystem* type_system, const goos::Object& src) {
|
||||
if (src.is_symbol()) {
|
||||
return type_system->make_typespec(symbol_string(src));
|
||||
} else if (src.is_pair()) {
|
||||
TypeSpec ts = type_system->make_typespec(symbol_string(car(&src)));
|
||||
const auto& rest = *cdr(&src);
|
||||
|
||||
for_each_in_list(rest,
|
||||
[&](const goos::Object& o) { ts.add_arg(parse_typespec(type_system, o)); });
|
||||
|
||||
return ts;
|
||||
} else {
|
||||
throw std::runtime_error("invalid typespec: " + src.print());
|
||||
}
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts) {
|
||||
auto iter = &deftype;
|
||||
|
||||
|
|
|
@ -22,3 +22,4 @@ struct DeftypeResult {
|
|||
};
|
||||
|
||||
DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts);
|
||||
TypeSpec parse_typespec(TypeSystem* type_system, const goos::Object& src);
|
||||
|
|
|
@ -12,14 +12,15 @@ add_executable(decompiler
|
|||
util/FileIO.cpp
|
||||
config.cpp
|
||||
util/LispPrint.cpp
|
||||
util/DecompilerTypeSystem.cpp
|
||||
Function/BasicBlocks.cpp
|
||||
Disasm/InstructionMatching.cpp
|
||||
TypeSystem/GoalType.cpp
|
||||
TypeSystem/GoalFunction.cpp
|
||||
TypeSystem/GoalSymbol.cpp
|
||||
TypeSystem/TypeInfo.cpp
|
||||
TypeSystem/TypeSpec.cpp Function/CfgVtx.cpp Function/CfgVtx.h)
|
||||
Function/CfgVtx.cpp Function/CfgVtx.h
|
||||
IR/BasicOpBuilder.cpp
|
||||
IR/IR.cpp)
|
||||
|
||||
target_link_libraries(decompiler
|
||||
minilzo
|
||||
common_util)
|
||||
common_util
|
||||
type_system
|
||||
fmt)
|
|
@ -44,6 +44,12 @@ struct InstructionAtom {
|
|||
std::string to_string(const LinkedObjectFile& file) 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_reg(Register r) const { return kind == REGISTER && reg == r; }
|
||||
|
||||
private:
|
||||
int32_t imm;
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
/*!
|
||||
* @file InstructionMatching.h
|
||||
* Utilities for checking if an instruction matches some criteria.
|
||||
*/
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_INSTRUCTIONMATCHING_H
|
||||
#define JAK_DISASSEMBLER_INSTRUCTIONMATCHING_H
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* @file OpcodeInfo.cpp
|
||||
* Decoding info for each opcode.
|
||||
*/
|
||||
|
||||
#include "OpcodeInfo.h"
|
||||
#include <cassert>
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_BASICBLOCKS_H
|
||||
#define JAK_DISASSEMBLER_BASICBLOCKS_H
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
|
@ -21,5 +18,3 @@ struct BasicBlock {
|
|||
std::vector<BasicBlock> find_blocks_in_function(const LinkedObjectFile& file,
|
||||
int seg,
|
||||
const Function& func);
|
||||
|
||||
#endif // JAK_DISASSEMBLER_BASICBLOCKS_H
|
||||
|
|
|
@ -1637,7 +1637,6 @@ void ControlFlowGraph::flag_early_exit(const std::vector<BasicBlock>& blocks) {
|
|||
* Build and resolve a Control Flow Graph as much as possible.
|
||||
*/
|
||||
std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file, int seg, Function& func) {
|
||||
printf("build cfg : %s\n", func.guessed_name.to_string().c_str());
|
||||
auto cfg = std::make_shared<ControlFlowGraph>();
|
||||
|
||||
const auto& blocks = cfg->create_blocks(func.basic_blocks.size());
|
||||
|
@ -1716,13 +1715,6 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file, int se
|
|||
|
||||
cfg->flag_early_exit(func.basic_blocks);
|
||||
|
||||
// if(func.guessed_name.to_string() == "(method 9 thread)")
|
||||
// cfg->find_cond_w_else();
|
||||
|
||||
// if (func.guessed_name.to_string() != "looping-code") {
|
||||
// return cfg;
|
||||
// }
|
||||
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
|
@ -1730,11 +1722,9 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file, int se
|
|||
// printf("%s\n", cfg->to_dot().c_str());
|
||||
// printf("%s\n", cfg->to_form()->toStringPretty().c_str());
|
||||
|
||||
changed = changed | cfg->find_cond_w_else();
|
||||
changed = changed | cfg->find_cond_n_else();
|
||||
changed = changed || cfg->find_cond_w_else();
|
||||
changed = changed || cfg->find_cond_n_else();
|
||||
changed = changed || cfg->find_while_loop_top_level();
|
||||
// //// printf("while loops? %d\n", changed);
|
||||
//// changed = changed || cfg->find_if_else_top_level();
|
||||
changed = changed || cfg->find_seq_top_level();
|
||||
changed = changed || cfg->find_short_circuits();
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "Function.h"
|
||||
#include "decompiler/Disasm/InstructionMatching.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
#include "decompiler/TypeSystem/TypeInfo.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
|
||||
namespace {
|
||||
std::vector<Register> gpr_backups = {make_gpr(Reg::GP), make_gpr(Reg::S5), make_gpr(Reg::S4),
|
||||
|
@ -418,7 +418,7 @@ void Function::check_epilogue(const LinkedObjectFile& file) {
|
|||
*
|
||||
* Updates the guessed_name of the function and updates type_info
|
||||
*/
|
||||
void Function::find_global_function_defs(LinkedObjectFile& file) {
|
||||
void Function::find_global_function_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts) {
|
||||
int state = 0;
|
||||
int label_id = -1;
|
||||
Register reg;
|
||||
|
@ -457,7 +457,8 @@ void Function::find_global_function_defs(LinkedObjectFile& file) {
|
|||
auto& func = file.get_function_at_label(label_id);
|
||||
assert(func.guessed_name.empty());
|
||||
func.guessed_name.set_as_global(name);
|
||||
get_type_info().inform_symbol(name, TypeSpec("function"));
|
||||
dts.add_symbol(name, "function");
|
||||
;
|
||||
// todo - inform function.
|
||||
}
|
||||
|
||||
|
@ -550,3 +551,41 @@ void Function::find_method_defs(LinkedObjectFile& file) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Function::add_basic_op(std::shared_ptr<IR> op, int start_instr, int end_instr) {
|
||||
op->is_basic_op = true;
|
||||
assert(end_instr > start_instr);
|
||||
|
||||
for (int i = start_instr; i < end_instr; i++) {
|
||||
instruction_to_basic_op[i] = basic_ops.size();
|
||||
}
|
||||
basic_op_to_instruction[basic_ops.size()] = start_instr;
|
||||
basic_ops.push_back(op);
|
||||
}
|
||||
|
||||
bool Function::instr_starts_basic_op(int idx) {
|
||||
auto op = instruction_to_basic_op.find(idx);
|
||||
if (op != instruction_to_basic_op.end()) {
|
||||
auto start_instr = basic_op_to_instruction.at(op->second);
|
||||
return start_instr == idx;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IR* Function::get_basic_op_at_instr(int idx) {
|
||||
return basic_ops.at(instruction_to_basic_op.at(idx)).get();
|
||||
}
|
||||
|
||||
int Function::get_basic_op_count() {
|
||||
return basic_ops.size();
|
||||
}
|
||||
|
||||
int Function::get_failed_basic_op_count() {
|
||||
int count = 0;
|
||||
for (auto& x : basic_ops) {
|
||||
if (dynamic_cast<IR_Failed*>(x.get())) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
|
@ -8,6 +8,9 @@
|
|||
#include "decompiler/Disasm/Instruction.h"
|
||||
#include "BasicBlocks.h"
|
||||
#include "CfgVtx.h"
|
||||
#include "decompiler/IR/IR.h"
|
||||
|
||||
class DecompilerTypeSystem;
|
||||
|
||||
struct FunctionName {
|
||||
enum class FunctionKind {
|
||||
|
@ -60,8 +63,14 @@ class Function {
|
|||
public:
|
||||
Function(int _start_word, int _end_word);
|
||||
void analyze_prologue(const LinkedObjectFile& file);
|
||||
void find_global_function_defs(LinkedObjectFile& file);
|
||||
void find_global_function_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts);
|
||||
void find_method_defs(LinkedObjectFile& file);
|
||||
void add_basic_op(std::shared_ptr<IR> op, int start_instr, int end_instr);
|
||||
bool has_basic_ops() { return !basic_ops.empty(); }
|
||||
bool instr_starts_basic_op(int idx);
|
||||
IR* get_basic_op_at_instr(int idx);
|
||||
int get_basic_op_count();
|
||||
int get_failed_basic_op_count();
|
||||
|
||||
int segment = -1;
|
||||
int start_word = -1;
|
||||
|
@ -115,6 +124,9 @@ class Function {
|
|||
|
||||
private:
|
||||
void check_epilogue(const LinkedObjectFile& file);
|
||||
std::vector<std::shared_ptr<IR>> basic_ops;
|
||||
std::unordered_map<int, int> instruction_to_basic_op;
|
||||
std::unordered_map<int, int> basic_op_to_instruction;
|
||||
};
|
||||
|
||||
#endif // NEXT_FUNCTION_H
|
||||
|
|
1412
decompiler/IR/BasicOpBuilder.cpp
Normal file
1412
decompiler/IR/BasicOpBuilder.cpp
Normal file
File diff suppressed because it is too large
Load diff
13
decompiler/IR/BasicOpBuilder.h
Normal file
13
decompiler/IR/BasicOpBuilder.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*!
|
||||
* @file BasicOpBuilder.h
|
||||
* Analyzes a basic block and converts instructions to BasicOps.
|
||||
* These will be used later to convert the Cfg into the nested IR format.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class Function;
|
||||
struct BasicBlock;
|
||||
class LinkedObjectFile;
|
||||
|
||||
void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjectFile* file);
|
391
decompiler/IR/IR.cpp
Normal file
391
decompiler/IR/IR.cpp
Normal file
|
@ -0,0 +1,391 @@
|
|||
#include "IR.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
|
||||
std::string IR::print(const LinkedObjectFile& file) const {
|
||||
return to_form(file)->toStringPretty();
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Register::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return toForm(reg.to_charp());
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Set::to_form(const LinkedObjectFile& file) const {
|
||||
return buildList(toForm("set!"), dst->to_form(file), src->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Store::to_form(const LinkedObjectFile& file) const {
|
||||
std::string store_operator;
|
||||
switch (kind) {
|
||||
case FLOAT:
|
||||
store_operator = "s.f";
|
||||
break;
|
||||
case INTEGER:
|
||||
switch (size) {
|
||||
case 1:
|
||||
store_operator = "s.b";
|
||||
break;
|
||||
case 2:
|
||||
store_operator = "s.h";
|
||||
break;
|
||||
case 4:
|
||||
store_operator = "s.w";
|
||||
break;
|
||||
case 8:
|
||||
store_operator = "s.d";
|
||||
break;
|
||||
case 16:
|
||||
store_operator = "s.q";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return buildList(toForm(store_operator), dst->to_form(file), src->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Failed::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return buildList("INVALID-OPERATION");
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Symbol::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return toForm("'" + name);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_SymbolValue::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return toForm(name);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_StaticAddress::to_form(const LinkedObjectFile& file) const {
|
||||
// return buildList(toForm("&"), file.get_label_name(label_id));
|
||||
return toForm(file.get_label_name(label_id));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Load::to_form(const LinkedObjectFile& file) const {
|
||||
std::string load_operator;
|
||||
switch (kind) {
|
||||
case FLOAT:
|
||||
load_operator = "l.f";
|
||||
break;
|
||||
case UNSIGNED:
|
||||
switch (size) {
|
||||
case 1:
|
||||
load_operator = "l.bu";
|
||||
break;
|
||||
case 2:
|
||||
load_operator = "l.hu";
|
||||
break;
|
||||
case 4:
|
||||
load_operator = "l.wu";
|
||||
break;
|
||||
case 8:
|
||||
load_operator = "l.d";
|
||||
break;
|
||||
case 16:
|
||||
load_operator = "l.q";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
case SIGNED:
|
||||
switch (size) {
|
||||
case 1:
|
||||
load_operator = "l.bs";
|
||||
break;
|
||||
case 2:
|
||||
load_operator = "l.hs";
|
||||
break;
|
||||
case 4:
|
||||
load_operator = "l.ws";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return buildList(toForm(load_operator), location->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_FloatMath2::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case DIV:
|
||||
math_operator = "/.f";
|
||||
break;
|
||||
case MUL:
|
||||
math_operator = "*.f";
|
||||
break;
|
||||
case ADD:
|
||||
math_operator = "+.f";
|
||||
break;
|
||||
case SUB:
|
||||
math_operator = "-.f";
|
||||
break;
|
||||
case MIN:
|
||||
math_operator = "min.f";
|
||||
break;
|
||||
case MAX:
|
||||
math_operator = "max.f";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return buildList(toForm(math_operator), arg0->to_form(file), arg1->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_IntMath2::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case ADD:
|
||||
math_operator = "+.i";
|
||||
break;
|
||||
case SUB:
|
||||
math_operator = "-.i";
|
||||
break;
|
||||
case MUL_SIGNED:
|
||||
math_operator = "*.si";
|
||||
break;
|
||||
case MUL_UNSIGNED:
|
||||
math_operator = "*.ui";
|
||||
break;
|
||||
case DIV_SIGNED:
|
||||
math_operator = "/.si";
|
||||
break;
|
||||
case MOD_SIGNED:
|
||||
math_operator = "mod.si";
|
||||
break;
|
||||
case DIV_UNSIGNED:
|
||||
math_operator = "/.ui";
|
||||
break;
|
||||
case MOD_UNSIGNED:
|
||||
math_operator = "mod.ui";
|
||||
break;
|
||||
case OR:
|
||||
math_operator = "logior";
|
||||
break;
|
||||
case AND:
|
||||
math_operator = "logand";
|
||||
break;
|
||||
case NOR:
|
||||
math_operator = "lognor";
|
||||
break;
|
||||
case XOR:
|
||||
math_operator = "logxor";
|
||||
break;
|
||||
case LEFT_SHIFT:
|
||||
math_operator = "shl";
|
||||
break;
|
||||
case RIGHT_SHIFT_ARITH:
|
||||
math_operator = "sar";
|
||||
break;
|
||||
case RIGHT_SHIFT_LOGIC:
|
||||
math_operator = "shr";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return buildList(toForm(math_operator), arg0->to_form(file), arg1->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_IntMath1::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case NOT:
|
||||
math_operator = "lognot";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return buildList(toForm(math_operator), arg->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_FloatMath1::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case FLOAT_TO_INT:
|
||||
math_operator = "int<-float";
|
||||
break;
|
||||
case INT_TO_FLOAT:
|
||||
math_operator = "float<-int";
|
||||
break;
|
||||
case ABS:
|
||||
math_operator = "abs.f";
|
||||
break;
|
||||
case NEG:
|
||||
math_operator = "neg.f";
|
||||
break;
|
||||
case SQRT:
|
||||
math_operator = "sqrt.f";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return buildList(toForm(math_operator), arg->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Call::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return buildList("call!");
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_IntegerConstant::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return toForm(std::to_string(value));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> BranchDelay::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
switch (kind) {
|
||||
case NOP:
|
||||
return buildList("nop");
|
||||
case SET_REG_FALSE:
|
||||
return buildList(toForm("set!"), destination->to_form(file), "'#f");
|
||||
case SET_REG_TRUE:
|
||||
return buildList(toForm("set!"), destination->to_form(file), "'#t");
|
||||
case SET_REG_REG:
|
||||
return buildList(toForm("set!"), destination->to_form(file), source->to_form(file));
|
||||
case UNKNOWN:
|
||||
return buildList("unknown-branch-delay");
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Nop::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return buildList("nop!");
|
||||
}
|
||||
|
||||
int Condition::num_args() const {
|
||||
switch (kind) {
|
||||
case NOT_EQUAL:
|
||||
case EQUAL:
|
||||
case LESS_THAN_SIGNED:
|
||||
case LESS_THAN_UNSIGNED:
|
||||
case GREATER_THAN_SIGNED:
|
||||
case GREATER_THAN_UNSIGNED:
|
||||
case LEQ_SIGNED:
|
||||
case GEQ_SIGNED:
|
||||
case LEQ_UNSIGNED:
|
||||
case GEQ_UNSIGNED:
|
||||
case FLOAT_EQUAL:
|
||||
case FLOAT_NOT_EQUAL:
|
||||
case FLOAT_LESS_THAN:
|
||||
case FLOAT_GEQ:
|
||||
return 2;
|
||||
case ZERO:
|
||||
case NONZERO:
|
||||
case FALSE:
|
||||
case TRUTHY:
|
||||
return 1;
|
||||
case ALWAYS:
|
||||
return 0;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> Condition::to_form(const LinkedObjectFile& file) const {
|
||||
int nargs = num_args();
|
||||
std::string condtion_operator;
|
||||
switch (kind) {
|
||||
case NOT_EQUAL:
|
||||
condtion_operator = "!=";
|
||||
break;
|
||||
case EQUAL:
|
||||
condtion_operator = "=";
|
||||
break;
|
||||
case LESS_THAN_SIGNED:
|
||||
condtion_operator = "<.si";
|
||||
break;
|
||||
case LESS_THAN_UNSIGNED:
|
||||
condtion_operator = "<.ui";
|
||||
break;
|
||||
case GREATER_THAN_SIGNED:
|
||||
condtion_operator = ">.si";
|
||||
break;
|
||||
case GREATER_THAN_UNSIGNED:
|
||||
condtion_operator = ">.ui";
|
||||
break;
|
||||
case LEQ_SIGNED:
|
||||
condtion_operator = "<=.si";
|
||||
break;
|
||||
case GEQ_SIGNED:
|
||||
condtion_operator = ">=.si";
|
||||
break;
|
||||
case LEQ_UNSIGNED:
|
||||
condtion_operator = "<=.ui";
|
||||
break;
|
||||
case GEQ_UNSIGNED:
|
||||
condtion_operator = ">=.ui";
|
||||
break;
|
||||
case ZERO:
|
||||
condtion_operator = "zero?";
|
||||
break;
|
||||
case NONZERO:
|
||||
condtion_operator = "nonzero?";
|
||||
break;
|
||||
case FALSE:
|
||||
condtion_operator = "not";
|
||||
break;
|
||||
case TRUTHY:
|
||||
condtion_operator = "";
|
||||
break;
|
||||
case ALWAYS:
|
||||
condtion_operator = "'#t";
|
||||
break;
|
||||
case FLOAT_EQUAL:
|
||||
condtion_operator = "=.f";
|
||||
break;
|
||||
case FLOAT_NOT_EQUAL:
|
||||
condtion_operator = "!=.f";
|
||||
break;
|
||||
case FLOAT_LESS_THAN:
|
||||
condtion_operator = "<.f";
|
||||
break;
|
||||
case FLOAT_GEQ:
|
||||
condtion_operator = ">=.f";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (nargs == 2) {
|
||||
return buildList(toForm(condtion_operator), src0->to_form(file), src1->to_form(file));
|
||||
} else if (nargs == 1) {
|
||||
if (condtion_operator.empty()) {
|
||||
return src0->to_form(file);
|
||||
} else {
|
||||
return buildList(toForm(condtion_operator), src0->to_form(file));
|
||||
}
|
||||
} else if (nargs == 0) {
|
||||
return toForm(condtion_operator);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Branch::to_form(const LinkedObjectFile& file) const {
|
||||
return buildList(toForm(likely ? "bl!" : "b!"), condition.to_form(file),
|
||||
toForm(file.get_label_name(dest_label_idx)), branch_delay.to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Compare::to_form(const LinkedObjectFile& file) const {
|
||||
return condition.to_form(file);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Suspend::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return buildList("suspend!");
|
||||
}
|
243
decompiler/IR/IR.h
Normal file
243
decompiler/IR/IR.h
Normal file
|
@ -0,0 +1,243 @@
|
|||
#ifndef JAK_IR_H
|
||||
#define JAK_IR_H
|
||||
|
||||
#include <cassert>
|
||||
#include "decompiler/Disasm/Register.h"
|
||||
#include "decompiler/util/LispPrint.h"
|
||||
|
||||
class LinkedObjectFile;
|
||||
|
||||
class IR {
|
||||
public:
|
||||
virtual std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const = 0;
|
||||
std::string print(const LinkedObjectFile& file) const;
|
||||
|
||||
bool is_basic_op = false;
|
||||
};
|
||||
|
||||
class IR_Failed : public IR {
|
||||
public:
|
||||
IR_Failed() = default;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Register : public IR {
|
||||
public:
|
||||
IR_Register(Register _reg, int _instr_idx) : reg(_reg), instr_idx(_instr_idx) {}
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
Register reg;
|
||||
int instr_idx = -1;
|
||||
};
|
||||
|
||||
class IR_Set : public IR {
|
||||
public:
|
||||
enum Kind {
|
||||
REG_64,
|
||||
LOAD,
|
||||
STORE,
|
||||
SYM_LOAD,
|
||||
SYM_STORE,
|
||||
FPR_TO_GPR64,
|
||||
GPR_TO_FPR,
|
||||
REG_FLT,
|
||||
REG_I128
|
||||
} kind;
|
||||
IR_Set(Kind _kind, std::shared_ptr<IR> _dst, std::shared_ptr<IR> _src)
|
||||
: kind(_kind), dst(std::move(_dst)), src(std::move(_src)) {}
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
std::shared_ptr<IR> dst, src;
|
||||
std::shared_ptr<IR> clobber = nullptr;
|
||||
};
|
||||
|
||||
class IR_Store : public IR_Set {
|
||||
public:
|
||||
enum Kind { INTEGER, FLOAT } kind;
|
||||
IR_Store(Kind _kind, std::shared_ptr<IR> _dst, std::shared_ptr<IR> _src, int _size)
|
||||
: IR_Set(IR_Set::LOAD, std::move(_dst), std::move(_src)), kind(_kind), size(_size) {}
|
||||
int size;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Symbol : public IR {
|
||||
public:
|
||||
IR_Symbol(std::string _name) : name(std::move(_name)) {}
|
||||
std::string name;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_SymbolValue : public IR {
|
||||
public:
|
||||
IR_SymbolValue(std::string _name) : name(std::move(_name)) {}
|
||||
std::string name;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_StaticAddress : public IR {
|
||||
public:
|
||||
IR_StaticAddress(int _label_id) : label_id(_label_id) {}
|
||||
int label_id = -1;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Load : public IR {
|
||||
public:
|
||||
enum Kind { UNSIGNED, SIGNED, FLOAT } kind;
|
||||
|
||||
IR_Load(Kind _kind, int _size, const std::shared_ptr<IR>& _location)
|
||||
: kind(_kind), size(_size), location(_location) {}
|
||||
int size;
|
||||
std::shared_ptr<IR> location;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_FloatMath2 : public IR {
|
||||
public:
|
||||
enum Kind { DIV, MUL, ADD, SUB, MIN, MAX } kind;
|
||||
IR_FloatMath2(Kind _kind, std::shared_ptr<IR> _arg0, std::shared_ptr<IR> _arg1)
|
||||
: kind(_kind), arg0(std::move(_arg0)), arg1(std::move(_arg1)) {}
|
||||
std::shared_ptr<IR> arg0, arg1;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_FloatMath1 : public IR {
|
||||
public:
|
||||
enum Kind { FLOAT_TO_INT, INT_TO_FLOAT, ABS, NEG, SQRT } kind;
|
||||
IR_FloatMath1(Kind _kind, std::shared_ptr<IR> _arg) : kind(_kind), arg(std::move(_arg)) {}
|
||||
std::shared_ptr<IR> arg;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_IntMath2 : public IR {
|
||||
public:
|
||||
enum Kind {
|
||||
ADD,
|
||||
SUB,
|
||||
MUL_SIGNED,
|
||||
DIV_SIGNED,
|
||||
MOD_SIGNED,
|
||||
DIV_UNSIGNED,
|
||||
MOD_UNSIGNED,
|
||||
OR,
|
||||
AND,
|
||||
NOR,
|
||||
XOR,
|
||||
LEFT_SHIFT,
|
||||
RIGHT_SHIFT_ARITH,
|
||||
RIGHT_SHIFT_LOGIC,
|
||||
MUL_UNSIGNED
|
||||
} kind;
|
||||
IR_IntMath2(Kind _kind, std::shared_ptr<IR> _arg0, std::shared_ptr<IR> _arg1)
|
||||
: kind(_kind), arg0(std::move(_arg0)), arg1(std::move(_arg1)) {}
|
||||
std::shared_ptr<IR> arg0, arg1;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_IntMath1 : public IR {
|
||||
public:
|
||||
enum Kind { NOT } kind;
|
||||
IR_IntMath1(Kind _kind, std::shared_ptr<IR> _arg) : kind(_kind), arg(std::move(_arg)) {}
|
||||
std::shared_ptr<IR> arg;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Call : public IR {
|
||||
public:
|
||||
IR_Call() = default;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_IntegerConstant : public IR {
|
||||
public:
|
||||
int64_t value;
|
||||
explicit IR_IntegerConstant(int64_t _value) : value(_value) {}
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
struct BranchDelay {
|
||||
enum Kind { NOP, SET_REG_FALSE, SET_REG_TRUE, SET_REG_REG, UNKNOWN } kind;
|
||||
std::shared_ptr<IR> destination = nullptr, source = nullptr;
|
||||
BranchDelay(Kind _kind) : kind(_kind) {}
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const;
|
||||
};
|
||||
|
||||
struct Condition {
|
||||
enum Kind {
|
||||
NOT_EQUAL,
|
||||
EQUAL,
|
||||
LESS_THAN_SIGNED,
|
||||
GREATER_THAN_SIGNED,
|
||||
LEQ_SIGNED,
|
||||
GEQ_SIGNED,
|
||||
LESS_THAN_UNSIGNED,
|
||||
GREATER_THAN_UNSIGNED,
|
||||
LEQ_UNSIGNED,
|
||||
GEQ_UNSIGNED,
|
||||
ZERO,
|
||||
NONZERO,
|
||||
FALSE,
|
||||
TRUTHY,
|
||||
ALWAYS,
|
||||
FLOAT_EQUAL,
|
||||
FLOAT_NOT_EQUAL,
|
||||
FLOAT_LESS_THAN,
|
||||
FLOAT_GEQ
|
||||
} kind;
|
||||
|
||||
Condition(Kind _kind,
|
||||
std::shared_ptr<IR> _src0,
|
||||
std::shared_ptr<IR> _src1,
|
||||
std::shared_ptr<IR> _clobber)
|
||||
: kind(_kind), src0(std::move(_src0)), src1(std::move(_src1)), clobber(std::move(_clobber)) {
|
||||
int nargs = num_args();
|
||||
if (nargs == 2) {
|
||||
assert(src0 && src1);
|
||||
} else if (nargs == 1) {
|
||||
assert(src0 && !src1);
|
||||
} else if (nargs == 0) {
|
||||
assert(!src0 && !src1);
|
||||
}
|
||||
}
|
||||
|
||||
int num_args() const;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const;
|
||||
std::shared_ptr<IR> src0, src1, clobber;
|
||||
};
|
||||
|
||||
class IR_Branch : public IR {
|
||||
public:
|
||||
IR_Branch(Condition _condition, int _dest_label_idx, BranchDelay _branch_delay, bool _likely)
|
||||
: condition(std::move(_condition)),
|
||||
dest_label_idx(_dest_label_idx),
|
||||
branch_delay(std::move(_branch_delay)),
|
||||
likely(_likely) {}
|
||||
|
||||
Condition condition;
|
||||
int dest_label_idx;
|
||||
BranchDelay branch_delay;
|
||||
bool likely;
|
||||
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Compare : public IR {
|
||||
public:
|
||||
explicit IR_Compare(Condition _condition) : condition(std::move(_condition)) {}
|
||||
|
||||
Condition condition;
|
||||
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Nop : public IR {
|
||||
public:
|
||||
IR_Nop() = default;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Suspend : public IR {
|
||||
public:
|
||||
IR_Suspend() = default;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
#endif // JAK_IR_H
|
|
@ -483,6 +483,8 @@ void LinkedObjectFile::process_fp_relative_links() {
|
|||
if (pprev_instr && pprev_instr->kind == InstructionKind::LUI) {
|
||||
assert(pprev_instr->get_dst(0).get_reg() == offset_reg);
|
||||
additional_offset = (1 << 16) * pprev_instr->get_imm_src().get_imm();
|
||||
pprev_instr->get_imm_src().set_label(
|
||||
get_label_id_for(seg, current_fp + atom.get_imm() + additional_offset));
|
||||
}
|
||||
atom.set_label(
|
||||
get_label_id_for(seg, current_fp + atom.get_imm() + additional_offset));
|
||||
|
@ -554,6 +556,12 @@ std::string LinkedObjectFile::print_disassembly() {
|
|||
auto& word = words_by_seg[seg].at(func.start_word + i);
|
||||
append_word_to_string(result, word);
|
||||
} else {
|
||||
if (func.has_basic_ops() && func.instr_starts_basic_op(i)) {
|
||||
if (line.length() < 40) {
|
||||
line.append(40 - line.length(), ' ');
|
||||
}
|
||||
line += ";; " + func.get_basic_op_at_instr(i)->print(*this);
|
||||
}
|
||||
result += line + "\n";
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <cstring>
|
||||
#include "LinkedObjectFileCreation.h"
|
||||
#include "decompiler/config.h"
|
||||
#include "decompiler/TypeSystem/TypeInfo.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
|
||||
// There are three link versions:
|
||||
// V2 - not really in use anymore, but V4 will resue logic from it (and the game didn't rename the
|
||||
|
@ -86,8 +86,9 @@ static uint32_t c_symlink2(LinkedObjectFile& f,
|
|||
uint32_t link_ptr_offset,
|
||||
SymbolLinkKind kind,
|
||||
const char* name,
|
||||
int seg_id) {
|
||||
get_type_info().inform_symbol_with_no_type_info(name);
|
||||
int seg_id,
|
||||
DecompilerTypeSystem& dts) {
|
||||
dts.add_symbol(name);
|
||||
auto initial_offset = code_ptr_offset;
|
||||
do {
|
||||
auto table_value = data.at(link_ptr_offset);
|
||||
|
@ -130,7 +131,7 @@ static uint32_t c_symlink2(LinkedObjectFile& f,
|
|||
word_kind = LinkedWord::EMPTY_PTR;
|
||||
break;
|
||||
case SymbolLinkKind::TYPE:
|
||||
get_type_info().inform_type(name);
|
||||
dts.add_symbol(name, "type");
|
||||
word_kind = LinkedWord::TYPE_PTR;
|
||||
break;
|
||||
default:
|
||||
|
@ -162,8 +163,9 @@ static uint32_t c_symlink3(LinkedObjectFile& f,
|
|||
uint32_t link_ptr,
|
||||
SymbolLinkKind kind,
|
||||
const char* name,
|
||||
int seg) {
|
||||
get_type_info().inform_symbol_with_no_type_info(name);
|
||||
int seg,
|
||||
DecompilerTypeSystem& dts) {
|
||||
dts.add_symbol(name);
|
||||
auto initial_offset = code_ptr;
|
||||
do {
|
||||
// seek, with a variable length encoding that sucks.
|
||||
|
@ -187,7 +189,7 @@ static uint32_t c_symlink3(LinkedObjectFile& f,
|
|||
word_kind = LinkedWord::EMPTY_PTR;
|
||||
break;
|
||||
case SymbolLinkKind::TYPE:
|
||||
get_type_info().inform_type(name);
|
||||
dts.add_symbol(name, "type");
|
||||
word_kind = LinkedWord::TYPE_PTR;
|
||||
break;
|
||||
default:
|
||||
|
@ -223,7 +225,8 @@ static uint32_t align16(uint32_t in) {
|
|||
*/
|
||||
static void link_v4(LinkedObjectFile& f,
|
||||
const std::vector<uint8_t>& data,
|
||||
const std::string& name) {
|
||||
const std::string& name,
|
||||
DecompilerTypeSystem& dts) {
|
||||
// read the V4 header to find where the link data really is
|
||||
const auto* header = (const LinkHeaderV4*)&data.at(0);
|
||||
uint32_t link_data_offset = header->code_size + sizeof(LinkHeaderV4); // no basic offset
|
||||
|
@ -358,7 +361,7 @@ static void link_v4(LinkedObjectFile& f,
|
|||
|
||||
link_ptr_offset += strlen(s_name) + 1;
|
||||
f.stats.total_v2_symbol_count++;
|
||||
link_ptr_offset = c_symlink2(f, data, code_offset, link_ptr_offset, kind, s_name, 0);
|
||||
link_ptr_offset = c_symlink2(f, data, code_offset, link_ptr_offset, kind, s_name, 0, dts);
|
||||
if (data.at(link_ptr_offset) == 0)
|
||||
break;
|
||||
}
|
||||
|
@ -384,7 +387,8 @@ static void assert_string_empty_after(const char* str, int size) {
|
|||
|
||||
static void link_v5(LinkedObjectFile& f,
|
||||
const std::vector<uint8_t>& data,
|
||||
const std::string& name) {
|
||||
const std::string& name,
|
||||
DecompilerTypeSystem& dts) {
|
||||
auto header = (const LinkHeaderV5*)(&data.at(0));
|
||||
if (header->n_segments == 1) {
|
||||
printf("abandon %s!\n", name.c_str());
|
||||
|
@ -539,10 +543,10 @@ static void link_v5(LinkedObjectFile& f,
|
|||
|
||||
if (std::string("_empty_") == sname) {
|
||||
link_ptr = c_symlink2(f, data, segment_data_offsets[seg_id], link_ptr,
|
||||
SymbolLinkKind::EMPTY_LIST, sname, seg_id);
|
||||
SymbolLinkKind::EMPTY_LIST, sname, seg_id, dts);
|
||||
} else {
|
||||
link_ptr = c_symlink2(f, data, segment_data_offsets[seg_id], link_ptr,
|
||||
SymbolLinkKind::SYMBOL, sname, seg_id);
|
||||
SymbolLinkKind::SYMBOL, sname, seg_id, dts);
|
||||
}
|
||||
} else if ((reloc & 0x3f) == 0x3f) {
|
||||
assert(false); // todo, does this ever get hit?
|
||||
|
@ -556,7 +560,7 @@ static void link_v5(LinkedObjectFile& f,
|
|||
const char* sname = (const char*)(&data.at(link_ptr));
|
||||
link_ptr += strlen(sname) + 1;
|
||||
link_ptr = c_symlink2(f, data, segment_data_offsets[seg_id], link_ptr,
|
||||
SymbolLinkKind::TYPE, sname, seg_id);
|
||||
SymbolLinkKind::TYPE, sname, seg_id, dts);
|
||||
}
|
||||
|
||||
sub_link_ptr = link_ptr;
|
||||
|
@ -586,7 +590,8 @@ static void link_v5(LinkedObjectFile& f,
|
|||
|
||||
static void link_v3(LinkedObjectFile& f,
|
||||
const std::vector<uint8_t>& data,
|
||||
const std::string& name) {
|
||||
const std::string& name,
|
||||
DecompilerTypeSystem& dts) {
|
||||
auto header = (const LinkHeaderV3*)(&data.at(0));
|
||||
assert(name == header->name);
|
||||
assert(header->segments == 3);
|
||||
|
@ -739,7 +744,7 @@ static void link_v3(LinkedObjectFile& f,
|
|||
// methods todo
|
||||
|
||||
s_name = (const char*)(&data.at(link_ptr));
|
||||
get_type_info().inform_type_method_count(s_name, reloc & 0x7f);
|
||||
// get_type_info().inform_type_method_count(s_name, reloc & 0x7f); todo
|
||||
kind = SymbolLinkKind::TYPE;
|
||||
}
|
||||
|
||||
|
@ -750,7 +755,7 @@ static void link_v3(LinkedObjectFile& f,
|
|||
|
||||
link_ptr += strlen(s_name) + 1;
|
||||
f.stats.v3_symbol_count++;
|
||||
link_ptr = c_symlink3(f, data, base_ptr, link_ptr, kind, s_name, seg_id);
|
||||
link_ptr = c_symlink3(f, data, base_ptr, link_ptr, kind, s_name, seg_id, dts);
|
||||
}
|
||||
segment_link_ends[seg_id] = link_ptr;
|
||||
}
|
||||
|
@ -775,19 +780,21 @@ static void link_v3(LinkedObjectFile& f,
|
|||
/*!
|
||||
* Main function to generate LinkedObjectFiles from raw object data.
|
||||
*/
|
||||
LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data, const std::string& name) {
|
||||
LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data,
|
||||
const std::string& name,
|
||||
DecompilerTypeSystem& dts) {
|
||||
LinkedObjectFile result;
|
||||
const auto* header = (const LinkHeaderCommon*)&data.at(0);
|
||||
|
||||
// use appropriate linker
|
||||
if (header->version == 3) {
|
||||
assert(header->type_tag == 0);
|
||||
link_v3(result, data, name);
|
||||
link_v3(result, data, name, dts);
|
||||
} else if (header->version == 4) {
|
||||
assert(header->type_tag == 0xffffffff);
|
||||
link_v4(result, data, name);
|
||||
link_v4(result, data, name, dts);
|
||||
} else if (header->version == 5) {
|
||||
link_v5(result, data, name);
|
||||
link_v5(result, data, name, dts);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
#include "LinkedObjectFile.h"
|
||||
|
||||
LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data, const std::string& name);
|
||||
class DecompilerTypeSystem;
|
||||
LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data,
|
||||
const std::string& name,
|
||||
DecompilerTypeSystem& dts);
|
||||
|
||||
#endif // NEXT_LINKEDOBJECTFILECREATION_H
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "common/util/Timer.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "decompiler/Function/BasicBlocks.h"
|
||||
#include "decompiler/IR/BasicOpBuilder.h"
|
||||
|
||||
/*!
|
||||
* Get a unique name for this object file.
|
||||
|
@ -64,6 +65,8 @@ ObjectFileData& ObjectFileDB::lookup_record(ObjectFileRecord rec) {
|
|||
*/
|
||||
ObjectFileDB::ObjectFileDB(const std::vector<std::string>& _dgos) {
|
||||
Timer timer;
|
||||
printf("- Loading Types...\n");
|
||||
dts.parse_type_defs({"decompiler", "config", "all-types.gc"});
|
||||
|
||||
printf("- Initializing ObjectFileDB...\n");
|
||||
for (auto& dgo : _dgos) {
|
||||
|
@ -71,7 +74,7 @@ ObjectFileDB::ObjectFileDB(const std::vector<std::string>& _dgos) {
|
|||
}
|
||||
|
||||
printf("ObjectFileDB Initialized:\n");
|
||||
printf(" total dgos: %lld\n", _dgos.size());
|
||||
printf(" total dgos: %d\n", int(_dgos.size()));
|
||||
printf(" total data: %d bytes\n", stats.total_dgo_bytes);
|
||||
printf(" total objs: %d\n", stats.total_obj_files);
|
||||
printf(" unique objs: %d\n", stats.unique_obj_files);
|
||||
|
@ -356,7 +359,7 @@ void ObjectFileDB::process_link_data() {
|
|||
LinkedObjectFile::Stats combined_stats;
|
||||
|
||||
for_each_obj([&](ObjectFileData& obj) {
|
||||
obj.linked_data = to_linked_object_file(obj.data, obj.record.name);
|
||||
obj.linked_data = to_linked_object_file(obj.data, obj.record.name, dts);
|
||||
combined_stats.add(obj.linked_data.stats);
|
||||
});
|
||||
|
||||
|
@ -543,7 +546,7 @@ void ObjectFileDB::analyze_functions() {
|
|||
auto& func = data.linked_data.functions_by_seg.at(2).front();
|
||||
assert(func.guessed_name.empty());
|
||||
func.guessed_name.set_as_top_level();
|
||||
func.find_global_function_defs(data.linked_data);
|
||||
func.find_global_function_defs(data.linked_data, dts);
|
||||
func.find_method_defs(data.linked_data);
|
||||
}
|
||||
});
|
||||
|
@ -591,6 +594,8 @@ void ObjectFileDB::analyze_functions() {
|
|||
int total_nontrivial_functions = 0;
|
||||
int total_resolved_nontrivial_functions = 0;
|
||||
int total_named_functions = 0;
|
||||
int total_basic_ops = 0;
|
||||
int total_failed_basic_ops = 0;
|
||||
|
||||
std::map<int, std::vector<std::string>> unresolved_by_length;
|
||||
if (get_config().find_basic_blocks) {
|
||||
|
@ -601,10 +606,18 @@ void ObjectFileDB::analyze_functions() {
|
|||
total_basic_blocks += blocks.size();
|
||||
func.basic_blocks = blocks;
|
||||
|
||||
total_functions++;
|
||||
if (!func.suspected_asm) {
|
||||
func.analyze_prologue(data.linked_data);
|
||||
func.cfg = build_cfg(data.linked_data, segment_id, func);
|
||||
total_functions++;
|
||||
for (auto& block : func.basic_blocks) {
|
||||
if (block.end_word > block.start_word) {
|
||||
add_basic_ops_to_block(&func, block, &data.linked_data);
|
||||
}
|
||||
}
|
||||
total_basic_ops += func.get_basic_op_count();
|
||||
total_failed_basic_ops += func.get_failed_basic_op_count();
|
||||
|
||||
if (func.cfg->is_fully_resolved()) {
|
||||
resolved_cfg_functions++;
|
||||
}
|
||||
|
@ -640,11 +653,15 @@ void ObjectFileDB::analyze_functions() {
|
|||
total_nontrivial_functions,
|
||||
100.f * float(total_resolved_nontrivial_functions) / float(total_nontrivial_functions));
|
||||
|
||||
for (auto& kv : unresolved_by_length) {
|
||||
printf("LEN %d\n", kv.first);
|
||||
for (auto& x : kv.second) {
|
||||
printf(" %s\n", x.c_str());
|
||||
}
|
||||
}
|
||||
int successful_basic_ops = total_basic_ops - total_failed_basic_ops;
|
||||
printf(" %d/%d basic ops converted successfully (%.2f%%)\n", successful_basic_ops,
|
||||
total_basic_ops, 100.f * float(successful_basic_ops) / float(total_basic_ops));
|
||||
|
||||
// for (auto& kv : unresolved_by_length) {
|
||||
// printf("LEN %d\n", kv.first);
|
||||
// for (auto& x : kv.second) {
|
||||
// printf(" %s\n", x.c_str());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "LinkedObjectFile.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
|
||||
/*!
|
||||
* A "record" which can be used to identify an object file.
|
||||
|
@ -55,6 +56,7 @@ class ObjectFileDB {
|
|||
void write_disassembly(const std::string& output_dir, bool disassemble_objects_without_functions);
|
||||
void analyze_functions();
|
||||
ObjectFileData& lookup_record(ObjectFileRecord rec);
|
||||
DecompilerTypeSystem dts;
|
||||
|
||||
private:
|
||||
void get_objs_from_dgo(const std::string& filename);
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
#include "GoalFunction.h"
|
|
@ -1,17 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_GOALFUNCTION_H
|
||||
#define JAK_DISASSEMBLER_GOALFUNCTION_H
|
||||
|
||||
class GoalFunction {
|
||||
public:
|
||||
// enum Kind {
|
||||
// GLOBAL_FUNCTION,
|
||||
// ANON_FUNCTION,
|
||||
// METHOD,
|
||||
// BEHAVIOR,
|
||||
// UNKNOWN
|
||||
// };
|
||||
};
|
||||
|
||||
#endif // JAK_DISASSEMBLER_GOALFUNCTION_H
|
|
@ -1 +0,0 @@
|
|||
#include "GoalSymbol.h"
|
|
@ -1,39 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_GOALSYMBOL_H
|
||||
#define JAK_DISASSEMBLER_GOALSYMBOL_H
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include "TypeSpec.h"
|
||||
|
||||
class GoalSymbol {
|
||||
public:
|
||||
GoalSymbol() = default;
|
||||
explicit GoalSymbol(std::string name) : m_name(std::move(name)) {}
|
||||
GoalSymbol(std::string name, TypeSpec ts) : m_name(std::move(name)), m_type(std::move(ts)) {
|
||||
m_has_type_info = true;
|
||||
}
|
||||
|
||||
bool has_type_info() const { return m_has_type_info; }
|
||||
|
||||
void set_type(TypeSpec ts) {
|
||||
if (m_has_type_info) {
|
||||
if (ts != m_type) {
|
||||
printf("symbol %s %s -> %s", m_name.c_str(), m_type.to_string().c_str(),
|
||||
ts.to_string().c_str());
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
m_has_type_info = true;
|
||||
m_type = std::move(ts);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
TypeSpec m_type;
|
||||
bool m_has_type_info = false;
|
||||
};
|
||||
|
||||
#endif // JAK_DISASSEMBLER_GOALSYMBOL_H
|
|
@ -1,13 +0,0 @@
|
|||
#include "GoalType.h"
|
||||
|
||||
void GoalType::set_methods(int n) {
|
||||
if (m_method_count_set) {
|
||||
if (m_method_count != n) {
|
||||
printf("Type %s had %d methods, set_methods tried to change it to %d\n", m_name.c_str(),
|
||||
m_method_count, n);
|
||||
}
|
||||
} else {
|
||||
m_method_count = n;
|
||||
m_method_count_set = true;
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_GOALTYPE_H
|
||||
#define JAK_DISASSEMBLER_GOALTYPE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class GoalType {
|
||||
public:
|
||||
GoalType() = default;
|
||||
GoalType(std::string name) : m_name(std::move(name)) {}
|
||||
bool has_info() const { return m_has_info; }
|
||||
|
||||
bool has_method_count() const { return m_method_count_set; }
|
||||
|
||||
void set_methods(int n);
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
bool m_has_info = false;
|
||||
bool m_method_count_set = false;
|
||||
int m_method_count = -1;
|
||||
};
|
||||
|
||||
#endif // JAK_DISASSEMBLER_GOALTYPE_H
|
|
@ -1,99 +0,0 @@
|
|||
#include "TypeInfo.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
TypeInfo gTypeInfo;
|
||||
}
|
||||
|
||||
TypeInfo::TypeInfo() {
|
||||
GoalType type_type("type");
|
||||
m_types["type"] = type_type;
|
||||
GoalSymbol type_symbol("type");
|
||||
m_symbols["type"] = type_symbol;
|
||||
}
|
||||
|
||||
TypeInfo& get_type_info() {
|
||||
return gTypeInfo;
|
||||
}
|
||||
|
||||
std::string TypeInfo::get_summary() {
|
||||
int total_symbols = 0;
|
||||
int syms_with_type_info = 0;
|
||||
for (const auto& kv : m_symbols) {
|
||||
total_symbols++;
|
||||
if (kv.second.has_type_info()) {
|
||||
syms_with_type_info++;
|
||||
}
|
||||
}
|
||||
|
||||
int total_types = 0;
|
||||
int types_with_info = 0;
|
||||
int types_with_method_count = 0;
|
||||
for (const auto& kv : m_types) {
|
||||
total_types++;
|
||||
if (kv.second.has_info()) {
|
||||
types_with_info++;
|
||||
}
|
||||
if (kv.second.has_method_count()) {
|
||||
types_with_method_count++;
|
||||
}
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
sprintf(buffer,
|
||||
"TypeInfo Summary\n"
|
||||
" Total Symbols: %d\n"
|
||||
" with type info: %d (%.2f%%)\n"
|
||||
" Total Types: %d\n"
|
||||
" with info: %d (%.2f%%)\n"
|
||||
" with method count: %d (%.2f%%)\n",
|
||||
total_symbols, syms_with_type_info,
|
||||
100.f * float(syms_with_type_info) / float(total_symbols), total_types, types_with_info,
|
||||
100.f * float(types_with_info) / float(total_types), types_with_method_count,
|
||||
100.f * float(types_with_method_count) / float(total_types));
|
||||
|
||||
return {buffer};
|
||||
}
|
||||
|
||||
/*!
|
||||
* inform TypeInfo that there is a symbol with this name.
|
||||
* Provides no type info - if some is already known there is no change.
|
||||
*/
|
||||
void TypeInfo::inform_symbol_with_no_type_info(const std::string& name) {
|
||||
if (m_symbols.find(name) == m_symbols.end()) {
|
||||
// only add it if we haven't seen this already.
|
||||
GoalSymbol sym(name);
|
||||
m_symbols[name] = sym;
|
||||
}
|
||||
}
|
||||
|
||||
void TypeInfo::inform_symbol(const std::string& name, TypeSpec type) {
|
||||
inform_symbol_with_no_type_info(name);
|
||||
m_symbols.at(name).set_type(std::move(type));
|
||||
}
|
||||
|
||||
void TypeInfo::inform_type(const std::string& name) {
|
||||
if (m_types.find(name) == m_types.end()) {
|
||||
GoalType typ(name);
|
||||
m_types[name] = typ;
|
||||
}
|
||||
inform_symbol(name, TypeSpec("type"));
|
||||
}
|
||||
|
||||
void TypeInfo::inform_type_method_count(const std::string& name, int methods) {
|
||||
// create type and symbol
|
||||
inform_type(name);
|
||||
m_types.at(name).set_methods(methods);
|
||||
}
|
||||
|
||||
std::string TypeInfo::get_all_symbols_debug() {
|
||||
std::string result = "const char* all_syms[" + std::to_string(m_symbols.size()) + "] = {";
|
||||
for (auto& x : m_symbols) {
|
||||
result += "\"" + x.first + "\",";
|
||||
}
|
||||
if (!result.empty()) {
|
||||
result.pop_back();
|
||||
}
|
||||
return result + "};";
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_TYPEINFO_H
|
||||
#define JAK_DISASSEMBLER_TYPEINFO_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include "GoalType.h"
|
||||
#include "GoalFunction.h"
|
||||
#include "GoalSymbol.h"
|
||||
|
||||
class TypeInfo {
|
||||
public:
|
||||
TypeInfo();
|
||||
|
||||
void inform_symbol(const std::string& name, TypeSpec type);
|
||||
void inform_symbol_with_no_type_info(const std::string& name);
|
||||
void inform_type(const std::string& name);
|
||||
void inform_type_method_count(const std::string& name, int methods);
|
||||
|
||||
std::string get_summary();
|
||||
std::string get_all_symbols_debug();
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, GoalType> m_types;
|
||||
std::unordered_map<std::string, GoalFunction> m_global_functions;
|
||||
std::unordered_map<std::string, GoalSymbol> m_symbols;
|
||||
};
|
||||
|
||||
TypeInfo& get_type_info();
|
||||
void init_type_info();
|
||||
|
||||
#endif // JAK_DISASSEMBLER_TYPEINFO_H
|
|
@ -1,51 +0,0 @@
|
|||
#include "TypeSpec.h"
|
||||
|
||||
std::string TypeSpec::to_string() const {
|
||||
if (m_args.empty()) {
|
||||
return m_base_type;
|
||||
} else {
|
||||
std::string result = "(";
|
||||
result += m_base_type;
|
||||
for (const auto& x : m_args) {
|
||||
result += " ";
|
||||
result += x.to_string();
|
||||
}
|
||||
result += ")";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> TypeSpec::to_form() const {
|
||||
if (m_args.empty()) {
|
||||
return toForm(m_base_type);
|
||||
} else {
|
||||
std::vector<std::shared_ptr<Form>> all;
|
||||
all.push_back(toForm(m_base_type));
|
||||
for (const auto& x : m_args) {
|
||||
all.push_back(x.to_form());
|
||||
}
|
||||
return buildList(all);
|
||||
}
|
||||
}
|
||||
|
||||
bool TypeSpec::operator==(const TypeSpec& other) const {
|
||||
if (m_base_type != other.m_base_type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_args.size() != other.m_args.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_args.size(); i++) {
|
||||
if (m_args[i] != other.m_args[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TypeSpec::operator!=(const TypeSpec& other) const {
|
||||
return !(*this == other);
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_TYPESPEC_H
|
||||
#define JAK_DISASSEMBLER_TYPESPEC_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "decompiler/util/LispPrint.h"
|
||||
|
||||
class TypeSpec {
|
||||
public:
|
||||
TypeSpec() = default;
|
||||
explicit TypeSpec(std::string base_type) : m_base_type(std::move(base_type)) {}
|
||||
TypeSpec(std::string base_type, std::vector<TypeSpec> args)
|
||||
: m_base_type(std::move(base_type)), m_args(std::move(args)) {}
|
||||
|
||||
std::string to_string() const;
|
||||
std::shared_ptr<Form> to_form() const;
|
||||
|
||||
bool operator==(const TypeSpec& other) const;
|
||||
bool operator!=(const TypeSpec& other) const;
|
||||
|
||||
private:
|
||||
std::string m_base_type;
|
||||
std::vector<TypeSpec> m_args;
|
||||
};
|
||||
|
||||
#endif // JAK_DISASSEMBLER_TYPESPEC_H
|
7965
decompiler/config/all-types.gc
Normal file
7965
decompiler/config/all-types.gc
Normal file
File diff suppressed because it is too large
Load diff
|
@ -3,13 +3,14 @@
|
|||
{
|
||||
"game_version":1,
|
||||
// the order here matters. KERNEL and GAME should go first
|
||||
"dgo_names":["CGO/KERNEL.CGO"
|
||||
, "CGO/GAME.CGO", "CGO/ENGINE.CGO"
|
||||
"dgo_names":["CGO/KERNEL.CGO", "CGO/GAME.CGO"],
|
||||
/*, "CGO/ENGINE.CGO"
|
||||
, "CGO/ART.CGO", "DGO/BEA.DGO", "DGO/CIT.DGO", "CGO/COMMON.CGO", "DGO/DAR.DGO", "DGO/DEM.DGO",
|
||||
"DGO/FIN.DGO", "DGO/INT.DGO", "DGO/JUB.DGO", "DGO/JUN.DGO", "CGO/JUNGLE.CGO", "CGO/L1.CGO", "DGO/FIC.DGO",
|
||||
"DGO/LAV.DGO", "DGO/MAI.DGO", "CGO/MAINCAVE.CGO", "DGO/MIS.DGO", "DGO/OGR.DGO", "CGO/RACERP.CGO", "DGO/ROB.DGO", "DGO/ROL.DGO",
|
||||
"DGO/SNO.DGO", "DGO/SUB.DGO", "DGO/SUN.DGO", "CGO/SUNKEN.CGO", "DGO/SWA.DGO", "DGO/TIT.DGO", "DGO/TRA.DGO", "DGO/VI1.DGO",
|
||||
"DGO/VI2.DGO", "DGO/VI3.DGO", "CGO/VILLAGEP.CGO", "CGO/WATER-AN.CGO"],
|
||||
"DGO/VI2.DGO", "DGO/VI3.DGO", "CGO/VILLAGEP.CGO", "CGO/WATER-AN.CGO"
|
||||
],*/
|
||||
|
||||
"write_disassembly":true,
|
||||
"write_hex_near_instructions":false,
|
||||
|
@ -29,9 +30,16 @@
|
|||
|
||||
"asm_functions_by_name":[
|
||||
// gcommon
|
||||
"ash", "abs", "min", "max", "collide-do-primitives", "draw-bones-check-longest-edge-asm",
|
||||
"ash", "abs", "min", "max", "(method 2 vec4s)", "quad-copy!", "(method 3 vec4s)", "breakpoint-range-set!",
|
||||
|
||||
// pskernel
|
||||
"resend-exception", "kernel-set-interrupt-vector", "kernel-set-exception-vector", "return-from-exception",
|
||||
"kernel-read", "kernel-read-function", "kernel-write", "kernel-write-function", "kernel-copy-to-kernel-ram",
|
||||
|
||||
"collide-do-primitives", "draw-bones-check-longest-edge-asm",
|
||||
"sp-launch-particles-var", "(method 15 collide-shape-prim-mesh)", "(method 15 collide-shape-prim-sphere)",
|
||||
"(method 45 collide-shape)", "cam-layout-save-cam-trans", "kernel-copy-function", "dma-sync-hang", "generic-no-light-dproc", "dma-sync-fast", "bsp-camera-asm",
|
||||
"(method 45 collide-shape)", "cam-layout-save-cam-trans", "kernel-copy-function", "dma-sync-hang", "generic-no-light-dproc",
|
||||
"dma-sync-fast", "bsp-camera-asm",
|
||||
"generic-none-dma-wait", "unpack-comp-rle", "level-remap-texture", "(method 10 collide-edge-hold-list)"
|
||||
]
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
#include "ObjectFile/ObjectFileDB.h"
|
||||
#include "config.h"
|
||||
#include "util/FileIO.h"
|
||||
#include "TypeSystem/TypeInfo.h"
|
||||
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
@ -48,8 +48,9 @@ int main(int argc, char** argv) {
|
|||
db.write_disassembly(out_folder, get_config().disassemble_objects_without_functions);
|
||||
}
|
||||
|
||||
printf("%s\n", get_type_info().get_summary().c_str());
|
||||
// printf("%d\n", InstructionKind::EE_OP_MAX);
|
||||
// printf("%s\n", get_type_info().get_all_symbols_debug().c_str());
|
||||
// todo print type summary
|
||||
// printf("%s\n", get_type_info().get_summary().c_str());
|
||||
|
||||
file_util::write_text_file(combine_path(out_folder, "all-syms.gc"), db.dts.dump_symbol_types());
|
||||
return 0;
|
||||
}
|
||||
|
|
78
decompiler/util/DecompilerTypeSystem.cpp
Normal file
78
decompiler/util/DecompilerTypeSystem.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include "DecompilerTypeSystem.h"
|
||||
#include "common/goos/Reader.h"
|
||||
#include "common/type_system/deftype.h"
|
||||
|
||||
DecompilerTypeSystem::DecompilerTypeSystem() {
|
||||
ts.add_builtin_types();
|
||||
}
|
||||
|
||||
namespace {
|
||||
// some utilities for parsing the type def file
|
||||
|
||||
goos::Object& car(goos::Object& pair) {
|
||||
if (pair.is_pair()) {
|
||||
return pair.as_pair()->car;
|
||||
} else {
|
||||
throw std::runtime_error("car called on something that wasn't a pair: " + pair.print());
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object& cdr(goos::Object& pair) {
|
||||
if (pair.is_pair()) {
|
||||
return pair.as_pair()->cdr;
|
||||
} else {
|
||||
throw std::runtime_error("cdr called on something that wasn't a pair");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void for_each_in_list(goos::Object& list, T f) {
|
||||
goos::Object* iter = &list;
|
||||
while (iter->is_pair()) {
|
||||
f(car(*iter));
|
||||
iter = &cdr(*iter);
|
||||
}
|
||||
|
||||
if (!iter->is_empty_list()) {
|
||||
throw std::runtime_error("malformed list");
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void DecompilerTypeSystem::parse_type_defs(const std::vector<std::string>& file_path) {
|
||||
goos::Reader reader;
|
||||
auto read = reader.read_from_file(file_path);
|
||||
auto data = cdr(read);
|
||||
|
||||
for_each_in_list(data, [&](goos::Object& o) {
|
||||
if (car(o).as_symbol()->name == "define-extern") {
|
||||
auto* rest = &cdr(o);
|
||||
auto sym_name = car(*rest);
|
||||
rest = &cdr(*rest);
|
||||
auto sym_type = car(*rest);
|
||||
if (!cdr(*rest).is_empty_list()) {
|
||||
throw std::runtime_error("malformed define-extern");
|
||||
}
|
||||
add_symbol(sym_name.as_symbol()->name, parse_typespec(&ts, sym_type));
|
||||
|
||||
} else if (car(o).as_symbol()->name == "deftype") {
|
||||
parse_deftype(cdr(o), &ts);
|
||||
} else {
|
||||
throw std::runtime_error("Decompiler cannot parse " + car(o).print());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::string DecompilerTypeSystem::dump_symbol_types() {
|
||||
assert(symbol_add_order.size() == symbols.size());
|
||||
std::string result;
|
||||
for (auto& symbol_name : symbol_add_order) {
|
||||
auto skv = symbol_types.find(symbol_name);
|
||||
if (skv == symbol_types.end()) {
|
||||
result += fmt::format(";;(define-extern {} object) ;; unknown type\n", symbol_name);
|
||||
} else {
|
||||
result += fmt::format("(define-extern {} {})\n", symbol_name, skv->second.print());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
46
decompiler/util/DecompilerTypeSystem.h
Normal file
46
decompiler/util/DecompilerTypeSystem.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#ifndef JAK_DECOMPILERTYPESYSTEM_H
|
||||
#define JAK_DECOMPILERTYPESYSTEM_H
|
||||
|
||||
#include "common/type_system/TypeSystem.h"
|
||||
#include "third-party/fmt/format.h"
|
||||
|
||||
class DecompilerTypeSystem {
|
||||
public:
|
||||
DecompilerTypeSystem();
|
||||
TypeSystem ts;
|
||||
std::unordered_map<std::string, TypeSpec> symbol_types;
|
||||
std::unordered_set<std::string> symbols;
|
||||
std::vector<std::string> symbol_add_order;
|
||||
|
||||
void add_symbol(const std::string& name) {
|
||||
if (symbols.find(name) == symbols.end()) {
|
||||
symbols.insert(name);
|
||||
symbol_add_order.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
void add_symbol(const std::string& name, const std::string& base_type) {
|
||||
add_symbol(name, TypeSpec(base_type));
|
||||
}
|
||||
|
||||
void add_symbol(const std::string& name, const TypeSpec& type_spec) {
|
||||
add_symbol(name);
|
||||
auto skv = symbol_types.find(name);
|
||||
if (skv == symbol_types.end() || skv->second == type_spec) {
|
||||
symbol_types[name] = type_spec;
|
||||
} else {
|
||||
if (ts.typecheck(type_spec, skv->second, "", false, false)) {
|
||||
} else {
|
||||
fmt::print("Attempting to redefine type of symbol {} from {} to {}\n", name,
|
||||
skv->second.print(), type_spec.print());
|
||||
throw std::runtime_error("Type redefinition");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parse_type_defs(const std::vector<std::string>& file_path);
|
||||
|
||||
std::string dump_symbol_types();
|
||||
};
|
||||
|
||||
#endif // JAK_DECOMPILERTYPESYSTEM_H
|
|
@ -514,3 +514,14 @@ std::shared_ptr<Form> buildList(std::vector<std::shared_ptr<Form>>& forms) {
|
|||
}
|
||||
return buildList(forms.data(), forms.size());
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> buildList(std::vector<std::string>& forms) {
|
||||
if (forms.empty()) {
|
||||
return gSymbolTable.getEmptyPair();
|
||||
}
|
||||
std::vector<std::shared_ptr<Form>> f;
|
||||
for (auto& x : forms) {
|
||||
f.push_back(toForm(x));
|
||||
}
|
||||
return buildList(f.data(), f.size());
|
||||
}
|
|
@ -122,6 +122,7 @@ std::shared_ptr<Form> buildList(const std::string& str);
|
|||
std::shared_ptr<Form> buildList(std::shared_ptr<Form> form);
|
||||
std::shared_ptr<Form> buildList(std::vector<std::shared_ptr<Form>>& forms);
|
||||
std::shared_ptr<Form> buildList(std::shared_ptr<Form>* forms, int count);
|
||||
std::shared_ptr<Form> buildList(std::vector<std::string>& forms);
|
||||
|
||||
template <typename... Args>
|
||||
std::shared_ptr<Form> buildList(const std::string& str, Args... rest) {
|
||||
|
|
Loading…
Reference in a new issue