Skip to content

Commit

Permalink
[decompiler] More inline vector functions (#3861)
Browse files Browse the repository at this point in the history
This adds more recognition for inlined vector functions to the
decompiler, which can clean up a bunch of ugly looking code/`rlet`s.


![image](https://github.com/user-attachments/assets/1f7b4627-81bd-481b-b828-76b9f7ba13b3)

Unfortunately, this changes the numbering of ops in the decomp, since
all the vector instructions get combined in a single "operation" by the
decompiler. I really tried to avoid having this ever happen in the
decompiler and this is one of the few cases where it has. So I had to
update a bunch of type casts.

For that reason I haven't turned this on in Jak 2 yet, although I am
planning to do that at some point. (probably at the same time as porting
back a bunch of jak 3 improvements to jak 2)

---------

Co-authored-by: water111 <[email protected]>
  • Loading branch information
water111 and water111 authored Feb 16, 2025
1 parent b07d0d9 commit 2a4d3d7
Show file tree
Hide file tree
Showing 340 changed files with 21,994 additions and 35,357 deletions.
6 changes: 6 additions & 0 deletions decompiler/IR2/AtomicOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,12 @@ std::string get_simple_expression_op_name(SimpleExpression::Kind kind) {
return "vec4dot";
case SimpleExpression::Kind::VECTOR_LENGTH:
return "veclength";
case SimpleExpression::Kind::VECTOR_LENGTH_SQUARED:
return "veclength-squared";
case SimpleExpression::Kind::VECTOR_PLUS_FLOAT_TIMES:
return "vecplusfloattimes";
case SimpleExpression::Kind::VECTOR_PLUS_TIMES:
return "vecplustimes";
case SimpleExpression::Kind::SET_ON_LESS_THAN:
case SimpleExpression::Kind::SET_ON_LESS_THAN_IMM:
return "set-on-less-than";
Expand Down Expand Up @@ -440,8 +444,10 @@ int get_simple_expression_arg_count(SimpleExpression::Kind kind) {
case SimpleExpression::Kind::SET_ON_LESS_THAN_IMM:
return 2;
case SimpleExpression::Kind::VECTOR_LENGTH:
case SimpleExpression::Kind::VECTOR_LENGTH_SQUARED:
return 1;
case SimpleExpression::Kind::VECTOR_PLUS_FLOAT_TIMES:
case SimpleExpression::Kind::VECTOR_PLUS_TIMES:
return 4;
default:
ASSERT(false);
Expand Down
2 changes: 2 additions & 0 deletions decompiler/IR2/AtomicOp.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,9 @@ class SimpleExpression {
VECTOR_3_DOT,
VECTOR_4_DOT,
VECTOR_LENGTH, // jak 2 only.
VECTOR_LENGTH_SQUARED, // jak 2 only.
VECTOR_PLUS_FLOAT_TIMES, // jak 2 only.
VECTOR_PLUS_TIMES,
SET_ON_LESS_THAN,
SET_ON_LESS_THAN_IMM
};
Expand Down
1 change: 1 addition & 0 deletions decompiler/IR2/AtomicOpTypeAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ TP_Type SimpleExpression::get_type(const TypeState& input,
case Kind::VECTOR_3_DOT:
case Kind::VECTOR_4_DOT:
case Kind::VECTOR_LENGTH:
case Kind::VECTOR_LENGTH_SQUARED:
return TP_Type::make_from_ts("float");
default:
throw std::runtime_error("Simple expression cannot get_type: " +
Expand Down
4 changes: 4 additions & 0 deletions decompiler/IR2/Form.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1890,8 +1890,12 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
return "mouse-hold?";
case FixedOperatorKind::VECTOR_LENGTH:
return "vector-length";
case FixedOperatorKind::VECTOR_LENGTH_SQUARED:
return "vector-length-squared";
case FixedOperatorKind::VECTOR_PLUS_FLOAT_TIMES:
return "vector+float*!";
case FixedOperatorKind::VECTOR_PLUS_TIMES:
return "vector+*!";
case FixedOperatorKind::FOCUS_TEST:
return "focus-test?";
default:
Expand Down
1 change: 1 addition & 0 deletions decompiler/IR2/Form.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ class SimpleExpressionElement : public FormElement {
void update_from_stack_vector_plus_float_times(const Env& env,
FormPool& pool,
FormStack& stack,
FixedOperatorKind op,
std::vector<FormElement*>* result,
bool allow_side_effects);
void update_from_stack_vectors_in_common(FixedOperatorKind kind,
Expand Down
17 changes: 13 additions & 4 deletions decompiler/IR2/FormExpressionAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1652,6 +1652,7 @@ void SimpleExpressionElement::update_from_stack_vector_plus_float_times(
const Env& env,
FormPool& pool,
FormStack& stack,
FixedOperatorKind op,
std::vector<FormElement*>* result,
bool allow_side_effects) {
std::vector<Form*> popped_args = pop_to_forms({m_expr.get_arg(0).var(), m_expr.get_arg(1).var(),
Expand All @@ -1668,9 +1669,8 @@ void SimpleExpressionElement::update_from_stack_vector_plus_float_times(
}

auto new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::VECTOR_PLUS_FLOAT_TIMES),
std::vector<Form*>{popped_args.at(0), popped_args.at(1), popped_args.at(2),
popped_args.at(3)});
GenericOperator::make_fixed(op), std::vector<Form*>{popped_args.at(0), popped_args.at(1),
popped_args.at(2), popped_args.at(3)});
result->push_back(new_form);
}

Expand Down Expand Up @@ -2585,8 +2585,17 @@ void SimpleExpressionElement::update_from_stack(const Env& env,
update_from_stack_vectors_in_common(FixedOperatorKind::VECTOR_LENGTH, env, pool, stack,
result, allow_side_effects);
break;
case SimpleExpression::Kind::VECTOR_LENGTH_SQUARED:
update_from_stack_vectors_in_common(FixedOperatorKind::VECTOR_LENGTH_SQUARED, env, pool,
stack, result, allow_side_effects);
break;
case SimpleExpression::Kind::VECTOR_PLUS_FLOAT_TIMES:
update_from_stack_vector_plus_float_times(env, pool, stack, result, allow_side_effects);
update_from_stack_vector_plus_float_times(
env, pool, stack, FixedOperatorKind::VECTOR_PLUS_FLOAT_TIMES, result, allow_side_effects);
break;
case SimpleExpression::Kind::VECTOR_PLUS_TIMES:
update_from_stack_vector_plus_float_times(
env, pool, stack, FixedOperatorKind::VECTOR_PLUS_TIMES, result, allow_side_effects);
break;
default:
throw std::runtime_error(
Expand Down
2 changes: 2 additions & 0 deletions decompiler/IR2/IR2_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,9 @@ enum class FixedOperatorKind {
PPOINTER_TO_PROCESS,
VECTOR_4_DOT,
VECTOR_LENGTH,
VECTOR_LENGTH_SQUARED,
VECTOR_PLUS_FLOAT_TIMES,
VECTOR_PLUS_TIMES,
SEND_EVENT,
CPAD_PRESSED_P,
CPAD_HOLD_P,
Expand Down
153 changes: 151 additions & 2 deletions decompiler/analysis/atomic_op_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1962,6 +1962,75 @@ std::unique_ptr<AtomicOp> convert_6(const Instruction& i0,
return nullptr;
}

std::unique_ptr<AtomicOp> convert_vector_length_squared(const Instruction* instrs, int idx) {
// 0: lqc2 vf1, 0(a0)
if (instrs[0].kind != InstructionKind::LQC2 || instrs[0].get_dst(0).get_reg() != make_vf(1) ||
!instrs[0].get_src(0).is_imm(0)) {
return nullptr;
}
Register vec_src = instrs[0].get_src(1).get_reg();

auto vf0 = make_vf(0);
auto vf1 = make_vf(1);
auto vf2 = make_vf(2);

// 1: vaddw.x vf2, vf0, vf0
if (instrs[1].kind != InstructionKind::VADD_BC || instrs[1].get_dst(0).get_reg() != vf2 ||
instrs[1].get_src(0).get_reg() != vf0 || instrs[1].get_src(1).get_reg() != vf0 ||
instrs[1].cop2_dest != 0b1000 || instrs[1].cop2_bc != 3) {
return nullptr;
}

// 2: vmul.xyzw vf1, vf1, vf1
if (instrs[2].kind != InstructionKind::VMUL || instrs[2].get_dst(0).get_reg() != vf1 ||
instrs[2].get_src(0).get_reg() != vf1 || instrs[2].get_src(1).get_reg() != vf1 ||
instrs[2].cop2_dest != 0b1111) {
return nullptr;
}

// 3: vmulax.x acc, vf2, vf1
if (instrs[3].kind != InstructionKind::VMULA_BC || instrs[3].get_src(0).get_reg() != vf2 ||
instrs[3].get_src(1).get_reg() != vf1 || instrs[3].cop2_dest != 0b1000 ||
instrs[3].cop2_bc != 0) {
return nullptr;
}

// 4: vmadday.x acc, vf2, vf1
if (instrs[4].kind != InstructionKind::VMADDA_BC || instrs[4].get_src(0).get_reg() != vf2 ||
instrs[4].get_src(1).get_reg() != vf1 || instrs[4].cop2_dest != 0b1000 ||
instrs[4].cop2_bc != 1) {
return nullptr;
}

// 5: vmaddz.x vf1, vf2, vf1
if (instrs[5].kind != InstructionKind::VMADD_BC || instrs[5].get_dst(0).get_reg() != vf1 ||
instrs[5].get_src(0).get_reg() != vf2 || instrs[5].get_src(1).get_reg() != vf1 ||
instrs[5].cop2_dest != 0b1000 || instrs[5].cop2_bc != 2) {
return nullptr;
}

// 6: qmfc2.i v0, vf1
if (instrs[6].kind != InstructionKind::QMFC2 || instrs[6].src->get_reg() != vf1) {
return nullptr;
}
auto dst = instrs[6].get_dst(0).get_reg();
return std::make_unique<SetVarOp>(
make_dst_var(dst, idx),
SimpleExpression(SimpleExpression::Kind::VECTOR_LENGTH_SQUARED, make_src_atom(vec_src, idx)),
idx);
}

std::unique_ptr<AtomicOp> convert_7(const Instruction* instrs, int idx, GameVersion version) {
if (version == GameVersion::Jak3) {
auto as_vector_length_squared = convert_vector_length_squared(instrs, idx);
if (as_vector_length_squared) {
return as_vector_length_squared;
}
}

return nullptr;
}

std::unique_ptr<AtomicOp> convert_vector_plus_float_times(const Instruction* instrs, int idx) {
// lqc2 vf2, 0(a2)
if (instrs[0].kind != InstructionKind::LQC2 || instrs[0].get_dst(0).get_reg() != make_vf(2) ||
Expand Down Expand Up @@ -2029,11 +2098,83 @@ std::unique_ptr<AtomicOp> convert_vector_plus_float_times(const Instruction* ins
idx);
}

std::unique_ptr<AtomicOp> convert_8(const Instruction* instrs, int idx) {
std::unique_ptr<AtomicOp> convert_vector_plus_times(const Instruction* instrs, int idx) {
// 0: mfc1 a3, f0
if (instrs[0].kind != InstructionKind::MFC1) {
return nullptr;
}
Register flt_src_3 = instrs[0].get_src(0).get_reg();
Register temp = instrs[0].get_dst(0).get_reg();

// 1: qmtc2.i vf7, a3
if (instrs[1].kind != InstructionKind::QMTC2 || instrs[1].get_dst(0).get_reg() != make_vf(7) ||
instrs[1].get_src(0).get_reg() != temp) {
return nullptr;
}
// 2: lqc2 vf5, 0(a2)
if (instrs[2].kind != InstructionKind::LQC2 || instrs[2].get_dst(0).get_reg() != make_vf(5) ||
!instrs[2].get_src(0).is_imm(0)) {
return nullptr;
}
Register vec_src_2 = instrs[2].get_src(1).get_reg();

// 3: lqc2 vf4, 0(a1)
if (instrs[3].kind != InstructionKind::LQC2 || instrs[3].get_dst(0).get_reg() != make_vf(4) ||
!instrs[3].get_src(0).is_imm(0)) {
return nullptr;
}
Register vec_src_1 = instrs[3].get_src(1).get_reg();

// 4: vaddx.w vf6, vf0, vf0
if (instrs[4].kind != InstructionKind::VADD_BC || instrs[4].get_dst(0).get_reg() != make_vf(6) ||
instrs[4].get_src(0).get_reg() != make_vf(0) ||
instrs[4].get_src(1).get_reg() != make_vf(0) || instrs[4].cop2_bc != 0 ||
instrs[4].cop2_dest != 0b0001) {
return nullptr;
}
// 5: vmulax.xyz acc, vf5, vf7
// vmulax.xyz acc, vf5, vf7
if (instrs[5].kind != InstructionKind::VMULA_BC || instrs[5].get_src(0).get_reg() != make_vf(5) ||
instrs[5].get_src(1).get_reg() != make_vf(7) || instrs[5].cop2_dest != 0b1110 ||
instrs[5].cop2_bc != 0) {
return nullptr;
}

// 6: vmaddw.xyz vf6, vf4, vf0
if (instrs[6].kind != InstructionKind::VMADD_BC || instrs[6].get_dst(0).get_reg() != make_vf(6) ||
instrs[6].get_src(0).get_reg() != make_vf(4) ||
instrs[6].get_src(1).get_reg() != make_vf(0) || instrs[6].cop2_dest != 0b1110 ||
instrs[6].cop2_bc != 3) {
return nullptr;
}

// 7: sqc2 vf6, 0(a0)
if (instrs[7].kind != InstructionKind::SQC2 || instrs[7].get_src(0).get_reg() != make_vf(6) ||
!instrs[7].get_src(1).is_imm(0)) {
return nullptr;
}
Register dst = instrs[7].get_src(2).get_reg();

return std::make_unique<SetVarOp>(
make_dst_var(dst, idx),
SimpleExpression(SimpleExpression::Kind::VECTOR_PLUS_TIMES, make_src_atom(dst, idx),
make_src_atom(vec_src_1, idx), make_src_atom(vec_src_2, idx),
make_src_atom(flt_src_3, idx)),
idx);
}

std::unique_ptr<AtomicOp> convert_8(const Instruction* instrs, int idx, GameVersion version) {
auto as_vector_float_plus_times = convert_vector_plus_float_times(instrs, idx);
if (as_vector_float_plus_times) {
return as_vector_float_plus_times;
}

if (version == GameVersion::Jak3) {
auto as_vector_plus_times = convert_vector_plus_times(instrs, idx);
if (as_vector_plus_times) {
return as_vector_plus_times;
}
}
return nullptr;
}

Expand Down Expand Up @@ -2378,13 +2519,21 @@ int convert_block_to_atomic_ops(int begin_idx,
}

if (!converted && n_instr >= 8) {
op = convert_8(&instr[0], op_idx);
op = convert_8(&instr[0], op_idx, version);
if (op) {
converted = true;
length = 8;
}
}

if (!converted && n_instr >= 7) {
op = convert_7(&instr[0], op_idx, version);
if (op) {
converted = true;
length = 7;
}
}

if (!converted && n_instr >= 6) {
// try 6 instructions
op = convert_6(instr[0], instr[1], instr[2], instr[3], instr[4], instr[5], op_idx);
Expand Down
43 changes: 31 additions & 12 deletions decompiler/config/jak3/all-types.gc
Original file line number Diff line number Diff line change
Expand Up @@ -2398,7 +2398,7 @@
(define-extern quaternion-rotate-y-to-vector! (function quaternion quaternion quaternion float quaternion))
(define-extern vector-rotate-around-axis!
"Rotate along y so z-axis points to match another. Use arg3 as the max rotation amount."
(function vector quaternion float vector vector))
(function vector vector float vector vector))
(define-extern vector-rotate-x! "Rotate vector along x axis." (function vector vector float vector))
(define-extern vector-rotate-y! "Rotate vector along y axis." (function vector vector float vector))
(define-extern vector-rotate-z! "Rotate vector along z axis." (function vector vector float vector))
Expand Down Expand Up @@ -17618,7 +17618,7 @@
exit ;; 16
)
(:methods
(talker-method-17 (_type_) none) ;; 17
(draw-text (_type_) none) ;; 17
)
)

Expand Down Expand Up @@ -19477,6 +19477,21 @@
;; ---aligner-h:align-opts

(deftype align-control (basic)
"Align-control is a utility for moving a process-drawable based on its animation.
The animation format has two special parent-like joints: prejoint and align.
The prejoint is the parent for all joints, so it causes all bones to move.
The align joint has no children, but the convention is that moving align will
cause the entire process-drawable's root to move, effectively moving the entire character.
Most of the time, this is the preferable way to move a character as part of an animation -
it will update their velocity and when the animation ends or is canceled,
the offset in position from playing the animation will stay. For example, if Jak punches,
his velocity will increase due to the animated align in the punch animation.
To implement this, the align-control computes the relative transform between align frames.
To apply the position offset, typically the velocity is updated, then the normal per-frame
physics update will end up moving the position. For orientation, there is no concept of
angular velocity, so instead align-control directly modifies the quaternion.
Unlike normal animation evaluation, the alignment's blend is computed in a different way
that doesn't blend and instead just picks the most recently pushed animation."
((flags align-flags :offset-assert 4) ;; guessed by decompiler
(process process-drawable :offset-assert 8) ;; guessed by decompiler
(frame-group art-joint-anim :offset-assert 12) ;; guessed by decompiler
Expand All @@ -19492,11 +19507,12 @@
:flag-assert #xe00000134
(:methods
(new (symbol type process) _type_) ;; 0
(compute-alignment! (_type_) transformq) ;; 9
(align! (_type_ align-opts float float float) trsqv) ;; 10
(align-vel-and-quat-only! (_type_ align-opts vector int float float) trsqv) ;; 11
(first-transform (_type_) transform) ;; 12
(second-transform (_type_) transform) ;; 13
(compute-delta-align! "Compute the change in align since the last time this was called." (_type_) transformq) ;; 9
(align! "Apply the alignment to the process-drawable." (_type_ align-opts float float float) trsqv) ;; 10
(adjust-root-no-gravity! "Adjust the quat and transv of the process drawable to take into account the
most recent change in align." (_type_ align-opts vector int float float) trsqv) ;; 11
(first-transform "Get the more recent align transform" (_type_) transform) ;; 12
(second-transform "Get the second most recent align trasnform" (_type_) transform) ;; 13
)
)

Expand Down Expand Up @@ -42344,6 +42360,7 @@
)

(deftype joint-exploder-list (structure)
"Linked list of joint-exploder-joint."
((head int32 :offset-assert 0)
(pre-moved? symbol :offset-assert 4) ;; guessed by decompiler
(bbox-valid? symbol :offset-assert 8) ;; guessed by decompiler
Expand All @@ -42364,6 +42381,8 @@
)

(deftype joint-exploder (process-drawable)
"The joint-exploder animates an object exploding into pieces.
For example, geyser rock dummies, or the jak 1 zoomer."
((parent (pointer process-drawable) :override)
(die-if-below-y float :offset-assert 200)
(die-if-beyond-xz-dist-sqrd float :offset-assert 204)
Expand All @@ -42379,14 +42398,14 @@
:size-assert #x158
:flag-assert #x1e00e00158
(:methods
(add-joint-to-list (_type_ joint-exploder-list int) int) ;; 20
(update-bbox-for-joint (_type_ joint-exploder-list joint-exploder-joint) none) ;; 21
(add-joint-to-list "Add a joint to the given linked list of joints." (_type_ joint-exploder-list int) int) ;; 20
(update-bbox-for-joint "Update the bounding box to include the joint" (_type_ joint-exploder-list joint-exploder-joint) none) ;; 21
(do-collision-response (_type_ joint-exploder-list) none) ;; 22
(init-joint-list (_type_) none) ;; 23
(remove-from-list-and-reset (_type_ joint-exploder-list int) int) ;; 24
(remove-from-list-and-reset "Remove a joint from the list and clear its transform" (_type_ joint-exploder-list int) int) ;; 24
(final-adjust (_type_ joint-exploder-list int) int) ;; 25
(integrate-and-kill (_type_ joint-exploder-list) none) ;; 26
(remove-joint-from-list (_type_ joint-exploder-list int) int) ;; 27
(remove-joint-from-list "Remove a joint from the linked list and invalidate the bbox." (_type_ joint-exploder-list int) int) ;; 27
(adjust-bbox-for-limits-along-axis (_type_ joint-exploder-list int) joint-exploder-list) ;; 28
(adjust-bbox-for-limits (_type_ joint-exploder-list) none) ;; 29
)
Expand All @@ -42395,7 +42414,7 @@
)
)

(define-extern joint-exploder-joint-callback (function draw-control cspace-array joint-control none))
(define-extern joint-exploder-joint-callback "Postbind callback to set bones to the result of the joint-exploder." (function draw-control cspace-array joint-control none))
(define-extern joint-exploder-init-by-other (function skeleton-group int joint-exploder-tuning joint-exploder-static-params object :behavior joint-exploder))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand Down
Loading

0 comments on commit 2a4d3d7

Please sign in to comment.