jak-project/decompiler/Function/Function.h

207 lines
5.7 KiB
C
Raw Permalink Normal View History

#pragma once
#include <stdexcept>
2020-08-22 23:30:17 -04:00
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
2020-08-22 23:30:17 -04:00
#include "BasicBlocks.h"
#include "CfgVtx.h"
#include "Warnings.h"
#include "common/type_system/TypeSpec.h"
#include "common/type_system/state.h"
#include "decompiler/Disasm/Instruction.h"
#include "decompiler/Disasm/Register.h"
#include "decompiler/analysis/atomic_op_builder.h"
#include "decompiler/config.h"
namespace decompiler {
class DecompilerTypeSystem;
2020-08-22 23:30:17 -04:00
struct FunctionName {
enum class FunctionKind {
2020-08-26 01:21:33 -04:00
UNIDENTIFIED, // hasn't been identified yet.
GLOBAL, // global named function
2020-08-22 23:30:17 -04:00
METHOD,
NV_STATE,
V_STATE,
TOP_LEVEL_INIT
2020-08-22 23:30:17 -04:00
} kind = FunctionKind::UNIDENTIFIED;
2020-08-26 01:21:33 -04:00
std::string function_name; // only applicable for GLOBAL
std::string type_name; // only applicable for METHOD or v state
std::string state_name; // for nv state or v state
StateHandler handler_kind; // for nv state or v state
2020-08-26 01:21:33 -04:00
int method_id = -1; // only applicable for METHOD
int unique_id = -1;
2020-08-22 23:30:17 -04:00
int id_in_object = -1;
std::string object_name;
2020-08-22 23:30:17 -04:00
std::string to_string() const {
2020-08-26 01:21:33 -04:00
switch (kind) {
2020-08-22 23:30:17 -04:00
case FunctionKind::GLOBAL:
return function_name;
case FunctionKind::METHOD:
return "(method " + std::to_string(method_id) + " " + type_name + ")";
case FunctionKind::TOP_LEVEL_INIT:
return "(top-level-login " + object_name + ")";
2020-08-22 23:30:17 -04:00
case FunctionKind::UNIDENTIFIED:
return "(anon-function " + std::to_string(id_in_object) + " " + object_name + ")";
case FunctionKind::NV_STATE:
return fmt::format("({} {})", handler_kind_to_name(handler_kind), state_name);
case FunctionKind::V_STATE:
return fmt::format("({} {} {})", handler_kind_to_name(handler_kind), state_name, type_name);
2020-08-22 23:30:17 -04:00
default:
throw std::runtime_error("Unsupported FunctionKind");
2020-08-22 23:30:17 -04:00
}
}
int get_anon_id() const {
ASSERT(kind == FunctionKind::UNIDENTIFIED);
return id_in_object;
}
2020-08-26 01:21:33 -04:00
bool empty() const { return kind == FunctionKind::UNIDENTIFIED; }
2020-08-22 23:30:17 -04:00
2022-11-12 17:56:07 -05:00
bool is_handler() const {
return kind == FunctionKind::NV_STATE || kind == FunctionKind::V_STATE;
}
bool is_handler(StateHandler shk) const { return is_handler() && handler_kind == shk; }
bool is_event_handler() const { return is_handler(StateHandler::EVENT); }
void set_as_top_level(const std::string& object_file_name) {
kind = FunctionKind::TOP_LEVEL_INIT;
object_name = object_file_name;
}
2020-08-22 23:30:17 -04:00
void set_as_global(std::string name) {
kind = FunctionKind::GLOBAL;
function_name = std::move(name);
}
void set_as_method(std::string tn, int id) {
kind = FunctionKind::METHOD;
type_name = std::move(tn);
method_id = id;
}
void set_as_nv_state(const std::string& state, StateHandler hk) {
state_name = state;
handler_kind = hk;
kind = FunctionKind::NV_STATE;
}
void set_as_v_state(const std::string& type, const std::string& state, StateHandler hk) {
state_name = state;
handler_kind = hk;
kind = FunctionKind::V_STATE;
type_name = type;
}
2020-08-22 23:30:17 -04:00
};
class Function {
public:
Function(int _start_word, int _end_word, GameVersion version);
~Function();
2020-08-22 23:30:17 -04:00
void analyze_prologue(const LinkedObjectFile& file);
void find_global_function_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts);
void find_method_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts);
void find_type_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts);
bool instr_starts_basic_op(int idx);
bool instr_starts_atomic_op(int idx);
const AtomicOp& get_atomic_op_at_instr(int idx);
BlockTopologicalSort bb_topo_sort();
std::string name() const;
2020-08-22 23:30:17 -04:00
TypeSpec type;
2020-08-22 23:30:17 -04:00
int segment = -1;
int start_word = -1;
int end_word = -1; // not inclusive, but does include padding.
FunctionName guessed_name;
std::string state_handler_as_anon_func;
2020-08-22 23:30:17 -04:00
bool suspected_asm = false;
bool is_inspect_method = false;
std::string method_of_type;
2020-08-22 23:30:17 -04:00
std::vector<Instruction> instructions;
std::vector<BasicBlock> basic_blocks;
std::shared_ptr<ControlFlowGraph> cfg = nullptr;
bool cfg_ok = false;
2020-08-22 23:30:17 -04:00
int prologue_start = -1;
int prologue_end = -1;
int epilogue_start = -1;
int epilogue_end = -1;
DecompWarnings warnings;
bool contains_asm_ops = false;
2020-08-22 23:30:17 -04:00
bool attempted_type_analysis = false;
2020-08-22 23:30:17 -04:00
struct Prologue {
bool decoded = false; // have we removed the prologue from basic blocks?
int total_stack_usage = -1;
// ra/fp are treated differently from other register backups
bool ra_backed_up = false;
int ra_backup_offset = -1;
bool fp_backed_up = false;
int fp_backup_offset = -1;
bool fp_set = false;
int n_gpr_backup = 0;
int gpr_backup_offset = -1;
int n_fpr_backup = 0;
int fpr_backup_offset = -1;
int n_stack_var_bytes = 0;
int stack_var_offset = -1;
bool epilogue_ok = false;
std::string to_string(int indent = 0) const;
} prologue;
bool uses_fp_register = false;
struct {
bool atomic_ops_attempted = false;
bool atomic_ops_succeeded = false;
std::shared_ptr<FunctionAtomicOps> atomic_ops = nullptr;
Env env;
std::shared_ptr<FormPool> form_pool = nullptr;
Form* top_form = nullptr;
2021-01-22 20:50:37 -05:00
std::string debug_form_string;
bool print_debug_forms = false;
bool expressions_succeeded = false;
[opengoal] make `none` a child of `object` (#3001) Previously, `object` and `none` were both top-level types. This made decompilation rather messy as they have no LCA and resulted in a lot of variables coming out as type `none` which is very very wrong and additionally there were plenty of casts to `object`. This changes it so `none` becomes a child of `object` (it is still represented by `NullType` which remains unusable in compilation). This change makes `object` the sole top-level type, and the type that can represent *any* GOAL object. I believe this matches the original GOAL built-in type structure. A function that has a return type of `object` can now return an integer or a `none` at the same time. However, keep in mind that the return value of `(none)` is still undefined, just as before. This also makes a cast to `object` meaningless in 90% of the situations it showed up in (as every single thing is already an `object`) and the decompiler will no longer emit them. Casts to `none` are also reduced. Yay! Additionally, state handlers also don't get the final `(none)` printed out anymore. The return type of a state handler is completely meaningless outside the event handler (which is return type `object` anyway) so there are no limitations on what the last form needs to be. I did this instead of making them return `object` to trick the decompiler into not trying to output a variable to be used as a return value (internally, in the decompiler they still have return type `none`, but they have `object` elsewhere). Fixes #1703 Fixes #830 Fixes #928
2023-09-22 05:54:49 -04:00
bool skip_final_none = false;
} ir2;
std::optional<std::string> mips2c_output;
std::vector<std::string> types_defined;
decomp3: more engine stuff, detect non-virtual state inheritance (#3377) - `speech` - `ambient` - `water-h` - `vol-h` - `generic-obs` - `carry-h` - `pilot-h` - `board-h` - `gun-h` - `flut-h` - `indax-h` - `lightjak-h` - `darkjak-h` - `target-util` - `history` - `collide-reaction-target` - `logic-target` - `sidekick` - `projectile` - `voicebox` - `ragdoll-edit` - most of `ragdoll` (not added to gsrc yet) - `curves` - `find-nearest` - `lightjak-wings` - `target-handler` - `target-anim` - `target` - `target2` - `target-swim` - `target-lightjak` - `target-invisible` - `target-death` - `target-gun` - `gun-util` - `board-util` - `target-board` - `board-states` - `mech-h` - `vol` - `vent` - `viewer` - `gem-pool` - `collectables` - `crates` - `secrets-menu` Additionally: - Detection of non-virtual state inheritance - Added a config file that allows overriding the process stack size set by `stack-size-set!` calls - Fix for integer multiplication with `r0` - Fixed detection for the following macros: - `static-attack-info` - `defpart` and `defpartgroup` (probably still needs adjustments, uses Jak 2 implementation at the moment) - `sound-play` (Jak 3 seems to always call `sound-play-by-name` with a `sound-group` of 0, so the macro has been temporarily defaulted to use that) One somewhat significant change made here that should be noted is that the return type of `process::init-from-entity!` was changed to `object`. I've been thinking about this for a while, since it looks a bit nicer without the `(none)` at the end and I have recently encountered init methods that early return `0`.
2024-03-03 15:15:27 -05:00
int process_stack_size = 0;
2020-08-22 23:30:17 -04:00
private:
void check_epilogue(const LinkedObjectFile& file);
2021-03-22 20:04:00 -04:00
void resize_first_block(int new_start, const LinkedObjectFile& file);
std::unordered_map<int, int> instruction_to_basic_op;
std::unordered_map<int, int> basic_op_to_instruction;
2020-08-22 23:30:17 -04:00
};
} // namespace decompiler