mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
[Decomp] Fix bitfields in dma-bucket (#367)
* fix bitfields in dma-bucket * fix dma-bucket
This commit is contained in:
parent
9867155e7c
commit
7737817ac7
|
@ -140,8 +140,15 @@ goos::Object SimpleAtom::to_form(const std::vector<DecompilerLabel>& labels, con
|
|||
switch (m_kind) {
|
||||
case Kind::VARIABLE:
|
||||
return m_variable.to_form(env);
|
||||
case Kind::INTEGER_CONSTANT:
|
||||
return goos::Object::make_integer(m_int);
|
||||
case Kind::INTEGER_CONSTANT: {
|
||||
if (std::abs(m_int) > INT32_MAX) {
|
||||
u64 v = m_int;
|
||||
return pretty_print::to_symbol(fmt::format("#x{:x}", v));
|
||||
} else {
|
||||
return goos::Object::make_integer(m_int);
|
||||
}
|
||||
}
|
||||
|
||||
case Kind::SYMBOL_PTR:
|
||||
if (m_string == "#t") {
|
||||
return pretty_print::to_symbol("#t");
|
||||
|
|
|
@ -609,8 +609,8 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
|
|||
u64 value;
|
||||
memcpy(&value, &word0.data, 4);
|
||||
memcpy(((u8*)&value) + 4, &word1.data, 4);
|
||||
return pool.alloc_single_element_form<ConstantTokenElement>(nullptr,
|
||||
fmt::format("#x{:x}", value));
|
||||
return pool.alloc_single_element_form<SimpleAtomElement>(
|
||||
nullptr, SimpleAtom::make_int_constant(value));
|
||||
}
|
||||
|
||||
// is it a constant bitfield?
|
||||
|
|
|
@ -90,6 +90,10 @@ const std::string& Env::remapped_name(const std::string& name) const {
|
|||
}
|
||||
}
|
||||
|
||||
goos::Object Env::get_variable_name_with_cast(const RegisterAccess& access) const {
|
||||
return get_variable_name_with_cast(access.reg(), access.idx(), access.mode());
|
||||
}
|
||||
|
||||
goos::Object Env::get_variable_name_with_cast(Register reg, int atomic_idx, AccessMode mode) const {
|
||||
if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR) {
|
||||
auto& var_info = m_var_names.lookup(reg, atomic_idx, mode);
|
||||
|
|
|
@ -52,6 +52,7 @@ class Env {
|
|||
|
||||
// TODO - remove this.
|
||||
goos::Object get_variable_name_with_cast(Register reg, int atomic_idx, AccessMode mode) const;
|
||||
goos::Object get_variable_name_with_cast(const RegisterAccess& access) const;
|
||||
std::string get_variable_name(const RegisterAccess& access) const;
|
||||
std::optional<TypeSpec> get_user_cast_for_access(const RegisterAccess& access) const;
|
||||
TypeSpec get_variable_type(const RegisterAccess& access, bool using_user_var_types) const;
|
||||
|
|
|
@ -309,7 +309,7 @@ goos::Object SetVarElement::to_form_internal(const Env& env) const {
|
|||
auto expected_type = env.get_variable_type(m_dst, true);
|
||||
if (!env.dts->ts.tc(expected_type, m_src_type)) {
|
||||
return pretty_print::build_list(
|
||||
"set!", m_dst.to_form(env),
|
||||
"set!", m_dst.to_form(env, RegisterAccess::Print::AS_VARIABLE_NO_CAST),
|
||||
pretty_print::build_list("the-as", expected_type.print(), m_src->to_form(env)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -712,10 +712,19 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
|
|||
GenericOperator::make_fixed(FixedOperatorKind::ADDITION_PTR), args.at(0), args.at(1));
|
||||
result->push_back(new_form);
|
||||
} else {
|
||||
auto cast = pool.alloc_single_element_form<CastElement>(
|
||||
auto casted0 = args.at(0);
|
||||
|
||||
auto arg0_type = env.get_types_before_op(m_my_idx).get(m_expr.get_arg(0).var().reg());
|
||||
if (!arg0_i && !arg0_u && arg0_type.typespec() != TypeSpec("binteger")) {
|
||||
casted0 = pool.alloc_single_element_form<CastElement>(
|
||||
nullptr, TypeSpec(arg0_i ? "int" : "uint"), args.at(0));
|
||||
}
|
||||
|
||||
auto casted1 = pool.alloc_single_element_form<CastElement>(
|
||||
nullptr, TypeSpec(arg0_i ? "int" : "uint"), args.at(1));
|
||||
|
||||
auto new_form = pool.alloc_element<GenericElement>(
|
||||
GenericOperator::make_fixed(FixedOperatorKind::ADDITION), args.at(0), cast);
|
||||
GenericOperator::make_fixed(FixedOperatorKind::ADDITION), casted0, casted1);
|
||||
result->push_back(new_form);
|
||||
}
|
||||
}
|
||||
|
@ -877,6 +886,16 @@ void SimpleExpressionElement::update_from_stack_copy_first_int_2(const Env& env,
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
Form* strip_int_or_uint_cast(Form* in) {
|
||||
auto as_cast = in->try_as_element<CastElement>();
|
||||
if (as_cast && (as_cast->type() == TypeSpec("int") || as_cast->type() == TypeSpec("uint"))) {
|
||||
return as_cast->source();
|
||||
}
|
||||
return in;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void SimpleExpressionElement::update_from_stack_logor_or_logand(const Env& env,
|
||||
FixedOperatorKind kind,
|
||||
FormPool& pool,
|
||||
|
@ -884,24 +903,26 @@ void SimpleExpressionElement::update_from_stack_logor_or_logand(const Env& env,
|
|||
std::vector<FormElement*>* result,
|
||||
bool allow_side_effects) {
|
||||
auto arg0_type = env.get_variable_type(m_expr.get_arg(0).var(), true);
|
||||
BitfieldManip::Kind manip_kind;
|
||||
if (kind == FixedOperatorKind::LOGAND) {
|
||||
manip_kind = BitfieldManip::Kind::LOGAND;
|
||||
} else if (kind == FixedOperatorKind::LOGIOR) {
|
||||
manip_kind = BitfieldManip::Kind::LOGIOR_WITH_CONSTANT_INT;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
auto type_info = env.dts->ts.lookup_type(arg0_type);
|
||||
auto bitfield_info = dynamic_cast<BitFieldType*>(type_info);
|
||||
if (bitfield_info && m_expr.get_arg(1).is_int()) {
|
||||
// andi, bitfield
|
||||
// andi, ori with bitfield.
|
||||
auto base = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0);
|
||||
auto read_elt = dynamic_cast<BitfieldReadElement*>(base->try_as_single_element());
|
||||
auto read_elt = dynamic_cast<BitfieldAccessElement*>(base->try_as_single_element());
|
||||
if (!read_elt) {
|
||||
read_elt = pool.alloc_element<BitfieldReadElement>(base, arg0_type);
|
||||
read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
|
||||
}
|
||||
|
||||
BitfieldManip::Kind manip_kind;
|
||||
if (kind == FixedOperatorKind::LOGAND) {
|
||||
manip_kind = BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT;
|
||||
} else if (kind == FixedOperatorKind::LOGIOR) {
|
||||
manip_kind = BitfieldManip::Kind::LOGIOR_WITH_CONSTANT_INT;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
BitfieldManip step(manip_kind, m_expr.get_arg(1).get_int());
|
||||
auto other = read_elt->push_step(step, env.dts->ts, pool);
|
||||
if (other) {
|
||||
|
@ -925,18 +946,45 @@ void SimpleExpressionElement::update_from_stack_logor_or_logand(const Env& env,
|
|||
allow_side_effects);
|
||||
|
||||
if (bitfield_info) {
|
||||
// mask didn't fit in imm
|
||||
auto arg1_atom = form_as_atom(args.at(1));
|
||||
// either the immediate didn't fit in the 16-bit imm or it's with a variable
|
||||
auto read_elt = dynamic_cast<BitfieldAccessElement*>(args.at(0)->try_as_single_element());
|
||||
if (!read_elt) {
|
||||
read_elt = pool.alloc_element<BitfieldAccessElement>(args.at(0), arg0_type);
|
||||
}
|
||||
|
||||
auto stripped_arg1 = strip_int_or_uint_cast(args.at(1));
|
||||
auto arg1_atom = form_as_atom(strip_int_or_uint_cast(args.at(1)));
|
||||
if (arg1_atom && arg1_atom->is_int()) {
|
||||
auto read_elt = dynamic_cast<BitfieldReadElement*>(args.at(0)->try_as_single_element());
|
||||
if (!read_elt) {
|
||||
read_elt = pool.alloc_element<BitfieldReadElement>(args.at(0), arg0_type);
|
||||
BitfieldManip::Kind manip_kind;
|
||||
if (kind == FixedOperatorKind::LOGAND) {
|
||||
manip_kind = BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT;
|
||||
} else if (kind == FixedOperatorKind::LOGIOR) {
|
||||
manip_kind = BitfieldManip::Kind::LOGIOR_WITH_CONSTANT_INT;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
BitfieldManip step(manip_kind, arg1_atom->get_int());
|
||||
auto other = read_elt->push_step(step, env.dts->ts, pool);
|
||||
assert(!other); // shouldn't be complete.
|
||||
result->push_back(read_elt);
|
||||
return;
|
||||
} else {
|
||||
BitfieldManip::Kind manip_kind;
|
||||
if (kind == FixedOperatorKind::LOGAND) {
|
||||
manip_kind = BitfieldManip::Kind::LOGAND_WITH_FORM;
|
||||
} else if (kind == FixedOperatorKind::LOGIOR) {
|
||||
manip_kind = BitfieldManip::Kind::LOGIOR_WITH_FORM;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
auto step = BitfieldManip::from_form(manip_kind, stripped_arg1);
|
||||
auto other = read_elt->push_step(step, env.dts->ts, pool);
|
||||
if (other) {
|
||||
result->push_back(other);
|
||||
} else {
|
||||
result->push_back(read_elt);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -967,7 +1015,7 @@ void SimpleExpressionElement::update_from_stack_left_shift(const Env& env,
|
|||
auto bitfield_info = dynamic_cast<BitFieldType*>(type_info);
|
||||
if (bitfield_info && m_expr.get_arg(1).is_int()) {
|
||||
auto base = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0);
|
||||
auto read_elt = pool.alloc_element<BitfieldReadElement>(base, arg0_type);
|
||||
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
|
||||
BitfieldManip step(BitfieldManip::Kind::LEFT_SHIFT, m_expr.get_arg(1).get_int());
|
||||
auto other = read_elt->push_step(step, env.dts->ts, pool);
|
||||
assert(!other); // shouldn't be complete.
|
||||
|
@ -988,7 +1036,7 @@ void SimpleExpressionElement::update_from_stack_right_shift_logic(const Env& env
|
|||
auto bitfield_info = dynamic_cast<BitFieldType*>(type_info);
|
||||
if (bitfield_info && m_expr.get_arg(1).is_int()) {
|
||||
auto base = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0);
|
||||
auto read_elt = pool.alloc_element<BitfieldReadElement>(base, arg0_type);
|
||||
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
|
||||
BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_LOGICAL, m_expr.get_arg(1).get_int());
|
||||
auto other = read_elt->push_step(step, env.dts->ts, pool);
|
||||
assert(other); // should be a high field.
|
||||
|
@ -999,7 +1047,7 @@ void SimpleExpressionElement::update_from_stack_right_shift_logic(const Env& env
|
|||
if (m_expr.get_arg(1).is_int()) {
|
||||
auto arg =
|
||||
pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0);
|
||||
auto as_bitfield_access = dynamic_cast<BitfieldReadElement*>(arg->try_as_single_element());
|
||||
auto as_bitfield_access = dynamic_cast<BitfieldAccessElement*>(arg->try_as_single_element());
|
||||
|
||||
if (as_bitfield_access) {
|
||||
BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_LOGICAL, m_expr.get_arg(1).get_int());
|
||||
|
@ -1040,7 +1088,7 @@ void SimpleExpressionElement::update_from_stack_right_shift_arith(const Env& env
|
|||
auto bitfield_info = dynamic_cast<BitFieldType*>(type_info);
|
||||
if (bitfield_info && m_expr.get_arg(1).is_int()) {
|
||||
auto base = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0);
|
||||
auto read_elt = pool.alloc_element<BitfieldReadElement>(base, arg0_type);
|
||||
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
|
||||
BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_ARITH, m_expr.get_arg(1).get_int());
|
||||
auto other = read_elt->push_step(step, env.dts->ts, pool);
|
||||
assert(other); // should be a high field.
|
||||
|
@ -1325,12 +1373,14 @@ void SetFormFormElement::push_to_stack(const Env& env, FormPool& pool, FormStack
|
|||
if (dst_form == src_form) {
|
||||
// success!
|
||||
auto value = src_as_bf_set->mods().at(0).value;
|
||||
value->parent_element = this;
|
||||
|
||||
// make the (-> thing bitfield)
|
||||
auto field_token = DerefToken::make_field_name(src_as_bf_set->mods().at(0).field_name);
|
||||
auto loc_elt = pool.alloc_element<DerefElement>(m_dst, false, field_token);
|
||||
loc_elt->inline_nested();
|
||||
auto loc = pool.alloc_single_form(nullptr, loc_elt);
|
||||
loc->parent_element = this;
|
||||
|
||||
m_dst = loc;
|
||||
m_src = value;
|
||||
|
@ -2218,7 +2268,7 @@ FormElement* ConditionElement::make_nonzero_check_generic(const Env& env,
|
|||
FormElement* bitfield_compare = nullptr;
|
||||
assert(source_forms.size() == 1);
|
||||
auto as_bitfield_op =
|
||||
dynamic_cast<BitfieldReadElement*>(source_forms.at(0)->try_as_single_element());
|
||||
dynamic_cast<BitfieldAccessElement*>(source_forms.at(0)->try_as_single_element());
|
||||
if (as_bitfield_op) {
|
||||
bitfield_compare = as_bitfield_op->push_step(
|
||||
BitfieldManip(BitfieldManip::Kind::NONZERO_COMPARE, 0), env.dts->ts, pool);
|
||||
|
@ -2587,7 +2637,7 @@ void push_asm_srl_to_stack(const AsmOp* op,
|
|||
auto bitfield_info = dynamic_cast<BitFieldType*>(type_info);
|
||||
if (bitfield_info) {
|
||||
auto base = pop_to_forms({*var}, env, pool, stack, true).at(0);
|
||||
auto read_elt = pool.alloc_element<BitfieldReadElement>(base, arg0_type);
|
||||
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
|
||||
BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_LOGICAL_32BIT, integer);
|
||||
auto other = read_elt->push_step(step, env.dts->ts, pool);
|
||||
assert(other); // should be a high field.
|
||||
|
@ -2843,7 +2893,7 @@ void ArrayFieldAccess::update_with_val(Form* new_val,
|
|||
new_val->to_string(env));
|
||||
}
|
||||
|
||||
auto base = mr.maps.forms.at(1);
|
||||
auto base = strip_int_or_uint_cast(mr.maps.forms.at(1));
|
||||
auto idx = mr.maps.forms.at(0);
|
||||
|
||||
assert(idx && base);
|
||||
|
|
|
@ -257,6 +257,13 @@ bool is_op_in_place(SetVarElement* elt,
|
|||
return false;
|
||||
}
|
||||
|
||||
// also check with casts. This avoid something like
|
||||
// (set! x (+ (the <blah> x) 1))
|
||||
auto src_var_cast = env.get_variable_name_with_cast(*first);
|
||||
if (src_var_cast.print() != dst_var) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*val_out = result.maps.forms.at(1);
|
||||
*base_out = first.value();
|
||||
return true;
|
||||
|
|
|
@ -62,9 +62,14 @@ void BitfieldStaticDefElement::get_modified_regs(RegSet&) const {}
|
|||
ModifiedCopyBitfieldElement::ModifiedCopyBitfieldElement(
|
||||
const TypeSpec& type,
|
||||
Form* base,
|
||||
const std::vector<BitfieldFormDef>& field_modifications)
|
||||
const std::vector<BitFieldDef>& field_modifications)
|
||||
: m_type(type), m_base(base), m_field_modifications(field_modifications) {
|
||||
m_base->parent_element = this;
|
||||
for (auto& mod : m_field_modifications) {
|
||||
if (mod.value) {
|
||||
mod.value->parent_element = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object ModifiedCopyBitfieldElement::to_form_internal(const Env& env) const {
|
||||
|
@ -112,29 +117,29 @@ void ModifiedCopyBitfieldElement::get_modified_regs(RegSet& regs) const {
|
|||
// BitfieldReadElement
|
||||
////////////////////////////////
|
||||
|
||||
BitfieldReadElement::BitfieldReadElement(Form* base_value, const TypeSpec& ts)
|
||||
BitfieldAccessElement::BitfieldAccessElement(Form* base_value, const TypeSpec& ts)
|
||||
: m_base(base_value), m_type(ts) {
|
||||
m_base->parent_element = this;
|
||||
}
|
||||
|
||||
goos::Object BitfieldReadElement::to_form_internal(const Env& env) const {
|
||||
goos::Object BitfieldAccessElement::to_form_internal(const Env& env) const {
|
||||
return pretty_print::build_list("incomplete-bitfield-access", m_base->to_form(env));
|
||||
}
|
||||
|
||||
void BitfieldReadElement::apply(const std::function<void(FormElement*)>& f) {
|
||||
void BitfieldAccessElement::apply(const std::function<void(FormElement*)>& f) {
|
||||
f(this);
|
||||
m_base->apply(f);
|
||||
}
|
||||
|
||||
void BitfieldReadElement::apply_form(const std::function<void(Form*)>& f) {
|
||||
void BitfieldAccessElement::apply_form(const std::function<void(Form*)>& f) {
|
||||
m_base->apply_form(f);
|
||||
}
|
||||
|
||||
void BitfieldReadElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
||||
void BitfieldAccessElement::collect_vars(RegAccessSet& vars, bool recursive) const {
|
||||
m_base->collect_vars(vars, recursive);
|
||||
}
|
||||
|
||||
void BitfieldReadElement::get_modified_regs(RegSet& regs) const {
|
||||
void BitfieldAccessElement::get_modified_regs(RegSet& regs) const {
|
||||
m_base->get_modified_regs(regs);
|
||||
}
|
||||
|
||||
|
@ -180,15 +185,49 @@ std::optional<BitField> find_field_from_mask(const TypeSystem& ts,
|
|||
return find_field(ts, type, mask_range->first(), mask_range->size(), {});
|
||||
}
|
||||
|
||||
Form* strip_int_or_uint_cast(Form* in) {
|
||||
auto as_cast = in->try_as_element<CastElement>();
|
||||
if (as_cast && (as_cast->type() == TypeSpec("int") || as_cast->type() == TypeSpec("uint"))) {
|
||||
return as_cast->source();
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
|
||||
const BitFieldType* type,
|
||||
const TypeSystem& ts) {
|
||||
// (shr (shl arg1 59) 44) for example
|
||||
auto matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::SHR),
|
||||
{Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::SHL),
|
||||
{Matcher::any(0), Matcher::any_integer(1)}),
|
||||
Matcher::any_integer(2)});
|
||||
auto mr = match(matcher, strip_int_or_uint_cast(form));
|
||||
if (mr.matched) {
|
||||
auto value = mr.maps.forms.at(0);
|
||||
int left = mr.maps.ints.at(1);
|
||||
int right = mr.maps.ints.at(2);
|
||||
int size = 64 - left;
|
||||
int offset = left - right;
|
||||
auto& f = find_field(ts, type, offset, size, {});
|
||||
BitFieldDef def;
|
||||
def.value = value;
|
||||
def.field_name = f.name();
|
||||
def.is_signed = false; // we don't know.
|
||||
return def;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/*!
|
||||
* Add a step to the bitfield access. If this completes the access, returns a form representing the
|
||||
* access
|
||||
*/
|
||||
FormElement* BitfieldReadElement::push_step(const BitfieldManip step,
|
||||
const TypeSystem& ts,
|
||||
FormPool& pool) {
|
||||
FormElement* BitfieldAccessElement::push_step(const BitfieldManip step,
|
||||
const TypeSystem& ts,
|
||||
FormPool& pool) {
|
||||
if (m_steps.empty() && step.kind == BitfieldManip::Kind::LEFT_SHIFT) {
|
||||
// for left/right shift combo to get a field.
|
||||
m_steps.push_back(step);
|
||||
|
@ -235,13 +274,13 @@ FormElement* BitfieldReadElement::push_step(const BitfieldManip step,
|
|||
return result;
|
||||
}
|
||||
|
||||
if (m_steps.empty() && step.kind == BitfieldManip::Kind::LOGAND) {
|
||||
if (m_steps.empty() && step.kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT) {
|
||||
// and with mask
|
||||
m_steps.push_back(step);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND &&
|
||||
if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT &&
|
||||
step.kind == BitfieldManip::Kind::NONZERO_COMPARE) {
|
||||
auto type = ts.lookup_type(m_type);
|
||||
auto as_bitfield = dynamic_cast<BitFieldType*>(type);
|
||||
|
@ -257,9 +296,9 @@ FormElement* BitfieldReadElement::push_step(const BitfieldManip step,
|
|||
}
|
||||
}
|
||||
|
||||
if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND &&
|
||||
if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT &&
|
||||
step.kind == BitfieldManip::Kind::LOGIOR_WITH_CONSTANT_INT) {
|
||||
// this is setting a 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;
|
||||
u64 value = step.amount;
|
||||
|
@ -284,12 +323,36 @@ FormElement* BitfieldReadElement::push_step(const BitfieldManip step,
|
|||
set_value = extract_bitfield<u64>(value, field->offset(), field->size());
|
||||
}
|
||||
|
||||
BitfieldFormDef def;
|
||||
BitFieldDef def;
|
||||
def.field_name = field->name();
|
||||
def.value = pool.alloc_single_element_form<SimpleAtomElement>(
|
||||
nullptr, SimpleAtom::make_int_constant(set_value));
|
||||
return pool.alloc_element<ModifiedCopyBitfieldElement>(m_type, m_base,
|
||||
std::vector<BitfieldFormDef>{def});
|
||||
std::vector<BitFieldDef>{def});
|
||||
}
|
||||
|
||||
if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT &&
|
||||
step.kind == BitfieldManip::Kind::LOGIOR_WITH_FORM) {
|
||||
// this is setting a bitfield to a variable
|
||||
u64 mask = m_steps.at(0).amount;
|
||||
|
||||
auto type = ts.lookup_type(m_type);
|
||||
auto as_bitfield = dynamic_cast<BitFieldType*>(type);
|
||||
assert(as_bitfield);
|
||||
// use the mask to figure out the field.
|
||||
auto field = find_field_from_mask(ts, as_bitfield, ~mask);
|
||||
assert(field);
|
||||
|
||||
auto val = get_bitfield_initial_set(step.value, as_bitfield, ts);
|
||||
assert(val);
|
||||
if (val->field_name != field->name()) {
|
||||
throw std::runtime_error("Incompatible bitfield set");
|
||||
}
|
||||
|
||||
return pool.alloc_element<ModifiedCopyBitfieldElement>(m_type, m_base,
|
||||
std::vector<BitFieldDef>{*val});
|
||||
|
||||
// todo check that the mask and the set are compatible with eachother
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unknown state in BitfieldReadElement");
|
||||
|
@ -342,41 +405,6 @@ std::optional<u64> get_goal_integer_constant(Form* in, const Env&) {
|
|||
return {};
|
||||
}
|
||||
|
||||
Form* strip_int_or_uint_cast(Form* in) {
|
||||
auto as_cast = in->try_as_element<CastElement>();
|
||||
if (as_cast && (as_cast->type() == TypeSpec("int") || as_cast->type() == TypeSpec("uint"))) {
|
||||
return as_cast->source();
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
|
||||
const BitFieldType* type,
|
||||
const TypeSystem& ts,
|
||||
const Env&) {
|
||||
// (shr (shl arg1 59) 44) for example
|
||||
auto matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::SHR),
|
||||
{Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::SHL),
|
||||
{Matcher::any(0), Matcher::any_integer(1)}),
|
||||
Matcher::any_integer(2)});
|
||||
auto mr = match(matcher, strip_int_or_uint_cast(form));
|
||||
if (mr.matched) {
|
||||
auto value = mr.maps.forms.at(0);
|
||||
int left = mr.maps.ints.at(1);
|
||||
int right = mr.maps.ints.at(2);
|
||||
int size = 64 - left;
|
||||
int offset = left - right;
|
||||
auto& f = find_field(ts, type, offset, size, {});
|
||||
BitFieldDef def;
|
||||
def.value = value;
|
||||
def.field_name = f.name();
|
||||
def.is_signed = false; // we don't know.
|
||||
return def;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BitFieldDef BitFieldDef::from_constant(const BitFieldConstantDef& constant, FormPool& pool) {
|
||||
|
@ -432,7 +460,7 @@ Form* cast_to_bitfield(const BitFieldType* type_info,
|
|||
|
||||
// now variables
|
||||
for (auto& arg : args) {
|
||||
auto maybe_field = get_bitfield_initial_set(arg, type_info, env.dts->ts, env);
|
||||
auto maybe_field = get_bitfield_initial_set(arg, type_info, env.dts->ts);
|
||||
if (!maybe_field) {
|
||||
// failed, just return cast.
|
||||
return pool.alloc_single_element_form<CastElement>(nullptr, typespec, in);
|
||||
|
|
|
@ -13,12 +13,15 @@ struct BitfieldManip {
|
|||
RIGHT_SHIFT_LOGICAL,
|
||||
RIGHT_SHIFT_LOGICAL_32BIT,
|
||||
RIGHT_SHIFT_ARITH,
|
||||
LOGAND,
|
||||
LOGAND_WITH_CONSTANT_INT,
|
||||
LOGIOR_WITH_CONSTANT_INT,
|
||||
LOGIOR_WITH_FORM,
|
||||
LOGAND_WITH_FORM,
|
||||
NONZERO_COMPARE,
|
||||
INVALID
|
||||
} kind = Kind::INVALID;
|
||||
s64 amount = -1;
|
||||
Form* value = nullptr;
|
||||
|
||||
bool is_right_shift() const {
|
||||
return kind == Kind::RIGHT_SHIFT_ARITH || kind == Kind::RIGHT_SHIFT_LOGICAL ||
|
||||
|
@ -44,11 +47,20 @@ struct BitfieldManip {
|
|||
}
|
||||
|
||||
BitfieldManip(Kind k, s64 imm) : kind(k), amount(imm) {}
|
||||
static BitfieldManip from_form(Kind k, Form* val) {
|
||||
BitfieldManip result;
|
||||
result.kind = k;
|
||||
result.value = val;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
BitfieldManip() = default;
|
||||
};
|
||||
|
||||
class BitfieldReadElement : public FormElement {
|
||||
class BitfieldAccessElement : public FormElement {
|
||||
public:
|
||||
BitfieldReadElement(Form* base_value, const TypeSpec& ts);
|
||||
BitfieldAccessElement(Form* base_value, const TypeSpec& ts);
|
||||
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;
|
||||
|
@ -95,11 +107,6 @@ class BitfieldStaticDefElement : public FormElement {
|
|||
std::vector<BitFieldDef> m_field_defs;
|
||||
};
|
||||
|
||||
struct BitfieldFormDef {
|
||||
Form* value;
|
||||
std::string field_name;
|
||||
};
|
||||
|
||||
/*!
|
||||
* This represents copying a bitfield object, then modifying the type.
|
||||
* It's an intermediate step to modifying a bitfield in place and it's not expected to appear
|
||||
|
@ -109,7 +116,7 @@ class ModifiedCopyBitfieldElement : public FormElement {
|
|||
public:
|
||||
ModifiedCopyBitfieldElement(const TypeSpec& type,
|
||||
Form* base,
|
||||
const std::vector<BitfieldFormDef>& field_modifications);
|
||||
const std::vector<BitFieldDef>& field_modifications);
|
||||
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;
|
||||
|
@ -117,12 +124,12 @@ class ModifiedCopyBitfieldElement : public FormElement {
|
|||
void get_modified_regs(RegSet& regs) const override;
|
||||
|
||||
Form* base() const { return m_base; }
|
||||
const std::vector<BitfieldFormDef> mods() const { return m_field_modifications; }
|
||||
const std::vector<BitFieldDef> mods() const { return m_field_modifications; }
|
||||
|
||||
private:
|
||||
TypeSpec m_type;
|
||||
Form* m_base = nullptr;
|
||||
std::vector<BitfieldFormDef> m_field_modifications;
|
||||
std::vector<BitFieldDef> m_field_modifications;
|
||||
};
|
||||
|
||||
Form* cast_to_bitfield(const BitFieldType* type_info,
|
||||
|
|
|
@ -2655,9 +2655,9 @@
|
|||
;;(define-extern dma-buffer object) ;; unknown type
|
||||
(define-extern dma-buffer-send-chain (function dma-bank-source dma-buffer none))
|
||||
(define-extern dma-buffer-send (function dma-bank dma-buffer none))
|
||||
(define-extern dma-bucket-insert-tag (function dma-bucket int dma-bucket (pointer uint64) dma-bucket))
|
||||
(define-extern dma-bucket-insert-tag (function dma-bucket int pointer (pointer dma-tag) pointer))
|
||||
(define-extern dma-buffer-add-buckets (function dma-buffer int none))
|
||||
(define-extern dma-buffer-patch-buckets (function dma-bucket int int))
|
||||
(define-extern dma-buffer-patch-buckets (function dma-bucket int dma-bucket))
|
||||
|
||||
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -160,14 +160,17 @@
|
|||
],
|
||||
|
||||
"dma-buffer-patch-buckets": [
|
||||
[3, "a0", "dma-bucket"],
|
||||
[[3, 5], "a2", "dma-bucket"],
|
||||
[7, "a0", "pointer"],
|
||||
[11, "a0", "dma-bucket"],
|
||||
//[12, "a2", "uint"],
|
||||
[13, "a0", "dma-bucket"],
|
||||
[14, "a0", "pointer"],
|
||||
[[15, 17], "a0", "dma-bucket"]
|
||||
[3, "a0", "dma-bucket"],
|
||||
[11, "a0", "dma-bucket"],
|
||||
[13, "a0", "dma-bucket"],
|
||||
[19, "a0", "dma-bucket"]
|
||||
],
|
||||
|
||||
"dma-bucket-insert-tag": [
|
||||
[[2, 6], "v1", "dma-bucket"],
|
||||
[3, "a0", "dma-bucket"]
|
||||
],
|
||||
|
||||
// LEVEL
|
||||
|
|
|
@ -678,7 +678,8 @@
|
|||
},
|
||||
|
||||
"dma-bucket-insert-tag": {
|
||||
"args": ["base", "idx", "tag-start", "tag-end"]
|
||||
"args": ["base", "idx", "tag-start", "tag-end"],
|
||||
"vars": { "v1-1": "bucket" }
|
||||
},
|
||||
|
||||
"disasm-vif-details": {
|
||||
|
|
|
@ -52,35 +52,31 @@
|
|||
|
||||
|
||||
(defun dma-buffer-patch-buckets ((bucket dma-bucket) (count int))
|
||||
"Patch last pointers in a sequence of buckets.
|
||||
Call this after you have added everything to buckets."
|
||||
(local-vars (i int))
|
||||
"After adding all data to buckets, call this to stitch together the chains for
|
||||
count consecutive buckets"
|
||||
(when (nonzero? bucket)
|
||||
(set! i 0)
|
||||
(while (< i count)
|
||||
;; make last tag of this bucket point to the next bucket.
|
||||
(set! (-> bucket last 0)
|
||||
(logior (logand (-> bucket last 0) #x80000000ffffffff)
|
||||
(shr (shl (+ (the-as uint bucket) 16) 33) 1)
|
||||
)
|
||||
)
|
||||
;; clear last, and move on to the next bucket.
|
||||
(set! (-> bucket last) (the (pointer dma-tag) 0))
|
||||
(dotimes (i count)
|
||||
;; set last tag's address to the next bucket.
|
||||
(set! (-> bucket last 0 addr) (the-as int (&+ (the-as pointer bucket) 16)))
|
||||
;; clear last pointer.
|
||||
(set! (-> bucket last) (the-as (pointer dma-tag) 0))
|
||||
;; next bucket
|
||||
(set! bucket (&+ bucket 16))
|
||||
(set! i (+ i 1))
|
||||
)
|
||||
)
|
||||
(the-as int bucket)
|
||||
bucket
|
||||
)
|
||||
|
||||
(defun dma-bucket-insert-tag ((base dma-bucket) (idx int) (tag-start dma-bucket) (tag-end (pointer uint64)))
|
||||
"Splice in a tag."
|
||||
(local-vars (bucket dma-bucket))
|
||||
;; seek to bucket we want to splice before.
|
||||
(set! bucket (&+ base (shl idx 4)))
|
||||
;; append to last tag (kind of a hack here with the types)
|
||||
(set! (-> (the dma-bucket (-> bucket last)) next) (the uint tag-start))
|
||||
;; make prev = end of tag so we can add more.
|
||||
(set! (-> bucket last) (the (pointer dma-tag) tag-end))
|
||||
(defun dma-bucket-insert-tag ((base dma-bucket) (idx int) (tag-start pointer) (tag-end (pointer dma-tag)))
|
||||
"Add a dma chain to the idx bucket"
|
||||
|
||||
;; find the bucket
|
||||
(let ((bucket (the-as dma-bucket (&+ base (the-as uint (shl idx 4))))))
|
||||
;; update our last bucket to point to this one.
|
||||
;; this is abusing the dma-bucket type to set the "addr" field of the dma-tag.
|
||||
(set! (-> (the-as dma-bucket (-> bucket last)) next) (the-as uint tag-start))
|
||||
;; remember this as the last tag in the bucket.
|
||||
(set! (-> bucket last) tag-end)
|
||||
)
|
||||
tag-start
|
||||
)
|
||||
|
|
60
test/decompiler/reference/dma-bucket_REF.gc
Normal file
60
test/decompiler/reference/dma-bucket_REF.gc
Normal file
|
@ -0,0 +1,60 @@
|
|||
;;-*-Lisp-*-
|
||||
(in-package goal)
|
||||
|
||||
;; definition for function dma-buffer-add-buckets
|
||||
;; INFO: Return type mismatch pointer vs none.
|
||||
(defun dma-buffer-add-buckets ((dma-buf dma-buffer) (count int))
|
||||
(let ((current-bucket (the-as dma-bucket (-> dma-buf base))))
|
||||
(dotimes (i count)
|
||||
(set!
|
||||
(-> current-bucket tag)
|
||||
(new 'static 'dma-tag
|
||||
:id #x2
|
||||
:addr (the-as int (&+ (the-as pointer current-bucket) 16))
|
||||
)
|
||||
)
|
||||
(set! (-> current-bucket last) (the-as (pointer dma-tag) current-bucket))
|
||||
(set!
|
||||
current-bucket
|
||||
(the-as dma-bucket (&+ (the-as pointer current-bucket) 16))
|
||||
)
|
||||
)
|
||||
(set! (-> dma-buf base) (the-as pointer current-bucket))
|
||||
)
|
||||
(none)
|
||||
)
|
||||
|
||||
;; definition for function dma-buffer-patch-buckets
|
||||
(defun dma-buffer-patch-buckets ((bucket dma-bucket) (count int))
|
||||
(when (nonzero? bucket)
|
||||
(dotimes (i count)
|
||||
(set! (-> bucket last 0 addr) (the-as int (&+ (the-as pointer bucket) 16)))
|
||||
(set! (-> bucket last) (the-as (pointer dma-tag) 0))
|
||||
(set! bucket (the-as dma-bucket (&+ (the-as pointer bucket) 16)))
|
||||
)
|
||||
)
|
||||
bucket
|
||||
)
|
||||
|
||||
;; definition for function dma-bucket-insert-tag
|
||||
(defun
|
||||
dma-bucket-insert-tag
|
||||
((base dma-bucket) (idx int) (tag-start pointer) (tag-end (pointer dma-tag)))
|
||||
(let
|
||||
((bucket
|
||||
(the-as dma-bucket (+ (the-as uint base) (the-as uint (shl idx 4))))
|
||||
)
|
||||
)
|
||||
(set! (-> (the-as dma-bucket (-> bucket last)) next) (the-as uint tag-start))
|
||||
(set! (-> bucket last) tag-end)
|
||||
)
|
||||
tag-start
|
||||
)
|
||||
|
||||
;; failed to figure out what this is:
|
||||
(let ((v0-0 0))
|
||||
)
|
||||
|
||||
;; failed to figure out what this is:
|
||||
(none)
|
||||
|
|
@ -997,3 +997,30 @@ TEST_F(FormRegressionTest, DmaBufferAddVuFunction) {
|
|||
" )";
|
||||
test_with_expr(func, type, expected, false, "", {}, "[[[9, 33], \"t2\", \"dma-packet\"]]");
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, DmaBucketInsertTag) {
|
||||
std::string func =
|
||||
"sll r0, r0, 0\n"
|
||||
" dsll v1, a1, 4\n"
|
||||
" daddu v1, a0, v1\n"
|
||||
" lwu a0, 8(v1)\n"
|
||||
" sw a2, 4(a0)\n"
|
||||
" sw a3, 8(v1)\n"
|
||||
" or v0, a2, r0\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function dma-bucket int pointer (pointer dma-tag) pointer)";
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (let ((v1-1 (the-as dma-bucket (+ (the-as uint arg0) (the-as uint (shl arg1 4))))))\n"
|
||||
" (set! (-> (the-as dma-bucket (-> v1-1 last)) next) (the-as uint arg2))\n"
|
||||
" (set! (-> v1-1 last) arg3)\n"
|
||||
" )\n"
|
||||
" arg2\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "", {},
|
||||
"[\n"
|
||||
" [[2, 6], \"v1\", \"dma-bucket\"],\n"
|
||||
" [3, \"a0\", \"dma-bucket\"]\n"
|
||||
" ]");
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ const std::unordered_set<std::string> g_object_files_to_decompile = {
|
|||
"trigonometry-h", /* transformq-h */ "matrix", "transform", "quaternion",
|
||||
"euler", /* geometry, trigonometry, */
|
||||
"gsound-h", "timer-h", "timer", "vif-h", "dma-h", "video-h", "vu1-user-h", "dma", "dma-buffer",
|
||||
"dma-bucket",
|
||||
/* gap */
|
||||
"bounding-box",
|
||||
/* gap */
|
||||
|
@ -31,7 +32,7 @@ const std::vector<std::string> g_object_files_to_check_against_reference = {
|
|||
/* transformq-h, */
|
||||
"matrix", "transform", "quaternion", "euler", /* geometry, trigonometry */
|
||||
"gsound-h", "timer-h", /* timer, */ "vif-h", "dma-h", "video-h", "vu1-user-h", "dma",
|
||||
"dma-buffer",
|
||||
"dma-buffer", "dma-bucket",
|
||||
/* gap */ "bounding-box",
|
||||
/* gap */
|
||||
"sync-info-h", "sync-info"};
|
||||
|
|
Loading…
Reference in a new issue