From f123bf368a86610e3b49e62059207fef16c4da25 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sat, 6 Mar 2021 07:46:26 -0800 Subject: [PATCH] decomp - vector.gc - Decompile vector.gc (#289) * temp: commit what i have so far decomp: Fix nonempty_intersection impl for MSVC Debugging use-case docs: Add info on getting ASan builds running on Visual Studio w/o exceptions * decomp: initial rlet implementation * decomp: cleanup pass of vector-rewrite stage * decomp: Commit in-progress vector.gc, shortcomings are TODO commented * decomp: More cleanup, rename from being `vector` instr specific Fundamentally, this process can be used for re-writing ANY inline-asm instruction * decomp: Support 4th arg ACC instructions * decomp: Final pass of vector.gc before implementing last instructions * decomp: Better warnings when hitting unimplemented instructs * compiler: Implement inverse-sqrt and mov.vf * decomp: Final manual pass over vector.gc, documented gaps * decomp: Finish decompiling what currently is possible in vector.gc * decomp: Fix Variable -> RegisterAccess conflict * decomp: codacy lint * Address review feedback * Address feedback part 2 * Resolve build failures --- Taskfile.yml | 8 +- decompiler/CMakeLists.txt | 5 +- decompiler/Disasm/Instruction.cpp | 66 +- decompiler/Disasm/Instruction.h | 4 + decompiler/Disasm/InstructionDecode.cpp | 2 +- decompiler/IR2/AtomicOp.cpp | 34 +- decompiler/IR2/AtomicOp.h | 3 +- decompiler/IR2/Form.cpp | 121 ++ decompiler/IR2/Form.h | 35 + decompiler/IR2/OpenGoalMapping.cpp | 214 ++++ decompiler/IR2/OpenGoalMapping.h | 52 + decompiler/ObjectFile/ObjectFileDB.h | 1 + decompiler/ObjectFile/ObjectFileDB_IR2.cpp | 26 +- decompiler/analysis/inline_asm_rewrite.cpp | 96 ++ decompiler/analysis/inline_asm_rewrite.h | 12 + .../jak1_ntsc_black_label/label_types.jsonc | 507 ++++---- doc/code_status.md | 27 + doc/goal_doc.md | 14 +- goal_src/engine/math/vector-h.gc | 3 +- goal_src/engine/math/vector.gc | 1100 ++++++++++++++++- goalc/compiler/Compiler.h | 2 + goalc/compiler/compilation/Asm.cpp | 138 ++- goalc/compiler/compilation/Atoms.cpp | 2 + .../with_game/test-vector-outer-product.gc | 2 +- 24 files changed, 2151 insertions(+), 323 deletions(-) create mode 100644 decompiler/IR2/OpenGoalMapping.cpp create mode 100644 decompiler/IR2/OpenGoalMapping.h create mode 100644 decompiler/analysis/inline_asm_rewrite.cpp create mode 100644 decompiler/analysis/inline_asm_rewrite.h diff --git a/Taskfile.yml b/Taskfile.yml index ed470503d0..8ce4505b6e 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -3,4 +3,10 @@ version: '3' tasks: format: cmds: - - python ./third-party/run-clang-format/run-clang-format.py -r common decompiler game goalc test -i \ No newline at end of file + - python ./third-party/run-clang-format/run-clang-format.py -r common decompiler game goalc test -i + run-game: + cmds: + - ./out/build/Debug/bin/gk.exe -fakeiso -debug + repl: + cmds: + - ./out/build/Debug/bin/goalc.exe \ No newline at end of file diff --git a/decompiler/CMakeLists.txt b/decompiler/CMakeLists.txt index c30f88629c..3a7f918906 100644 --- a/decompiler/CMakeLists.txt +++ b/decompiler/CMakeLists.txt @@ -51,7 +51,7 @@ add_library( util/TP_Type.cpp config.cpp -) + "analysis/inline_asm_rewrite.cpp" "analysis/inline_asm_rewrite.h" "IR2/OpenGoalMapping.cpp") target_link_libraries(decomp lzokay @@ -60,8 +60,7 @@ target_link_libraries(decomp ) add_executable(decompiler - main.cpp - ) + main.cpp) target_link_libraries(decompiler decomp diff --git a/decompiler/Disasm/Instruction.cpp b/decompiler/Disasm/Instruction.cpp index b78c738016..4c27bd593a 100644 --- a/decompiler/Disasm/Instruction.cpp +++ b/decompiler/Disasm/Instruction.cpp @@ -106,6 +106,14 @@ int32_t InstructionAtom::get_imm() const { return imm; } +/*! + * Get the VF_FIELD as an integer immediate, or error if not applicable. + */ +int32_t InstructionAtom::get_vf_field() const { + assert(kind == VF_FIELD); + return imm; +} + /*! * Get as label index, or error if not a label. */ @@ -148,6 +156,33 @@ bool InstructionAtom::operator==(const InstructionAtom& other) const { } } +char Instruction::cop2_bc_to_char() const { + switch (cop2_bc) { + case 0: + return 'x'; + case 1: + return 'y'; + case 2: + return 'z'; + case 3: + return 'w'; + default: + return '?'; + } +} +std::string Instruction::cop2_dest_to_char() const { + std::string dest = "."; + if (cop2_dest & 8) + dest.push_back('x'); + if (cop2_dest & 4) + dest.push_back('y'); + if (cop2_dest & 2) + dest.push_back('z'); + if (cop2_dest & 1) + dest.push_back('w'); + return dest; +} + /*! * Convert just the name of the opcode to a string, omitting src/dst, but including * suffixes (interlock, broadcasts and destination) @@ -165,37 +200,14 @@ std::string Instruction::op_name_to_string() const { // optional "broadcast" specification for COP2 opcodes. if (cop2_bc != 0xff) { - switch (cop2_bc) { - case 0: - result.push_back('x'); - break; - case 1: - result.push_back('y'); - break; - case 2: - result.push_back('z'); - break; - case 3: - result.push_back('w'); - break; - default: - result.push_back('?'); - break; - } + result.push_back(cop2_bc_to_char()); } // optional "destination" specification for COP2 opcodes. if (cop2_dest != 0xff) { - result += "."; - if (cop2_dest & 8) - result.push_back('x'); - if (cop2_dest & 4) - result.push_back('y'); - if (cop2_dest & 2) - result.push_back('z'); - if (cop2_dest & 1) - result.push_back('w'); + result.append(cop2_dest_to_char()); } + return result; } @@ -373,4 +385,4 @@ bool Instruction::operator==(const Instruction& other) const { return true; } -} // namespace decompiler \ No newline at end of file +} // namespace decompiler diff --git a/decompiler/Disasm/Instruction.h b/decompiler/Disasm/Instruction.h index 419020286b..b38be3d7fe 100644 --- a/decompiler/Disasm/Instruction.h +++ b/decompiler/Disasm/Instruction.h @@ -42,6 +42,7 @@ struct InstructionAtom { Register get_reg() const; int32_t get_imm() const; + int32_t get_vf_field() const; int get_label() const; std::string get_sym() const; @@ -104,6 +105,9 @@ class Instruction { uint8_t cop2_dest = 0xff; // 0xff indicates "don't print dest" uint8_t cop2_bc = 0xff; // 0xff indicates "don't print bc" uint8_t il = 0xff; // 0xff indicates "don't print il" + + char cop2_bc_to_char() const; + std::string cop2_dest_to_char() const; }; } // namespace decompiler #endif // NEXT_INSTRUCTION_H diff --git a/decompiler/Disasm/InstructionDecode.cpp b/decompiler/Disasm/InstructionDecode.cpp index 552dc5f7a7..87f7f12a94 100644 --- a/decompiler/Disasm/InstructionDecode.cpp +++ b/decompiler/Disasm/InstructionDecode.cpp @@ -1172,4 +1172,4 @@ Instruction decode_instruction(LinkedWord& word, LinkedObjectFile& file, int seg return i; } -} // namespace decompiler \ No newline at end of file +} // namespace decompiler diff --git a/decompiler/IR2/AtomicOp.cpp b/decompiler/IR2/AtomicOp.cpp index 462c10f6b4..0483f838de 100644 --- a/decompiler/IR2/AtomicOp.cpp +++ b/decompiler/IR2/AtomicOp.cpp @@ -6,6 +6,7 @@ #include "common/goos/PrettyPrinter.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" #include "AtomicOp.h" +#include "OpenGoalMapping.h" namespace decompiler { ///////////////////////////// @@ -475,6 +476,37 @@ goos::Object AsmOp::to_form(const std::vector& labels, const En return pretty_print::build_list(forms); } +goos::Object AsmOp::to_open_goal_form(const std::vector& labels, + const Env& env) const { + std::vector forms; + + std::vector> src; + for (int i = 0; i < m_instr.n_src; i++) { + auto v = m_src[i]; + src.push_back(v); + } + + OpenGOALAsm goalOp = OpenGOALAsm(m_instr, m_dst, src); + forms.push_back(pretty_print::to_symbol(goalOp.full_function_name())); + + assert(m_instr.n_dst <= 1); + if (m_instr.n_dst == 1) { + if (m_dst.has_value()) { + // then print it as a variable + forms.push_back(m_dst.value().to_form(env)); + } else { + // print the atom + forms.push_back(pretty_print::to_symbol(m_instr.get_dst(0).to_string(labels))); + } + } + + assert(m_instr.n_src <= 4); + std::vector args = goalOp.get_args(labels, env); + forms.insert(forms.end(), args.begin(), args.end()); + + return pretty_print::build_list(forms); +} // namespace decompiler + bool AsmOp::operator==(const AtomicOp& other) const { if (typeid(AsmOp) != typeid(other)) { return false; @@ -1553,4 +1585,4 @@ void FunctionEndOp::collect_vars(RegAccessSet& vars) const { vars.insert(m_return_reg); } } -} // namespace decompiler \ No newline at end of file +} // namespace decompiler diff --git a/decompiler/IR2/AtomicOp.h b/decompiler/IR2/AtomicOp.h index 665d0aa9d7..4f029bce88 100644 --- a/decompiler/IR2/AtomicOp.h +++ b/decompiler/IR2/AtomicOp.h @@ -297,6 +297,7 @@ class AsmOp : public AtomicOp { public: AsmOp(Instruction instr, int my_idx); goos::Object to_form(const std::vector& labels, const Env& env) const override; + goos::Object to_open_goal_form(const std::vector& labels, const Env& env) const; bool operator==(const AtomicOp& other) const override; bool is_sequence_point() const override; RegisterAccess get_set_destination() const override; @@ -706,4 +707,4 @@ class FunctionEndOp : public AtomicOp { }; bool get_as_reg_offset(const SimpleExpression& expr, IR2_RegOffset* out); -} // namespace decompiler \ No newline at end of file +} // namespace decompiler diff --git a/decompiler/IR2/Form.cpp b/decompiler/IR2/Form.cpp index 7a50200adc..bb0afbfc0d 100644 --- a/decompiler/IR2/Form.cpp +++ b/decompiler/IR2/Form.cpp @@ -4,6 +4,7 @@ #include "decompiler/ObjectFile/LinkedObjectFile.h" #include "common/goos/PrettyPrinter.h" #include "common/type_system/TypeSystem.h" +#include namespace decompiler { @@ -513,6 +514,59 @@ void AsmOpElement::get_modified_regs(RegSet& regs) const { } } +///////////////////////////// +// OpenGoalAsmOpElement +///////////////////////////// + +OpenGoalAsmOpElement::OpenGoalAsmOpElement(const AsmOp* op) : m_op(op) {} + +goos::Object OpenGoalAsmOpElement::to_form_internal(const Env& env) const { + return m_op->to_open_goal_form(env.file->labels, env); +} + +void OpenGoalAsmOpElement::apply(const std::function& f) { + f(this); +} + +void OpenGoalAsmOpElement::apply_form(const std::function&) {} + +void OpenGoalAsmOpElement::collect_vars(RegAccessSet& vars, bool) const { + m_op->collect_vars(vars); +} + +void OpenGoalAsmOpElement::collect_vf_regs(RegSet& regs) const { + for (auto r : m_op->read_regs()) { + if (r.get_kind() == Reg::RegisterKind::VF || + r.get_kind() == Reg::RegisterKind::COP2_MACRO_SPECIAL) { + regs.insert(r); + } + } + + for (auto r : m_op->write_regs()) { + if (r.get_kind() == Reg::RegisterKind::VF || + r.get_kind() == Reg::RegisterKind::COP2_MACRO_SPECIAL) { + regs.insert(r); + } + } + + for (auto r : m_op->clobber_regs()) { + if (r.get_kind() == Reg::RegisterKind::VF || + r.get_kind() == Reg::RegisterKind::COP2_MACRO_SPECIAL) { + regs.insert(r); + } + } +} + +void OpenGoalAsmOpElement::get_modified_regs(RegSet& regs) const { + for (auto r : m_op->write_regs()) { + regs.insert(r); + } + + for (auto r : m_op->clobber_regs()) { + regs.insert(r); + } +} + ///////////////////////////// // ConditionElement ///////////////////////////// @@ -817,6 +871,73 @@ void EmptyElement::apply_form(const std::function&) {} void EmptyElement::collect_vars(RegAccessSet&, bool) const {} void EmptyElement::get_modified_regs(RegSet&) const {} +///////////////////////////// +// RLetElement +///////////////////////////// + +bool cmp(Register x, Register y) { + int comparison = x.to_string().compare(y.to_string()); + if (comparison <= 0) + return true; + return false; +} + +RLetElement::RLetElement(Form* _body, RegSet _regs) : body(_body) { + for (auto& reg : _regs) { + sorted_regs.push_back(reg); + } + std::sort(sorted_regs.begin(), sorted_regs.end(), cmp); +} + +void RLetElement::apply(const std::function& f) { + f(this); + body->apply(f); +} + +goos::Object RLetElement::to_form_internal(const Env& env) const { + std::vector regs; + for (auto& reg : sorted_regs) { + if (reg.get_kind() == Reg::RegisterKind::VF || + reg.get_kind() == Reg::RegisterKind::COP2_MACRO_SPECIAL) { + std::string reg_name = reg.to_string() == "ACC" ? "acc" : reg.to_string(); + regs.push_back( + pretty_print::build_list(pretty_print::to_symbol(fmt::format("{} :class vf", reg_name)))); + } + } + + std::vector rletForm; + rletForm.push_back(pretty_print::to_symbol("rlet")); + rletForm.push_back(pretty_print::build_list(regs)); + + // NOTE - initialize any relevant registers in the body first + for (auto& reg : sorted_regs) { + if (reg.get_kind() == Reg::RegisterKind::VF && reg.to_string() == "vf0") { + // TODO - a good idea to move this to a macro like initialize-constant-vector! or something. + // There could be some clever way to do this initialization that's faster that a normal static + // load. + rletForm.push_back( + pretty_print::to_symbol("(.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0))")); + } + } + + body->inline_forms(rletForm, env); + return pretty_print::build_list(rletForm); +} + +void RLetElement::apply_form(const std::function& f) { + body->apply_form(f); +} + +void RLetElement::collect_vars(RegAccessSet& vars, bool recursive) const { + if (recursive) { + body->collect_vars(vars, recursive); + } +} + +void RLetElement::get_modified_regs(RegSet& regs) const { + body->get_modified_regs(regs); +} + ///////////////////////////// // WhileElement ///////////////////////////// diff --git a/decompiler/IR2/Form.h b/decompiler/IR2/Form.h index 8ac544f285..428129abdc 100644 --- a/decompiler/IR2/Form.h +++ b/decompiler/IR2/Form.h @@ -383,6 +383,24 @@ class AsmOpElement : public FormElement { const AsmOp* m_op; }; +/*! + * A wrapper around a single AsmOp, with OpenGOAL considerations + */ +class OpenGoalAsmOpElement : public FormElement { + public: + explicit OpenGoalAsmOpElement(const AsmOp* op); + goos::Object to_form_internal(const Env& env) const override; + void apply(const std::function& f) override; + void apply_form(const std::function& f) override; + void collect_vars(RegAccessSet& vars, bool recursive) const override; + void collect_vf_regs(RegSet& regs) const; + void get_modified_regs(RegSet& regs) const override; + const AsmOp* op() const { return m_op; } + + private: + const AsmOp* m_op; +}; + /*! * A "condition" like (< a b). This can be used as a boolean value directly: (set! a (< b c)) * or it can be used as a branch condition: (if (< a b)). As a result, it implements both push @@ -576,6 +594,23 @@ class EmptyElement : public FormElement { void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; }; +/*! + * Represents an OpenGOAL 'rlet' expressions + */ +class RLetElement : public FormElement { + public: + enum class RegClass { VF }; + + explicit RLetElement(Form* _body, RegSet _regs); + goos::Object to_form_internal(const Env& env) const override; + void apply(const std::function& f) override; + void apply_form(const std::function& f) override; + void collect_vars(RegAccessSet& vars, bool recursive) const override; + void get_modified_regs(RegSet& regs) const override; + Form* body = nullptr; + std::vector sorted_regs; +}; + /*! * Represents a GOAL while loop and more complicated loops which have the "while" format of checking * the condition before the first loop. This will not include infinite while loops. diff --git a/decompiler/IR2/OpenGoalMapping.cpp b/decompiler/IR2/OpenGoalMapping.cpp new file mode 100644 index 0000000000..50f9d1793c --- /dev/null +++ b/decompiler/IR2/OpenGoalMapping.cpp @@ -0,0 +1,214 @@ +#include "OpenGoalMapping.h" +#include "common/goos/PrettyPrinter.h" +#include + +namespace decompiler { + +typedef OpenGOALAsm::InstructionModifiers MOD; + +const std::map MIPS_ASM_TO_OPEN_GOAL_FUNCS = { + // ----- EE ------- + // TODO - these are waiting on proper 128-bit int support in OpenGOAL + {InstructionKind::PSLLW, {"TODO.PSLLW", {}}}, + {InstructionKind::PSRAW, {"TODO.PSRAW", {}}}, + {InstructionKind::PSUBW, {"TODO.PSUBW", {}}}, + + {InstructionKind::LQ, {"TODO.LQ", {MOD::OFFSET}}}, + {InstructionKind::SQ, {"TODO.SQ", {MOD::OFFSET}}}, + + // NOTE - depending on how this is used, this may case issues! Be Warned! + // lots of implicit logic in OpenGOAL depending on argument types! + {InstructionKind::MFC1, {".mov", {}}}, + + // ---- COP2 ----- + // TODO - VMOVE supports dest, but OpenGOAL does NOT yet! + {InstructionKind::VMOVE, {".mov.vf", {MOD::DEST_MASK}}}, + + // Load and Store + {InstructionKind::LQC2, {".lvf", {MOD::OFFSET}}}, + {InstructionKind::QMFC2, {".mov", {}}}, + {InstructionKind::SQC2, {".svf", {MOD::OFFSET, MOD::SWAP_FIRST_TWO_SOURCE_ARGS}}}, + {InstructionKind::QMTC2, {".mov", {}}}, + + // Redundant ops, NOP and WAIT + {InstructionKind::VNOP, {".nop.vf", {}}}, + {InstructionKind::VWAITQ, {".wait.vf", {}}}, + + // Max / Min + {InstructionKind::VMAX, {".max.vf", {MOD::DEST_MASK}}}, + {InstructionKind::VMAX_BC, {".max.{}.vf", {MOD::BROADCAST, MOD::DEST_MASK}}}, + {InstructionKind::VMINI, {".min.vf", {MOD::DEST_MASK}}}, + {InstructionKind::VMINI_BC, {".min.{}.vf", {MOD::BROADCAST, MOD::DEST_MASK}}}, + + // Addition / Addition with ACC + {InstructionKind::VADD, {".add.vf", {MOD::DEST_MASK}}}, + {InstructionKind::VADD_BC, {".add.{}.vf", {MOD::BROADCAST, MOD::DEST_MASK}}}, + {InstructionKind::VADDA, {".add.vf", {MOD::DEST_MASK}}}, + {InstructionKind::VADDA_BC, {".add.{}.vf", {MOD::BROADCAST, MOD::DEST_MASK}}}, + + // Subtraction + {InstructionKind::VSUB, {".sub.vf", {MOD::DEST_MASK}}}, + {InstructionKind::VSUB_BC, {".sub.{}.vf", {MOD::BROADCAST, MOD::DEST_MASK}}}, + + // Multiplication / Multiplication with ACC + {InstructionKind::VMUL, {".mul.vf", {MOD::DEST_MASK}}}, + {InstructionKind::VMUL_BC, {".mul.{}.vf", {MOD::BROADCAST, MOD::DEST_MASK}}}, + {InstructionKind::VMULA, {".mul.vf", {MOD::DEST_MASK}}}, + {InstructionKind::VMULA_BC, {".mul.{}.vf", {MOD::BROADCAST, MOD::DEST_MASK}}}, + + // Add or Subtract with the resulting product / use the ACC + {InstructionKind::VMADD, {".add.mul.vf", {MOD::DEST_MASK, MOD::ACC_THIRD_SRC_ARG}}}, + {InstructionKind::VMADD_BC, + {".add.mul.{}.vf", {MOD::BROADCAST, MOD::DEST_MASK, MOD::ACC_THIRD_SRC_ARG}}}, + {InstructionKind::VMADDA, {".add.mul.vf", {MOD::DEST_MASK, MOD::ACC_THIRD_SRC_ARG}}}, + {InstructionKind::VMADDA_BC, + {".add.mul.{}.vf", {MOD::BROADCAST, MOD::DEST_MASK, MOD::ACC_THIRD_SRC_ARG}}}, + {InstructionKind::VMSUB, {".sub.mul.vf", {MOD::DEST_MASK, MOD::ACC_THIRD_SRC_ARG}}}, + {InstructionKind::VMSUB_BC, + {".sub.mul.{}.vf", {MOD::BROADCAST, MOD::DEST_MASK, MOD::ACC_THIRD_SRC_ARG}}}, + {InstructionKind::VMSUBA_BC, + {".sub.mul.{}.vf", {MOD::BROADCAST, MOD::DEST_MASK, MOD::ACC_THIRD_SRC_ARG}}}, + {InstructionKind::VMSUBQ, {".sub.mul.vf", {MOD::DEST_MASK, MOD::ACC_THIRD_SRC_ARG}}}, + + // Absolute value + {InstructionKind::VABS, {".abs.vf", {MOD::DEST_MASK}}}, + + // Outer-product + // NOTE - currently it's assumed these groups of instructions will be replaced with 1 + {InstructionKind::VOPMULA, {"TODO.VOPMULA.vf", {}}}, + {InstructionKind::VOPMSUB, {".outer.product.vf", {MOD::SWAP_FIRST_TWO_SOURCE_ARGS}}}, + + // Division + {InstructionKind::VDIV, {".div.vf", {MOD::FTF, MOD::FSF}}}, + + // Square-root + {InstructionKind::VSQRT, {".sqrt.vf", {MOD::FTF}}}, + {InstructionKind::VRSQRT, {".isqrt.vf", {MOD::FTF, MOD::FSF}}}, + + // Operations using the result of division + {InstructionKind::VADDQ, {".add.vf", {MOD::DEST_MASK}}}, + {InstructionKind::VSUBQ, {".sub.vf", {MOD::DEST_MASK}}}, + {InstructionKind::VMULQ, {".mul.vf", {MOD::DEST_MASK}}}, + {InstructionKind::VMULAQ, {".mul.vf", {MOD::DEST_MASK}}}, + + //// Random number generation + {InstructionKind::VRGET, {"TODO.VRGET", {}}}, + {InstructionKind::VRXOR, {"TODO.VRXOR", {}}}, + {InstructionKind::VRNEXT, {"TODO.VRNEXT", {}}}, + + //// VU Integer operations + {InstructionKind::VMTIR, {"TODO.VMTIR", {}}}, + {InstructionKind::VIAND, {"TODO.VIAND", {}}}, + {InstructionKind::VIADDI, {"TODO.VIADDI", {}}}, + + //// Load/store from VU memory + {InstructionKind::VLQI, {"TODO.VLQI", {}}}, + {InstructionKind::VSQI, {"TODO.VSQI", {}}}, + + //// Fixed point conversions + {InstructionKind::VFTOI0, {".ftoi.vf", {MOD::DEST_MASK}}}, + {InstructionKind::VITOF0, {".itof.vf", {MOD::DEST_MASK}}}, + + {InstructionKind::VFTOI4, {"TODO.VFTOI4", {}}}, + + {InstructionKind::VITOF12, {"TODO.VITOF12", {}}}, + {InstructionKind::VFTOI12, {"TODO.VFTOI12", {}}}, + + {InstructionKind::VITOF15, {"TODO.VITOF15", {}}}, + + //// Status Checks + {InstructionKind::VCLIP, {"TODO.VCLIP", {}}}, +}; + +bool OpenGOALAsm::Function::allows_modifier(InstructionModifiers mod) { + return std::find(modifiers.begin(), modifiers.end(), mod) != modifiers.end(); +} + +OpenGOALAsm::OpenGOALAsm(Instruction _instr) : instr(_instr) { + if (MIPS_ASM_TO_OPEN_GOAL_FUNCS.count(instr.kind) == 0) { + valid = false; + } else { + func = MIPS_ASM_TO_OPEN_GOAL_FUNCS.at(instr.kind); + if (func.funcTemplate.rfind("TODO", 0) == 0) { + todo = true; + } + } +} + +OpenGOALAsm::OpenGOALAsm(Instruction _instr, + std::optional _dst, + const std::vector>& _src) + : instr(_instr), m_dst(_dst), m_src(_src) { + if (MIPS_ASM_TO_OPEN_GOAL_FUNCS.count(instr.kind) == 0) { + valid = false; + } else { + func = MIPS_ASM_TO_OPEN_GOAL_FUNCS.at(instr.kind); + if (func.funcTemplate.rfind("TODO", 0) == 0) { + todo = true; + } + } +} + +std::string OpenGOALAsm::full_function_name() { + std::string func_name = func.funcTemplate; + // OpenGOAL uses the function name for broadcast specification + if (func.allows_modifier(MOD::BROADCAST)) { + if (instr.cop2_bc != 0xff) { + std::string bc = std::string(1, instr.cop2_bc_to_char()); + func_name = fmt::format(func_name, bc); + } + } + return func_name; +} + +std::vector OpenGOALAsm::get_args(const std::vector& labels, + const Env& env) { + std::vector args; + std::vector named_args; + + for (int i = 0; i < instr.n_src; i++) { + auto v = m_src.at(i); + InstructionAtom atom = instr.get_src(i); + + if (v.has_value()) { + // Normal register / constant args + args.push_back(v.value().to_form(env)); + } else if (atom.kind == InstructionAtom::AtomKind::VF_FIELD) { + // Handle FTF/FSF operations + if (func.allows_modifier(MOD::FTF) && named_args.size() == 0) { + named_args.push_back( + pretty_print::to_symbol(fmt::format(":ftf #b{:b}", atom.get_vf_field()))); + } else if (func.allows_modifier(MOD::FSF)) { + named_args.push_back( + pretty_print::to_symbol(fmt::format(":fsf #b{:b}", atom.get_vf_field()))); + } + } else if (func.allows_modifier(MOD::OFFSET) && atom.kind == InstructionAtom::AtomKind::IMM) { + // Handle offsetting + if (atom.get_imm() != 0) { + named_args.push_back(pretty_print::to_symbol(fmt::format(":offset {}", atom.get_imm()))); + } + } else { + args.push_back(pretty_print::to_symbol(atom.to_string(labels))); + } + } + + // Handle third-argument accumulator + if (func.allows_modifier(MOD::ACC_THIRD_SRC_ARG)) { + args.push_back(pretty_print::to_symbol("acc")); + } + + // Handle destination masks + if (func.allows_modifier(MOD::DEST_MASK) && instr.cop2_dest != 0xff && instr.cop2_dest != 15) { + named_args.push_back(pretty_print::to_symbol(fmt::format(":mask #b{:b}", instr.cop2_dest))); + } + + // Some functions are configured, or its easiest to swap the source args + // NOTE - this currently assumes it is the first two args that must be swapped + if (func.allows_modifier(MOD::SWAP_FIRST_TWO_SOURCE_ARGS)) { + std::swap(args.at(0), args.at(1)); + } + + args.insert(args.end(), named_args.begin(), named_args.end()); + return args; +} +} // namespace decompiler diff --git a/decompiler/IR2/OpenGoalMapping.h b/decompiler/IR2/OpenGoalMapping.h new file mode 100644 index 0000000000..d029143f53 --- /dev/null +++ b/decompiler/IR2/OpenGoalMapping.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "common/goos/Object.h" +#include "decompiler/Disasm/Register.h" +#include "decompiler/Disasm/Instruction.h" +#include "decompiler/IR2/IR2_common.h" +#include "Env.h" +#include "AtomicOp.h" + +namespace decompiler { + +struct OpenGOALAsm { + enum class InstructionModifiers { + BROADCAST, + DEST_MASK, + FTF, + FSF, + OFFSET, + SWAP_FIRST_TWO_SOURCE_ARGS, + ACC_THIRD_SRC_ARG + }; + + struct Function { + std::string funcTemplate = ""; + std::vector modifiers = {}; + + bool allows_modifier(InstructionModifiers); + }; + + OpenGOALAsm(Instruction _instr); + + OpenGOALAsm(Instruction _instr, + std::optional _dst, + const std::vector>& _src); + + bool valid = true; + bool todo = false; + std::optional m_dst; + std::vector> m_src; + Instruction instr; + OpenGOALAsm::Function func; + + std::string full_function_name(); + std::vector get_args(const std::vector& labels, const Env& env); +}; +extern const std::map MIPS_ASM_TO_OPEN_GOAL_FUNCS; +} // namespace decompiler diff --git a/decompiler/ObjectFile/ObjectFileDB.h b/decompiler/ObjectFile/ObjectFileDB.h index 220550da30..0c9c34a629 100644 --- a/decompiler/ObjectFile/ObjectFileDB.h +++ b/decompiler/ObjectFile/ObjectFileDB.h @@ -76,6 +76,7 @@ class ObjectFileDB { void ir2_store_current_forms(); void ir2_build_expressions(); void ir2_insert_lets(); + void ir2_rewrite_inline_asm_instructions(); void ir2_write_results(const std::string& output_dir); std::string ir2_to_file(ObjectFileData& data); std::string ir2_function_to_string(ObjectFileData& data, Function& function, int seg); diff --git a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp index 633320de87..5184803d20 100644 --- a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp +++ b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp @@ -15,6 +15,7 @@ #include "decompiler/analysis/cfg_builder.h" #include "decompiler/analysis/final_output.h" #include "decompiler/analysis/expression_build.h" +#include "decompiler/analysis/inline_asm_rewrite.h" #include "common/goos/PrettyPrinter.h" #include "decompiler/IR2/Form.h" @@ -46,7 +47,8 @@ void ObjectFileDB::analyze_functions_ir2(const std::string& output_dir) { ir2_store_current_forms(); lg::info("Expression building..."); ir2_build_expressions(); - + lg::info("Re-writing inline asm instructions..."); + ir2_rewrite_inline_asm_instructions(); if (get_config().insert_lets) { lg::info("Inserting lets..."); ir2_insert_lets(); @@ -444,6 +446,28 @@ void ObjectFileDB::ir2_insert_lets() { combined_stats.vars_in_lets, combined_stats.total_vars, timer.getMs()); } +void ObjectFileDB::ir2_rewrite_inline_asm_instructions() { + Timer timer; + int total = 0; + int attempted = 0; + int successful = 0; + for_each_function_def_order([&](Function& func, int segment_id, ObjectFileData& data) { + (void)segment_id; + (void)data; + total++; + if (func.ir2.top_form && func.ir2.env.has_type_analysis()) { + attempted++; + if (rewrite_inline_asm_instructions(func.ir2.top_form, *func.ir2.form_pool, func, dts)) { + successful++; + func.ir2.print_debug_forms = true; + } + } + }); + + lg::info("{}/{}/{} rewrote inline-asm instructions in {:.2f} ms\n", successful, attempted, total, + timer.getMs()); +} + void ObjectFileDB::ir2_write_results(const std::string& output_dir) { Timer timer; lg::info("Writing IR2 results to file..."); diff --git a/decompiler/analysis/inline_asm_rewrite.cpp b/decompiler/analysis/inline_asm_rewrite.cpp new file mode 100644 index 0000000000..2a82674f0e --- /dev/null +++ b/decompiler/analysis/inline_asm_rewrite.cpp @@ -0,0 +1,96 @@ +#include "inline_asm_rewrite.h" + +#include "decompiler/Function/Function.h" +#include "decompiler/IR2/Form.h" +#include "decompiler/IR2/FormStack.h" +#include "decompiler/util/DecompilerTypeSystem.h" +#include "common/goos/PrettyPrinter.h" +#include "decompiler/IR2/OpenGoalMapping.h" +#include "decompiler/analysis/reg_usage.h" + +namespace decompiler { + +bool rewrite_inline_asm_instructions(Form* top_level_form, + FormPool& pool, + Function& f, + const DecompilerTypeSystem& dts) { + assert(top_level_form); + + try { + RegSet vf_regs; + // Iterate through all TLFs + top_level_form->apply_form([&](Form* form) { + std::vector new_entries; + for (auto& entry : form->elts()) { + // All vector instructions are inline assembly, so we only care to re-write assembly + // operations + AsmOpElement* elem = dynamic_cast(entry); + if (!elem) { + new_entries.push_back(entry); + continue; + } + + // We then convert the normal AsmOpElement to a more tailor-made FormElement that has + // OpenGOAL considerations Not _all_ assembly instructors are vector + OpenGOALAsm asmOp = OpenGOALAsm(elem->op()->instruction()); + if (!asmOp.valid) { + // If its an invalid or unsupported exception, skip it + /*lg::warn("[ASM Re-Write] - Unsupported inline assembly instruction kind - [{}]", + asmOp.instr.kind);*/ + f.warnings.general_warning(";; Unsupported inline assembly instruction kind - [{}]", + asmOp.instr.kind); + new_entries.push_back(entry); + continue; + } else if (elem->op()->instruction().kind == InstructionKind::VOPMULA) { + // So far, the only instruction we deal with in pairs is the outer-product + // This is kinda a hack, internally the src args of VOPMSUB will be swapped which is + // correct and the first op we skip. + // In the future if this needs to support more, it will be worth cleaning this up + continue; + } else if (asmOp.todo) { + // If its an invalid or unsupported exception, skip it + /*lg::warn("[ASM Re-Write] - Inline assembly instruction marked with TODO - [{}]", + asmOp.full_function_name());*/ + f.warnings.general_warning(";; Inline assembly instruction marked with TODO - [{}]", + asmOp.full_function_name()); + } + + // If we've made it this far, it's an AsmOperation that is also a supported vector + // instruction by OpenGOAL All we have to do is convert it to the correct `FormElement` that + // will write the form so it works for OpenGOAL + + OpenGoalAsmOpElement* newElem = pool.alloc_element(elem->op()); + newElem->collect_vf_regs(vf_regs); + + new_entries.push_back(newElem); + } + + assert(!new_entries.empty()); + form->clear(); + for (auto x : new_entries) { + form->push_back(x); + } + }); + + // If we have to wrap the entire function in an 'rlet' + if (!vf_regs.empty()) { + Form* body = pool.alloc_empty_form(); + for (auto& entry : top_level_form->elts()) { + body->push_back(entry); + } + + RLetElement* rlet = pool.alloc_element(body, vf_regs); + top_level_form->clear(); + top_level_form->push_back(rlet); + } + } catch (std::exception& e) { + std::string warning = fmt::format("ASM instruction re-writing failed in {}: {}", + f.guessed_name.to_string(), e.what()); + lg::warn(warning); + f.warnings.general_warning(";; {}", warning); + return false; + } + + return true; +} +} // namespace decompiler diff --git a/decompiler/analysis/inline_asm_rewrite.h b/decompiler/analysis/inline_asm_rewrite.h new file mode 100644 index 0000000000..ade77bff7c --- /dev/null +++ b/decompiler/analysis/inline_asm_rewrite.h @@ -0,0 +1,12 @@ +#pragma once + +namespace decompiler { +class Form; +class Function; +class FormPool; +class DecompilerTypeSystem; +bool rewrite_inline_asm_instructions(Form* top_level_form, + FormPool& pool, + Function& f, + const DecompilerTypeSystem& dts); +} // namespace decompiler diff --git a/decompiler/config/jak1_ntsc_black_label/label_types.jsonc b/decompiler/config/jak1_ntsc_black_label/label_types.jsonc index bef767c9b1..e779aed3aa 100644 --- a/decompiler/config/jak1_ntsc_black_label/label_types.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/label_types.jsonc @@ -1,287 +1,286 @@ { - "gcommon":[ - ["L345", "float", true], - ["L346", "uint64", true] - ], + "gcommon": [ + ["L345", "float", true], + ["L346", "uint64", true] + ], - "math":[ - ["L41", "float", true], - ["L34", "float", true], - ["L35", "float", true] - ], + "math": [ + ["L41", "float", true], + ["L34", "float", true], + ["L35", "float", true] + ], - "trigonometry":[ - ["L143", "float", true], - ["L144", "float", true], - ["L145", "float", true], - ["L137", "float", true], - ["L106", "float", true], - ["L134", "float", true], - ["L112", "float", true], - ["L114", "float", true], - ["L135", "float", true], - ["L121", "float", true], - ["L150", "float", true], - ["L147", "float", true], - ["L149", "float", true], - ["L107", "float", true], - ["L129", "float", true], - ["L152", "float", true], - ["L109", "float", true], - ["L138", "float", true], - ["L127", "float", true], - ["L128", "float", true], - ["L110", "float", true], - ["L136", "float", true] - ], + "trigonometry": [ + ["L143", "float", true], + ["L144", "float", true], + ["L145", "float", true], + ["L137", "float", true], + ["L106", "float", true], + ["L134", "float", true], + ["L112", "float", true], + ["L114", "float", true], + ["L135", "float", true], + ["L121", "float", true], + ["L150", "float", true], + ["L147", "float", true], + ["L149", "float", true], + ["L107", "float", true], + ["L129", "float", true], + ["L152", "float", true], + ["L109", "float", true], + ["L138", "float", true], + ["L127", "float", true], + ["L128", "float", true], + ["L110", "float", true], + ["L136", "float", true] + ], - "pad":[ - ["L44", "float", true], - ["L42", "float", true], - ["L43", "float", true], - ["L41", "float", true] - ], + "pad": [ + ["L44", "float", true], + ["L42", "float", true], + ["L43", "float", true], + ["L41", "float", true] + ], - "loader-h":[ - ["L10", "float", true] - ], + "loader-h": [["L10", "float", true]], - "dma-disasm":[ - ["L148", "(array vif-disasm-element)", true] - ], - "level-h":[ - ["L3", "level-group", true] - ], + "dma-disasm": [["L148", "(array vif-disasm-element)", true]], + "level-h": [["L3", "level-group", true]], - "level-info":[ - ["L964", "level-load-info", true], - ["L867", "level-load-info", true], - ["L851", "level-load-info", true], - ["L822", "level-load-info", true], - ["L812", "level-load-info", true], - ["L531", "level-load-info", true], - ["L512", "level-load-info", true], - ["L495", "level-load-info", true], - ["L479", "level-load-info", true], - ["L271", "level-load-info", true], - ["L255", "level-load-info", true], - ["L237", "level-load-info", true], - ["L215", "level-load-info", true], - ["L175", "level-load-info", true], - ["L153", "level-load-info", true], - ["L143", "level-load-info", true], - ["L131", "level-load-info", true], - ["L112", "level-load-info", true], - ["L72", "level-load-info", true], - ["L52", "level-load-info", true], - ["L48", "level-load-info", true], - ["L41", "level-load-info", true], - ["L35", "level-load-info", true], - ["L30", "level-load-info", true], - ["L28", "level-load-info", true], - ["L544", "level-load-info", true], - ["L2", "pair", true] + "level-info": [ + ["L964", "level-load-info", true], + ["L867", "level-load-info", true], + ["L851", "level-load-info", true], + ["L822", "level-load-info", true], + ["L812", "level-load-info", true], + ["L531", "level-load-info", true], + ["L512", "level-load-info", true], + ["L495", "level-load-info", true], + ["L479", "level-load-info", true], + ["L271", "level-load-info", true], + ["L255", "level-load-info", true], + ["L237", "level-load-info", true], + ["L215", "level-load-info", true], + ["L175", "level-load-info", true], + ["L153", "level-load-info", true], + ["L143", "level-load-info", true], + ["L131", "level-load-info", true], + ["L112", "level-load-info", true], + ["L72", "level-load-info", true], + ["L52", "level-load-info", true], + ["L48", "level-load-info", true], + ["L41", "level-load-info", true], + ["L35", "level-load-info", true], + ["L30", "level-load-info", true], + ["L28", "level-load-info", true], + ["L544", "level-load-info", true], + ["L2", "pair", true] + ], + "level-h": [["L3", "_auto_", true]], - ], + "math-camera": [ + ["L51", "float", true], + ["L52", "float", true], + ["L53", "float", true], + ["L55", "float", true], + ["L56", "float", true], + ["L38", "float", true], + ["L37", "float", true], + ["L54", "float", true], + ["L50", "float", true], + ["L49", "float", true], + ["L27", "float", true], + ["L35", "float", true], + ["L34", "float", true], + ["L28", "float", true], + ["L26", "float", true], + ["L34", "float", true], + ["L33", "float", true], + ["L30", "float", true], + ["L44", "float", true], + ["L45", "float", true], + ["L32", "float", true], + ["L31", "float", true], + ["L47", "float", true], + ["L48", "float", true], + ["L46", "float", true], + ["L36", "float", true], + ["L43", "float", true], + ["L42", "float", true], + ["L40", "float", true], + ["L41", "float", true], + ["L39", "float", true], + ["L59", "float", true] + ], - "level-h":[ - ["L3", "_auto_", true]], + "quaternion": [["L80", "float", true]], - "math-camera":[ - ["L51", "float", true], - ["L52", "float", true], - ["L53", "float", true], - ["L55", "float", true], - ["L56", "float", true], - ["L38", "float", true], - ["L37", "float", true], - ["L54", "float", true], - ["L50", "float", true], - ["L49", "float", true], - ["L27", "float", true], - ["L35", "float", true], - ["L34", "float", true], - ["L28", "float", true], - ["L26", "float", true], - ["L34", "float", true], - ["L33", "float", true], - ["L30", "float", true], - ["L44", "float", true], - ["L45", "float", true], - ["L32", "float", true], - ["L31", "float", true], - ["L47", "float", true], - ["L48", "float", true], - ["L46", "float", true], - ["L36", "float", true], - ["L43", "float", true], - ["L42", "float", true], - ["L40", "float", true], - ["L41", "float", true], - ["L39", "float", true], - ["L59", "float", true] + "font-h": [ + ["L18", "matrix", true], + ["L19", "float", true], + ["L17", "font-work", true] + ], - ], + "display": [ + ["L47", "float", true], + ["L42", "float", true], + ["L43", "float", true], + ["L44", "float", true], + ["L45", "float", true], + ["L48", "float", true], + ["L46", "float", true], + ["L50", "float", true], + ["L49", "float", true] + ], - "quaternion":[ - ["L80", "float", true] - ], + "text-h": [["L2", "_auto_", true]], - "font-h":[ - ["L18", "matrix", true], - ["L19", "float", true], - ["L17", "font-work", true] - ], + "font-h": [ + ["L18", "matrix", true], + ["L19", "float", true], + ["L17", "font-work", true] + ], - "display":[ - ["L47", "float", true], - ["L42", "float", true], - ["L43", "float", true], - ["L44", "float", true], - ["L45", "float", true], - ["L48", "float", true], - ["L46", "float", true], - ["L50", "float", true], - ["L49", "float", true] - ], + "display": [ + ["L47", "float", true], + ["L42", "float", true], + ["L43", "float", true], + ["L44", "float", true], + ["L45", "float", true], + ["L48", "float", true], + ["L46", "float", true], + ["L50", "float", true], + ["L49", "float", true] + ], - "text-h":[ - ["L2", "_auto_", true]], + "text-h": [["L2", "_auto_", true]], - "ocean-trans-tables":[ - ["L1", "(pointer float)", true, 16], - ["L2", "(pointer float)", true, 160], - ["L3", "(pointer float)", true, 100], - ["L4", "(pointer float)", true, 72], - ["L5", "(pointer float)", true, 72], - ["L6", "(pointer float)", true, 72], - ["L7", "(pointer float)", true, 72], - ["L8", "(pointer float)", true, 44], - ["L9", "(pointer float)", true, 44], - ["L10", "(pointer float)", true, 44], - ["L11", "(pointer float)", true, 44], - ["L12", "(pointer float)", true, 40], - ["L13", "(pointer float)", true, 40], - ["L14", "(pointer float)", true, 40], - ["L15", "(pointer float)", true, 40], - ["L16", "(pointer float)", true, 28], - ["L17", "(pointer float)", true, 28], - ["L18", "(pointer float)", true, 28], - ["L19", "(pointer float)", true, 28] - ], + "ocean-trans-tables": [ + ["L1", "(pointer float)", true, 16], + ["L2", "(pointer float)", true, 160], + ["L3", "(pointer float)", true, 100], + ["L4", "(pointer float)", true, 72], + ["L5", "(pointer float)", true, 72], + ["L6", "(pointer float)", true, 72], + ["L7", "(pointer float)", true, 72], + ["L8", "(pointer float)", true, 44], + ["L9", "(pointer float)", true, 44], + ["L10", "(pointer float)", true, 44], + ["L11", "(pointer float)", true, 44], + ["L12", "(pointer float)", true, 40], + ["L13", "(pointer float)", true, 40], + ["L14", "(pointer float)", true, 40], + ["L15", "(pointer float)", true, 40], + ["L16", "(pointer float)", true, 28], + ["L17", "(pointer float)", true, 28], + ["L18", "(pointer float)", true, 28], + ["L19", "(pointer float)", true, 28] + ], - "ocean-tables":[ - ["L26", "(inline-array sphere)", true, 36], - ["L25", "(pointer uint32)", true, 2548] - ], + "ocean-tables": [ + ["L26", "(inline-array sphere)", true, 36], + ["L25", "(pointer uint32)", true, 2548] + ], - "ocean-frames":[ - ["L1", "(pointer uint32)", true, 16384] - ], + "ocean-frames": [["L1", "(pointer uint32)", true, 16384]], - "sky-h":[ - ["L73", "float", true], - ["L71", "float", true], - ["L72", "float", true] - ], + "sky-h": [ + ["L73", "float", true], + ["L71", "float", true], + ["L72", "float", true] + ], - "mood-h":[ - ["L3", "float", true] - ], + "mood-h": [["L3", "float", true]], - "merc-h":[ - ["L4", "float", true], - ["L5", "float", true], - ["L6", "float", true], - ["L7", "float", true] - ], + "merc-h": [ + ["L4", "float", true], + ["L5", "float", true], + ["L6", "float", true], + ["L7", "float", true] + ], - "shadow-cpu-h":[ - ["L9", "float", true], - ["L10", "float", true] - ], + "shadow-cpu-h": [ + ["L9", "float", true], + ["L10", "float", true] + ], - "game-info-h":[ - ["L4", "game-bank", true], - ["L3", "game-info", true] - ], + "game-info-h": [ + ["L4", "game-bank", true], + ["L3", "game-info", true] + ], - "wind-h":[ - ["L3", "_auto_", true] - ], + "wind-h": [["L3", "_auto_", true]], - "dynamics-h":[ - ["L7", "float", true], - ["L6", "_auto_", true] - ], + "dynamics-h": [ + ["L7", "float", true], + ["L6", "_auto_", true] + ], - "surface-h":[ - ["L72", "float", true], - ["L71", "float", true], - ["L68", "_auto_", true], - ["L67", "_auto_", true], - ["L66", "_auto_", true], - ["L65", "_auto_", true], - ["L64", "_auto_", true], - ["L63", "_auto_", true], - ["L62", "_auto_", true], - ["L61", "_auto_", true], - ["L60", "_auto_", true], - ["L59", "_auto_", true], - ["L58", "_auto_", true], - ["L57", "_auto_", true], - ["L56", "_auto_", true], - ["L55", "_auto_", true], - ["L54", "_auto_", true], - ["L53", "_auto_", true], - ["L52", "_auto_", true], - ["L51", "_auto_", true], - ["L50", "_auto_", true], - ["L49", "_auto_", true], - ["L48", "_auto_", true], - ["L47", "_auto_", true], - ["L46", "_auto_", true], - ["L45", "_auto_", true], - ["L44", "_auto_", true], - ["L43", "_auto_", true], - ["L42", "_auto_", true], - ["L41", "_auto_", true], - ["L40", "_auto_", true], - ["L39", "_auto_", true], - ["L38", "_auto_", true], - ["L37", "_auto_", true], - ["L36", "_auto_", true], - ["L35", "_auto_", true], - ["L34", "_auto_", true], - ["L33", "_auto_", true], - ["L32", "_auto_", true] - ], + "surface-h": [ + ["L72", "float", true], + ["L71", "float", true], + ["L68", "_auto_", true], + ["L67", "_auto_", true], + ["L66", "_auto_", true], + ["L65", "_auto_", true], + ["L64", "_auto_", true], + ["L63", "_auto_", true], + ["L62", "_auto_", true], + ["L61", "_auto_", true], + ["L60", "_auto_", true], + ["L59", "_auto_", true], + ["L58", "_auto_", true], + ["L57", "_auto_", true], + ["L56", "_auto_", true], + ["L55", "_auto_", true], + ["L54", "_auto_", true], + ["L53", "_auto_", true], + ["L52", "_auto_", true], + ["L51", "_auto_", true], + ["L50", "_auto_", true], + ["L49", "_auto_", true], + ["L48", "_auto_", true], + ["L47", "_auto_", true], + ["L46", "_auto_", true], + ["L45", "_auto_", true], + ["L44", "_auto_", true], + ["L43", "_auto_", true], + ["L42", "_auto_", true], + ["L41", "_auto_", true], + ["L40", "_auto_", true], + ["L39", "_auto_", true], + ["L38", "_auto_", true], + ["L37", "_auto_", true], + ["L36", "_auto_", true], + ["L35", "_auto_", true], + ["L34", "_auto_", true], + ["L33", "_auto_", true], + ["L32", "_auto_", true] + ], - "pat-h":[ - ["L1", "(inline-array pat-mode-info)", true, 4] - ], + "pat-h": [["L1", "(inline-array pat-mode-info)", true, 4]], - "fact-h":[ - ["L35", "_auto_", true] - ], + "fact-h": [["L35", "_auto_", true]], - "smush-control-h":[ - ["L20", "float", true], - ["L18", "float", true], - ["L19", "float", true], - ["L17", "float", true] - ], + "smush-control-h": [ + ["L20", "float", true], + ["L18", "float", true], + ["L19", "float", true], + ["L17", "float", true] + ], - "collide-shape-h":[ - ["L27", "float", true], - ["L25", "_auto_", true], - ["L24", "_auto_", true] - ], + "collide-shape-h": [ + ["L27", "float", true], + ["L25", "_auto_", true], + ["L24", "_auto_", true] + ], - "collide-edge-grab-h":[ - ["L1", "collide-edge-work", true]] + "collide-edge-grab-h": [["L1", "collide-edge-work", true]], - - -} \ No newline at end of file + "vector": [ + ["L113", "float", true], + ["L112", "float", true], + ["L111", "float", true] + ] +} diff --git a/doc/code_status.md b/doc/code_status.md index a456f54895..44b912a012 100644 --- a/doc/code_status.md +++ b/doc/code_status.md @@ -127,6 +127,33 @@ ## `display-h`: **Done** ## `vector`: asm +- Largely decompiled successfully and compiles! + - Functions are currently undocumented and still with rough variable names +- Some functions are currently failing to decompile: + - `rand-vu-sphere-point!` + - `vector-deg-lerp-clamp!` + - `vector=` +- Some functions are currently skipped due to instructions not being supported. + - These instructions are: + - `sphere<-vector+r!` + - `sphere<-vector!` + - `vector-deg-diff` + - `vector-degmod` + - `vector-degf` + - `vector-degi` + - `vector4-lerp-clamp!` + - `vector-normalize-copy!` + - The affects functions are: + - `LQ` + - `SQ` + - `PSLLW` + - `PSUBW` + - `PSRAW` +- Some functions are currently skipped because of missing functionality: + - The method gpr->fpr of type int could not be found. + - `spheres-overlap?` + - `vector-normalize-ret-len!` + ## `fileio`: **Done** diff --git a/doc/goal_doc.md b/doc/goal_doc.md index b41bdfbf64..8efa0666f8 100644 --- a/doc/goal_doc.md +++ b/doc/goal_doc.md @@ -1390,11 +1390,11 @@ Inserts a `FWAIT` assembly instruction, x86 does not require as much synchroniza ## `.lvf` ```lisp -(.lvf dst-reg src-loc [:color #t|#f]) +(.lvf dst-reg src-loc [:color #t|#f] [:offset ]) ``` -Load a vector float register from `src-loc`. The `dst-reg` must be a vector float register. The `src-loc` can be a gpr containing a GOAL pointer or expression which gives a GOAL pointer. There is no type checking on the `src-loc` so be careful. The load uses `vmovaps`, so the source must be 16-byte aligned. +Load a vector float register from `src-loc`. The `dst-reg` must be a vector float register. The `src-loc` can be a gpr containing a GOAL pointer or expression which gives a GOAL pointer. There is no type checking on the `src-loc` so be careful. The load uses `vmovaps`, so the source must be 16-byte aligned. -If the source is in the form `base-reg + constant-offset`, like from a `(&-> my-object my-inline-vector-field)`, the constant offset will be folded into the load instruction like `vmovaps xmm1, [r15 + rax + 12]`. +If the source is in the form `base-reg + constant-offset`, like from a `(&-> my-object my-inline-vector-field)`, the constant offset will be folded into the load instruction like `vmovaps xmm1, [r15 + rax + 12]`. An explicit offset can be provided via the `:offset` keyword, and will be used if applicable. If the source is an immediate `(new 'static ...)` form that results in a statically allocated variable, it will use `RIP` relative addressing (32-bit immediate) form. This means that the code: ```lisp @@ -1404,7 +1404,7 @@ will be just a single instruction to do a `vmovaps xmm1, [rip + XXX]`. ## `.svf` ```lisp -(.svf dst-loc src-reg [:color #t|#f]) +(.svf dst-loc src-reg [:color #t|#f] [:offset ]) ``` Store a vector float. Works similarly to the `lvf` form, but there is no optimized case for storing into a static because this isn't allowed in GOAL. @@ -1479,12 +1479,16 @@ Calculates the outer-product of `src1` and `src2` and stores the result in `dst` Given 2 vectors `V1 = <1,2,3,4>` and `V2 = <5,6,7,8>` and assume `VDEST = <0, 0, 0, 999>` The outer product is computed like so (only x,y,z components are operated on): + `x = (V1y * V2z) - (V2y * V1z) => (2 * 7) - (6 * 3) => -4` + `y = (V1z * V2x) - (V2z * V1x) => (3 * 5) - (7 * 1) => 8` + `z = (V1x * V2y) - (V2x * V1y) => (1 * 6) - (5 * 2) => -4` + `w = N/A, left alone => 999` -`VDEST = <-4, 8, -4, 999>` +> `VDEST = <-4, 8, -4, 999>` ## `.blend.vf` ```lisp diff --git a/goal_src/engine/math/vector-h.gc b/goal_src/engine/math/vector-h.gc index ae3311862a..ed7139629d 100644 --- a/goal_src/engine/math/vector-h.gc +++ b/goal_src/engine/math/vector-h.gc @@ -493,8 +493,7 @@ (set! (-> ,v x) ,xv) (set! (-> ,v y) ,yv) (set! (-> ,v z) ,zv) - (set! (-> ,v w) ,wv)) - ) + (set! (-> ,v w) ,wv))) (defun vector-dot ((a vector) (b vector)) "Take the dot product of two vectors. diff --git a/goal_src/engine/math/vector.gc b/goal_src/engine/math/vector.gc index 68ff5d5a6b..28b3525ced 100644 --- a/goal_src/engine/math/vector.gc +++ b/goal_src/engine/math/vector.gc @@ -5,10 +5,1098 @@ ;; name in dgo: vector ;; dgos: GAME, ENGINE -(defun vector-identity! ((arg0 vector)) - (set! (-> arg0 data 0) 1.0) - (set! (-> arg0 data 1) 1.0) - (set! (-> arg0 data 2) 1.0) - (set! (-> arg0 data 3) 1.0) - arg0 +;; ---- TOP LEVEL COMMENT TODO ---- + +;; TODO - rand-vu-sphere-point! (fails to decomp) + +;; (defun sphere<-vector+r! ((arg0 sphere) (arg1 vector) (arg2 float)) +;; (local-vars (v1-0 int)) +;; (TODO.LQ v1-0 arg1) +;; (TODO.SQ v1-0 arg0) +;; (set! (-> arg0 data 3) arg2) +;; arg0 +;; ) + +;; (defun sphere<-vector! ((arg0 sphere) (arg1 vector)) +;; (local-vars (v1-0 int) (f0-0 float)) +;; (set! f0-0 (-> arg0 data 3)) +;; (TODO.LQ v1-0 arg1) +;; (TODO.SQ v1-0 arg0) +;; (set! (-> arg0 data 3) f0-0) +;; arg0 +;; ) + +;; (defun spheres-overlap? ((arg0 vector) (arg1 vector)) +;; "TODO" +;; (local-vars (v1-0 int) +;; (a0-1 int)) +;; (rlet ((vf0 :class vf) +;; (vf1 :class vf) +;; (vf2 :class vf) +;; (vf3 :class vf) +;; (vf4 :class vf)) +;; (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) +;; (.lvf vf1 arg0) +;; (.lvf vf2 arg1) +;; (.sub.vf vf3 vf1 vf2 :mask #b1110) +;; (.mul.vf vf3 vf3 vf3 :mask #b1110) +;; (.add.w.vf vf4 vf1 vf2 :mask #b1) +;; (.mul.w.vf vf4 vf4 vf4 :mask #b1) +;; (.add.y.vf vf3 vf3 vf3 :mask #b1000) +;; (.add.z.vf vf3 vf3 vf3 :mask #b1000) +;; (.add.w.vf vf4 vf0 vf4 :mask #b1000) +;; (.mov a0-1 vf4) +;; (.mov v1-0 vf3) +;; (>= (the-as float (gpr->fpr a0-1)) +;; (the-as float (gpr->fpr v1-0))))) + +(defun vector3s-! ((arg0 vector) (arg1 vector) (arg2 vector)) + "Subtract 2 vectors3: c = (a - b)" + (set! (-> arg0 data 0) (- (-> arg1 data 0) (-> arg2 data 0))) + (set! (-> arg0 data 1) (- (-> arg1 data 1) (-> arg2 data 1))) + (set! (-> arg0 data 2) (- (-> arg1 data 2) (-> arg2 data 2))) + arg0) + +(defun vector3s*float! ((arg0 vector) (arg1 vector) (arg2 float)) + "Multiply 2 vectors3 by some float" + (set! (-> arg0 data 0) (* (-> arg1 data 0) arg2)) + (set! (-> arg0 data 1) (* (-> arg1 data 1) arg2)) + (set! (-> arg0 data 2) (* (-> arg1 data 2) arg2)) + arg0) + +(defun vector3s+! ((arg0 vector) (arg1 vector) (arg2 vector)) + "Add 2 vectors3" + (set! (-> arg0 data 0) (+ (-> arg1 data 0) (-> arg2 data 0))) + (set! (-> arg0 data 1) (+ (-> arg1 data 1) (-> arg2 data 1))) + (set! (-> arg0 data 2) (+ (-> arg1 data 2) (-> arg2 data 2))) + arg0) + +(defun vector3s-copy! ((arg0 vector) (arg1 vector)) + "Copy a vector3s" + (set! (-> arg0 data 0) (-> arg1 data 0)) + (set! (-> arg0 data 1) (-> arg1 data 1)) + (set! (-> arg0 data 2) (-> arg1 data 2)) + arg0) + +;; TODO - vector-deg-lerp-clamp! (fails to decomp) +;; - WARN: Type Propagation failed: Function vector-deg-lerp-clamp! has unknown type + +;; (defun vector-deg-diff ((arg0 vector) (arg1 vector) (arg2 vector)) +;; "TODO" +;; (local-vars (v0-0 int) +;; (v1-0 int) +;; (v1-1 int) +;; (v1-2 int) +;; (v1-3 int) +;; (a1-1 int) +;; (a1-2 int)) +;; (rlet ((vf1 :class vf) +;; (vf2 :class vf)) +;; (.lvf vf1 arg1) +;; (.lvf vf2 arg2) +;; (.ftoi.vf vf1 vf1) +;; (.ftoi.vf vf2 vf2) +;; (.mov a1-1 vf1) +;; (.mov v1-0 vf2) +;; (TODO.PSLLW a1-2 a1-1 16) +;; (TODO.PSLLW v1-1 v1-0 16) +;; (TODO.PSUBW v1-2 a1-2 v1-1) +;; (TODO.PSRAW v1-3 v1-2 16) +;; (.mov vf1 v1-3) +;; (.itof.vf vf1 vf1) +;; (.svf arg0 vf1) +;; (.mov v0-0 vf1) +;; (the-as vector v0-0))) + +;; (defun vector-degmod ((arg0 vector) (arg1 vector)) +;; "TODO" +;; (local-vars (v1-0 int) +;; (v1-1 int) +;; (v1-2 int)) +;; (rlet ((vf1 :class vf)) +;; (.lvf vf1 arg1) +;; (.ftoi.vf vf1 vf1) +;; (.mov v1-0 vf1) +;; (TODO.PSLLW v1-1 v1-0 16) +;; (TODO.PSRAW v1-2 v1-1 16) +;; (.mov vf1 v1-2) +;; (.itof.vf vf1 vf1) +;; (.svf arg0 vf1) +;; arg0)) + +;; (defun vector-degf ((arg0 vector) (arg1 vector)) +;; "TODO" +;; (local-vars (v1-0 int) +;; (v1-1 int)) +;; (rlet ((vf1 :class vf)) +;; (TODO.LQ v1-0 arg1) +;; (TODO.PSRAW v1-1 v1-0 16) +;; (.mov vf1 v1-1) +;; (.itof.vf vf1 vf1) +;; (.svf arg0 vf1) +;; arg0)) + +;; (defun vector-degi ((arg0 vector) (arg1 vector)) +;; "TODO" +;; (local-vars (v1-0 int) +;; (v1-1 int)) +;; (rlet ((vf1 :class vf)) +;; (.lvf vf1 arg1) +;; (.ftoi.vf vf1 vf1) +;; (.mov v1-0 vf1) +;; (TODO.PSLLW v1-1 v1-0 16) +;; (TODO.SQ v1-1 arg0) +;; arg0)) + +;; (defun vector4-lerp-clamp! ((arg0 vector) (arg1 vector) (arg2 float) (arg3 float)) +;; "TODO" +;; (local-vars (v1-0 int) +;; (v1-1 int) +;; (v1-2 vector) +;; (a1-1 float) +;; (f0-2 float)) +;; (rlet ((vf1 :class vf) +;; (vf2 :class vf) +;; (vf3 :class vf) +;; (vf4 :class vf)) +;; (cond +;; ((>= 0.000000 arg3) +;; (TODO.LQ v1-0 arg1) +;; (TODO.SQ v1-0 arg0)) +;; ((>= arg3 (l.f POSITIVE_ONE)) +;; (TODO.LQ v1-1 arg2) +;; (TODO.SQ v1-1 arg0)) +;; (else +;; (set! v1-2 arg0) +;; (set! f0-2 arg3) +;; (.lvf vf1 arg1) +;; (.lvf vf2 arg2) +;; (set! a1-1 f0-2) +;; (.mov vf4 a1-1) +;; (.sub.vf vf2 vf2 vf1) +;; (.mul.x.vf vf2 vf2 vf4) +;; (.add.vf vf3 vf1 vf2) +;; (.svf v1-2 vf3))) +;; arg0)) + +(defun vector4-lerp! ((arg0 vector) (arg1 vector)) + "TODO" + (local-vars (a2-0 none) + (a3-0 none)) + (rlet ((vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf) + (vf4 :class vf)) + (.lvf vf1 arg1) + (.lvf vf2 a2-0) + (.mov vf4 a3-0) + (.sub.vf vf2 vf2 vf1) + (.mul.x.vf vf2 vf2 vf4) + (.add.vf vf3 vf1 vf2) + (.svf arg0 vf3) + arg0)) + +;; (defun vector-lerp-clamp! ((arg0 vector) (arg1 vector) (arg2 float) (arg3 float)) +;; "TODO" +;; (local-vars (v1-0 int) +;; (v1-1 int) +;; (v1-2 vector) +;; (a1-1 float) +;; (f0-2 float)) +;; (rlet ((vf0 :class vf) +;; (vf1 :class vf) +;; (vf2 :class vf) +;; (vf3 :class vf) +;; (vf4 :class vf)) +;; (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) +;; (cond +;; ((>= 0.000000 arg3) +;; (TODO.LQ v1-0 arg1) +;; (TODO.SQ v1-0 arg0)) +;; ((>= arg3 (l.f POSITIVE_ONE)) +;; (TODO.LQ v1-1 arg2) +;; (TODO.SQ v1-1 arg0)) +;; (else +;; (set! v1-2 arg0) +;; (set! f0-2 arg3) +;; (.lvf vf1 arg1) +;; (.lvf vf2 arg2) +;; (set! a1-1 f0-2) +;; (.mov vf4 a1-1) +;; (.add.x.vf vf3 vf0 vf0 :mask #b1) +;; (.sub.vf vf2 vf2 vf1) +;; (.mul.x.vf vf2 vf2 vf4) +;; (.add.vf vf3 vf1 vf2 :mask #b1110) +;; (.svf v1-2 vf3))) +;; arg0)) + +(defun vector-lerp! ((arg0 vector) (arg1 vector)) + "TODO" + (local-vars (a2-0 none) + (a3-0 none)) + (rlet ((vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf) + (vf4 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf1 arg1) + (.lvf vf2 a2-0) + (.mov vf4 a3-0) + (.add.x.vf vf3 vf0 vf0 :mask #b1) + (.sub.vf vf2 vf2 vf1) + (.mul.x.vf vf2 vf2 vf4) + (.add.vf vf3 vf1 vf2 :mask #b1110) + (.svf arg0 vf3) + arg0)) + +(defun rot-zyx-from-vector! ((arg0 vector) (arg1 vector)) + "TODO" + (local-vars (v1-0 float) + (f0-1 float) + (f0-5 float) + (f0-6 float) + (f26-0 float) + (f28-0 float) + (f30-0 float)) + (set! f28-0 (-> arg1 data 2)) + (set! f30-0 (- (-> arg1 data 1))) + (set! f0-1 (atan f30-0 f28-0)) + (set! (-> arg0 data 0) f0-1) + (set! f26-0 (- f0-1)) + (set! f0-5 (- (* f28-0 (cos f26-0)) (* f30-0 (sin f26-0)))) + (set! f0-6 (atan (-> arg1 data 0) f0-5)) + (set! (-> arg0 data 1) f0-6) + (set! v1-0 f0-6) + (set! (-> arg0 data 2) 0.000000) + arg0) + +(defun rot-zxy-from-vector! ((arg0 vector) (arg1 vector)) + "TODO" + (local-vars (v1-0 float) + (f0-0 float) + (f0-4 float) + (f0-5 float) + (f26-0 float) + (f28-0 float) + (f30-0 float)) + (set! f28-0 (-> arg1 data 2)) + (set! f30-0 (-> arg1 data 0)) + (set! f0-0 (atan f30-0 f28-0)) + (set! (-> arg0 data 1) f0-0) + (set! f26-0 (- f0-0)) + (set! f0-4 (- (* f28-0 (cos f26-0)) (* f30-0 (sin f26-0)))) + (set! f0-5 (atan (- (-> arg1 data 1)) f0-4)) + (set! (-> arg0 data 0) f0-5) + (set! v1-0 f0-5) + (set! (-> arg0 data 2) 0.000000) + arg0) + +(defun vector-cvt.s.w! ((arg0 vector) (arg1 vector)) + "TODO" + (rlet ((vf1 :class vf)) + (.lvf vf1 arg1) + (.itof.vf vf1 vf1) + (.svf arg0 vf1) + arg0)) + +(defun vector-cvt.w.s! ((arg0 vector) (arg1 vector)) + "TODO" + (rlet ((vf1 :class vf)) + (.lvf vf1 arg1) + (.ftoi.vf vf1 vf1) + (.svf arg0 vf1) + arg0 + ) ) + +(defun rotate-y<-vector+vector ((arg0 vector) (arg1 vector)) + "TODO" + (atan + (- (-> arg1 data 0) (-> arg0 data 0)) + (- (-> arg1 data 2) (-> arg0 data 2)))) + +;; (defun vector-rotate-around-y! ((arg0 vector) (arg1 vector) (arg2 float)) +;; "TODO" +;; (local-vars (v1-0 int) (f0-0 float) (f26-0 float) (f28-0 float) (f30-0 float)) +;; (set! f26-0 (-> arg1 data 2)) +;; (set! f30-0 (-> arg1 data 0)) +;; (set! f28-0 (cos arg2)) +;; (set! f0-0 (sin arg2)) +;; (TODO.lq v1-0 arg1) +;; (TODO.sq v1-0 arg0) +;; (set! (-> arg0 data 2) (- (* f26-0 f28-0) (* f30-0 f0-0))) +;; (set! (-> arg0 data 0) (+ (* f26-0 f0-0) (* f30-0 f28-0))) +;; arg0) + +(defun vector-xz-length ((arg0 vector)) + "TODO" + (sqrtf + (+ (* (-> arg0 data 0) (-> arg0 data 0)) + (* (-> arg0 data 2) (-> arg0 data 2))))) + +(defun vector-xz-length-max! ((arg0 vector) (arg1 float)) + "TODO" + (local-vars (v1-4 float) + (f0-0 float) + (f0-1 float)) + (set! f0-0 (vector-xz-length arg0)) + (when + (not (or (= f0-0 0.000000) + (< f0-0 arg1))) + (set! f0-0 (/ f0-0 arg1)) + (when (!= f0-0 0.000000) + (set! (-> arg0 data 0) (/ (-> arg0 data 0) f0-0)) + (set! f0-1 (/ (-> arg0 data 2) f0-0)) + (set! (-> arg0 data 2) f0-1) + (set! v1-4 f0-1))) + arg0) + +(defun vector-length ((arg0 vector)) + "TODO" + (local-vars (v0-0 float)) + (rlet ((acc :class vf) + (Q :class vf) + (vf0 :class vf) + (vf1 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf1 arg0) + (.mul.vf vf1 vf1 vf1) + (.mul.x.vf acc vf0 vf1 :mask #b1) + (.add.mul.y.vf acc vf0 vf1 acc :mask #b1) + (.add.mul.z.vf vf1 vf0 vf1 acc :mask #b1) + (.sqrt.vf Q vf1 :ftf #b11) + (.add.w.vf vf1 vf0 vf0 :mask #b1000) + (.wait.vf) + (.mul.vf vf1 vf1 Q :mask #b1000) + (.nop.vf) + (.nop.vf) + (.mov v0-0 vf1) + (the-as float v0-0))) + +(defun vector-length-max! ((arg0 vector) (arg1 float)) + "TODO" + (local-vars (v1-4 float) + (f0-0 float) + (f0-1 float)) + (set! f0-0 (vector-length arg0)) + (when + (not (or (= f0-0 0.000000) + (< f0-0 arg1))) + (set! f0-0 (/ f0-0 arg1)) + (when (!= f0-0 0.000000) + (set! (-> arg0 data 0) (/ (-> arg0 data 0) f0-0)) + (set! (-> arg0 data 1) (/ (-> arg0 data 1) f0-0)) + (set! f0-1 (/ (-> arg0 data 2) f0-0)) + (set! (-> arg0 data 2) f0-1) + (set! v1-4 f0-1))) + arg0) + +(defun vector-xz-normalize! ((arg0 vector) (arg1 float)) + "TODO" + (local-vars (v1-1 float) + (v1-2 float) + (f0-0 float) + (f0-5 float)) + (set! f0-0 (vector-xz-length arg0)) + (when (!= f0-0 0.000000) + (set! v1-1 (/ arg1 f0-0)) + (set! (-> arg0 data 0) (* (-> arg0 data 0) v1-1)) + (set! f0-5 (* (-> arg0 data 2) v1-1)) + (set! (-> arg0 data 2) f0-5) + (set! v1-2 f0-5)) + arg0) + +;; (defun vector-normalize-copy! ((arg0 vector) (arg1 vector) (arg2 float)) +;; "TODO" +;; (local-vars (v1-0 int) +;; (v1-1 float) +;; (v1-2 float) +;; (f0-0 float) +;; (f0-7 float)) +;; (set! f0-0 (vector-length arg1)) +;; (cond +;; ((= f0-0 0.000000) +;; (TODO.LQ v1-0 arg1) +;; (TODO.SQ v1-0 arg0)) +;; (else +;; (set! v1-1 (/ arg2 f0-0)) +;; (set! (-> arg0 data 0) (* (-> arg1 data 0) v1-1)) +;; (set! (-> arg0 data 1) (* (-> arg1 data 1) v1-1)) +;; (set! f0-7 (* (-> arg1 data 2) v1-1)) +;; (set! (-> arg0 data 2) f0-7) +;; (set! v1-2 f0-7))) +;; (set! (-> arg0 data 3) (l.f POSITIVE_ONE)) +;; arg0) + +;; (defun vector-normalize-ret-len! ((a0-0 vector) (a1-0 float)) +;; "TODO" +;; (local-vars (v0-0 float) +;; (v1-0 float) +;; (v1-1 int) +;; (f0-0 int) +;; (f0-1 float)) +;; (rlet ((acc :class vf) +;; (Q :class vf) +;; (vf0 :class vf) +;; (vf1 :class vf) +;; (vf2 :class vf) +;; (vf3 :class vf)) +;; (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) +;; (.lvf vf1 a0-0) +;; (.mul.vf vf2 vf1 vf1 :mask #b1110) +;; (set! v1-0 a1-0) +;; (.mov vf3 v1-0) +;; (.mul.x.vf acc vf0 vf2 :mask #b1) +;; (.add.mul.y.vf acc vf0 vf2 acc :mask #b1) +;; (.add.mul.z.vf vf2 vf0 vf2 acc :mask #b1) +;; (.isqrt.vf Q vf3 vf2 :ftf #b0 :fsf #b11) +;; (.add.w.vf vf2 vf0 vf2 :mask #b1000) +;; (.mov v1-1 vf2) +;; (set! f0-0 (gpr->fpr v1-1)) +;; (set! f0-1 (sqrt.s f0-0)) +;; (set! v0-0 (fpr->gpr f0-1)) +;; (.wait.vf) +;; (.mul.vf vf1 vf1 Q :mask #b1110) +;; (.nop.vf) +;; (.nop.vf) +;; (.nop.vf) +;; (.svf a0-0 vf1) +;; (ret-value v0-0))) + +(defun vector-normalize! ((arg0 vector) (arg1 vector)) + "TODO" + (local-vars (v1-0 vector)) + (rlet ((acc :class vf) + (Q :class vf) + (vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf1 arg0) + (.mul.vf vf2 vf1 vf1 :mask #b1110) + (set! v1-0 arg1) + (.mov vf3 v1-0) + (.mul.x.vf acc vf0 vf2 :mask #b1) + (.add.mul.y.vf acc vf0 vf2 acc :mask #b1) + (.add.mul.z.vf vf2 vf0 vf2 acc :mask #b1) + (.isqrt.vf Q vf3 vf2 :ftf #b0 :fsf #b11) + (.wait.vf) + (.mul.vf vf1 vf1 Q :mask #b1110) + (.nop.vf) + (.nop.vf) + (.nop.vf) + (.svf arg0 vf1) + arg0)) + +(defun vector-vector-xz-distance-squared ((arg0 vector) (arg1 vector)) + "TODO" + (local-vars (v0-0 int)) + (rlet ((vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf)) + (.lvf vf2 arg0) + (.lvf vf3 arg1) + (.sub.vf vf1 vf3 vf2) + (.mul.vf vf1 vf1 vf1) + (.add.z.vf vf1 vf1 vf1 :mask #b1000) + (.mov v0-0 vf1) + (the-as float v0-0))) + +(defun vector-vector-xz-distance ((arg0 vector) (arg1 vector)) + "TODO" + (local-vars (v0-0 int)) + (rlet ((acc :class vf) + (Q :class vf) + (vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf2 arg0) + (.lvf vf3 arg1) + (.sub.vf vf1 vf3 vf2) + (.mul.vf vf1 vf1 vf1) + (.mul.x.vf acc vf0 vf1 :mask #b1) + (.add.mul.z.vf vf1 vf0 vf1 acc :mask #b1) + (.sqrt.vf Q vf1 :ftf #b11) + (.add.w.vf vf1 vf0 vf0 :mask #b1000) + (.wait.vf) + (.mul.vf vf1 vf1 Q :mask #b1000) + (.nop.vf) + (.nop.vf) + (.mov v0-0 vf1) + (the-as float v0-0))) + +(defun vector-vector-distance-squared ((arg0 vector) (arg1 vector)) + "TODO" + (local-vars (v0-0 int)) + (rlet ((vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf)) + (.lvf vf2 arg0) + (.lvf vf3 arg1) + (.sub.vf vf1 vf3 vf2) + (.mul.vf vf1 vf1 vf1) + (.add.y.vf vf1 vf1 vf1 :mask #b1000) + (.add.z.vf vf1 vf1 vf1 :mask #b1000) + (.mov v0-0 vf1) + (the-as float v0-0))) + +(defun vector-vector-distance ((arg0 vector) (arg1 vector)) + "TODO" + (local-vars (v0-0 int)) + (rlet ((acc :class vf) + (Q :class vf) + (vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf2 arg0) + (.lvf vf3 arg1) + (.sub.vf vf1 vf3 vf2) + (.mul.vf vf1 vf1 vf1) + (.mul.x.vf acc vf0 vf1 :mask #b1) + (.add.mul.y.vf acc vf0 vf1 acc :mask #b1) + (.add.mul.z.vf vf1 vf0 vf1 acc :mask #b1) + (.sqrt.vf Q vf1 :ftf #b11) + (.add.w.vf vf1 vf0 vf0 :mask #b1000) + (.wait.vf) + (.mul.vf vf1 vf1 Q :mask #b1000) + (.nop.vf) + (.nop.vf) + (.mov v0-0 vf1) + (the-as float v0-0))) + +(defun vector-xz-length-squared ((arg0 vector)) + "TODO" + (+ (* (-> arg0 data 0) (-> arg0 data 0)) + (* (-> arg0 data 2) (-> arg0 data 2)))) + +(defun vector-length-squared ((arg0 vector)) + "TODO" + (local-vars (v0-0 int)) + (rlet ((acc :class vf) + (vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf1 arg0) + (.add.w.vf vf2 vf0 vf0 :mask #b1000) + (.mul.vf vf1 vf1 vf1) + (.mul.x.vf acc vf2 vf1 :mask #b1000) + (.add.mul.y.vf acc vf2 vf1 acc :mask #b1000) + (.add.mul.z.vf vf1 vf2 vf1 acc :mask #b1000) + (.mov v0-0 vf1) + (the-as float v0-0))) + +(defun vector-from-ups! ((arg0 vector) (arg1 vector)) + "TODO" + (local-vars (at-0 int) + (f0-0 float)) + (rlet ((vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf1 arg1) + (set! f0-0 (-> *display* seconds-per-frame)) + (.mov at-0 f0-0) + (.mov vf2 at-0) + (.mov.vf vf1 vf0 :mask #b1) + (.mul.x.vf vf1 vf1 vf2 :mask #b1110) + (.svf arg0 vf1) + arg0)) + +(defun vector-to-ups! ((arg0 vector) (arg1 vector)) + "TODO" + (local-vars (at-0 int) + (f0-0 float)) + (rlet ((vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf1 arg1) + (set! f0-0 (-> *display* frames-per-second)) + (.mov at-0 f0-0) + (.mov vf2 at-0) + (.mov.vf vf1 vf0 :mask #b1) + (.mul.x.vf vf1 vf1 vf2 :mask #b1110) + (.svf arg0 vf1) + arg0)) + +(defun vector+float*! ((arg0 vector) (arg1 vector) (arg2 vector) (arg3 float)) + "TODO" + (rlet ((acc :class vf) + (vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf) + (vf4 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf2 arg2) + (.lvf vf1 arg1) + (.mov vf3 arg3) + (.add.x.vf vf4 vf0 vf0 :mask #b1) + (.mul.x.vf acc vf2 vf3) + (.add.mul.w.vf vf4 vf1 vf0 acc :mask #b1110) + (.svf arg0 vf4) + arg0)) + +(defun vector-v*float++! ((arg0 vector) (arg1 vector) (arg2 float)) + "TODO" + (vector+float*! arg0 arg0 arg1 (* arg2 (-> *display* seconds-per-frame))) + arg0) + +(defun vector-v*float! ((arg0 vector) (arg1 float) (arg2 float)) + "TODO" + (local-vars (v0-0 vector) + (v1-0 float) + (v1-1 float) + (f0-1 float)) + (rlet ((vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (set! v0-0 arg0) + (set! v1-0 arg1) + (set! f0-1 (* arg2 (-> *display* seconds-per-frame))) + (.lvf vf1 v1-0) + (set! v1-1 f0-1) + (.mov vf2 v1-1) + (.add.x.vf vf1 vf0 vf0 :mask #b1) + (.mul.x.vf vf1 vf1 vf2 :mask #b1110) + (.svf v0-0 vf1) + v0-0)) + +(defun vector-v++! ((arg0 vector) (arg1 vector)) + "TODO" + (vector+float*! arg0 arg0 arg1 (-> *display* seconds-per-frame)) + arg0) + +(defun vector-v*float+! ((arg0 vector) (arg1 vector) (arg2 vector) (arg3 float)) + "TODO" + (vector+float*! arg0 arg1 arg2 (* arg3 (-> *display* seconds-per-frame))) + arg0) + +(defun vector-v+! ((arg0 vector) (arg1 vector) (arg2 vector)) + "TODO" + (vector+float*! arg0 arg1 arg2 (-> *display* seconds-per-frame)) + arg0) + +(defun vector-v! ((arg0 vector)) + "TODO" + (local-vars (v1-0 vector) + (a1-0 vector) + (a1-1 float) + (f0-0 float)) + (rlet ((vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (set! v1-0 arg0) + (set! a1-0 arg0) + (set! f0-0 (-> *display* seconds-per-frame)) + (.lvf vf1 a1-0) + (set! a1-1 f0-0) + (.mov vf2 a1-1) + (.add.x.vf vf1 vf0 vf0 :mask #b1) + (.mul.x.vf vf1 vf1 vf2 :mask #b1110) + (.svf v1-0 vf1) + arg0)) + +(defun vector-seconds! ((arg0 vector)) + "TODO" + (set! (-> arg0 data 0) (* 300.000000 (-> arg0 data 0))) + (set! (-> arg0 data 1) (* 300.000000 (-> arg0 data 1))) + (set! (-> arg0 data 2) (* 300.000000 (-> arg0 data 2))) + arg0) + +(defun vector-seconds ((arg0 vector) (arg1 vector)) + "TODO" + (set! (-> arg0 data 0) (* 300.000000 (-> arg1 data 0))) + (set! (-> arg0 data 1) (* 300.000000 (-> arg1 data 1))) + (set! (-> arg0 data 2) (* 300.000000 (-> arg1 data 2))) + arg0) + +(defun vector-identity! ((arg0 vector)) + "TODO" + (set! (-> arg0 data 0) 1.000000) + (set! (-> arg0 data 1) 1.000000) + (set! (-> arg0 data 2) 1.000000) + (set! (-> arg0 data 3) 1.000000) + arg0) + +(defun seek-with-smooth ((arg0 float) (arg1 float) (arg2 float) (arg3 float) (arg4 float)) + "TODO" + (local-vars (v1-1 float) + (v1-3 float) + (f0-1 float) + (f0-2 float) + (f1-4 float)) + (set! f0-1 (- arg1 arg0)) + (cond + ((>= arg4 (fabs f0-1)) arg1) + (else + (set! f0-2 (* f0-1 arg3)) + (set! f1-4 (- arg2)) + (cond + ((< f0-2 f1-4) + (set! f0-2 f1-4) + (set! v1-1 f0-2)) + ((< arg2 f0-2) + (set! f0-2 arg2) + (set! v1-3 f0-2))) + (+ f0-2 arg0)))) + +(defun vector-seek-3d-smooth! ((arg0 vector) (arg1 vector) (arg2 float) (arg3 float)) + "TODO" + (local-vars (v1-4 float) + (v1-5 float) + (f0-1 float) + (f0-4 float) + (f0-5 float) + (f0-7 float) + (f1-2 float) + (f1-3 float) + (f2-6 float) + (f3-1 float) + (f3-4 float) + (f3-5 float) + (f3-7 float)) + (set! f0-1 (- (-> arg1 data 0) (-> arg0 data 0))) + (set! f1-2 (- (-> arg1 data 1) (-> arg0 data 1))) + (set! f3-1 (- (-> arg1 data 2) (-> arg0 data 2))) + (when + (or (!= f0-1 0.000000) (!= f1-2 0.000000) (!= f3-1 0.000000)) + (set! f2-6 (* f0-1 arg3)) + (set! f1-3 (* f1-2 arg3)) + (set! f0-4 (* f3-1 arg3)) + (set! f3-4 (+ (+ (* f2-6 f2-6) (* f1-3 f1-3)) (* f0-4 f0-4))) + (set! f3-5 (sqrtf f0-4)) + (cond + ((>= arg2 f3-5) + (set! (-> arg0 data 0) (+ (-> arg0 data 0) f2-6)) + (set! (-> arg0 data 1) (+ (-> arg0 data 1) f1-3)) + (set! f0-5 (+ (-> arg0 data 2) f0-4)) + (set! (-> arg0 data 2) f0-5) + (set! v1-4 f0-5)) + (else + (set! f3-7 (/ arg2 f3-5)) + (set! (-> arg0 data 0) (+ (-> arg0 data 0) (* f3-7 f2-6))) + (set! (-> arg0 data 1) (+ (-> arg0 data 1) (* f3-7 f1-3))) + (set! f0-7 (+ (-> arg0 data 2) (* f3-7 f0-4))) + (set! (-> arg0 data 2) f0-7) + (set! v1-5 f0-7)))) + arg0) + +(defun vector-seek-2d-yz-smooth! ((arg0 vector) (arg1 vector) (arg2 float) (arg3 float)) + "TODO" + (local-vars (v1-3 float) + (v1-4 float) + (f0-1 float) + (f0-3 float) + (f0-4 float) + (f0-6 float) + (f1-5 float) + (f2-1 float) + (f2-3 float) + (f2-4 float) + (f2-6 float)) + (set! f0-1 (- (-> arg1 data 1) (-> arg0 data 1))) + (set! f2-1 (- (-> arg1 data 2) (-> arg0 data 2))) + (when + (or (!= f0-1 0.000000) (!= f2-1 0.000000)) + (set! f1-5 (* f0-1 arg3)) + (set! f0-3 (* f2-1 arg3)) + (set! f2-3 (+ (* f1-5 f1-5) (* f0-3 f0-3))) + (set! f2-4 (sqrtf f0-3)) + (cond + ((>= arg2 f2-4) + (set! (-> arg0 data 1) (+ (-> arg0 data 1) f1-5)) + (set! f0-4 (+ (-> arg0 data 2) f0-3)) + (set! (-> arg0 data 2) f0-4) + (set! v1-3 f0-4)) + (else + (set! f2-6 (/ arg2 f2-4)) + (set! (-> arg0 data 1) (+ (-> arg0 data 1) (* f2-6 f1-5))) + (set! f0-6 (+ (-> arg0 data 2) (* f2-6 f0-3))) + (set! (-> arg0 data 2) f0-6) + (set! v1-4 f0-6)))) + arg0) + +(defun vector-seek-2d-xz-smooth! ((arg0 vector) (arg1 vector) (arg2 float) (arg3 float)) + "TODO" + (local-vars (v1-3 float) + (v1-4 float) + (f0-1 float) + (f0-3 float) + (f0-4 float) + (f0-6 float) + (f1-5 float) + (f2-1 float) + (f2-3 float) + (f2-4 float) + (f2-6 float)) + (set! f0-1 (- (-> arg1 data 0) (-> arg0 data 0))) + (set! f2-1 (- (-> arg1 data 2) (-> arg0 data 2))) + (when + (or (!= f0-1 0.000000) (!= f2-1 0.000000)) + (set! f1-5 (* f0-1 arg3)) + (set! f0-3 (* f2-1 arg3)) + (set! f2-3 (+ (* f1-5 f1-5) (* f0-3 f0-3))) + (set! f2-4 (sqrtf f0-3)) + (cond + ((>= arg2 f2-4) + (set! (-> arg0 data 0) (+ (-> arg0 data 0) f1-5)) + (set! f0-4 (+ (-> arg0 data 2) f0-3)) + (set! (-> arg0 data 2) f0-4) + (set! v1-3 f0-4)) + (else + (set! f2-6 (/ arg2 f2-4)) + (set! (-> arg0 data 0) (+ (-> arg0 data 0) (* f2-6 f1-5))) + (set! f0-6 (+ (-> arg0 data 2) (* f2-6 f0-3))) + (set! (-> arg0 data 2) f0-6) + (set! v1-4 f0-6)))) + arg0) + +(defun vector-seek! ((arg0 vector) (arg1 vector) (arg2 float)) + "TODO" + (rlet ((vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf) + (vf4 :class vf) + (vf5 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.mov vf4 arg2) + (.lvf vf1 arg1) + (.lvf vf2 arg0) + (.add.x.vf vf1 vf0 vf0 :mask #b1) + (.sub.x.vf vf5 vf0 vf4 :mask #b1000) + (.sub.vf vf3 vf1 vf2 :mask #b1110) + (.min.x.vf vf3 vf3 vf4 :mask #b1110) + (.max.x.vf vf3 vf3 vf5 :mask #b1110) + (.add.vf vf1 vf2 vf3 :mask #b1110) + (.svf arg0 vf1) + arg0)) + +(defun vector-delta ((arg0 vector) (arg1 vector)) + "TODO" + (local-vars (v0-0 int)) + (rlet ((acc :class vf) + (vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf1 arg0) + (.lvf vf2 arg1) + (.sub.vf vf1 vf2 vf1) + (.abs.vf vf1 vf1) + (.mul.x.vf acc vf0 vf1 :mask #b1) + (.add.mul.y.vf acc vf0 vf1 acc :mask #b1) + (.add.mul.z.vf vf3 vf0 vf1 acc :mask #b1) + (.add.w.vf vf3 vf0 vf3 :mask #b1000) + (.mov v0-0 vf3) + (the-as float v0-0))) + +;; TODO - vector= (decomp failed) + +(defun vector-negate-in-place! ((arg0 vector)) + "TODO" + (rlet ((vf0 :class vf) + (vf1 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf1 arg0) + (.sub.vf vf1 vf0 vf1 :mask #b1110) + (.svf arg0 vf1) + arg0)) + +(defun vector-negate! ((arg0 vector) (arg1 vector)) + "TODO" + (rlet ((vf0 :class vf) + (vf1 :class vf) + (vf4 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf1 arg1) + (.sub.vf vf4 vf0 vf1 :mask #b1110) + (.add.x.vf vf4 vf0 vf0 :mask #b1) + (.svf arg0 vf4) + arg0)) + +(defun vector-float/! ((arg0 vector) (arg1 vector) (arg2 float)) + "TODO" + (rlet ((Q :class vf) + (vf0 :class vf) + (vf1 :class vf) + (vf3 :class vf) + (vf4 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.mov vf3 arg2) + (.div.vf Q vf0 vf3 :ftf #b11 :fsf #b0) + (.lvf vf1 arg1) + (.add.x.vf vf4 vf0 vf0 :mask #b1) + (.wait.vf) + (.mul.vf vf4 vf1 Q :mask #b1110) + (.nop.vf) + (.nop.vf) + (.svf arg0 vf4) + arg0)) + +(defun vector--float*! ((arg0 vector) (arg1 vector) (arg2 vector) (arg3 float)) + "TODO" + (rlet ((acc :class vf) + (vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf) + (vf4 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf2 arg2) + (.lvf vf1 arg1) + (.mov vf3 arg3) + (.add.x.vf vf4 vf0 vf0 :mask #b1) + (.mul.w.vf acc vf1 vf0) + (.sub.mul.x.vf vf4 vf2 vf3 acc :mask #b1110) + (.svf arg0 vf4) + arg0)) + +(defun vector-average! ((arg0 vector) (arg1 vector) (arg2 vector)) + "TODO" + (local-vars (v1-0 int)) + (rlet ((acc :class vf) + (vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf) + (vf4 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (set! v1-0 #x3f000000) + (.lvf vf1 arg1) + (.lvf vf2 arg2) + (.mov vf3 v1-0) + (.add.x.vf vf4 vf0 vf0 :mask #b1) + (.mul.x.vf acc vf1 vf3) + (.add.mul.x.vf vf4 vf2 vf3 acc :mask #b1110) + (.svf arg0 vf4) + arg0)) + +(defun vector-float*! ((arg0 vector) (arg1 vector) (arg2 float)) + "TODO" + (rlet ((vf0 :class vf) + (vf1 :class vf) + (vf2 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf1 arg1) + (.mov vf2 arg2) + (.add.x.vf vf1 vf0 vf0 :mask #b1) + (.mul.x.vf vf1 vf1 vf2 :mask #b1110) + (.svf arg0 vf1) + arg0)) + +(defun vector/! ((arg0 vector) (arg1 vector) (arg2 vector)) + "TODO" + (local-vars (v1-0 float)) + (rlet ((Q :class vf) + (vf0 :class vf) + (vf4 :class vf) + (vf5 :class vf) + (vf6 :class vf) + (vf7 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf5 arg2) + (.div.vf Q vf0 vf5 :ftf #b11 :fsf #b1) + (.add.x.vf vf6 vf0 vf0 :mask #b1) + (.lvf vf4 arg1) + (set! v1-0 (/ (-> arg1 data 0) (-> arg2 data 0))) + (.wait.vf) + (.mul.vf vf6 vf4 Q :mask #b100) + (.nop.vf) + (.nop.vf) + (.div.vf Q vf0 vf5 :ftf #b11 :fsf #b10) + (.mov vf7 v1-0) + (.add.x.vf vf6 vf0 vf7 :mask #b1000) + (.wait.vf) + (.mul.vf vf6 vf4 Q :mask #b10) + (.nop.vf) + (.nop.vf) + (.svf arg0 vf6) + arg0)) + +(defun vector-*! ((arg0 vector) (arg1 vector) (arg2 vector) (arg3 float)) + "TODO" + (rlet ((acc :class vf) + (vf0 :class vf) + (vf4 :class vf) + (vf5 :class vf) + (vf6 :class vf) + (vf7 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.mov vf7 arg3) + (.lvf vf5 arg2) + (.lvf vf4 arg1) + (.add.x.vf vf6 vf0 vf0 :mask #b1) + (.mul.w.vf acc vf4 vf0 :mask #b1110) + (.sub.mul.x.vf vf6 vf5 vf7 acc :mask #b1110) + (.svf arg0 vf6) + arg0)) + +(defun vector+*! ((arg0 vector) (arg1 vector) (arg2 vector) (arg3 float)) + "TODO" + (rlet ((acc :class vf) + (vf0 :class vf) + (vf4 :class vf) + (vf5 :class vf) + (vf6 :class vf) + (vf7 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.mov vf7 arg3) + (.lvf vf5 arg2) + (.lvf vf4 arg1) + (.add.x.vf vf6 vf0 vf0 :mask #b1) + (.mul.x.vf acc vf5 vf7 :mask #b1110) + (.add.mul.w.vf vf6 vf4 vf0 acc :mask #b1110) + (.svf arg0 vf6) + arg0)) + +(defun vector*! ((arg0 vector) (arg1 vector) (arg2 vector)) + "TODO" + (rlet ((vf0 :class vf) + (vf4 :class vf) + (vf5 :class vf) + (vf6 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.lvf vf4 arg1) + (.lvf vf5 arg2) + (.add.x.vf vf6 vf0 vf0 :mask #b1) + (.mul.vf vf6 vf4 vf5 :mask #b1110) + (.svf arg0 vf6) + arg0)) + +(defun vector+float! ((arg0 vector) (arg1 vector) (arg2 float)) + "TODO" + (rlet ((vf0 :class vf) + (vf4 :class vf) + (vf5 :class vf) + (vf6 :class vf)) + (.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0)) + (.mov vf6 arg2) + (.lvf vf4 arg1) + (.add.x.vf vf5 vf0 vf0 :mask #b1) + (.add.x.vf vf5 vf4 vf6 :mask #b1110) + (.svf arg0 vf5) + arg0)) + +(defun vector-cross! ((arg0 vector) (arg1 vector) (arg2 vector)) + "TODO" + (rlet ((acc :class vf) + (vf1 :class vf) + (vf2 :class vf) + (vf3 :class vf)) + (.lvf vf1 arg1) + (.lvf vf2 arg2) + (.outer.product.vf vf3 vf1 vf2) + (.svf arg0 vf3) + arg0)) \ No newline at end of file diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index a68035bf41..0d2a4cd14a 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -347,6 +347,7 @@ class Compiler { // Vector Float Operations Val* compile_asm_lvf(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_asm_svf(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_asm_mov_vf(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_asm_blend_vf(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_asm_wait_vf(const goos::Object& form, const goos::Object& rest, Env* env); @@ -401,6 +402,7 @@ class Compiler { Val* compile_asm_div_vf(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_asm_sqrt_vf(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_asm_inv_sqrt_vf(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_asm_itof_vf(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_asm_ftoi_vf(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_asm_pw_sll(const goos::Object& form, const goos::Object& rest, Env* env); diff --git a/goalc/compiler/compilation/Asm.cpp b/goalc/compiler/compilation/Asm.cpp index 05a57f5831..a429a2de05 100644 --- a/goalc/compiler/compilation/Asm.cpp +++ b/goalc/compiler/compilation/Asm.cpp @@ -263,7 +263,9 @@ Val* Compiler::compile_asm_wait_vf(const goos::Object& form, const goos::Object& */ Val* Compiler::compile_asm_lvf(const goos::Object& form, const goos::Object& rest, Env* env) { auto args = get_va(form, rest); - va_check(form, args, {{}, {}}, {{"color", {false, goos::ObjectType::SYMBOL}}}); + va_check(form, args, {{}, {}}, + {{"color", {false, goos::ObjectType::SYMBOL}}, + {"offset", {false, goos::ObjectType::INTEGER}}}); bool color = true; if (args.has_named("color")) { color = get_true_or_false(form, args.named.at("color")); @@ -274,23 +276,36 @@ Val* Compiler::compile_asm_lvf(const goos::Object& form, const goos::Object& res throw_compiler_error(form, "Cannot .lvf into this. Got a {}.", dest->print()); } auto src = compile_error_guard(args.unnamed.at(1), env); - auto as_co = dynamic_cast(src); + auto as_sv = dynamic_cast(src); + // Loading directly from a static value is not supported! + if (as_sv && args.has_named("offset")) { + throw_compiler_error(form, "Cannot .lvf from a static value"); + } else if (as_sv && !args.has_named("offset")) { + env->emit_ir(dest, as_sv->obj); + return get_none(); + } + + auto as_co = dynamic_cast(src); + RegVal* baseReg = as_co ? as_co->base->to_gpr(env) : src->to_gpr(env); + int offset = 0; + + if (as_co) { + if (!args.has_named("offset")) { + offset = as_co->offset; + } else { + offset = as_co->offset + args.named.at("offset").as_int(); + } + } else if (args.has_named("offset")) { + offset = args.named.at("offset").as_int(); + } + MemLoadInfo info; info.sign_extend = false; info.size = 16; info.reg = RegClass::VECTOR_FLOAT; - if (as_co) { - // can do a clever offset here - env->emit_ir(dest, as_co->offset, as_co->base->to_gpr(env), info, color); - } else if (as_sv) { - if (!color) { - throw std::runtime_error("no color nyi for static loads"); - } - env->emit_ir(dest, as_sv->obj); - } else { - env->emit_ir(dest, 0, src->to_gpr(env), info, color); - } + env->emit_ir(dest, offset, baseReg, info, color); + return get_none(); } @@ -299,7 +314,9 @@ Val* Compiler::compile_asm_lvf(const goos::Object& form, const goos::Object& res */ Val* Compiler::compile_asm_svf(const goos::Object& form, const goos::Object& rest, Env* env) { auto args = get_va(form, rest); - va_check(form, args, {{}, {}}, {{"color", {false, goos::ObjectType::SYMBOL}}}); + va_check(form, args, {{}, {}}, + {{"color", {false, goos::ObjectType::SYMBOL}}, + {"offset", {false, goos::ObjectType::INTEGER}}}); bool color = true; if (args.has_named("color")) { color = get_true_or_false(form, args.named.at("color")); @@ -313,16 +330,24 @@ Val* Compiler::compile_asm_svf(const goos::Object& form, const goos::Object& res } auto as_co = dynamic_cast(dest); + RegVal* baseReg = as_co ? as_co->base->to_gpr(env) : dest->to_gpr(env); + int offset = 0; + + if (as_co) { + if (!args.has_named("offset")) { + offset = as_co->offset; + } else { + offset = as_co->offset + args.named.at("offset").as_int(); + } + } else if (args.has_named("offset")) { + offset = args.named.at("offset").as_int(); + } + MemLoadInfo info; info.sign_extend = false; info.size = 16; info.reg = RegClass::VECTOR_FLOAT; - if (as_co) { - // can do a clever offset here - env->emit_ir(src, as_co->offset, as_co->base->to_gpr(env), 16, color); - } else { - env->emit_ir(src, 0, dest->to_gpr(env), 16, color); - } + env->emit_ir(src, offset, baseReg, info.size, color); return get_none(); } @@ -337,6 +362,33 @@ void Compiler::check_vector_float_regs(const goos::Object& form, } } +Val* Compiler::compile_asm_mov_vf(const goos::Object& form, const goos::Object& rest, Env* env) { + auto args = get_va(form, rest); + va_check( + form, args, {{}, {}}, + {{"color", {false, goos::ObjectType::SYMBOL}}, {"mask", {false, goos::ObjectType::INTEGER}}}); + bool color = true; + if (args.has_named("color")) { + color = get_true_or_false(form, args.named.at("color")); + } + + auto dest = compile_error_guard(args.unnamed.at(0), env)->to_reg(env); + auto src = compile_error_guard(args.unnamed.at(1), env)->to_reg(env); + check_vector_float_regs(form, env, {{"destination", dest}, {"source", src}}); + + u8 mask = 0b1111; + if (args.has_named("mask")) { + mask = args.named.at("mask").as_int(); + if (mask > 15) { + throw_compiler_error(form, "The value {} is out of range for a blend mask (0-15 inclusive).", + mask); + } + } + + env->emit_ir(color, dest, dest, src, mask); + return get_none(); +} + Val* Compiler::compile_asm_blend_vf(const goos::Object& form, const goos::Object& rest, Env* env) { auto args = get_va(form, rest); va_check( @@ -994,6 +1046,52 @@ Val* Compiler::compile_asm_sqrt_vf(const goos::Object& form, const goos::Object& return get_none(); } +Val* Compiler::compile_asm_inv_sqrt_vf(const goos::Object& form, + const goos::Object& rest, + Env* env) { + auto args = get_va(form, rest); + va_check(form, args, {{}, {}, {}}, + { + {"color", {false, goos::ObjectType::SYMBOL}}, + {"fsf", {true, goos::ObjectType::INTEGER}}, + {"ftf", {true, goos::ObjectType::INTEGER}}, + }); + bool color = true; + if (args.has_named("color")) { + color = get_true_or_false(form, args.named.at("color")); + } + + auto dest = compile_error_guard(args.unnamed.at(0), env)->to_reg(env); + auto src1 = compile_error_guard(args.unnamed.at(1), env)->to_reg(env); + auto src2 = compile_error_guard(args.unnamed.at(2), env)->to_reg(env); + check_vector_float_regs(form, env, + {{"destination", dest}, {"first source", src1}, {"second source", src2}}); + + u8 fsf = args.named.at("fsf").as_int(); + if (fsf > 3) { + throw_compiler_error(form, "The value {} is out of range for fsf (0-3 inclusive).", fsf); + } + u8 ftf = args.named.at("ftf").as_int(); + if (ftf > 3) { + throw_compiler_error(form, "The value {} is out of range for ftf (0-3 inclusive).", ftf); + } + + // Save one temp reg, use the destination as one + auto temp_reg = env->make_vfr(dest->type()); + + // Splat src1's value into the dest reg, keep it simple, this way no matter which vector component + // is accessed from the final result will be the correct answer + env->emit_ir(color, dest, src1, ftf_fsf_to_vector_element(fsf)); + // Splat src1's value into the the temp reg + env->emit_ir(color, temp_reg, src2, ftf_fsf_to_vector_element(ftf)); + // Square Root the temp reg + env->emit_ir(color, temp_reg, temp_reg); + + // Perform the Division + env->emit_ir(color, dest, dest, temp_reg, IR_VFMath3Asm::Kind::DIV); + return get_none(); +} + Val* Compiler::compile_asm_outer_product_vf(const goos::Object& form, const goos::Object& rest, Env* env) { diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 4301174a05..33bfa9c95a 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -28,6 +28,7 @@ const std::unordered_map< // INLINE ASM - VECTOR FLOAT OPERATIONS {".lvf", &Compiler::compile_asm_lvf}, {".svf", &Compiler::compile_asm_svf}, + {".mov.vf", &Compiler::compile_asm_mov_vf}, {".blend.vf", &Compiler::compile_asm_blend_vf}, {".nop.vf", &Compiler::compile_asm_nop_vf}, @@ -87,6 +88,7 @@ const std::unordered_map< {".div.vf", &Compiler::compile_asm_div_vf}, {".sqrt.vf", &Compiler::compile_asm_sqrt_vf}, + {".isqrt.vf", &Compiler::compile_asm_inv_sqrt_vf}, {".itof.vf", &Compiler::compile_asm_itof_vf}, {".ftoi.vf", &Compiler::compile_asm_ftoi_vf}, diff --git a/test/goalc/source_templates/with_game/test-vector-outer-product.gc b/test/goalc/source_templates/with_game/test-vector-outer-product.gc index 6492db65ad..6b3ccaa944 100644 --- a/test/goalc/source_templates/with_game/test-vector-outer-product.gc +++ b/test/goalc/source_templates/with_game/test-vector-outer-product.gc @@ -1,7 +1,7 @@ (defun test-vector-outer-product () (let ((vector-in-1 (new 'stack 'vector)) (vector-in-2 (new 'stack 'vector)) - (vector-out (new 'stack 'vector))) + (vector-out (new 'stack 'vector))) (set-vector! vector-in-1 1.0 2.0 3.0 4.0) (set-vector! vector-in-2 5.0 6.0 7.0 8.0)