support non-virtual gos in decompiler (#707)

This commit is contained in:
water111
2021-07-17 18:07:21 -04:00
committed by GitHub
parent 0ed587cba9
commit b70fcb2f7a
48 changed files with 824 additions and 733 deletions
+33 -1
View File
@@ -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");
}
+78 -1
View File
@@ -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;
}
}
+18 -15
View File
@@ -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"
}
}
}
+7
View File
@@ -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;
}
+6
View File
@@ -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);
+9
View File
@@ -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()) {