mirror of
https://github.com/open-goal/jak-project
synced 2026-07-11 15:28:58 -04:00
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
This commit is contained in:
+7
-1
@@ -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
|
||||
- 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1172,4 +1172,4 @@ Instruction decode_instruction(LinkedWord& word, LinkedObjectFile& file, int seg
|
||||
|
||||
return i;
|
||||
}
|
||||
} // namespace decompiler
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -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<DecompilerLabel>& labels, const En
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
goos::Object AsmOp::to_open_goal_form(const std::vector<DecompilerLabel>& labels,
|
||||
const Env& env) const {
|
||||
std::vector<goos::Object> forms;
|
||||
|
||||
std::vector<std::optional<RegisterAccess>> 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<goos::Object> 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
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -297,6 +297,7 @@ class AsmOp : public AtomicOp {
|
||||
public:
|
||||
AsmOp(Instruction instr, int my_idx);
|
||||
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env& env) const override;
|
||||
goos::Object to_open_goal_form(const std::vector<DecompilerLabel>& 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
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
#include "common/type_system/TypeSystem.h"
|
||||
#include <algorithm>
|
||||
|
||||
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<void(FormElement*)>& f) {
|
||||
f(this);
|
||||
}
|
||||
|
||||
void OpenGoalAsmOpElement::apply_form(const std::function<void(Form*)>&) {}
|
||||
|
||||
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(Form*)>&) {}
|
||||
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<void(FormElement*)>& f) {
|
||||
f(this);
|
||||
body->apply(f);
|
||||
}
|
||||
|
||||
goos::Object RLetElement::to_form_internal(const Env& env) const {
|
||||
std::vector<goos::Object> 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<goos::Object> 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<void(Form*)>& 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
|
||||
/////////////////////////////
|
||||
|
||||
@@ -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<void(FormElement*)>& f) override;
|
||||
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
|
||||
void apply_form(const std::function<void(Form*)>& f) override;
|
||||
void collect_vars(RegAccessSet& vars, bool recursive) const override;
|
||||
void get_modified_regs(RegSet& regs) const override;
|
||||
Form* body = nullptr;
|
||||
std::vector<decompiler::Register> 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.
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
#include "OpenGoalMapping.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
#include <optional>
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
typedef OpenGOALAsm::InstructionModifiers MOD;
|
||||
|
||||
const std::map<InstructionKind, OpenGOALAsm::Function> 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<RegisterAccess> _dst,
|
||||
const std::vector<std::optional<RegisterAccess>>& _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<goos::Object> OpenGOALAsm::get_args(const std::vector<DecompilerLabel>& labels,
|
||||
const Env& env) {
|
||||
std::vector<goos::Object> args;
|
||||
std::vector<goos::Object> 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
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include <map>
|
||||
#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<InstructionModifiers> modifiers = {};
|
||||
|
||||
bool allows_modifier(InstructionModifiers);
|
||||
};
|
||||
|
||||
OpenGOALAsm(Instruction _instr);
|
||||
|
||||
OpenGOALAsm(Instruction _instr,
|
||||
std::optional<RegisterAccess> _dst,
|
||||
const std::vector<std::optional<RegisterAccess>>& _src);
|
||||
|
||||
bool valid = true;
|
||||
bool todo = false;
|
||||
std::optional<RegisterAccess> m_dst;
|
||||
std::vector<std::optional<RegisterAccess>> m_src;
|
||||
Instruction instr;
|
||||
OpenGOALAsm::Function func;
|
||||
|
||||
std::string full_function_name();
|
||||
std::vector<goos::Object> get_args(const std::vector<DecompilerLabel>& labels, const Env& env);
|
||||
};
|
||||
extern const std::map<InstructionKind, OpenGOALAsm::Function> MIPS_ASM_TO_OPEN_GOAL_FUNCS;
|
||||
} // namespace decompiler
|
||||
@@ -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);
|
||||
|
||||
@@ -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...");
|
||||
|
||||
@@ -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<FormElement*> 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<AsmOpElement*>(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<OpenGoalAsmOpElement>(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<RLetElement>(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
|
||||
@@ -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
|
||||
@@ -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]],
|
||||
|
||||
|
||||
|
||||
}
|
||||
"vector": [
|
||||
["L113", "float", true],
|
||||
["L112", "float", true],
|
||||
["L111", "float", true]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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**
|
||||
|
||||
|
||||
+9
-5
@@ -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 <int>])
|
||||
```
|
||||
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 <int>])
|
||||
```
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
+1094
-6
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
@@ -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<MemoryOffsetConstantVal*>(src);
|
||||
|
||||
auto as_sv = dynamic_cast<StaticVal*>(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<IR_StaticVarLoad>(dest, as_sv->obj);
|
||||
return get_none();
|
||||
}
|
||||
|
||||
auto as_co = dynamic_cast<MemoryOffsetConstantVal*>(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<IR_LoadConstOffset>(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<IR_StaticVarLoad>(dest, as_sv->obj);
|
||||
} else {
|
||||
env->emit_ir<IR_LoadConstOffset>(dest, 0, src->to_gpr(env), info, color);
|
||||
}
|
||||
env->emit_ir<IR_LoadConstOffset>(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<MemoryOffsetConstantVal*>(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<IR_StoreConstOffset>(src, as_co->offset, as_co->base->to_gpr(env), 16, color);
|
||||
} else {
|
||||
env->emit_ir<IR_StoreConstOffset>(src, 0, dest->to_gpr(env), 16, color);
|
||||
}
|
||||
env->emit_ir<IR_StoreConstOffset>(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<IR_BlendVF>(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<IR_SplatVF>(color, dest, src1, ftf_fsf_to_vector_element(fsf));
|
||||
// Splat src1's value into the the temp reg
|
||||
env->emit_ir<IR_SplatVF>(color, temp_reg, src2, ftf_fsf_to_vector_element(ftf));
|
||||
// Square Root the temp reg
|
||||
env->emit_ir<IR_SqrtVF>(color, temp_reg, temp_reg);
|
||||
|
||||
// Perform the Division
|
||||
env->emit_ir<IR_VFMath3Asm>(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) {
|
||||
|
||||
@@ -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},
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user