[decompiler] as-type and font method support (#3855)

Add support for `as-type` macro, and detecting inline font methods. This
works in all three games but I've only updated jak 3's goal_src for now.
Eventually I will go back and work through the others, but I want to get
more decompiler features in first.


![image](https://github.com/user-attachments/assets/5c31bf85-97b4-437c-bc4b-dc054e60551e)

---------

Co-authored-by: water111 <awaterford1111445@gmail.com>
This commit is contained in:
water111
2025-02-01 21:23:11 -05:00
committed by GitHub
parent d5590ab638
commit 48cb9bb787
645 changed files with 5391 additions and 16694 deletions
+73 -5
View File
@@ -4638,6 +4638,67 @@ Form* try_rewrite_as_process_to_ppointer(CondNoElseElement* value,
GenericOperator::make_fixed(FixedOperatorKind::PROCESS_TO_PPOINTER), repopped);
}
/*!
* (if (type? foo bar)
* foo
* )
*/
Form* try_rewrite_as_as_type(CondNoElseElement* value,
FormStack& stack,
FormPool& pool,
const Env& env,
const TypeSpec& resulting_type) {
if (value->entries.size() != 1) {
return nullptr;
}
auto condition = value->entries.at(0).condition;
auto body = value->entries[0].body;
auto condition_matcher = Matcher::op(
GenericOpMatcher::condition(IR2_Condition::Kind::TRUTHY),
{Matcher::func(Matcher::symbol("type?"), {Matcher::any_reg(0), Matcher::any_symbol(1)})});
auto condition_mr = match(condition_matcher, condition);
if (!condition_mr.matched) {
return nullptr;
}
auto body_matcher = Matcher::any_reg(0);
auto body_mr = match(body_matcher, body);
if (!body_mr.matched) {
body_mr = match(Matcher::cast_to_any(1, body_matcher), body);
if (!body_mr.matched) {
return nullptr;
}
}
auto body_var = body_mr.maps.regs.at(0).value();
auto condition_var = condition_mr.maps.regs.at(0).value();
auto* menv = const_cast<Env*>(&env);
menv->disable_use(body_var);
auto repopped = stack.pop_reg(condition_var, {}, env, true);
if (!repopped) {
repopped = var_to_form(condition_var, pool);
}
auto new_type = condition_mr.maps.strings.at(1);
auto result = pool.form<GenericElement>(
GenericOperator::make_function(pool.form<ConstantTokenElement>("as-type")),
std::vector<Form*>{repopped, pool.form<ConstantTokenElement>(new_type)});
// silly cast situation:
// sometimes there is something dumb like (the specific (as-type foo general))
// we have to make sure that we keep the leading cast.
// HACK: inserting casts more aggressively in Jak 2 because I am too lazy to fix up all the
// slightly wrong casts that matter now.
if (resulting_type != TypeSpec(new_type) &&
(env.version == GameVersion::Jak2 || env.dts->ts.tc(TypeSpec(new_type), resulting_type))) {
return pool.form<CastElement>(resulting_type, result);
} else {
return result;
}
}
// (if x (-> x 0 self)) -> (ppointer->process x)
Form* try_rewrite_as_pppointer_to_process(CondNoElseElement* value,
FormStack& stack,
@@ -4810,11 +4871,18 @@ void CondNoElseElement::push_to_stack(const Env& env, FormPool& pool, FormStack&
stack.push_value_to_reg(write_as_value, as_ja_group, true,
env.get_variable_type(final_destination, false));
} else {
// lg::print("func {} final destination {} type {}\n", env.func->name(),
// final_destination.to_string(env),
// env.get_variable_type(final_destination, false).print());
stack.push_value_to_reg(write_as_value, pool.alloc_single_form(nullptr, this), true,
env.get_variable_type(final_destination, false));
auto as_as_type = try_rewrite_as_as_type(this, stack, pool, env,
env.get_variable_type(final_destination, true));
if (as_as_type) {
stack.push_value_to_reg(write_as_value, as_as_type, true,
env.get_variable_type(final_destination, false));
} else {
// lg::print("func {} final destination {} type {}\n", env.func->name(),
// final_destination.to_string(env),
// env.get_variable_type(final_destination, false).print());
stack.push_value_to_reg(write_as_value, pool.alloc_single_form(nullptr, this), true,
env.get_variable_type(final_destination, false));
}
}
}
}