[decompiler] Support accessing 128-bit bitfields (#514)

* support some 128-bit bitfield stuff

* fix bug

* support type stuff correctly
This commit is contained in:
water111
2021-05-21 20:36:04 -04:00
committed by GitHub
parent 6ac399ca33
commit 3fbdd662dc
14 changed files with 497 additions and 279 deletions
+2 -1
View File
@@ -43,7 +43,8 @@ InstructionParser::InstructionParser() {
InstructionKind::BGTZ, InstructionKind::BLTZL, InstructionKind::BGTZL,
InstructionKind::BGEZL, InstructionKind::MTC1, InstructionKind::MFC1,
InstructionKind::MFLO, InstructionKind::MFHI, InstructionKind::MTLO1,
InstructionKind::MFLO1, InstructionKind::SYNCL, InstructionKind::PCPYUD}) {
InstructionKind::MFLO1, InstructionKind::SYNCL, InstructionKind::PCPYUD,
InstructionKind::PEXTUW, InstructionKind::POR}) {
auto& info = gOpcodeInfo[int(i)];
if (info.defined) {
m_opcode_name_lookup[info.name] = int(i);
+37 -1
View File
@@ -289,7 +289,11 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
// this could be a bitfield access or a multiply.
// we pick bitfield access if the parent is a bitfield.
if (dynamic_cast<BitFieldType*>(dts.ts.lookup_type(arg0_type.typespec()))) {
return TP_Type::make_from_left_shift_bitfield(arg0_type.typespec(), m_args[1].get_int());
return TP_Type::make_from_left_shift_bitfield(arg0_type.typespec(), m_args[1].get_int(),
false);
} else if (arg0_type.kind == TP_Type::Kind::PCPYUD_BITFIELD) {
return TP_Type::make_from_left_shift_bitfield(arg0_type.get_bitfield_type(),
m_args[1].get_int(), true);
} else {
return TP_Type::make_from_product(1ull << m_args[1].get_int(), is_signed(dts, arg0_type));
}
@@ -307,6 +311,9 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
if (arg0_type.kind == TP_Type::Kind::LEFT_SHIFTED_BITFIELD && m_args[1].is_int()) {
// second op in left/right shift combo
int end_bit = 64 - arg0_type.get_left_shift();
if (arg0_type.pcpyud()) {
end_bit += 64;
}
int size = 64 - m_args[1].get_int();
int start_bit = end_bit - size;
@@ -592,6 +599,35 @@ TypeState AsmOp::propagate_types_internal(const TypeState& input,
return result;
}
if (m_instr.kind == InstructionKind::PCPYUD) {
if (m_src[1] && m_src[1]->reg() == Register(Reg::GPR, Reg::R0)) {
assert(m_src[0]);
auto& in_type = result.get(m_src[0]->reg());
auto bf = dynamic_cast<BitFieldType*>(dts.ts.lookup_type(in_type.typespec()));
if (bf) {
assert(m_dst);
result.get(m_dst->reg()) = TP_Type::make_from_pcpyud_bitfield(in_type.typespec());
return result;
}
}
}
// pextuw t0, r0, gp
if (m_instr.kind == InstructionKind::PEXTUW) {
if (m_src[0] && m_src[0]->reg() == Register(Reg::GPR, Reg::R0)) {
// sponge
assert(m_src[1]);
auto type = dts.ts.lookup_type(result.get(m_src[1]->reg()).typespec());
auto as_bitfield = dynamic_cast<BitFieldType*>(type);
if (as_bitfield) {
auto field = find_field(dts.ts, as_bitfield, 64, 32, true);
assert(m_dst);
result.get(m_dst->reg()) = TP_Type::make_from_ts(field.type());
return result;
}
}
}
if (m_dst.has_value()) {
auto kind = m_dst->reg().get_kind();
if (kind == Reg::FPR) {
+208 -15
View File
@@ -1121,12 +1121,22 @@ void SimpleExpressionElement::update_from_stack_left_shift(const Env& env,
} else {
// try to turn this into a multiplication, if possible
if (m_expr.get_arg(1).is_int()) {
auto args = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects);
int sa = m_expr.get_arg(1).get_int();
auto as_ba = args.at(0)->try_as_element<BitfieldAccessElement>();
if (as_ba) {
BitfieldManip step(BitfieldManip::Kind::LEFT_SHIFT, m_expr.get_arg(1).get_int());
auto other = as_ba->push_step(step, env.dts->ts, pool, env);
assert(!other); // shouldn't be complete.
result->push_back(as_ba);
return;
}
// somewhat arbitrary threshold to switch from multiplications to shift.
if (sa < 10) {
s64 multiplier = (s64(1) << sa);
auto args = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects);
auto new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::MULTIPLICATION), args.at(0),
pool.alloc_single_element_form<SimpleAtomElement>(
@@ -1134,6 +1144,33 @@ void SimpleExpressionElement::update_from_stack_left_shift(const Env& env,
result->push_back(new_form);
return;
}
auto arg0_i = is_int_type(env, m_my_idx, m_expr.get_arg(0).var());
auto arg0_u = is_uint_type(env, m_my_idx, m_expr.get_arg(0).var());
if (!arg0_i && !arg0_u) {
auto bti = dynamic_cast<EnumType*>(env.dts->ts.lookup_type(arg0_type));
if (bti) {
auto new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::SHL), args.at(0),
cast_form(
pool.alloc_single_element_form<SimpleAtomElement>(nullptr, m_expr.get_arg(1)),
arg0_type, pool, env));
result->push_back(new_form);
} else {
auto new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::SHL),
pool.alloc_single_element_form<CastElement>(nullptr, TypeSpec("int"), args.at(0)),
pool.alloc_single_element_form<SimpleAtomElement>(nullptr, m_expr.get_arg(1)));
result->push_back(new_form);
}
} else {
auto new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::SHL), args.at(0),
pool.alloc_single_element_form<SimpleAtomElement>(nullptr, m_expr.get_arg(1)));
result->push_back(new_form);
}
return;
}
update_from_stack_copy_first_int_2(env, FixedOperatorKind::SHL, pool, stack, result,
@@ -1226,22 +1263,52 @@ void SimpleExpressionElement::update_from_stack_right_shift_arith(const Env& env
assert(other); // should be a high field.
result->push_back(other);
} else {
if (m_expr.get_arg(1).is_int() && m_expr.get_arg(1).get_int() < 10) {
auto arg0_i = is_int_type(env, m_my_idx, m_expr.get_arg(0).var());
auto arg =
pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0);
int sa = m_expr.get_arg(1).get_int();
if (m_expr.get_arg(1).is_int()) {
if (m_expr.get_arg(1).get_int() < 10) {
auto arg0_i = is_int_type(env, m_my_idx, m_expr.get_arg(0).var());
auto arg =
pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0);
int sa = m_expr.get_arg(1).get_int();
if (!arg0_i) {
arg = cast_form(arg, TypeSpec("int"), pool, env);
if (!arg0_i) {
arg = cast_form(arg, TypeSpec("int"), pool, env);
}
s64 multiplier = (s64(1) << sa);
auto new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::DIVISION), arg,
pool.alloc_single_element_form<SimpleAtomElement>(
nullptr, SimpleAtom::make_int_constant(multiplier)));
result->push_back(new_form);
return;
}
auto arg0_i = is_int_type(env, m_my_idx, m_expr.get_arg(0).var());
auto arg0_u = is_uint_type(env, m_my_idx, m_expr.get_arg(0).var());
auto args = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects);
auto as_ba = args.at(0)->try_as_element<BitfieldAccessElement>();
if (as_ba) {
BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_ARITH, m_expr.get_arg(1).get_int());
auto other = as_ba->push_step(step, env.dts->ts, pool, env);
assert(other); // should be a high field.
result->push_back(other);
return;
}
if (!arg0_i && !arg0_u) {
auto new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::SAR),
pool.alloc_single_element_form<CastElement>(nullptr, TypeSpec("int"), args.at(0)),
pool.alloc_single_element_form<SimpleAtomElement>(nullptr, m_expr.get_arg(1)));
result->push_back(new_form);
} else {
auto new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::SAR), args.at(0),
pool.alloc_single_element_form<SimpleAtomElement>(nullptr, m_expr.get_arg(1)));
result->push_back(new_form);
}
s64 multiplier = (s64(1) << sa);
auto new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(FixedOperatorKind::DIVISION), arg,
pool.alloc_single_element_form<SimpleAtomElement>(
nullptr, SimpleAtom::make_int_constant(multiplier)));
result->push_back(new_form);
return;
}
@@ -2728,7 +2795,7 @@ void ReturnElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
namespace {
void push_asm_srl_to_stack(const AsmOp* op,
FormElement* form_elt,
FormElement* /*form_elt*/,
const Env& env,
FormPool& pool,
FormStack& stack) {
@@ -2754,6 +2821,123 @@ void push_asm_srl_to_stack(const AsmOp* op,
assert(other); // should be a high field.
stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, other), true,
env.get_variable_type(*dst, true));
} else {
// stack.push_form_element(form_elt, true);
auto src_var = pop_to_forms({*var}, env, pool, stack, true).at(0);
auto as_ba = src_var->try_as_element<BitfieldAccessElement>();
if (as_ba) {
BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_LOGICAL_32BIT, integer);
auto other = as_ba->push_step(step, env.dts->ts, pool, env);
assert(other); // should immediately get a field.
stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, other), true,
env.get_variable_type(*dst, true));
} else {
throw std::runtime_error("Got invalid bitfield manip for srl");
}
}
}
void push_asm_sllv_to_stack(const AsmOp* op,
FormElement* form_elt,
const Env& env,
FormPool& pool,
FormStack& stack) {
auto var = op->src(0);
assert(var.has_value());
auto dst = op->dst();
assert(dst.has_value());
auto sav = op->src(1);
assert(sav.has_value());
auto arg0_type = env.get_variable_type(*var, true);
auto type_info = env.dts->ts.lookup_type(arg0_type);
auto bitfield_info = dynamic_cast<BitFieldType*>(type_info);
if (sav->reg() == Register(Reg::GPR, Reg::R0)) {
if (bitfield_info) {
auto base = pop_to_forms({*var}, env, pool, stack, true).at(0);
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
BitfieldManip step(BitfieldManip::Kind::SLLV_SEXT, 0);
auto other = read_elt->push_step(step, env.dts->ts, pool, env);
assert(other); // should immediately get a field.
stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, other), true,
env.get_variable_type(*dst, true));
} else {
auto src_var = pop_to_forms({*var}, env, pool, stack, true).at(0);
auto as_ba = src_var->try_as_element<BitfieldAccessElement>();
if (as_ba) {
BitfieldManip step(BitfieldManip::Kind::SLLV_SEXT, 0);
auto other = as_ba->push_step(step, env.dts->ts, pool, env);
assert(other); // should immediately get a field.
stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, other), true,
env.get_variable_type(*dst, true));
} else {
throw std::runtime_error("Got invalid bitfield manip for sllv");
}
}
} else {
stack.push_form_element(form_elt, true);
}
}
void push_asm_pcpyud_to_stack(const AsmOp* op,
FormElement* form_elt,
const Env& env,
FormPool& pool,
FormStack& stack) {
// pcpyud v1, gp, r0 for example.
auto var = op->src(0);
assert(var.has_value());
auto dst = op->dst();
assert(dst.has_value());
auto possible_r0 = op->src(1);
assert(possible_r0.has_value());
auto arg0_type = env.get_variable_type(*var, true);
auto type_info = env.dts->ts.lookup_type(arg0_type);
auto bitfield_info = dynamic_cast<BitFieldType*>(type_info);
if (bitfield_info && possible_r0->reg() == Register(Reg::GPR, Reg::R0)) {
auto base = pop_to_forms({*var}, env, pool, stack, true).at(0);
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
read_elt->push_pcpyud();
stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, read_elt), true,
env.get_variable_type(*dst, true));
} else {
stack.push_form_element(form_elt, true);
}
}
void push_asm_pextuw_to_stack(const AsmOp* op,
FormElement* form_elt,
const Env& env,
FormPool& pool,
FormStack& stack) {
// (.pextuw t0-0 r0-0 obj)
auto var = op->src(1);
assert(var.has_value());
auto dst = op->dst();
assert(dst.has_value());
auto possible_r0 = op->src(0);
assert(possible_r0.has_value());
auto arg0_type = env.get_variable_type(*var, true);
auto type_info = env.dts->ts.lookup_type(arg0_type);
auto bitfield_info = dynamic_cast<BitFieldType*>(type_info);
if (bitfield_info && possible_r0->reg() == Register(Reg::GPR, Reg::R0)) {
auto base = pop_to_forms({*var}, env, pool, stack, true).at(0);
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
BitfieldManip step(BitfieldManip::Kind::PEXTUW, 0);
auto other = read_elt->push_step(step, env.dts->ts, pool, env);
assert(other); // should immediately get a field.
stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, other), true,
env.get_variable_type(*dst, true));
} else {
stack.push_form_element(form_elt, true);
}
@@ -2768,6 +2952,15 @@ void push_asm_to_stack(const AsmOp* op,
case InstructionKind::SRL:
push_asm_srl_to_stack(op, form_elt, env, pool, stack);
break;
case InstructionKind::SLLV:
push_asm_sllv_to_stack(op, form_elt, env, pool, stack);
break;
case InstructionKind::PCPYUD:
push_asm_pcpyud_to_stack(op, form_elt, env, pool, stack);
break;
case InstructionKind::PEXTUW:
push_asm_pextuw_to_stack(op, form_elt, env, pool, stack);
break;
default:
stack.push_form_element(form_elt, true);
break;
+63 -3
View File
@@ -171,7 +171,9 @@ const BitField& find_field(const TypeSystem& ts,
std::optional<bool> looking_for_unsigned) {
for (auto& field : type->fields()) {
if (field.size() == size && field.offset() == start_bit) {
bool is_int = ts.tc(TypeSpec("integer"), field.type());
// the GOAL compiler sign extended floats.
bool is_int =
ts.tc(TypeSpec("integer"), field.type()) || ts.tc(TypeSpec("float"), field.type());
bool is_uint = ts.tc(TypeSpec("uinteger"), field.type());
// allow sign match, or unsigned+not a number.
@@ -182,6 +184,12 @@ const BitField& find_field(const TypeSystem& ts,
// matched!
return field;
}
// this is a hack to work around bad code where extracting the (pointer process)
// would sign extend the address.
if (!is_int && !is_uint && type->get_name() == "handle") {
return field;
}
} else {
return field;
}
@@ -286,6 +294,13 @@ std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
} // namespace
void BitfieldAccessElement::push_pcpyud() {
if (!m_steps.empty()) {
throw std::runtime_error("Unexpected pcpyud!");
}
m_got_pcpyud = true;
}
/*!
* Add a step to the bitfield access. If this completes the access, returns a form representing the
* access
@@ -294,6 +309,8 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step,
const TypeSystem& ts,
FormPool& pool,
const Env& env) {
int pcpyud_offset = m_got_pcpyud ? 64 : 0;
if (m_steps.empty() && step.kind == BitfieldManip::Kind::LEFT_SHIFT) {
// for left/right shift combo to get a field.
m_steps.push_back(step);
@@ -305,7 +322,38 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step,
bool is_unsigned = step.right_shift_unsigned();
int shift_size = step.get_shift_start_bit();
int size = shift_size - step.amount;
int start_bit = shift_size - size;
int start_bit = pcpyud_offset + shift_size - size;
auto type = ts.lookup_type(m_type);
auto as_bitfield = dynamic_cast<BitFieldType*>(type);
assert(as_bitfield);
auto field = find_field(ts, as_bitfield, start_bit, size, is_unsigned);
auto result =
pool.alloc_element<DerefElement>(m_base, false, DerefToken::make_field_name(field.name()));
result->inline_nested();
return result;
}
if (m_steps.empty() && step.kind == BitfieldManip::Kind::SLLV_SEXT) {
bool is_unsigned = false;
int size = 32;
int start_bit = 0 + pcpyud_offset;
auto type = ts.lookup_type(m_type);
auto as_bitfield = dynamic_cast<BitFieldType*>(type);
assert(as_bitfield);
auto field = find_field(ts, as_bitfield, start_bit, size, is_unsigned);
auto result =
pool.alloc_element<DerefElement>(m_base, false, DerefToken::make_field_name(field.name()));
result->inline_nested();
return result;
}
if (m_steps.empty() && step.kind == BitfieldManip::Kind::PEXTUW) {
if (m_got_pcpyud) {
throw std::runtime_error("unknown pcpyud PEXTUW sequence in bitfield");
}
bool is_unsigned = true;
int size = 32;
int start_bit = 64;
auto type = ts.lookup_type(m_type);
auto as_bitfield = dynamic_cast<BitFieldType*>(type);
assert(as_bitfield);
@@ -318,7 +366,7 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step,
if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LEFT_SHIFT) {
// second op in left/right shift combo
int end_bit = 64 - m_steps.at(0).amount;
int end_bit = pcpyud_offset + 64 - m_steps.at(0).amount;
if (!step.is_right_shift()) {
throw std::runtime_error("Unexpected step 1");
}
@@ -342,12 +390,18 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step,
if (m_steps.empty() && step.kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT) {
// and with mask
if (m_got_pcpyud) {
throw std::runtime_error("unknown pcpyud LOGAND_WITH_CONSTANT_INT sequence in bitfield");
}
m_steps.push_back(step);
return nullptr;
}
if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT &&
step.kind == BitfieldManip::Kind::NONZERO_COMPARE) {
if (m_got_pcpyud) {
throw std::runtime_error("unknown pcpyud NONZERO_COMPARE sequence in bitfield");
}
auto type = ts.lookup_type(m_type);
auto as_bitfield = dynamic_cast<BitFieldType*>(type);
assert(as_bitfield);
@@ -364,6 +418,9 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step,
if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT &&
step.kind == BitfieldManip::Kind::LOGIOR_WITH_CONSTANT_INT) {
if (m_got_pcpyud) {
throw std::runtime_error("unknown pcpyud LOGIOR_WITH_CONSTANT_INT sequence in bitfield");
}
// this is setting a bitfield to a constant.
// first, let's check that the mask is used properly:
u64 mask = m_steps.at(0).amount;
@@ -399,6 +456,9 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step,
if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT &&
step.kind == BitfieldManip::Kind::LOGIOR_WITH_FORM) {
if (m_got_pcpyud) {
throw std::runtime_error("unknown pcpyud LOGIOR_WITH_FORM sequence in bitfield");
}
// this is setting a bitfield to a variable
u64 mask = m_steps.at(0).amount;
+8
View File
@@ -18,6 +18,8 @@ struct BitfieldManip {
LOGIOR_WITH_FORM,
LOGAND_WITH_FORM,
NONZERO_COMPARE,
SLLV_SEXT, // sllv x, y, r0
PEXTUW,
INVALID
} kind = Kind::INVALID;
s64 amount = -1;
@@ -72,6 +74,10 @@ struct BitfieldManip {
return "logand-form";
case Kind::NONZERO_COMPARE:
return "nonzero-compare";
case Kind::SLLV_SEXT:
return "sllv-sext";
case Kind::PEXTUW:
return "pextuw";
case Kind::INVALID:
default:
assert(false);
@@ -94,8 +100,10 @@ class BitfieldAccessElement : public FormElement {
const TypeSystem& ts,
FormPool& pool,
const Env& env);
void push_pcpyud();
private:
bool m_got_pcpyud = false;
Form* m_base = nullptr;
TypeSpec m_type;
std::vector<BitfieldManip> m_steps;
+10 -1
View File
@@ -215,7 +215,6 @@ std::unique_ptr<AtomicOp> make_standard_store(const Instruction& i0,
std::unique_ptr<AtomicOp> make_asm_op(const Instruction& i0, int idx) {
switch (i0.kind) {
case InstructionKind::POR:
case InstructionKind::SLLV: // goal will use dsllv
case InstructionKind::SLL: // goal will use dsll
case InstructionKind::PCPYUD:
@@ -442,6 +441,14 @@ std::unique_ptr<AtomicOp> convert_or_1(const Instruction& i0, int idx) {
return std::make_unique<SetVarOp>(dest, src, idx);
}
std::unique_ptr<AtomicOp> convert_por_1(const Instruction& i0, int idx) {
if (is_gpr_3(i0, InstructionKind::POR, {}, {}, rr0())) {
return std::make_unique<SetVarOp>(make_dst_var(i0, idx),
make_src_atom(i0.get_src(0).get_reg(), idx).as_expr(), idx);
}
return std::make_unique<AsmOp>(i0, idx);
}
std::unique_ptr<AtomicOp> convert_ori_1(const Instruction& i0, int idx) {
auto dest = make_dst_var(i0, idx);
SimpleExpression src;
@@ -713,6 +720,8 @@ std::unique_ptr<AtomicOp> convert_1(const Instruction& i0, int idx, bool hint_in
switch (i0.kind) {
case InstructionKind::OR:
return convert_or_1(i0, idx);
case InstructionKind::POR:
return convert_por_1(i0, idx);
case InstructionKind::ORI:
return convert_ori_1(i0, idx);
case InstructionKind::AND:
@@ -381,15 +381,12 @@
],
// RES
"(method 4 res-tag)": [[12, "a0", "type"]],
"(method 19 res-lump)": [
[46, "t2", "(pointer uint64)"],
[100, "t3", "(pointer uint64)"],
[184, "t5", "(pointer uint64)"],
[64, "t6", "(pointer uint64)"]
],
"(method 13 res-lump)": [[6, "a0", "res-tag"]],
"(method 14 res-lump)": [[2, "a0", "res-tag"]],
// SHADOW-CPU-H
"(method 10 shadow-control)": [[1, "v1", "int"]],
+12 -2
View File
@@ -60,7 +60,13 @@ std::string TP_Type::print() const {
case Kind::NON_VIRTUAL_METHOD:
return fmt::format("<method {}>", m_ts.print());
case Kind::LEFT_SHIFTED_BITFIELD:
return fmt::format("(<{}> << {})", m_ts.print(), m_int);
if (m_pcpyud) {
return fmt::format("(<{}> << {}) :u", m_ts.print(), m_int);
} else {
return fmt::format("(<{}> << {})", m_ts.print(), m_int);
}
case Kind::PCPYUD_BITFIELD:
return fmt::format("<pcpyud {}>", m_ts.print());
case Kind::INVALID:
default:
assert(false);
@@ -108,7 +114,9 @@ bool TP_Type::operator==(const TP_Type& other) const {
return m_int == other.m_int && m_ts == other.m_ts &&
m_extra_multiplier == other.m_extra_multiplier;
case Kind::LEFT_SHIFTED_BITFIELD:
return m_int == other.m_int && m_ts == other.m_ts;
return m_int == other.m_int && m_ts == other.m_ts && m_pcpyud == other.m_pcpyud;
case Kind::PCPYUD_BITFIELD:
return m_pcpyud == other.m_pcpyud && m_ts == other.m_ts;
case Kind::INVALID:
default:
assert(false);
@@ -163,6 +171,8 @@ TypeSpec TP_Type::typespec() const {
return m_ts;
case Kind::LEFT_SHIFTED_BITFIELD:
return TypeSpec("int"); // ideally this is never used.
case Kind::PCPYUD_BITFIELD:
return TypeSpec("int");
case Kind::INVALID:
default:
assert(false);
+22 -2
View File
@@ -32,6 +32,7 @@ class TP_Type {
DYNAMIC_METHOD_ACCESS, // partial access into a
VIRTUAL_METHOD,
NON_VIRTUAL_METHOD,
PCPYUD_BITFIELD,
LEFT_SHIFTED_BITFIELD, // (bitfield << some-constant)
INVALID
} kind = Kind::UNINITIALIZED;
@@ -59,6 +60,7 @@ class TP_Type {
case Kind::VIRTUAL_METHOD:
case Kind::NON_VIRTUAL_METHOD:
case Kind::LEFT_SHIFTED_BITFIELD:
case Kind::PCPYUD_BITFIELD:
return false;
case Kind::UNINITIALIZED:
case Kind::OBJECT_NEW_METHOD:
@@ -211,11 +213,20 @@ class TP_Type {
return result;
}
static TP_Type make_from_left_shift_bitfield(const TypeSpec& ts, int amount) {
static TP_Type make_from_left_shift_bitfield(const TypeSpec& ts, int amount, bool pcpyud) {
TP_Type result;
result.kind = Kind::LEFT_SHIFTED_BITFIELD;
result.m_ts = ts;
result.m_int = amount;
result.m_pcpyud = pcpyud;
return result;
}
static TP_Type make_from_pcpyud_bitfield(const TypeSpec& ts) {
TP_Type result;
result.kind = Kind::PCPYUD_BITFIELD;
result.m_ts = ts;
result.m_pcpyud = true;
return result;
}
@@ -265,14 +276,23 @@ class TP_Type {
}
const TypeSpec& get_bitfield_type() const {
assert(kind == Kind::LEFT_SHIFTED_BITFIELD);
assert(kind == Kind::LEFT_SHIFTED_BITFIELD || kind == Kind::PCPYUD_BITFIELD);
return m_ts;
}
bool pcpyud() const {
if (kind == Kind::LEFT_SHIFTED_BITFIELD) {
return m_pcpyud;
}
assert(false);
return false;
}
private:
TypeSpec m_ts;
std::string m_str;
int64_t m_int = 0;
bool m_pcpyud = false; // have we extracted the top doubleword of a bitfield?
int64_t m_extra_multiplier = 0;
};
+24 -153
View File
@@ -767,160 +767,31 @@
;; definition for method 3 of type gif-tag
;; INFO: Return type mismatch object vs gif-tag.
;; WARN: Unsupported inline assembly instruction kind - [por gp, a0, r0]
;; WARN: Unsupported inline assembly instruction kind - [por a2, gp, r0]
;; WARN: Unsupported inline assembly instruction kind - [srl a2, v1, 28]
(defmethod inspect gif-tag ((obj gif-tag))
(local-vars
(r0-0 none)
(v1-6 uint128)
(v1-8 uint128)
(v1-10 uint128)
(v1-12 uint128)
(v1-14 uint128)
(v1-16 uint128)
(v1-18 uint128)
(v1-20 uint128)
(v1-21 uint128)
(v1-23 uint128)
(v1-25 uint128)
(v1-27 uint128)
(v1-29 uint128)
(v1-31 uint128)
(v1-33 uint128)
(v1-35 uint128)
(a2-0 uint128)
(a2-15 uint128)
(gp-0 uint128)
)
(.por gp-0 obj r0-0)
(let ((t9-0 format)
(a0-1 #t)
(a1-0 "[~8x] gif-tag~%")
)
(.por a2-0 gp-0 r0-0)
(t9-0 a0-1 a1-0 a2-0)
)
(format #t "~Tnloop: ~4d~%" (shr (shl (the-as int gp-0) 49) 49))
(format #t "~Teop : ~4d~%" (shr (shl (the-as int gp-0) 48) 63))
(format #t "~Tid : ~4d~%" (shr (shl (the-as int gp-0) 18) 50))
(format #t "~Tpre : ~4d~%" (shr (shl (the-as int gp-0) 17) 63))
(format #t "~Tprim : ~4d~%" (shr (* gp-0 64) 53))
(format #t "~Tflg : ~4d~%" (shr (* gp-0 16) 62))
(format #t "~Tnreg : ~4d~%" (shr (the-as int gp-0) 60))
(let ((t9-8 format)
(a0-9 #t)
(a1-8 "~Tregs0 : ~4d~%")
)
(.pcpyud v1-6 gp-0 r0-0)
(t9-8 a0-9 a1-8 (shr (shl (the-as int v1-6) 60) 60))
)
(let ((t9-9 format)
(a0-10 #t)
(a1-9 "~Tregs1 : ~4d~%")
)
(.pcpyud v1-8 gp-0 r0-0)
(t9-9 a0-10 a1-9 (shr (shl (the-as int v1-8) 56) 60))
)
(let ((t9-10 format)
(a0-11 #t)
(a1-10 "~Tregs2 : ~4d~%")
)
(.pcpyud v1-10 gp-0 r0-0)
(t9-10 a0-11 a1-10 (shr (shl (the-as int v1-10) 52) 60))
)
(let ((t9-11 format)
(a0-12 #t)
(a1-11 "~Tregs3 : ~4d~%")
)
(.pcpyud v1-12 gp-0 r0-0)
(t9-11 a0-12 a1-11 (shr (shl (the-as int v1-12) 48) 60))
)
(let ((t9-12 format)
(a0-13 #t)
(a1-12 "~Tregs4 : ~4d~%")
)
(.pcpyud v1-14 gp-0 r0-0)
(t9-12 a0-13 a1-12 (shr (shl (the-as int v1-14) 44) 60))
)
(let ((t9-13 format)
(a0-14 #t)
(a1-13 "~Tregs5 : ~4d~%")
)
(.pcpyud v1-16 gp-0 r0-0)
(t9-13 a0-14 a1-13 (shr (shl (the-as int v1-16) 40) 60))
)
(let ((t9-14 format)
(a0-15 #t)
(a1-14 "~Tregs6 : ~4d~%")
)
(.pcpyud v1-18 gp-0 r0-0)
(t9-14 a0-15 a1-14 (shr (shl (the-as int v1-18) 36) 60))
)
(let ((t9-15 format)
(a0-16 #t)
(a1-15 "~Tregs7 : ~4d~%")
)
(.pcpyud v1-20 gp-0 r0-0)
(.srl a2-15 v1-20 28)
(t9-15 a0-16 a1-15 a2-15)
)
(let ((t9-16 format)
(a0-17 #t)
(a1-16 "~Tregs8 : ~4d~%")
)
(.pcpyud v1-21 gp-0 r0-0)
(t9-16 a0-17 a1-16 (shr (shl (the-as int v1-21) 28) 60))
)
(let ((t9-17 format)
(a0-18 #t)
(a1-17 "~Tregs9 : ~4d~%")
)
(.pcpyud v1-23 gp-0 r0-0)
(t9-17 a0-18 a1-17 (shr (shl (the-as int v1-23) 24) 60))
)
(let ((t9-18 format)
(a0-19 #t)
(a1-18 "~Tregs10: ~4d~%")
)
(.pcpyud v1-25 gp-0 r0-0)
(t9-18 a0-19 a1-18 (shr (shl (the-as int v1-25) 20) 60))
)
(let ((t9-19 format)
(a0-20 #t)
(a1-19 "~Tregs11: ~4d~%")
)
(.pcpyud v1-27 gp-0 r0-0)
(t9-19 a0-20 a1-19 (shr (shl (the-as int v1-27) 16) 60))
)
(let ((t9-20 format)
(a0-21 #t)
(a1-20 "~Tregs12: ~4d~%")
)
(.pcpyud v1-29 gp-0 r0-0)
(t9-20 a0-21 a1-20 (shr (shl (the-as int v1-29) 12) 60))
)
(let ((t9-21 format)
(a0-22 #t)
(a1-21 "~Tregs13: ~4d~%")
)
(.pcpyud v1-31 gp-0 r0-0)
(t9-21 a0-22 a1-21 (shr (* v1-31 256) 60))
)
(let ((t9-22 format)
(a0-23 #t)
(a1-22 "~Tregs14: ~4d~%")
)
(.pcpyud v1-33 gp-0 r0-0)
(t9-22 a0-23 a1-22 (shr (* v1-33 16) 60))
)
(let ((t9-23 format)
(a0-24 #t)
(a1-23 "~Tregs15: ~4d~%")
)
(.pcpyud v1-35 gp-0 r0-0)
(the-as gif-tag (t9-23 a0-24 a1-23 (shr (the-as int v1-35) 60)))
)
(format #t "[~8x] gif-tag~%" obj)
(format #t "~Tnloop: ~4d~%" (-> obj nloop))
(format #t "~Teop : ~4d~%" (-> obj eop))
(format #t "~Tid : ~4d~%" (-> obj id))
(format #t "~Tpre : ~4d~%" (-> obj pre))
(format #t "~Tprim : ~4d~%" (-> obj prim))
(format #t "~Tflg : ~4d~%" (-> obj flg))
(format #t "~Tnreg : ~4d~%" (-> obj nreg))
(format #t "~Tregs0 : ~4d~%" (-> obj regs0))
(format #t "~Tregs1 : ~4d~%" (-> obj regs1))
(format #t "~Tregs2 : ~4d~%" (-> obj regs2))
(format #t "~Tregs3 : ~4d~%" (-> obj regs3))
(format #t "~Tregs4 : ~4d~%" (-> obj regs4))
(format #t "~Tregs5 : ~4d~%" (-> obj regs5))
(format #t "~Tregs6 : ~4d~%" (-> obj regs6))
(format #t "~Tregs7 : ~4d~%" (-> obj regs7))
(format #t "~Tregs8 : ~4d~%" (-> obj regs8))
(format #t "~Tregs9 : ~4d~%" (-> obj regs9))
(format #t "~Tregs10: ~4d~%" (-> obj regs10))
(format #t "~Tregs11: ~4d~%" (-> obj regs11))
(format #t "~Tregs12: ~4d~%" (-> obj regs12))
(format #t "~Tregs13: ~4d~%" (-> obj regs13))
(format #t "~Tregs14: ~4d~%" (-> obj regs14))
(the-as gif-tag (format #t "~Tregs15: ~4d~%" (-> obj regs15)))
)
;; definition for symbol *fog-color*, type int
+15 -82
View File
@@ -112,94 +112,27 @@
)
;; definition for method 3 of type vec4s
;; INFO: Return type mismatch uint128 vs vec4s.
;; WARN: Unsupported inline assembly instruction kind - [por gp, a0, r0]
;; WARN: Unsupported inline assembly instruction kind - [por a2, gp, r0]
;; WARN: Unsupported inline assembly instruction kind - [sllv a2, gp, r0]
;; WARN: Unsupported inline assembly instruction kind - [sllv a2, v1, r0]
;; WARN: Unsupported inline assembly instruction kind - [por v0, gp, r0]
(defmethod inspect vec4s ((obj vec4s))
(local-vars
(r0-0 none)
(v0-5 uint128)
(v1-0 uint128)
(v1-1 uint128)
(a2-0 uint128)
(a2-1 uint128)
(a2-3 uint128)
(gp-0 uint128)
)
(.por gp-0 obj r0-0)
(let ((t9-0 format)
(a0-1 #t)
(a1-0 "[~8x] ~A~%")
)
(.por a2-0 gp-0 r0-0)
(t9-0 a0-1 a1-0 a2-0 'vec4s)
)
(let ((t9-1 format)
(a0-2 #t)
(a1-1 "~Tx: ~f~%")
)
(.sllv a2-1 gp-0 r0-0)
(t9-1 a0-2 a1-1 a2-1)
)
(format #t "~Ty: ~f~%" (sar (the-as int gp-0) 32))
(let ((t9-3 format)
(a0-4 #t)
(a1-3 "~Tz: ~f~%")
)
(.pcpyud v1-0 gp-0 r0-0)
(.sllv a2-3 v1-0 r0-0)
(t9-3 a0-4 a1-3 a2-3)
)
(let ((t9-4 format)
(a0-5 #t)
(a1-4 "~Tw: ~f~%")
)
(.pcpyud v1-1 gp-0 r0-0)
(t9-4 a0-5 a1-4 (sar (the-as int v1-1) 32))
)
(.por v0-5 gp-0 r0-0)
(the-as vec4s v0-5)
(format #t "[~8x] ~A~%" obj 'vec4s)
(format #t "~Tx: ~f~%" (-> obj x))
(format #t "~Ty: ~f~%" (-> obj y))
(format #t "~Tz: ~f~%" (-> obj z))
(format #t "~Tw: ~f~%" (-> obj w))
obj
)
;; definition for method 2 of type vec4s
;; INFO: Return type mismatch uint128 vs vec4s.
;; WARN: Unsupported inline assembly instruction kind - [por gp, a0, r0]
;; WARN: Unsupported inline assembly instruction kind - [sllv a2, gp, r0]
;; WARN: Unsupported inline assembly instruction kind - [sllv t0, v1, r0]
;; WARN: Unsupported inline assembly instruction kind - [por t2, gp, r0]
;; WARN: Unsupported inline assembly instruction kind - [por v0, gp, r0]
(defmethod print vec4s ((obj vec4s))
(local-vars
(r0-0 none)
(v0-1 uint128)
(v1-0 uint128)
(v1-1 uint128)
(a2-0 uint128)
(t0-0 uint128)
(t2-0 uint128)
(gp-0 uint128)
(format
#t
"#<vector ~F ~F ~F ~F @ #x~X>"
(-> obj x)
(-> obj y)
(-> obj z)
(-> obj w)
obj
)
(.por gp-0 obj r0-0)
(let ((t9-0 format)
(a0-1 #t)
(a1-0 "#<vector ~F ~F ~F ~F @ #x~X>")
)
(.sllv a2-0 gp-0 r0-0)
(let ((a3-0 (sar (the-as int gp-0) 32)))
(.pcpyud v1-0 gp-0 r0-0)
(.sllv t0-0 v1-0 r0-0)
(.pcpyud v1-1 gp-0 r0-0)
(let ((t1-0 (sar (the-as int v1-1) 32)))
(.por t2-0 gp-0 r0-0)
(t9-0 a0-1 a1-0 a2-0 a3-0 t0-0 t1-0 t2-0)
)
)
)
(.por v0-1 gp-0 r0-0)
(the-as vec4s v0-1)
obj
)
;; definition of type bfloat
@@ -286,9 +286,8 @@
;; definition for method 2 of type handle
;; WARN: Unsupported inline assembly instruction kind - [subu a2, v1, s7]
;; WARN: Unsupported inline assembly instruction kind - [sllv a2, v1, r0]
(defmethod print handle ((obj handle))
(local-vars (r0-0 none) (a2-0 int) (a2-2 (pointer process)) (s7-0 none))
(local-vars (a2-0 int) (s7-0 none))
(cond
((nonzero? obj)
(let ((t9-0 format)
@@ -297,15 +296,17 @@
(v1-0 obj)
)
(.subu a2-0 v1-0 s7-0)
(t9-0 a0-1 a1-0 (and (nonzero? a2-0) (begin
(.sllv a2-2 v1-0 r0-0)
(let ((a3-0 (-> a2-2 0)))
(if (= (-> v1-0 pid) (-> a3-0 pid))
a3-0
)
)
)
)
(t9-0
a0-1
a1-0
(and
(nonzero? a2-0)
(let ((a3-0 (-> (the-as (pointer process) (-> v1-0 process)) 0)))
(if (= (-> v1-0 pid) (-> a3-0 pid))
a3-0
)
)
)
(-> obj pid)
)
)
@@ -1142,4 +1142,88 @@ TEST_F(FormRegressionTest, Method11FontContext) {
" arg0\n"
" )";
test_with_expr(func, type, expected);
}
// 128-bit bitfields, also type for pextuw
TEST_F(FormRegressionTest, Method4ResTag) {
std::string func =
"sll r0, r0, 0\n"
"L135:\n"
" pcpyud v1, a0, r0\n"
" dsrl32 v1, v1, 31\n"
" bne v1, r0, L136\n"
" sll r0, r0, 0\n"
" pcpyud v1, a0, r0\n"
" dsll v1, v1, 1\n"
" dsrl32 v1, v1, 17\n"
" dsll v0, v1, 2\n"
" beq r0, r0, L137\n"
" sll r0, r0, 0\n"
"L136:\n"
" pcpyud v1, a0, r0\n"
" dsll v1, v1, 1\n"
" dsrl32 v1, v1, 17\n"
" pextuw a0, r0, a0\n"
" lhu a0, 8(a0)\n"
" multu3 v0, v1, a0\n"
"L137:\n"
" jr ra\n"
" daddu sp, sp, r0";
std::string type = "(function res-tag int)";
std::string expected =
"(the-as int (if (zero? (-> arg0 inlined?))\n"
" (* (-> arg0 elt-count) 4)\n"
" (* (the-as uint (-> arg0 elt-count)) (-> arg0 elt-type size))\n"
" )\n"
" )";
test_with_expr(func, type, expected);
}
TEST_F(FormRegressionTest, Method2Vec4s) {
std::string func =
"sll r0, r0, 0\n"
" daddiu sp, sp, -32\n"
" sd ra, 0(sp)\n"
" sd fp, 8(sp)\n"
" or fp, t9, r0\n"
" sq gp, 16(sp)\n"
" por gp, a0, r0\n"
" lw t9, format(s7)\n"
" daddiu a0, s7, #t\n"
" daddiu a1, fp, L344\n"
" sllv a2, gp, r0\n"
" dsra32 a3, gp, 0\n"
" pcpyud v1, gp, r0\n"
" sllv t0, v1, r0\n"
" pcpyud v1, gp, r0\n"
" dsra32 t1, v1, 0\n"
" por t2, gp, r0\n"
" jalr ra, t9\n"
" sll v0, ra, 0\n"
" por v0, gp, r0\n"
" ld ra, 0(sp)\n"
" ld fp, 8(sp)\n"
" lq gp, 16(sp)\n"
" jr ra\n"
" daddiu sp, sp, 32";
std::string type = "(function vec4s vec4s)";
std::string expected =
"(begin\n"
" (format\n"
" #t\n"
" \"#<vector ~F ~F ~F ~F @ #x~X>\"\n"
" (-> arg0 x)\n"
" (-> arg0 y)\n"
" (-> arg0 z)\n"
" (-> arg0 w)\n"
" arg0\n"
" )\n"
" arg0\n"
" )";
test_with_expr(func, type, expected, false, "", {{"L344", "#<vector ~F ~F ~F ~F @ #x~X>"}});
}
-5
View File
@@ -75,8 +75,6 @@ const std::unordered_set<std::string> g_functions_to_skip_compiling = {
"abs", "ash", "min", "max", "lognor",
// weird PS2 specific debug registers:
"breakpoint-range-set!",
// int128 fancy stuff.
"(method 3 vec4s)", "(method 2 vec4s)",
// does weird stuff with the type system.
"print", "printl", "inspect",
// inline assembly
@@ -119,9 +117,6 @@ const std::unordered_set<std::string> g_functions_to_skip_compiling = {
// dma-disasm
"disasm-dma-list", // missing a single cast :(
// gs
"(method 3 gif-tag)", // inspect for a 128-bit type.
// math camera
"transform-point-vector!", "transform-point-qword!", "transform-point-vector-scale!",