mirror of
https://github.com/open-goal/jak-project
synced 2026-09-09 20:21:28 -04:00
[decompiler] clean up vector detection and add vector float product detection (#638)
* change * recognize vector float product and update tests
This commit is contained in:
@@ -291,6 +291,12 @@ std::string get_simple_expression_op_name(SimpleExpression::Kind kind) {
|
||||
return "max.ui";
|
||||
case SimpleExpression::Kind::PCPYLD:
|
||||
return "pcypld";
|
||||
case SimpleExpression::Kind::VECTOR_PLUS:
|
||||
return "vector+!2";
|
||||
case SimpleExpression::Kind::VECTOR_MINUS:
|
||||
return "vector-!2";
|
||||
case SimpleExpression::Kind::VECTOR_FLOAT_PRODUCT:
|
||||
return "vector-float*!2";
|
||||
default:
|
||||
assert(false);
|
||||
return {};
|
||||
@@ -342,6 +348,10 @@ int get_simple_expression_arg_count(SimpleExpression::Kind kind) {
|
||||
case SimpleExpression::Kind::MAX_UNSIGNED:
|
||||
case SimpleExpression::Kind::PCPYLD:
|
||||
return 2;
|
||||
case SimpleExpression::Kind::VECTOR_PLUS:
|
||||
case SimpleExpression::Kind::VECTOR_MINUS:
|
||||
case SimpleExpression::Kind::VECTOR_FLOAT_PRODUCT:
|
||||
return 3;
|
||||
default:
|
||||
assert(false);
|
||||
return -1;
|
||||
@@ -362,6 +372,18 @@ SimpleExpression::SimpleExpression(Kind kind, const SimpleAtom& arg0, const Simp
|
||||
assert(get_simple_expression_arg_count(kind) == 2);
|
||||
}
|
||||
|
||||
SimpleExpression::SimpleExpression(Kind kind,
|
||||
const SimpleAtom& arg0,
|
||||
const SimpleAtom& arg1,
|
||||
const SimpleAtom& arg2)
|
||||
: n_args(3) {
|
||||
m_args[0] = arg0;
|
||||
m_args[1] = arg1;
|
||||
m_args[2] = arg2;
|
||||
m_kind = kind;
|
||||
assert(get_simple_expression_arg_count(kind) == 3);
|
||||
}
|
||||
|
||||
goos::Object SimpleExpression::to_form(const std::vector<DecompilerLabel>& labels,
|
||||
const Env& env) const {
|
||||
std::vector<goos::Object> forms;
|
||||
|
||||
@@ -223,6 +223,9 @@ class SimpleExpression {
|
||||
MIN_UNSIGNED,
|
||||
MAX_UNSIGNED,
|
||||
PCPYLD,
|
||||
VECTOR_PLUS,
|
||||
VECTOR_MINUS,
|
||||
VECTOR_FLOAT_PRODUCT
|
||||
};
|
||||
|
||||
// how many arguments?
|
||||
@@ -235,6 +238,10 @@ class SimpleExpression {
|
||||
SimpleExpression() = default;
|
||||
SimpleExpression(Kind kind, const SimpleAtom& arg0);
|
||||
SimpleExpression(Kind kind, const SimpleAtom& arg0, const SimpleAtom& arg1);
|
||||
SimpleExpression(Kind kind,
|
||||
const SimpleAtom& arg0,
|
||||
const SimpleAtom& arg1,
|
||||
const SimpleAtom& arg2);
|
||||
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env& env) const;
|
||||
std::string to_string(const Env& env) const;
|
||||
bool operator==(const SimpleExpression& other) const;
|
||||
@@ -256,7 +263,7 @@ class SimpleExpression {
|
||||
|
||||
private:
|
||||
Kind m_kind = Kind::INVALID;
|
||||
SimpleAtom m_args[2];
|
||||
SimpleAtom m_args[3];
|
||||
s8 n_args = -1;
|
||||
};
|
||||
|
||||
|
||||
@@ -195,6 +195,11 @@ TP_Type SimpleExpression::get_type(const TypeState& input,
|
||||
case Kind::MOD_UNSIGNED:
|
||||
case Kind::PCPYLD:
|
||||
return TP_Type::make_from_ts("uint");
|
||||
case Kind::VECTOR_PLUS:
|
||||
case Kind::VECTOR_MINUS:
|
||||
return TP_Type::make_from_ts("vector");
|
||||
case Kind::VECTOR_FLOAT_PRODUCT:
|
||||
return TP_Type::make_from_ts("vector");
|
||||
default:
|
||||
throw std::runtime_error("Simple expression cannot get_type: " +
|
||||
to_form(env.file->labels, env).print());
|
||||
|
||||
@@ -1541,6 +1541,12 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) {
|
||||
return ".asm.sllv.r0";
|
||||
case FixedOperatorKind::ASM_MADDS:
|
||||
return ".asm.madd.s";
|
||||
case FixedOperatorKind::VECTOR_MINUS:
|
||||
return "vector-!";
|
||||
case FixedOperatorKind::VECTOR_PLUS:
|
||||
return "vector+!";
|
||||
case FixedOperatorKind::VECTOR_FLOAT_PRODUCT:
|
||||
return "vector-float*!";
|
||||
default:
|
||||
assert(false);
|
||||
return "";
|
||||
|
||||
@@ -190,6 +190,17 @@ class SimpleExpressionElement : public FormElement {
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects);
|
||||
void update_from_stack_vector_plus_minus(bool is_add,
|
||||
const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects);
|
||||
void update_from_stack_vector_float_product(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects);
|
||||
|
||||
const SimpleExpression& expr() const { return m_expr; }
|
||||
|
||||
|
||||
@@ -950,6 +950,54 @@ void SimpleExpressionElement::update_from_stack_pcypld(const Env& env,
|
||||
result->push_back(new_form);
|
||||
}
|
||||
|
||||
void SimpleExpressionElement::update_from_stack_vector_plus_minus(bool is_add,
|
||||
const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) {
|
||||
std::vector<Form*> popped_args =
|
||||
pop_to_forms({m_expr.get_arg(0).var(), m_expr.get_arg(1).var(), m_expr.get_arg(2).var()}, env,
|
||||
pool, stack, allow_side_effects);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
auto arg_type = env.get_types_before_op(m_my_idx).get(m_expr.get_arg(i).var().reg());
|
||||
if (arg_type.typespec() != TypeSpec("vector")) {
|
||||
popped_args.at(i) = cast_form(popped_args.at(i), TypeSpec("vector"), pool, env);
|
||||
}
|
||||
}
|
||||
|
||||
auto new_form = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(is_add ? FixedOperatorKind::VECTOR_PLUS
|
||||
: FixedOperatorKind::VECTOR_MINUS),
|
||||
std::vector<Form*>{popped_args.at(0), popped_args.at(1), popped_args.at(2)});
|
||||
result->push_back(new_form);
|
||||
}
|
||||
|
||||
void SimpleExpressionElement::update_from_stack_vector_float_product(
|
||||
const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) {
|
||||
std::vector<Form*> popped_args =
|
||||
pop_to_forms({m_expr.get_arg(0).var(), m_expr.get_arg(1).var(), m_expr.get_arg(2).var()}, env,
|
||||
pool, stack, allow_side_effects);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
auto arg_type = env.get_types_before_op(m_my_idx).get(m_expr.get_arg(i).var().reg());
|
||||
TypeSpec desired_type(i == 2 ? "float" : "vector");
|
||||
if (arg_type.typespec() != desired_type) {
|
||||
popped_args.at(i) = cast_form(popped_args.at(i), desired_type, pool, env);
|
||||
}
|
||||
}
|
||||
|
||||
auto new_form = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::VECTOR_FLOAT_PRODUCT),
|
||||
std::vector<Form*>{popped_args.at(0), popped_args.at(1), popped_args.at(2)});
|
||||
result->push_back(new_form);
|
||||
}
|
||||
|
||||
void SimpleExpressionElement::update_from_stack_copy_first_int_2(const Env& env,
|
||||
FixedOperatorKind kind,
|
||||
FormPool& pool,
|
||||
@@ -1559,6 +1607,15 @@ void SimpleExpressionElement::update_from_stack(const Env& env,
|
||||
case SimpleExpression::Kind::PCPYLD:
|
||||
update_from_stack_pcypld(env, pool, stack, result, allow_side_effects);
|
||||
break;
|
||||
case SimpleExpression::Kind::VECTOR_PLUS:
|
||||
update_from_stack_vector_plus_minus(true, env, pool, stack, result, allow_side_effects);
|
||||
break;
|
||||
case SimpleExpression::Kind::VECTOR_MINUS:
|
||||
update_from_stack_vector_plus_minus(false, env, pool, stack, result, allow_side_effects);
|
||||
break;
|
||||
case SimpleExpression::Kind::VECTOR_FLOAT_PRODUCT:
|
||||
update_from_stack_vector_float_product(env, pool, stack, result, allow_side_effects);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
fmt::format("SimpleExpressionElement::update_from_stack NYI for {}", to_string(env)));
|
||||
@@ -3786,110 +3843,6 @@ Form* is_load_store_vector_to_reg(const Register& reg,
|
||||
return mr.maps.forms.at(0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* try to convert to an assembly op, return nullptr if we can't.
|
||||
*/
|
||||
const AsmOp* get_asm_op(FormElement* form) {
|
||||
auto as_asm = dynamic_cast<OpenGoalAsmOpElement*>(form);
|
||||
if (as_asm) {
|
||||
return as_asm->op();
|
||||
}
|
||||
|
||||
auto two = dynamic_cast<AsmOpElement*>(form);
|
||||
if (two) {
|
||||
return two->op();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Is this vmove.w vfX, vf0? This is a common trick to set the w field.
|
||||
*/
|
||||
bool is_set_w_1(const Register& reg, FormElement* form, const Env&) {
|
||||
auto as_asm = get_asm_op(form);
|
||||
if (!as_asm) {
|
||||
return false;
|
||||
}
|
||||
auto instr = as_asm->instruction();
|
||||
|
||||
if (instr.kind != InstructionKind::VMOVE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (instr.cop2_dest != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!instr.get_src(0).is_reg(Register(Reg::VF, 0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!instr.get_dst(0).is_reg(reg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Is this a COP2 op in the form vblah.mask vfX, vfY, vfZ?
|
||||
*/
|
||||
bool is_vf_3op_dst(InstructionKind kind,
|
||||
u8 dest_mask,
|
||||
const Register& dst,
|
||||
const Register& src0,
|
||||
const Register& src1,
|
||||
|
||||
FormElement* form) {
|
||||
auto as_asm = get_asm_op(form);
|
||||
if (!as_asm) {
|
||||
return false;
|
||||
}
|
||||
auto instr = as_asm->instruction();
|
||||
|
||||
if (instr.kind != kind) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (instr.cop2_dest != dest_mask) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!instr.get_src(0).is_reg(src0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!instr.get_src(1).is_reg(src1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!instr.get_dst(0).is_reg(dst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Make a vf register.
|
||||
*/
|
||||
Register vfr(int idx) {
|
||||
return Register(Reg::VF, idx);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Try to pop a variable from the stack again. If we are detecting a macro that flips argument
|
||||
* evaluation order, we can use this to fix it up and remove temporaries.
|
||||
* If the previous pop succeeded, this does nothing.
|
||||
*/
|
||||
Form* repop_arg(Form* in, FormStack& stack, const Env& env, FormPool& pool) {
|
||||
auto as_atom = form_as_atom(in);
|
||||
if (as_atom && as_atom->is_var()) {
|
||||
return pop_to_forms({as_atom->var()}, env, pool, stack, true).at(0);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Imagine:
|
||||
* x = foo
|
||||
@@ -3934,122 +3887,6 @@ std::optional<RegisterAccess> form_as_ra(Form* form) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
* Handle an inlined call to vector-!
|
||||
*/
|
||||
bool try_vector_add_sub_inline(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
bool is_add,
|
||||
FormElement* store_element) {
|
||||
// we are looking for 5 ops, none are sets, the store element is passed in separately, before
|
||||
// propagating
|
||||
auto elts = stack.try_getting_active_stack_entries({false, false, false, false});
|
||||
if (!elts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
if (is_add) {
|
||||
// third (.vmove.w vf6 vf0)
|
||||
if (!is_set_w_1(Register(Reg::VF, 6), elts->at(idx++).elt, env)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check first: (.lvf vf4 (&-> arg1 quad))
|
||||
auto first =
|
||||
is_load_store_vector_to_reg(Register(Reg::VF, 4), elts->at(idx++).elt, true, nullptr);
|
||||
if (!first) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// second (.lvf vf5 (&-> a0-1 quad))
|
||||
auto second =
|
||||
is_load_store_vector_to_reg(Register(Reg::VF, 5), elts->at(idx++).elt, true, nullptr);
|
||||
if (!second) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_add) {
|
||||
// third (.vmove.w vf6 vf0)
|
||||
if (!is_set_w_1(Register(Reg::VF, 6), elts->at(idx++).elt, env)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 4th (.vsub.xyz vf6 vf4 vf5)
|
||||
if (!is_vf_3op_dst(is_add ? InstructionKind::VADD : InstructionKind::VSUB, 14, vfr(6), vfr(4),
|
||||
vfr(5), elts->at(idx++).elt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5th (and remember the index)
|
||||
int store_idx = -1;
|
||||
auto store = is_load_store_vector_to_reg(Register(Reg::VF, 6), store_element, false, &store_idx);
|
||||
if (!store) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the store here _should_ have failed propagation and just given us a variable.
|
||||
// if this is causing issues, we can run this check before propagating, as well call this from
|
||||
// the function that attempts the pop.
|
||||
auto store_var = form_as_ra(store);
|
||||
if (!store_var) {
|
||||
env.func->warnings.general_warning("Almost found vector add/sub, but couldn't get store var.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove these from the stack.
|
||||
stack.pop(4);
|
||||
|
||||
// ignore the store as a use. This will allow the entire vector-! expression to be expression
|
||||
// propagated, if it is appropriate.
|
||||
if (store_var) {
|
||||
auto menv = const_cast<Env*>(&env);
|
||||
menv->disable_use(*store_var);
|
||||
}
|
||||
|
||||
// repop the arguments in the opposite order. this can eliminate temporaries as this will
|
||||
// use the opposite order of the original attempt.
|
||||
second = repop_arg(second, stack, env, pool);
|
||||
first = repop_arg(first, stack, env, pool);
|
||||
|
||||
// now try to see if we can pop the first arg (destination vector).
|
||||
bool got_orig = false;
|
||||
RegisterAccess orig;
|
||||
|
||||
store = repop_passthrough_arg(store, stack, env, &orig, &got_orig);
|
||||
|
||||
// create the actual vector-! form
|
||||
Form* new_thing = pool.alloc_single_element_form<GenericElement>(
|
||||
nullptr,
|
||||
GenericOperator::make_function(pool.alloc_single_element_form<ConstantTokenElement>(
|
||||
nullptr, is_add ? "vector+!" : "vector-!")),
|
||||
std::vector<Form*>{store, first, second});
|
||||
|
||||
if (got_orig) {
|
||||
// we got a value for the destination. because we used the special repop passthrough,
|
||||
// we're responsible for inserting a set to set the var that we "stole" from.
|
||||
// We do this through push_value_to_reg, so it can be propagated if needed, but only if
|
||||
// somebody will actually read the output.
|
||||
// to tell, we look at the live out of the store op and the end - the earlier one would of
|
||||
// course be live out always because the store will read it again.
|
||||
auto& op_info = env.reg_use().op.at(store_idx);
|
||||
if (op_info.live.find(orig.reg()) == op_info.live.end()) {
|
||||
// nobody reads it, don't bother.
|
||||
stack.push_form_element(new_thing->elts().at(0), true);
|
||||
} else {
|
||||
stack.push_value_to_reg(orig, new_thing, true, TypeSpec("vector"));
|
||||
}
|
||||
|
||||
} else {
|
||||
stack.push_form_element(new_thing->elts().at(0), true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool try_vector_reset_inline(const Env& env,
|
||||
FormPool& pool,
|
||||
FormStack& stack,
|
||||
@@ -4132,14 +3969,6 @@ void VectorFloatLoadStoreElement::push_to_stack(const Env& env, FormPool& pool,
|
||||
auto name = env.func->guessed_name.to_string();
|
||||
// don't find vector-! inside of vector-!.
|
||||
if (!m_is_load && name != "vector-!" && name != "vector+!" && name != "vector-reset!") {
|
||||
if (try_vector_add_sub_inline(env, pool, stack, true, this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (try_vector_add_sub_inline(env, pool, stack, false, this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (try_vector_reset_inline(env, pool, stack, this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -148,6 +148,9 @@ enum class FixedOperatorKind {
|
||||
ADDRESS_OF,
|
||||
ASM_SLLV_R0,
|
||||
ASM_MADDS,
|
||||
VECTOR_PLUS,
|
||||
VECTOR_MINUS,
|
||||
VECTOR_FLOAT_PRODUCT,
|
||||
INVALID
|
||||
};
|
||||
|
||||
|
||||
@@ -45,6 +45,10 @@ Register rsp() {
|
||||
return make_gpr(Reg::SP);
|
||||
}
|
||||
|
||||
Register make_vf(int idx) {
|
||||
return Register(Reg::VF, idx);
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Variable Helpers
|
||||
/////////////////////////
|
||||
@@ -1393,6 +1397,100 @@ std::unique_ptr<AtomicOp> convert_4(const Instruction& i0,
|
||||
// OP 5 Conversions
|
||||
//////////////////////
|
||||
|
||||
std::unique_ptr<AtomicOp> convert_vector_plus(const Instruction& i0,
|
||||
const Instruction& i1,
|
||||
const Instruction& i2,
|
||||
const Instruction& i3,
|
||||
const Instruction& i4,
|
||||
int idx) {
|
||||
// vmove.w vf6, vf0
|
||||
if (i0.kind != InstructionKind::VMOVE || i0.get_src(0).get_reg() != make_vf(0) ||
|
||||
i0.get_dst(0).get_reg() != make_vf(6) || i0.cop2_dest != 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// lqc2 vf4, 0(a1) (src1)
|
||||
if (i1.kind != InstructionKind::LQC2 || i1.get_dst(0).get_reg() != make_vf(4) ||
|
||||
!i1.get_src(0).is_imm(0)) {
|
||||
return nullptr;
|
||||
}
|
||||
Register src1 = i1.get_src(1).get_reg();
|
||||
|
||||
// lqc2 vf5, 0(a2) (src2)
|
||||
if (i2.kind != InstructionKind::LQC2 || i2.get_dst(0).get_reg() != make_vf(5) ||
|
||||
!i2.get_src(0).is_imm(0)) {
|
||||
return nullptr;
|
||||
}
|
||||
Register src2 = i2.get_src(1).get_reg();
|
||||
|
||||
// vadd.xyz vf6, vf4, vf5
|
||||
if (i3.kind != InstructionKind::VADD || i3.get_dst(0).get_reg() != make_vf(6) ||
|
||||
i3.get_src(0).get_reg() != make_vf(4) || i3.get_src(1).get_reg() != make_vf(5) ||
|
||||
i3.cop2_dest != 14) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// sqc2 vf6, 0(a0) (dst)
|
||||
if (i4.kind != InstructionKind::SQC2 || i4.get_src(0).get_reg() != make_vf(6) ||
|
||||
!i4.get_src(1).is_imm(0)) {
|
||||
return nullptr;
|
||||
}
|
||||
Register dst = i4.get_src(2).get_reg();
|
||||
|
||||
return std::make_unique<SetVarOp>(
|
||||
make_dst_var(dst, idx),
|
||||
SimpleExpression(SimpleExpression::Kind::VECTOR_PLUS, make_src_atom(dst, idx),
|
||||
make_src_atom(src1, idx), make_src_atom(src2, idx)),
|
||||
idx);
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicOp> convert_vector_minus(const Instruction& i0,
|
||||
const Instruction& i1,
|
||||
const Instruction& i2,
|
||||
const Instruction& i3,
|
||||
const Instruction& i4,
|
||||
int idx) {
|
||||
// lqc2 vf4, 0(a1) (src1)
|
||||
if (i0.kind != InstructionKind::LQC2 || i0.get_dst(0).get_reg() != make_vf(4) ||
|
||||
!i0.get_src(0).is_imm(0)) {
|
||||
return nullptr;
|
||||
}
|
||||
Register src1 = i0.get_src(1).get_reg();
|
||||
|
||||
// lqc2 vf5, 0(a2) (src2)
|
||||
if (i1.kind != InstructionKind::LQC2 || i1.get_dst(0).get_reg() != make_vf(5) ||
|
||||
!i1.get_src(0).is_imm(0)) {
|
||||
return nullptr;
|
||||
}
|
||||
Register src2 = i1.get_src(1).get_reg();
|
||||
|
||||
// vmove.w vf6, vf0
|
||||
if (i2.kind != InstructionKind::VMOVE || i2.get_src(0).get_reg() != make_vf(0) ||
|
||||
i2.get_dst(0).get_reg() != make_vf(6) || i2.cop2_dest != 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// vadd.xyz vf6, vf4, vf5
|
||||
if (i3.kind != InstructionKind::VSUB || i3.get_dst(0).get_reg() != make_vf(6) ||
|
||||
i3.get_src(0).get_reg() != make_vf(4) || i3.get_src(1).get_reg() != make_vf(5) ||
|
||||
i3.cop2_dest != 14) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// sqc2 vf6, 0(a0) (dst)
|
||||
if (i4.kind != InstructionKind::SQC2 || i4.get_src(0).get_reg() != make_vf(6) ||
|
||||
!i4.get_src(1).is_imm(0)) {
|
||||
return nullptr;
|
||||
}
|
||||
Register dst = i4.get_src(2).get_reg();
|
||||
|
||||
return std::make_unique<SetVarOp>(
|
||||
make_dst_var(dst, idx),
|
||||
SimpleExpression(SimpleExpression::Kind::VECTOR_MINUS, make_src_atom(dst, idx),
|
||||
make_src_atom(src1, idx), make_src_atom(src2, idx)),
|
||||
idx);
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicOp> convert_5(const Instruction& i0,
|
||||
const Instruction& i1,
|
||||
const Instruction& i2,
|
||||
@@ -1410,6 +1508,86 @@ std::unique_ptr<AtomicOp> convert_5(const Instruction& i0,
|
||||
i3.get_src(0).is_reg(s6) && i4.kind == InstructionKind::MFLO1 && i4.get_dst(0).is_reg(s6)) {
|
||||
return std::make_unique<SpecialOp>(SpecialOp::Kind::SUSPEND, idx);
|
||||
}
|
||||
|
||||
auto as_vector_plus = convert_vector_plus(i0, i1, i2, i3, i4, idx);
|
||||
if (as_vector_plus) {
|
||||
return as_vector_plus;
|
||||
}
|
||||
|
||||
auto as_vector_minus = convert_vector_minus(i0, i1, i2, i3, i4, idx);
|
||||
if (as_vector_minus) {
|
||||
return as_vector_minus;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicOp> convert_vector_float_product(const Instruction& i0,
|
||||
const Instruction& i1,
|
||||
const Instruction& i2,
|
||||
const Instruction& i3,
|
||||
const Instruction& i4,
|
||||
const Instruction& i5,
|
||||
int idx) {
|
||||
// lqc2 vf1, 0(vect_in)
|
||||
if (i0.kind != InstructionKind::LQC2 || i0.get_dst(0).get_reg() != make_vf(1) ||
|
||||
!i0.get_src(0).is_imm(0)) {
|
||||
return nullptr;
|
||||
}
|
||||
Register vec_src = i0.get_src(1).get_reg();
|
||||
|
||||
// mfc1 gpr_temp, float_in
|
||||
if (i1.kind != InstructionKind::MFC1) {
|
||||
return nullptr;
|
||||
}
|
||||
Register gpr_temp = i1.get_dst(0).get_reg();
|
||||
Register float_src = i1.get_src(0).get_reg();
|
||||
|
||||
// qmtc2.i vf2, gpr_temp
|
||||
if (i2.kind != InstructionKind::QMTC2 || i2.get_dst(0).get_reg() != make_vf(2) ||
|
||||
i2.get_src(0).get_reg() != gpr_temp) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// vaddx.w vf1, vf0, vf0
|
||||
if (i3.kind != InstructionKind::VADD_BC || i3.get_dst(0).get_reg() != make_vf(1) ||
|
||||
i3.get_src(0).get_reg() != make_vf(0) || i3.get_src(1).get_reg() != make_vf(0) ||
|
||||
i3.cop2_bc != 0 || i3.cop2_dest != 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// vmulx.xyz vf1, vf1, vf2
|
||||
if (i4.kind != InstructionKind::VMUL_BC || i4.get_dst(0).get_reg() != make_vf(1) ||
|
||||
i4.get_src(0).get_reg() != make_vf(1) || i4.get_src(1).get_reg() != make_vf(2) ||
|
||||
i4.cop2_dest != 14 || i4.cop2_bc != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// sqc2 vf1, 0(gE)
|
||||
if (i5.kind != InstructionKind::SQC2 || i5.get_src(0).get_reg() != make_vf(1) ||
|
||||
!i5.get_src(1).is_imm(0)) {
|
||||
return nullptr;
|
||||
}
|
||||
Register dst = i5.get_src(2).get_reg();
|
||||
|
||||
return std::make_unique<SetVarOp>(
|
||||
make_dst_var(dst, idx),
|
||||
SimpleExpression(SimpleExpression::Kind::VECTOR_FLOAT_PRODUCT, make_src_atom(dst, idx),
|
||||
make_src_atom(vec_src, idx), make_src_atom(float_src, idx)),
|
||||
idx);
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicOp> convert_6(const Instruction& i0,
|
||||
const Instruction& i1,
|
||||
const Instruction& i2,
|
||||
const Instruction& i3,
|
||||
const Instruction& i4,
|
||||
const Instruction& i5,
|
||||
int idx) {
|
||||
auto as_vector_float_product = convert_vector_float_product(i0, i1, i2, i3, i4, i5, idx);
|
||||
if (as_vector_float_product) {
|
||||
return as_vector_float_product;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1445,7 +1623,16 @@ int convert_block_to_atomic_ops(int begin_idx,
|
||||
warnings.warn_sq_lq();
|
||||
}
|
||||
|
||||
if (n_instr >= 5) {
|
||||
if (n_instr >= 6) {
|
||||
// try 6 instructions
|
||||
op = convert_6(instr[0], instr[1], instr[2], instr[3], instr[4], instr[5], op_idx);
|
||||
if (op) {
|
||||
converted = true;
|
||||
length = 6;
|
||||
}
|
||||
}
|
||||
|
||||
if (!converted && n_instr >= 5) {
|
||||
// try 5 instructions
|
||||
op = convert_5(instr[0], instr[1], instr[2], instr[3], instr[4], op_idx);
|
||||
if (op) {
|
||||
|
||||
@@ -336,6 +336,23 @@ FormElement* fix_up_abs_2(LetElement* in, const Env& env, FormPool& pool) {
|
||||
return in;
|
||||
}
|
||||
|
||||
FormElement* rewrite_empty_let(LetElement* in, const Env&, FormPool&) {
|
||||
if (in->entries().size() != 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!in->body()->elts().empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto reg = in->entries().at(0).dest.reg();
|
||||
if (reg.get_kind() == Reg::GPR && !reg.allowed_local_gpr()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return in->entries().at(0).src->try_as_single_element();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Attempt to rewrite a let as another form. If it cannot be rewritten, this will return nullptr.
|
||||
*/
|
||||
@@ -360,6 +377,11 @@ FormElement* rewrite_let(LetElement* in, const Env& env, FormPool& pool) {
|
||||
return as_abs_2;
|
||||
}
|
||||
|
||||
auto as_unused = rewrite_empty_let(in, env, pool);
|
||||
if (as_unused) {
|
||||
return as_unused;
|
||||
}
|
||||
|
||||
// nothing matched.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user