Merge remote-tracking branch 'water111/master' into generative-tests

This commit is contained in:
Tyler Wilding
2020-10-09 13:31:08 -04:00
67 changed files with 12989 additions and 1256 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
- name: Build Project
run: |
cd build
make -j
make -j4
- name: Run Tests
timeout-minutes: 5
run: ./test_code_coverage.sh
+4 -1
View File
@@ -1,3 +1,6 @@
[submodule "third-party/googletest"]
path = third-party/googletest
url = https://github.com/google/googletest.git
url = https://github.com/google/googletest.git
[submodule "third-party/spdlog"]
path = third-party/spdlog
url = https://github.com/gabime/spdlog.git
+14
View File
@@ -1,6 +1,9 @@
# Top Level CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(jak)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug")
endif()
set(CMAKE_CXX_STANDARD 17)
@@ -49,6 +52,14 @@ endif()
# includes relative to top level jak-project folder
include_directories(./)
# build spdlog as a shared library to improve compile times
# adding this as a SYSTEM include suppresses all the terrible warnings in spdlog
include_directories(SYSTEM third-party/spdlog/include)
# this makes spdlog generate a shared library that we can link against
set(SPDLOG_BUILD_SHARED ON)
# this makes the spdlog includes not use the header only version, making compiling faster
add_definitions(-DSPDLOG_COMPILED_LIB)
# build asset packer/unpacker
add_subdirectory(asset_tool)
@@ -85,6 +96,9 @@ add_subdirectory(third-party/minilzo)
# build format library
add_subdirectory(third-party/fmt)
# build spdlog library
add_subdirectory(third-party/spdlog)
# windows memory management lib
IF (WIN32)
add_subdirectory(third-party/mman)
+8 -1
View File
@@ -1,2 +1,9 @@
add_library(goos SHARED Object.cpp TextDB.cpp Reader.cpp Interpreter.cpp InterpreterEval.cpp)
IF (WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2")
ELSE()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
ENDIF()
add_library(goos SHARED Object.cpp TextDB.cpp Reader.cpp Interpreter.cpp InterpreterEval.cpp PrettyPrinter.cpp)
target_link_libraries(goos common_util fmt)
+1 -1
View File
@@ -637,4 +637,4 @@ Object Interpreter::eval_error(const Object& form,
throw_eval_error(form, "Error: " + args.unnamed.at(0).as_string()->data);
return EmptyListObject::make_new();
}
} // namespace goos
} // namespace goos
+3
View File
@@ -4,6 +4,9 @@
namespace goos {
std::shared_ptr<EmptyListObject> gEmptyList = nullptr;
std::shared_ptr<EmptyListObject>& get_empty_list() {
return gEmptyList;
}
/*!
* Convert type to string (name in brackets)
+4 -4
View File
@@ -319,7 +319,7 @@ class Object {
// There is a single heap allocated EmptyListObject.
class EmptyListObject;
extern std::shared_ptr<EmptyListObject> gEmptyList;
std::shared_ptr<EmptyListObject>& get_empty_list();
class EmptyListObject : public HeapObject {
public:
@@ -327,10 +327,10 @@ class EmptyListObject : public HeapObject {
static Object make_new() {
Object obj;
obj.type = ObjectType::EMPTY_LIST;
if (!gEmptyList) {
gEmptyList = std::make_shared<EmptyListObject>();
if (!get_empty_list()) {
get_empty_list() = std::make_shared<EmptyListObject>();
}
obj.heap_obj = gEmptyList;
obj.heap_obj = get_empty_list();
return obj;
}
+567
View File
@@ -0,0 +1,567 @@
#include <cassert>
#include <stdexcept>
#include <utility>
#include "PrettyPrinter.h"
#include "Reader.h"
namespace pretty_print {
/*!
* A single token which cannot be split between lines.
*/
struct FormToken {
enum class TokenKind {
WHITESPACE,
STRING,
OPEN_PAREN,
DOT,
CLOSE_PAREN,
EMPTY_PAIR,
SPECIAL_STRING // has different alignment rules than STRING
} kind;
explicit FormToken(TokenKind _kind, std::string _str = "") : kind(_kind), str(std::move(_str)) {}
std::string str;
std::string toString() const {
std::string s;
switch (kind) {
case TokenKind::WHITESPACE:
s.push_back(' ');
break;
case TokenKind::STRING:
s.append(str);
break;
case TokenKind::OPEN_PAREN:
s.push_back('(');
break;
case TokenKind::DOT:
s.push_back('.');
break;
case TokenKind::CLOSE_PAREN:
s.push_back(')');
break;
case TokenKind::EMPTY_PAIR:
s.append("()");
break;
case TokenKind::SPECIAL_STRING:
s.append(str);
break;
default:
throw std::runtime_error("toString unknown token kind");
}
return s;
}
};
/*!
* Convert a GOOS object to tokens and add it to the list.
* This is the main function which recursively builds a list of tokens out of an s-expression.
*
* Note that not all GOOS objects can be pretty printed. Only the ones that can be directly
* generated by the reader.
*/
void add_to_token_list(const goos::Object& obj, std::vector<FormToken>* tokens) {
switch (obj.type) {
case goos::ObjectType::EMPTY_LIST:
tokens->emplace_back(FormToken::TokenKind::EMPTY_PAIR);
break;
// all of these can just be printed to a string and turned into a 'symbol'
case goos::ObjectType::INTEGER:
case goos::ObjectType::FLOAT:
case goos::ObjectType::CHAR:
case goos::ObjectType::SYMBOL:
case goos::ObjectType::STRING:
tokens->emplace_back(FormToken::TokenKind::STRING, obj.print());
break;
// it's important to break the pair up into smaller tokens which can then be split
// across lines.
case goos::ObjectType::PAIR: {
tokens->emplace_back(FormToken::TokenKind::OPEN_PAREN);
auto* to_print = &obj;
for (;;) {
if (to_print->is_pair()) {
// first print the car into our token list:
add_to_token_list(to_print->as_pair()->car, tokens);
// then load up the cdr as the next thing to print
to_print = &to_print->as_pair()->cdr;
if (to_print->is_empty_list()) {
// we're done, add a close paren and finish
tokens->emplace_back(FormToken::TokenKind::CLOSE_PAREN);
return;
} else {
// more to print, add whitespace
tokens->emplace_back(FormToken::TokenKind::WHITESPACE);
}
} else {
// got an improper list.
// add a dot, space
tokens->emplace_back(FormToken::TokenKind::DOT);
tokens->emplace_back(FormToken::TokenKind::WHITESPACE);
// then the thing and a close paren.
add_to_token_list(*to_print, tokens);
tokens->emplace_back(FormToken::TokenKind::CLOSE_PAREN);
return; // and we're done with this list.
}
}
} break;
// these are unsupported by the pretty printer.
case goos::ObjectType::ARRAY: // todo, we should probably handle arrays.
case goos::ObjectType::LAMBDA:
case goos::ObjectType::MACRO:
case goos::ObjectType::ENVIRONMENT:
throw std::runtime_error("tried to pretty print a goos object kind which is not supported.");
default:
assert(false);
}
}
/*!
* Linked list node representing a token in the output (whitespace, paren, newline, etc)
*/
struct PrettyPrinterNode {
FormToken* tok = nullptr; // if we aren't a newline, we will have a token.
int line = -1; // line that token occurs on. undef for newlines
int lineIndent = -1; // indent of line. only valid for first token in the line
int offset = -1; // offset of beginning of token from left margin
int specialIndentDelta = 0;
bool is_line_separator = false; // true if line separator (not a token)
PrettyPrinterNode *next = nullptr, *prev = nullptr; // linked list
PrettyPrinterNode* paren =
nullptr; // pointer to open paren if in parens. open paren points to close and vice versa
explicit PrettyPrinterNode(FormToken* _tok) { tok = _tok; }
PrettyPrinterNode() = default;
};
/*!
* Container to track and cleanup all nodes after use.
*/
struct NodePool {
std::vector<PrettyPrinterNode*> nodes;
PrettyPrinterNode* allocate(FormToken* x) {
auto result = new PrettyPrinterNode(x);
nodes.push_back(result);
return result;
}
PrettyPrinterNode* allocate() {
auto result = new PrettyPrinterNode;
nodes.push_back(result);
return result;
}
NodePool() = default;
~NodePool() {
for (auto& x : nodes) {
delete x;
}
}
// so we don't accidentally copy this.
NodePool& operator=(const NodePool&) = delete;
NodePool(const NodePool&) = delete;
};
/*!
* Splice in a line break after the given node, it there isn't one already and if it isn't the last
* node.
*/
void insertNewlineAfter(NodePool& pool, PrettyPrinterNode* node, int specialIndentDelta) {
if (node->next && !node->next->is_line_separator) {
auto* nl = pool.allocate();
auto* next = node->next;
node->next = nl;
nl->prev = node;
nl->next = next;
next->prev = nl;
nl->is_line_separator = true;
nl->specialIndentDelta = specialIndentDelta;
}
}
/*!
* Splice in a line break before the given node, if there isn't one already and if it isn't the
* first node.
*/
void insertNewlineBefore(NodePool& pool, PrettyPrinterNode* node, int specialIndentDelta) {
if (node->prev && !node->prev->is_line_separator) {
auto* nl = pool.allocate();
auto* prev = node->prev;
prev->next = nl;
nl->prev = prev;
nl->next = node;
node->prev = nl;
nl->is_line_separator = true;
nl->specialIndentDelta = specialIndentDelta;
}
}
/*!
* Break a list across multiple lines. This is how line lengths are decreased.
* This does not compute the proper indentation and leaves the list in a bad state.
* After this has been called, the entire selection should be reformatted with propagate_pretty
*/
void breakList(NodePool& pool, PrettyPrinterNode* leftParen) {
assert(!leftParen->is_line_separator);
assert(leftParen->tok->kind == FormToken::TokenKind::OPEN_PAREN);
auto* rp = leftParen->paren;
assert(rp->tok->kind == FormToken::TokenKind::CLOSE_PAREN);
for (auto* n = leftParen->next; n && n != rp; n = n->next) {
if (!n->is_line_separator) {
if (n->tok->kind == FormToken::TokenKind::OPEN_PAREN) {
n = n->paren;
assert(n->tok->kind == FormToken::TokenKind::CLOSE_PAREN);
insertNewlineAfter(pool, n, 0);
} else if (n->tok->kind != FormToken::TokenKind::WHITESPACE) {
assert(n->tok->kind != FormToken::TokenKind::CLOSE_PAREN);
insertNewlineAfter(pool, n, 0);
}
}
}
}
/*!
* Compute proper line numbers, offsets, and indents for a list of tokens with newlines
* Will add newlines for close parens if needed.
*/
static PrettyPrinterNode* propagatePretty(NodePool& pool,
PrettyPrinterNode* list,
int line_length) {
// propagate line numbers
PrettyPrinterNode* rv = nullptr;
int line = list->line;
for (auto* n = list; n; n = n->next) {
if (n->is_line_separator) {
line++;
} else {
n->line = line;
// add the weird newline.
if (n->tok->kind == FormToken::TokenKind::CLOSE_PAREN) {
if (n->line != n->paren->line) {
if (n->prev && !n->prev->is_line_separator) {
insertNewlineBefore(pool, n, 0);
line++;
}
if (n->next && !n->next->is_line_separator) {
insertNewlineAfter(pool, n, 0);
}
}
}
}
}
// compute offsets and indents
std::vector<int> indentStack;
indentStack.push_back(0);
int offset = 0;
PrettyPrinterNode* line_start = list;
bool previous_line_sep = false;
for (auto* n = list; n; n = n->next) {
if (n->is_line_separator) {
previous_line_sep = true;
offset = indentStack.back() += n->specialIndentDelta;
} else {
if (previous_line_sep) {
line_start = n;
n->lineIndent = offset;
previous_line_sep = false;
}
n->offset = offset;
offset += n->tok->toString().length();
if (offset > line_length && !rv)
rv = line_start;
if (n->tok->kind == FormToken::TokenKind::OPEN_PAREN) {
if (!n->prev || n->prev->is_line_separator) {
indentStack.push_back(offset + 1);
} else {
indentStack.push_back(offset - 1);
}
}
if (n->tok->kind == FormToken::TokenKind::CLOSE_PAREN) {
indentStack.pop_back();
}
}
}
return rv;
}
/*!
* Get the token on the start of the next line. nullptr if we're the last line.
*/
PrettyPrinterNode* getNextLine(PrettyPrinterNode* start) {
assert(!start->is_line_separator);
int line = start->line;
for (;;) {
if (start->is_line_separator || start->line == line) {
if (start->next)
start = start->next;
else
return nullptr;
} else {
break;
}
}
return start;
}
/*!
* Get the next open paren on the current line (can start in the middle of line, not inclusive of
* start) nullptr if there's no open parens on the rest of this line.
*/
PrettyPrinterNode* getNextListOnLine(PrettyPrinterNode* start) {
int line = start->line;
assert(!start->is_line_separator);
if (!start->next || start->next->is_line_separator)
return nullptr;
start = start->next;
while (!start->is_line_separator && start->line == line) {
if (start->tok->kind == FormToken::TokenKind::OPEN_PAREN)
return start;
if (!start->next)
return nullptr;
start = start->next;
}
return nullptr;
}
/*!
* Get the first open paren on the current line (can start in the middle of line, inclusive of
* start) nullptr if there's no open parens on the rest of this line
*/
PrettyPrinterNode* getFirstListOnLine(PrettyPrinterNode* start) {
int line = start->line;
assert(!start->is_line_separator);
while (!start->is_line_separator && start->line == line) {
if (start->tok->kind == FormToken::TokenKind::OPEN_PAREN)
return start;
if (!start->next)
return nullptr;
start = start->next;
}
return nullptr;
}
/*!
* Get the first token on the first line which exceeds the max length
*/
PrettyPrinterNode* getFirstBadLine(PrettyPrinterNode* start, int line_length) {
assert(!start->is_line_separator);
int currentLine = start->line;
auto* currentLineNode = start;
for (;;) {
if (start->is_line_separator) {
assert(start->next);
start = start->next;
} else {
if (start->line != currentLine) {
currentLine = start->line;
currentLineNode = start;
}
if (start->offset > line_length) {
return currentLineNode;
}
if (!start->next) {
return nullptr;
}
start = start->next;
}
}
}
/*!
* Break insertion algorithm.
*/
void insertBreaksAsNeeded(NodePool& pool, PrettyPrinterNode* head, int line_length) {
PrettyPrinterNode* last_line_complete = nullptr;
PrettyPrinterNode* line_to_start_line_search = head;
// loop over lines
for (;;) {
// compute lines as needed
propagatePretty(pool, head, line_length);
// search for a bad line starting at the last line we fixed
PrettyPrinterNode* candidate_line = getFirstBadLine(line_to_start_line_search, line_length);
// if we got the same line we started on, this means we couldn't fix it.
if (candidate_line == last_line_complete) {
candidate_line = nullptr; // so we say our candidate was bad and try to find another
PrettyPrinterNode* next_line = getNextLine(line_to_start_line_search);
if (next_line) {
candidate_line = getFirstBadLine(next_line, line_length);
}
}
if (!candidate_line)
break;
// okay, we have a line which needs fixing.
assert(!candidate_line->prev || candidate_line->prev->is_line_separator);
PrettyPrinterNode* form_to_start = getFirstListOnLine(candidate_line);
for (;;) {
if (!form_to_start) {
// this means we failed to hit the desired line length...
break;
}
breakList(pool, form_to_start);
propagatePretty(pool, head, line_length);
if (getFirstBadLine(candidate_line, line_length) != candidate_line) {
break;
}
form_to_start = getNextListOnLine(form_to_start);
if (!form_to_start)
break;
}
last_line_complete = candidate_line;
line_to_start_line_search = candidate_line;
}
}
void insertSpecialBreaks(NodePool& pool, PrettyPrinterNode* node) {
for (; node; node = node->next) {
if (!node->is_line_separator && node->tok->kind == FormToken::TokenKind::STRING) {
std::string& name = node->tok->str;
if (name == "deftype") { // todo!
auto* parent_type_dec = getNextListOnLine(node);
if (parent_type_dec) {
insertNewlineAfter(pool, parent_type_dec->paren, 0);
}
}
if (name == "defun" || name == "defmethod") {
auto* parent_type_dec = getNextListOnLine(node);
if (parent_type_dec) {
insertNewlineAfter(pool, parent_type_dec->paren, 0);
}
}
if (name.at(0) == '"') {
insertNewlineAfter(pool, node, 0);
}
}
}
}
std::string to_string(const goos::Object& obj, int line_length) {
NodePool pool;
std::vector<FormToken> tokens;
add_to_token_list(obj, &tokens);
assert(!tokens.empty());
std::string pretty;
// build linked list of nodes
PrettyPrinterNode* head = pool.allocate(&tokens[0]);
PrettyPrinterNode* node = head;
head->line = 0;
head->offset = 0;
head->lineIndent = 0;
int offset = head->tok->toString().length();
for (size_t i = 1; i < tokens.size(); i++) {
node->next = pool.allocate(&tokens[i]);
node->next->prev = node;
node = node->next;
node->line = 0;
node->offset = offset;
offset += node->tok->toString().length();
node->lineIndent = 0;
}
// attach parens.
std::vector<PrettyPrinterNode*> parenStack;
parenStack.push_back(nullptr);
for (PrettyPrinterNode* n = head; n; n = n->next) {
if (n->tok->kind == FormToken::TokenKind::OPEN_PAREN) {
parenStack.push_back(n);
} else if (n->tok->kind == FormToken::TokenKind::CLOSE_PAREN) {
n->paren = parenStack.back();
parenStack.back()->paren = n;
parenStack.pop_back();
} else {
n->paren = parenStack.back();
}
}
assert(parenStack.size() == 1);
assert(!parenStack.back());
insertSpecialBreaks(pool, head);
propagatePretty(pool, head, line_length);
insertBreaksAsNeeded(pool, head, line_length);
// write to string
bool newline_prev = true;
for (PrettyPrinterNode* n = head; n; n = n->next) {
if (n->is_line_separator) {
pretty.push_back('\n');
newline_prev = true;
} else {
if (newline_prev) {
pretty.append(n->lineIndent, ' ');
newline_prev = false;
if (n->tok->kind == FormToken::TokenKind::WHITESPACE)
continue;
}
pretty.append(n->tok->toString());
}
}
return pretty;
}
goos::Reader pretty_printer_reader;
goos::Reader& get_pretty_printer_reader() {
return pretty_printer_reader;
}
goos::Object to_symbol(const std::string& str) {
return goos::SymbolObject::make_new(pretty_printer_reader.symbolTable, str);
}
goos::Object build_list(const std::string& str) {
return build_list(to_symbol(str));
}
goos::Object build_list(const goos::Object& obj) {
return goos::PairObject::make_new(obj, goos::EmptyListObject::make_new());
}
goos::Object build_list(const std::vector<goos::Object>& objects) {
if (objects.empty()) {
return goos::EmptyListObject::make_new();
} else {
return build_list(objects.data(), objects.size());
}
}
// build a list out of an array of forms
goos::Object build_list(const goos::Object* objects, int count) {
assert(count);
auto car = objects[0];
goos::Object cdr;
if (count - 1) {
cdr = build_list(objects + 1, count - 1);
} else {
cdr = goos::EmptyListObject::make_new();
}
return goos::PairObject::make_new(car, cdr);
}
// build a list out of a vector of strings that are converted to symbols
goos::Object build_list(const std::vector<std::string>& symbols) {
if (symbols.empty()) {
return goos::EmptyListObject::make_new();
}
std::vector<goos::Object> f;
f.reserve(symbols.size());
for (auto& x : symbols) {
f.push_back(to_symbol(x));
}
return build_list(f.data(), f.size());
}
} // namespace pretty_print
+49
View File
@@ -0,0 +1,49 @@
/*!
* @file PrettyPrinter.h
* A Pretty Printer for GOOS.
*/
#pragma once
#include <string>
#include <vector>
#include "common/goos/Object.h"
#include "common/goos/Reader.h"
namespace pretty_print {
// main pretty print function
std::string to_string(const goos::Object& obj, int line_length = 80);
// string -> object (as a symbol)
goos::Object to_symbol(const std::string& str);
// list with a single symbol from a string
goos::Object build_list(const std::string& str);
// wrap an object in a list
goos::Object build_list(const goos::Object& obj);
// build a list out of a vector of forms
goos::Object build_list(const std::vector<goos::Object>& objects);
// build a list out of an array of forms
goos::Object build_list(const goos::Object* objects, int count);
// build a list out of a vector of strings that are converted to symbols
goos::Object build_list(const std::vector<std::string>& symbols);
// fancy wrapper functions. Due to template magic these can call each other
// and accept mixed arguments!
template <typename... Args>
goos::Object build_list(const std::string& str, Args... rest) {
return goos::PairObject::make_new(to_symbol(str), build_list(rest...));
}
template <typename... Args>
goos::Object build_list(const goos::Object& car, Args... rest) {
return goos::PairObject::make_new(car, build_list(rest...));
}
goos::Reader& get_pretty_printer_reader();
} // namespace pretty_print
+18 -18
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;
+1
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);
+1 -1
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
+9 -8
View File
@@ -1,5 +1,4 @@
add_executable(decompiler
util/LispPrint.cpp
main.cpp
ObjectFile/ObjectFileDB.cpp
Disasm/Instruction.cpp
@@ -11,15 +10,17 @@ add_executable(decompiler
Function/Function.cpp
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/CfgBuilder.cpp
IR/IR.cpp)
target_link_libraries(decompiler
goos
minilzo
common_util)
common_util
type_system
fmt)
+6
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;
+5
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
+5
View File
@@ -1,3 +1,8 @@
/*!
* @file OpcodeInfo.cpp
* Decoding info for each opcode.
*/
#include "OpcodeInfo.h"
#include <cassert>
-5
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
+56 -66
View File
@@ -140,8 +140,8 @@ std::string BlockVtx::to_string() {
}
}
std::shared_ptr<Form> BlockVtx::to_form() {
return toForm("b" + std::to_string(block_id));
goos::Object BlockVtx::to_form() {
return pretty_print::to_symbol("b" + std::to_string(block_id));
}
std::string SequenceVtx::to_string() {
@@ -152,117 +152,117 @@ std::string SequenceVtx::to_string() {
return result;
}
std::shared_ptr<Form> SequenceVtx::to_form() {
std::vector<std::shared_ptr<Form>> forms;
forms.push_back(toForm("seq"));
goos::Object SequenceVtx::to_form() {
std::vector<goos::Object> forms;
forms.push_back(pretty_print::to_symbol("seq"));
for (auto* x : seq) {
forms.push_back(x->to_form());
}
return buildList(forms);
return pretty_print::build_list(forms);
}
std::string EntryVtx::to_string() {
return "ENTRY";
}
std::shared_ptr<Form> EntryVtx::to_form() {
return toForm("entry");
goos::Object EntryVtx::to_form() {
return pretty_print::to_symbol("entry");
}
std::string ExitVtx::to_string() {
return "EXIT";
}
std::shared_ptr<Form> ExitVtx::to_form() {
return toForm("exit");
goos::Object ExitVtx::to_form() {
return pretty_print::to_symbol("exit");
}
std::string CondWithElse::to_string() {
return "CONDWE" + std::to_string(uid);
}
std::shared_ptr<Form> CondWithElse::to_form() {
std::vector<std::shared_ptr<Form>> forms;
forms.push_back(toForm("cond"));
goos::Object CondWithElse::to_form() {
std::vector<goos::Object> forms;
forms.push_back(pretty_print::to_symbol("cond"));
for (const auto& x : entries) {
std::vector<std::shared_ptr<Form>> e = {x.condition->to_form(), x.body->to_form()};
forms.push_back(buildList(e));
std::vector<goos::Object> e = {x.condition->to_form(), x.body->to_form()};
forms.push_back(pretty_print::build_list(e));
}
std::vector<std::shared_ptr<Form>> e = {toForm("else"), else_vtx->to_form()};
forms.push_back(buildList(e));
return buildList(forms);
std::vector<goos::Object> e = {pretty_print::to_symbol("else"), else_vtx->to_form()};
forms.push_back(pretty_print::build_list(e));
return pretty_print::build_list(forms);
}
std::string CondNoElse::to_string() {
return "CONDNE" + std::to_string(uid);
}
std::shared_ptr<Form> CondNoElse::to_form() {
std::vector<std::shared_ptr<Form>> forms;
forms.push_back(toForm("cond"));
goos::Object CondNoElse::to_form() {
std::vector<goos::Object> forms;
forms.push_back(pretty_print::to_symbol("cond"));
for (const auto& x : entries) {
std::vector<std::shared_ptr<Form>> e = {x.condition->to_form(), x.body->to_form()};
forms.push_back(buildList(e));
std::vector<goos::Object> e = {x.condition->to_form(), x.body->to_form()};
forms.push_back(pretty_print::build_list(e));
}
return buildList(forms);
return pretty_print::build_list(forms);
}
std::string WhileLoop::to_string() {
return "WHL" + std::to_string(uid);
}
std::shared_ptr<Form> WhileLoop::to_form() {
std::vector<std::shared_ptr<Form>> forms = {toForm("while"), condition->to_form(),
body->to_form()};
return buildList(forms);
goos::Object WhileLoop::to_form() {
std::vector<goos::Object> forms = {pretty_print::to_symbol("while"), condition->to_form(),
body->to_form()};
return pretty_print::build_list(forms);
}
std::string UntilLoop::to_string() {
return "UNTL" + std::to_string(uid);
}
std::shared_ptr<Form> UntilLoop::to_form() {
std::vector<std::shared_ptr<Form>> forms = {toForm("until"), condition->to_form(),
body->to_form()};
return buildList(forms);
goos::Object UntilLoop::to_form() {
std::vector<goos::Object> forms = {pretty_print::to_symbol("until"), condition->to_form(),
body->to_form()};
return pretty_print::build_list(forms);
}
std::string UntilLoop_single::to_string() {
return "UNTLS" + std::to_string(uid);
}
std::shared_ptr<Form> UntilLoop_single::to_form() {
std::vector<std::shared_ptr<Form>> forms = {toForm("until1"), block->to_form()};
return buildList(forms);
goos::Object UntilLoop_single::to_form() {
std::vector<goos::Object> forms = {pretty_print::to_symbol("until1"), block->to_form()};
return pretty_print::build_list(forms);
}
std::string InfiniteLoopBlock::to_string() {
return "INFL" + std::to_string(uid);
}
std::shared_ptr<Form> InfiniteLoopBlock::to_form() {
std::vector<std::shared_ptr<Form>> forms = {toForm("inf-loop"), block->to_form()};
return buildList(forms);
goos::Object InfiniteLoopBlock::to_form() {
std::vector<goos::Object> forms = {pretty_print::to_symbol("inf-loop"), block->to_form()};
return pretty_print::build_list(forms);
}
std::string ShortCircuit::to_string() {
return "SC" + std::to_string(uid);
}
std::shared_ptr<Form> ShortCircuit::to_form() {
std::vector<std::shared_ptr<Form>> forms;
forms.push_back(toForm("sc"));
goos::Object ShortCircuit::to_form() {
std::vector<goos::Object> forms;
forms.push_back(pretty_print::to_symbol("sc"));
for (const auto& x : entries) {
forms.push_back(x->to_form());
}
return buildList(forms);
return pretty_print::build_list(forms);
}
/*
std::shared_ptr<Form> IfElseVtx::to_form() {
std::vector<std::shared_ptr<Form>> forms = {toForm("if"), condition->to_form(),
goos::Object IfElseVtx::to_form() {
std::vector<goos::Object> forms = {pretty_print::to_symbol("if"), condition->to_form(),
true_case->to_form(), false_case->to_form()};
return buildList(forms);
return pretty_print::build_list(forms);
}
std::string IfElseVtx::to_string() {
@@ -274,10 +274,10 @@ std::string GotoEnd::to_string() {
return "goto_end" + std::to_string(uid);
}
std::shared_ptr<Form> GotoEnd::to_form() {
std::vector<std::shared_ptr<Form>> forms = {toForm("return-from-function"), body->to_form(),
unreachable_block->to_form()};
return buildList(forms);
goos::Object GotoEnd::to_form() {
std::vector<goos::Object> forms = {pretty_print::to_symbol("return-from-function"),
body->to_form(), unreachable_block->to_form()};
return pretty_print::build_list(forms);
}
ControlFlowGraph::ControlFlowGraph() {
@@ -361,17 +361,17 @@ CfgVtx* ControlFlowGraph::get_single_top_level() {
* Turn into a form. If fully resolved, prints the nested control flow. Otherwise puts all the
* ungrouped stuff into an "(ungrouped ...)" form and prints that.
*/
std::shared_ptr<Form> ControlFlowGraph::to_form() {
goos::Object ControlFlowGraph::to_form() {
if (get_top_level_vertices_count() == 1) {
return get_single_top_level()->to_form();
} else {
std::vector<std::shared_ptr<Form>> forms = {toForm("ungrouped")};
std::vector<goos::Object> forms = {pretty_print::to_symbol("ungrouped")};
for (auto* x : m_node_pool) {
if (!x->parent && x != entry() && x != exit()) {
forms.push_back(x->to_form());
}
}
return buildList(forms);
return pretty_print::build_list(forms);
}
}
@@ -380,8 +380,7 @@ std::shared_ptr<Form> ControlFlowGraph::to_form() {
* ungrouped stuff into an "(ungrouped ...)" form and prints that.
*/
std::string ControlFlowGraph::to_form_string() {
// todo - fix bug in pretty printing and reduce this to 80!
return to_form()->toStringPretty(0, 140);
return pretty_print::to_string(to_form());
}
// bool ControlFlowGraph::compact_top_level() {
@@ -1637,7 +1636,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 +1714,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 +1721,10 @@ 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_n_else();
changed = changed || cfg->find_cond_w_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();
+16 -16
View File
@@ -6,7 +6,7 @@
#include <string>
#include <vector>
#include <cassert>
#include "decompiler/util/LispPrint.h"
#include "common/goos/PrettyPrinter.h"
/*!
* In v, find an item equal to old, and replace it with replace.
@@ -61,8 +61,8 @@ void replace_exactly_one_in(std::vector<T>& v, T old, T replace) {
*/
class CfgVtx {
public:
virtual std::string to_string() = 0; // convert to a single line string for debugging
virtual std::shared_ptr<Form> to_form() = 0; // recursive print as LISP form.
virtual std::string to_string() = 0; // convert to a single line string for debugging
virtual goos::Object to_form() = 0; // recursive print as LISP form.
virtual ~CfgVtx() = default;
CfgVtx* parent = nullptr; // parent structure, or nullptr if top level
@@ -125,7 +125,7 @@ class CfgVtx {
class EntryVtx : public CfgVtx {
public:
EntryVtx() = default;
std::shared_ptr<Form> to_form() override;
goos::Object to_form() override;
std::string to_string() override;
};
@@ -135,7 +135,7 @@ class EntryVtx : public CfgVtx {
class ExitVtx : public CfgVtx {
public:
std::string to_string() override;
std::shared_ptr<Form> to_form() override;
goos::Object to_form() override;
};
/*!
@@ -145,7 +145,7 @@ class BlockVtx : public CfgVtx {
public:
explicit BlockVtx(int id) : block_id(id) {}
std::string to_string() override;
std::shared_ptr<Form> to_form() override;
goos::Object to_form() override;
int block_id = -1; // which block are we?
bool is_early_exit_block = false; // are we an empty block at the end for early exits to jump to?
};
@@ -157,7 +157,7 @@ class BlockVtx : public CfgVtx {
class SequenceVtx : public CfgVtx {
public:
std::string to_string() override;
std::shared_ptr<Form> to_form() override;
goos::Object to_form() override;
std::vector<CfgVtx*> seq;
};
@@ -169,7 +169,7 @@ class SequenceVtx : public CfgVtx {
class CondWithElse : public CfgVtx {
public:
std::string to_string() override;
std::shared_ptr<Form> to_form() override;
goos::Object to_form() override;
struct Entry {
Entry() = default;
@@ -190,7 +190,7 @@ class CondWithElse : public CfgVtx {
class CondNoElse : public CfgVtx {
public:
std::string to_string() override;
std::shared_ptr<Form> to_form() override;
goos::Object to_form() override;
struct Entry {
Entry() = default;
@@ -205,7 +205,7 @@ class CondNoElse : public CfgVtx {
class WhileLoop : public CfgVtx {
public:
std::string to_string() override;
std::shared_ptr<Form> to_form() override;
goos::Object to_form() override;
CfgVtx* condition = nullptr;
CfgVtx* body = nullptr;
@@ -214,7 +214,7 @@ class WhileLoop : public CfgVtx {
class UntilLoop : public CfgVtx {
public:
std::string to_string() override;
std::shared_ptr<Form> to_form() override;
goos::Object to_form() override;
CfgVtx* condition = nullptr;
CfgVtx* body = nullptr;
@@ -223,7 +223,7 @@ class UntilLoop : public CfgVtx {
class UntilLoop_single : public CfgVtx {
public:
std::string to_string() override;
std::shared_ptr<Form> to_form() override;
goos::Object to_form() override;
CfgVtx* block = nullptr;
};
@@ -231,21 +231,21 @@ class UntilLoop_single : public CfgVtx {
class ShortCircuit : public CfgVtx {
public:
std::string to_string() override;
std::shared_ptr<Form> to_form() override;
goos::Object to_form() override;
std::vector<CfgVtx*> entries;
};
class InfiniteLoopBlock : public CfgVtx {
public:
std::string to_string() override;
std::shared_ptr<Form> to_form() override;
goos::Object to_form() override;
CfgVtx* block;
};
class GotoEnd : public CfgVtx {
public:
std::string to_string() override;
std::shared_ptr<Form> to_form() override;
goos::Object to_form() override;
CfgVtx* body = nullptr;
CfgVtx* unreachable_block = nullptr;
};
@@ -260,7 +260,7 @@ class ControlFlowGraph {
ControlFlowGraph();
~ControlFlowGraph();
std::shared_ptr<Form> to_form();
goos::Object to_form();
std::string to_form_string();
std::string to_dot();
int get_top_level_vertices_count();
+42 -3
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;
}
std::shared_ptr<IR> Function::get_basic_op_at_instr(int idx) {
return basic_ops.at(instruction_to_basic_op.at(idx));
}
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;
}
+15 -1
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,16 @@ 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);
std::shared_ptr<IR> get_basic_op_at_instr(int idx);
int get_basic_op_count();
int get_failed_basic_op_count();
std::shared_ptr<IR> ir = nullptr;
int segment = -1;
int start_word = -1;
@@ -115,6 +126,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
+13
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);
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <memory>
class IR;
class Function;
class LinkedObjectFile;
class ControlFlowGraph;
std::shared_ptr<IR> build_cfg_ir(Function& function, ControlFlowGraph& cfg, LinkedObjectFile& file);
+803
View File
@@ -0,0 +1,803 @@
#include "IR.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
std::vector<std::shared_ptr<IR>> IR::get_all_ir(LinkedObjectFile& file) const {
(void)file;
std::vector<std::shared_ptr<IR>> result;
get_children(&result);
size_t last_checked = 0;
size_t last_last_checked = -1;
while (last_checked != last_last_checked) {
last_last_checked = last_checked;
auto end_of_check = result.size();
for (size_t i = last_checked; i < end_of_check; i++) {
auto it = result.at(i).get();
assert(it);
it->get_children(&result);
}
last_checked = end_of_check;
}
return result;
}
std::string IR::print(const LinkedObjectFile& file) const {
return pretty_print::to_string(to_form(file));
}
goos::Object IR_Failed::to_form(const LinkedObjectFile& file) const {
(void)file;
return pretty_print::build_list("INVALID-OPERATION");
}
void IR_Failed::get_children(std::vector<std::shared_ptr<IR>>* output) const {
(void)output;
}
goos::Object IR_Register::to_form(const LinkedObjectFile& file) const {
(void)file;
return pretty_print::to_symbol(reg.to_charp());
}
void IR_Register::get_children(std::vector<std::shared_ptr<IR>>* output) const {
(void)output;
}
goos::Object IR_Set::to_form(const LinkedObjectFile& file) const {
return pretty_print::build_list(pretty_print::to_symbol("set!"), dst->to_form(file),
src->to_form(file));
}
void IR_Set::get_children(std::vector<std::shared_ptr<IR>>* output) const {
// note that we are not returning clobber here because it shouldn't contain anything that
// the IR simplification code should touch.
output->push_back(dst);
output->push_back(src);
}
goos::Object 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 pretty_print::build_list(pretty_print::to_symbol(store_operator), dst->to_form(file),
src->to_form(file));
}
goos::Object IR_Symbol::to_form(const LinkedObjectFile& file) const {
(void)file;
return pretty_print::to_symbol("'" + name);
}
void IR_Symbol::get_children(std::vector<std::shared_ptr<IR>>* output) const {
(void)output;
}
goos::Object IR_SymbolValue::to_form(const LinkedObjectFile& file) const {
(void)file;
return pretty_print::to_symbol(name);
}
void IR_SymbolValue::get_children(std::vector<std::shared_ptr<IR>>* output) const {
(void)output;
}
goos::Object IR_StaticAddress::to_form(const LinkedObjectFile& file) const {
// return pretty_print::build_list(pretty_print::to_symbol("&"), file.get_label_name(label_id));
return pretty_print::to_symbol(file.get_label_name(label_id));
}
void IR_StaticAddress::get_children(std::vector<std::shared_ptr<IR>>* output) const {
(void)output;
}
goos::Object 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 pretty_print::build_list(pretty_print::to_symbol(load_operator), location->to_form(file));
}
void IR_Load::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(location);
}
goos::Object 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 pretty_print::build_list(pretty_print::to_symbol(math_operator), arg0->to_form(file),
arg1->to_form(file));
}
void IR_FloatMath2::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(arg0);
output->push_back(arg1);
}
void IR_FloatMath1::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(arg);
}
goos::Object 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 pretty_print::build_list(pretty_print::to_symbol(math_operator), arg0->to_form(file),
arg1->to_form(file));
}
void IR_IntMath2::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(arg0);
output->push_back(arg1);
}
goos::Object IR_IntMath1::to_form(const LinkedObjectFile& file) const {
std::string math_operator;
switch (kind) {
case NOT:
math_operator = "lognot";
break;
case ABS:
math_operator = "abs.si";
break;
default:
assert(false);
}
return pretty_print::build_list(pretty_print::to_symbol(math_operator), arg->to_form(file));
}
void IR_IntMath1::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(arg);
}
goos::Object 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 pretty_print::build_list(pretty_print::to_symbol(math_operator), arg->to_form(file));
}
goos::Object IR_Call::to_form(const LinkedObjectFile& file) const {
(void)file;
return pretty_print::build_list("call!");
}
void IR_Call::get_children(std::vector<std::shared_ptr<IR>>* output) const {
(void)output;
}
goos::Object IR_IntegerConstant::to_form(const LinkedObjectFile& file) const {
(void)file;
return pretty_print::to_symbol(std::to_string(value));
}
void IR_IntegerConstant::get_children(std::vector<std::shared_ptr<IR>>* output) const {
(void)output;
}
goos::Object BranchDelay::to_form(const LinkedObjectFile& file) const {
(void)file;
switch (kind) {
case NOP:
return pretty_print::build_list("nop");
case SET_REG_FALSE:
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
"'#f");
case SET_REG_TRUE:
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
"'#t");
case SET_REG_REG:
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
source->to_form(file));
case SET_BINTEGER:
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
"binteger");
case SET_PAIR:
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
"pair");
case DSLLV:
return pretty_print::build_list(
pretty_print::to_symbol("set!"), destination->to_form(file),
pretty_print::build_list(pretty_print::to_symbol("shl"), source->to_form(file),
source2->to_form(file)));
case NEGATE:
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
pretty_print::build_list("-", source->to_form(file)));
case UNKNOWN:
return pretty_print::build_list("unknown-branch-delay");
default:
assert(false);
}
}
void BranchDelay::get_children(std::vector<std::shared_ptr<IR>>* output) const {
if (destination) {
output->push_back(destination);
}
if (source) {
output->push_back(source);
}
if (source2) {
output->push_back(source2);
}
}
goos::Object IR_Nop::to_form(const LinkedObjectFile& file) const {
(void)file;
return pretty_print::build_list("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:
case GREATER_THAN_ZERO_SIGNED:
case GEQ_ZERO_SIGNED:
case LESS_THAN_ZERO:
case LEQ_ZERO_SIGNED:
return 1;
case ALWAYS:
case NEVER:
return 0;
default:
assert(false);
}
}
void Condition::get_children(std::vector<std::shared_ptr<IR>>* output) const {
if (src0) {
output->push_back(src0);
}
if (src1) {
output->push_back(src1);
}
}
void Condition::invert() {
switch (kind) {
case NOT_EQUAL:
kind = EQUAL;
break;
case EQUAL:
kind = NOT_EQUAL;
break;
case LESS_THAN_SIGNED:
kind = GEQ_SIGNED;
break;
case GREATER_THAN_SIGNED:
kind = LEQ_SIGNED;
break;
case LEQ_SIGNED:
kind = GREATER_THAN_SIGNED;
break;
case GEQ_SIGNED:
kind = LESS_THAN_SIGNED;
break;
case GREATER_THAN_ZERO_SIGNED:
kind = LEQ_ZERO_SIGNED;
break;
case LEQ_ZERO_SIGNED:
kind = GREATER_THAN_ZERO_SIGNED;
break;
case LESS_THAN_ZERO:
kind = GEQ_ZERO_SIGNED;
break;
case GEQ_ZERO_SIGNED:
kind = LESS_THAN_ZERO;
break;
case LESS_THAN_UNSIGNED:
kind = GEQ_UNSIGNED;
break;
case GREATER_THAN_UNSIGNED:
kind = LEQ_UNSIGNED;
break;
case LEQ_UNSIGNED:
kind = GREATER_THAN_UNSIGNED;
break;
case GEQ_UNSIGNED:
kind = LESS_THAN_UNSIGNED;
break;
case ZERO:
kind = NONZERO;
break;
case NONZERO:
kind = ZERO;
break;
case FALSE:
kind = TRUTHY;
break;
case TRUTHY:
kind = FALSE;
break;
case ALWAYS:
kind = NEVER;
break;
case NEVER:
kind = ALWAYS;
break;
case FLOAT_EQUAL:
kind = FLOAT_NOT_EQUAL;
break;
case FLOAT_NOT_EQUAL:
kind = FLOAT_EQUAL;
break;
case FLOAT_LESS_THAN:
kind = FLOAT_GEQ;
break;
case FLOAT_GEQ:
kind = FLOAT_LESS_THAN;
break;
default:
assert(false);
}
}
goos::Object 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 NEVER:
condtion_operator = "'#f";
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;
case GREATER_THAN_ZERO_SIGNED:
condtion_operator = ">0.si";
break;
case GEQ_ZERO_SIGNED:
condtion_operator = ">=0.si";
break;
case LESS_THAN_ZERO:
condtion_operator = "<0.si";
break;
case LEQ_ZERO_SIGNED:
condtion_operator = "<=0.si";
break;
default:
assert(false);
}
if (nargs == 2) {
return pretty_print::build_list(pretty_print::to_symbol(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 pretty_print::build_list(pretty_print::to_symbol(condtion_operator),
src0->to_form(file));
}
} else if (nargs == 0) {
return pretty_print::to_symbol(condtion_operator);
} else {
assert(false);
}
}
goos::Object IR_Branch::to_form(const LinkedObjectFile& file) const {
return pretty_print::build_list(
pretty_print::to_symbol(likely ? "bl!" : "b!"), condition.to_form(file),
pretty_print::to_symbol(file.get_label_name(dest_label_idx)), branch_delay.to_form(file));
}
void IR_Branch::get_children(std::vector<std::shared_ptr<IR>>* output) const {
condition.get_children(output);
branch_delay.get_children(output);
}
goos::Object IR_Compare::to_form(const LinkedObjectFile& file) const {
return condition.to_form(file);
}
void IR_Compare::get_children(std::vector<std::shared_ptr<IR>>* output) const {
condition.get_children(output);
}
goos::Object IR_Suspend::to_form(const LinkedObjectFile& file) const {
(void)file;
return pretty_print::build_list("suspend!");
}
void IR_Nop::get_children(std::vector<std::shared_ptr<IR>>* output) const {
(void)output;
}
void IR_Suspend::get_children(std::vector<std::shared_ptr<IR>>* output) const {
(void)output;
}
goos::Object IR_Begin::to_form(const LinkedObjectFile& file) const {
std::vector<goos::Object> list;
list.push_back(pretty_print::to_symbol("begin"));
for (auto& x : forms) {
list.push_back(x->to_form(file));
}
return pretty_print::build_list(list);
}
void IR_Begin::get_children(std::vector<std::shared_ptr<IR>>* output) const {
for (auto& x : forms) {
output->push_back(x);
}
}
namespace {
void print_inlining_begin(std::vector<goos::Object>* output, IR* ir, const LinkedObjectFile& file) {
auto as_begin = dynamic_cast<IR_Begin*>(ir);
if (as_begin) {
for (auto& x : as_begin->forms) {
output->push_back(x->to_form(file));
}
} else {
output->push_back(ir->to_form(file));
}
}
bool is_single_expression(IR* in) {
return !dynamic_cast<IR_Begin*>(in);
}
} // namespace
goos::Object IR_WhileLoop::to_form(const LinkedObjectFile& file) const {
std::vector<goos::Object> list;
list.push_back(pretty_print::to_symbol("while"));
list.push_back(condition->to_form(file));
print_inlining_begin(&list, body.get(), file);
return pretty_print::build_list(list);
}
void IR_WhileLoop::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(condition);
output->push_back(body);
}
goos::Object IR_CondWithElse::to_form(const LinkedObjectFile& file) const {
// for now we only turn it into an if statement if both cases won't require a begin at the top
// level. I think it is more common to write these as a two-case cond instead of an if with begin.
if (entries.size() == 1 && is_single_expression(entries.front().body.get()) &&
is_single_expression(else_ir.get())) {
std::vector<goos::Object> list;
list.push_back(pretty_print::to_symbol("if"));
list.push_back(entries.front().condition->to_form(file));
list.push_back(entries.front().body->to_form(file));
list.push_back(else_ir->to_form(file));
return pretty_print::build_list(list);
} else {
std::vector<goos::Object> list;
list.push_back(pretty_print::to_symbol("cond"));
for (auto& e : entries) {
std::vector<goos::Object> entry;
entry.push_back(e.condition->to_form(file));
print_inlining_begin(&entry, e.body.get(), file);
list.push_back(pretty_print::build_list(entry));
}
std::vector<goos::Object> else_form;
else_form.push_back(pretty_print::to_symbol("else"));
print_inlining_begin(&else_form, else_ir.get(), file);
list.push_back(pretty_print::build_list(else_form));
return pretty_print::build_list(list);
}
}
void IR_CondWithElse::get_children(std::vector<std::shared_ptr<IR>>* output) const {
for (auto& e : entries) {
output->push_back(e.condition);
output->push_back(e.body);
}
output->push_back(else_ir);
}
goos::Object IR_GetRuntimeType::to_form(const LinkedObjectFile& file) const {
std::vector<goos::Object> list = {pretty_print::to_symbol("type-of"), object->to_form(file)};
return pretty_print::build_list(list);
}
void IR_GetRuntimeType::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(object);
}
goos::Object IR_Cond::to_form(const LinkedObjectFile& file) const {
if (entries.size() == 1 && is_single_expression(entries.front().body.get())) {
// print as an if statement if we can put the body in a single form.
std::vector<goos::Object> list;
list.push_back(pretty_print::to_symbol("if"));
list.push_back(entries.front().condition->to_form(file));
list.push_back(entries.front().body->to_form(file));
return pretty_print::build_list(list);
} else if (entries.size() == 1) {
// turn into a when if the body requires multiple forms
// todo check to see if the condition starts with a NOT and this can be simplified to an
// unless.
std::vector<goos::Object> list;
list.push_back(pretty_print::to_symbol("when"));
list.push_back(entries.front().condition->to_form(file));
print_inlining_begin(&list, entries.front().body.get(), file);
return pretty_print::build_list(list);
} else {
std::vector<goos::Object> list;
list.push_back(pretty_print::to_symbol("cond"));
for (auto& e : entries) {
std::vector<goos::Object> entry;
entry.push_back(e.condition->to_form(file));
print_inlining_begin(&entry, e.body.get(), file);
list.push_back(pretty_print::build_list(entry));
}
return pretty_print::build_list(list);
}
}
void IR_Cond::get_children(std::vector<std::shared_ptr<IR>>* output) const {
for (auto& e : entries) {
output->push_back(e.condition);
output->push_back(e.body);
}
}
goos::Object IR_ShortCircuit::to_form(const LinkedObjectFile& file) const {
std::vector<goos::Object> forms;
switch (kind) {
case UNKNOWN:
forms.push_back(pretty_print::to_symbol("unknown-sc"));
break;
case AND:
forms.push_back(pretty_print::to_symbol("and"));
break;
case OR:
forms.push_back(pretty_print::to_symbol("or"));
break;
default:
assert(false);
}
for (auto& x : entries) {
forms.push_back(x.condition->to_form(file));
}
return pretty_print::build_list(forms);
}
void IR_ShortCircuit::get_children(std::vector<std::shared_ptr<IR>>* output) const {
for (auto& x : entries) {
output->push_back(x.condition);
if (x.output) {
output->push_back(x.output);
}
}
}
goos::Object IR_Ash::to_form(const LinkedObjectFile& file) const {
return pretty_print::build_list(pretty_print::to_symbol(is_signed ? "ash.si" : "ash.ui"),
value->to_form(file), shift_amount->to_form(file));
}
void IR_Ash::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(value);
output->push_back(shift_amount);
}
+373
View File
@@ -0,0 +1,373 @@
#ifndef JAK_IR_H
#define JAK_IR_H
#include <cassert>
#include <utility>
#include "decompiler/Disasm/Register.h"
#include "common/goos/PrettyPrinter.h"
class LinkedObjectFile;
class IR {
public:
virtual goos::Object to_form(const LinkedObjectFile& file) const = 0;
std::vector<std::shared_ptr<IR>> get_all_ir(LinkedObjectFile& file) const;
std::string print(const LinkedObjectFile& file) const;
virtual void get_children(std::vector<std::shared_ptr<IR>>* output) const = 0;
bool is_basic_op = false;
};
class IR_Failed : public IR {
public:
IR_Failed() = default;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_Register : public IR {
public:
IR_Register(Register _reg, int _instr_idx) : reg(_reg), instr_idx(_instr_idx) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) 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)) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) 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;
goos::Object to_form(const LinkedObjectFile& file) const override;
};
class IR_Symbol : public IR {
public:
explicit IR_Symbol(std::string _name) : name(std::move(_name)) {}
std::string name;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_SymbolValue : public IR {
public:
explicit IR_SymbolValue(std::string _name) : name(std::move(_name)) {}
std::string name;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_StaticAddress : public IR {
public:
explicit IR_StaticAddress(int _label_id) : label_id(_label_id) {}
int label_id = -1;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_Load : public IR {
public:
enum Kind { UNSIGNED, SIGNED, FLOAT } kind;
IR_Load(Kind _kind, int _size, std::shared_ptr<IR> _location)
: kind(_kind), size(_size), location(std::move(_location)) {}
int size;
std::shared_ptr<IR> location;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) 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;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) 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;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) 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;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_IntMath1 : public IR {
public:
enum Kind { NOT, ABS } kind;
IR_IntMath1(Kind _kind, std::shared_ptr<IR> _arg) : kind(_kind), arg(std::move(_arg)) {}
std::shared_ptr<IR> arg;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_Call : public IR {
public:
IR_Call() = default;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_IntegerConstant : public IR {
public:
int64_t value;
explicit IR_IntegerConstant(int64_t _value) : value(_value) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
struct BranchDelay {
enum Kind {
NOP,
SET_REG_FALSE,
SET_REG_TRUE,
SET_REG_REG,
SET_BINTEGER,
SET_PAIR,
DSLLV,
NEGATE,
UNKNOWN
} kind;
std::shared_ptr<IR> destination = nullptr, source = nullptr, source2 = nullptr;
explicit BranchDelay(Kind _kind) : kind(_kind) {}
goos::Object to_form(const LinkedObjectFile& file) const;
void get_children(std::vector<std::shared_ptr<IR>>* output) const;
};
struct Condition {
enum Kind {
NOT_EQUAL,
EQUAL,
LESS_THAN_SIGNED,
GREATER_THAN_SIGNED,
LEQ_SIGNED,
GEQ_SIGNED,
GREATER_THAN_ZERO_SIGNED,
LEQ_ZERO_SIGNED,
LESS_THAN_ZERO,
GEQ_ZERO_SIGNED,
LESS_THAN_UNSIGNED,
GREATER_THAN_UNSIGNED,
LEQ_UNSIGNED,
GEQ_UNSIGNED,
ZERO,
NONZERO,
FALSE,
TRUTHY,
ALWAYS,
NEVER,
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;
goos::Object to_form(const LinkedObjectFile& file) const;
std::shared_ptr<IR> src0, src1, clobber;
void get_children(std::vector<std::shared_ptr<IR>>* output) const;
void invert();
};
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;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_Compare : public IR {
public:
explicit IR_Compare(Condition _condition) : condition(std::move(_condition)) {}
Condition condition;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_Nop : public IR {
public:
IR_Nop() = default;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_Suspend : public IR {
public:
IR_Suspend() = default;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_Begin : public IR {
public:
IR_Begin() = default;
explicit IR_Begin(const std::vector<std::shared_ptr<IR>>& _forms) : forms(std::move(_forms)) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
std::vector<std::shared_ptr<IR>> forms;
};
class IR_WhileLoop : public IR {
public:
IR_WhileLoop(std::shared_ptr<IR> _condition, std::shared_ptr<IR> _body)
: condition(std::move(_condition)), body(std::move(_body)) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
std::shared_ptr<IR> condition, body;
};
class IR_CondWithElse : public IR {
public:
struct Entry {
std::shared_ptr<IR> condition = nullptr;
std::shared_ptr<IR> body = nullptr;
bool cleaned = false;
};
std::vector<Entry> entries;
std::shared_ptr<IR> else_ir;
IR_CondWithElse(std::vector<Entry> _entries, std::shared_ptr<IR> _else_ir)
: entries(std::move(_entries)), else_ir(std::move(_else_ir)) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
// this one doesn't have an else statement. Will return false if none of the cases are taken.
class IR_Cond : public IR {
public:
struct Entry {
std::shared_ptr<IR> condition = nullptr;
std::shared_ptr<IR> body = nullptr;
std::shared_ptr<IR> false_destination = nullptr;
bool cleaned = false;
};
std::vector<Entry> entries;
explicit IR_Cond(std::vector<Entry> _entries) : entries(std::move(_entries)) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
// this will work on pairs, bintegers, or basics
class IR_GetRuntimeType : public IR {
public:
std::shared_ptr<IR> object, clobber;
IR_GetRuntimeType(std::shared_ptr<IR> _object, std::shared_ptr<IR> _clobber)
: object(std::move(_object)), clobber(std::move(_clobber)) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_ShortCircuit : public IR {
public:
struct Entry {
std::shared_ptr<IR> condition = nullptr;
std::shared_ptr<IR> output = nullptr; // where the delay slot writes to.
bool cleaned = false;
};
enum Kind { UNKNOWN, AND, OR } kind = UNKNOWN;
std::shared_ptr<IR> final_result = nullptr; // the register that the final result goes in.
std::vector<Entry> entries;
explicit IR_ShortCircuit(std::vector<Entry> _entries) : entries(std::move(_entries)) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_Ash : public IR {
public:
std::shared_ptr<IR> shift_amount, value, clobber;
bool is_signed = true;
IR_Ash(std::shared_ptr<IR> _shift_amount,
std::shared_ptr<IR> _value,
std::shared_ptr<IR> _clobber,
bool _is_signed)
: shift_amount(std::move(_shift_amount)),
value(std::move(_value)),
clobber(std::move(_clobber)),
is_signed(_is_signed) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
#endif // JAK_IR_H
+34 -21
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";
}
@@ -614,6 +622,11 @@ std::string LinkedObjectFile::print_disassembly() {
*/
}
if (func.ir) {
result += ";; ir\n";
result += func.ir->print(*this);
}
result += "\n\n\n";
}
@@ -706,7 +719,9 @@ std::string LinkedObjectFile::print_scripts() {
if (label_id != -1) {
auto& label = labels.at(label_id);
if ((label.offset & 7) == 2) {
result += to_form_script(seg, word_idx, already_printed)->toStringPretty(0, 100) + "\n";
// result += to_form_script(seg, word_idx, already_printed)->toStringPretty(0, 100) +
// "\n";
result += pretty_print::to_string(to_form_script(seg, word_idx, already_printed)) + "\n";
}
}
}
@@ -728,16 +743,14 @@ bool LinkedObjectFile::is_empty_list(int seg, int byte_idx) {
* Note : this takes the address of the car of the pair. which is perhaps a bit confusing
* (in GOAL, this would be (&-> obj car))
*/
std::shared_ptr<Form> LinkedObjectFile::to_form_script(int seg,
int word_idx,
std::vector<bool>& seen) {
goos::Object LinkedObjectFile::to_form_script(int seg, int word_idx, std::vector<bool>& seen) {
// the object to currently print. to start off, create pair from the car address we've been given.
int goal_print_obj = word_idx * 4 + 2;
// resulting form. we can't have a totally empty list (as an empty list looks like a symbol,
// so it wouldn't be flagged), so it's safe to make this a pair.
auto result = std::make_shared<Form>();
result->kind = FormKind::PAIR;
auto result = goos::PairObject::make_new(goos::EmptyListObject::make_new(),
goos::EmptyListObject::make_new());
// the current pair to fill out.
auto fill = result;
@@ -747,14 +760,14 @@ std::shared_ptr<Form> LinkedObjectFile::to_form_script(int seg,
// check the thing to print is a a pair.
if ((goal_print_obj & 7) == 2) {
// first convert the car (again, with (&-> obj car))
fill->pair[0] = to_form_script_object(seg, goal_print_obj - 2, seen);
fill.as_pair()->car = to_form_script_object(seg, goal_print_obj - 2, seen);
seen.at(goal_print_obj / 4) = true;
auto cdr_addr = goal_print_obj + 2;
if (is_empty_list(seg, cdr_addr)) {
// the list has ended!
fill->pair[1] = gSymbolTable.getEmptyPair();
fill.as_pair()->cdr = goos::EmptyListObject::make_new();
return result;
} else {
// cdr object should be aligned.
@@ -764,12 +777,12 @@ std::shared_ptr<Form> LinkedObjectFile::to_form_script(int seg,
if (cdr_word.kind == LinkedWord::PTR && (labels.at(cdr_word.label_id).offset & 7) == 2) {
// yes, proper list. add another pair and link it in to the list.
goal_print_obj = labels.at(cdr_word.label_id).offset;
fill->pair[1] = std::make_shared<Form>();
fill->pair[1]->kind = FormKind::PAIR;
fill = fill->pair[1];
fill.as_pair()->cdr = goos::PairObject::make_new(goos::EmptyListObject::make_new(),
goos::EmptyListObject::make_new());
fill = fill.as_pair()->cdr;
} else {
// improper list, put the last thing in and end
fill->pair[1] = to_form_script_object(seg, cdr_addr, seen);
fill.as_pair()->cdr = to_form_script_object(seg, cdr_addr, seen);
return result;
}
}
@@ -801,10 +814,10 @@ bool LinkedObjectFile::is_string(int seg, int byte_idx) {
/*!
* Convert a (pointer object) to some nice representation.
*/
std::shared_ptr<Form> LinkedObjectFile::to_form_script_object(int seg,
int byte_idx,
std::vector<bool>& seen) {
std::shared_ptr<Form> result;
goos::Object LinkedObjectFile::to_form_script_object(int seg,
int byte_idx,
std::vector<bool>& seen) {
goos::Object result;
switch (byte_idx & 7) {
case 0:
@@ -812,10 +825,10 @@ std::shared_ptr<Form> LinkedObjectFile::to_form_script_object(int seg,
auto& word = words_by_seg.at(seg).at(byte_idx / 4);
if (word.kind == LinkedWord::SYM_PTR) {
// .symbol xxxx
result = toForm(word.symbol_name);
result = pretty_print::to_symbol(word.symbol_name);
} else if (word.kind == LinkedWord::PLAIN_DATA) {
// .word xxxxx
result = toForm(std::to_string(word.data));
result = pretty_print::to_symbol(std::to_string(word.data));
} else if (word.kind == LinkedWord::PTR) {
// might be a sub-list, or some other random pointer
auto offset = labels.at(word.label_id).offset;
@@ -824,14 +837,14 @@ std::shared_ptr<Form> LinkedObjectFile::to_form_script_object(int seg,
result = to_form_script(seg, offset / 4, seen);
} else {
if (is_string(seg, offset)) {
result = toForm(get_goal_string(seg, offset / 4 - 1));
result = pretty_print::to_symbol(get_goal_string(seg, offset / 4 - 1));
} else {
// some random pointer, just print the label.
result = toForm(labels.at(word.label_id).name);
result = pretty_print::to_symbol(labels.at(word.label_id).name);
}
}
} else if (word.kind == LinkedWord::EMPTY_PTR) {
result = gSymbolTable.getEmptyPair();
result = goos::EmptyListObject::make_new();
} else {
std::string debug;
append_word_to_string(debug, word);
+3 -3
View File
@@ -15,7 +15,7 @@
#include <unordered_set>
#include "LinkedWord.h"
#include "decompiler/Function/Function.h"
#include "decompiler/util/LispPrint.h"
#include "common/goos/PrettyPrinter.h"
/*!
* A label to a location in this object file.
@@ -124,8 +124,8 @@ class LinkedObjectFile {
std::vector<Label> labels;
private:
std::shared_ptr<Form> to_form_script(int seg, int word_idx, std::vector<bool>& seen);
std::shared_ptr<Form> to_form_script_object(int seg, int byte_idx, std::vector<bool>& seen);
goos::Object to_form_script(int seg, int word_idx, std::vector<bool>& seen);
goos::Object to_form_script_object(int seg, int byte_idx, std::vector<bool>& seen);
bool is_empty_list(int seg, int byte_idx);
bool is_string(int seg, int byte_idx);
std::string get_goal_string(int seg, int word_idx);
@@ -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
+54 -21
View File
@@ -18,6 +18,8 @@
#include "common/util/Timer.h"
#include "common/util/FileUtil.h"
#include "decompiler/Function/BasicBlocks.h"
#include "decompiler/IR/BasicOpBuilder.h"
#include "decompiler/IR/CfgBuilder.h"
/*!
* Get a unique name for this object file.
@@ -64,6 +66,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 +75,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 +360,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 +547,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);
}
});
@@ -588,34 +592,52 @@ void ObjectFileDB::analyze_functions() {
// }
}
int total_nontrivial_functions = 0;
int total_resolved_nontrivial_functions = 0;
int total_trivial_cfg_functions = 0;
int total_named_functions = 0;
int total_basic_ops = 0;
int total_failed_basic_ops = 0;
int asm_funcs = 0;
int non_asm_funcs = 0;
int successful_cfg_irs = 0;
std::map<int, std::vector<std::string>> unresolved_by_length;
if (get_config().find_basic_blocks) {
timer.start();
int total_basic_blocks = 0;
for_each_function([&](Function& func, int segment_id, ObjectFileData& data) {
// printf("in %s\n", func.guessed_name.to_string().c_str());
auto blocks = find_blocks_in_function(data.linked_data, segment_id, func);
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();
func.ir = build_cfg_ir(func, *func.cfg, data.linked_data);
non_asm_funcs++;
if (func.ir) {
successful_cfg_irs++;
}
if (func.cfg->is_fully_resolved()) {
resolved_cfg_functions++;
}
} else {
resolved_cfg_functions++;
asm_funcs++;
}
if (func.basic_blocks.size() > 1 && !func.suspected_asm) {
total_nontrivial_functions++;
if (func.cfg->is_fully_resolved()) {
total_resolved_nontrivial_functions++;
} else {
if (!func.guessed_name.empty()) {
unresolved_by_length[func.end_word - func.start_word].push_back(
@@ -624,27 +646,38 @@ void ObjectFileDB::analyze_functions() {
}
}
if (!func.suspected_asm && func.basic_blocks.size() <= 1) {
total_trivial_cfg_functions++;
}
if (!func.guessed_name.empty()) {
total_named_functions++;
}
// if (func.guessed_name.to_string() == "inspect") {
// assert(false);
// }
});
printf("Found %d functions (%d with nontrivial cfgs)\n", total_functions,
total_nontrivial_functions);
printf("Found %d functions (%d with no control flow)\n", total_functions,
total_trivial_cfg_functions);
printf("Named %d/%d functions (%.2f%%)\n", total_named_functions, total_functions,
100.f * float(total_named_functions) / float(total_functions));
printf("Excluding %d asm functions\n", asm_funcs);
printf("Found %d basic blocks in %.3f ms\n", total_basic_blocks, timer.getMs());
printf(" %d/%d functions passed cfg analysis stage (%.2f%%)\n", resolved_cfg_functions,
total_functions, 100.f * float(resolved_cfg_functions) / float(total_functions));
printf(" %d/%d nontrivial cfg's resolved (%.2f%%)\n", total_resolved_nontrivial_functions,
total_nontrivial_functions,
100.f * float(total_resolved_nontrivial_functions) / float(total_nontrivial_functions));
non_asm_funcs, 100.f * float(resolved_cfg_functions) / float(non_asm_funcs));
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));
printf(" %d/%d cfgs converted to ir (%.2f%%)\n", successful_cfg_irs, non_asm_funcs,
100.f * float(successful_cfg_irs) / float(non_asm_funcs));
for (auto& kv : unresolved_by_length) {
printf("LEN %d\n", kv.first);
for (auto& x : kv.second) {
printf(" %s\n", x.c_str());
}
}
// for (auto& kv : unresolved_by_length) {
// printf("LEN %d\n", kv.first);
// for (auto& x : kv.second) {
// printf(" %s\n", x.c_str());
// }
// }
}
}
+2
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);
-1
View File
@@ -1 +0,0 @@
#include "GoalFunction.h"
-17
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
-1
View File
@@ -1 +0,0 @@
#include "GoalSymbol.h"
-39
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
-13
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;
}
}
-25
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
-99
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 + "};";
}
-32
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
-51
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);
}
-28
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
+47 -6
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,
@@ -22,16 +23,56 @@
"write_hexdump_on_v3_only":true,
// to write out "scripts", which are currently just all the linked lists found
"write_scripts":false,
"write_scripts":true,
// Experimental Stuff
"find_basic_blocks":true,
"asm_functions_by_name":[
// gcommon
"ash", "abs", "min", "max", "collide-do-primitives", "draw-bones-check-longest-edge-asm",
"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",
// this one needs more investigation. nothing looks weird about it but it fails...
"camera-change-to",
// two back to back arithmetic shifts...
"texture-relocate",
// this one fails due to false compaction where an else case has only a not expression in it.
"master-is-hopeful-better?",
// fails for unknown reason
"target-falling-anim-trans", "change-brother",
// merged right typecase... can probably handle this
"cspace-inspect-tree",
// these are all valid, but use short circuiting branches in strange ways. There's probably a few compiler uses that we're not
"(method 21 actor-link-info)","(method 20 actor-link-info)","(method 28 collide-shape-prim-mesh)", "(method 35 collide-shape)",
"debug-menu-item-var-render", "(method 14 level)","add-blue-motion","anim-tester-add-newobj","(method 27 orb-cache-top)",
// real asm
"cspace<-parented-transformq-joint!", "blerc-a-fragment", "render-boundary-tri", "render-boundary-quad",
"(method 19 collide-shape-prim-sphere)","vector-segment-distance-point!", "exp", "(method 11 collide-mesh-cache)",
"(method 13 collide-edge-work)", "ambient-inspect",
"(method 11 cpu-thread)", "atan0", "sincos!", "sincos-rad!", "disasm-dma-list", "vblank-handler", "vif1-handler",
"vif1-handler-debug", "entity-actor-count", "decompress-frame-data-pair-to-accumulator",
"decompress-frame-data-to-accumulator", "normalize-frame-quaternions", "clear-frame-accumulator",
"generic-copy-vtx-dclr-dtex", "generic-no-light-dproc-only", "generic-no-light-proc", "mercneric-bittable-asm",
"generic-tie-decompress", "matrix-axis-sin-cos!", "matrix-axis-sin-cos-vu!", "generic-prepare-dma-single",
"(method 13 collide-shape-prim-sphere)", "(method 14 collide-shape-prim-sphere)", "(method 12 collide-shape-prim-sphere)",
"adgif-shader<-texture-with-update!", "generic-interp-dproc", "sprite-draw-distorters", "draw-bones", "(method 9 collide-mesh-cache)",
"(method 18 collide-shape-prim-sphere)","birth-pickup-at-point",
"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)"
]
}
+5 -4
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;
}
+78
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;
}
+46
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
-516
View File
@@ -1,516 +0,0 @@
#include "LispPrint.h"
#include <cassert>
#include <iostream>
#include <vector>
//////// HACK - symbol table now looks up by string, which makes it really stupid and store
// all strings twice.
// should probably just remove it
/*!
* String interning
*/
std::string* SymbolTable::intern(const std::string& str) {
if (map.find(str) == map.end()) {
auto* new_string = new std::string(str);
map[str] = new_string;
return new_string;
} else {
return map[str];
}
}
/*!
* Global interned string table
*/
SymbolTable gSymbolTable;
SymbolTable::SymbolTable() {
empty_pair = std::make_shared<Form>();
empty_pair->kind = FormKind::EMPTY_LIST;
}
SymbolTable::~SymbolTable() {
for (const auto& kv : map)
delete kv.second;
}
/*!
* Convert a form to a one-line string.
*/
std::string Form::toStringSimple() {
std::string result;
buildStringSimple(result);
return result;
}
void Form::buildStringSimple(std::string& str) {
std::vector<FormToken> tokens;
toTokenList(tokens);
for (auto& token : tokens) {
switch (token.kind) {
case TokenKind::WHITESPACE:
str.push_back(' ');
break;
case TokenKind::SYMBOL:
str.append(*token.str);
break;
case TokenKind::OPEN_PAREN:
str.push_back('(');
break;
case TokenKind::DOT:
str.push_back('.');
break;
case TokenKind::CLOSE_PAREN:
str.push_back(')');
break;
case TokenKind::EMPTY_PAIR:
str.append("()");
break;
case TokenKind::SPECIAL_SYMBOL:
str.append(*token.str);
break;
default:
throw std::runtime_error("buildStringSimple unknown token kind");
}
}
}
void Form::toTokenList(std::vector<FormToken>& tokens) {
switch (kind) {
case FormKind::SYMBOL:
tokens.emplace_back(TokenKind::SYMBOL, symbol);
break;
case FormKind::PAIR: {
tokens.emplace_back(TokenKind::OPEN_PAREN);
Form* toPrint = this;
for (;;) {
if (toPrint->kind == FormKind::PAIR) {
toPrint->pair[0]->toTokenList(tokens); // print CAR
toPrint = toPrint->pair[1].get();
if (toPrint->kind == FormKind::EMPTY_LIST) {
tokens.emplace_back(TokenKind::CLOSE_PAREN);
return;
} else {
tokens.emplace_back(TokenKind::WHITESPACE);
}
} else { // not a proper list!
tokens.emplace_back(TokenKind::DOT);
tokens.emplace_back(TokenKind::WHITESPACE);
toPrint->toTokenList(tokens);
tokens.emplace_back(TokenKind::CLOSE_PAREN);
return;
}
}
} break;
case FormKind::EMPTY_LIST:
tokens.emplace_back(TokenKind::EMPTY_PAIR);
break;
default:
throw std::runtime_error("unhandled form type in buildSimpleString");
break;
}
}
///////////////////
// Pretty Printer
///////////////////
/*!
* Linked list node representing a token in the output (whitespace, paren, newline, etc)
*/
struct PrettyPrinterNode {
FormToken* tok = nullptr; // if we aren't a newline, we will have a token.
int line = -1; // line that token occurs on. undef for newlines
int lineIndent = -1; // indent of line. only valid for first token in the line
int offset = -1; // offset of beginning of token from left margin
int specialIndentDelta = 0;
bool is_line_separator = false; // true if line separator (not a token)
PrettyPrinterNode *next = nullptr, *prev = nullptr; // linked list
PrettyPrinterNode* paren =
nullptr; // pointer to open paren if in parens. open paren points to close and vice versa
explicit PrettyPrinterNode(FormToken& _tok) { tok = &_tok; }
PrettyPrinterNode() = default;
};
/*!
* Splice in a line break after the given node, it there isn't one already and if it isn't the last
* node.
*/
static void insertNewlineAfter(PrettyPrinterNode* node, int specialIndentDelta) {
if (node->next && !node->next->is_line_separator) {
auto* nl = new PrettyPrinterNode;
auto* next = node->next;
node->next = nl;
nl->prev = node;
nl->next = next;
next->prev = nl;
nl->is_line_separator = true;
nl->specialIndentDelta = specialIndentDelta;
}
}
/*!
* Splice in a line break before the given node, if there isn't one already and if it isn't the
* first node.
*/
static void insertNewlineBefore(PrettyPrinterNode* node, int specialIndentDelta) {
if (node->prev && !node->prev->is_line_separator) {
auto* nl = new PrettyPrinterNode;
auto* prev = node->prev;
prev->next = nl;
nl->prev = prev;
nl->next = node;
node->prev = nl;
nl->is_line_separator = true;
nl->specialIndentDelta = specialIndentDelta;
}
}
/*!
* Break a list across multiple lines. This is the fundamental reducing operation of this algorithm
*/
static void breakList(PrettyPrinterNode* leftParen) {
assert(!leftParen->is_line_separator);
assert(leftParen->tok->kind == TokenKind::OPEN_PAREN);
auto* rp = leftParen->paren;
assert(rp->tok->kind == TokenKind::CLOSE_PAREN);
for (auto* n = leftParen->next; n && n != rp; n = n->next) {
if (!n->is_line_separator) {
if (n->tok->kind == TokenKind::OPEN_PAREN) {
n = n->paren;
assert(n->tok->kind == TokenKind::CLOSE_PAREN);
insertNewlineAfter(n, 0);
} else if (n->tok->kind != TokenKind::WHITESPACE) {
assert(n->tok->kind != TokenKind::CLOSE_PAREN);
insertNewlineAfter(n, 0);
}
}
}
}
/*!
* Compute proper line numbers, offsets, and indents for a list of tokens with newlines
* Will add newlines for close parens if needed.
*/
static PrettyPrinterNode* propagatePretty(PrettyPrinterNode* list, int line_length) {
// propagate line numbers
PrettyPrinterNode* rv = nullptr;
int line = list->line;
for (auto* n = list; n; n = n->next) {
if (n->is_line_separator) {
line++;
} else {
n->line = line;
// add the weird newline.
if (n->tok->kind == TokenKind::CLOSE_PAREN) {
if (n->line != n->paren->line) {
if (n->prev && !n->prev->is_line_separator) {
insertNewlineBefore(n, 0);
line++;
}
if (n->next && !n->next->is_line_separator) {
insertNewlineAfter(n, 0);
}
}
}
}
}
// compute offsets and indents
std::vector<int> indentStack;
indentStack.push_back(0);
int offset = 0;
PrettyPrinterNode* line_start = list;
bool previous_line_sep = false;
for (auto* n = list; n; n = n->next) {
if (n->is_line_separator) {
previous_line_sep = true;
offset = indentStack.back() += n->specialIndentDelta;
} else {
if (previous_line_sep) {
line_start = n;
n->lineIndent = offset;
previous_line_sep = false;
}
n->offset = offset;
offset += n->tok->toString().length();
if (offset > line_length && !rv)
rv = line_start;
if (n->tok->kind == TokenKind::OPEN_PAREN) {
if (!n->prev || n->prev->is_line_separator) {
indentStack.push_back(offset + 1);
} else {
indentStack.push_back(offset - 1);
}
}
if (n->tok->kind == TokenKind::CLOSE_PAREN) {
indentStack.pop_back();
}
}
}
return rv;
}
/*!
* Get the token on the start of the next line. nullptr if we're the last line.
*/
static PrettyPrinterNode* getNextLine(PrettyPrinterNode* start) {
assert(!start->is_line_separator);
int line = start->line;
for (;;) {
if (start->is_line_separator || start->line == line) {
if (start->next)
start = start->next;
else
return nullptr;
} else {
break;
}
}
return start;
}
/*!
* Get the next open paren on the current line (can start in the middle of line, not inclusive of
* start) nullptr if there's no open parens on the rest of this line.
*/
static PrettyPrinterNode* getNextListOnLine(PrettyPrinterNode* start) {
int line = start->line;
assert(!start->is_line_separator);
if (!start->next || start->next->is_line_separator)
return nullptr;
start = start->next;
while (!start->is_line_separator && start->line == line) {
if (start->tok->kind == TokenKind::OPEN_PAREN)
return start;
if (!start->next)
return nullptr;
start = start->next;
}
return nullptr;
}
/*!
* Get the first open paren on the current line (can start in the middle of line, inclusive of
* start) nullptr if there's no open parens on the rest of this line
*/
static PrettyPrinterNode* getFirstListOnLine(PrettyPrinterNode* start) {
int line = start->line;
assert(!start->is_line_separator);
while (!start->is_line_separator && start->line == line) {
if (start->tok->kind == TokenKind::OPEN_PAREN)
return start;
if (!start->next)
return nullptr;
start = start->next;
}
return nullptr;
}
/*!
* Get the first token on the first line which exceeds the max length
*/
static PrettyPrinterNode* getFirstBadLine(PrettyPrinterNode* start, int line_length) {
assert(!start->is_line_separator);
int currentLine = start->line;
auto* currentLineNode = start;
for (;;) {
if (start->is_line_separator) {
assert(start->next);
start = start->next;
} else {
if (start->line != currentLine) {
currentLine = start->line;
currentLineNode = start;
}
if (start->offset > line_length) {
return currentLineNode;
}
if (!start->next) {
return nullptr;
}
start = start->next;
}
}
}
/*!
* Break insertion algorithm.
*/
static void insertBreaksAsNeeded(PrettyPrinterNode* head, int line_length) {
PrettyPrinterNode* last_line_complete = nullptr;
PrettyPrinterNode* line_to_start_line_search = head;
// loop over lines
for (;;) {
// compute lines as needed
propagatePretty(head, line_length);
// search for a bad line starting at the last line we fixed
PrettyPrinterNode* candidate_line = getFirstBadLine(line_to_start_line_search, line_length);
// if we got the same line we started on, this means we couldn't fix it.
if (candidate_line == last_line_complete) {
candidate_line = nullptr; // so we say our candidate was bad and try to find another
PrettyPrinterNode* next_line = getNextLine(line_to_start_line_search);
if (next_line) {
candidate_line = getFirstBadLine(next_line, line_length);
}
}
if (!candidate_line)
break;
// okay, we have a line which needs fixing.
assert(!candidate_line->prev || candidate_line->prev->is_line_separator);
PrettyPrinterNode* form_to_start = getFirstListOnLine(candidate_line);
for (;;) {
if (!form_to_start) {
printf("pretty printer has failed. Fix the bug or increase the the line length.\n");
assert(false);
}
breakList(form_to_start);
propagatePretty(head, line_length);
if (getFirstBadLine(candidate_line, line_length) != candidate_line) {
break;
}
form_to_start = getNextListOnLine(form_to_start);
if (!form_to_start)
break;
}
last_line_complete = candidate_line;
line_to_start_line_search = candidate_line;
}
}
static void insertSpecialBreaks(PrettyPrinterNode* node) {
for (; node; node = node->next) {
if (!node->is_line_separator && node->tok->kind == TokenKind::SYMBOL) {
std::string& name = *node->tok->str;
if (name == "deftype") {
auto* parent_type_dec = getNextListOnLine(node);
if (parent_type_dec) {
insertNewlineAfter(parent_type_dec->paren, 0);
}
}
}
}
}
std::string Form::toStringPretty(int indent, int line_length) {
(void)indent;
(void)line_length;
std::vector<FormToken> tokens;
toTokenList(tokens);
assert(!tokens.empty());
std::string pretty;
// build linked list of nodes
PrettyPrinterNode* head = new PrettyPrinterNode(tokens[0]);
PrettyPrinterNode* node = head;
head->line = 0;
head->offset = 0;
head->lineIndent = 0;
int offset = head->tok->toString().length();
for (size_t i = 1; i < tokens.size(); i++) {
node->next = new PrettyPrinterNode(tokens[i]);
node->next->prev = node;
node = node->next;
node->line = 0;
node->offset = offset;
offset += node->tok->toString().length();
node->lineIndent = 0;
}
// attach parens.
std::vector<PrettyPrinterNode*> parenStack;
parenStack.push_back(nullptr);
for (PrettyPrinterNode* n = head; n; n = n->next) {
if (n->tok->kind == TokenKind::OPEN_PAREN) {
parenStack.push_back(n);
} else if (n->tok->kind == TokenKind::CLOSE_PAREN) {
n->paren = parenStack.back();
parenStack.back()->paren = n;
parenStack.pop_back();
} else {
n->paren = parenStack.back();
}
}
assert(parenStack.size() == 1);
assert(!parenStack.back());
insertSpecialBreaks(head);
propagatePretty(head, line_length);
insertBreaksAsNeeded(head, line_length);
// write to string
bool newline_prev = true;
for (PrettyPrinterNode* n = head; n; n = n->next) {
if (n->is_line_separator) {
pretty.push_back('\n');
newline_prev = true;
} else {
if (newline_prev) {
pretty.append(n->lineIndent, ' ');
newline_prev = false;
if (n->tok->kind == TokenKind::WHITESPACE)
continue;
}
pretty.append(n->tok->toString());
}
}
for (;;) {
if (!head)
break;
auto* next = head->next;
delete head;
head = next;
}
return pretty;
}
std::shared_ptr<Form> toForm(const std::string& str) {
auto f = std::make_shared<Form>();
f->kind = FormKind::SYMBOL;
f->symbol = gSymbolTable.intern(str);
return f;
}
std::shared_ptr<Form> buildList(std::shared_ptr<Form> form) {
auto f = std::make_shared<Form>();
f->kind = FormKind::PAIR;
f->pair[0] = form;
f->pair[1] = gSymbolTable.getEmptyPair();
return f;
}
std::shared_ptr<Form> buildList(const std::string& str) {
return buildList(toForm(str));
}
std::shared_ptr<Form> buildList(std::shared_ptr<Form>* forms, int count) {
auto f = std::make_shared<Form>();
f->kind = FormKind::PAIR;
f->pair[0] = forms[0];
if (count - 1) {
f->pair[1] = buildList(forms + 1, count - 1);
} else {
f->pair[1] = gSymbolTable.getEmptyPair();
}
return f;
}
std::shared_ptr<Form> buildList(std::vector<std::shared_ptr<Form>>& forms) {
if (forms.empty()) {
return gSymbolTable.getEmptyPair();
}
return buildList(forms.data(), forms.size());
}
-144
View File
@@ -1,144 +0,0 @@
#pragma once
#ifndef JAK2_DISASSEMBLER_LISPPRINT_H
#define JAK2_DISASSEMBLER_LISPPRINT_H
#include <memory>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>
/*!
* What type of thing is it?
*/
enum class FormKind {
SYMBOL,
HEX_NUMBER,
DECIMAL_NUMBER,
BINARY_NUMBER,
SIGNED_NUMBER,
STRING,
EMPTY_LIST,
PAIR
};
/*!
* Tokens in a textual representation
*/
enum class TokenKind {
WHITESPACE,
SYMBOL,
OPEN_PAREN,
DOT,
CLOSE_PAREN,
EMPTY_PAIR,
SPECIAL_SYMBOL
};
/*!
* Token in a text representation
*/
struct FormToken {
explicit FormToken(TokenKind _kind, std::string* _str = nullptr) : kind(_kind), str(_str) {}
TokenKind kind;
union {
std::string* str;
};
std::string toString() {
std::string s;
switch (kind) {
case TokenKind::WHITESPACE:
s.push_back(' ');
break;
case TokenKind::SYMBOL:
s.append(*str);
break;
case TokenKind::OPEN_PAREN:
s.push_back('(');
break;
case TokenKind::DOT:
s.push_back('.');
break;
case TokenKind::CLOSE_PAREN:
s.push_back(')');
break;
case TokenKind::EMPTY_PAIR:
s.append("()");
break;
case TokenKind::SPECIAL_SYMBOL:
s.append(*str);
break;
default:
throw std::runtime_error("toString unknown token kind");
}
return s;
}
};
/*!
* S-Expression Form
*/
class Form {
public:
FormKind kind;
std::string* symbol;
std::shared_ptr<Form> pair[2];
std::string toStringSimple();
std::string toStringPretty(int indent = 0, int line_length = 80);
void toTokenList(std::vector<FormToken>& tokens);
private:
void buildStringSimple(std::string& str);
};
/*!
* Symbol table to reduce the number of strings everywhere.
*/
class SymbolTable {
public:
SymbolTable();
std::string* intern(const std::string& str);
~SymbolTable();
std::shared_ptr<Form> getEmptyPair() { return empty_pair; }
private:
std::unordered_map<std::string, std::string*> map;
std::shared_ptr<Form> empty_pair;
};
/*!
* Global symbol table used for the compiler/decompiler
*/
extern SymbolTable gSymbolTable;
std::shared_ptr<Form> toForm(const std::string& str); //
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);
template <typename... Args>
std::shared_ptr<Form> buildList(const std::string& str, Args... rest) {
auto f = std::make_shared<Form>();
f->kind = FormKind::PAIR;
f->pair[0] = toForm(str);
f->pair[1] = buildList(rest...);
return f;
}
template <typename... Args>
std::shared_ptr<Form> buildList(std::shared_ptr<Form> car, Args... rest) {
auto f = std::make_shared<Form>();
f->kind = FormKind::PAIR;
f->pair[0] = car;
f->pair[1] = buildList(rest...);
return f;
}
#endif // JAK2_DISASSEMBLER_LISPPRINT_H
+5 -5
View File
@@ -26,7 +26,7 @@ endif(CMAKE_COMPILER_IS_GNUCXX)
enable_language(ASM_NASM)
set(CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS} asm)
set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <INCLUDES> <FLAGS> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <INCLUDES> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
set_source_files_properties(kernel/asm_funcs.asm PROPERTIES COMPILE_FLAGS "-g")
set(RUNTIME_SOURCE
runtime.cpp
@@ -81,12 +81,12 @@ add_executable(gk main.cpp)
IF (WIN32)
# set stuff for windows
target_link_libraries(runtime mman cross_sockets common_util)
target_link_libraries(gk cross_sockets mman common_util runtime)
target_link_libraries(runtime mman cross_sockets common_util spdlog)
target_link_libraries(gk cross_sockets mman common_util runtime spdlog)
ELSE()
# set stuff for other systems
target_link_libraries(runtime pthread cross_sockets common_util)
target_link_libraries(gk cross_sockets pthread common_util runtime)
target_link_libraries(runtime pthread cross_sockets common_util spdlog)
target_link_libraries(gk cross_sockets pthread common_util runtime spdlog)
ENDIF()
+6 -6
View File
@@ -16,6 +16,7 @@
#include "game/common/ramdisk_rpc_types.h"
#include "game/common/loader_rpc_types.h"
#include "game/common/play_rpc_types.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
using namespace ee;
@@ -175,9 +176,8 @@ void BeginLoadingDGO(const char* name, Ptr<u8> buffer1, Ptr<u8> buffer2, Ptr<u8>
// file name
strcpy(sMsg[msgID].name, name);
printf("[Begin Loading DGO RPC] %s, 0x%x, 0x%x, 0x%x\n", name, buffer1.offset, buffer2.offset,
currentHeap.offset);
spdlog::debug("[Begin Loading DGO RPC] {}, 0x{}, 0x{}, 0x{}", name, buffer1.offset,
buffer2.offset, currentHeap.offset);
// this RPC will return once we have loaded the first object file.
// but we call async, so we don't block here.
RpcCall(DGO_RPC_CHANNEL, DGO_RPC_LOAD_FNO, true, mess, sizeof(RPC_Dgo_Cmd), mess,
@@ -299,7 +299,7 @@ void load_and_link_dgo(u64 name_gstr, u64 heap_info, u64 flag, u64 buffer_size)
* This does not use the mutli-threaded linker and will block until the entire file is done.e
*/
void load_and_link_dgo_from_c(const char* name, Ptr<kheapinfo> heap, u32 linkFlag, s32 bufferSize) {
printf("[Load and Link DGO From C] %s\n", name);
spdlog::debug("[Load and Link DGO From C] {}", name);
u32 oldShowStall = sShowStallMsg;
// remember where the heap top point is so we can clear temporary allocations
@@ -348,7 +348,7 @@ void load_and_link_dgo_from_c(const char* name, Ptr<kheapinfo> heap, u32 linkFla
char objName[64];
strcpy(objName, (dgoObj + 4).cast<char>().c()); // name from dgo object header
printf("[link and exec] %s %d\n", objName, lastObjectLoaded);
spdlog::debug("[link and exec] {} {}", objName, lastObjectLoaded);
link_and_exec(obj, objName, objSize, heap, linkFlag); // link now!
// inform IOP we are done
@@ -357,4 +357,4 @@ void load_and_link_dgo_from_c(const char* name, Ptr<kheapinfo> heap, u32 linkFla
}
}
sShowStallMsg = oldShowStall;
}
}
+22 -22
View File
@@ -25,7 +25,7 @@
#include "game/sce/libcdvd_ee.h"
#include "game/sce/stubs.h"
#include "common/symbols.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
using namespace ee;
/*!
@@ -148,13 +148,13 @@ void InitParms(int argc, const char* const* argv) {
* DONE, EXACT
*/
void InitCD() {
printf("Initializing CD drive\nThis may take a while ...\n");
spdlog::info("Initializing CD drive\nThis may take a while...\n");
sceCdInit(SCECdINIT);
sceCdMmode(SCECdDVD);
while (sceCdDiskReady(0) == SCECdNotReady) {
printf("Drive not ready ... insert a disk!\n");
spdlog::debug("Drive not ready... insert a disk!\n");
}
printf("Disk type %d\n", sceCdGetDiskType());
spdlog::debug("Disk type {}\n", sceCdGetDiskType());
}
/*!
@@ -172,22 +172,22 @@ void InitIOP() {
if (!reboot) {
// reboot with development IOP kernel
printf("Rebooting IOP...\n");
spdlog::debug("Rebooting IOP...");
while (!sceSifRebootIop("host0:/usr/local/sce/iop/modules/ioprp221.img")) {
printf("Failed, retrying...\n");
spdlog::debug("Failed, retrying");
}
while (!sceSifSyncIop()) {
printf("Syncing...\n");
spdlog::debug("Syncing...");
}
} else {
// reboot with IOP kernel off of the disk
// reboot with development IOP kernel
printf("Rebooting IOP...\n");
spdlog::debug("Rebooting IOP...");
while (!sceSifRebootIop("cdrom0:\\DRIVERS\\IOPRP221.IMG;1")) {
printf("Failed, retrying...\n");
spdlog::debug("Failed, retrying");
}
while (!sceSifSyncIop()) {
printf("Syncing...\n");
spdlog::debug("Syncing...");
}
}
@@ -238,7 +238,7 @@ void InitIOP() {
sceSifLoadModule("host0:/usr/home/src/989snd10/iop/989ERR.IRX", 0, nullptr);
printf("Initializing CD library\n");
spdlog::debug("Initializing CD library...");
auto rv = sceSifLoadModule("host0:binee/overlord.irx", cmd + len + 1 - overlord_boot_command,
overlord_boot_command);
if (rv < 0) {
@@ -270,7 +270,7 @@ void InitIOP() {
MsgErr("loading 989snd.irx failed\n");
}
printf("Initializing CD library in ISO_CD mode\n");
spdlog::debug("Initializing CD library in ISO_CD mode...");
auto rv = sceSifLoadModule("cdrom0:\\\\DRIVERS\\\\OVERLORD.IRX;1",
cmd + len + 1 - overlord_boot_command, overlord_boot_command);
if (rv < 0) {
@@ -281,7 +281,7 @@ void InitIOP() {
if (rv < 0) {
MsgErr("MC driver init failed %d\n", rv);
} else {
printf("InitIOP OK\n");
spdlog::info("InitIOP OK");
}
}
@@ -302,8 +302,8 @@ int InitMachine() {
// initialize the global heap
u32 global_heap_size = GLOBAL_HEAP_END - HEAP_START;
float size_mb = ((float)global_heap_size) / (float)(1 << 20);
printf("gkernel: global heap - 0x%x to 0x%x (size %.3f MB)\n", HEAP_START, GLOBAL_HEAP_END,
size_mb);
spdlog::info("gkernel: global heap 0x{} to 0x{} (size {} MB)", HEAP_START, GLOBAL_HEAP_END,
size_mb);
kinitheap(kglobalheap, Ptr<u8>(HEAP_START), global_heap_size);
// initialize the debug heap, if appropriate
@@ -312,8 +312,8 @@ int InitMachine() {
kinitheap(kdebugheap, Ptr<u8>(DEBUG_HEAP_START), debug_heap_size);
float debug_size_mb = ((float)debug_heap_size) / (float)(1 << 20);
float gap_size_mb = ((float)DEBUG_HEAP_START - GLOBAL_HEAP_END) / (float)(1 << 20);
printf("gkernel: debug heap - 0x%x to 0x%x (size %.3f MB, gap %.3f MB)\n", DEBUG_HEAP_START,
debug_heap_end, debug_size_mb, gap_size_mb);
spdlog::info("gkernel: global heap 0x{} to 0x{} (size {} MB, gap {} MB)", DEBUG_HEAP_START,
debug_heap_end, debug_size_mb, gap_size_mb);
} else {
// if no debug, we make the kheapinfo structure NULL so GOAL knows not to use it.
kdebugheap.offset = 0;
@@ -338,9 +338,9 @@ int InitMachine() {
InitGoalProto();
}
printf("InitSound\n");
spdlog::info("InitSound");
InitSound(); // do nothing!
printf("InitRPC\n");
spdlog::info("InitRPC");
InitRPC(); // connect to IOP
reset_output(); // reset output buffers
clear_print();
@@ -350,9 +350,9 @@ int InitMachine() {
return goal_status;
}
printf("InitListenerConnect\n");
spdlog::info("InitListenerConnect");
InitListenerConnect();
printf("InitCheckListener\n");
spdlog::info("InitCheckListener");
InitCheckListener();
Msg(6, "kernel: machine started\n");
return 0;
@@ -619,7 +619,7 @@ void InitMachineScheme() {
new_pair(s7.offset + FIX_SYM_GLOBAL_HEAP, *((s7 + FIX_SYM_PAIR_TYPE).cast<u32>()),
make_string_from_c("common"), kernel_packages->value);
printf("calling play!\n");
spdlog::info("calling fake play~");
call_goal_function_by_name("play");
}
}
+5 -2
View File
@@ -19,6 +19,7 @@
#include "common/symbols.h"
#include "common/versions.h"
#include "common/goal_constants.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
//! Controls link mode when EnableMethodSet = 0, MasterDebug = 1, DiskBoot = 0. Will enable a
//! warning message if EnableMethodSet = 1
@@ -1895,8 +1896,10 @@ s32 InitHeapAndSymbol() {
(kernel_version >> 3) & 0xffff);
return -1;
} else {
printf("Got correct kernel version %d.%d\n", kernel_version >> 0x13,
(kernel_version >> 3) & 0xffff);
spdlog::info("Got correct kernel version {}.{}", kernel_version >> 0x13,
(kernel_version >> 3) & 0xffff);
// printf("Got correct kernel version %d.%d\n", kernel_version >> 0x13,
// (kernel_version >> 3) & 0xffff);
}
}
+6 -2
View File
@@ -5,14 +5,18 @@
#include <cstdio>
#include "runtime.h"
#include "common/versions.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include "third-party/spdlog/include/spdlog/sinks/basic_file_sink.h"
int main(int argc, char** argv) {
while (true) {
spdlog::set_level(spdlog::level::debug);
// run the runtime in a loop so we can reset the game and have it restart cleanly
printf("gk %d.%d\n", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR);
spdlog::info("gk {}.{} OK!\n", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR);
if (exec_runtime(argc, argv) == 2) {
return 0;
}
}
return 0;
}
}
+13 -6
View File
@@ -16,6 +16,7 @@
#include "isocommon.h"
#include "overlord.h"
#include "common/util/FileUtil.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
using namespace iop;
@@ -215,7 +216,8 @@ uint32_t FS_GetLength(FileRecord* fr) {
* This is an ISO FS API Function
*/
LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) {
printf("[OVERLORD] FS Open %s\n", fr->name); // Added
// printf("[OVERLORD] FS Open %s\n", fr->name); // Added
spdlog::debug("[OVERLORD] FS Open {}", fr->name);
LoadStackEntry* selected = nullptr;
// find first unused spot on load stack.
for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) {
@@ -229,7 +231,8 @@ LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) {
return selected;
}
}
printf("[OVERLORD ISO CD] Failed to FS_Open %s\n", fr->name);
// printf("[OVERLORD ISO CD] Failed to FS_Open %s\n", fr->name);
spdlog::warn("[OVERLORD] Failed to FS Open {}", fr->name);
ExitIOP();
return nullptr;
}
@@ -240,7 +243,8 @@ LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) {
* This is an ISO FS API Function
*/
LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) {
printf("[OVERLORD] FS Open %s\n", fr->name); // Added
// printf("[OVERLORD] FS Open %s\n", fr->name); // Added
spdlog::debug("[OVERLORD] FS_OpenWad {}", fr->name);
LoadStackEntry* selected = nullptr;
for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) {
if (!sLoadStack[i].fr) {
@@ -250,7 +254,8 @@ LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) {
return selected;
}
}
printf("[OVERLORD ISO CD] Failed to FS_OpenWad %s\n", fr->name);
// printf("[OVERLORD ISO CD] Failed to FS_OpenWad %s\n", fr->name);
spdlog::warn("[OVERLORD] Failed to FS_OpenWad {}", fr->name);
ExitIOP();
return nullptr;
}
@@ -260,7 +265,8 @@ LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) {
* This is an ISO FS API Function
*/
void FS_Close(LoadStackEntry* fd) {
printf("[OVERLORD] FS Close %s\n", fd->fr->name);
// printf("[OVERLORD] FS Close %s\n", fd->fr->name);
spdlog::debug("[OVERLORD] FS_Close {}", fd->fr->name);
// close the FD
fd->fr = nullptr;
@@ -279,7 +285,8 @@ uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len) {
int32_t real_size = len;
if (len < 0) {
// not sure what this is about...
printf("[OVERLORD ISO CD] negative length warning!\n");
// printf("[OVERLORD ISO CD] negative length warning!\n");
spdlog::warn("[OVERLORD ISO CD] Negative length warning!");
real_size = len + 0x7ff;
}
+7 -4
View File
@@ -4,6 +4,7 @@
* This is a huge mess
*/
#include "third-party/spdlog/include/spdlog/spdlog.h"
#include <assert.h>
#include <cstring>
#include <cstdio>
@@ -492,9 +493,11 @@ u32 RunDGOStateMachine(IsoMessage* _cmd, IsoBufferHeader* buffer) {
// if we are done with header
if (cmd->bytes_processed == sizeof(DgoHeader)) {
printf("[Overlord DGO] Got DGO file header for %s with %d objects\n",
cmd->dgo_header.name,
cmd->dgo_header.object_count); // added
// printf("[Overlord DGO] Got DGO file header for %s with %d objects\n",
// cmd->dgo_header.name,
// cmd->dgo_header.object_count); // added
spdlog::info("[Overlord DGO] Got DGO file header for {} with {} objects",
cmd->dgo_header.name, cmd->dgo_header.object_count);
cmd->bytes_processed = 0;
cmd->objects_loaded = 0;
if (cmd->dgo_header.object_count == 1) {
@@ -923,4 +926,4 @@ void CancelDGO(RPC_Dgo_Cmd* cmd) {
// TODO - VAG_MarkLoopStart
// TODO - VAG_MarkLoopEnd
// TODO - VAG_MarkNonloopStart
// TODO - VAG_MarkNonloopEnd
// TODO - VAG_MarkNonloopEnd
+6 -3
View File
@@ -1,5 +1,6 @@
#include "iso_api.h"
#include "game/sce/iop.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
using namespace iop;
@@ -7,7 +8,8 @@ using namespace iop;
* Load a File to IOP memory (blocking)
*/
void LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length) {
printf("[OVERLORD] LoadISOFileToIOP %s, %d/%d bytes\n", file->name, length, file->size);
// printf("[OVERLORD] LoadISOFileToIOP %s, %d/%d bytes\n", file->name, length, file->size);
spdlog::debug("[OVERLORD] LoadISOFileToIOP {}, {}/{} bytes", file->name, length, file->size);
IsoCommandLoadSingle cmd;
cmd.cmd_id = LOAD_TO_IOP_CMD_ID;
cmd.messagebox_to_reply = 0;
@@ -27,7 +29,8 @@ void LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length) {
* Load a File to IOP memory (blocking)
*/
void LoadISOFileToEE(FileRecord* file, uint32_t addr, uint32_t length) {
printf("[OVERLORD] LoadISOFileToEE %s, %d/%d bytes\n", file->name, length, file->size);
// printf("[OVERLORD] LoadISOFileToEE %s, %d/%d bytes\n", file->name, length, file->size);
spdlog::debug("[OVERLORD] LoadISOFileToEE {}, {}/{} bytes", file->name, length, file->size);
IsoCommandLoadSingle cmd;
cmd.cmd_id = LOAD_TO_EE_CMD_ID;
cmd.messagebox_to_reply = 0;
@@ -41,4 +44,4 @@ void LoadISOFileToEE(FileRecord* file, uint32_t addr, uint32_t length) {
if (cmd.status) {
cmd.length_to_copy = 0;
}
}
}
+17 -14
View File
@@ -68,7 +68,7 @@ void deci2_runner(SystemThreadInterface& iface) {
iface.initialization_complete();
// in our own thread, wait for the EE to register the first protocol driver
printf("[DECI2] waiting for EE to register protos\n");
spdlog::debug("[DECI2] Waiting for EE to register protos");
server.wait_for_protos_ready();
// then allow the server to accept connections
if (!server.init()) {
@@ -76,11 +76,13 @@ void deci2_runner(SystemThreadInterface& iface) {
}
printf("[DECI2] waiting for listener...\n");
// spdlog::debug("[DECI2] Waiting for listener..."); --> disabled temporarily, some weird race
// condition?
bool saw_listener = false;
while (!iface.get_want_exit()) {
if (server.check_for_listener()) {
if (!saw_listener) {
printf("[DECI2] Connected!\n");
spdlog::debug("[DECI2] Connected!");
}
saw_listener = true;
// we have a listener, run!
@@ -117,19 +119,19 @@ void ee_runner(SystemThreadInterface& iface) {
}
if (g_ee_main_mem == (u8*)(-1)) {
printf(" Failed to initialize main memory! %s\n", strerror(errno));
spdlog::debug("Failed to initialize main memory! {}", strerror(errno));
iface.initialization_complete();
return;
}
printf(" Main memory mapped at 0x%016llx\n", (u64)(g_ee_main_mem));
printf(" Main memory size 0x%x bytes (%.3f MB)\n", EE_MAIN_MEM_SIZE,
(double)EE_MAIN_MEM_SIZE / (1 << 20));
spdlog::debug("Main memory mapped at 0x{:016x}", (u64)(g_ee_main_mem));
spdlog::debug("Main memory size 0x{} bytes ({} MB)", EE_MAIN_MEM_SIZE,
(double)EE_MAIN_MEM_SIZE / (1 << 20));
printf("[EE] Initialization complete!\n");
spdlog::debug("[EE] Initialization complete!");
iface.initialization_complete();
printf("[EE] Run!\n");
spdlog::debug("[EE] Run!");
memset((void*)g_ee_main_mem, 0, EE_MAIN_MEM_SIZE);
// prevent access to the first 1 MB of memory.
@@ -151,7 +153,7 @@ void ee_runner(SystemThreadInterface& iface) {
kprint_init_globals();
goal_main(g_argc, g_argv);
printf("[EE] Done!\n");
spdlog::debug("[EE] Done!");
// // kill the IOP todo
iop::LIBRARY_kill();
@@ -167,7 +169,7 @@ void ee_runner(SystemThreadInterface& iface) {
*/
void iop_runner(SystemThreadInterface& iface) {
IOP iop;
printf("[IOP] Restart!\n");
spdlog::debug("[IOP] Restart!");
iop.reset_allocator();
ee::LIBRARY_sceSif_register(&iop);
iop::LIBRARY_register(&iop);
@@ -190,12 +192,12 @@ void iop_runner(SystemThreadInterface& iface) {
iface.initialization_complete();
printf("[IOP] Wait for OVERLORD to be started...\n");
spdlog::debug("[IOP] Wait for OVERLORD to start...");
iop.wait_for_overlord_start_cmd();
if (iop.status == IOP_OVERLORD_INIT) {
printf("[IOP] Run!\n");
spdlog::debug("[IOP] Run!");
} else {
printf("[IOP] shutdown!\n");
spdlog::debug("[IOP] Shutdown!");
return;
}
@@ -257,6 +259,7 @@ u32 exec_runtime(int argc, char** argv) {
// join and exit
tm.join();
printf("GOAL Runtime Shutdown (code %d)\n", MasterExit);
// printf("GOAL Runtime Shutdown (code %d)\n", MasterExit);
spdlog::info("GOAL Runtime Shutdown (code {})", MasterExit);
return MasterExit;
}
+2 -1
View File
@@ -69,7 +69,8 @@ s32 sceDeci2Open(u16 protocol, void* opt, void (*handler)(s32 event, s32 param,
drv.id = protocol_count + 1;
drv.active = true;
protocols[protocol_count++] = drv;
printf("[DECI2] Add new protocol driver %d for 0x%x\n", drv.id, drv.protocol);
// printf("[DECI2] Add new protocol driver %d for 0x%x\n", drv.id, drv.protocol);
spdlog::info("[DECI2] Add new protocol driver {} for 0x{}", drv.id, drv.protocol);
server->unlock();
if (protocol_count == 1) {
+1
View File
@@ -4,6 +4,7 @@
#define JAK1_IOP_H
#include "common/common_types.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
#define SMEM_Low (0)
#define SMEM_High (1)
+1
View File
@@ -19,6 +19,7 @@
#include <condition_variable>
#include <functional>
#include "game/system/deci_common.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
class Deci2Server {
public:
+9 -6
View File
@@ -3,6 +3,7 @@
#endif
#include "SystemThread.h"
#include "third-party/spdlog/include/spdlog/spdlog.h"
//////////////////////
// Thread Manager //
@@ -13,7 +14,8 @@
*/
SystemThread& SystemThreadManager::create_thread(const std::string& name) {
if (thread_count >= MAX_SYSTEM_THREADS) {
throw std::runtime_error("Out of System Threads! Please increase MAX_SYSTEM_THREADS");
spdlog::critical("Out of System Threads! MAX_SYSTEM_THREADS is ", MAX_SYSTEM_THREADS);
throw std::runtime_error("Out of System Threads! Please increase MAX_SYSTEM_THREADS");
}
auto& thread = threads[thread_count];
@@ -49,7 +51,7 @@ void SystemThreadManager::print_stats() {
*/
void SystemThreadManager::shutdown() {
for (int i = 0; i < thread_count; i++) {
printf("# Stop %s\n", threads[i].name.c_str());
spdlog::debug("# Stop {}", threads[i].name.c_str());
threads[i].stop();
}
}
@@ -59,7 +61,7 @@ void SystemThreadManager::shutdown() {
*/
void SystemThreadManager::join() {
for (int i = 0; i < thread_count; i++) {
printf("# Join %s\n", threads[i].name.c_str());
spdlog::debug(" # Join {}", threads[i].name.c_str());
if (threads[i].running) {
threads[i].join();
}
@@ -73,7 +75,7 @@ void* bootstrap_thread_func(void* x) {
SystemThread* thd = (SystemThread*)x;
SystemThreadInterface iface(thd);
thd->function(iface);
printf("[SYSTEM] Thread %s is returning\n", thd->name.c_str());
spdlog::debug("[SYSTEM] Thread {} is returning", thd->name.c_str());
return nullptr;
}
@@ -81,7 +83,8 @@ void* bootstrap_thread_func(void* x) {
* Start a thread and wait for its initialization
*/
void SystemThread::start(std::function<void(SystemThreadInterface&)> f) {
printf("# Initialize %s...\n", name.c_str());
spdlog::debug("# Initialize {}...", name.c_str());
function = f;
thread = std::thread(bootstrap_thread_func, this);
running = true;
@@ -118,7 +121,7 @@ void SystemThreadInterface::initialization_complete() {
std::unique_lock<std::mutex> mlk(thread.initialization_mutex);
thread.initialization_complete = true;
thread.initialization_cv.notify_all();
printf("# %s initialized\n", thread.name.c_str());
spdlog::debug("# {} initialized", thread.name.c_str());
}
/*!
+2 -2
View File
@@ -60,8 +60,8 @@ void find_basic_blocks(RegAllocCache* cache, const AllocationInput& in) {
}
}
if (!found) {
printf("[RegAlloc Error] couldn't find basic block beginning with instr %d of %lld\n", instr,
in.instructions.size());
printf("[RegAlloc Error] couldn't find basic block beginning with instr %d of %d\n", instr,
int(in.instructions.size()));
}
assert(found);
return result;
+17 -15
View File
@@ -2,21 +2,23 @@ include("goalc/CMakeLists.txt")
add_executable(goalc-test
test_main.cpp
#test_test.cpp
#test_reader.cpp
#test_goos.cpp
#test_listener_deci2.cpp
#test_kernel.cpp
#all_jak1_symbols.cpp
#test_type_system.cpp
#test_CodeTester.cpp
#test_emitter_slow.cpp
#test_emitter_loads_and_store.cpp
#test_emitter_xmm32.cpp
#test_emitter_integer_math.cpp
#test_common_util.cpp
${GOALC_TEST_FRAMEWORK_SOURCES}
${GOALC_TEST_CASES})
test_test.cpp
test_reader.cpp
test_goos.cpp
test_listener_deci2.cpp
test_kernel.cpp
all_jak1_symbols.cpp
test_type_system.cpp
test_CodeTester.cpp
test_emitter_slow.cpp
test_emitter_loads_and_store.cpp
test_emitter_xmm32.cpp
test_emitter_integer_math.cpp
test_common_util.cpp
test_compiler_and_runtime.cpp
test_deftype.cpp
${GOALC_TEST_FRAMEWORK_SOURCES}
${GOALC_TEST_CASES})
enable_testing()
+62
View File
@@ -0,0 +1,62 @@
#include "gtest/gtest.h"
#include "common/goos/Reader.h"
#include "common/util/FileUtil.h"
#include "common/goos/PrettyPrinter.h"
using namespace goos;
namespace {
Object read(const std::string& str) {
auto body = pretty_print::get_pretty_printer_reader().read_from_string(str).as_pair()->cdr;
EXPECT_TRUE(body.as_pair()->cdr.is_empty_list());
return body.as_pair()->car;
}
std::string pprint(const Object& o, int len = 80) {
return pretty_print::to_string(o, len);
}
// read then pretty print a string.
std::string ppr(const std::string& in, int len = 80) {
return pprint(read(in), len);
}
} // namespace
TEST(PrettyPrinter, Basics) {
EXPECT_EQ(ppr("test"), "test");
EXPECT_EQ(ppr("(l 12 asdf)"), "(l 12 asdf)");
// force it to break
EXPECT_EQ(ppr("(thing 12 asd asfd sdfjk)", 10), "(thing\n 12\n asd\n asfd\n sdfjk\n )");
}
TEST(PrettyPrinter, ReadAgain) {
// first read the gcommon file
auto gcommon_code = pretty_print::get_pretty_printer_reader().read_from_file(
{"goal_src", "kernel", "gcommon.gc"});
// pretty print it
auto printed_gcommon = pretty_print::to_string(gcommon_code);
auto gcommon_code2 = pretty_print::get_pretty_printer_reader()
.read_from_string(printed_gcommon)
.as_pair()
->cdr.as_pair()
->car;
auto printed_gcommon2 = pretty_print::to_string(gcommon_code);
EXPECT_TRUE(gcommon_code == gcommon_code2);
}
TEST(PrettyPrinter, ReadAgainVeryShortLines) {
// first read the gcommon file
auto gcommon_code = pretty_print::get_pretty_printer_reader().read_from_file(
{"goal_src", "kernel", "gcommon.gc"});
// pretty print it but with a very short line length. This looks terrible but will hopefully
// hit many of the cases for line breaking.
auto printed_gcommon = pretty_print::to_string(gcommon_code, 80);
auto gcommon_code2 = pretty_print::get_pretty_printer_reader()
.read_from_string(printed_gcommon)
.as_pair()
->cdr.as_pair()
->car;
auto printed_gcommon2 = pretty_print::to_string(gcommon_code);
EXPECT_TRUE(gcommon_code == gcommon_code2);
}
Vendored Submodule
+1
Submodule third-party/spdlog added at cbe9448650