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:
water111 2020-09-29 20:24:15 -04:00 committed by GitHub
parent c9b53d51ff
commit 376c273845
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 10346 additions and 392 deletions

View file

@ -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;

View file

@ -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);

View file

@ -3,4 +3,4 @@
# Directory of this script
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
$DIR/build/decompiler/decompiler $DIR/decompiler/config/jak1_ntsc_black_label.jsonc $DIR/iso_data $DIR/decompiler_out
$DIR/build/decompiler/decompiler $DIR/decompiler/config/jak1_ntsc_black_label.jsonc $DIR/iso_data $DIR/decompiler_out

View file

@ -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)

View file

@ -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;

View file

@ -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

View file

@ -1,3 +1,8 @@
/*!
* @file OpcodeInfo.cpp
* Decoding info for each opcode.
*/
#include "OpcodeInfo.h"
#include <cassert>

View file

@ -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

View file

@ -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();

View file

@ -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.
}
@ -549,4 +550,42 @@ 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;
}

View file

@ -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

File diff suppressed because it is too large Load diff

View 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
View 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
View 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

View file

@ -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";
}

View file

@ -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);
}

View file

@ -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

View file

@ -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());
// }
// }
}
}

View file

@ -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);

View file

@ -1 +0,0 @@
#include "GoalFunction.h"

View file

@ -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

View file

@ -1 +0,0 @@
#include "GoalSymbol.h"

View file

@ -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

View file

@ -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;
}
}

View file

@ -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

View file

@ -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 + "};";
}

View file

@ -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

View file

@ -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);
}

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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)"
]
}

View file

@ -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;
}

View 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;
}

View 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

View file

@ -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());
}

View file

@ -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) {