mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 21:27:52 -04:00
2969833b2d
- `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`.
119 lines
3.8 KiB
C++
119 lines
3.8 KiB
C++
#include <stdexcept>
|
|
|
|
#include "decompiler/Disasm/DecompilerLabel.h"
|
|
#include "stack_spill.h"
|
|
#include "third-party/fmt/core.h"
|
|
|
|
namespace decompiler {
|
|
|
|
std::string StackSpillSlot::print() const {
|
|
return fmt::format("[{:3d}] {}{}", offset, is_signed ? 's' : 'u', size * 8);
|
|
}
|
|
|
|
void StackSpillMap::add_access(const StackSpillSlot& access) {
|
|
auto existing = m_slot_map.find(access.offset);
|
|
if (existing != m_slot_map.end()) {
|
|
if (access != existing->second) {
|
|
// the GOAL float -> GPR loads are just totally wrong.
|
|
if (existing->second.size == 16 && access.size == 4) {
|
|
existing->second.size = 4;
|
|
return;
|
|
}
|
|
|
|
if (existing->second.size == 4 && access.size == 16) {
|
|
return;
|
|
}
|
|
|
|
throw std::runtime_error(fmt::format("Inconsistent stack access:\n{}\n{}\n",
|
|
existing->second.print(), access.print()));
|
|
}
|
|
} else {
|
|
m_slot_map.insert({access.offset, access});
|
|
}
|
|
}
|
|
|
|
const StackSpillSlot& StackSpillMap::lookup(int offset) const {
|
|
auto result = m_slot_map.find(offset);
|
|
if (result == m_slot_map.end()) {
|
|
throw std::runtime_error(fmt::format("unknown stack spill slot at offset {}", offset));
|
|
}
|
|
return result->second;
|
|
}
|
|
|
|
void StackSpillMap::finalize() {
|
|
// how many variables exist at each byte. should be 1 or 0.
|
|
int max_offset = 0;
|
|
for (auto& slot : m_slot_map) {
|
|
max_offset = std::max(max_offset, slot.second.offset + slot.second.size);
|
|
}
|
|
|
|
ASSERT(max_offset < 8192); // just a sanity check here
|
|
std::vector<int> var_count(max_offset, 0);
|
|
|
|
for (auto& slot : m_slot_map) {
|
|
for (int i = 0; i < slot.second.size; i++) {
|
|
var_count.at(slot.second.offset + i)++;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < var_count.size(); i++) {
|
|
if (var_count[i] > 1) {
|
|
throw std::runtime_error(
|
|
fmt::format("There are {} variables at stack offset {}", var_count[i], i));
|
|
}
|
|
}
|
|
}
|
|
|
|
int StackSpillMap::size() const {
|
|
return m_slot_map.size();
|
|
}
|
|
|
|
namespace {
|
|
struct StackInstrInfo {
|
|
InstructionKind kind;
|
|
bool is_load;
|
|
int size;
|
|
bool is_signed;
|
|
};
|
|
|
|
constexpr StackInstrInfo stack_instrs[] = {{InstructionKind::SQ, false, 16, false},
|
|
{InstructionKind::LQ, true, 16, false},
|
|
{InstructionKind::SW, false, 4, false},
|
|
{InstructionKind::SH, false, 2, false},
|
|
{InstructionKind::SB, false, 1, false},
|
|
{InstructionKind::LBU, true, 1, false},
|
|
//{InstructionKind::LWU, true, 4, false}
|
|
{InstructionKind::SD, false, 8, false},
|
|
{InstructionKind::SWC1, false, 4, false},
|
|
{InstructionKind::LWC1, true, 4, false},
|
|
{InstructionKind::SB, false, 1, false},
|
|
{InstructionKind::LBU, true, 1, false}};
|
|
} // namespace
|
|
|
|
StackSpillMap build_spill_map(const std::vector<Instruction>& instructions, Range<int> range) {
|
|
StackSpillMap map;
|
|
|
|
for (auto idx : range) {
|
|
auto& instr = instructions.at(idx);
|
|
|
|
for (auto& instr_template : stack_instrs) {
|
|
if (instr.kind == instr_template.kind) {
|
|
// we are the right kind.
|
|
auto src_reg = instr.get_src(instr_template.is_load ? 1 : 2).get_reg();
|
|
if (src_reg == Register(Reg::GPR, Reg::SP)) {
|
|
StackSpillSlot slot;
|
|
slot.offset = instr.get_src(instr_template.is_load ? 0 : 1).get_imm();
|
|
slot.size = instr_template.size;
|
|
slot.is_signed = instr_template.is_signed;
|
|
map.add_access(slot);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
map.finalize();
|
|
return map;
|
|
}
|
|
} // namespace decompiler
|