mirror of
https://github.com/open-goal/jak-project
synced 2026-09-10 04:22:13 -04:00
[Decomp] Decompile engine math library types (#272)
* decompile some stuff * fix typo * playing around with trigonometry * more progress on trig * more trig * comments and small fixes * finish trig
This commit is contained in:
@@ -94,7 +94,8 @@ class Function {
|
||||
bool run_type_analysis_ir2(const TypeSpec& my_type,
|
||||
DecompilerTypeSystem& dts,
|
||||
LinkedObjectFile& file,
|
||||
const std::unordered_map<int, std::vector<TypeHint>>& hints);
|
||||
const std::unordered_map<int, std::vector<TypeHint>>& hints,
|
||||
const std::unordered_map<std::string, LabelType>& label_types);
|
||||
BlockTopologicalSort bb_topo_sort();
|
||||
|
||||
TypeSpec type;
|
||||
|
||||
@@ -44,12 +44,15 @@ void try_apply_hints(int idx,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool Function::run_type_analysis_ir2(const TypeSpec& my_type,
|
||||
DecompilerTypeSystem& dts,
|
||||
LinkedObjectFile& file,
|
||||
const std::unordered_map<int, std::vector<TypeHint>>& hints) {
|
||||
bool Function::run_type_analysis_ir2(
|
||||
const TypeSpec& my_type,
|
||||
DecompilerTypeSystem& dts,
|
||||
LinkedObjectFile& file,
|
||||
const std::unordered_map<int, std::vector<TypeHint>>& hints,
|
||||
const std::unordered_map<std::string, LabelType>& label_types) {
|
||||
(void)file;
|
||||
ir2.env.set_type_hints(hints);
|
||||
ir2.env.set_label_types(label_types);
|
||||
// STEP 0 - set decompiler type system settings for this function. In config we can manually
|
||||
// specify some settings for type propagation to reduce the strictness of type propagation.
|
||||
// TODO - this is kinda hacky so that it works in both unit tests and actual decompilation.
|
||||
|
||||
@@ -1064,7 +1064,7 @@ void LoadVarOp::collect_vars(VariableSet& vars) const {
|
||||
/////////////////////////////
|
||||
|
||||
IR2_BranchDelay::IR2_BranchDelay(Kind kind) : m_kind(kind) {
|
||||
assert(m_kind == Kind::NOP || m_kind == Kind::NO_DELAY);
|
||||
assert(m_kind == Kind::NOP || m_kind == Kind::NO_DELAY || m_kind == Kind::UNKNOWN);
|
||||
}
|
||||
|
||||
IR2_BranchDelay::IR2_BranchDelay(Kind kind, Variable var0) : m_kind(kind) {
|
||||
@@ -1129,6 +1129,8 @@ goos::Object IR2_BranchDelay::to_form(const std::vector<DecompilerLabel>& labels
|
||||
assert(m_var[1].has_value());
|
||||
return pretty_print::build_list("set!", m_var[0]->to_form(env),
|
||||
pretty_print::build_list("-", m_var[1]->to_form(env)));
|
||||
case Kind::UNKNOWN:
|
||||
return pretty_print::build_list("unknown-branch-delay!");
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
@@ -1237,6 +1239,78 @@ void BranchOp::collect_vars(VariableSet& vars) const {
|
||||
m_branch_delay.collect_vars(vars);
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// AsmBranchOp
|
||||
/////////////////////////////
|
||||
|
||||
AsmBranchOp::AsmBranchOp(bool likely,
|
||||
IR2_Condition condition,
|
||||
int label,
|
||||
std::shared_ptr<AtomicOp> branch_delay,
|
||||
int my_idx)
|
||||
: AtomicOp(my_idx),
|
||||
m_likely(likely),
|
||||
m_condition(std::move(condition)),
|
||||
m_label(label),
|
||||
m_branch_delay(std::move(branch_delay)) {}
|
||||
|
||||
goos::Object AsmBranchOp::to_form(const std::vector<DecompilerLabel>& labels,
|
||||
const Env& env) const {
|
||||
std::vector<goos::Object> forms;
|
||||
|
||||
if (m_likely) {
|
||||
forms.push_back(pretty_print::to_symbol("bl!"));
|
||||
} else {
|
||||
forms.push_back(pretty_print::to_symbol("b!"));
|
||||
}
|
||||
|
||||
forms.push_back(m_condition.to_form(labels, env));
|
||||
forms.push_back(pretty_print::to_symbol(labels.at(m_label).name));
|
||||
forms.push_back(m_branch_delay->to_form(labels, env));
|
||||
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
bool AsmBranchOp::operator==(const AtomicOp& other) const {
|
||||
if (typeid(BranchOp) != typeid(other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto po = dynamic_cast<const AsmBranchOp*>(&other);
|
||||
assert(po);
|
||||
return m_likely == po->m_likely && m_condition == po->m_condition && m_label == po->m_label &&
|
||||
m_branch_delay == po->m_branch_delay;
|
||||
}
|
||||
|
||||
bool AsmBranchOp::is_sequence_point() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
Variable AsmBranchOp::get_set_destination() const {
|
||||
throw std::runtime_error("AsmBranchOp cannot be treated as a set! operation");
|
||||
}
|
||||
|
||||
void AsmBranchOp::update_register_info() {
|
||||
m_condition.get_regs(&m_read_regs);
|
||||
m_branch_delay->update_register_info();
|
||||
for (auto x : m_branch_delay->read_regs()) {
|
||||
m_read_regs.push_back(x);
|
||||
}
|
||||
|
||||
for (auto x : m_branch_delay->write_regs()) {
|
||||
m_write_regs.push_back(x);
|
||||
}
|
||||
|
||||
for (auto x : m_branch_delay->clobber_regs()) {
|
||||
m_clobber_regs.push_back(x);
|
||||
}
|
||||
}
|
||||
|
||||
void AsmBranchOp::collect_vars(VariableSet& vars) const {
|
||||
m_condition.collect_vars(vars);
|
||||
m_branch_delay->collect_vars(vars);
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// SpecialOp
|
||||
/////////////////////////////
|
||||
|
||||
@@ -541,6 +541,34 @@ class BranchOp : public AtomicOp {
|
||||
IR2_BranchDelay m_branch_delay;
|
||||
};
|
||||
|
||||
/*!
|
||||
* This represents an unknown branch instruction that we think was generated from inline assembly
|
||||
*/
|
||||
class AsmBranchOp : public AtomicOp {
|
||||
public:
|
||||
AsmBranchOp(bool likely,
|
||||
IR2_Condition condition,
|
||||
int label,
|
||||
std::shared_ptr<AtomicOp> branch_delay,
|
||||
int my_idx);
|
||||
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env& env) const override;
|
||||
bool operator==(const AtomicOp& other) const override;
|
||||
bool is_sequence_point() const override;
|
||||
Variable get_set_destination() const override;
|
||||
FormElement* get_as_form(FormPool& pool, const Env& env) const override;
|
||||
void update_register_info() override;
|
||||
TypeState propagate_types_internal(const TypeState& input,
|
||||
const Env& env,
|
||||
DecompilerTypeSystem& dts) override;
|
||||
void collect_vars(VariableSet& vars) const override;
|
||||
|
||||
private:
|
||||
bool m_likely = false;
|
||||
IR2_Condition m_condition;
|
||||
int m_label = -1;
|
||||
std::shared_ptr<AtomicOp> m_branch_delay;
|
||||
};
|
||||
|
||||
/*!
|
||||
* A "special" op has no arguments.
|
||||
* NOP, BREAK, SUSPEND,
|
||||
|
||||
@@ -362,6 +362,24 @@ FormElement* LoadVarOp::get_as_form(FormPool& pool, const Env& env) const {
|
||||
}
|
||||
}
|
||||
|
||||
if (m_src.is_identity() && m_src.get_arg(0).is_label() &&
|
||||
(m_kind == Kind::FLOAT || m_kind == Kind::SIGNED) && m_size == 4) {
|
||||
// try to see if we're loading a constant
|
||||
auto label = env.file->labels.at(m_src.get_arg(0).label());
|
||||
auto label_name = label.name;
|
||||
auto hint = env.label_types().find(label_name);
|
||||
if (hint != env.label_types().end()) {
|
||||
if (hint->second.is_const && hint->second.type_name == "float") {
|
||||
auto word = env.file->words_by_seg.at(label.target_segment).at(label.offset / 4);
|
||||
assert(word.kind == LinkedWord::PLAIN_DATA);
|
||||
float value;
|
||||
memcpy(&value, &word.data, 4);
|
||||
auto float_elt = pool.alloc_single_element_form<ConstantFloatElement>(nullptr, value);
|
||||
return pool.alloc_element<SetVarElement>(m_dst, float_elt, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto source = pool.alloc_single_element_form<SimpleExpressionElement>(nullptr, m_src, m_my_idx);
|
||||
auto load = pool.alloc_single_element_form<LoadSourceElement>(nullptr, source, m_size, m_kind);
|
||||
return pool.alloc_element<SetVarElement>(m_dst, load, true);
|
||||
@@ -403,4 +421,8 @@ FormElement* ConditionalMoveFalseOp::get_as_form(FormPool& pool, const Env&) con
|
||||
FormElement* FunctionEndOp::get_as_form(FormPool& pool, const Env&) const {
|
||||
return pool.alloc_element<AtomicOpElement>(this);
|
||||
}
|
||||
|
||||
FormElement* AsmBranchOp::get_as_form(FormPool& pool, const Env&) const {
|
||||
return pool.alloc_element<AtomicOpElement>(this);
|
||||
}
|
||||
} // namespace decompiler
|
||||
@@ -142,6 +142,7 @@ TP_Type SimpleExpression::get_type(const TypeState& input,
|
||||
return in_type;
|
||||
}
|
||||
case Kind::FPR_TO_GPR:
|
||||
return m_args[0].get_type(input, env, dts);
|
||||
case Kind::DIV_S:
|
||||
case Kind::SUB_S:
|
||||
case Kind::MUL_S:
|
||||
@@ -518,6 +519,12 @@ TP_Type LoadVarOp::get_src_type(const TypeState& input,
|
||||
// this could technically hide loading a different type from inside of a static basic.
|
||||
return TP_Type::make_from_ts(dts.ts.make_typespec("uint"));
|
||||
}
|
||||
|
||||
auto label_name = env.file->labels.at(src.label()).name;
|
||||
auto hint = env.label_types().find(label_name);
|
||||
if (hint != env.label_types().end()) {
|
||||
return TP_Type::make_from_ts(env.dts->parse_type_spec(hint->second.type_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -869,4 +876,15 @@ void FunctionEndOp::mark_function_as_no_return_value() {
|
||||
m_function_has_return_value = false;
|
||||
}
|
||||
|
||||
TypeState AsmBranchOp::propagate_types_internal(const TypeState& input,
|
||||
const Env&,
|
||||
DecompilerTypeSystem&) {
|
||||
// for now, just make everything uint
|
||||
TypeState output = input;
|
||||
for (auto x : m_write_regs) {
|
||||
output.get(x) = TP_Type::make_from_ts("uint");
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace decompiler
|
||||
@@ -107,6 +107,11 @@ class Env {
|
||||
return m_var_names.eliminated_move_op_ids.find(op_id) !=
|
||||
m_var_names.eliminated_move_op_ids.end();
|
||||
}
|
||||
const std::unordered_map<std::string, LabelType>& label_types() const { return m_label_types; }
|
||||
|
||||
void set_label_types(const std::unordered_map<std::string, LabelType>& types) {
|
||||
m_label_types = types;
|
||||
}
|
||||
|
||||
LinkedObjectFile* file = nullptr;
|
||||
DecompilerTypeSystem* dts = nullptr;
|
||||
@@ -129,5 +134,6 @@ class Env {
|
||||
|
||||
std::unordered_map<int, std::vector<TypeHint>> m_typehints;
|
||||
std::unordered_map<std::string, std::string> m_var_remap;
|
||||
std::unordered_map<std::string, LabelType> m_label_types;
|
||||
};
|
||||
} // namespace decompiler
|
||||
@@ -1643,6 +1643,31 @@ void ConstantTokenElement::apply_form(const std::function<void(Form*)>&) {}
|
||||
void ConstantTokenElement::collect_vars(VariableSet&) const {}
|
||||
void ConstantTokenElement::get_modified_regs(RegSet&) const {}
|
||||
|
||||
/////////////////////////////
|
||||
// ConstantFloatElement
|
||||
/////////////////////////////
|
||||
|
||||
ConstantFloatElement::ConstantFloatElement(float value) : m_value(value) {}
|
||||
|
||||
void ConstantFloatElement::apply(const std::function<void(FormElement*)>&) {}
|
||||
void ConstantFloatElement::apply_form(const std::function<void(Form*)>&) {}
|
||||
void ConstantFloatElement::collect_vars(VariableSet&) const {}
|
||||
void ConstantFloatElement::get_modified_regs(RegSet&) const {}
|
||||
|
||||
goos::Object ConstantFloatElement::to_form_internal(const Env&) const {
|
||||
// return goos::Object::make_float(m_value);
|
||||
int rounded = m_value;
|
||||
bool exact_int = ((float)rounded) == m_value;
|
||||
if (m_value == 0.5 || m_value == -0.5 || m_value == 0.0 || m_value == 1.0 || m_value == -1.0 ||
|
||||
exact_int) {
|
||||
return goos::Object::make_float(m_value);
|
||||
} else {
|
||||
u32 value;
|
||||
memcpy(&value, &m_value, 4);
|
||||
return pretty_print::build_list("the-as", "float", fmt::format("#x{:x}", value));
|
||||
}
|
||||
}
|
||||
|
||||
StorePlainDeref::StorePlainDeref(DerefElement* dst,
|
||||
SimpleExpression expr,
|
||||
int my_idx,
|
||||
|
||||
+19
-1
@@ -1027,7 +1027,7 @@ class StringConstantElement : public FormElement {
|
||||
|
||||
class ConstantTokenElement : public FormElement {
|
||||
public:
|
||||
ConstantTokenElement(const std::string& value);
|
||||
explicit ConstantTokenElement(const std::string& value);
|
||||
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;
|
||||
@@ -1043,6 +1043,24 @@ class ConstantTokenElement : public FormElement {
|
||||
std::string m_value;
|
||||
};
|
||||
|
||||
class ConstantFloatElement : public FormElement {
|
||||
public:
|
||||
explicit ConstantFloatElement(float value);
|
||||
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(VariableSet& vars) const override;
|
||||
void get_modified_regs(RegSet& regs) const override;
|
||||
void update_from_stack(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) override;
|
||||
|
||||
private:
|
||||
float m_value;
|
||||
};
|
||||
|
||||
class StorePlainDeref : public FormElement {
|
||||
public:
|
||||
StorePlainDeref(DerefElement* dst,
|
||||
|
||||
@@ -101,14 +101,21 @@ void pop_helper(const std::vector<Variable>& vars,
|
||||
std::vector<size_t> submit_reg_to_var;
|
||||
|
||||
// build submission for stack
|
||||
std::unordered_map<Register, int, Register::hash> reg_counts;
|
||||
for (auto& v : vars) {
|
||||
reg_counts[v.reg()]++;
|
||||
}
|
||||
|
||||
for (size_t var_idx = 0; var_idx < vars.size(); var_idx++) {
|
||||
const auto& var = vars.at(var_idx);
|
||||
auto& ri = env.reg_use().op.at(var.idx());
|
||||
RegSet consumes_to_use = consumes.value_or(ri.consumes);
|
||||
if (consumes_to_use.find(var.reg()) != consumes_to_use.end()) {
|
||||
// we consume the register, so it's safe to try popping.
|
||||
submit_reg_to_var.push_back(var_idx);
|
||||
submit_regs.push_back(var.reg());
|
||||
if (reg_counts.at(var.reg()) == 1) {
|
||||
// we consume the register, so it's safe to try popping.
|
||||
submit_reg_to_var.push_back(var_idx);
|
||||
submit_regs.push_back(var.reg());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -912,6 +919,15 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
|
||||
if (m_src->is_single_element()) {
|
||||
auto src_as_se = dynamic_cast<SimpleExpressionElement*>(m_src->back());
|
||||
if (src_as_se) {
|
||||
if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY &&
|
||||
m_dst.reg().get_kind() == Reg::FPR && src_as_se->expr().get_arg(0).is_int() &&
|
||||
src_as_se->expr().get_arg(0).get_int() == 0) {
|
||||
stack.push_value_to_reg(m_dst,
|
||||
pool.alloc_single_element_form<ConstantFloatElement>(nullptr, 0.0),
|
||||
true, m_var_info);
|
||||
return;
|
||||
}
|
||||
|
||||
if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY &&
|
||||
src_as_se->expr().get_arg(0).is_var()) {
|
||||
// this can happen late in the case of coloring moves which are also gpr -> fpr's
|
||||
@@ -946,7 +962,8 @@ void SetFormFormElement::push_to_stack(const Env&, FormPool&, FormStack& stack)
|
||||
}
|
||||
|
||||
void StoreInSymbolElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) {
|
||||
auto sym = pool.alloc_single_element_form<ConstantTokenElement>(nullptr, m_sym_name);
|
||||
auto sym = pool.alloc_single_element_form<SimpleExpressionElement>(
|
||||
nullptr, SimpleAtom::make_sym_val(m_sym_name).as_expr(), m_my_idx);
|
||||
auto val = pool.alloc_single_element_form<SimpleExpressionElement>(nullptr, m_value, m_my_idx);
|
||||
val->update_children_from_stack(env, pool, stack, true);
|
||||
|
||||
@@ -1780,6 +1797,15 @@ FormElement* ConditionElement::make_generic(const Env&,
|
||||
casted);
|
||||
}
|
||||
|
||||
case IR2_Condition::Kind::LEQ_ZERO_SIGNED: {
|
||||
auto casted = make_cast(source_forms, types, TypeSpec("int"), pool);
|
||||
auto zero = pool.alloc_single_element_form<SimpleAtomElement>(
|
||||
nullptr, SimpleAtom::make_int_constant(0));
|
||||
casted.push_back(zero);
|
||||
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::LEQ),
|
||||
casted);
|
||||
}
|
||||
|
||||
case IR2_Condition::Kind::GEQ_ZERO_SIGNED: {
|
||||
auto casted = make_cast(source_forms, types, TypeSpec("int"), pool);
|
||||
auto zero = pool.alloc_single_element_form<SimpleAtomElement>(
|
||||
@@ -2319,4 +2345,13 @@ void ConstantTokenElement::update_from_stack(const Env&,
|
||||
result->push_back(this);
|
||||
}
|
||||
|
||||
void ConstantFloatElement::update_from_stack(const Env&,
|
||||
FormPool&,
|
||||
FormStack&,
|
||||
std::vector<FormElement*>* result,
|
||||
bool) {
|
||||
mark_popped();
|
||||
result->push_back(this);
|
||||
}
|
||||
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -283,7 +283,8 @@ void ObjectFileDB::ir2_type_analysis_pass() {
|
||||
attempted_functions++;
|
||||
// try type analysis here.
|
||||
auto hints = get_config().type_hints_by_function_by_idx[func.guessed_name.to_string()];
|
||||
if (func.run_type_analysis_ir2(ts, dts, data.linked_data, hints)) {
|
||||
auto label_types = get_config().label_types[data.to_unique_name()];
|
||||
if (func.run_type_analysis_ir2(ts, dts, data.linked_data, hints, label_types)) {
|
||||
successful_functions++;
|
||||
} else {
|
||||
func.warnings.type_prop_warning("Type analysis failed");
|
||||
@@ -469,6 +470,31 @@ std::string ObjectFileDB::ir2_to_file(ObjectFileData& data) {
|
||||
}
|
||||
|
||||
result += '\n';
|
||||
} else if (func.ir2.atomic_ops_succeeded) {
|
||||
auto& ao = func.ir2.atomic_ops;
|
||||
for (size_t i = 0; i < ao->ops.size(); i++) {
|
||||
auto& op = ao->ops.at(i);
|
||||
|
||||
if (!dynamic_cast<FunctionEndOp*>(op.get())) {
|
||||
auto instr_idx = ao->atomic_op_to_instruction.at(i);
|
||||
|
||||
// check for a label to print
|
||||
auto label_id = data.linked_data.get_label_at(seg, (func.start_word + instr_idx) * 4);
|
||||
if (label_id != -1) {
|
||||
result += fmt::format("(label {})\n", data.linked_data.labels.at(label_id).name);
|
||||
}
|
||||
// check for no misaligned labels in code segments.
|
||||
for (int j = 1; j < 4; j++) {
|
||||
assert(data.linked_data.get_label_at(seg, (func.start_word + instr_idx) * 4 + j) ==
|
||||
-1);
|
||||
}
|
||||
|
||||
// print assembly ops.
|
||||
}
|
||||
|
||||
// print instruction
|
||||
result += fmt::format(" {}\n", op->to_string(func.ir2.env));
|
||||
}
|
||||
}
|
||||
|
||||
if (func.ir2.print_debug_forms) {
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace decompiler {
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<AtomicOp> convert_1(const Instruction& i0, int idx);
|
||||
|
||||
//////////////////////
|
||||
// Register Helpers
|
||||
//////////////////////
|
||||
@@ -376,7 +378,8 @@ std::unique_ptr<AtomicOp> make_branch(const IR2_Condition& condition,
|
||||
if (branch_delay.is_known()) {
|
||||
return std::make_unique<BranchOp>(likely, condition, dest_label, branch_delay, my_idx);
|
||||
} else {
|
||||
return nullptr;
|
||||
auto delay_op = std::shared_ptr<AtomicOp>(convert_1(delay, my_idx));
|
||||
return std::make_unique<AsmBranchOp>(likely, condition, dest_label, delay_op, my_idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,5 +125,17 @@ void set_config(const std::string& path_to_config_file) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto label_types_json = read_json_file_from_config(cfg, "label_types_file");
|
||||
for (auto& kv : label_types_json.items()) {
|
||||
auto& obj_name = kv.key();
|
||||
auto& types = kv.value();
|
||||
for (auto& x : types) {
|
||||
const auto& name = x.at(0).get<std::string>();
|
||||
const auto& type_name = x.at(1).get<std::string>();
|
||||
bool is_const = x.at(2).get<bool>();
|
||||
gConfig.label_types[obj_name][name] = {type_name, is_const};
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace decompiler
|
||||
@@ -15,6 +15,11 @@ struct TypeHint {
|
||||
std::string type_name;
|
||||
};
|
||||
|
||||
struct LabelType {
|
||||
std::string type_name;
|
||||
bool is_const = false;
|
||||
};
|
||||
|
||||
struct Config {
|
||||
int game_version = -1;
|
||||
std::vector<std::string> dgo_names;
|
||||
@@ -46,6 +51,8 @@ struct Config {
|
||||
anon_function_types_by_obj_by_id;
|
||||
std::unordered_map<std::string, std::vector<std::string>> function_arg_names;
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> function_var_names;
|
||||
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, LabelType>> label_types;
|
||||
bool run_ir2 = false;
|
||||
};
|
||||
|
||||
|
||||
+105
-46
@@ -770,11 +770,31 @@
|
||||
; (define-extern seek function)
|
||||
; ;;(define-extern xyzw object) ;; unknown type
|
||||
;
|
||||
; ;;(define-extern *random-generator* object) ;; unknown type
|
||||
;
|
||||
; ;;(define-extern rgba object) ;; unknown type
|
||||
(define-extern *random-generator* random-generator) ;; unknown type
|
||||
;
|
||||
|
||||
(deftype rgba (uint32)
|
||||
((r uint8 :offset 0)
|
||||
(g uint8 :offset 8)
|
||||
(b uint8 :offset 16)
|
||||
(a uint8 :offset 24)
|
||||
)
|
||||
:flag-assert #x900000004
|
||||
)
|
||||
|
||||
;; TODO: fields
|
||||
(deftype xyzw (uint128)
|
||||
()
|
||||
:flag-assert #x900000010
|
||||
)
|
||||
|
||||
;; TODO: fields
|
||||
(deftype xyzwh (uint128)
|
||||
()
|
||||
:flag-assert #x900000010
|
||||
)
|
||||
|
||||
|
||||
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -1434,7 +1454,7 @@
|
||||
(define-extern matrix-translate! function)
|
||||
(define-extern matrix-4x4-inverse! function)
|
||||
|
||||
(define-extern vector-sincos! function)
|
||||
|
||||
(define-extern trs-matrix-calc! function)
|
||||
(define-extern transform-matrix-parent-calc! function)
|
||||
(define-extern transform-matrix-calc! function)
|
||||
@@ -1453,6 +1473,81 @@
|
||||
(define-extern eul->quat function)
|
||||
(define-extern matrix->eul function)
|
||||
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; TRIGONOMETRY ;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
(deftype float-type (uint32)
|
||||
()
|
||||
)
|
||||
|
||||
(define-extern radmod (function float float))
|
||||
(define-extern deg- (function float float float))
|
||||
(define-extern deg-diff (function float float float))
|
||||
(define-extern deg-seek (function float float float float))
|
||||
(define-extern deg-seek-smooth (function float float float float float))
|
||||
(define-extern deg-lerp-clamp (function float float float float))
|
||||
(define-extern sin (function float float))
|
||||
(define-extern sin-rad (function float float))
|
||||
(define-extern *sin-poly-vec* vector)
|
||||
(define-extern *sin-poly-vec2* vector)
|
||||
(define-extern vector-sin-rad! (function vector vector vector))
|
||||
(define-extern cos-rad (function float float))
|
||||
(define-extern *cos-poly-vec* vector)
|
||||
(define-extern vector-cos-rad! (function vector vector vector))
|
||||
(define-extern vector-sincos-rad! (function vector vector vector int))
|
||||
(define-extern vector-sincos! (function vector vector vector int))
|
||||
(define-extern tan-rad (function float float))
|
||||
(define-extern vector-rad<-vector-deg! (function vector vector none))
|
||||
(define-extern vector-rad<-vector-deg/2! (function vector vector int))
|
||||
(define-extern atan0 (function float float float))
|
||||
(define-extern atan-series-rad (function float float))
|
||||
(define-extern atan-rad (function float float))
|
||||
(define-extern atan2-rad (function float float float))
|
||||
(define-extern acos-rad (function float float))
|
||||
(define-extern acos (function float float))
|
||||
|
||||
(define-extern coserp (function float float float float))
|
||||
(define-extern sinerp-clamp (function float float float float))
|
||||
;;(define-extern exp-slead object) ;; unknown type
|
||||
(define-extern coserp180-clamp (function float float float float))
|
||||
;; unknown type
|
||||
(define-extern exp (function float float))
|
||||
|
||||
|
||||
|
||||
(define-extern deg-seek (function float float float float))
|
||||
(define-extern coserp180 (function float float float float))
|
||||
(define-extern sign (function float float))
|
||||
(define-extern sinerp (function float float float float))
|
||||
;; unknown type
|
||||
(define-extern ease-in-out (function int int float))
|
||||
|
||||
|
||||
(define-extern asin (function float float))
|
||||
;;(define-extern sincos-table object) ;; unknown type
|
||||
;;(define-extern exp-strail object) ;; unknown type
|
||||
|
||||
(define-extern coserp-clamp (function float float float float))
|
||||
(define-extern tan (function float float))
|
||||
|
||||
;; ;; unknown type
|
||||
|
||||
;;(define-extern binary-table object) ;; unknown type
|
||||
|
||||
(define-extern sincos! (function (pointer float) float int))
|
||||
(define-extern sincos-rad! (function (pointer float) float int))
|
||||
|
||||
|
||||
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; GSOUND-H ;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
|
||||
|
||||
;(define-extern sound-rpc-set-falloff-curve object) ;; unknown type
|
||||
;;(define-extern *sound-bank-2* object) ;; unknown type
|
||||
@@ -31720,8 +31815,8 @@
|
||||
;;(define-extern transformq object) ;; unknown type
|
||||
;;(define-extern trsqv object) ;; unknown type
|
||||
;;(define-extern trsq object) ;; unknown type
|
||||
(define-extern deg-diff function)
|
||||
(define-extern vector-y-angle function)
|
||||
|
||||
(define-extern vector-y-angle (function vector float))
|
||||
|
||||
|
||||
(define-extern quaternion-zero! function)
|
||||
@@ -31775,18 +31870,13 @@
|
||||
(define-extern vector-y-quaternion! function)
|
||||
(define-extern vector-rotate-y! function)
|
||||
(define-extern quaternion-i! function)
|
||||
(define-extern sincos-rad! function)
|
||||
(define-extern vector-sincos-rad! function)
|
||||
|
||||
(define-extern atan-series-rad function)
|
||||
(define-extern atan2-rad function)
|
||||
|
||||
|
||||
|
||||
(define-extern quaternion-from-two-vectors-max-angle! function)
|
||||
|
||||
(define-extern vector-rad<-vector-deg/2! function)
|
||||
(define-extern vector-sin-rad! function)
|
||||
(define-extern acos-rad function)
|
||||
|
||||
(define-extern acos function)
|
||||
(define-extern vector-reflect-flat! function)
|
||||
(define-extern forward-down->inv-matrix function)
|
||||
(define-extern circle-test function)
|
||||
@@ -31830,38 +31920,7 @@
|
||||
(define-extern vector-reflect-flat-above! function)
|
||||
(define-extern vector-circle-tangent-new function)
|
||||
|
||||
(define-extern coserp function)
|
||||
(define-extern sinerp-clamp function)
|
||||
;;(define-extern exp-slead object) ;; unknown type
|
||||
(define-extern coserp180-clamp function)
|
||||
;;(define-extern *sin-poly-vec2* object) ;; unknown type
|
||||
(define-extern exp function)
|
||||
(define-extern deg-lerp-clamp function)
|
||||
(define-extern deg- function)
|
||||
(define-extern atan-rad function)
|
||||
(define-extern deg-seek function)
|
||||
(define-extern coserp180 function)
|
||||
(define-extern sign function)
|
||||
(define-extern sinerp function)
|
||||
;;(define-extern *sin-poly-vec* object) ;; unknown type
|
||||
(define-extern ease-in-out function)
|
||||
(define-extern cos-rad function)
|
||||
(define-extern atan0 function)
|
||||
(define-extern asin function)
|
||||
(define-extern vector-cos-rad! function)
|
||||
;;(define-extern sincos-table object) ;; unknown type
|
||||
;;(define-extern exp-strail object) ;; unknown type
|
||||
(define-extern tan-rad function)
|
||||
(define-extern coserp-clamp function)
|
||||
(define-extern tan function)
|
||||
(define-extern sin-rad function)
|
||||
;;(define-extern float-type object) ;; unknown type
|
||||
(define-extern deg-seek-smooth function)
|
||||
;;(define-extern binary-table object) ;; unknown type
|
||||
;;(define-extern *cos-poly-vec* object) ;; unknown type
|
||||
(define-extern sincos! function)
|
||||
(define-extern vector-rad<-vector-deg! function)
|
||||
(define-extern radmod function)
|
||||
|
||||
;
|
||||
;;(define-extern vif-stat object) ;; unknown type
|
||||
;;(define-extern vif-fbrst object) ;; unknown type
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"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]
|
||||
]
|
||||
}
|
||||
@@ -345,7 +345,41 @@
|
||||
"previous-brother":{
|
||||
"args":["proc"],
|
||||
"vars":{"v1-0":"parent", "v1-2":"child"}
|
||||
},
|
||||
|
||||
"deg-seek":{
|
||||
"args":["in", "target", "max-diff"],
|
||||
"vars":{"v1-1":"in-int", "a0-2":"target-int", "a1-2":"max-diff-int", "a2-1":"diff", "a3-0":"abs-diff"}
|
||||
},
|
||||
|
||||
"deg-seek-smooth":{
|
||||
"args":["in", "target", "max-diff", "amount"],
|
||||
"vars":{"f0-1":"step"}
|
||||
},
|
||||
|
||||
"deg-lerp-clamp":{
|
||||
"args":["min-val", "max-val", "in"]
|
||||
},
|
||||
|
||||
"sinerp-clamp":{
|
||||
"args":["minimum", "maximum", "amount"]
|
||||
},
|
||||
|
||||
"coserp-clamp":{
|
||||
"args":["minimum", "maximum", "amount"]
|
||||
},
|
||||
"coserp":{
|
||||
"args":["minimum", "maximum", "amount"]
|
||||
},
|
||||
|
||||
"coserp180-clamp":{
|
||||
"args":["minimum", "maximum", "amount"]
|
||||
},
|
||||
"coserp180":{
|
||||
"args":["minimum", "maximum", "amount"]
|
||||
},
|
||||
"ease-in-out":{
|
||||
"args":["total", "progress"]
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+57
-1
@@ -27,5 +27,61 @@
|
||||
## `dgo-h`: **Done**
|
||||
- Just type definitions. These don't seem to match the version of DGO files found in the game, so maybe this is outdated? Also GOAL never sees DGOs, they are always processed on the IOP.
|
||||
|
||||
## `gstate`:
|
||||
## `gstate`: **Done**
|
||||
- Doing a `go` from a non-main thread of the process that is changing state is implemented a tiny bit differently. I don't think it should matter.
|
||||
|
||||
# ENGINE
|
||||
|
||||
## `types-h`: **Done**
|
||||
- Just some bitfield types.
|
||||
|
||||
## `vu1-macros`: **Done**
|
||||
- Empty
|
||||
|
||||
## `math`: **Done**
|
||||
- The VU random generator has been rewritten, it used the PS2's (very bad) random hardware
|
||||
- The "31 bit" integer random generator was rewritten, it used very strange inline assembly.
|
||||
|
||||
## `vector-h`: **Done**
|
||||
- Has some very simple, manually rewritten VU functions
|
||||
|
||||
## `gravity-h`: **Done**
|
||||
- Empty
|
||||
|
||||
## `bounding-box-h`: **Done**
|
||||
|
||||
## `matrix-h`: **Done**
|
||||
- `matrix-copy!` is a good example of where the OpenGOAL compiler's register allocator does poorly.
|
||||
|
||||
## `quaternion-h`: **Done**
|
||||
- No comments
|
||||
|
||||
## `euler-h`: **Done**
|
||||
- Uses boxed arrays
|
||||
|
||||
## `transform-h`: **Done**
|
||||
- No comments
|
||||
|
||||
## `geometry-h`: **Done**
|
||||
- No comments
|
||||
|
||||
## `trigonometry-h`: **Done**
|
||||
- Empty
|
||||
|
||||
## `transformq-h`:
|
||||
- Needs stack stuff
|
||||
|
||||
## `bounding-box`:
|
||||
|
||||
## `matrix`:
|
||||
|
||||
## `transform`:
|
||||
|
||||
## `quaternion`:
|
||||
|
||||
## `euler`:
|
||||
|
||||
## `geometry`:
|
||||
|
||||
## `trigonometry`: **Done**
|
||||
- `sincos!` and `sincos-rad!` have a bug where cosine is slightly off
|
||||
|
||||
@@ -1419,6 +1419,23 @@ The outer product is computed like so (only x,y,z components are operated on):
|
||||
```
|
||||
Wrapper around `vblendps` (VEX xmm128 version) instruction. The `mask` must evaluate to a constant integer at compile time. The integer must be in the range of 0-15.
|
||||
|
||||
## `.itof.vf` and `.ftoi.vf`
|
||||
```
|
||||
(.itof.vf dst src [:mask mask-val] [:color #t|#f])
|
||||
(.ftoi.vf dst src [:mask mask-val] [:color #t|#f])
|
||||
```
|
||||
|
||||
Wrapper around `vcvtdq2ps` and `vcvtps2dq` to convert packed 32-bit signed integers to packed 32-bit floats and back. The `mask` and `color` arguments behave like other assembly operations.
|
||||
|
||||
## `.pw.sra`, `.pw.srl`, and `pw.sll`
|
||||
```
|
||||
(.pw.sra dst src shift-amount [:mask mask-val] [:color #t|#f])
|
||||
(.pw.srl dst src shift-amount [:mask mask-val] [:color #t|#f])
|
||||
(.pw.sll dst src shift-amount [:mask mask-val] [:color #t|#f])
|
||||
```
|
||||
|
||||
Wrapper around `vpsrld`, `vpsrad`, and `vpslld`. Does shifts on each of the 4 32-bit integers in the register.
|
||||
|
||||
# Compiler Forms - Unsorted
|
||||
|
||||
## `let`
|
||||
|
||||
@@ -14,6 +14,17 @@ _stack_call_linux:
|
||||
pop rax
|
||||
; align stack
|
||||
sub rsp, 8
|
||||
|
||||
sub rsp, 128
|
||||
movaps [rsp], xmm8
|
||||
movaps [rsp + 16], xmm9
|
||||
movaps [rsp + 32], xmm10
|
||||
movaps [rsp + 48], xmm11
|
||||
movaps [rsp + 64], xmm12
|
||||
movaps [rsp + 80], xmm13
|
||||
movaps [rsp + 96], xmm14
|
||||
movaps [rsp + 112], xmm15
|
||||
|
||||
; create stack array of arguments
|
||||
push r11
|
||||
push r10
|
||||
@@ -23,6 +34,7 @@ _stack_call_linux:
|
||||
push rdx
|
||||
push rsi
|
||||
push rdi
|
||||
|
||||
; set first argument
|
||||
mov rdi, rsp
|
||||
; call function
|
||||
@@ -36,6 +48,17 @@ _stack_call_linux:
|
||||
pop r9
|
||||
pop r10
|
||||
pop r11
|
||||
|
||||
movaps xmm8, [rsp]
|
||||
movaps xmm9, [rsp + 16]
|
||||
movaps xmm10, [rsp + 32]
|
||||
movaps xmm11, [rsp + 48]
|
||||
movaps xmm12, [rsp + 64]
|
||||
movaps xmm13, [rsp + 80]
|
||||
movaps xmm14, [rsp + 96]
|
||||
movaps xmm15, [rsp + 112]
|
||||
add rsp, 128
|
||||
|
||||
; restore stack
|
||||
add rsp, 8
|
||||
; return!
|
||||
@@ -48,6 +71,16 @@ _stack_call_win32:
|
||||
; to make sure the stack frame is aligned
|
||||
sub rsp, 8
|
||||
|
||||
sub rsp, 128
|
||||
movaps [rsp], xmm8
|
||||
movaps [rsp + 16], xmm9
|
||||
movaps [rsp + 32], xmm10
|
||||
movaps [rsp + 48], xmm11
|
||||
movaps [rsp + 64], xmm12
|
||||
movaps [rsp + 80], xmm13
|
||||
movaps [rsp + 96], xmm14
|
||||
movaps [rsp + 112], xmm15
|
||||
|
||||
; push all registers and create the register array on the stack
|
||||
push r11
|
||||
push r10
|
||||
@@ -76,6 +109,17 @@ _stack_call_win32:
|
||||
pop r9
|
||||
pop r10
|
||||
pop r11
|
||||
|
||||
movaps xmm8, [rsp]
|
||||
movaps xmm9, [rsp + 16]
|
||||
movaps xmm10, [rsp + 32]
|
||||
movaps xmm11, [rsp + 48]
|
||||
movaps xmm12, [rsp + 64]
|
||||
movaps xmm13, [rsp + 80]
|
||||
movaps xmm14, [rsp + 96]
|
||||
movaps xmm15, [rsp + 112]
|
||||
add rsp, 128
|
||||
|
||||
add rsp, 8
|
||||
ret
|
||||
|
||||
|
||||
@@ -42,8 +42,19 @@
|
||||
|
||||
(defun matrix-copy! ((dst matrix) (src matrix))
|
||||
"Copy src to dst."
|
||||
;; actual implementation is in assembly, unrolled quad copies, loads/stores spaced out.
|
||||
(dotimes (i 16 dst)
|
||||
(set! (-> dst data i) (-> src data i))
|
||||
(rlet ((r0 :class vf)
|
||||
(r1 :class vf)
|
||||
(r2 :class vf)
|
||||
(r3 :class vf)
|
||||
)
|
||||
(.lvf r0 (&-> src quad 0))
|
||||
(.lvf r1 (&-> src quad 1))
|
||||
(.lvf r2 (&-> src quad 2))
|
||||
(.lvf r3 (&-> src quad 3))
|
||||
(.svf (&-> dst quad 0) r0)
|
||||
(.svf (&-> dst quad 1) r1)
|
||||
(.svf (&-> dst quad 2) r2)
|
||||
(.svf (&-> dst quad 3) r3)
|
||||
)
|
||||
)
|
||||
dst
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -423,9 +423,9 @@
|
||||
:size-assert #x28
|
||||
:flag-assert #xb00000028
|
||||
(:methods
|
||||
(dummy-9 () none 9)
|
||||
(dummy-10 () none 10)
|
||||
)
|
||||
(dummy-9 () none 9)
|
||||
(dummy-10 () none 10)
|
||||
)
|
||||
)
|
||||
|
||||
(deftype cylinder-flat (structure)
|
||||
@@ -438,9 +438,9 @@
|
||||
:size-assert #x28
|
||||
:flag-assert #xb00000028
|
||||
(:methods
|
||||
(dummy-9 () none 9)
|
||||
(dummy-10 () none 10)
|
||||
)
|
||||
(dummy-9 () none 9)
|
||||
(dummy-10 () none 10)
|
||||
)
|
||||
)
|
||||
|
||||
;; vector-h
|
||||
@@ -489,52 +489,122 @@
|
||||
|
||||
(defmacro set-vector! (v xv yv zv wv)
|
||||
`(begin
|
||||
(set! (-> ,v x) ,xv)
|
||||
(set! (-> ,v y) ,yv)
|
||||
(set! (-> ,v z) ,zv)
|
||||
(set! (-> ,v w) ,wv))
|
||||
(set! (-> ,v x) ,xv)
|
||||
(set! (-> ,v y) ,yv)
|
||||
(set! (-> ,v z) ,zv)
|
||||
(set! (-> ,v w) ,wv))
|
||||
)
|
||||
|
||||
(defun vector-dot ((a vector) (b vector))
|
||||
"Take the dot product of two vectors.
|
||||
Only does the x, y, z compoments.
|
||||
Originally handwritten assembly to space out loads and use FPU accumulator"
|
||||
(declare (inline))
|
||||
(let ((result 0.))
|
||||
(+! result (* (-> a x) (-> b x)))
|
||||
(+! result (* (-> a y) (-> b y)))
|
||||
(+! result (* (-> a z) (-> b z)))
|
||||
result
|
||||
)
|
||||
)
|
||||
(declare (inline))
|
||||
(let ((result 0.))
|
||||
(+! result (* (-> a x) (-> b x)))
|
||||
(+! result (* (-> a y) (-> b y)))
|
||||
(+! result (* (-> a z) (-> b z)))
|
||||
result
|
||||
)
|
||||
)
|
||||
|
||||
(defun vector-dot-vu ((a vector) (b vector))
|
||||
"Take the dot product of two vectors.
|
||||
Only does the x, y, z components.
|
||||
Originally implemented using VU macro ops"
|
||||
(declare (inline))
|
||||
(vector-dot a b)
|
||||
(declare (inline))
|
||||
(rlet ((vf1 :class vf)
|
||||
(vf2 :class vf)
|
||||
(result :class fpr :type float))
|
||||
;; (.lqc2 vf1 0 arg0)
|
||||
(.lvf vf1 a)
|
||||
;; (.lqc2 vf2 0 arg1)
|
||||
(.lvf vf2 b)
|
||||
;; (.vmul.xyzw vf1 vf1 vf2)
|
||||
(.mul.vf vf1 vf1 vf2)
|
||||
;; (.vaddy.x vf1 vf1 vf1)
|
||||
(.add.y.vf vf1 vf1 vf1 :mask #b1)
|
||||
;; (.vaddz.x vf1 vf1 vf1)
|
||||
(.add.z.vf vf1 vf1 vf1 :mask #b1)
|
||||
;; (.qmfc2.i v0-0 vf1)
|
||||
(.mov result vf1)
|
||||
result
|
||||
)
|
||||
)
|
||||
|
||||
(defun vector4-dot ((a vector) (b vector))
|
||||
"Take the dot product of two vectors.
|
||||
Does the x, y, z, and w compoments"
|
||||
(declare (inline))
|
||||
(let ((result 0.))
|
||||
(+! result (* (-> a x) (-> b x)))
|
||||
(+! result (* (-> a y) (-> b y)))
|
||||
(+! result (* (-> a z) (-> b z)))
|
||||
(+! result (* (-> a w) (-> b w)))
|
||||
result
|
||||
(declare (inline))
|
||||
(let ((result 0.))
|
||||
(+! result (* (-> a x) (-> b x)))
|
||||
(+! result (* (-> a y) (-> b y)))
|
||||
(+! result (* (-> a z) (-> b z)))
|
||||
(+! result (* (-> a w) (-> b w)))
|
||||
result
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro print-vf (vf &key (name #f))
|
||||
`(let ((temp (new 'stack 'vector)))
|
||||
(.svf temp ,vf)
|
||||
,(if name
|
||||
`(format #t "~A: ~`vector`P~%" (quote ,name) temp)
|
||||
`(format #t "~`vector`P~%" temp)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro print-vf-hex (vf)
|
||||
`(let ((temp (new 'stack 'vector4w)))
|
||||
(.svf temp ,vf)
|
||||
(format #t "~`vector4w`P~%" temp)
|
||||
)
|
||||
)
|
||||
|
||||
(defun vector4-dot-vu ((a vector) (b vector))
|
||||
"Take the dot product of two vectors.
|
||||
Does the x, y, z, and w compoments
|
||||
Originally implemented using VU macro ops"
|
||||
(declare (inline))
|
||||
(vector4-dot a b)
|
||||
(declare (inline))
|
||||
(rlet ((vf1 :class vf)
|
||||
(vf2 :class vf)
|
||||
(vf3 :class vf)
|
||||
(acc :class vf)
|
||||
(vf0 :class vf)
|
||||
(result :class fpr :type float))
|
||||
(.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0))
|
||||
;; (.lqc2 vf1 0 arg0)
|
||||
(.lvf vf1 a)
|
||||
;; (.lqc2 vf2 0 arg1)
|
||||
(.lvf vf2 b)
|
||||
|
||||
;; (.vmul.xyzw vf1 vf1 vf2)
|
||||
;; set vf1 to element-wise products
|
||||
(.mul.vf vf1 vf1 vf2)
|
||||
|
||||
;; (.vaddw.x vf3 vf0 vf0)
|
||||
;; set vf3x to 1
|
||||
(.xor.vf vf3 vf3 vf3)
|
||||
(.add.w.vf vf3 vf0 vf0 :mask #b1)
|
||||
|
||||
;; (.vmulax.x acc vf3 vf1)
|
||||
;; acc.x is now (xa * xb)
|
||||
(.mul.x.vf acc vf3 vf1 :mask #b1)
|
||||
|
||||
;; (.vmadday.x acc vf3 vf1)
|
||||
;; acc += thing
|
||||
(.add.mul.y.vf acc vf3 vf1 acc :mask #b1)
|
||||
|
||||
;; (.vmaddaz.x acc vf3 vf1)
|
||||
(.add.mul.z.vf acc vf3 vf1 acc :mask #b1)
|
||||
|
||||
;; (.vmaddw.x vf1 vf3 vf1)
|
||||
(.add.mul.w.vf vf1 vf3 vf1 acc :mask #b1)
|
||||
;; (.qmfc2.i v0-0 vf1)
|
||||
(.mov result vf1)
|
||||
result
|
||||
)
|
||||
)
|
||||
|
||||
(defun vector+! ((dst vector) (a vector) (b vector))
|
||||
@@ -544,18 +614,18 @@
|
||||
(vf1 :class vf :reset-here #t)
|
||||
(vf2 :class vf :reset-here #t)
|
||||
(vf3 :class vf :reset-here #t))
|
||||
; load vectors
|
||||
(.lvf vf2 a)
|
||||
(.lvf vf3 b)
|
||||
; set vf0 to zero
|
||||
(.xor.vf vf0 vf0 vf0)
|
||||
; add
|
||||
(.add.vf vf1 vf2 vf3)
|
||||
; set w = 0
|
||||
(.blend.vf vf1 vf1 vf0 :mask #b1000)
|
||||
; store
|
||||
(.svf dst vf1)
|
||||
)
|
||||
;; load vectors
|
||||
(.lvf vf2 a)
|
||||
(.lvf vf3 b)
|
||||
;; set vf0 to zero
|
||||
(.xor.vf vf0 vf0 vf0)
|
||||
;; add
|
||||
(.add.vf vf1 vf2 vf3)
|
||||
;; set w = 0
|
||||
(.blend.vf vf1 vf1 vf0 :mask #b1000)
|
||||
;; store
|
||||
(.svf dst vf1)
|
||||
)
|
||||
dst
|
||||
)
|
||||
|
||||
@@ -566,18 +636,18 @@
|
||||
(vf1 :class vf :reset-here #t)
|
||||
(vf2 :class vf :reset-here #t)
|
||||
(vf3 :class vf :reset-here #t))
|
||||
; load vectors
|
||||
(.lvf vf2 a)
|
||||
(.lvf vf3 b)
|
||||
; set vf0 to zero
|
||||
(.xor.vf vf0 vf0 vf0)
|
||||
; subtract
|
||||
(.sub.vf vf1 vf2 vf3)
|
||||
; set w = 0
|
||||
(.blend.vf vf1 vf1 vf0 :mask #b1000)
|
||||
; store
|
||||
(.svf dst vf1)
|
||||
)
|
||||
;; load vectors
|
||||
(.lvf vf2 a)
|
||||
(.lvf vf3 b)
|
||||
;; set vf0 to zero
|
||||
(.xor.vf vf0 vf0 vf0)
|
||||
;; subtract
|
||||
(.sub.vf vf1 vf2 vf3)
|
||||
;; set w = 0
|
||||
(.blend.vf vf1 vf1 vf0 :mask #b1000)
|
||||
;; store
|
||||
(.svf dst vf1)
|
||||
)
|
||||
dst
|
||||
)
|
||||
|
||||
@@ -585,11 +655,11 @@
|
||||
"Set xyzw to 0."
|
||||
(declare (inline))
|
||||
(rlet ((vf1 :class vf :reset-here #t))
|
||||
; set vf1 = 0
|
||||
(.xor.vf vf1 vf1 vf1)
|
||||
; store the 0
|
||||
(.svf dest vf1)
|
||||
)
|
||||
;; set vf1 = 0
|
||||
(.xor.vf vf1 vf1 vf1)
|
||||
;; store the 0
|
||||
(.svf dest vf1)
|
||||
)
|
||||
dest
|
||||
)
|
||||
|
||||
@@ -606,9 +676,9 @@
|
||||
The vectors must be aligned."
|
||||
(declare (inline))
|
||||
(rlet ((vf1 :class vf :reset-here #t))
|
||||
(.lvf vf1 src)
|
||||
(.svf dst vf1)
|
||||
)
|
||||
(.lvf vf1 src)
|
||||
(.svf dst vf1)
|
||||
)
|
||||
dst
|
||||
)
|
||||
|
||||
|
||||
@@ -191,6 +191,10 @@
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro defun-extern (function-name &rest type-info)
|
||||
`(define-extern ,function-name (function ,@type-info))
|
||||
)
|
||||
|
||||
;; Define a new function, but only if we're debugging.
|
||||
;; TODO - should place the function in the debug segment!
|
||||
(defmacro defun-debug (name bindings &rest body)
|
||||
|
||||
@@ -72,6 +72,16 @@ class Compiler {
|
||||
emitter::Register::VF_ELEMENT broadcastElement,
|
||||
Env* env);
|
||||
|
||||
Val* compile_asm_vf_math2(const goos::Object& form,
|
||||
const goos::Object& rest,
|
||||
IR_VFMath2Asm::Kind kind,
|
||||
Env* env);
|
||||
|
||||
Val* compile_asm_vf_math2_imm_u8(const goos::Object& form,
|
||||
const goos::Object& rest,
|
||||
IR_VFMath2Asm::Kind kind,
|
||||
Env* env);
|
||||
|
||||
Val* compile_asm_vf_math4_two_operation(const goos::Object& form,
|
||||
const goos::Object& rest,
|
||||
IR_VFMath3Asm::Kind first_op_kind,
|
||||
@@ -150,6 +160,11 @@ class Compiler {
|
||||
Env* env,
|
||||
bool call_constructor);
|
||||
|
||||
StaticResult fill_static_array(const goos::Object& form,
|
||||
const goos::Object& rest,
|
||||
bool boxed,
|
||||
Env* env);
|
||||
|
||||
TypeSystem m_ts;
|
||||
std::unique_ptr<GlobalEnv> m_global_env = nullptr;
|
||||
std::unique_ptr<None> m_none = nullptr;
|
||||
@@ -360,6 +375,11 @@ 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_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);
|
||||
Val* compile_asm_pw_srl(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_asm_pw_sra(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
|
||||
// Atoms
|
||||
|
||||
|
||||
@@ -110,6 +110,14 @@ void regset_common(emitter::ObjectGenerator* gen,
|
||||
} else if (src_class == RegClass::GPR_64 && dst_class == RegClass::FLOAT) {
|
||||
// gpr -> xmm 1x
|
||||
gen->add_instr(IGen::movd_xmm32_gpr32(dst_reg, src_reg), irec);
|
||||
} else if (src_class == RegClass::VECTOR_FLOAT && dst_class == RegClass::FLOAT) {
|
||||
gen->add_instr(IGen::mov_xmm32_xmm32(dst_reg, src_reg), irec);
|
||||
} else if (src_class == RegClass::FLOAT && dst_class == RegClass::VECTOR_FLOAT) {
|
||||
gen->add_instr(IGen::mov_xmm32_xmm32(dst_reg, src_reg), irec);
|
||||
} else if (src_class == RegClass::GPR_64 && dst_class == RegClass::VECTOR_FLOAT) {
|
||||
gen->add_instr(IGen::movd_xmm32_gpr32(dst_reg, src_reg), irec);
|
||||
} else if (src_class == RegClass::VECTOR_FLOAT && dst_class == RegClass::GPR_64) {
|
||||
gen->add_instr(IGen::movd_gpr32_xmm32(dst_reg, src_reg), irec);
|
||||
} else {
|
||||
assert(false); // unhandled move.
|
||||
}
|
||||
@@ -1426,6 +1434,99 @@ void IR_VFMath3Asm::do_codegen(emitter::ObjectGenerator* gen,
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// AsmVF2
|
||||
///////////////////////
|
||||
|
||||
IR_VFMath2Asm::IR_VFMath2Asm(bool use_color,
|
||||
const RegVal* dst,
|
||||
const RegVal* src,
|
||||
Kind kind,
|
||||
std::optional<int64_t> imm)
|
||||
: IR_Asm(use_color), m_dst(dst), m_src(src), m_kind(kind), m_imm(std::move(imm)) {}
|
||||
|
||||
std::string IR_VFMath2Asm::print() {
|
||||
std::string function;
|
||||
bool use_imm = false;
|
||||
switch (m_kind) {
|
||||
case Kind::ITOF:
|
||||
function = ".itof.vf";
|
||||
break;
|
||||
case Kind::FTOI:
|
||||
function = ".ftoi.vf";
|
||||
break;
|
||||
case Kind::PW_SLL:
|
||||
use_imm = true;
|
||||
function = ".pw.sll";
|
||||
break;
|
||||
case Kind::PW_SRL:
|
||||
use_imm = true;
|
||||
function = ".pw.srl";
|
||||
break;
|
||||
case Kind::PW_SRA:
|
||||
use_imm = true;
|
||||
function = ".pw.sra";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (use_imm) {
|
||||
assert(m_imm.has_value());
|
||||
return fmt::format("{}{} {}, {}, {}", function, get_color_suffix_string(), m_dst->print(),
|
||||
m_src->print(), *m_imm);
|
||||
} else {
|
||||
return fmt::format("{}{} {}, {}", function, get_color_suffix_string(), m_dst->print(),
|
||||
m_src->print());
|
||||
}
|
||||
}
|
||||
|
||||
RegAllocInstr IR_VFMath2Asm::to_rai() {
|
||||
RegAllocInstr rai;
|
||||
if (m_use_coloring) {
|
||||
rai.write.push_back(m_dst->ireg());
|
||||
rai.read.push_back(m_src->ireg());
|
||||
}
|
||||
return rai;
|
||||
}
|
||||
|
||||
void IR_VFMath2Asm::do_codegen(emitter::ObjectGenerator* gen,
|
||||
const AllocationResult& allocs,
|
||||
emitter::IR_Record irec) {
|
||||
auto dst = get_reg_asm(m_dst, allocs, irec, m_use_coloring);
|
||||
auto src = get_reg_asm(m_src, allocs, irec, m_use_coloring);
|
||||
|
||||
switch (m_kind) {
|
||||
case Kind::ITOF:
|
||||
gen->add_instr(IGen::itof_vf(dst, src), irec);
|
||||
break;
|
||||
case Kind::FTOI:
|
||||
gen->add_instr(IGen::ftoi_vf(dst, src), irec);
|
||||
break;
|
||||
case Kind::PW_SLL:
|
||||
// you are technically allowed to put values > 32 in here.
|
||||
assert(m_imm.has_value());
|
||||
assert(*m_imm >= 0);
|
||||
assert(*m_imm <= 255);
|
||||
gen->add_instr(IGen::pw_sll(dst, src, *m_imm), irec);
|
||||
break;
|
||||
case Kind::PW_SRL:
|
||||
assert(m_imm.has_value());
|
||||
assert(*m_imm >= 0);
|
||||
assert(*m_imm <= 255);
|
||||
gen->add_instr(IGen::pw_srl(dst, src, *m_imm), irec);
|
||||
break;
|
||||
case Kind::PW_SRA:
|
||||
assert(m_imm.has_value());
|
||||
assert(*m_imm >= 0);
|
||||
assert(*m_imm <= 255);
|
||||
gen->add_instr(IGen::pw_sra(dst, src, *m_imm), irec);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Blend VF
|
||||
|
||||
IR_BlendVF::IR_BlendVF(bool use_color,
|
||||
|
||||
@@ -546,6 +546,27 @@ class IR_VFMath3Asm : public IR_Asm {
|
||||
Kind m_kind;
|
||||
};
|
||||
|
||||
class IR_VFMath2Asm : public IR_Asm {
|
||||
public:
|
||||
enum class Kind { ITOF, FTOI, PW_SLL, PW_SRL, PW_SRA };
|
||||
IR_VFMath2Asm(bool use_color,
|
||||
const RegVal* dst,
|
||||
const RegVal* src,
|
||||
Kind kind,
|
||||
std::optional<int64_t> = std::nullopt);
|
||||
std::string print() override;
|
||||
RegAllocInstr to_rai() override;
|
||||
void do_codegen(emitter::ObjectGenerator* gen,
|
||||
const AllocationResult& allocs,
|
||||
emitter::IR_Record irec) override;
|
||||
|
||||
protected:
|
||||
const RegVal* m_dst = nullptr;
|
||||
const RegVal* m_src = nullptr;
|
||||
Kind m_kind;
|
||||
std::optional<int64_t> m_imm;
|
||||
};
|
||||
|
||||
class IR_BlendVF : public IR_Asm {
|
||||
public:
|
||||
IR_BlendVF(bool use_color, const RegVal* dst, const RegVal* src1, const RegVal* src2, u8 mask);
|
||||
|
||||
@@ -267,6 +267,8 @@ std::vector<goos::Object> Compiler::get_list_as_vector(const goos::Object& o,
|
||||
if (max_length >= 0 && n >= max_length) {
|
||||
if (rest_out) {
|
||||
*rest_out = *cur;
|
||||
} else {
|
||||
throw std::runtime_error("get_list_as_vector would discard arguments");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -282,7 +282,6 @@ Val* Compiler::compile_asm_lvf(const goos::Object& form, const goos::Object& res
|
||||
info.reg = RegClass::VECTOR_FLOAT;
|
||||
if (as_co) {
|
||||
// can do a clever offset here
|
||||
assert(false);
|
||||
env->emit_ir<IR_LoadConstOffset>(dest, as_co->offset, as_co->base->to_gpr(env), info, color);
|
||||
} else if (as_sv) {
|
||||
if (!color) {
|
||||
@@ -431,6 +430,116 @@ Val* Compiler::compile_asm_vf_math3(const goos::Object& form,
|
||||
return get_none();
|
||||
}
|
||||
|
||||
Val* Compiler::compile_asm_vf_math2(const goos::Object& form,
|
||||
const goos::Object& rest,
|
||||
IR_VFMath2Asm::Kind kind,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// If the entire destination is to be copied, we can optimize out the blend
|
||||
if (mask == 0b1111) {
|
||||
env->emit_ir<IR_VFMath2Asm>(color, dest, src, kind);
|
||||
} else {
|
||||
auto temp_reg = env->make_vfr(dest->type());
|
||||
// Perform the arithmetic operation on the two vectors into a temporary register
|
||||
env->emit_ir<IR_VFMath2Asm>(color, temp_reg, src, kind);
|
||||
// Blend the result back into the destination register using the mask
|
||||
env->emit_ir<IR_BlendVF>(color, dest, dest, temp_reg, mask);
|
||||
}
|
||||
|
||||
return get_none();
|
||||
}
|
||||
|
||||
Val* Compiler::compile_asm_vf_math2_imm_u8(const goos::Object& form,
|
||||
const goos::Object& rest,
|
||||
IR_VFMath2Asm::Kind kind,
|
||||
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}});
|
||||
s64 imm;
|
||||
if (!try_getting_constant_integer(args.unnamed.at(2), &imm, env)) {
|
||||
throw_compiler_error(form, "Could not evaluate {} as a compile-time integer.",
|
||||
args.unnamed.at(2).print());
|
||||
}
|
||||
|
||||
if (imm < 0 || imm > 255) {
|
||||
throw_compiler_error(form, "Immediate {} is invalid. The value {} is out of range for a uint8.",
|
||||
args.unnamed.at(2).print(), imm);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// If the entire destination is to be copied, we can optimize out the blend
|
||||
if (mask == 0b1111) {
|
||||
env->emit_ir<IR_VFMath2Asm>(color, dest, src, kind, imm);
|
||||
} else {
|
||||
auto temp_reg = env->make_vfr(dest->type());
|
||||
// Perform the arithmetic operation on the two vectors into a temporary register
|
||||
env->emit_ir<IR_VFMath2Asm>(color, temp_reg, src, kind, imm);
|
||||
// Blend the result back into the destination register using the mask
|
||||
env->emit_ir<IR_BlendVF>(color, dest, dest, temp_reg, mask);
|
||||
}
|
||||
|
||||
return get_none();
|
||||
}
|
||||
|
||||
Val* Compiler::compile_asm_pw_sll(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
return compile_asm_vf_math2_imm_u8(form, rest, IR_VFMath2Asm::Kind::PW_SLL, env);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_asm_pw_srl(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
return compile_asm_vf_math2_imm_u8(form, rest, IR_VFMath2Asm::Kind::PW_SRL, env);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_asm_pw_sra(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
return compile_asm_vf_math2_imm_u8(form, rest, IR_VFMath2Asm::Kind::PW_SRA, env);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_asm_itof_vf(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
return compile_asm_vf_math2(form, rest, IR_VFMath2Asm::Kind::ITOF, env);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_asm_ftoi_vf(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
return compile_asm_vf_math2(form, rest, IR_VFMath2Asm::Kind::FTOI, env);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_asm_xor_vf(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
return compile_asm_vf_math3(form, rest, IR_VFMath3Asm::Kind::XOR,
|
||||
emitter::Register::VF_ELEMENT::NONE, env);
|
||||
|
||||
@@ -87,6 +87,12 @@ static const std::unordered_map<
|
||||
|
||||
{".div.vf", &Compiler::compile_asm_div_vf},
|
||||
{".sqrt.vf", &Compiler::compile_asm_sqrt_vf},
|
||||
{".itof.vf", &Compiler::compile_asm_itof_vf},
|
||||
{".ftoi.vf", &Compiler::compile_asm_ftoi_vf},
|
||||
|
||||
{".pw.sll", &Compiler::compile_asm_pw_sll},
|
||||
{".pw.srl", &Compiler::compile_asm_pw_srl},
|
||||
{".pw.sra", &Compiler::compile_asm_pw_sra},
|
||||
|
||||
// BLOCK FORMS
|
||||
{"top-level", &Compiler::compile_top_level},
|
||||
|
||||
@@ -182,6 +182,11 @@ Val* Compiler::compile_asm_file(const goos::Object& form, const goos::Object& re
|
||||
printf(" %12s %4.0f", e.first.c_str(), e.second);
|
||||
}
|
||||
printf("\n");
|
||||
} else {
|
||||
auto total_time = total_timer.getMs();
|
||||
if (total_time > 10.0) {
|
||||
fmt::print("[ASM-FILE] {} took {:.2f} ms\n", obj_file_name, total_time);
|
||||
}
|
||||
}
|
||||
|
||||
return get_none();
|
||||
|
||||
@@ -434,69 +434,9 @@ StaticResult Compiler::compile_static(const goos::Object& form, Env* env) {
|
||||
}
|
||||
|
||||
if (unquote(args.at(1)).as_symbol()->name == "boxed-array") {
|
||||
// (new 'static 'boxed-array ...)
|
||||
// get all arguments now
|
||||
args = get_list_as_vector(rest, &constructor_args);
|
||||
if (args.size() < 4) {
|
||||
throw_compiler_error(form,
|
||||
"new static boxed array must have type and min-size arguments");
|
||||
}
|
||||
auto content_type = parse_typespec(args.at(2));
|
||||
s64 min_size;
|
||||
if (!try_getting_constant_integer(args.at(3), &min_size, env)) {
|
||||
throw_compiler_error(form, "The length {} is not valid.", args.at(3).print());
|
||||
}
|
||||
s32 length = std::max(min_size, s64(args.size() - 4));
|
||||
// todo - generalize this array stuff if we ever need other types of static arrays.
|
||||
auto pointer_type = m_ts.make_pointer_typespec(content_type);
|
||||
auto deref_info = m_ts.get_deref_info(pointer_type);
|
||||
assert(deref_info.can_deref);
|
||||
assert(deref_info.mem_deref);
|
||||
auto array_size_bytes = length * deref_info.stride;
|
||||
// todo, segments
|
||||
auto obj = std::make_unique<StaticBasic>(MAIN_SEGMENT, "array");
|
||||
obj->data.resize(16 + array_size_bytes);
|
||||
// 0 - 4 : type tag (set automatically)
|
||||
// 4 - 8 : length
|
||||
memcpy(obj->data.data() + 4, &length, 4);
|
||||
// 8 - 12 allocated length
|
||||
memcpy(obj->data.data() + 8, &length, 4);
|
||||
// 12 - 16 content type
|
||||
obj->add_type_record(content_type.base_type(), 12);
|
||||
|
||||
// now add arguments:
|
||||
for (size_t i = 4; i < args.size(); i++) {
|
||||
int arg_idx = i - 4;
|
||||
int elt_offset = 16 + arg_idx * deref_info.stride;
|
||||
auto sr = compile_static(args.at(i), env);
|
||||
if (is_integer(content_type)) {
|
||||
typecheck(form, TypeSpec("integer"), sr.typespec());
|
||||
} else {
|
||||
typecheck(form, content_type, sr.typespec());
|
||||
}
|
||||
if (sr.is_symbol()) {
|
||||
assert(deref_info.stride == 4);
|
||||
obj->add_symbol_record(sr.symbol_name(), elt_offset);
|
||||
u32 symbol_placeholder = 0xffffffff;
|
||||
memcpy(obj->data.data() + elt_offset, &symbol_placeholder, 4);
|
||||
} else if (sr.is_reference()) {
|
||||
assert(deref_info.stride == 4);
|
||||
obj->add_pointer_record(elt_offset, sr.reference(), sr.reference()->get_addr_offset());
|
||||
} else if (sr.is_constant_data()) {
|
||||
if (!integer_fits(sr.constant_data(), deref_info.load_size, deref_info.sign_extend)) {
|
||||
throw_compiler_error(form, "The integer {} doesn't fit in element {} of array of {}",
|
||||
sr.constant_data(), arg_idx, content_type.print());
|
||||
}
|
||||
u64 data = sr.constant_data();
|
||||
memcpy(obj->data.data() + elt_offset, &data, deref_info.load_size);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
auto result = StaticResult::make_structure_reference(
|
||||
obj.get(), m_ts.make_array_typespec(content_type));
|
||||
fie->add_static(std::move(obj));
|
||||
return result;
|
||||
return fill_static_array(form, rest, true, env);
|
||||
} else if (unquote(args.at(1)).as_symbol()->name == "array") {
|
||||
return fill_static_array(form, rest, false, env);
|
||||
} else {
|
||||
auto ts = parse_typespec(unquote(args.at(1)));
|
||||
if (ts == TypeSpec("string")) {
|
||||
@@ -520,6 +460,18 @@ StaticResult Compiler::compile_static(const goos::Object& form, Env* env) {
|
||||
throw_compiler_error(form, "Cannot construct a static {}.", ts.print());
|
||||
}
|
||||
}
|
||||
} else if (first.is_symbol() && first.as_symbol()->name == "the-as") {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {}}, {});
|
||||
auto type = parse_typespec(args.unnamed.at(0));
|
||||
if (type == TypeSpec("float")) {
|
||||
s64 value;
|
||||
if (try_getting_constant_integer(args.unnamed.at(1), &value, env)) {
|
||||
if (integer_fits(value, 4, false)) {
|
||||
return StaticResult::make_constant_data(value, TypeSpec("float"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// maybe an enum
|
||||
s64 int_out;
|
||||
@@ -533,6 +485,90 @@ StaticResult Compiler::compile_static(const goos::Object& form, Env* env) {
|
||||
return {};
|
||||
}
|
||||
|
||||
StaticResult Compiler::fill_static_array(const goos::Object& form,
|
||||
const goos::Object& rest,
|
||||
bool boxed,
|
||||
Env* env) {
|
||||
auto fie = get_parent_env_of_type<FileEnv>(env);
|
||||
// (new 'static 'boxed-array ...)
|
||||
// get all arguments now
|
||||
auto args = get_list_as_vector(rest);
|
||||
if (args.size() < 4) {
|
||||
throw_compiler_error(form, "new static boxed array must have type and min-size arguments");
|
||||
}
|
||||
auto content_type = parse_typespec(args.at(2));
|
||||
s64 min_size;
|
||||
if (!try_getting_constant_integer(args.at(3), &min_size, env)) {
|
||||
throw_compiler_error(form, "The length {} is not valid.", args.at(3).print());
|
||||
}
|
||||
s32 length = std::max(min_size, s64(args.size() - 4));
|
||||
// todo - generalize this array stuff if we ever need other types of static arrays.
|
||||
auto pointer_type = m_ts.make_pointer_typespec(content_type);
|
||||
auto deref_info = m_ts.get_deref_info(pointer_type);
|
||||
assert(deref_info.can_deref);
|
||||
assert(deref_info.mem_deref);
|
||||
auto array_data_size_bytes = length * deref_info.stride;
|
||||
// todo, segments
|
||||
std::unique_ptr<StaticStructure> obj;
|
||||
if (boxed) {
|
||||
obj = std::make_unique<StaticBasic>(MAIN_SEGMENT, "array");
|
||||
} else {
|
||||
obj = std::make_unique<StaticStructure>(MAIN_SEGMENT);
|
||||
}
|
||||
|
||||
int array_header_size = boxed ? 16 : 0;
|
||||
obj->data.resize(array_header_size + array_data_size_bytes);
|
||||
|
||||
if (boxed) {
|
||||
// 0 - 4 : type tag (set automatically)
|
||||
// 4 - 8 : length
|
||||
memcpy(obj->data.data() + 4, &length, 4);
|
||||
// 8 - 12 allocated length
|
||||
memcpy(obj->data.data() + 8, &length, 4);
|
||||
// 12 - 16 content type
|
||||
obj->add_type_record(content_type.base_type(), 12);
|
||||
}
|
||||
|
||||
// now add arguments:
|
||||
for (size_t i = 4; i < args.size(); i++) {
|
||||
int arg_idx = i - 4;
|
||||
int elt_offset = array_header_size + arg_idx * deref_info.stride;
|
||||
auto sr = compile_static(args.at(i), env);
|
||||
if (is_integer(content_type)) {
|
||||
typecheck(form, TypeSpec("integer"), sr.typespec());
|
||||
} else {
|
||||
typecheck(form, content_type, sr.typespec());
|
||||
}
|
||||
if (sr.is_symbol()) {
|
||||
assert(deref_info.stride == 4);
|
||||
obj->add_symbol_record(sr.symbol_name(), elt_offset);
|
||||
u32 symbol_placeholder = 0xffffffff;
|
||||
memcpy(obj->data.data() + elt_offset, &symbol_placeholder, 4);
|
||||
} else if (sr.is_reference()) {
|
||||
assert(deref_info.stride == 4);
|
||||
obj->add_pointer_record(elt_offset, sr.reference(), sr.reference()->get_addr_offset());
|
||||
} else if (sr.is_constant_data()) {
|
||||
if (!integer_fits(sr.constant_data(), deref_info.load_size, deref_info.sign_extend)) {
|
||||
throw_compiler_error(form, "The integer {} doesn't fit in element {} of array of {}",
|
||||
sr.constant_data(), arg_idx, content_type.print());
|
||||
}
|
||||
u64 data = sr.constant_data();
|
||||
memcpy(obj->data.data() + elt_offset, &data, deref_info.load_size);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
TypeSpec result_type;
|
||||
if (boxed) {
|
||||
result_type = m_ts.make_array_typespec(content_type);
|
||||
} else {
|
||||
result_type = m_ts.make_pointer_typespec(content_type);
|
||||
}
|
||||
auto result = StaticResult::make_structure_reference(obj.get(), result_type);
|
||||
fie->add_static(std::move(obj));
|
||||
return result;
|
||||
}
|
||||
|
||||
Val* Compiler::compile_new_static_bitfield(const goos::Object& form,
|
||||
const TypeSpec& type,
|
||||
const goos::Object& _field_defs,
|
||||
|
||||
@@ -761,7 +761,8 @@ Val* Compiler::compile_static_new(const goos::Object& form,
|
||||
const goos::Object* rest,
|
||||
Env* env) {
|
||||
auto unquoted = unquote(type);
|
||||
if (unquoted.is_symbol() && unquoted.as_symbol()->name == "boxed-array") {
|
||||
if (unquoted.is_symbol() &&
|
||||
(unquoted.as_symbol()->name == "boxed-array" || unquoted.as_symbol()->name == "array")) {
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto sr = compile_static(form, env);
|
||||
auto result = fe->alloc_val<StaticVal>(sr.reference(), sr.typespec());
|
||||
@@ -884,7 +885,7 @@ Val* Compiler::compile_new(const goos::Object& form, const goos::Object& _rest,
|
||||
return compile_static_new(form, type, rest, env);
|
||||
} else if (allocation == "stack") {
|
||||
return compile_stack_new(form, type, rest, env, true);
|
||||
} else if (allocation == "stack-no-constructor") {
|
||||
} else if (allocation == "stack-no-clear") {
|
||||
return compile_stack_new(form, type, rest, env, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -2339,6 +2339,56 @@ class IGen {
|
||||
instr.set_vex_modrm_and_rex(dst.hw_id(), src.hw_id(), VEX3::LeadingBytes::P_0F, 0b0);
|
||||
return instr;
|
||||
}
|
||||
|
||||
static Instruction itof_vf(Register dst, Register src) {
|
||||
assert(dst.is_xmm());
|
||||
assert(src.is_xmm());
|
||||
Instruction instr(0x5b); // VCVTDQ2PS
|
||||
instr.set_vex_modrm_and_rex(dst.hw_id(), src.hw_id(), VEX3::LeadingBytes::P_0F, 0);
|
||||
return instr;
|
||||
}
|
||||
|
||||
static Instruction ftoi_vf(Register dst, Register src) {
|
||||
assert(dst.is_xmm());
|
||||
assert(src.is_xmm());
|
||||
Instruction instr(0x5b); // VCVTDQ2PS
|
||||
instr.set_vex_modrm_and_rex(dst.hw_id(), src.hw_id(), VEX3::LeadingBytes::P_0F, 0, false,
|
||||
VexPrefix::P_66);
|
||||
return instr;
|
||||
}
|
||||
|
||||
static Instruction pw_sra(Register dst, Register src, u8 imm) {
|
||||
assert(dst.is_xmm());
|
||||
assert(src.is_xmm());
|
||||
// VEX.128.66.0F.WIG 72 /4 ib VPSRAD xmm1, xmm2, imm8
|
||||
Instruction instr(0x72);
|
||||
instr.set_vex_modrm_and_rex(4, src.hw_id(), VEX3::LeadingBytes::P_0F, dst.hw_id(), false,
|
||||
VexPrefix::P_66);
|
||||
instr.set(Imm(1, imm));
|
||||
return instr;
|
||||
}
|
||||
|
||||
static Instruction pw_srl(Register dst, Register src, u8 imm) {
|
||||
assert(dst.is_xmm());
|
||||
assert(src.is_xmm());
|
||||
// VEX.128.66.0F.WIG 72 /2 ib VPSRLD xmm1, xmm2, imm8
|
||||
Instruction instr(0x72);
|
||||
instr.set_vex_modrm_and_rex(2, src.hw_id(), VEX3::LeadingBytes::P_0F, dst.hw_id(), false,
|
||||
VexPrefix::P_66);
|
||||
instr.set(Imm(1, imm));
|
||||
return instr;
|
||||
}
|
||||
|
||||
static Instruction pw_sll(Register dst, Register src, u8 imm) {
|
||||
assert(dst.is_xmm());
|
||||
assert(src.is_xmm());
|
||||
// VEX.128.66.0F.WIG 72 /6 ib VPSLLD xmm1, xmm2, imm8
|
||||
Instruction instr(0x72);
|
||||
instr.set_vex_modrm_and_rex(6, src.hw_id(), VEX3::LeadingBytes::P_0F, dst.hw_id(), false,
|
||||
VexPrefix::P_66);
|
||||
instr.set(Imm(1, imm));
|
||||
return instr;
|
||||
}
|
||||
};
|
||||
} // namespace emitter
|
||||
|
||||
|
||||
@@ -52,14 +52,14 @@ enum X86_REG : s8 {
|
||||
XMM5,
|
||||
XMM6,
|
||||
XMM7,
|
||||
XMM8,
|
||||
XMM9,
|
||||
XMM10,
|
||||
XMM11,
|
||||
XMM12,
|
||||
XMM13,
|
||||
XMM14,
|
||||
XMM15,
|
||||
XMM8, // saved
|
||||
XMM9, // saved
|
||||
XMM10, // saved
|
||||
XMM11, // saved
|
||||
XMM12, // saved
|
||||
XMM13, // saved
|
||||
XMM14, // saved
|
||||
XMM15, // saved
|
||||
};
|
||||
|
||||
class Register {
|
||||
|
||||
@@ -111,7 +111,7 @@ std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
|
||||
test->func.ir2.atomic_ops_succeeded = true;
|
||||
test->func.ir2.env.set_end_var(test->func.ir2.atomic_ops->end_op().return_var());
|
||||
|
||||
EXPECT_TRUE(test->func.run_type_analysis_ir2(function_type, *dts, test->file, hints));
|
||||
EXPECT_TRUE(test->func.run_type_analysis_ir2(function_type, *dts, test->file, hints, {}));
|
||||
|
||||
test->func.ir2.env.set_reg_use(analyze_ir2_register_usage(test->func));
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ TEST_F(FormRegressionTest, ExprSeek) {
|
||||
" (set! f2-0 (- arg1 arg0))\n"
|
||||
" (cond\n"
|
||||
" ((>= arg2 (fabs f2-0)) arg1)\n"
|
||||
" ((>= f2-0 0) (+ arg0 arg2))\n"
|
||||
" ((>= f2-0 0.000000) (+ arg0 arg2))\n"
|
||||
" (else (- arg0 arg2))\n"
|
||||
" )\n"
|
||||
" )";
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
(defmacro print-vf-hex (vf)
|
||||
`(let ((temp (new 'stack 'vector4w)))
|
||||
(.svf temp ,vf)
|
||||
(format #t "~X ~X ~X ~X~%" (-> temp data 0) (-> temp data 1) (-> temp data 2) (-> temp data 3))
|
||||
)
|
||||
)
|
||||
|
||||
(defun pw-shift-test ()
|
||||
(let ((temp (new 'stack 'vector4w)))
|
||||
(rlet ((vf1 :class vf)
|
||||
(vf2 :class vf)
|
||||
)
|
||||
;;(.lvf vf1 (new 'static 'vector4w :x #xfafffff0 :y #xfbfffff0 :z #xfcfffff0 :w #xfdfffff0))
|
||||
(set-vector! temp #xaafffff0 #xbbfffff0 #xccfffff0 #xddfffff0)
|
||||
(.lvf vf1 temp)
|
||||
(print-vf-hex vf1) ;; unchanged
|
||||
(.pw.sra vf2 vf1 2)
|
||||
(print-vf-hex vf2) ;; should sign extend
|
||||
(.pw.sll vf1 vf1 4)
|
||||
(print-vf-hex vf1) ;; original, removing top
|
||||
(.pw.srl vf2 vf1 2) ;; should make it positive.
|
||||
(print-vf-hex vf2)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(pw-shift-test)
|
||||
0
|
||||
@@ -0,0 +1,4 @@
|
||||
(let ((test-array (new 'static 'array int16 10 1 2 -10)))
|
||||
(format #t "~d ~d ~d~%" (-> test-array 0) (-> test-array 1) (-> test-array 2))
|
||||
0
|
||||
)
|
||||
@@ -0,0 +1,164 @@
|
||||
|
||||
(defmacro pf (x)
|
||||
`(format #t "~f~%" ,x)
|
||||
)
|
||||
|
||||
(defmacro pfv (x)
|
||||
`(format #t "~f ~f ~f ~f~%" (-> ,x x) (-> ,x y) (-> ,x z) (-> ,x w))
|
||||
)
|
||||
|
||||
(defmacro pr (x)
|
||||
`(format #t "~r~%" ,x)
|
||||
)
|
||||
|
||||
(defconstant PI_OVER_6 0.523598)
|
||||
(defconstant PI_OVER_4 0.785398)
|
||||
(defconstant PI_OVER_3 1.047198)
|
||||
(defconstant PI_OVER_2 1.570796)
|
||||
|
||||
;; degrees conversion and printing
|
||||
(format #t "~r~%" (degrees 2.0)) ;; 2.000
|
||||
(format #t "~r~%" (degrees -45)) ;; -45.000
|
||||
|
||||
;; radmod
|
||||
(pf (radmod 1.2)) ;; 1.200
|
||||
(pf (radmod -1.2)) ;; -1.200
|
||||
(pf (radmod 4.0)) ;; -2.2831
|
||||
(pf (radmod -5.0)) ;; 1.2831
|
||||
(format #t "~%")
|
||||
|
||||
;; sin
|
||||
(pf (sin (degrees 30))) ;; 0.5
|
||||
(pf (sin (degrees 90))) ;; 1
|
||||
(pf (sin (degrees (- 45)))) ;; -.707
|
||||
(pf (sin (degrees (- 60)))) ;; -.866
|
||||
(format #t "~%")
|
||||
|
||||
;; sin-rad
|
||||
(pf (sin-rad PI_OVER_6))
|
||||
(pf (sin-rad PI_OVER_2))
|
||||
(pf (sin-rad (- PI_OVER_4)))
|
||||
(pf (sin-rad (- PI_OVER_3)))
|
||||
(format #t "~%")
|
||||
|
||||
;; vector-sin-rad!
|
||||
(let ((in (new 'stack 'vector))
|
||||
(out (new 'stack 'vector))
|
||||
)
|
||||
(set-vector! in PI_OVER_6 (- PI_OVER_4) PI_OVER_3 (- PI_OVER_2))
|
||||
(vector-sin-rad! out in)
|
||||
(pfv out)
|
||||
)
|
||||
(format #t "~%")
|
||||
|
||||
;; cos-rad (this one is correct)
|
||||
(pf (cos-rad PI_OVER_6))
|
||||
(pf (cos-rad PI_OVER_2))
|
||||
(pf (cos-rad (- PI_OVER_4)))
|
||||
(pf (cos-rad (- PI_OVER_3)))
|
||||
(format #t "~%")
|
||||
|
||||
;; vector-cos-rad!
|
||||
(let ((in (new 'stack 'vector))
|
||||
(out (new 'stack 'vector))
|
||||
)
|
||||
(set-vector! in PI_OVER_6 (- PI_OVER_4) PI_OVER_3 (- PI_OVER_2))
|
||||
(vector-cos-rad! out in)
|
||||
(pfv out)
|
||||
)
|
||||
(format #t "~%")
|
||||
|
||||
;; vector-sincos-rad!
|
||||
(let ((in (new 'stack 'vector))
|
||||
(out-sin (new 'stack 'vector))
|
||||
(out-cos (new 'stack 'vector))
|
||||
)
|
||||
(set-vector! in PI_OVER_6 (- PI_OVER_4) PI_OVER_3 (- PI_OVER_2))
|
||||
(vector-sincos-rad! out-sin out-cos in)
|
||||
(pfv out-sin)
|
||||
(pfv out-cos)
|
||||
)
|
||||
(format #t "~%")
|
||||
|
||||
;; sincos-rad! (has cosine bug)
|
||||
(let ((out (new 'stack 'array 'float 2)))
|
||||
(sincos-rad! out PI_OVER_4)
|
||||
(format #t "~f ~f~%" (-> out 0) (-> out 1))
|
||||
(sincos-rad! out (- PI_OVER_2))
|
||||
(format #t "~f ~f~%" (-> out 0) (-> out 1))
|
||||
)
|
||||
(format #t "~%")
|
||||
|
||||
;; sincos! (has cosine bug)
|
||||
(format #t "sincos!~%")
|
||||
(let ((out (new 'stack 'array 'float 2)))
|
||||
(sincos! out (+ 0.0 (degrees 765)))
|
||||
(format #t "~f ~f~%" (-> out 0) (-> out 1))
|
||||
(sincos! out (+ (degrees 750) -0.0))
|
||||
(format #t "~f ~f~%" (-> out 0) (-> out 1))
|
||||
)
|
||||
(format #t "~%")
|
||||
|
||||
;; vector-rad<-vector-deg!
|
||||
(let ((out (new 'stack 'vector))
|
||||
(in (new 'stack 'vector))
|
||||
)
|
||||
(set-vector! in (degrees 1.0) (degrees 182.0) (degrees -183.0) (degrees 790.0))
|
||||
(vector-rad<-vector-deg! out in)
|
||||
(pfv out)
|
||||
)
|
||||
(format #t "~%")
|
||||
|
||||
;; vector-rad<-vector-deg/2!
|
||||
(let ((out (new 'stack 'vector))
|
||||
(in (new 'stack 'vector))
|
||||
)
|
||||
(set-vector! in (degrees (* 2.0 1.0)) (degrees (* 2.0 182.0)) (degrees (* 2.0 -183.0)) (degrees (* 2.0 790.0)))
|
||||
(vector-rad<-vector-deg/2! out in)
|
||||
(pfv out)
|
||||
)
|
||||
(format #t "~%")
|
||||
|
||||
;; tan
|
||||
(pf (tan (degrees 0)))
|
||||
(pf (tan (degrees 45.0)))
|
||||
(pf (tan (degrees -30.0)))
|
||||
(format #t "~%")
|
||||
|
||||
;; atan-rad
|
||||
(pf (atan-rad 0.5))
|
||||
(pf (atan-rad 0.707))
|
||||
(format #t "~%")
|
||||
|
||||
;; atan2-rad
|
||||
(pf (atan2-rad 1.0 2.0))
|
||||
(pf (atan2-rad -2.0 4.0))
|
||||
(pf (atan2-rad 1.0 -2.0))
|
||||
(pf (atan2-rad -1.0 -2.0))
|
||||
(format #t "~%")
|
||||
|
||||
;; exp
|
||||
(pf (exp 0.0))
|
||||
(pf (exp 0.2))
|
||||
(pf (exp 1.0))
|
||||
(pf (exp 1.3))
|
||||
(pf (exp 3.45))
|
||||
(pf (exp -1.0))
|
||||
(pf (exp -0.5))
|
||||
(pf (exp -2.34))
|
||||
(format #t "~%")
|
||||
|
||||
;; atan2
|
||||
(pr (atan 2.0 4.0))
|
||||
(pr (atan -.707 1.0))
|
||||
(pr (atan 1.732 -2.0))
|
||||
(pr (atan -2.0 -1.0))
|
||||
(format #t "~%")
|
||||
|
||||
;; asin
|
||||
(pr (asin 0.707))
|
||||
(pr (asin -0.866))
|
||||
(format #t "~%")
|
||||
0
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
(defmacro print-vf (vf)
|
||||
`(let ((temp (new 'stack 'vector)))
|
||||
(.svf temp ,vf)
|
||||
(format #t "~f ~f ~f ~f~%" (-> temp x) (-> temp y) (-> temp z) (-> temp w))
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro print-vf-hex (vf)
|
||||
`(let ((temp (new 'stack 'vector4w)))
|
||||
(.svf temp ,vf)
|
||||
(format #t "~d ~d ~d ~d~%" (-> temp data 0) (-> temp data 1) (-> temp data 2) (-> temp data 3))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
(defun itof-test ()
|
||||
(rlet ((vf1 :class vf)
|
||||
(vf2 :class vf))
|
||||
(.lvf vf1 (new 'static 'vector :x 1.0 :y -2.0 :z 3.0 :w 4.0))
|
||||
(.ftoi.vf vf2 vf1)
|
||||
(print-vf vf1)
|
||||
(print-vf-hex vf2)
|
||||
(.itof.vf vf2 vf2)
|
||||
(print-vf vf2)
|
||||
)
|
||||
)
|
||||
|
||||
(itof-test)
|
||||
0
|
||||
@@ -346,6 +346,80 @@ TEST_F(WithGameTests, StaticBoxedArray) {
|
||||
{"4 asdf \"test\" (a b) 0 object 12 12\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, Trig) {
|
||||
runner.run_static_test(env, testCategory, "test-trig.gc",
|
||||
{"2.0000\n" // 2 deg
|
||||
"-45.0000\n" // -45 deg
|
||||
"1.2000\n"
|
||||
"-1.2000\n"
|
||||
"-2.2831\n" // wrap
|
||||
"1.2831\n" // wrapped
|
||||
"\n"
|
||||
"0.4999\n" // sin
|
||||
"1.0000\n"
|
||||
"-0.7071\n"
|
||||
"-0.8659\n"
|
||||
"\n"
|
||||
"0.4999\n" // sin-rads
|
||||
"1.0000\n"
|
||||
"-0.7071\n"
|
||||
"-0.8660\n"
|
||||
"\n"
|
||||
"0.4999 -0.7071 0.8660 -1.0000\n" // vector-sin-rad!
|
||||
"\n"
|
||||
"0.8660\n" // cos-rads
|
||||
"0.0000\n"
|
||||
"0.7071\n"
|
||||
"0.4999\n"
|
||||
"\n"
|
||||
"0.8660 0.7071 0.4999 0.0000\n" // vector-cos-rad
|
||||
"\n"
|
||||
"0.4999 -0.7071 0.8660 -1.0000\n" // vector-sincos
|
||||
"0.8660 0.7071 0.4999 0.0000\n"
|
||||
"\n"
|
||||
"0.7071 0.7082\n" // sincos with cosine bug
|
||||
"-1.0000 0.0047\n" // sincos, with cosine bug
|
||||
"\n"
|
||||
"sincos!\n"
|
||||
"0.7071 0.7082\n" // also with cosine bugs
|
||||
"0.4999 0.8665\n"
|
||||
"\n"
|
||||
"0.0174 -3.1066 3.0892 1.2217\n" // vector-rad<-vector deg
|
||||
"\n"
|
||||
"0.0174 -3.1066 3.0892 1.2217\n" // with div/2
|
||||
"\n"
|
||||
"0.0000\n" // tan
|
||||
"1.0000\n"
|
||||
"-0.5773\n"
|
||||
"\n"
|
||||
"0.4636\n" // atan-rad
|
||||
"0.6154\n"
|
||||
"\n"
|
||||
"0.4636\n" // atan2
|
||||
"-0.4636\n"
|
||||
"2.6779\n"
|
||||
"-2.6779\n"
|
||||
"\n"
|
||||
"1.0000\n" // exp
|
||||
"1.2214\n"
|
||||
"2.7182\n"
|
||||
"3.6692\n"
|
||||
"31.5003\n"
|
||||
"0.3678\n"
|
||||
"0.6065\n"
|
||||
"0.0963\n"
|
||||
"\n"
|
||||
"26.5650\n"
|
||||
"-35.2603\n"
|
||||
"139.1074\n"
|
||||
"-116.5650\n"
|
||||
"\n"
|
||||
"44.9913\n"
|
||||
"-59.9970\n"
|
||||
"\n"
|
||||
"0\n"});
|
||||
}
|
||||
|
||||
// VECTOR FLOAT TESTS
|
||||
|
||||
// ---- One off Tests
|
||||
@@ -385,6 +459,29 @@ TEST_F(WithGameTests, ShortCircuit) {
|
||||
get_test_pass_string("short-circuit", 13));
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, VectorFloatToInt) {
|
||||
runner.run_static_test(env, testCategory, "test-vector-int-float-conversions.gc",
|
||||
{"1.0000 -2.0000 3.0000 4.0000\n"
|
||||
"1 -2 3 4\n"
|
||||
"1.0000 -2.0000 3.0000 4.0000\n"
|
||||
"0\n"});
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, PWShifts) {
|
||||
runner.run_static_test(env, testCategory, "test-pw-shifts.gc",
|
||||
{"ffffffffaafffff0 ffffffffbbfffff0 ffffffffccfffff0 ffffffffddfffff0\n"
|
||||
"ffffffffeabffffc ffffffffeefffffc fffffffff33ffffc fffffffff77ffffc\n"
|
||||
"ffffffffafffff00 ffffffffbfffff00 ffffffffcfffff00 ffffffffdfffff00\n"
|
||||
"2bffffc0 2fffffc0 33ffffc0 37ffffc0\n"
|
||||
"0\n"});
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, StaticArray) {
|
||||
runner.run_static_test(env, testCategory, "test-static-array.gc",
|
||||
{"1 2 -10\n"
|
||||
"0\n"});
|
||||
}
|
||||
|
||||
TEST(TypeConsistency, TypeConsistency) {
|
||||
Compiler compiler;
|
||||
compiler.enable_throw_on_redefines();
|
||||
|
||||
@@ -322,3 +322,53 @@ TEST(EmitterAVX, RIP) {
|
||||
tester.emit(IGen::loadvf_rip_plus_s32(XMM0 + 13, -123));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(true), "C5F8281D85FFFFFFC578282D85FFFFFF");
|
||||
}
|
||||
|
||||
TEST(EmitterAVX, ITOF) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(1024);
|
||||
tester.emit(IGen::itof_vf(XMM0 + 3, XMM0 + 4));
|
||||
tester.emit(IGen::itof_vf(XMM0 + 3, XMM0 + 14));
|
||||
tester.emit(IGen::itof_vf(XMM0 + 13, XMM0 + 4));
|
||||
tester.emit(IGen::itof_vf(XMM0 + 13, XMM0 + 14));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(true), "C5F85BDCC4C1785BDEC5785BECC441785BEE");
|
||||
}
|
||||
|
||||
TEST(EmitterAVX, FTOI) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(1024);
|
||||
tester.emit(IGen::ftoi_vf(XMM0 + 3, XMM0 + 4));
|
||||
tester.emit(IGen::ftoi_vf(XMM0 + 3, XMM0 + 14));
|
||||
tester.emit(IGen::ftoi_vf(XMM0 + 13, XMM0 + 4));
|
||||
tester.emit(IGen::ftoi_vf(XMM0 + 13, XMM0 + 14));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(true), "C5F95BDCC4C1795BDEC5795BECC441795BEE");
|
||||
}
|
||||
|
||||
TEST(EmitterAVX, VPSRAD) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(1024);
|
||||
tester.emit(IGen::pw_sra(XMM0 + 3, XMM0 + 4, 3));
|
||||
tester.emit(IGen::pw_sra(XMM0 + 3, XMM0 + 14, 4));
|
||||
tester.emit(IGen::pw_sra(XMM0 + 13, XMM0 + 4, 5));
|
||||
tester.emit(IGen::pw_sra(XMM0 + 13, XMM0 + 14, 6));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(true), "C5E172E403C4C16172E604C59172E405C4C11172E606");
|
||||
}
|
||||
|
||||
TEST(EmitterAVX, VPSRLD) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(1024);
|
||||
tester.emit(IGen::pw_srl(XMM0 + 3, XMM0 + 4, 3));
|
||||
tester.emit(IGen::pw_srl(XMM0 + 3, XMM0 + 14, 4));
|
||||
tester.emit(IGen::pw_srl(XMM0 + 13, XMM0 + 4, 5));
|
||||
tester.emit(IGen::pw_srl(XMM0 + 13, XMM0 + 14, 6));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(true), "C5E172D403C4C16172D604C59172D405C4C11172D606");
|
||||
}
|
||||
|
||||
TEST(EmitterAVX, VPSLLD) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(1024);
|
||||
tester.emit(IGen::pw_sll(XMM0 + 3, XMM0 + 4, 3));
|
||||
tester.emit(IGen::pw_sll(XMM0 + 3, XMM0 + 14, 4));
|
||||
tester.emit(IGen::pw_sll(XMM0 + 13, XMM0 + 4, 5));
|
||||
tester.emit(IGen::pw_sll(XMM0 + 13, XMM0 + 14, 6));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(true), "C5E172F403C4C16172F604C59172F405C4C11172F606");
|
||||
}
|
||||
Reference in New Issue
Block a user