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:
Tyler Wilding
2021-03-06 07:46:26 -08:00
committed by GitHub
parent e560b1cb55
commit f123bf368a
24 changed files with 2151 additions and 323 deletions
+7 -1
View File
@@ -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
+2 -3
View File
@@ -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
+39 -27
View File
@@ -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
+4
View File
@@ -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
+1 -1
View File
@@ -1172,4 +1172,4 @@ Instruction decode_instruction(LinkedWord& word, LinkedObjectFile& file, int seg
return i;
}
} // namespace decompiler
} // namespace decompiler
+33 -1
View File
@@ -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
+2 -1
View File
@@ -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
+121
View File
@@ -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
/////////////////////////////
+35
View File
@@ -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.
+214
View File
@@ -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
+52
View File
@@ -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
+1
View File
@@ -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);
+25 -1
View File
@@ -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
+12
View File
@@ -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]
]
}
+27
View File
@@ -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
View File
@@ -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
+1 -2
View File
@@ -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.
File diff suppressed because it is too large Load Diff
+2
View File
@@ -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);
+118 -20
View File
@@ -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) {
+2
View File
@@ -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)