mirror of
https://github.com/open-goal/jak-project
synced 2026-09-12 20:56:09 -04:00
support non-virtual gos in decompiler (#707)
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include "decompiler/util/TP_Type.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "decompiler/IR2/bitfields.h"
|
||||
#include "common/type_system/state.h"
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
@@ -81,6 +82,8 @@ TP_Type SimpleAtom::get_type(const TypeState& input,
|
||||
// which actually means that you get the first address in the symbol table.
|
||||
// it's not really a linked symbol, but the basic op builder represents it as one.
|
||||
return TP_Type::make_from_ts(TypeSpec("pointer"));
|
||||
} else if (m_string == "enter-state") {
|
||||
return TP_Type::make_enter_state();
|
||||
}
|
||||
|
||||
// look up the type of the symbol
|
||||
@@ -798,9 +801,18 @@ TypeState SetVarConditionOp::propagate_types_internal(const TypeState& input,
|
||||
TypeState StoreOp::propagate_types_internal(const TypeState& input,
|
||||
const Env& env,
|
||||
DecompilerTypeSystem& dts) {
|
||||
TypeState output = input;
|
||||
|
||||
// look for setting the next state of the current process
|
||||
IR2_RegOffset ro;
|
||||
if (get_as_reg_offset(m_addr, &ro)) {
|
||||
if (ro.reg == Register(Reg::GPR, Reg::S6) && ro.offset == 72) {
|
||||
output.next_state_type = m_value.get_type(input, env, dts);
|
||||
}
|
||||
}
|
||||
(void)env;
|
||||
(void)dts;
|
||||
return input;
|
||||
return output;
|
||||
}
|
||||
|
||||
TP_Type LoadVarOp::get_src_type(const TypeState& input,
|
||||
@@ -1113,6 +1125,26 @@ TypeState CallOp::propagate_types_internal(const TypeState& input,
|
||||
throw std::runtime_error("Called something that was not a function: " + in_type.print());
|
||||
}
|
||||
|
||||
// If we call enter-state, update our type.
|
||||
if (in_tp.kind == TP_Type::Kind::ENTER_STATE_FUNCTION) {
|
||||
// this is a GO!
|
||||
auto state_type = input.next_state_type.typespec();
|
||||
if (state_type.base_type() != "state") {
|
||||
throw std::runtime_error(
|
||||
fmt::format("At op {}, called enter-state, but the current next-state has type {}, which "
|
||||
"is not a valid state.",
|
||||
m_my_idx, input.next_state_type.print()));
|
||||
}
|
||||
|
||||
if (state_type.arg_count() == 0) {
|
||||
throw std::runtime_error(fmt::format(
|
||||
"At op {}, tried to enter-state, but the type of (-> s6 next-state) is just a plain "
|
||||
"state. The decompiler must know the specific state type.",
|
||||
m_my_idx));
|
||||
}
|
||||
in_type = state_to_go_function(state_type);
|
||||
}
|
||||
|
||||
if (in_type.arg_count() < 1) {
|
||||
throw std::runtime_error("Called a function, but we do not know its type");
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "decompiler/util/data_decompile.h"
|
||||
#include "decompiler/IR2/bitfields.h"
|
||||
#include "common/util/BitUtils.h"
|
||||
#include "common/type_system/state.h"
|
||||
|
||||
/*
|
||||
* TODO
|
||||
@@ -2334,6 +2335,34 @@ void AbsElement::update_from_stack(const Env& env,
|
||||
result->push_back(new_form);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/*!
|
||||
* Try to recognize setting the next state.
|
||||
*/
|
||||
Form* get_set_next_state(FormElement* set_elt, const Env& env) {
|
||||
auto as_set = dynamic_cast<SetFormFormElement*>(set_elt);
|
||||
if (!as_set) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto dst = as_set->dst();
|
||||
auto dst_matcher =
|
||||
Matcher::deref(Matcher::any_reg(0), false, {DerefTokenMatcher::string("next-state")});
|
||||
auto mr = match(dst_matcher, dst);
|
||||
if (!mr.matched) {
|
||||
fmt::print("failed to match dst {}\n", dst->to_string(env));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mr.maps.regs.at(0)->reg() != Register(Reg::GPR, Reg::S6)) {
|
||||
fmt::print("failed to match pp reg, got {}\n", mr.maps.regs.at(0)->reg().to_string());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return as_set->src();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
///////////////////
|
||||
// FunctionCallElement
|
||||
///////////////////
|
||||
@@ -2354,11 +2383,42 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
}
|
||||
|
||||
TypeSpec function_type;
|
||||
auto& tp_type = env.get_types_before_op(all_pop_vars.at(0).idx()).get(all_pop_vars.at(0).reg());
|
||||
auto& in_type_state = env.get_types_before_op(all_pop_vars.at(0).idx());
|
||||
auto& tp_type = in_type_state.get(all_pop_vars.at(0).reg());
|
||||
if (env.has_type_analysis()) {
|
||||
function_type = tp_type.typespec();
|
||||
}
|
||||
|
||||
// if we're actually a go:
|
||||
Form* go_next_state = nullptr;
|
||||
if (tp_type.kind == TP_Type::Kind::ENTER_STATE_FUNCTION) {
|
||||
auto& next_state_type = in_type_state.next_state_type;
|
||||
if (next_state_type.typespec().base_type() != "state") {
|
||||
throw std::runtime_error("Bad state type in expressions (not state): " +
|
||||
next_state_type.print());
|
||||
}
|
||||
if (next_state_type.typespec().arg_count() == 0) {
|
||||
throw std::runtime_error("Bad state type in expressions (no args): " +
|
||||
next_state_type.print());
|
||||
}
|
||||
|
||||
// modify our type for the go.
|
||||
function_type = state_to_go_function(next_state_type.typespec());
|
||||
|
||||
// up next, we need to deal with the
|
||||
// (set! (-> pp next-state) process-drawable-art-error)
|
||||
auto stack_back = stack.pop_back(pool);
|
||||
|
||||
auto next_state = get_set_next_state(stack_back, env);
|
||||
if (!next_state) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Expressions couldn't figure out this go. The back of the stack was {} and "
|
||||
"we expected to see something set (-> pp next-state) instead.",
|
||||
stack_back->to_string(env)));
|
||||
}
|
||||
go_next_state = next_state;
|
||||
}
|
||||
|
||||
bool swap_function =
|
||||
tp_type.kind == TP_Type::Kind::NON_VIRTUAL_METHOD && all_pop_vars.size() >= 2;
|
||||
if (tp_type.kind == TP_Type::Kind::NON_VIRTUAL_METHOD) {
|
||||
@@ -2410,6 +2470,16 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
|
||||
FormElement* new_form = nullptr;
|
||||
|
||||
if (go_next_state) {
|
||||
arg_forms.insert(arg_forms.begin(), go_next_state);
|
||||
auto go_form = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_function(
|
||||
pool.alloc_single_element_form<ConstantTokenElement>(nullptr, "go")),
|
||||
arg_forms);
|
||||
result->push_back(go_form);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// deal with virtual method calls.
|
||||
auto matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::METHOD_OF_OBJECT),
|
||||
@@ -2448,6 +2518,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
new_form = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_function(mr.maps.forms.at(1)), arg_forms);
|
||||
result->push_back(new_form);
|
||||
assert(!go_next_state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2496,6 +2567,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
auto new_op = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::OBJECT_NEW), new_args);
|
||||
result->push_back(new_op);
|
||||
assert(!go_next_state);
|
||||
return;
|
||||
}
|
||||
if (name == "new" && type_1 == "type") {
|
||||
@@ -2503,6 +2575,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
auto new_op = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::TYPE_NEW), new_args);
|
||||
result->push_back(new_op);
|
||||
assert(!go_next_state);
|
||||
return;
|
||||
} else if (name == "new") {
|
||||
constexpr int allocation = 2;
|
||||
@@ -2538,6 +2611,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
auto cons_op = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::CONS), cons_args);
|
||||
result->push_back(cons_op);
|
||||
assert(!go_next_state);
|
||||
return;
|
||||
} else {
|
||||
// just normal construction on the heap
|
||||
@@ -2547,6 +2621,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
auto new_op = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::NEW), new_args);
|
||||
result->push_back(new_op);
|
||||
assert(!go_next_state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2596,6 +2671,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
auto gop = GenericOperator::make_function(method_op);
|
||||
|
||||
result->push_back(pool.alloc_element<GenericElement>(gop, arg_forms));
|
||||
assert(!go_next_state);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2633,6 +2709,7 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
}
|
||||
result->push_back(pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::NEW), stack_new_args));
|
||||
assert(!go_next_state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -962,13 +962,15 @@
|
||||
;; - Types
|
||||
|
||||
(define-extern process-tree type) ; deftype provided by C Kernel
|
||||
(declare-type process basic)
|
||||
|
||||
(deftype process-tree (basic)
|
||||
((name basic :offset-assert 4)
|
||||
(mask process-mask :offset-assert 8)
|
||||
(parent (pointer process-tree) :offset-assert 12)
|
||||
(brother (pointer process-tree) :offset-assert 16)
|
||||
(child (pointer process-tree) :offset-assert 20)
|
||||
(ppointer (pointer process-tree) :offset-assert 24)
|
||||
(ppointer (pointer process) :offset-assert 24)
|
||||
(self process-tree :offset-assert 28)
|
||||
)
|
||||
(:methods
|
||||
@@ -5831,8 +5833,8 @@
|
||||
:size-assert #x20
|
||||
:flag-assert #x1200000020
|
||||
(:methods
|
||||
(dummy-9 (_type_) _type_ 9) ;; probably login or init.
|
||||
(dummy-10 (_type_ drawable display-frame) int 10) ;; display-frame is from the method inspect tool
|
||||
(login (_type_) _type_ 9) ;; probably login or init.
|
||||
(draw (_type_ drawable display-frame) int 10) ;; display-frame is from the method inspect tool
|
||||
(dummy-11 (_type_ int) none 11) ; int - length
|
||||
(dummy-12 (_type_ int) none 12) ; int - length
|
||||
(dummy-13 (_type_ int) none 13) ; int - length
|
||||
@@ -9654,7 +9656,8 @@
|
||||
|
||||
;; - Symbols
|
||||
|
||||
(define-extern process-drawable-art-error state)
|
||||
(declare-type process-drawable process)
|
||||
(define-extern process-drawable-art-error (state string process-drawable))
|
||||
(define-extern *FACT-bank* fact-bank) ;; unknown type
|
||||
|
||||
|
||||
@@ -10489,7 +10492,7 @@
|
||||
((vertex vector 3 :inline :offset-assert 0)
|
||||
(intersect vector :inline :offset-assert 48)
|
||||
(normal vector :inline :offset-assert 64)
|
||||
(pat uint32 :offset-assert 80)
|
||||
(pat pat-surface :offset-assert 80)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x54
|
||||
@@ -10497,9 +10500,9 @@
|
||||
)
|
||||
|
||||
(deftype collide-mesh-tri (structure)
|
||||
((vertex-index uint8 3 :offset-assert 0)
|
||||
(unused uint8 :offset-assert 3)
|
||||
(pat uint32 :offset-assert 4)
|
||||
((vertex-index uint8 3 :offset-assert 0)
|
||||
(unused uint8 :offset-assert 3)
|
||||
(pat pat-surface :offset-assert 4)
|
||||
)
|
||||
:pack-me
|
||||
:method-count-assert 9
|
||||
@@ -10549,7 +10552,7 @@
|
||||
((vertex vector 3 :inline :offset-assert 0)
|
||||
(normal vector :inline :offset-assert 48)
|
||||
(bbox4w bounding-box4w :inline :offset-assert 64)
|
||||
(pat uint32 :offset 60)
|
||||
(pat pat-surface :offset 60)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x60
|
||||
@@ -10703,7 +10706,7 @@
|
||||
)
|
||||
|
||||
(deftype collide-shape-prim-sphere (collide-shape-prim)
|
||||
((pat uint32 :offset-assert 72)
|
||||
((pat pat-surface :offset-assert 72)
|
||||
)
|
||||
:method-count-assert 28
|
||||
:size-assert #x4c
|
||||
@@ -10805,7 +10808,7 @@
|
||||
(max-iteration-count uint8 :offset-assert 144)
|
||||
(nav-flags uint8 :offset-assert 145)
|
||||
(pad-byte uint8 2 :offset-assert 146)
|
||||
(pat-ignore-mask uint32 :offset-assert 148)
|
||||
(pat-ignore-mask pat-surface :offset-assert 148)
|
||||
(event-self basic :offset-assert 152)
|
||||
(event-other basic :offset-assert 156)
|
||||
(root-prim basic :offset-assert 160)
|
||||
@@ -10853,9 +10856,9 @@
|
||||
((rider-time uint64 :offset-assert 184)
|
||||
(rider-last-move vector :inline :offset-assert 192)
|
||||
(trans-old vector 3 :inline :offset-assert 208)
|
||||
(poly-pat uint32 :offset-assert 256)
|
||||
(cur-pat uint32 :offset-assert 260)
|
||||
(ground-pat uint32 :offset-assert 264)
|
||||
(poly-pat pat-surface :offset-assert 256)
|
||||
(cur-pat pat-surface :offset-assert 260)
|
||||
(ground-pat pat-surface :offset-assert 264)
|
||||
(status uint64 :offset-assert 272)
|
||||
(old-status uint64 :offset-assert 280)
|
||||
(prev-status uint64 :offset-assert 288)
|
||||
@@ -10919,7 +10922,7 @@
|
||||
(surface-normal vector :inline :offset-assert 80)
|
||||
(time uint64 :offset-assert 96)
|
||||
(status uint64 :offset-assert 104)
|
||||
(pat uint32 :offset-assert 112)
|
||||
(pat pat-surface :offset-assert 112)
|
||||
(reaction-flag uint32 :offset-assert 116)
|
||||
)
|
||||
:method-count-assert 10
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
"(method 0 fact-info)": [
|
||||
[81, "v0", "float"],
|
||||
[16, "t9", "(function string none)"],
|
||||
//[16, "t9", "(function string none)"],
|
||||
["_stack_", 16, "res-tag"],
|
||||
[[32, 43], "v1", "(pointer int32)"],
|
||||
[86, "gp", "fact-info"]
|
||||
@@ -553,7 +553,6 @@
|
||||
"(method 0 fact-info-target)": [[[3, 20], "gp", "fact-info-target"]],
|
||||
|
||||
"(method 0 align-control)": [
|
||||
[[8, 13], "t9", "(function object object)"],
|
||||
[[14, 18], "v0", "align-control"]
|
||||
],
|
||||
|
||||
@@ -609,8 +608,7 @@
|
||||
|
||||
"(method 11 joint-mod)": [
|
||||
[15, "s3", "process-drawable"],
|
||||
[[26, 66], "s3", "fact-info-enemy"],
|
||||
[[45, 50], "v1", "(pointer process)"]
|
||||
[[26, 66], "s3", "fact-info-enemy"]
|
||||
],
|
||||
|
||||
"joint-mod-look-at-handler": [[[2, 254], "gp", "joint-mod"]],
|
||||
@@ -721,7 +719,6 @@
|
||||
"actor-link-subtask-complete-hook": [[1, "v1", "entity-links"]],
|
||||
|
||||
"(method 0 vol-control)": [
|
||||
[[9, 14], "t9", "(function object object)"],
|
||||
[30, "s5", "res-lump"],
|
||||
[36, "s5", "res-lump"],
|
||||
[58, "s5", "res-lump"],
|
||||
@@ -747,7 +744,6 @@
|
||||
"(method 12 art-group)": [[13, "a0", "art-joint-anim"]],
|
||||
|
||||
"(method 0 path-control)": [
|
||||
[15, "t9", "(function string none)"],
|
||||
["_stack_", 16, "res-tag"]
|
||||
],
|
||||
|
||||
@@ -763,8 +759,6 @@
|
||||
[77, "a0", "entity-links"]
|
||||
],
|
||||
|
||||
"(method 0 nav-control)": [[17, "t9", "(function string none)"]],
|
||||
|
||||
"add-debug-point": [
|
||||
[125, "a3", "pointer"],
|
||||
[[27, 144], "a0", "(pointer uint64)"],
|
||||
|
||||
@@ -1840,8 +1840,7 @@
|
||||
"vars": {
|
||||
"gp-0": ["obj", "fact-info"],
|
||||
"s5-0": "ent",
|
||||
"sv-16": "tag",
|
||||
"t9-1": ["go-func", "(function string none)"]
|
||||
"sv-16": "tag"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1983,8 +1982,7 @@
|
||||
"vars": {
|
||||
"s1-0": "proc-drawable",
|
||||
"s3-1": ["enemy-facts", "fact-info-enemy"],
|
||||
"f30-0": "dist",
|
||||
"v1-12": ["ppointer", "(pointer process)"]
|
||||
"f30-0": "dist"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2382,5 +2380,14 @@
|
||||
"sv-32":"load-status",
|
||||
"sv-40":"heap-free"
|
||||
}
|
||||
},
|
||||
|
||||
"(method 16 process-drawable)" : {
|
||||
"vars": {
|
||||
"s3-0":"body-T-world",
|
||||
"s0-0":"world-T-body",
|
||||
"s2-0":"grav-rt-body",
|
||||
"a1-5":"vel-rt-body"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,6 +379,13 @@ bool DecompilerTypeSystem::tp_lca(TypeState* combined, const TypeState& add) {
|
||||
}
|
||||
}
|
||||
|
||||
bool diff = false;
|
||||
auto new_type = tp_lca(combined->next_state_type, add.next_state_type, &diff);
|
||||
if (diff) {
|
||||
result = true;
|
||||
combined->next_state_type = new_type;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ std::string TP_Type::print() const {
|
||||
return fmt::format("<pcpyud-and {}>", m_ts.print());
|
||||
case Kind::LABEL_ADDR:
|
||||
return "<label-addr>";
|
||||
case Kind::ENTER_STATE_FUNCTION:
|
||||
return "<enter-state-func>";
|
||||
case Kind::INVALID:
|
||||
default:
|
||||
assert(false);
|
||||
@@ -124,6 +126,7 @@ bool TP_Type::operator==(const TP_Type& other) const {
|
||||
case Kind::PCPYUD_BITFIELD_AND:
|
||||
return m_pcpyud == other.m_pcpyud && m_ts == other.m_ts;
|
||||
case Kind::LABEL_ADDR:
|
||||
case Kind::ENTER_STATE_FUNCTION:
|
||||
return true;
|
||||
case Kind::INVALID:
|
||||
default:
|
||||
@@ -185,6 +188,9 @@ TypeSpec TP_Type::typespec() const {
|
||||
return TypeSpec("int");
|
||||
case Kind::LABEL_ADDR:
|
||||
return TypeSpec("pointer"); // ?
|
||||
case Kind::ENTER_STATE_FUNCTION:
|
||||
// give a general function so we can't call it normally.
|
||||
return TypeSpec("function");
|
||||
case Kind::INVALID:
|
||||
default:
|
||||
assert(false);
|
||||
|
||||
@@ -36,6 +36,7 @@ class TP_Type {
|
||||
PCPYUD_BITFIELD_AND,
|
||||
LEFT_SHIFTED_BITFIELD, // (bitfield << some-constant)
|
||||
LABEL_ADDR,
|
||||
ENTER_STATE_FUNCTION,
|
||||
INVALID
|
||||
} kind = Kind::UNINITIALIZED;
|
||||
TP_Type() = default;
|
||||
@@ -65,6 +66,7 @@ class TP_Type {
|
||||
case Kind::PCPYUD_BITFIELD:
|
||||
case Kind::PCPYUD_BITFIELD_AND:
|
||||
case Kind::LABEL_ADDR:
|
||||
case Kind::ENTER_STATE_FUNCTION:
|
||||
return false;
|
||||
case Kind::UNINITIALIZED:
|
||||
case Kind::OBJECT_NEW_METHOD:
|
||||
@@ -254,6 +256,12 @@ class TP_Type {
|
||||
return result;
|
||||
}
|
||||
|
||||
static TP_Type make_enter_state() {
|
||||
TP_Type result;
|
||||
result.kind = Kind::ENTER_STATE_FUNCTION;
|
||||
return result;
|
||||
}
|
||||
|
||||
static TP_Type make_label_addr() {
|
||||
TP_Type result;
|
||||
result.kind = Kind::LABEL_ADDR;
|
||||
@@ -352,6 +360,7 @@ class TypeState {
|
||||
std::unordered_map<int, TP_Type> spill_slots;
|
||||
TP_Type gpr_types[32];
|
||||
TP_Type fpr_types[32];
|
||||
TP_Type next_state_type;
|
||||
std::string print_gpr_masked(u32 mask) const;
|
||||
TP_Type& get(const Register& r) {
|
||||
switch (r.get_kind()) {
|
||||
|
||||
Reference in New Issue
Block a user