[decomp] Fix flipped byte addressing add and support some VU ops in the parser (#631)

* fix flipped order

* format

* parser hack

* actually flip
This commit is contained in:
water111 2021-06-26 20:02:21 -04:00 committed by GitHub
parent 69e24ae577
commit 10b00e57ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 140 additions and 14 deletions

View file

@ -44,7 +44,8 @@ InstructionParser::InstructionParser() {
InstructionKind::BGEZL, InstructionKind::MTC1, InstructionKind::MFC1,
InstructionKind::MFLO, InstructionKind::MFHI, InstructionKind::MTLO1,
InstructionKind::MFLO1, InstructionKind::SYNCL, InstructionKind::PCPYUD,
InstructionKind::PEXTUW, InstructionKind::POR}) {
InstructionKind::PEXTUW, InstructionKind::POR, InstructionKind::VMOVE,
InstructionKind::VSUB, InstructionKind::LQC2, InstructionKind::SQC2}) {
auto& info = gOpcodeInfo[int(i)];
if (info.defined) {
m_opcode_name_lookup[info.name] = int(i);
@ -72,6 +73,29 @@ std::string get_until_space(std::string& instr) {
return name;
}
std::string get_instr_name(std::string& instr) {
assert(!instr.empty());
size_t i;
for (i = 0; i < instr.length(); i++) {
if (instr[i] == ' ') {
break;
}
// add.s should not stop at the .
if (instr.size() > (i + 1) && instr[0] == 'v' && instr[i + 1] != 's' && instr[i + 1] != 'l' &&
instr[i + 1] != 'e' && instr[i] == '.') {
break;
}
}
auto name = instr.substr(0, i);
if (i == instr.length()) {
instr.clear();
} else {
instr = instr.substr(i + 1);
}
return name;
}
std::string get_comma_separated(std::string& instr) {
assert(!instr.empty());
auto arg = get_until_space(instr);
@ -149,12 +173,52 @@ std::vector<std::string> string_to_lines(const std::string& str) {
}
}
u8 cop2_dst(const std::string& str) {
auto ptr = str.data();
u8 result = 0;
if (!*ptr) {
return result;
}
if (*ptr == 'x') {
result |= 8;
ptr++;
}
if (!*ptr) {
return result;
}
if (*ptr == 'y') {
result |= 4;
ptr++;
}
if (!*ptr) {
return result;
}
if (*ptr == 'z') {
result |= 2;
ptr++;
}
if (!*ptr) {
return result;
}
if (*ptr == 'w') {
result |= 1;
ptr++;
}
if (*ptr) {
assert(false);
}
return result;
}
} // namespace
Instruction InstructionParser::parse_single_instruction(
std::string str,
const std::vector<DecompilerLabel>& labels) {
auto name = get_until_space(str);
auto name = get_instr_name(str);
auto lookup = m_opcode_name_lookup.find(name);
if (lookup == m_opcode_name_lookup.end()) {
throw std::runtime_error("InstructionParser cannot handle opcode " + name);
@ -198,6 +262,19 @@ Instruction InstructionParser::parse_single_instruction(
}
} break;
case DecodeType::VF: {
auto reg_name = get_comma_separated(str);
Register reg(reg_name);
assert(reg.get_kind() == Reg::VF);
InstructionAtom atom;
atom.set_reg(reg);
if (step.is_src) {
instr.add_src(atom);
} else {
instr.add_dst(atom);
}
} break;
case DecodeType::IMM: {
InstructionAtom atom;
std::string atom_str;
@ -248,6 +325,13 @@ Instruction InstructionParser::parse_single_instruction(
instr.add_dst(atom);
}
} break;
case DecodeType::DEST: {
auto thing = get_until_space(str);
instr.cop2_dest = cop2_dst(thing);
break;
}
default:
assert(false);
}

View file

@ -150,6 +150,14 @@ Register::Register(const std::string& name) {
}
}
// next vfs
for (int i = 0; i < 32; i++) {
if (name == vf_names[i]) {
id = (Reg::VF << REG_CATEGORY_SHIFT) | i;
return;
}
}
throw std::runtime_error("Unknown register name: " + name);
}

View file

@ -383,8 +383,9 @@ FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const {
// we pass along the register offset because code generation seems to be a bit
// different in different cases.
auto source = pool.alloc_element<ArrayFieldAccess>(
ro.var, tokens, input_type.get_multiplier(), ro.offset);
auto source =
pool.alloc_element<ArrayFieldAccess>(ro.var, tokens, input_type.get_multiplier(),
ro.offset, input_type.flipped_add_order());
// auto val = pool.alloc_single_element_form<SimpleExpressionElement>(
// nullptr, m_value.as_expr(), m_my_idx);
@ -529,8 +530,8 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
// we pass along the register offset because code generation seems to be a bit
// different in different cases.
return pool.alloc_single_element_form<ArrayFieldAccess>(nullptr, ro.var, tokens,
rd_in.stride, ro.offset);
return pool.alloc_single_element_form<ArrayFieldAccess>(
nullptr, ro.var, tokens, rd_in.stride, ro.offset, input_type.flipped_add_order());
}
}

View file

@ -492,17 +492,19 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
}
if (m_kind == Kind::ADD && arg0_type.is_product() && arg1_type.kind == TP_Type::Kind::TYPESPEC) {
return TP_Type::make_object_plus_product(arg1_type.typespec(), arg0_type.get_multiplier());
return TP_Type::make_object_plus_product(arg1_type.typespec(), arg0_type.get_multiplier(),
true);
}
if (m_kind == Kind::ADD && arg1_type.is_product() && arg0_type.kind == TP_Type::Kind::TYPESPEC) {
return TP_Type::make_object_plus_product(arg0_type.typespec(), arg1_type.get_multiplier());
return TP_Type::make_object_plus_product(arg0_type.typespec(), arg1_type.get_multiplier(),
false);
}
if ((m_kind == Kind::ADD || m_kind == Kind::SUB) &&
arg0_type.typespec().base_type() == "pointer" && tc(dts, TypeSpec("integer"), arg1_type)) {
if (m_kind == Kind::ADD && !m_args[1].is_int()) {
return TP_Type::make_object_plus_product(arg0_type.typespec(), 1);
return TP_Type::make_object_plus_product(arg0_type.typespec(), 1, false);
}
// plain pointer plus integer = plain pointer
return TP_Type::make_from_ts(arg0_type.typespec());
@ -527,7 +529,7 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
// byte access of offset array field trick.
// arg1 holds a structure.
// arg0 is an integer in a register.
return TP_Type::make_object_plus_product(arg1_type.typespec(), 1);
return TP_Type::make_object_plus_product(arg1_type.typespec(), 1, true);
}
}

View file

@ -1886,11 +1886,13 @@ void DynamicMethodAccess::get_modified_regs(RegSet&) const {}
ArrayFieldAccess::ArrayFieldAccess(RegisterAccess source,
const std::vector<DerefToken>& deref_tokens,
int expected_stride,
int constant_offset)
int constant_offset,
bool flipped)
: m_source(source),
m_deref_tokens(deref_tokens),
m_expected_stride(expected_stride),
m_constant_offset(constant_offset) {
m_constant_offset(constant_offset),
m_flipped(flipped) {
for (auto& token : m_deref_tokens) {
if (token.kind() == DerefToken::Kind::INTEGER_EXPRESSION) {
token.expr()->parent_element = this;

View file

@ -1097,7 +1097,8 @@ class ArrayFieldAccess : public FormElement {
ArrayFieldAccess(RegisterAccess source,
const std::vector<DerefToken>& deref_tokens,
int expected_stride,
int constant_offset);
int constant_offset,
bool flipped);
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;
@ -1115,11 +1116,14 @@ class ArrayFieldAccess : public FormElement {
std::vector<FormElement*>* result,
bool allow_side_effects);
bool flipped() const { return m_flipped; }
private:
RegisterAccess m_source;
std::vector<DerefToken> m_deref_tokens;
int m_expected_stride = -1;
int m_constant_offset = -1;
bool m_flipped = false;
};
class GetMethodElement : public FormElement {
@ -1353,6 +1357,8 @@ class VectorFloatLoadStoreElement : public FormElement {
void get_modified_regs(RegSet& regs) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
void collect_vf_regs(RegSet& regs) const;
bool is_load() const { return m_is_load; }
Register vf_reg() const { return m_vf_reg; }
private:
Register m_vf_reg;

View file

@ -3468,6 +3468,10 @@ void ArrayFieldAccess::update_with_val(Form* new_val,
auto base = match_result.maps.forms.at(0);
assert(idx && base);
if (m_flipped) {
std::swap(idx, base);
}
std::vector<DerefToken> tokens = m_deref_tokens;
for (auto& x : tokens) {
if (x.kind() == DerefToken::Kind::EXPRESSION_PLACEHOLDER) {

View file

@ -215,11 +215,15 @@ class TP_Type {
return result;
}
static TP_Type make_object_plus_product(const TypeSpec& ts, int64_t multiplier) {
/*!
* flipped means it's int + obj.
*/
static TP_Type make_object_plus_product(const TypeSpec& ts, int64_t multiplier, bool flipped) {
TP_Type result;
result.kind = Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT;
result.m_ts = ts;
result.m_int = multiplier;
result.m_flipped_order = flipped;
return result;
}
@ -314,6 +318,11 @@ class TP_Type {
return m_method_id;
}
bool flipped_add_order() const {
assert(kind == Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT);
return m_flipped_order;
}
private:
TypeSpec m_ts;
TypeSpec m_method_from_type;
@ -321,6 +330,7 @@ class TP_Type {
std::string m_str;
int64_t m_int = 0;
bool m_pcpyud = false; // have we extracted the top doubleword of a bitfield?
bool m_flipped_order = false;
int64_t m_extra_multiplier = 0;
};

View file

@ -40,3 +40,12 @@ TEST(DecompilerInstructionParser, ProgramLabels) {
auto result = parser.parse_program(program);
EXPECT_EQ(result.print(), program);
}
TEST(DecompilerInstructionParser, VU) {
InstructionParser parser;
std::string program =
" vmove.xy vf1, vf2\n"
" vsub.yw vf1, vf2, vf25\n";
auto result = parser.parse_program(program);
EXPECT_EQ(result.print(), program);
}