mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 00:57:44 -04:00
[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:
parent
69e24ae577
commit
10b00e57ae
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -39,4 +39,13 @@ TEST(DecompilerInstructionParser, ProgramLabels) {
|
|||
" jr ra\n";
|
||||
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);
|
||||
}
|
Loading…
Reference in a new issue