mirror of
https://github.com/open-goal/jak-project
synced 2026-08-21 14:54:53 -04:00
[Decompiler] Support virtual method calls (#260)
* support virtual methods kinda * more
This commit is contained in:
@@ -428,7 +428,7 @@ AsmOp::AsmOp(Instruction instr, int my_idx) : AtomicOp(my_idx), m_instr(std::mov
|
||||
}
|
||||
}
|
||||
|
||||
assert(m_instr.n_src <= 3);
|
||||
assert(m_instr.n_src <= 4);
|
||||
for (int i = 0; i < m_instr.n_src; i++) {
|
||||
auto& src = m_instr.get_src(i);
|
||||
if (src.is_reg()) {
|
||||
@@ -455,7 +455,7 @@ goos::Object AsmOp::to_form(const std::vector<DecompilerLabel>& labels, const En
|
||||
}
|
||||
}
|
||||
|
||||
assert(m_instr.n_src <= 3);
|
||||
assert(m_instr.n_src <= 4);
|
||||
for (int i = 0; i < m_instr.n_src; i++) {
|
||||
if (m_src[i].has_value()) {
|
||||
forms.push_back(m_src[i].value().to_form(env));
|
||||
|
||||
@@ -303,7 +303,7 @@ class AsmOp : public AtomicOp {
|
||||
private:
|
||||
Instruction m_instr;
|
||||
std::optional<Variable> m_dst;
|
||||
std::optional<Variable> m_src[3];
|
||||
std::optional<Variable> m_src[4];
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -585,10 +585,12 @@ class CallOp : public AtomicOp {
|
||||
void collect_vars(VariableSet& vars) const override;
|
||||
const std::vector<Variable>& arg_vars() const { return m_arg_vars; }
|
||||
Variable function_var() const { return m_function_var; }
|
||||
bool is_method() const { return m_is_virtual_method; }
|
||||
|
||||
protected:
|
||||
TypeSpec m_call_type;
|
||||
bool m_call_type_set = false;
|
||||
bool m_is_virtual_method = false;
|
||||
|
||||
std::vector<Variable> m_arg_vars;
|
||||
Variable m_function_var;
|
||||
|
||||
@@ -237,8 +237,9 @@ FormElement* LoadVarOp::get_as_form(FormPool& pool, const Env& env) const {
|
||||
if (get_as_reg_offset(m_src, &ro)) {
|
||||
auto& input_type = env.get_types_before_op(m_my_idx).get(ro.reg);
|
||||
|
||||
if (input_type.kind == TP_Type::Kind::TYPE_OF_TYPE_OR_CHILD && ro.offset >= 16 &&
|
||||
(ro.offset & 3) == 0 && m_size == 4 && m_kind == Kind::UNSIGNED) {
|
||||
if ((input_type.kind == TP_Type::Kind::TYPE_OF_TYPE_NO_VIRTUAL ||
|
||||
input_type.kind == TP_Type::Kind::TYPE_OF_TYPE_OR_CHILD) &&
|
||||
ro.offset >= 16 && (ro.offset & 3) == 0 && m_size == 4 && m_kind == Kind::UNSIGNED) {
|
||||
// method get of fixed type
|
||||
auto type_name = input_type.get_type_objects_typespec().base_type();
|
||||
auto method_id = (ro.offset - 16) / 4;
|
||||
|
||||
@@ -88,7 +88,7 @@ TP_Type SimpleAtom::get_type(const TypeState& input,
|
||||
|
||||
if (type->second == TypeSpec("type")) {
|
||||
// if we get a type by symbol, we should remember which type we got it from.
|
||||
return TP_Type::make_type_object(TypeSpec(m_string));
|
||||
return TP_Type::make_type_no_virtual_object(TypeSpec(m_string));
|
||||
}
|
||||
|
||||
if (type->second == TypeSpec("function")) {
|
||||
@@ -396,10 +396,10 @@ TypeState IR2_BranchDelay::propagate_types(const TypeState& input,
|
||||
output.get(m_var[0]->reg()) = TP_Type::make_from_ts(TypeSpec("symbol"));
|
||||
break;
|
||||
case Kind::SET_BINTEGER:
|
||||
output.get(m_var[0]->reg()) = TP_Type::make_type_object(TypeSpec("binteger"));
|
||||
output.get(m_var[0]->reg()) = TP_Type::make_type_no_virtual_object(TypeSpec("binteger"));
|
||||
break;
|
||||
case Kind::SET_PAIR:
|
||||
output.get(m_var[0]->reg()) = TP_Type::make_type_object(TypeSpec("pair"));
|
||||
output.get(m_var[0]->reg()) = TP_Type::make_type_no_virtual_object(TypeSpec("pair"));
|
||||
break;
|
||||
case Kind::NOP:
|
||||
case Kind::NO_DELAY:
|
||||
@@ -500,8 +500,9 @@ TP_Type LoadVarOp::get_src_type(const TypeState& input,
|
||||
if (get_as_reg_offset(m_src, &ro)) {
|
||||
auto& input_type = input.get(ro.reg);
|
||||
|
||||
if (input_type.kind == TP_Type::Kind::TYPE_OF_TYPE_OR_CHILD && ro.offset >= 16 &&
|
||||
(ro.offset & 3) == 0 && m_size == 4 && m_kind == Kind::UNSIGNED) {
|
||||
if ((input_type.kind == TP_Type::Kind::TYPE_OF_TYPE_OR_CHILD ||
|
||||
input_type.kind == TP_Type::Kind::TYPE_OF_TYPE_NO_VIRTUAL) &&
|
||||
ro.offset >= 16 && (ro.offset & 3) == 0 && m_size == 4 && m_kind == Kind::UNSIGNED) {
|
||||
// method get of fixed type
|
||||
auto type_name = input_type.get_type_objects_typespec().base_type();
|
||||
auto method_id = (ro.offset - 16) / 4;
|
||||
@@ -511,7 +512,12 @@ TP_Type LoadVarOp::get_src_type(const TypeState& input,
|
||||
// remember that we're an object new.
|
||||
return TP_Type::make_object_new(method_type);
|
||||
}
|
||||
return TP_Type::make_from_ts(method_type);
|
||||
if (method_id == GOAL_NEW_METHOD ||
|
||||
input_type.kind == TP_Type::Kind::TYPE_OF_TYPE_NO_VIRTUAL) {
|
||||
return TP_Type::make_from_ts(method_type);
|
||||
} else {
|
||||
return TP_Type::make_method(method_type);
|
||||
}
|
||||
}
|
||||
|
||||
if (input_type.kind == TP_Type::Kind::TYPESPEC && input_type.typespec() == TypeSpec("type") &&
|
||||
@@ -594,7 +600,7 @@ TP_Type LoadVarOp::get_src_type(const TypeState& input,
|
||||
// load_path.push_back("type");
|
||||
// load_path_set = true;
|
||||
|
||||
return TP_Type::make_type_object(input_type.typespec().base_type());
|
||||
return TP_Type::make_type_allow_virtual_object(input_type.typespec().base_type());
|
||||
}
|
||||
|
||||
if (input_type.kind == TP_Type::Kind::DYNAMIC_METHOD_ACCESS && ro.offset == 16) {
|
||||
@@ -699,6 +705,7 @@ TypeState CallOp::propagate_types_internal(const TypeState& input,
|
||||
const Reg::Gpr arg_regs[8] = {Reg::A0, Reg::A1, Reg::A2, Reg::A3,
|
||||
Reg::T0, Reg::T1, Reg::T2, Reg::T3};
|
||||
|
||||
m_is_virtual_method = false;
|
||||
TypeState end_types = input;
|
||||
|
||||
auto in_tp = input.get(Register(Reg::GPR, Reg::T9));
|
||||
@@ -789,6 +796,11 @@ TypeState CallOp::propagate_types_internal(const TypeState& input,
|
||||
for (uint32_t i = 0; i < in_type.arg_count() - 1; i++) {
|
||||
m_read_regs.emplace_back(Reg::GPR, arg_regs[i]);
|
||||
m_arg_vars.push_back(Variable(VariableMode::READ, m_read_regs.back(), m_my_idx));
|
||||
if (i == 0 && in_tp.kind == TP_Type::Kind::METHOD) {
|
||||
m_read_regs.pop_back();
|
||||
m_arg_vars.pop_back();
|
||||
m_is_virtual_method = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_write_regs.clear();
|
||||
|
||||
@@ -622,6 +622,11 @@ class CondNoElseElement : public FormElement {
|
||||
void collect_vars(VariableSet& vars) const override;
|
||||
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
|
||||
void get_modified_regs(RegSet& regs) const override;
|
||||
void update_from_stack(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) override;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -940,6 +945,11 @@ class GetMethodElement : public FormElement {
|
||||
void apply_form(const std::function<void(Form*)>& f) override;
|
||||
void collect_vars(VariableSet& vars) const override;
|
||||
void get_modified_regs(RegSet& regs) const override;
|
||||
void update_from_stack(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) override;
|
||||
|
||||
private:
|
||||
Form* m_in = nullptr;
|
||||
@@ -973,6 +983,11 @@ class ConstantTokenElement : public FormElement {
|
||||
void apply_form(const std::function<void(Form*)>& f) override;
|
||||
void collect_vars(VariableSet& vars) const override;
|
||||
void get_modified_regs(RegSet& regs) const override;
|
||||
void update_from_stack(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) override;
|
||||
|
||||
private:
|
||||
std::string m_value;
|
||||
|
||||
@@ -904,13 +904,28 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
for (size_t i = 0; i < nargs; i++) {
|
||||
all_pop_vars.push_back(m_op->arg_vars().at(i));
|
||||
}
|
||||
|
||||
TypeSpec function_type;
|
||||
bool is_method = false;
|
||||
auto& tp_type = env.get_types_before_op(all_pop_vars.at(0).idx()).get(all_pop_vars.at(0).reg());
|
||||
if (env.has_type_analysis()) {
|
||||
if (tp_type.kind == TP_Type::Kind::METHOD && all_pop_vars.size() >= 1) {
|
||||
is_method = true;
|
||||
}
|
||||
function_type = tp_type.typespec();
|
||||
}
|
||||
|
||||
assert(is_method == m_op->is_method());
|
||||
|
||||
// if method, don't pop the obj arg.
|
||||
// Variable method_obj_var;
|
||||
// if (is_method) {
|
||||
// method_obj_var = all_pop_vars.at(1);
|
||||
// all_pop_vars.erase(all_pop_vars.begin() + 1);
|
||||
// }
|
||||
|
||||
auto unstacked = pop_to_forms(all_pop_vars, env, pool, stack, allow_side_effects);
|
||||
std::vector<Form*> arg_forms;
|
||||
TypeSpec function_type;
|
||||
if (env.has_type_analysis()) {
|
||||
function_type =
|
||||
env.get_types_before_op(all_pop_vars.at(0).idx()).get(all_pop_vars.at(0).reg()).typespec();
|
||||
}
|
||||
|
||||
for (size_t arg_id = 0; arg_id < nargs; arg_id++) {
|
||||
auto val = unstacked.at(arg_id + 1); // first is the function itself.
|
||||
@@ -929,8 +944,39 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
}
|
||||
}
|
||||
|
||||
auto new_form = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_function(unstacked.at(0)), arg_forms);
|
||||
FormElement* new_form = nullptr;
|
||||
if (is_method) {
|
||||
auto matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::METHOD_OF_OBJECT),
|
||||
{Matcher::any(0), Matcher::any(1)});
|
||||
auto mr = match(matcher, unstacked.at(0));
|
||||
if (!mr.matched) {
|
||||
throw std::runtime_error("Failed to match method call. Got " +
|
||||
unstacked.at(0)->to_string(env));
|
||||
}
|
||||
|
||||
auto unsafe = stack.unsafe_peek(Register(Reg::GPR, Reg::A0));
|
||||
if (unsafe) {
|
||||
if (!unsafe->try_as_single_element()) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Peek got something weird: {}\n", unsafe->to_string(env)));
|
||||
}
|
||||
if (unsafe->try_as_single_element() != mr.maps.forms.at(0)->try_as_single_element()) {
|
||||
throw std::runtime_error(fmt::format("Invalid method call. {} vs {}.",
|
||||
unsafe->to_string(env),
|
||||
mr.maps.forms.at(0)->to_string(env)));
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Failed to peek for arg0");
|
||||
}
|
||||
|
||||
arg_forms.insert(arg_forms.begin(), unsafe);
|
||||
new_form = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_function(mr.maps.forms.at(1)), arg_forms);
|
||||
|
||||
} else {
|
||||
new_form = pool.alloc_element<GenericElement>(GenericOperator::make_function(unstacked.at(0)),
|
||||
arg_forms);
|
||||
}
|
||||
|
||||
{
|
||||
// detect method calls:
|
||||
@@ -1021,7 +1067,10 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Failed to match new method");
|
||||
lg::warn("Got a suspicious new method. This may be fine, but should be uncommon: {}",
|
||||
temp_form->to_string(env));
|
||||
// throw std::runtime_error("Failed to match new method: " +
|
||||
// temp_form->to_string(env));
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Method call detected, not yet implemented");
|
||||
@@ -1713,7 +1762,8 @@ void ArrayFieldAccess::update_from_stack(const Env& env,
|
||||
// reg1 is idx
|
||||
|
||||
auto reg0_matcher =
|
||||
Matcher::match_or({Matcher::cast("uint", Matcher::any(0)), Matcher::any(0)});
|
||||
Matcher::match_or({Matcher::cast("int", Matcher::any(0)),
|
||||
Matcher::cast("uint", Matcher::any(0)), Matcher::any(0)});
|
||||
auto reg1_matcher =
|
||||
Matcher::match_or({Matcher::cast("uint", Matcher::any(1)), Matcher::any(1)});
|
||||
auto sll_matcher =
|
||||
@@ -1722,10 +1772,14 @@ void ArrayFieldAccess::update_from_stack(const Env& env,
|
||||
auto matcher = Matcher::fixed_op(FixedOperatorKind::ADDITION, {reg0_matcher, sll_matcher});
|
||||
auto match_result = match(matcher, new_val);
|
||||
if (!match_result.matched) {
|
||||
fmt::print("power {}\n", power_of_two);
|
||||
throw std::runtime_error(
|
||||
"Couldn't match ArrayFieldAccess (stride power of 2, 0 offset) values: " +
|
||||
new_val->to_string(env));
|
||||
matcher = Matcher::fixed_op(FixedOperatorKind::ADDITION, {sll_matcher, reg0_matcher});
|
||||
match_result = match(matcher, new_val);
|
||||
if (!match_result.matched) {
|
||||
fmt::print("power {}\n", power_of_two);
|
||||
throw std::runtime_error(
|
||||
"Couldn't match ArrayFieldAccess (stride power of 2, 0 offset) values: " +
|
||||
new_val->to_string(env));
|
||||
}
|
||||
}
|
||||
|
||||
auto idx = match_result.maps.forms.at(1);
|
||||
@@ -1878,4 +1932,28 @@ void StringConstantElement::update_from_stack(const Env&,
|
||||
result->push_back(this);
|
||||
}
|
||||
|
||||
void GetMethodElement::update_from_stack(const Env&,
|
||||
FormPool&,
|
||||
FormStack&,
|
||||
std::vector<FormElement*>* result,
|
||||
bool) {
|
||||
result->push_back(this);
|
||||
}
|
||||
|
||||
void CondNoElseElement::update_from_stack(const Env&,
|
||||
FormPool&,
|
||||
FormStack&,
|
||||
std::vector<FormElement*>* result,
|
||||
bool) {
|
||||
result->push_back(this);
|
||||
}
|
||||
|
||||
void ConstantTokenElement::update_from_stack(const Env&,
|
||||
FormPool&,
|
||||
FormStack&,
|
||||
std::vector<FormElement*>* result,
|
||||
bool) {
|
||||
result->push_back(this);
|
||||
}
|
||||
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -146,6 +146,26 @@ Form* FormStack::pop_reg(Register reg,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Form* FormStack::unsafe_peek(Register reg) {
|
||||
RegSet modified;
|
||||
for (size_t i = m_stack.size(); i-- > 0;) {
|
||||
auto& entry = m_stack.at(i);
|
||||
if (entry.active) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
entry.source->get_modified_regs(modified);
|
||||
if (modified.find(reg) != modified.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (entry.destination.has_value() && entry.destination->reg() == reg) {
|
||||
return entry.source;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<FormElement*> FormStack::rewrite(FormPool& pool) {
|
||||
std::vector<FormElement*> result;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ class FormStack {
|
||||
const Env& env,
|
||||
bool allow_side_effects);
|
||||
Form* pop_reg(Register reg, const RegSet& barrier, const Env& env, bool allow_side_effects);
|
||||
Form* unsafe_peek(Register reg);
|
||||
bool is_single_expression();
|
||||
std::vector<FormElement*> rewrite(FormPool& pool);
|
||||
std::vector<FormElement*> rewrite_to_get_var(FormPool& pool, const Variable& var, const Env& env);
|
||||
|
||||
+171
-108
@@ -1375,6 +1375,131 @@
|
||||
(define-extern box-vector-inside? (function bounding-box vector symbol))
|
||||
(define-extern box-vector-enside? (function bounding-box vector symbol))
|
||||
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; MATRIX ;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
(define-extern matrix-identity! (function matrix matrix))
|
||||
(define-extern matrix+! (function matrix matrix matrix matrix))
|
||||
(define-extern matrix-! (function matrix matrix matrix matrix))
|
||||
(define-extern matrix*! (function matrix matrix matrix matrix))
|
||||
(define-extern matrixp*! (function matrix matrix matrix matrix))
|
||||
(define-extern vector-matrix*! (function vector vector matrix vector))
|
||||
(define-extern vector-rotate*! (function vector vector matrix vector))
|
||||
(define-extern vector3s-matrix*! function)
|
||||
(define-extern vector3s-rotate*! function)
|
||||
|
||||
(define-extern matrix-inverse-of-rot-trans! function)
|
||||
(define-extern matrix-3x3-determinant function)
|
||||
(define-extern matrix-3x3-inverse-transpose! function)
|
||||
|
||||
(define-extern matrix-scale! function)
|
||||
(define-extern column-scale-matrix! function)
|
||||
(define-extern matrix-rotate-yx! function)
|
||||
(define-extern matrix-y-angle function)
|
||||
(define-extern matrix-3x3-inverse! function)
|
||||
(define-extern matrix-rotate-z! function)
|
||||
;;(define-extern *identity-matrix* object) ;; unknown type
|
||||
(define-extern matrix-axis-angle! function)
|
||||
(define-extern matrix-rotate-zyx! function)
|
||||
(define-extern matrix-rotate-x! function)
|
||||
|
||||
(define-extern matrix-rotate-yxy! function)
|
||||
|
||||
(define-extern matrix-4x4-determinant function)
|
||||
(define-extern matrix-inv-scale! function)
|
||||
(define-extern matrix-rotate-yzx! function)
|
||||
(define-extern matrix-axis-sin-cos! function)
|
||||
(define-extern matrix-lerp! function)
|
||||
(define-extern matrix-rotate-y! function)
|
||||
|
||||
(define-extern matrix-rotate-xyz! function)
|
||||
(define-extern scale-matrix! function)
|
||||
(define-extern matrix-rotate-yxz! function)
|
||||
(define-extern matrix-rotate-zxy! function)
|
||||
|
||||
(define-extern matrix3-determinant function)
|
||||
(define-extern matrix3-inverse-transpose! function)
|
||||
(define-extern matrix-translate+! function)
|
||||
(define-extern matrix-transpose! function)
|
||||
(define-extern matrix-4x4-inverse-transpose! function)
|
||||
|
||||
(define-extern matrix-axis-sin-cos-vu! function)
|
||||
(define-extern matrix-translate! function)
|
||||
(define-extern matrix-4x4-inverse! function)
|
||||
|
||||
(define-extern vector-sincos! function)
|
||||
(define-extern trs-matrix-calc! function)
|
||||
(define-extern transform-matrix-parent-calc! function)
|
||||
(define-extern transform-matrix-calc! function)
|
||||
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; EULER ;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
(define-extern set-eul! (function euler-angles float float float int euler-angles))
|
||||
(define-extern eul->matrix function)
|
||||
(define-extern quat->eul function)
|
||||
|
||||
|
||||
(define-extern eul->quat function)
|
||||
(define-extern matrix->eul function)
|
||||
|
||||
|
||||
;(define-extern sound-rpc-set-falloff-curve object) ;; unknown type
|
||||
;;(define-extern *sound-bank-2* object) ;; unknown type
|
||||
;;(define-extern sound-rpc-reload-info object) ;; unknown type
|
||||
;;(define-extern sound-rpc-stop-sound object) ;; unknown type
|
||||
;;(define-extern sound-rpc-list-sounds object) ;; unknown type
|
||||
;;(define-extern *current-sound-id* object) ;; unknown type
|
||||
;;(define-extern sound-bank-id object) ;; unknown type
|
||||
;;(define-extern sound-rpc-get-irx-version object) ;; unknown type
|
||||
;;(define-extern sound-rpc-load-music object) ;; unknown type
|
||||
;;(define-extern sound-rpc-set-ear-trans object) ;; unknown type
|
||||
;;(define-extern ambient-sound object) ;; unknown type
|
||||
;;(define-extern sound-rpc-pause-group object) ;; unknown type
|
||||
;;(define-extern sound-rpc-shutdown object) ;; unknown type
|
||||
;;(define-extern sound-rpc-unload-bank object) ;; unknown type
|
||||
;;(define-extern sound-rpc-pause-sound object) ;; unknown type
|
||||
;;(define-extern sound-id object) ;; unknown type
|
||||
(deftype sound-id (uint32)
|
||||
()
|
||||
)
|
||||
|
||||
(deftype sound-bank-id (uint32)
|
||||
()
|
||||
)
|
||||
|
||||
(deftype sound-name (uint128)
|
||||
()
|
||||
)
|
||||
;;(define-extern sound-play-parms object) ;; unknown type
|
||||
;;(define-extern sound-rpc-load-bank object) ;; unknown type
|
||||
;;(define-extern sound-rpc-set-master-volume object) ;; unknown type
|
||||
;;(define-extern sound-rpc-set-param object) ;; unknown type
|
||||
;;(define-extern *sound-bank-1* object) ;; unknown type
|
||||
;;(define-extern sound-rpc-stop-group object) ;; unknown type
|
||||
;;(define-extern sound-rpc-play object) ;; unknown type
|
||||
;;(define-extern sound-rpc-cmd object) ;; unknown type
|
||||
(define-extern sound-spec type)
|
||||
;;(define-extern sound-rpc-set-sound-falloff object) ;; unknown type
|
||||
;;(define-extern sound-rpc-union object) ;; unknown type
|
||||
;;(define-extern sound-rpc-unload-music object) ;; unknown type
|
||||
;;(define-extern sound-rpc-set-language object) ;; unknown type
|
||||
;;(define-extern sound-rpc-set-reverb object) ;; unknown type
|
||||
;;(define-extern sound-rpc-sound-cmd object) ;; unknown type
|
||||
;;(define-extern sound-rpc-group-cmd object) ;; unknown type
|
||||
;;(define-extern sound-rpc-bank-cmd object) ;; unknown type
|
||||
;;(define-extern sound-rpc-continue-sound object) ;; unknown type
|
||||
;;(define-extern sound-rpc-set-flava object) ;; unknown type
|
||||
;;(define-extern sound-name object) ;; unknown type
|
||||
;;(define-extern sound-rpc-continue-group object) ;; unknown type
|
||||
|
||||
|
||||
(deftype sound-rpc-cmd (structure)
|
||||
((rsvd1 uint16 :offset-assert 0)
|
||||
(command uint16 :offset-assert 2)
|
||||
@@ -1803,13 +1928,29 @@
|
||||
;;;;;;;;;;;;;
|
||||
|
||||
; ;; vif-h
|
||||
; (deftype vif-stat (uint32)
|
||||
; ()
|
||||
; :method-count-assert 9
|
||||
; :size-assert #x4
|
||||
; :flag-assert #x900000004
|
||||
; ;; likely a bitfield type
|
||||
; )
|
||||
(deftype vif-stat (uint32)
|
||||
()
|
||||
:method-count-assert 9
|
||||
:size-assert #x4
|
||||
:flag-assert #x900000004
|
||||
;; likely a bitfield type
|
||||
)
|
||||
|
||||
(deftype vif-fbrst (uint32)
|
||||
()
|
||||
:method-count-assert 9
|
||||
:size-assert #x4
|
||||
:flag-assert #x900000004
|
||||
;; likely a bitfield type
|
||||
)
|
||||
|
||||
(deftype vif-err (uint32)
|
||||
()
|
||||
:method-count-assert 9
|
||||
:size-assert #x4
|
||||
:flag-assert #x900000004
|
||||
;; likely a bitfield type
|
||||
)
|
||||
|
||||
;; vif-h
|
||||
(deftype vif-bank (structure)
|
||||
@@ -1842,13 +1983,21 @@
|
||||
:flag-assert #x900000174
|
||||
)
|
||||
; ;; dma-h
|
||||
; (deftype dma-chcr (uint32)
|
||||
; ()
|
||||
; :method-count-assert 9
|
||||
; :size-assert #x4
|
||||
; :flag-assert #x900000004
|
||||
; ;; likely a bitfield type
|
||||
; )
|
||||
(deftype dma-chcr (uint32)
|
||||
()
|
||||
:method-count-assert 9
|
||||
:size-assert #x4
|
||||
:flag-assert #x900000004
|
||||
;; likely a bitfield type
|
||||
)
|
||||
|
||||
(deftype dma-ctrl (uint32)
|
||||
()
|
||||
:method-count-assert 9
|
||||
:size-assert #x4
|
||||
:flag-assert #x900000004
|
||||
;; likely a bitfield type
|
||||
)
|
||||
|
||||
(deftype dma-bank (structure)
|
||||
((chcr uint32 :offset 0)
|
||||
@@ -1913,13 +2062,13 @@
|
||||
)
|
||||
|
||||
; ;; dma-h
|
||||
; (deftype dma-tag (uint64)
|
||||
; ()
|
||||
; :method-count-assert 9
|
||||
; :size-assert #x8
|
||||
; :flag-assert #x900000008
|
||||
; ;; likely a bitfield type
|
||||
; )
|
||||
(deftype dma-tag (uint64)
|
||||
()
|
||||
:method-count-assert 9
|
||||
:size-assert #x8
|
||||
:flag-assert #x900000008
|
||||
;; likely a bitfield type
|
||||
)
|
||||
|
||||
|
||||
; ;; dma-h
|
||||
@@ -31574,51 +31723,6 @@
|
||||
(define-extern deg-diff function)
|
||||
(define-extern vector-y-angle function)
|
||||
|
||||
(define-extern vector3s-rotate*! function)
|
||||
(define-extern matrix-inverse-of-rot-trans! function)
|
||||
(define-extern matrix-3x3-determinant function)
|
||||
(define-extern matrix-3x3-inverse-transpose! function)
|
||||
(define-extern vector-matrix*! function)
|
||||
(define-extern matrix-scale! function)
|
||||
(define-extern column-scale-matrix! function)
|
||||
(define-extern matrix-rotate-yx! function)
|
||||
(define-extern matrix-y-angle function)
|
||||
(define-extern matrix-3x3-inverse! function)
|
||||
(define-extern matrix-rotate-z! function)
|
||||
;;(define-extern *identity-matrix* object) ;; unknown type
|
||||
(define-extern matrix-axis-angle! function)
|
||||
(define-extern matrix-rotate-zyx! function)
|
||||
(define-extern matrix-rotate-x! function)
|
||||
(define-extern matrix+! function)
|
||||
(define-extern matrix-rotate-yxy! function)
|
||||
(define-extern matrix-identity! function)
|
||||
(define-extern vector3s-matrix*! function)
|
||||
(define-extern matrix-4x4-determinant function)
|
||||
(define-extern matrix-inv-scale! function)
|
||||
(define-extern matrix-rotate-yzx! function)
|
||||
(define-extern matrix-axis-sin-cos! function)
|
||||
(define-extern matrix-lerp! function)
|
||||
(define-extern matrix-rotate-y! function)
|
||||
(define-extern matrix*! function)
|
||||
(define-extern matrix-rotate-xyz! function)
|
||||
(define-extern scale-matrix! function)
|
||||
(define-extern matrix-rotate-yxz! function)
|
||||
(define-extern matrix-rotate-zxy! function)
|
||||
(define-extern vector-rotate*! function)
|
||||
(define-extern matrix3-determinant function)
|
||||
(define-extern matrix3-inverse-transpose! function)
|
||||
(define-extern matrix-translate+! function)
|
||||
(define-extern matrix-transpose! function)
|
||||
(define-extern matrix-4x4-inverse-transpose! function)
|
||||
(define-extern matrixp*! function)
|
||||
(define-extern matrix-axis-sin-cos-vu! function)
|
||||
(define-extern matrix-translate! function)
|
||||
(define-extern matrix-4x4-inverse! function)
|
||||
(define-extern matrix-! function)
|
||||
(define-extern vector-sincos! function)
|
||||
(define-extern trs-matrix-calc! function)
|
||||
(define-extern transform-matrix-parent-calc! function)
|
||||
(define-extern transform-matrix-calc! function)
|
||||
|
||||
(define-extern quaternion-zero! function)
|
||||
(define-extern quaternion-set! function)
|
||||
@@ -31683,11 +31787,6 @@
|
||||
(define-extern acos-rad function)
|
||||
|
||||
(define-extern acos function)
|
||||
(define-extern quat->eul function)
|
||||
(define-extern set-eul! function)
|
||||
(define-extern eul->matrix function)
|
||||
(define-extern eul->quat function)
|
||||
(define-extern matrix->eul function)
|
||||
(define-extern vector-reflect-flat! function)
|
||||
(define-extern forward-down->inv-matrix function)
|
||||
(define-extern circle-test function)
|
||||
@@ -31763,43 +31862,7 @@
|
||||
(define-extern sincos! function)
|
||||
(define-extern vector-rad<-vector-deg! function)
|
||||
(define-extern radmod function)
|
||||
;;(define-extern sound-rpc-set-falloff-curve object) ;; unknown type
|
||||
;;(define-extern *sound-bank-2* object) ;; unknown type
|
||||
;;(define-extern sound-rpc-reload-info object) ;; unknown type
|
||||
;;(define-extern sound-rpc-stop-sound object) ;; unknown type
|
||||
;;(define-extern sound-rpc-list-sounds object) ;; unknown type
|
||||
;;(define-extern *current-sound-id* object) ;; unknown type
|
||||
;;(define-extern sound-bank-id object) ;; unknown type
|
||||
;;(define-extern sound-rpc-get-irx-version object) ;; unknown type
|
||||
;;(define-extern sound-rpc-load-music object) ;; unknown type
|
||||
;;(define-extern sound-rpc-set-ear-trans object) ;; unknown type
|
||||
;;(define-extern ambient-sound object) ;; unknown type
|
||||
;;(define-extern sound-rpc-pause-group object) ;; unknown type
|
||||
;;(define-extern sound-rpc-shutdown object) ;; unknown type
|
||||
;;(define-extern sound-rpc-unload-bank object) ;; unknown type
|
||||
;;(define-extern sound-rpc-pause-sound object) ;; unknown type
|
||||
;;(define-extern sound-id object) ;; unknown type
|
||||
;;(define-extern sound-play-parms object) ;; unknown type
|
||||
;;(define-extern sound-rpc-load-bank object) ;; unknown type
|
||||
;;(define-extern sound-rpc-set-master-volume object) ;; unknown type
|
||||
;;(define-extern sound-rpc-set-param object) ;; unknown type
|
||||
;;(define-extern *sound-bank-1* object) ;; unknown type
|
||||
;;(define-extern sound-rpc-stop-group object) ;; unknown type
|
||||
;;(define-extern sound-rpc-play object) ;; unknown type
|
||||
;;(define-extern sound-rpc-cmd object) ;; unknown type
|
||||
(define-extern sound-spec type)
|
||||
;;(define-extern sound-rpc-set-sound-falloff object) ;; unknown type
|
||||
;;(define-extern sound-rpc-union object) ;; unknown type
|
||||
;;(define-extern sound-rpc-unload-music object) ;; unknown type
|
||||
;;(define-extern sound-rpc-set-language object) ;; unknown type
|
||||
;;(define-extern sound-rpc-set-reverb object) ;; unknown type
|
||||
;;(define-extern sound-rpc-sound-cmd object) ;; unknown type
|
||||
;;(define-extern sound-rpc-group-cmd object) ;; unknown type
|
||||
;;(define-extern sound-rpc-bank-cmd object) ;; unknown type
|
||||
;;(define-extern sound-rpc-continue-sound object) ;; unknown type
|
||||
;;(define-extern sound-rpc-set-flava object) ;; unknown type
|
||||
;;(define-extern sound-name object) ;; unknown type
|
||||
;;(define-extern sound-rpc-continue-group object) ;; unknown type
|
||||
;
|
||||
;;(define-extern vif-stat object) ;; unknown type
|
||||
;;(define-extern vif-fbrst object) ;; unknown type
|
||||
;;(define-extern vif-err object) ;; unknown type
|
||||
|
||||
@@ -213,7 +213,14 @@ TP_Type DecompilerTypeSystem::tp_lca(const TP_Type& existing,
|
||||
return new_result;
|
||||
}
|
||||
case TP_Type::Kind::TYPE_OF_TYPE_OR_CHILD: {
|
||||
auto new_result = TP_Type::make_type_object(ts.lowest_common_ancestor(
|
||||
auto new_result = TP_Type::make_type_allow_virtual_object(ts.lowest_common_ancestor(
|
||||
existing.get_type_objects_typespec(), add.get_type_objects_typespec()));
|
||||
*changed = (new_result != existing);
|
||||
return new_result;
|
||||
}
|
||||
|
||||
case TP_Type::Kind::TYPE_OF_TYPE_NO_VIRTUAL: {
|
||||
auto new_result = TP_Type::make_type_no_virtual_object(ts.lowest_common_ancestor(
|
||||
existing.get_type_objects_typespec(), add.get_type_objects_typespec()));
|
||||
*changed = (new_result != existing);
|
||||
return new_result;
|
||||
@@ -265,6 +272,12 @@ TP_Type DecompilerTypeSystem::tp_lca(const TP_Type& existing,
|
||||
return TP_Type::make_from_ts("int");
|
||||
}
|
||||
|
||||
case TP_Type::Kind::METHOD:
|
||||
// never allow this to remain method
|
||||
*changed = true;
|
||||
return TP_Type::make_from_ts(
|
||||
ts.lowest_common_ancestor(existing.typespec(), add.typespec()));
|
||||
|
||||
case TP_Type::Kind::FALSE_AS_NULL:
|
||||
case TP_Type::Kind::UNINITIALIZED:
|
||||
case TP_Type::Kind::DYNAMIC_METHOD_ACCESS:
|
||||
@@ -288,6 +301,22 @@ TP_Type DecompilerTypeSystem::tp_lca(const TP_Type& existing,
|
||||
return result_type;
|
||||
}
|
||||
|
||||
if (existing.kind == TP_Type::Kind::TYPE_OF_TYPE_NO_VIRTUAL &&
|
||||
add.kind == TP_Type::Kind::TYPE_OF_TYPE_OR_CHILD) {
|
||||
auto result_type = TP_Type::make_type_no_virtual_object(ts.lowest_common_ancestor(
|
||||
existing.get_type_objects_typespec(), add.get_type_objects_typespec()));
|
||||
*changed = (result_type != existing);
|
||||
return result_type;
|
||||
}
|
||||
|
||||
if (existing.kind == TP_Type::Kind::TYPE_OF_TYPE_OR_CHILD &&
|
||||
add.kind == TP_Type::Kind::TYPE_OF_TYPE_NO_VIRTUAL) {
|
||||
auto result_type = TP_Type::make_type_no_virtual_object(ts.lowest_common_ancestor(
|
||||
existing.get_type_objects_typespec(), add.get_type_objects_typespec()));
|
||||
*changed = (result_type != existing);
|
||||
return result_type;
|
||||
}
|
||||
|
||||
// otherwise, as an absolute fallback, convert both to TypeSpecs and do TypeSpec LCA
|
||||
auto new_result = TP_Type::make_from_ts(
|
||||
coerce_to_reg_type(ts.lowest_common_ancestor(existing.typespec(), add.typespec())));
|
||||
|
||||
@@ -31,6 +31,8 @@ std::string TP_Type::print() const {
|
||||
return m_ts.print();
|
||||
case Kind::TYPE_OF_TYPE_OR_CHILD:
|
||||
return fmt::format("<the type {}>", m_ts.print());
|
||||
case Kind::TYPE_OF_TYPE_NO_VIRTUAL:
|
||||
return fmt::format("<the etype {}>", m_ts.print());
|
||||
case Kind::FALSE_AS_NULL:
|
||||
return fmt::format("'#f");
|
||||
case Kind::UNINITIALIZED:
|
||||
@@ -51,6 +53,8 @@ std::string TP_Type::print() const {
|
||||
return fmt::format("<integer {} + {}>", m_int, m_ts.print());
|
||||
case Kind::DYNAMIC_METHOD_ACCESS:
|
||||
return fmt::format("<dynamic-method-access>");
|
||||
case Kind::METHOD:
|
||||
return fmt::format("<method {}>", m_ts.print());
|
||||
case Kind::INVALID:
|
||||
default:
|
||||
assert(false);
|
||||
@@ -67,6 +71,10 @@ bool TP_Type::operator==(const TP_Type& other) const {
|
||||
return m_ts == other.m_ts;
|
||||
case Kind::TYPE_OF_TYPE_OR_CHILD:
|
||||
return m_ts == other.m_ts;
|
||||
case Kind::TYPE_OF_TYPE_NO_VIRTUAL:
|
||||
return m_ts == other.m_ts;
|
||||
case Kind::METHOD:
|
||||
return m_ts == other.m_ts;
|
||||
case Kind::FALSE_AS_NULL:
|
||||
return true;
|
||||
case Kind::UNINITIALIZED:
|
||||
@@ -102,6 +110,7 @@ TypeSpec TP_Type::typespec() const {
|
||||
case Kind::TYPESPEC:
|
||||
return m_ts;
|
||||
case Kind::TYPE_OF_TYPE_OR_CHILD:
|
||||
case Kind::TYPE_OF_TYPE_NO_VIRTUAL:
|
||||
return TypeSpec("type");
|
||||
case Kind::FALSE_AS_NULL:
|
||||
return TypeSpec("symbol");
|
||||
@@ -129,6 +138,8 @@ TypeSpec TP_Type::typespec() const {
|
||||
return TypeSpec("object");
|
||||
case Kind::FORMAT_STRING:
|
||||
return TypeSpec("string");
|
||||
case Kind::METHOD:
|
||||
return m_ts;
|
||||
case Kind::INVALID:
|
||||
default:
|
||||
assert(false);
|
||||
|
||||
@@ -15,8 +15,9 @@ namespace decompiler {
|
||||
class TP_Type {
|
||||
public:
|
||||
enum class Kind {
|
||||
TYPESPEC, // just a normal typespec
|
||||
TYPE_OF_TYPE_OR_CHILD, // a type object, of the given type of a child type.
|
||||
TYPESPEC, // just a normal typespec
|
||||
TYPE_OF_TYPE_OR_CHILD, // a type object, of the given type of a child type.
|
||||
TYPE_OF_TYPE_NO_VIRTUAL,
|
||||
FALSE_AS_NULL, // the GOAL "false" object, possibly used as a null.
|
||||
UNINITIALIZED, // representing data which is uninitialized.
|
||||
PRODUCT_WITH_CONSTANT, // representing: (val * multiplier)
|
||||
@@ -28,6 +29,7 @@ class TP_Type {
|
||||
INTEGER_CONSTANT_PLUS_VAR, // constant + variable. used in stuff like (&-> obj inline-val-arr
|
||||
// x)
|
||||
DYNAMIC_METHOD_ACCESS, // partial access into a
|
||||
METHOD,
|
||||
INVALID
|
||||
} kind = Kind::UNINITIALIZED;
|
||||
TP_Type() = default;
|
||||
@@ -73,6 +75,13 @@ class TP_Type {
|
||||
|
||||
static TP_Type make_from_ts(const std::string& ts) { return make_from_ts(TypeSpec(ts)); }
|
||||
|
||||
static TP_Type make_method(const TypeSpec& method_type) {
|
||||
TP_Type result;
|
||||
result.kind = Kind::METHOD;
|
||||
result.m_ts = method_type;
|
||||
return result;
|
||||
}
|
||||
|
||||
static TP_Type make_from_string(const std::string& str) {
|
||||
TP_Type result;
|
||||
result.kind = Kind::STRING_CONSTANT;
|
||||
@@ -80,13 +89,20 @@ class TP_Type {
|
||||
return result;
|
||||
}
|
||||
|
||||
static TP_Type make_type_object(const TypeSpec& type) {
|
||||
static TP_Type make_type_allow_virtual_object(const TypeSpec& type) {
|
||||
TP_Type result;
|
||||
result.kind = Kind::TYPE_OF_TYPE_OR_CHILD;
|
||||
result.m_ts = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
static TP_Type make_type_no_virtual_object(const TypeSpec& type) {
|
||||
TP_Type result;
|
||||
result.kind = Kind::TYPE_OF_TYPE_NO_VIRTUAL;
|
||||
result.m_ts = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
static TP_Type make_false() {
|
||||
TP_Type result;
|
||||
result.kind = Kind::FALSE_AS_NULL;
|
||||
@@ -149,7 +165,7 @@ class TP_Type {
|
||||
}
|
||||
|
||||
const TypeSpec& get_type_objects_typespec() const {
|
||||
assert(kind == Kind::TYPE_OF_TYPE_OR_CHILD);
|
||||
assert(kind == Kind::TYPE_OF_TYPE_OR_CHILD || kind == Kind::TYPE_OF_TYPE_NO_VIRTUAL);
|
||||
return m_ts;
|
||||
}
|
||||
|
||||
|
||||
@@ -2053,7 +2053,7 @@ TEST_F(FormRegressionTest, ExprPrintl) {
|
||||
" bgtzl v1, L62\n"
|
||||
" lw v1, pair(s7)\n"
|
||||
"\n"
|
||||
" lwu v1, -4(a0)\n"
|
||||
" lwu v1, -4(a0)\n" // want to cheat this read.
|
||||
"L62:\n"
|
||||
" lwu t9, 24(v1)\n"
|
||||
" jalr ra, t9\n"
|
||||
|
||||
@@ -660,93 +660,434 @@ TEST_F(FormRegressionTest, ExprMethod0Process) {
|
||||
"\t\t[13, [\"v0\", \"process\"]]]"));
|
||||
}
|
||||
|
||||
// TEST_F(FormRegressionTest, ExprInspectProcessHeap) {
|
||||
TEST_F(FormRegressionTest, ExprInspectProcessHeap) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L251:\n"
|
||||
" daddiu sp, sp, -64\n"
|
||||
" sd ra, 0(sp)\n"
|
||||
" sq s4, 16(sp)\n"
|
||||
" sq s5, 32(sp)\n"
|
||||
" sq gp, 48(sp)\n"
|
||||
|
||||
" or gp, a0, r0\n"
|
||||
" lwu v1, 76(gp)\n"
|
||||
" daddiu s5, v1, 4\n"
|
||||
" beq r0, r0, L253\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
"L252:\n"
|
||||
" or a0, s5, r0\n"
|
||||
" lwu v1, -4(a0)\n"
|
||||
" lwu t9, 28(v1)\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" or v1, v0, r0\n"
|
||||
" addiu s4, r0, -16\n"
|
||||
" or a0, s5, r0\n"
|
||||
" lwu v1, -4(a0)\n"
|
||||
" lwu t9, 36(v1)\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" or v1, v0, r0\n"
|
||||
" daddiu v1, v1, 15\n"
|
||||
" and v1, s4, v1\n"
|
||||
" daddu s5, s5, v1\n"
|
||||
|
||||
"L253:\n"
|
||||
" lwu v1, 84(gp)\n"
|
||||
" slt v1, s5, v1\n"
|
||||
" bne v1, r0, L252\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" or v0, s7, r0\n"
|
||||
" ld ra, 0(sp)\n"
|
||||
" lq gp, 48(sp)\n"
|
||||
" lq s5, 32(sp)\n"
|
||||
" lq s4, 16(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 64";
|
||||
std::string type = "(function process symbol)";
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (set! s5-0 (+ (-> arg0 heap-base) (the-as uint 4)))\n"
|
||||
" (while\n"
|
||||
" (< (the-as int s5-0) (the-as int (-> arg0 heap-cur)))\n"
|
||||
" (inspect (the-as basic s5-0))\n"
|
||||
" (set!\n"
|
||||
" (the-as int s5-0)\n"
|
||||
" (+ (the-as int s5-0) (logand -16 (+ (asize-of s5-0) 15)))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (quote #f)\n"
|
||||
" )\n"
|
||||
"";
|
||||
test_with_expr(func, type, expected, false, "", {},
|
||||
parse_hint_json("[\t\t[4, [\"s5\", \"basic\"]],\n"
|
||||
"\t\t[17, [\"s5\", \"int\"]]]"));
|
||||
}
|
||||
|
||||
// note: skipped method 3 process
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod5Process) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" lw v1, process(s7)\n"
|
||||
" lhu v1, 8(v1)\n"
|
||||
" lw a0, 68(a0)\n"
|
||||
" daddu v0, v1, a0\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function process int)";
|
||||
std::string expected =
|
||||
"(the-as int (+ (-> process size) (the-as uint (-> arg0 allocated-length))))";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod2Process) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" daddiu sp, sp, -32\n"
|
||||
" sd ra, 0(sp)\n"
|
||||
" sd fp, 8(sp)\n"
|
||||
" or fp, t9, r0\n"
|
||||
" sq gp, 16(sp)\n"
|
||||
|
||||
" or gp, a0, r0\n"
|
||||
" lw t9, format(s7)\n"
|
||||
" daddiu a0, s7, #t\n"
|
||||
" daddiu a1, fp, L317\n"
|
||||
" lwu a2, -4(gp)\n"
|
||||
" lwu a3, 0(gp)\n"
|
||||
" lwu t0, 32(gp)\n"
|
||||
" lwu v1, 52(gp)\n"
|
||||
" beq s7, v1, L245\n"
|
||||
" or t1, s7, r0\n"
|
||||
|
||||
" lwu v1, 52(gp)\n"
|
||||
" lwu t1, 0(v1)\n"
|
||||
|
||||
"L245:\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" lw t9, format(s7)\n"
|
||||
" daddiu a0, s7, #t\n"
|
||||
" daddiu a1, fp, L316\n"
|
||||
" lwu v1, 44(gp)\n"
|
||||
" lwu v1, 28(v1)\n"
|
||||
" lwu a2, 44(gp)\n"
|
||||
" lwu a2, 24(a2)\n"
|
||||
" dsubu a2, v1, a2\n"
|
||||
" lwu v1, 40(gp)\n"
|
||||
" lw a3, 32(v1)\n"
|
||||
" lw v1, 68(gp)\n"
|
||||
" lwu t0, 80(gp)\n"
|
||||
" lwu t1, 84(gp)\n"
|
||||
" dsubu t0, t0, t1\n"
|
||||
" dsubu t0, v1, t0\n"
|
||||
" lw t1, 68(gp)\n"
|
||||
" or t2, gp, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" or v0, gp, r0\n"
|
||||
" ld ra, 0(sp)\n"
|
||||
" ld fp, 8(sp)\n"
|
||||
" lq gp, 16(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 32";
|
||||
std::string type = "(function process process)";
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (format\n"
|
||||
" (quote #t)\n"
|
||||
" \"#<~A ~S ~A :state ~S \"\n"
|
||||
" (-> arg0 type)\n"
|
||||
" (-> arg0 name)\n"
|
||||
" (-> arg0 status)\n"
|
||||
" (if (-> arg0 state) (-> arg0 state name))\n"
|
||||
" )\n"
|
||||
" (format\n"
|
||||
" (quote #t)\n"
|
||||
" \":stack ~D/~D :heap ~D/~D @ #x~X>\"\n"
|
||||
" (- (-> arg0 top-thread stack-top) (the-as uint (-> arg0 top-thread sp)))\n"
|
||||
" (-> arg0 main-thread stack-size)\n"
|
||||
" (-\n"
|
||||
" (-> arg0 allocated-length)\n"
|
||||
" (- (-> arg0 heap-top) (the-as uint (-> arg0 heap-cur)))\n"
|
||||
" )\n"
|
||||
" (-> arg0 allocated-length)\n"
|
||||
" arg0\n"
|
||||
" )\n"
|
||||
" arg0\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "",
|
||||
{{"L317", "#<~A ~S ~A :state ~S "}, {"L316", ":stack ~D/~D :heap ~D/~D @ #x~X>"}});
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod0DeadPool) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" daddiu sp, sp, -112\n"
|
||||
" sd ra, 0(sp)\n"
|
||||
" sq s1, 16(sp)\n"
|
||||
" sq s2, 32(sp)\n"
|
||||
" sq s3, 48(sp)\n"
|
||||
" sq s4, 64(sp)\n"
|
||||
" sq s5, 80(sp)\n"
|
||||
" sq gp, 96(sp)\n"
|
||||
|
||||
" or gp, a0, r0\n"
|
||||
" or s5, a2, r0\n"
|
||||
" or s4, a3, r0\n"
|
||||
" or s2, t0, r0\n"
|
||||
" lw v1, object(s7)\n"
|
||||
" lwu t9, 16(v1)\n"
|
||||
" or a0, gp, r0\n"
|
||||
" or v1, a1, r0\n"
|
||||
" lhu a2, 8(a1)\n"
|
||||
" or a1, v1, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" or s3, v0, r0\n"
|
||||
" sw s2, 0(s3)\n"
|
||||
" addiu v1, r0, 256\n"
|
||||
" sw v1, 4(s3)\n"
|
||||
" sw s7, 8(s3)\n"
|
||||
" sw s7, 12(s3)\n"
|
||||
" sw s7, 16(s3)\n"
|
||||
" sw s3, 24(s3)\n"
|
||||
" daddiu v1, s3, 24\n"
|
||||
" sw v1, 20(s3)\n"
|
||||
" addiu s2, r0, 0\n"
|
||||
" beq r0, r0, L233\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
"L230:\n"
|
||||
" lwu s1, 16(s3)\n"
|
||||
" lw v1, process(s7)\n"
|
||||
" lwu t9, 16(v1)\n"
|
||||
" or a0, gp, r0\n"
|
||||
" lw a1, process(s7)\n"
|
||||
" daddiu a2, s7, dead\n"
|
||||
" or a3, s4, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" or v1, v0, r0\n"
|
||||
" or a0, v1, r0\n"
|
||||
" beq s7, a0, L231\n"
|
||||
" or a1, s7, r0\n"
|
||||
|
||||
" lwu a1, 20(a0)\n"
|
||||
|
||||
"L231:\n"
|
||||
" sw a1, 16(s3)\n"
|
||||
" or a0, s3, r0\n"
|
||||
" beq s7, a0, L232\n"
|
||||
" or a1, s7, r0\n"
|
||||
|
||||
" lwu a1, 20(a0)\n"
|
||||
|
||||
"L232:\n"
|
||||
" sw a1, 8(v1)\n"
|
||||
" sw s3, 28(v1)\n"
|
||||
" sw s1, 12(v1)\n"
|
||||
" daddiu s2, s2, 1\n"
|
||||
|
||||
"L233:\n"
|
||||
" slt v1, s2, s5\n"
|
||||
" bne v1, r0, L230\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" or v1, s7, r0\n"
|
||||
" or v1, s7, r0\n"
|
||||
" or v0, s3, r0\n"
|
||||
" ld ra, 0(sp)\n"
|
||||
" lq gp, 96(sp)\n"
|
||||
" lq s5, 80(sp)\n"
|
||||
" lq s4, 64(sp)\n"
|
||||
" lq s3, 48(sp)\n"
|
||||
" lq s2, 32(sp)\n"
|
||||
" lq s1, 16(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 112";
|
||||
std::string type = "(function symbol type int int basic dead-pool)";
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (set! s3-0 (object-new arg0 arg1 (the-as int (-> arg1 size))))\n"
|
||||
" (set! (-> s3-0 name) arg4)\n"
|
||||
" (set! (-> s3-0 mask) 256)\n"
|
||||
" (set! (-> s3-0 parent) (quote #f))\n"
|
||||
" (set! (-> s3-0 brother) (quote #f))\n"
|
||||
" (set! (-> s3-0 child) (quote #f))\n"
|
||||
" (set! (-> s3-0 self) s3-0)\n"
|
||||
" (set! (-> s3-0 ppointer) (&-> s3-0 self))\n"
|
||||
" (set! s2-1 0)\n"
|
||||
" (while\n"
|
||||
" (< s2-1 arg2)\n"
|
||||
" (set! s1-0 (-> s3-0 child))\n"
|
||||
" (set! v1-5 ((method-of-type process new) arg0 process (quote dead) arg3))\n"
|
||||
" (set! a0-3 v1-5)\n"
|
||||
" (set! (-> s3-0 child) (if a0-3 (-> a0-3 ppointer)))\n"
|
||||
" (set! a0-4 s3-0)\n"
|
||||
" (set! (-> v1-5 parent) (if a0-4 (-> a0-4 ppointer)))\n"
|
||||
" (set! (-> v1-5 pool) s3-0)\n"
|
||||
" (set! (-> v1-5 brother) s1-0)\n"
|
||||
" (set! s2-1 (+ s2-1 1))\n"
|
||||
" )\n"
|
||||
" s3-0\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "dead-pool");
|
||||
}
|
||||
|
||||
// TODO - sketchy types, and likely a bug in the game.
|
||||
// TEST_F(FormRegressionTest, ExprMethod14DeadPool) {
|
||||
// std::string func =
|
||||
// " sll r0, r0, 0\n"
|
||||
// "L251:\n"
|
||||
// " daddiu sp, sp, -64\n"
|
||||
// " sd ra, 0(sp)\n"
|
||||
// " sd fp, 8(sp)\n"
|
||||
// " or fp, t9, r0\n"
|
||||
// " sq s4, 16(sp)\n"
|
||||
// " sq s5, 32(sp)\n"
|
||||
// " sq gp, 48(sp)\n"
|
||||
//
|
||||
// " or gp, a0, r0\n"
|
||||
// " lwu v1, 76(gp)\n"
|
||||
// " daddiu s5, v1, 4\n"
|
||||
// " beq r0, r0, L253\n"
|
||||
// " sll r0, r0, 0\n"
|
||||
// " or s5, a0, r0\n"
|
||||
// " or gp, a1, r0\n"
|
||||
// " lwu s4, 16(s5)\n"
|
||||
// " bnel s7, s4, L223\n"
|
||||
//
|
||||
// "L252:\n"
|
||||
// " or a0, s5, r0\n"
|
||||
// " or v1, s7, r0\n"
|
||||
//
|
||||
// " lw v1, *debug-segment*(s7)\n"
|
||||
// " beql s7, v1, L223\n"
|
||||
//
|
||||
// " or v1, v1, r0\n"
|
||||
//
|
||||
// " lw v1, *debug-dead-pool*(s7)\n"
|
||||
// " dsubu a0, s5, v1\n"
|
||||
// " daddiu v1, s7, 8\n"
|
||||
// " movz v1, s7, a0\n"
|
||||
//
|
||||
// "L223:\n"
|
||||
// " beq s7, v1, L225\n"
|
||||
// " or v1, s7, r0\n"
|
||||
//
|
||||
// " lw a0, *debug-dead-pool*(s7)\n"
|
||||
// " lwu v1, -4(a0)\n"
|
||||
// " lwu t9, 28(v1)\n"
|
||||
// " lwu t9, 72(v1)\n"
|
||||
// " or a1, gp, r0\n"
|
||||
// " jalr ra, t9\n"
|
||||
// " sll v0, ra, 0\n"
|
||||
//
|
||||
// " or v1, v0, r0\n"
|
||||
// " addiu s4, r0, -16\n"
|
||||
// " or a0, s5, r0\n"
|
||||
// " lwu v1, -4(a0)\n"
|
||||
// " lwu t9, 36(v1)\n"
|
||||
// " or s4, v0, r0\n"
|
||||
// " beq s7, s4, L225\n"
|
||||
// " or v1, s7, r0\n"
|
||||
//
|
||||
// " lw t9, format(s7)\n"
|
||||
// " addiu a0, r0, 0\n"
|
||||
// " daddiu a1, fp, L315\n"
|
||||
// " or a2, gp, r0\n"
|
||||
// " or v1, s4, r0\n"
|
||||
// " beq s7, v1, L224\n"
|
||||
// " or a3, s7, r0\n"
|
||||
//
|
||||
// " lwu v1, 0(v1)\n"
|
||||
// " lwu a3, 24(v1)\n"
|
||||
//
|
||||
// "L224:\n"
|
||||
// " lwu t0, 0(s5)\n"
|
||||
// " jalr ra, t9\n"
|
||||
// " sll v0, ra, 0\n"
|
||||
//
|
||||
// "\n"
|
||||
// " or v1, v0, r0\n"
|
||||
// " daddiu v1, v1, 15\n"
|
||||
// " and v1, s4, v1\n"
|
||||
// " daddu s5, s5, v1\n"
|
||||
//
|
||||
// "L253:\n"
|
||||
// " lwu v1, 84(gp)\n"
|
||||
// " slt v1, s5, v1\n"
|
||||
// " bne v1, r0, L252\n"
|
||||
// "L225:\n"
|
||||
// " beq s7, s4, L226\n"
|
||||
// " sll r0, r0, 0\n"
|
||||
//
|
||||
// " lwu v1, 0(s4)\n"
|
||||
// " sw gp, -4(v1)\n"
|
||||
// " lwu v0, 0(s4)\n"
|
||||
// " beq r0, r0, L228\n"
|
||||
// " sll r0, r0, 0\n"
|
||||
//
|
||||
// "L226:\n"
|
||||
// " lw t9, format(s7)\n"
|
||||
// " addiu a0, r0, 0\n"
|
||||
// " daddiu a1, fp, L314\n"
|
||||
// " beq s7, s4, L227\n"
|
||||
// " or a3, s7, r0\n"
|
||||
//
|
||||
// " lwu v1, 0(s4)\n"
|
||||
// " lwu a3, 24(v1)\n"
|
||||
//
|
||||
// "L227:\n"
|
||||
// " lwu t0, 0(s5)\n"
|
||||
// " or a2, gp, r0\n"
|
||||
// " jalr ra, t9\n"
|
||||
// " sll v0, ra, 0\n"
|
||||
//
|
||||
// " or v0, s7, r0\n"
|
||||
//
|
||||
// "L228:\n"
|
||||
// " ld ra, 0(sp)\n"
|
||||
// " ld fp, 8(sp)\n"
|
||||
// " lq gp, 48(sp)\n"
|
||||
// " lq s5, 32(sp)\n"
|
||||
// " lq s4, 16(sp)\n"
|
||||
// " jr ra\n"
|
||||
// " daddiu sp, sp, 64";
|
||||
// std::string type = "(function process symbol)";
|
||||
// std::string type = "(function dead-pool type int process)";
|
||||
// std::string expected =
|
||||
// "(begin\n"
|
||||
// " (set!\n"
|
||||
// " v0-0\n"
|
||||
// " (if\n"
|
||||
// " (= (-> arg0 type) symbol)\n"
|
||||
// " (object-new arg0 arg1 (the-as int (+ (-> process size) (the-as uint arg3))))\n"
|
||||
// " (+ (the-as int arg0) 4)\n"
|
||||
// " (set! s4-0 (-> arg0 child))\n"
|
||||
// " (when\n"
|
||||
// " (and (not s4-0) *debug-segment* (!= arg0 *debug-dead-pool*))\n"
|
||||
// " (set! s4-0 (get-process *debug-dead-pool* arg1 arg2))\n"
|
||||
// " (when\n"
|
||||
// " s4-0\n"
|
||||
// " (set! t9-1 format)\n"
|
||||
// " (set! a0-2 0)\n"
|
||||
// " (set!\n"
|
||||
// " a1-2\n"
|
||||
// " \"WARNING: ~A ~A had to be allocated from the debug pool, because ~A was empty.~%\"\n"
|
||||
// " )\n"
|
||||
// " (set! a2-1 arg1)\n"
|
||||
// " (set! v1-6 s4-0)\n"
|
||||
// " (t9-1 a0-2 a1-2 a2-1 (if v1-6 (-> v1-6 name 6)) (-> arg0 name))\n"
|
||||
// " )\n"
|
||||
// " )\n"
|
||||
// " (the-as\n"
|
||||
// " process\n"
|
||||
// " (cond\n"
|
||||
// " (s4-0\n"
|
||||
// " (set! (-> (the-as (pointer process-tree) s4-0) 0 type) arg1)\n"
|
||||
// " (-> s4-0 0)\n"
|
||||
// " )\n"
|
||||
// " (else\n"
|
||||
// " (format\n"
|
||||
// " 0\n"
|
||||
// " \"WARNING: ~A ~A could not be allocated, because ~A was empty.~%\"\n"
|
||||
// " arg1\n"
|
||||
// " (if s4-0 (-> s4-0 0 self))\n"
|
||||
// " (-> arg0 name)\n"
|
||||
// " )\n"
|
||||
// " (quote #f)\n"
|
||||
// " )\n"
|
||||
// " )\n"
|
||||
// " )\n"
|
||||
// " (set! (-> (the-as process v0-0) name) arg2)\n"
|
||||
// " (set! (-> v0-0 status) (quote dead))\n"
|
||||
// " (set! (-> v0-0 pid) 0)\n"
|
||||
// " (set! (-> v0-0 pool) (quote #f))\n"
|
||||
// " (set! (-> v0-0 allocated-length) arg3)\n"
|
||||
// " (set! (-> v0-0 top-thread) (quote #f))\n"
|
||||
// " (set! (-> v0-0 main-thread) (quote #f))\n"
|
||||
// " (set! v1-5 (-> v0-0 stack))\n"
|
||||
// " (set! (-> v0-0 heap-cur) v1-5)\n"
|
||||
// " (set! (-> v0-0 heap-base) v1-5)\n"
|
||||
// " (set! (-> v0-0 heap-top) (&-> v0-0 stack (-> v0-0 allocated-length)))\n"
|
||||
// " (set! (-> v0-0 stack-frame-top) (-> v0-0 heap-top))\n"
|
||||
// " (set! (-> v0-0 stack-frame-top) (quote #f))\n"
|
||||
// " (set! (-> v0-0 state) (quote #f))\n"
|
||||
// " (set! (-> v0-0 next-state) (quote #f))\n"
|
||||
// " (set! (-> v0-0 entity) (quote #f))\n"
|
||||
// " (set! (-> v0-0 trans-hook) (quote #f))\n"
|
||||
// " (set! (-> v0-0 post-hook) (quote #f))\n"
|
||||
// " (set! (-> v0-0 event-hook) (quote #f))\n"
|
||||
// " (set! (-> v0-0 parent) (quote #f))\n"
|
||||
// " (set! (-> v0-0 brother) (quote #f))\n"
|
||||
// " (set! (-> v0-0 child) (quote #f))\n"
|
||||
// " (set! (-> v0-0 self) v0-0)\n"
|
||||
// " (set! (-> v0-0 ppointer) (&-> v0-0 self))\n"
|
||||
// " v0-0\n"
|
||||
// " )";
|
||||
// test_with_expr(func, type, expected, false, "", {},
|
||||
// parse_hint_json("[\t\t[4, [\"s5\", \"basic\"]],\n"
|
||||
// "\t\t[17, [\"s5\", \"int\"]]]"));
|
||||
// test_with_expr(
|
||||
// func, type, expected, false, "dead-pool",
|
||||
// {{"L315", "WARNING: ~A ~A had to be allocated from the debug pool, because ~A was
|
||||
// empty.~%"},
|
||||
// {"L314", "WARNING: ~A ~A could not be allocated, because ~A was empty.~%"}},
|
||||
// parse_hint_json("[\t\t[25, [\"v1\", \"(pointer process-tree)\"]],\n"
|
||||
// "\t\t[30, [\"s4\", \"(pointer process-tree)\"]]]"));
|
||||
//}
|
||||
Reference in New Issue
Block a user