mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
[Decompiler] More fixes for gkernel (#261)
* fix bugs and more address of support * support for zero checks * fix tests
This commit is contained in:
parent
d01ecf0a9e
commit
14d602c594
|
@ -12,7 +12,7 @@ namespace versions {
|
|||
constexpr s32 GOAL_VERSION_MAJOR = 0;
|
||||
constexpr s32 GOAL_VERSION_MINOR = 6;
|
||||
|
||||
constexpr int DECOMPILER_VERSION = 2;
|
||||
constexpr int DECOMPILER_VERSION = 4;
|
||||
|
||||
// these versions are from the game
|
||||
constexpr u32 ART_FILE_VERSION = 6;
|
||||
|
|
|
@ -1266,14 +1266,18 @@ void CallOp::collect_vars(VariableSet& vars) const {
|
|||
// ConditionalMoveFalseOp
|
||||
/////////////////////////////
|
||||
|
||||
ConditionalMoveFalseOp::ConditionalMoveFalseOp(Variable dst, Variable src, bool on_zero, int my_idx)
|
||||
: AtomicOp(my_idx), m_dst(dst), m_src(src), m_on_zero(on_zero) {}
|
||||
ConditionalMoveFalseOp::ConditionalMoveFalseOp(Variable dst,
|
||||
Variable src,
|
||||
Variable old_value,
|
||||
bool on_zero,
|
||||
int my_idx)
|
||||
: AtomicOp(my_idx), m_dst(dst), m_src(src), m_old_value(old_value), m_on_zero(on_zero) {}
|
||||
|
||||
goos::Object ConditionalMoveFalseOp::to_form(const std::vector<DecompilerLabel>& labels,
|
||||
const Env& env) const {
|
||||
(void)labels;
|
||||
return pretty_print::build_list(m_on_zero ? "cmove-#f-zero" : "cmove-#f-nonzero",
|
||||
m_dst.to_form(env), m_src.to_form(env));
|
||||
m_dst.to_form(env), m_src.to_form(env), m_old_value.to_form(env));
|
||||
}
|
||||
|
||||
bool ConditionalMoveFalseOp::operator==(const AtomicOp& other) const {
|
||||
|
@ -1283,7 +1287,8 @@ bool ConditionalMoveFalseOp::operator==(const AtomicOp& other) const {
|
|||
|
||||
auto po = dynamic_cast<const ConditionalMoveFalseOp*>(&other);
|
||||
assert(po);
|
||||
return m_dst == po->m_dst && m_src == po->m_src && m_on_zero == po->m_on_zero;
|
||||
return m_dst == po->m_dst && m_src == po->m_src && m_on_zero == po->m_on_zero &&
|
||||
m_old_value == po->m_old_value;
|
||||
}
|
||||
|
||||
bool ConditionalMoveFalseOp::is_sequence_point() const {
|
||||
|
@ -1297,11 +1302,13 @@ Variable ConditionalMoveFalseOp::get_set_destination() const {
|
|||
void ConditionalMoveFalseOp::update_register_info() {
|
||||
m_write_regs.push_back(m_dst.reg());
|
||||
m_read_regs.push_back(m_src.reg());
|
||||
m_read_regs.push_back(m_old_value.reg());
|
||||
}
|
||||
|
||||
void ConditionalMoveFalseOp::collect_vars(VariableSet& vars) const {
|
||||
vars.insert(m_dst);
|
||||
vars.insert(m_src);
|
||||
vars.insert(m_old_value);
|
||||
}
|
||||
|
||||
bool get_as_reg_offset(const SimpleExpression& expr, IR2_RegOffset* out) {
|
||||
|
|
|
@ -611,7 +611,7 @@ class CallOp : public AtomicOp {
|
|||
*/
|
||||
class ConditionalMoveFalseOp : public AtomicOp {
|
||||
public:
|
||||
ConditionalMoveFalseOp(Variable dst, Variable src, bool on_zero, int my_idx);
|
||||
ConditionalMoveFalseOp(Variable dst, Variable src, Variable old_value, bool on_zero, int my_idx);
|
||||
goos::Object to_form(const std::vector<DecompilerLabel>& labels, const Env& env) const override;
|
||||
bool operator==(const AtomicOp& other) const override;
|
||||
bool is_sequence_point() const override;
|
||||
|
@ -624,7 +624,7 @@ class ConditionalMoveFalseOp : public AtomicOp {
|
|||
void collect_vars(VariableSet& vars) const override;
|
||||
|
||||
private:
|
||||
Variable m_dst, m_src;
|
||||
Variable m_dst, m_src, m_old_value;
|
||||
bool m_on_zero;
|
||||
};
|
||||
|
||||
|
|
|
@ -339,6 +339,7 @@ FormElement* LoadVarOp::get_as_form(FormPool& pool, const Env& env) const {
|
|||
for (auto& x : rd.tokens) {
|
||||
tokens.push_back(to_token(x));
|
||||
}
|
||||
|
||||
auto load =
|
||||
pool.alloc_single_element_form<DerefElement>(nullptr, source, rd.addr_of, tokens);
|
||||
return pool.alloc_element<SetVarElement>(m_dst, load, true);
|
||||
|
@ -414,9 +415,7 @@ FormElement* CallOp::get_as_form(FormPool& pool, const Env& env) const {
|
|||
}
|
||||
|
||||
FormElement* ConditionalMoveFalseOp::get_as_form(FormPool& pool, const Env&) const {
|
||||
auto source =
|
||||
pool.alloc_single_element_form<SimpleAtomElement>(nullptr, SimpleAtom::make_var(m_src));
|
||||
return pool.alloc_element<ConditionalMoveFalseElement>(m_dst, source, m_on_zero);
|
||||
return pool.alloc_element<ConditionalMoveFalseElement>(m_dst, m_old_value, m_src, m_on_zero);
|
||||
}
|
||||
|
||||
FormElement* FunctionEndOp::get_as_form(FormPool& pool, const Env&) const {
|
||||
|
|
|
@ -258,6 +258,12 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
|
|||
return TP_Type::make_partial_dyanmic_vtable_access();
|
||||
}
|
||||
|
||||
if (arg1_type.is_integer_constant() &&
|
||||
arg0_type.kind == TP_Type::Kind::PRODUCT_WITH_CONSTANT) {
|
||||
return TP_Type::make_from_integer_constant_plus_product(
|
||||
arg1_type.get_integer_constant(), arg0_type.typespec(), arg0_type.get_multiplier());
|
||||
}
|
||||
|
||||
if (arg1_type.is_integer_constant() && is_int_or_uint(dts, arg0_type)) {
|
||||
return TP_Type::make_from_integer_constant_plus_var(arg1_type.get_integer_constant(),
|
||||
arg0_type.typespec());
|
||||
|
@ -268,6 +274,28 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
|
|||
break;
|
||||
}
|
||||
|
||||
if (arg0_type.kind == TP_Type::Kind::INTEGER_CONSTANT_PLUS_VAR_MULT && m_kind == Kind::ADD) {
|
||||
FieldReverseLookupInput rd_in;
|
||||
rd_in.offset = arg0_type.get_add_int_constant();
|
||||
rd_in.stride = arg0_type.get_mult_int_constant();
|
||||
rd_in.base_type = arg1_type.typespec();
|
||||
auto out = env.dts->ts.reverse_field_lookup(rd_in);
|
||||
if (out.success) {
|
||||
return TP_Type::make_from_ts(coerce_to_reg_type(out.result_type));
|
||||
}
|
||||
}
|
||||
|
||||
if (arg0_type.kind == TP_Type::Kind::INTEGER_CONSTANT_PLUS_VAR && m_kind == Kind::ADD) {
|
||||
FieldReverseLookupInput rd_in;
|
||||
rd_in.offset = arg0_type.get_integer_constant();
|
||||
rd_in.stride = 1;
|
||||
rd_in.base_type = arg1_type.typespec();
|
||||
auto out = env.dts->ts.reverse_field_lookup(rd_in);
|
||||
if (out.success) {
|
||||
return TP_Type::make_from_ts(coerce_to_reg_type(out.result_type));
|
||||
}
|
||||
}
|
||||
|
||||
if (arg0_type == arg1_type && is_int_or_uint(dts, arg0_type)) {
|
||||
// both are the same type and both are int/uint, so we assume that we're doing integer math.
|
||||
// we strip off any weird things like multiplication or integer constant.
|
||||
|
|
|
@ -971,29 +971,26 @@ void TypeOfElement::get_modified_regs(RegSet&) const {}
|
|||
/////////////////////////////
|
||||
|
||||
ConditionalMoveFalseElement::ConditionalMoveFalseElement(Variable _dest,
|
||||
Form* _source,
|
||||
Variable _old_value,
|
||||
Variable _source,
|
||||
bool _on_zero)
|
||||
: dest(_dest), source(_source), on_zero(_on_zero) {
|
||||
source->parent_element = this;
|
||||
}
|
||||
: dest(_dest), old_value(_old_value), source(_source), on_zero(_on_zero) {}
|
||||
|
||||
goos::Object ConditionalMoveFalseElement::to_form_internal(const Env& env) const {
|
||||
return pretty_print::build_list(on_zero ? "cmove-#f-zero" : "cmove-#f-nonzero", dest.to_form(env),
|
||||
source->to_form(env));
|
||||
source.to_form(env), old_value.to_form(env));
|
||||
}
|
||||
|
||||
void ConditionalMoveFalseElement::apply(const std::function<void(FormElement*)>& f) {
|
||||
f(this);
|
||||
source->apply(f);
|
||||
}
|
||||
|
||||
void ConditionalMoveFalseElement::apply_form(const std::function<void(Form*)>& f) {
|
||||
source->apply_form(f);
|
||||
}
|
||||
void ConditionalMoveFalseElement::apply_form(const std::function<void(Form*)>&) {}
|
||||
|
||||
void ConditionalMoveFalseElement::collect_vars(VariableSet& vars) const {
|
||||
vars.insert(dest);
|
||||
source->collect_vars(vars);
|
||||
vars.insert(old_value);
|
||||
vars.insert(source);
|
||||
}
|
||||
|
||||
void ConditionalMoveFalseElement::get_modified_regs(RegSet& regs) const {
|
||||
|
|
|
@ -719,9 +719,10 @@ class TypeOfElement : public FormElement {
|
|||
class ConditionalMoveFalseElement : public FormElement {
|
||||
public:
|
||||
Variable dest;
|
||||
Form* source = nullptr;
|
||||
Variable old_value;
|
||||
Variable source;
|
||||
bool on_zero = false;
|
||||
ConditionalMoveFalseElement(Variable _dest, Form* _source, bool _on_zero);
|
||||
ConditionalMoveFalseElement(Variable _dest, Variable _old_value, Variable _source, bool _on_zero);
|
||||
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;
|
||||
|
|
|
@ -465,6 +465,40 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env,
|
|||
throw std::runtime_error("Failed to match for stride 1 address access with add.");
|
||||
}
|
||||
}
|
||||
} else if (arg0_type.kind == TP_Type::Kind::INTEGER_CONSTANT_PLUS_VAR_MULT) {
|
||||
// try to see if this is valid, from the type system.
|
||||
FieldReverseLookupInput input;
|
||||
input.offset = arg0_type.get_add_int_constant();
|
||||
input.stride = arg0_type.get_mult_int_constant();
|
||||
input.base_type = arg1_type.typespec();
|
||||
auto out = env.dts->ts.reverse_field_lookup(input);
|
||||
if (out.success) {
|
||||
// it is. now we have to modify things
|
||||
// first, look for the index
|
||||
auto arg0_matcher =
|
||||
Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::ADDITION),
|
||||
{Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::MULTIPLICATION),
|
||||
{Matcher::integer(input.stride), Matcher::any(0)}),
|
||||
Matcher::integer(input.offset)});
|
||||
auto match_result = match(arg0_matcher, args.at(0));
|
||||
if (match_result.matched) {
|
||||
bool used_index = false;
|
||||
std::vector<DerefToken> tokens;
|
||||
for (auto& tok : out.tokens) {
|
||||
if (tok.kind == FieldReverseLookupOutput::Token::Kind::VAR_IDX) {
|
||||
assert(!used_index);
|
||||
used_index = true;
|
||||
tokens.push_back(DerefToken::make_int_expr(match_result.maps.forms.at(0)));
|
||||
} else {
|
||||
tokens.push_back(to_token(tok));
|
||||
}
|
||||
}
|
||||
result->push_back(pool.alloc_element<DerefElement>(args.at(1), out.addr_of, tokens));
|
||||
return;
|
||||
} else {
|
||||
throw std::runtime_error("Failed to match for stride (non power 2) with add");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -955,6 +989,16 @@ void FunctionCallElement::update_from_stack(const Env& env,
|
|||
}
|
||||
|
||||
auto unsafe = stack.unsafe_peek(Register(Reg::GPR, Reg::A0));
|
||||
if (!unsafe) {
|
||||
if (!stack.is_root()) {
|
||||
fmt::print("STACK:\n{}\n\n", stack.print(env));
|
||||
throw std::runtime_error("Peek got to back and not root stack");
|
||||
}
|
||||
// failed to peek by reaching the end AND root stack, means we just take the function
|
||||
// argument.
|
||||
assert(false); // want to test this before enabling.
|
||||
unsafe = mr.maps.forms.at(0);
|
||||
}
|
||||
if (unsafe) {
|
||||
if (!unsafe->try_as_single_element()) {
|
||||
throw std::runtime_error(
|
||||
|
@ -1160,7 +1204,7 @@ void DerefElement::update_from_stack(const Env& env,
|
|||
|
||||
void UntilElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) {
|
||||
for (auto form : {condition, body}) {
|
||||
FormStack temp_stack;
|
||||
FormStack temp_stack(false);
|
||||
for (auto& entry : form->elts()) {
|
||||
entry->push_to_stack(env, pool, temp_stack);
|
||||
}
|
||||
|
@ -1176,7 +1220,7 @@ void UntilElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stac
|
|||
|
||||
void WhileElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) {
|
||||
for (auto form : {condition, body}) {
|
||||
FormStack temp_stack;
|
||||
FormStack temp_stack(false);
|
||||
for (auto& entry : form->elts()) {
|
||||
entry->push_to_stack(env, pool, temp_stack);
|
||||
}
|
||||
|
@ -1199,14 +1243,14 @@ void CondNoElseElement::push_to_stack(const Env& env, FormPool& pool, FormStack&
|
|||
}
|
||||
for (auto& entry : entries) {
|
||||
for (auto form : {entry.condition, entry.body}) {
|
||||
FormStack temp_stack;
|
||||
FormStack temp_stack(false);
|
||||
for (auto& elt : form->elts()) {
|
||||
elt->push_to_stack(env, pool, temp_stack);
|
||||
}
|
||||
|
||||
std::vector<FormElement*> new_entries;
|
||||
if (form == entry.body && used_as_value) {
|
||||
new_entries = temp_stack.rewrite_to_get_var(pool, final_destination, env);
|
||||
new_entries = rewrite_to_get_var(temp_stack, pool, final_destination);
|
||||
} else {
|
||||
new_entries = temp_stack.rewrite(pool);
|
||||
}
|
||||
|
@ -1246,6 +1290,38 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac
|
|||
std::optional<Variable> last_var;
|
||||
bool rewrite_as_set = true;
|
||||
|
||||
// process conditions and bodies
|
||||
for (auto& entry : entries) {
|
||||
for (auto form : {entry.condition, entry.body}) {
|
||||
FormStack temp_stack(false);
|
||||
for (auto& elt : form->elts()) {
|
||||
elt->push_to_stack(env, pool, temp_stack);
|
||||
}
|
||||
|
||||
std::vector<FormElement*> new_entries;
|
||||
new_entries = temp_stack.rewrite(pool);
|
||||
|
||||
form->clear();
|
||||
for (auto e : new_entries) {
|
||||
form->push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process else.
|
||||
FormStack temp_stack(false);
|
||||
for (auto& elt : else_ir->elts()) {
|
||||
elt->push_to_stack(env, pool, temp_stack);
|
||||
}
|
||||
|
||||
std::vector<FormElement*> new_entries;
|
||||
new_entries = temp_stack.rewrite(pool);
|
||||
|
||||
else_ir->clear();
|
||||
for (auto e : new_entries) {
|
||||
else_ir->push_back(e);
|
||||
}
|
||||
|
||||
// collect all forms which should write the output.
|
||||
std::vector<Form*> write_output_forms;
|
||||
for (const auto& entry : entries) {
|
||||
|
@ -1280,44 +1356,12 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac
|
|||
}
|
||||
}
|
||||
|
||||
// process everything.
|
||||
for (auto& entry : entries) {
|
||||
for (auto form : {entry.condition, entry.body}) {
|
||||
FormStack temp_stack;
|
||||
for (auto& elt : form->elts()) {
|
||||
elt->push_to_stack(env, pool, temp_stack);
|
||||
}
|
||||
|
||||
std::vector<FormElement*> new_entries;
|
||||
if (form == entry.body && rewrite_as_set && !set_unused) {
|
||||
new_entries = temp_stack.rewrite_to_get_var(pool, *last_var, env);
|
||||
} else {
|
||||
new_entries = temp_stack.rewrite(pool);
|
||||
}
|
||||
|
||||
form->clear();
|
||||
for (auto e : new_entries) {
|
||||
form->push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process else.
|
||||
FormStack temp_stack;
|
||||
for (auto& elt : else_ir->elts()) {
|
||||
elt->push_to_stack(env, pool, temp_stack);
|
||||
}
|
||||
|
||||
std::vector<FormElement*> new_entries;
|
||||
// rewrite extra sets as needed.
|
||||
if (rewrite_as_set && !set_unused) {
|
||||
new_entries = temp_stack.rewrite_to_get_var(pool, *last_var, env);
|
||||
} else {
|
||||
new_entries = temp_stack.rewrite(pool);
|
||||
}
|
||||
|
||||
else_ir->clear();
|
||||
for (auto e : new_entries) {
|
||||
else_ir->push_back(e);
|
||||
for (auto& entry : entries) {
|
||||
rewrite_to_get_var(entry.body->elts(), pool, *last_var);
|
||||
}
|
||||
rewrite_to_get_var(else_ir->elts(), pool, *last_var);
|
||||
}
|
||||
|
||||
// raise expression.
|
||||
|
@ -1360,14 +1404,14 @@ void ShortCircuitElement::push_to_stack(const Env& env, FormPool& pool, FormStac
|
|||
}
|
||||
for (int i = 0; i < int(entries.size()); i++) {
|
||||
auto& entry = entries.at(i);
|
||||
FormStack temp_stack;
|
||||
FormStack temp_stack(false);
|
||||
for (auto& elt : entry.condition->elts()) {
|
||||
elt->push_to_stack(env, pool, temp_stack);
|
||||
}
|
||||
|
||||
std::vector<FormElement*> new_entries;
|
||||
if (i == int(entries.size()) - 1) {
|
||||
new_entries = temp_stack.rewrite_to_get_var(pool, final_result, env);
|
||||
new_entries = rewrite_to_get_var(temp_stack, pool, final_result);
|
||||
} else {
|
||||
new_entries = temp_stack.rewrite(pool);
|
||||
}
|
||||
|
@ -1406,14 +1450,14 @@ void ShortCircuitElement::update_from_stack(const Env& env,
|
|||
}
|
||||
for (int i = 0; i < int(entries.size()); i++) {
|
||||
auto& entry = entries.at(i);
|
||||
FormStack temp_stack;
|
||||
FormStack temp_stack(false);
|
||||
for (auto& elt : entry.condition->elts()) {
|
||||
elt->push_to_stack(env, pool, temp_stack);
|
||||
}
|
||||
|
||||
std::vector<FormElement*> new_entries;
|
||||
if (i == int(entries.size()) - 1) {
|
||||
new_entries = temp_stack.rewrite_to_get_var(pool, final_result, env);
|
||||
new_entries = rewrite_to_get_var(temp_stack, pool, final_result);
|
||||
} else {
|
||||
new_entries = temp_stack.rewrite(pool);
|
||||
}
|
||||
|
@ -1629,13 +1673,13 @@ void ConditionElement::update_from_stack(const Env& env,
|
|||
}
|
||||
|
||||
void ReturnElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) {
|
||||
FormStack temp_stack;
|
||||
FormStack temp_stack(false);
|
||||
for (auto& elt : return_code->elts()) {
|
||||
elt->push_to_stack(env, pool, temp_stack);
|
||||
}
|
||||
|
||||
std::vector<FormElement*> new_entries;
|
||||
new_entries = temp_stack.rewrite_to_get_var(pool, env.end_var(), env);
|
||||
new_entries = rewrite_to_get_var(temp_stack, pool, env.end_var());
|
||||
|
||||
return_code->clear();
|
||||
for (auto e : new_entries) {
|
||||
|
@ -1865,7 +1909,33 @@ void ArrayFieldAccess::update_from_stack(const Env& env,
|
|||
auto deref = pool.alloc_element<DerefElement>(base, false, tokens);
|
||||
result->push_back(deref);
|
||||
} else {
|
||||
throw std::runtime_error("Not power of two case, not yet implemented (offset)");
|
||||
// (+ v0-0 (the-as uint (* 12 (+ a3-0 -1))))
|
||||
auto mult_matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::MULTIPLICATION),
|
||||
{Matcher::integer(m_expected_stride), Matcher::any(0)});
|
||||
mult_matcher = Matcher::match_or({Matcher::cast("uint", mult_matcher), mult_matcher});
|
||||
auto add_matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::ADDITION),
|
||||
{Matcher::any(1), mult_matcher});
|
||||
|
||||
auto mr = match(add_matcher, new_val);
|
||||
if (!mr.matched) {
|
||||
throw std::runtime_error("Failed to match non-power of two case: " +
|
||||
new_val->to_string(env));
|
||||
}
|
||||
|
||||
auto base = mr.maps.forms.at(1);
|
||||
auto idx = mr.maps.forms.at(0);
|
||||
|
||||
assert(idx && base);
|
||||
|
||||
std::vector<DerefToken> tokens = m_deref_tokens;
|
||||
for (auto& x : tokens) {
|
||||
if (x.kind() == DerefToken::Kind::EXPRESSION_PLACEHOLDER) {
|
||||
x = DerefToken::make_int_expr(idx);
|
||||
}
|
||||
}
|
||||
|
||||
auto deref = pool.alloc_element<DerefElement>(base, false, tokens);
|
||||
result->push_back(deref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1904,8 +1974,29 @@ void EmptyElement::push_to_stack(const Env&, FormPool&, FormStack& stack) {
|
|||
stack.push_form_element(this, true);
|
||||
}
|
||||
|
||||
void ConditionalMoveFalseElement::push_to_stack(const Env&, FormPool&, FormStack& stack) {
|
||||
stack.push_form_element(this, true);
|
||||
bool is_symbol_true(const Form* form) {
|
||||
auto as_simple = dynamic_cast<SimpleExpressionElement*>(form->try_as_single_element());
|
||||
if (as_simple && as_simple->expr().is_identity() && as_simple->expr().get_arg(0).is_sym_ptr() &&
|
||||
as_simple->expr().get_arg(0).get_str() == "#t") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConditionalMoveFalseElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) {
|
||||
// pop the value and the original
|
||||
auto popped = pop_to_forms({old_value, source}, env, pool, stack, true);
|
||||
if (!is_symbol_true(popped.at(0))) {
|
||||
throw std::runtime_error("Got unrecognized ConditionalMoveFalseElement original: " +
|
||||
popped.at(0)->to_string(env));
|
||||
}
|
||||
stack.push_value_to_reg(dest,
|
||||
pool.alloc_single_element_form<GenericElement>(
|
||||
nullptr,
|
||||
GenericOperator::make_compare(on_zero ? IR2_Condition::Kind::NONZERO
|
||||
: IR2_Condition::Kind::ZERO),
|
||||
std::vector<Form*>{popped.at(1)}),
|
||||
true);
|
||||
}
|
||||
|
||||
void SimpleAtomElement::push_to_stack(const Env&, FormPool&, FormStack& stack) {
|
||||
|
|
|
@ -75,8 +75,9 @@ void FormStack::push_form_element(FormElement* elt, bool sequence_point) {
|
|||
Form* FormStack::pop_reg(const Variable& var,
|
||||
const RegSet& barrier,
|
||||
const Env& env,
|
||||
bool allow_side_effects) {
|
||||
return pop_reg(var.reg(), barrier, env, allow_side_effects);
|
||||
bool allow_side_effects,
|
||||
int begin_idx) {
|
||||
return pop_reg(var.reg(), barrier, env, allow_side_effects, begin_idx);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -91,10 +92,15 @@ bool nonempty_intersection(const RegSet& a, const RegSet& b) {
|
|||
Form* FormStack::pop_reg(Register reg,
|
||||
const RegSet& barrier,
|
||||
const Env& env,
|
||||
bool allow_side_effects) {
|
||||
bool allow_side_effects,
|
||||
int begin_idx) {
|
||||
(void)env; // keep this for easy debugging.
|
||||
RegSet modified;
|
||||
for (size_t i = m_stack.size(); i-- > 0;) {
|
||||
size_t begin = m_stack.size();
|
||||
if (begin_idx >= 0) {
|
||||
begin = begin_idx;
|
||||
}
|
||||
for (size_t i = begin; i-- > 0;) {
|
||||
auto& entry = m_stack.at(i);
|
||||
if (entry.active) {
|
||||
if (entry.destination.has_value() && entry.destination->reg() == reg) {
|
||||
|
@ -111,7 +117,7 @@ Form* FormStack::pop_reg(Register reg,
|
|||
assert(entry.source);
|
||||
if (entry.non_seq_source.has_value()) {
|
||||
assert(entry.sequence_point == false);
|
||||
auto result = pop_reg(entry.non_seq_source->reg(), barrier, env, allow_side_effects);
|
||||
auto result = pop_reg(entry.non_seq_source->reg(), barrier, env, allow_side_effects, i);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
@ -151,12 +157,12 @@ Form* FormStack::unsafe_peek(Register reg) {
|
|||
for (size_t i = m_stack.size(); i-- > 0;) {
|
||||
auto& entry = m_stack.at(i);
|
||||
if (entry.active) {
|
||||
return nullptr;
|
||||
throw std::runtime_error("Failed to unsafe peek 1");
|
||||
}
|
||||
|
||||
entry.source->get_modified_regs(modified);
|
||||
if (modified.find(reg) != modified.end()) {
|
||||
return nullptr;
|
||||
throw std::runtime_error("Failed to unsafe peek 2");
|
||||
}
|
||||
|
||||
if (entry.destination.has_value() && entry.destination->reg() == reg) {
|
||||
|
@ -186,15 +192,9 @@ std::vector<FormElement*> FormStack::rewrite(FormPool& pool) {
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<FormElement*> FormStack::rewrite_to_get_var(FormPool& pool,
|
||||
const Variable& var,
|
||||
const Env&) {
|
||||
// first, rewrite as normal.
|
||||
auto default_result = rewrite(pool);
|
||||
|
||||
// try a few different ways to "naturally" rewrite this so the value of the form is the
|
||||
// value in the given register.
|
||||
|
||||
void rewrite_to_get_var(std::vector<FormElement*>& default_result,
|
||||
FormPool& pool,
|
||||
const Variable& var) {
|
||||
auto last_op_as_set = dynamic_cast<SetVarElement*>(default_result.back());
|
||||
if (last_op_as_set && last_op_as_set->dst().reg() == var.reg()) {
|
||||
default_result.pop_back();
|
||||
|
@ -202,10 +202,17 @@ std::vector<FormElement*> FormStack::rewrite_to_get_var(FormPool& pool,
|
|||
form->parent_form = nullptr; // will get set later, this makes it obvious if I forget.
|
||||
default_result.push_back(form);
|
||||
}
|
||||
return default_result;
|
||||
} else {
|
||||
default_result.push_back(pool.alloc_element<SimpleAtomElement>(SimpleAtom::make_var(var)));
|
||||
return default_result;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<FormElement*> rewrite_to_get_var(FormStack& stack,
|
||||
FormPool& pool,
|
||||
const Variable& var) {
|
||||
auto default_result = stack.rewrite(pool);
|
||||
rewrite_to_get_var(default_result, pool, var);
|
||||
return default_result;
|
||||
}
|
||||
|
||||
} // namespace decompiler
|
|
@ -12,7 +12,7 @@ class Form;
|
|||
*/
|
||||
class FormStack {
|
||||
public:
|
||||
FormStack() = default;
|
||||
explicit FormStack(bool is_root_stack) : m_is_root_stack(is_root_stack) {}
|
||||
void push_value_to_reg(Variable var,
|
||||
Form* value,
|
||||
bool sequence_point,
|
||||
|
@ -25,13 +25,18 @@ class FormStack {
|
|||
Form* pop_reg(const Variable& var,
|
||||
const RegSet& barrier,
|
||||
const Env& env,
|
||||
bool allow_side_effects);
|
||||
Form* pop_reg(Register reg, const RegSet& barrier, const Env& env, bool allow_side_effects);
|
||||
bool allow_side_effects,
|
||||
int begin_idx = -1);
|
||||
Form* pop_reg(Register reg,
|
||||
const RegSet& barrier,
|
||||
const Env& env,
|
||||
bool allow_side_effects,
|
||||
int begin_idx = -1);
|
||||
Form* unsafe_peek(Register reg);
|
||||
bool is_single_expression();
|
||||
std::vector<FormElement*> rewrite(FormPool& pool);
|
||||
std::vector<FormElement*> rewrite_to_get_var(FormPool& pool, const Variable& var, const Env& env);
|
||||
std::string print(const Env& env);
|
||||
bool is_root() const { return m_is_root_stack; }
|
||||
|
||||
private:
|
||||
struct StackEntry {
|
||||
|
@ -49,5 +54,11 @@ class FormStack {
|
|||
std::string print(const Env& env) const;
|
||||
};
|
||||
std::vector<StackEntry> m_stack;
|
||||
bool m_is_root_stack = false;
|
||||
};
|
||||
|
||||
void rewrite_to_get_var(std::vector<FormElement*>& default_result,
|
||||
FormPool& pool,
|
||||
const Variable& var);
|
||||
std::vector<FormElement*> rewrite_to_get_var(FormStack& stack, FormPool& pool, const Variable& var);
|
||||
} // namespace decompiler
|
||||
|
|
|
@ -603,9 +603,9 @@ std::unique_ptr<AtomicOp> convert_sd_1(const Instruction& i0, int idx) {
|
|||
// movn or movz
|
||||
std::unique_ptr<AtomicOp> convert_cmov_1(const Instruction& i0, int idx) {
|
||||
if (i0.get_src(0).is_reg(rs7())) {
|
||||
return std::make_unique<ConditionalMoveFalseOp>(make_dst_var(i0, idx),
|
||||
make_src_var(i0.get_src(1).get_reg(), idx),
|
||||
i0.kind == InstructionKind::MOVZ, idx);
|
||||
return std::make_unique<ConditionalMoveFalseOp>(
|
||||
make_dst_var(i0, idx), make_src_var(i0.get_src(1).get_reg(), idx),
|
||||
make_src_var(i0.get_dst(0).get_reg(), idx), i0.kind == InstructionKind::MOVZ, idx);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ bool convert_to_expressions(Form* top_level_form,
|
|||
// fmt::print("Before anything:\n{}\n",
|
||||
// pretty_print::to_string(top_level_form->to_form(f.ir2.env)));
|
||||
try {
|
||||
FormStack stack;
|
||||
FormStack stack(true);
|
||||
for (auto& entry : top_level_form->elts()) {
|
||||
// fmt::print("push {} to stack\n", entry->to_form(f.ir2.env).print());
|
||||
entry->push_to_stack(f.ir2.env, pool, stack);
|
||||
|
@ -70,7 +70,7 @@ bool convert_to_expressions(Form* top_level_form,
|
|||
std::vector<FormElement*> new_entries;
|
||||
if (f.type.last_arg() != TypeSpec("none")) {
|
||||
auto return_var = f.ir2.atomic_ops->end_op().return_var();
|
||||
new_entries = stack.rewrite_to_get_var(pool, return_var, f.ir2.env);
|
||||
new_entries = rewrite_to_get_var(stack, pool, return_var);
|
||||
auto reg_return_type =
|
||||
f.ir2.env.get_types_after_op(f.ir2.atomic_ops->ops.size() - 1).get(return_var.reg());
|
||||
if (!dts.ts.typecheck(f.type.last_arg(), reg_return_type.typespec(), "", false, false)) {
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
|
||||
"(method 14 dead-pool)":[
|
||||
// bug in game!
|
||||
[25, ["v1", "(pointer process-tree)"]],
|
||||
[24, ["v1", "(pointer process-tree)"]],
|
||||
[30, ["s4", "(pointer process-tree)"]]
|
||||
],
|
||||
|
||||
|
|
|
@ -194,6 +194,10 @@
|
|||
"vars":{"v1-1":"in-goal-mem"}
|
||||
},
|
||||
|
||||
"(method 23 dead-pool-heap)":{
|
||||
"args":["this", "rec"]
|
||||
},
|
||||
|
||||
"seek":{
|
||||
"args":["x", "target", "diff"],
|
||||
"vars":{"f2-0":"err"}
|
||||
|
|
|
@ -272,6 +272,11 @@ TP_Type DecompilerTypeSystem::tp_lca(const TP_Type& existing,
|
|||
return TP_Type::make_from_ts("int");
|
||||
}
|
||||
|
||||
case TP_Type::Kind::INTEGER_CONSTANT_PLUS_VAR_MULT:
|
||||
// a bit lazy here, but I don't think you can ever merge these.
|
||||
*changed = true;
|
||||
return TP_Type::make_from_ts("int");
|
||||
|
||||
case TP_Type::Kind::METHOD:
|
||||
// never allow this to remain method
|
||||
*changed = true;
|
||||
|
|
|
@ -51,6 +51,8 @@ std::string TP_Type::print() const {
|
|||
return fmt::format("<integer {}>", m_int);
|
||||
case Kind::INTEGER_CONSTANT_PLUS_VAR:
|
||||
return fmt::format("<integer {} + {}>", m_int, m_ts.print());
|
||||
case Kind::INTEGER_CONSTANT_PLUS_VAR_MULT:
|
||||
return fmt::format("<integer {} + ({} x {})>", m_int, m_extra_multiplier, m_ts.print());
|
||||
case Kind::DYNAMIC_METHOD_ACCESS:
|
||||
return fmt::format("<dynamic-method-access>");
|
||||
case Kind::METHOD:
|
||||
|
@ -95,6 +97,9 @@ bool TP_Type::operator==(const TP_Type& other) const {
|
|||
return m_int == other.m_int && m_ts == other.m_ts;
|
||||
case Kind::DYNAMIC_METHOD_ACCESS:
|
||||
return true;
|
||||
case Kind::INTEGER_CONSTANT_PLUS_VAR_MULT:
|
||||
return m_int == other.m_int && m_ts == other.m_ts &&
|
||||
m_extra_multiplier == other.m_extra_multiplier;
|
||||
case Kind::INVALID:
|
||||
default:
|
||||
assert(false);
|
||||
|
@ -133,6 +138,7 @@ TypeSpec TP_Type::typespec() const {
|
|||
case Kind::INTEGER_CONSTANT:
|
||||
return TypeSpec("int");
|
||||
case Kind::INTEGER_CONSTANT_PLUS_VAR:
|
||||
case Kind::INTEGER_CONSTANT_PLUS_VAR_MULT:
|
||||
return m_ts;
|
||||
case Kind::DYNAMIC_METHOD_ACCESS:
|
||||
return TypeSpec("object");
|
||||
|
|
|
@ -26,9 +26,9 @@ class TP_Type {
|
|||
STRING_CONSTANT, // a string that's part of the string pool
|
||||
FORMAT_STRING, // a string with a given number of format arguments
|
||||
INTEGER_CONSTANT, // a constant integer.
|
||||
INTEGER_CONSTANT_PLUS_VAR, // constant + variable. used in stuff like (&-> obj inline-val-arr
|
||||
// x)
|
||||
DYNAMIC_METHOD_ACCESS, // partial access into a
|
||||
INTEGER_CONSTANT_PLUS_VAR, // constant + variable. for dynamic addr of
|
||||
INTEGER_CONSTANT_PLUS_VAR_MULT, // like var + 100 + 12 * var2
|
||||
DYNAMIC_METHOD_ACCESS, // partial access into a
|
||||
METHOD,
|
||||
INVALID
|
||||
} kind = Kind::UNINITIALIZED;
|
||||
|
@ -130,6 +130,17 @@ class TP_Type {
|
|||
return result;
|
||||
}
|
||||
|
||||
static TP_Type make_from_integer_constant_plus_product(int64_t constant,
|
||||
const TypeSpec& var_type,
|
||||
int64_t multiplier) {
|
||||
TP_Type result;
|
||||
result.kind = Kind::INTEGER_CONSTANT_PLUS_VAR_MULT;
|
||||
result.m_int = constant;
|
||||
result.m_extra_multiplier = multiplier;
|
||||
result.m_ts = var_type;
|
||||
return result;
|
||||
}
|
||||
|
||||
static TP_Type make_from_product(int64_t multiplier, bool is_signed) {
|
||||
TP_Type result;
|
||||
result.kind = Kind::PRODUCT_WITH_CONSTANT;
|
||||
|
@ -189,10 +200,22 @@ class TP_Type {
|
|||
return m_int;
|
||||
}
|
||||
|
||||
u64 get_add_int_constant() const {
|
||||
assert(kind == Kind::INTEGER_CONSTANT_PLUS_VAR_MULT);
|
||||
return m_int;
|
||||
}
|
||||
|
||||
u64 get_mult_int_constant() const {
|
||||
assert(kind == Kind::INTEGER_CONSTANT_PLUS_VAR_MULT);
|
||||
return m_extra_multiplier;
|
||||
}
|
||||
|
||||
private:
|
||||
TypeSpec m_ts;
|
||||
std::string m_str;
|
||||
int64_t m_int = 0;
|
||||
|
||||
int64_t m_extra_multiplier = 0;
|
||||
};
|
||||
|
||||
struct TypeState {
|
||||
|
|
|
@ -23,4 +23,16 @@
|
|||
- Fixed a bug where integer `abs` appeared instead of `fabs`.
|
||||
- Some support for float -> integer conversions, but it is not 100% yet.
|
||||
- Eliminate inserted coloring moves for function arguments that use `mtc1`.
|
||||
- Support for `>=` for signed numbers.
|
||||
- Support for `>=` for signed numbers.
|
||||
|
||||
## Version 4
|
||||
- Fix bug in decoding of `vdiv`, `vsqrt`, and `vrsqrt` instructions
|
||||
- Support for virtual method calls (may not recognize 100% of cases yet)
|
||||
- Support for "weird" new calls
|
||||
- Fixed a few "update from stack NYI" errors
|
||||
- Array access recognized in more cases with power of two stride.
|
||||
- Support for getting the address of something in an inline array with a stride that's not 1 or a power of 2.
|
||||
- Fixed a bug in unscrambling coloring moves which sometimes caused the wrong values to be used.
|
||||
- Improved nested cond rewriting to eliminate temporaries in more cases when used as a value
|
||||
- Support `zero?` and `nonzero?` which are evaluated to GOAL booleans.
|
||||
- Fix bug where method calls that "passed through" `a0` from the caller were not recognized.
|
|
@ -530,8 +530,8 @@ TEST(DecompilerAtomicOpBuilder, MINS) {
|
|||
}
|
||||
|
||||
TEST(DecompilerAtomicOpBuilder, MOVN) {
|
||||
test_case(assembly_from_list({"movn a1, s7, a2"}), {"(cmove-#f-nonzero a1 a2)"}, {{"a1"}},
|
||||
{{"a2"}}, {{}});
|
||||
test_case(assembly_from_list({"movn a1, s7, a2"}), {"(cmove-#f-nonzero a1 a2 a1)"}, {{"a1"}},
|
||||
{{"a2", "a1"}}, {{}});
|
||||
}
|
||||
|
||||
TEST(DecompilerAtomicOpBuilder, MOVS) {
|
||||
|
@ -539,8 +539,8 @@ TEST(DecompilerAtomicOpBuilder, MOVS) {
|
|||
}
|
||||
|
||||
TEST(DecompilerAtomicOpBuilder, MOVZ) {
|
||||
test_case(assembly_from_list({"movz a1, s7, a2"}), {"(cmove-#f-zero a1 a2)"}, {{"a1"}}, {{"a2"}},
|
||||
{{}});
|
||||
test_case(assembly_from_list({"movz a1, s7, a2"}), {"(cmove-#f-zero a1 a2 a1)"}, {{"a1"}},
|
||||
{{"a2", "a1"}}, {{}});
|
||||
}
|
||||
|
||||
TEST(DecompilerAtomicOpBuilder, MTC1) {
|
||||
|
|
|
@ -945,149 +945,747 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPool) {
|
|||
test_with_expr(func, type, expected, false, "dead-pool");
|
||||
}
|
||||
|
||||
// TODO - sketchy types, and likely a bug in the game.
|
||||
// TEST_F(FormRegressionTest, ExprMethod14DeadPool) {
|
||||
TEST_F(FormRegressionTest, ExprMethod14DeadPool) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" daddiu sp, sp, -64\n"
|
||||
" sd ra, 0(sp)\n"
|
||||
" sd fp, 8(sp)\n"
|
||||
" or fp, t9, r0\n"
|
||||
" sq s4, 16(sp)\n"
|
||||
" sq s5, 32(sp)\n"
|
||||
" sq gp, 48(sp)\n"
|
||||
|
||||
" or s5, a0, r0\n"
|
||||
" or gp, a1, r0\n"
|
||||
" lwu s4, 16(s5)\n"
|
||||
" bnel s7, s4, L223\n"
|
||||
|
||||
" or v1, s7, r0\n"
|
||||
|
||||
" lw v1, *debug-segment*(s7)\n"
|
||||
" beql s7, v1, L223\n"
|
||||
|
||||
" or v1, v1, r0\n"
|
||||
|
||||
" lw v1, *debug-dead-pool*(s7)\n"
|
||||
" dsubu a0, s5, v1\n"
|
||||
" daddiu v1, s7, 8\n"
|
||||
" movz v1, s7, a0\n"
|
||||
|
||||
"L223:\n"
|
||||
" beq s7, v1, L225\n"
|
||||
" or v1, s7, r0\n"
|
||||
|
||||
" lw a0, *debug-dead-pool*(s7)\n"
|
||||
" lwu v1, -4(a0)\n"
|
||||
" lwu t9, 72(v1)\n"
|
||||
" or a1, gp, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" or s4, v0, r0\n"
|
||||
" beq s7, s4, L225\n"
|
||||
" or v1, s7, r0\n"
|
||||
|
||||
" lw t9, format(s7)\n"
|
||||
" addiu a0, r0, 0\n"
|
||||
" daddiu a1, fp, L315\n"
|
||||
" or a2, gp, r0\n"
|
||||
" or v1, s4, r0\n"
|
||||
" beq s7, v1, L224\n"
|
||||
" or a3, s7, r0\n"
|
||||
|
||||
" lwu v1, 0(v1)\n"
|
||||
" lwu a3, 24(v1)\n"
|
||||
|
||||
"L224:\n"
|
||||
" lwu t0, 0(s5)\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
"\n"
|
||||
" or v1, v0, r0\n"
|
||||
|
||||
"L225:\n"
|
||||
" beq s7, s4, L226\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" lwu v1, 0(s4)\n"
|
||||
" sw gp, -4(v1)\n"
|
||||
" lwu v0, 0(s4)\n"
|
||||
" beq r0, r0, L228\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
"L226:\n"
|
||||
" lw t9, format(s7)\n"
|
||||
" addiu a0, r0, 0\n"
|
||||
" daddiu a1, fp, L314\n"
|
||||
" beq s7, s4, L227\n"
|
||||
" or a3, s7, r0\n"
|
||||
|
||||
" lwu v1, 0(s4)\n"
|
||||
" lwu a3, 24(v1)\n"
|
||||
|
||||
"L227:\n"
|
||||
" lwu t0, 0(s5)\n"
|
||||
" or a2, gp, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" or v0, s7, r0\n"
|
||||
|
||||
"L228:\n"
|
||||
" ld ra, 0(sp)\n"
|
||||
" ld fp, 8(sp)\n"
|
||||
" lq gp, 48(sp)\n"
|
||||
" lq s5, 32(sp)\n"
|
||||
" lq s4, 16(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 64";
|
||||
std::string type = "(function dead-pool type int process)";
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (set! s4-0 (-> arg0 child))\n"
|
||||
" (when\n"
|
||||
" (and (not s4-0) *debug-segment* (!= arg0 *debug-dead-pool*))\n"
|
||||
" (set! s4-0 (get-process *debug-dead-pool* arg1 arg2))\n"
|
||||
" (when\n"
|
||||
" s4-0\n"
|
||||
" (set! t9-1 format)\n"
|
||||
" (set! a0-2 0)\n"
|
||||
" (set!\n"
|
||||
" a1-2\n"
|
||||
" \"WARNING: ~A ~A had to be allocated from the debug pool, because ~A was empty.~%\"\n"
|
||||
" )\n"
|
||||
" (set! a2-1 arg1)\n"
|
||||
" (set! v1-6 s4-0)\n"
|
||||
" (t9-1\n"
|
||||
" a0-2\n"
|
||||
" a1-2\n"
|
||||
" a2-1\n"
|
||||
" (if v1-6 (-> (the-as (pointer process-tree) v1-6) 0 self))\n"
|
||||
" (-> arg0 name)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (the-as\n"
|
||||
" process\n"
|
||||
" (cond\n"
|
||||
" (s4-0\n"
|
||||
" (set! (-> (the-as (pointer process-tree) s4-0) 0 type) arg1)\n"
|
||||
" (-> s4-0 0)\n"
|
||||
" )\n"
|
||||
" (else\n"
|
||||
" (format\n"
|
||||
" 0\n"
|
||||
" \"WARNING: ~A ~A could not be allocated, because ~A was empty.~%\"\n"
|
||||
" arg1\n"
|
||||
" (if s4-0 (-> s4-0 0 self))\n"
|
||||
" (-> arg0 name)\n"
|
||||
" )\n"
|
||||
" (quote #f)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )";
|
||||
|
||||
// note - there's likely an actual bug here.
|
||||
test_with_expr(
|
||||
func, type, expected, false, "dead-pool",
|
||||
{{"L315", "WARNING: ~A ~A had to be allocated from the debug pool, because ~A was empty.~%"},
|
||||
{"L314", "WARNING: ~A ~A could not be allocated, because ~A was empty.~%"}},
|
||||
parse_hint_json("[\t\t[24, [\"v1\", \"(pointer process-tree)\"]],\n"
|
||||
"\t\t[30, [\"s4\", \"(pointer process-tree)\"]]]"));
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod15DeadPool) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" daddiu sp, sp, -16\n"
|
||||
" sd ra, 0(sp)\n"
|
||||
|
||||
" lw t9, change-parent(s7)\n"
|
||||
" or v1, a1, r0\n"
|
||||
" or a1, a0, r0\n"
|
||||
" or a0, v1, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" ld ra, 0(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 16";
|
||||
std::string type = "(function dead-pool process none)";
|
||||
std::string expected = "(change-parent arg1 arg0)";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod0DeadPoolHeap) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" daddiu sp, sp, -64\n"
|
||||
" sd ra, 0(sp)\n"
|
||||
" sq s4, 16(sp)\n"
|
||||
" sq s5, 32(sp)\n"
|
||||
" sq gp, 48(sp)\n"
|
||||
|
||||
" or s4, a2, r0\n"
|
||||
" or s5, a3, r0\n"
|
||||
" or gp, t0, r0\n"
|
||||
" lw v1, object(s7)\n"
|
||||
" lwu t9, 16(v1)\n"
|
||||
" or v1, a1, r0\n"
|
||||
" lhu a1, 8(a1)\n"
|
||||
" addiu a2, r0, -16\n"
|
||||
" addiu a3, r0, 12\n"
|
||||
" mult3 a3, a3, s5\n"
|
||||
" daddiu a3, a3, 15\n"
|
||||
" and a2, a2, a3\n"
|
||||
" daddu a1, a1, a2\n"
|
||||
" daddu a2, a1, gp\n"
|
||||
" or a1, v1, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" sw s4, 0(v0)\n"
|
||||
" addiu v1, r0, 256\n"
|
||||
" sw v1, 4(v0)\n"
|
||||
" sw s5, 28(v0)\n"
|
||||
" sw s7, 8(v0)\n"
|
||||
" sw s7, 12(v0)\n"
|
||||
" sw s7, 16(v0)\n"
|
||||
" sw v0, 24(v0)\n"
|
||||
" daddiu v1, v0, 24\n"
|
||||
" sw v1, 20(v0)\n"
|
||||
" or v1, s5, r0\n"
|
||||
" beq r0, r0, L220\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
"L219:\n"
|
||||
" daddiu v1, v1, -1\n"
|
||||
" addiu a0, r0, 12\n"
|
||||
" mult3 a0, a0, v1\n"
|
||||
" daddiu a0, a0, 100\n"
|
||||
" daddu a0, a0, v0\n"
|
||||
" lw a1, *null-process*(s7)\n"
|
||||
" sw a1, 0(a0)\n"
|
||||
" addiu a1, r0, 12\n"
|
||||
" daddiu a2, v1, 1\n"
|
||||
" mult3 a1, a1, a2\n"
|
||||
" daddiu a1, a1, 100\n"
|
||||
" daddu a1, a1, v0\n"
|
||||
" sw a1, 8(a0)\n"
|
||||
|
||||
"L220:\n"
|
||||
" bne v1, r0, L219\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" or v1, s7, r0\n"
|
||||
" or v1, s7, r0\n"
|
||||
" daddiu v1, v0, 100\n"
|
||||
" sw v1, 96(v0)\n"
|
||||
" sw s7, 76(v0)\n"
|
||||
" addiu v1, r0, 12\n"
|
||||
" daddiu a0, s5, -1\n"
|
||||
" mult3 v1, v1, a0\n"
|
||||
" daddu v1, v0, v1\n"
|
||||
" sw s7, 108(v1)\n"
|
||||
" daddiu v1, v0, 76\n"
|
||||
" sw v1, 80(v0)\n"
|
||||
" sw s7, 84(v0)\n"
|
||||
" sw s7, 76(v0)\n"
|
||||
" daddiu v1, v0, 76\n"
|
||||
" sw v1, 48(v0)\n"
|
||||
" sw s7, 52(v0)\n"
|
||||
" addiu v1, r0, -16\n"
|
||||
" daddiu a0, v0, 115\n"
|
||||
" addiu a1, r0, 12\n"
|
||||
" mult3 a1, a1, s5\n"
|
||||
" daddu a0, a0, a1\n"
|
||||
" and v1, v1, a0\n"
|
||||
" sw v1, 60(v0)\n"
|
||||
" lwu v1, 60(v0)\n"
|
||||
" sw v1, 68(v0)\n"
|
||||
" lwu v1, 60(v0)\n"
|
||||
" daddu v1, v1, gp\n"
|
||||
" sw v1, 64(v0)\n"
|
||||
" lwu v1, 64(v0)\n"
|
||||
" sw v1, 72(v0)\n"
|
||||
" ld ra, 0(sp)\n"
|
||||
" lq gp, 48(sp)\n"
|
||||
" lq s5, 32(sp)\n"
|
||||
" lq s4, 16(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 64";
|
||||
std::string type = "(function symbol type basic int int dead-pool-heap)";
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (set!\n"
|
||||
" v0-0\n"
|
||||
" (object-new\n"
|
||||
" arg0\n"
|
||||
" arg1\n"
|
||||
" (the-as\n"
|
||||
" int\n"
|
||||
" (+\n"
|
||||
" (+ (-> arg1 size) (the-as uint (logand -16 (+ (* 12 arg3) 15))))\n"
|
||||
" (the-as uint arg4)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! (-> v0-0 name) arg2)\n"
|
||||
" (set! (-> v0-0 mask) 256)\n"
|
||||
" (set! (-> v0-0 allocated-length) arg3)\n"
|
||||
" (set! (-> v0-0 parent) (quote #f))\n"
|
||||
" (set! (-> v0-0 brother) (quote #f))\n"
|
||||
" (set! (-> v0-0 child) (quote #f))\n"
|
||||
" (set! (-> v0-0 self) v0-0)\n"
|
||||
" (set! (-> v0-0 ppointer) (&-> v0-0 self))\n"
|
||||
" (set! v1-4 arg3)\n"
|
||||
" (while\n"
|
||||
" (nonzero? v1-4)\n"
|
||||
" (set! v1-4 (+ v1-4 -1))\n"
|
||||
" (set! a0-4 (-> v0-0 process-list v1-4))\n"
|
||||
" (set! (-> a0-4 process) *null-process*)\n"
|
||||
" (set! (-> a0-4 next) (-> v0-0 process-list (+ v1-4 1)))\n"
|
||||
" )\n"
|
||||
" (set! (-> v0-0 dead-list next) (-> v0-0 process-list))\n"
|
||||
" (set! (-> v0-0 alive-list process) (quote #f))\n"
|
||||
" (set! (-> v0-0 process-list (+ arg3 -1) next) (quote #f))\n"
|
||||
" (set! (-> v0-0 alive-list prev) (-> v0-0 alive-list))\n"
|
||||
" (set! (-> v0-0 alive-list next) (quote #f))\n"
|
||||
" (set! (-> v0-0 alive-list process) (quote #f))\n"
|
||||
" (set! (-> v0-0 first-gap) (-> v0-0 alive-list))\n"
|
||||
" (set! (-> v0-0 first-shrink) (quote #f))\n"
|
||||
" (set!\n"
|
||||
" (-> v0-0 heap base)\n"
|
||||
" (logand\n"
|
||||
" -16\n"
|
||||
" (the-as int (+ (+ (the-as int v0-0) 115) (the-as uint (* 12 arg3))))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! (-> v0-0 heap current) (-> v0-0 heap base))\n"
|
||||
" (set! (-> v0-0 heap top) (+ (-> v0-0 heap base) (the-as uint arg4)))\n"
|
||||
" (set! (-> v0-0 heap top-base) (-> v0-0 heap top))\n"
|
||||
" v0-0\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "dead-pool-heap", {},
|
||||
parse_hint_json("[\t\t[60, [\"v0\", \"int\"]],\n"
|
||||
"\t\t[61, [\"a0\", \"pointer\"], [\"v0\", \"dead-pool-heap\"]]]"));
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod22DeadPoolHeap) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" lwu v1, 0(a1)\n"
|
||||
" beq s7, v1, L216\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" lwu v1, 0(a1)\n"
|
||||
" lw v1, 68(v1)\n"
|
||||
" daddiu v1, v1, -4\n"
|
||||
" lw a0, process(s7)\n"
|
||||
" lhu a0, 8(a0)\n"
|
||||
" daddu v1, v1, a0\n"
|
||||
" lwu a0, 0(a1)\n"
|
||||
" daddu v0, v1, a0\n"
|
||||
" beq r0, r0, L217\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
"L216:\n"
|
||||
" lwu v0, 60(a0)\n"
|
||||
|
||||
"L217:\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function dead-pool-heap dead-pool-heap-rec pointer)";
|
||||
std::string expected =
|
||||
"(if\n"
|
||||
" (-> arg1 process)\n"
|
||||
" (+\n"
|
||||
" (+ (+ (-> arg1 process allocated-length) -4) (the-as int (-> process size)))\n"
|
||||
" (the-as int (-> arg1 process))\n"
|
||||
" )\n"
|
||||
" (-> arg0 heap base)\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod21DeadPoolHeap) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L209:\n"
|
||||
" lwu v1, 0(a1)\n"
|
||||
" beq s7, v1, L212\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" lwu v1, 0(a1)\n"
|
||||
" lw a2, process(s7)\n"
|
||||
" lhu a2, 8(a2)\n"
|
||||
" daddu v1, v1, a2\n"
|
||||
" lwu a2, 0(a1)\n"
|
||||
" lw a2, 68(a2)\n"
|
||||
" daddu v1, v1, a2\n"
|
||||
" lwu a2, 8(a1)\n"
|
||||
" beq s7, a2, L210\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" lwu a0, 8(a1)\n"
|
||||
" lwu a0, 0(a0)\n"
|
||||
" dsubu v0, a0, v1\n"
|
||||
" beq r0, r0, L211\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
"L210:\n"
|
||||
" lwu a0, 64(a0)\n"
|
||||
" daddiu v1, v1, 4\n"
|
||||
" dsubu v0, a0, v1\n"
|
||||
|
||||
"L211:\n"
|
||||
" beq r0, r0, L214\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
"L212:\n"
|
||||
" lwu v1, 8(a1)\n"
|
||||
" beq s7, v1, L213\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" lwu v1, 8(a1)\n"
|
||||
" lwu v1, 0(v1)\n"
|
||||
" lwu a0, 60(a0)\n"
|
||||
" daddiu a0, a0, 4\n"
|
||||
" dsubu v0, v1, a0\n"
|
||||
" beq r0, r0, L214\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
"L213:\n"
|
||||
" lwu v1, 64(a0)\n"
|
||||
" lwu a0, 60(a0)\n"
|
||||
" dsubu v0, v1, a0\n"
|
||||
|
||||
"L214:\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function dead-pool-heap dead-pool-heap-rec int)";
|
||||
std::string expected =
|
||||
"(cond\n"
|
||||
" ((-> arg1 process)\n"
|
||||
" (set!\n"
|
||||
" v1-3\n"
|
||||
" (+\n"
|
||||
" (+ (-> arg1 process) (the-as uint (-> process size)))\n"
|
||||
" (the-as uint (-> arg1 process allocated-length))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (if\n"
|
||||
" (-> arg1 next)\n"
|
||||
" (- (-> arg1 next process) (the-as uint v1-3))\n"
|
||||
" (- (-> arg0 heap top) (the-as uint (+ v1-3 (the-as uint 4))))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (else\n"
|
||||
" (if\n"
|
||||
" (-> arg1 next)\n"
|
||||
" (-\n"
|
||||
" (-> arg1 next process)\n"
|
||||
" (the-as uint (+ (-> arg0 heap base) (the-as uint 4)))\n"
|
||||
" )\n"
|
||||
" (- (-> arg0 heap top) (the-as uint (-> arg0 heap base)))\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "", {},
|
||||
parse_hint_json("[\t\t[5, [\"v1\", \"pointer\"]],\n"
|
||||
"\t\t[13, [\"a0\", \"pointer\"]],\n"
|
||||
"\t\t[25, [\"v1\", \"pointer\"]]]"));
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod3DeadPoolHeap) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" daddiu sp, sp, -128\n"
|
||||
" sd ra, 0(sp)\n"
|
||||
" sd fp, 8(sp)\n"
|
||||
" or fp, t9, r0\n"
|
||||
" sq s0, 16(sp)\n"
|
||||
" sq s1, 32(sp)\n"
|
||||
" sq s2, 48(sp)\n"
|
||||
" sq s3, 64(sp)\n"
|
||||
" sq s4, 80(sp)\n"
|
||||
" sq s5, 96(sp)\n"
|
||||
" sq gp, 112(sp)\n"
|
||||
|
||||
" or gp, a0, r0\n"
|
||||
|
||||
// CUT HERE
|
||||
|
||||
" lwu v1, 64(gp)\n"
|
||||
" lwu a0, 60(gp)\n"
|
||||
" dsubu s5, v1, a0\n"
|
||||
" lwu v1, 80(gp)\n"
|
||||
" beq s7, v1, L199\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" or a0, gp, r0\n"
|
||||
" lwu v1, -4(a0)\n"
|
||||
" lwu t9, 100(v1)\n"
|
||||
" lwu a1, 80(gp)\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" or v1, v0, r0\n"
|
||||
" beq r0, r0, L200\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
"L199:\n"
|
||||
" or v1, s5, r0\n"
|
||||
|
||||
"L200:\n"
|
||||
" lw t9, format(s7)\n"
|
||||
" daddiu a0, s7, #t\n"
|
||||
" daddiu a1, fp, L300\n"
|
||||
" daddiu a2, gp, 100\n"
|
||||
" dsubu a3, s5, v1\n"
|
||||
" or t0, s5, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" or v1, v0, r0\n"
|
||||
" daddiu s5, gp, 76\n"
|
||||
" addiu s4, r0, 0\n"
|
||||
" beq r0, r0, L204\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
"L201:\n"
|
||||
" lwu v1, 0(s5)\n"
|
||||
" beq s7, v1, L202\n"
|
||||
" or v1, s7, r0\n"
|
||||
|
||||
" lw t9, format(s7)\n"
|
||||
" daddiu a0, s7, #t\n"
|
||||
" daddiu a1, fp, L299\n"
|
||||
" or a2, s4, r0\n"
|
||||
" or a3, s5, r0\n"
|
||||
" lwu t0, 0(s5)\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
"\n"
|
||||
" or v1, v0, r0\n"
|
||||
|
||||
"L202:\n"
|
||||
" or a0, gp, r0\n"
|
||||
" lwu v1, -4(a0)\n"
|
||||
" lwu t9, 100(v1)\n"
|
||||
" or a1, s5, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
"\n"
|
||||
" or s3, v0, r0\n"
|
||||
" beq s3, r0, L203\n"
|
||||
" or v1, s7, r0\n"
|
||||
|
||||
" lw s2, format(s7)\n"
|
||||
" daddiu s1, s7, #t\n"
|
||||
" daddiu s0, fp, L298\n"
|
||||
" or a0, gp, r0\n"
|
||||
" lwu v1, -4(a0)\n"
|
||||
" lwu t9, 104(v1)\n"
|
||||
" or a1, s5, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" or a3, v0, r0\n"
|
||||
" or t9, s2, r0\n"
|
||||
" or a0, s1, r0\n"
|
||||
" or a1, s0, r0\n"
|
||||
" or a2, s3, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
"\n"
|
||||
" or v1, v0, r0\n"
|
||||
|
||||
"L203:\n"
|
||||
" lwu s5, 8(s5)\n"
|
||||
" daddiu s4, s4, 1\n"
|
||||
|
||||
"L204:\n"
|
||||
" bne s7, s5, L201\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" or v1, s7, r0\n"
|
||||
" or v0, gp, r0\n"
|
||||
" ld ra, 0(sp)\n"
|
||||
" ld fp, 8(sp)\n"
|
||||
" lq gp, 112(sp)\n"
|
||||
" lq s5, 96(sp)\n"
|
||||
" lq s4, 80(sp)\n"
|
||||
" lq s3, 64(sp)\n"
|
||||
" lq s2, 48(sp)\n"
|
||||
" lq s1, 32(sp)\n"
|
||||
" lq s0, 16(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 128";
|
||||
std::string type = "(function dead-pool-heap dead-pool-heap)";
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (set! s5-0 (- (-> arg0 heap top) (the-as uint (-> arg0 heap base))))\n"
|
||||
" (set!\n"
|
||||
" v1-3\n"
|
||||
" (if (-> arg0 alive-list prev) (gap-size arg0 (-> arg0 alive-list prev)) s5-0)\n"
|
||||
" )\n"
|
||||
" (format\n"
|
||||
" (quote #t)\n"
|
||||
" \"~Tprocess-list[0] @ #x~X ~D/~D bytes used~%\"\n"
|
||||
" (-> arg0 process-list)\n"
|
||||
" (- s5-0 v1-3)\n"
|
||||
" s5-0\n"
|
||||
" )\n"
|
||||
" (set! s5-1 (-> arg0 alive-list))\n"
|
||||
" (set! s4-0 0)\n"
|
||||
" (while\n"
|
||||
" s5-1\n"
|
||||
" (if\n"
|
||||
" (-> s5-1 process)\n"
|
||||
" (format\n"
|
||||
" (quote #t)\n"
|
||||
" \"~T [~3D] #<dead-pool-heap-rec @ #x~X> ~A~%\"\n"
|
||||
" s4-0\n"
|
||||
" s5-1\n"
|
||||
" (-> s5-1 process)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! s3-0 (gap-size arg0 s5-1))\n"
|
||||
" (if\n"
|
||||
" (nonzero? s3-0)\n"
|
||||
" (format\n"
|
||||
" (quote #t)\n"
|
||||
" \"~T gap: ~D bytes @ #x~X~%\"\n"
|
||||
" s3-0\n"
|
||||
" (gap-location arg0 s5-1)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (set! s5-1 (-> s5-1 next))\n"
|
||||
" (set! s4-0 (+ s4-0 1))\n"
|
||||
" )\n"
|
||||
" arg0\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "",
|
||||
{{"L300", "~Tprocess-list[0] @ #x~X ~D/~D bytes used~%"},
|
||||
{"L299", "~T [~3D] #<dead-pool-heap-rec @ #x~X> ~A~%"},
|
||||
{"L298", "~T gap: ~D bytes @ #x~X~%"}});
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod5DeadPoolHeap) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" addiu v1, r0, -4\n"
|
||||
" dsubu v1, v1, a0\n"
|
||||
" lwu a0, 64(a0)\n"
|
||||
" daddu v0, v1, a0\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function dead-pool-heap int)";
|
||||
std::string expected = "(+ (- -4 (the-as int arg0)) (-> arg0 heap top))";
|
||||
test_with_expr(func, type, expected, false, "", {},
|
||||
parse_hint_json("[[3, [\"v1\", \"int\"], [\"a0\", \"int\"]]]"));
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod19DeadPoolHeap) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
"L194:\n"
|
||||
" daddiu sp, sp, -48\n"
|
||||
" sd ra, 0(sp)\n"
|
||||
" sq s5, 16(sp)\n"
|
||||
" sq gp, 32(sp)\n"
|
||||
|
||||
" or gp, a0, r0\n"
|
||||
" lwu v1, 80(gp)\n"
|
||||
" beq s7, v1, L195\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" or a0, gp, r0\n"
|
||||
" lwu v1, -4(a0)\n"
|
||||
" lwu t9, 96(v1)\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
"\n"
|
||||
" or s5, v0, r0\n"
|
||||
" or a0, gp, r0\n"
|
||||
" lwu v1, -4(a0)\n"
|
||||
" lwu t9, 100(v1)\n"
|
||||
" lwu a1, 80(gp)\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
"\n"
|
||||
" or v1, v0, r0\n"
|
||||
" dsubu v0, s5, v1\n"
|
||||
" beq r0, r0, L196\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
"L195:\n"
|
||||
" addiu v0, r0, 0\n"
|
||||
|
||||
"L196:\n"
|
||||
" ld ra, 0(sp)\n"
|
||||
" lq gp, 32(sp)\n"
|
||||
" lq s5, 16(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 48";
|
||||
std::string type = "(function dead-pool-heap int)";
|
||||
std::string expected =
|
||||
"(if\n"
|
||||
" (-> arg0 alive-list prev)\n"
|
||||
" (- (memory-total arg0) (gap-size arg0 (-> arg0 alive-list prev)))\n"
|
||||
" 0\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod20DeadPoolHeap) {
|
||||
std::string func =
|
||||
" sll r0, r0, 0\n"
|
||||
" lwu v1, 64(a0)\n"
|
||||
" lwu a0, 60(a0)\n"
|
||||
" dsubu v0, v1, a0\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function dead-pool-heap int)";
|
||||
std::string expected = "(- (-> arg0 heap top) (the-as uint (-> arg0 heap base)))";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
||||
//
|
||||
// TEST_F(FormRegressionTest, ExprMethod25DeadPoolHeap) {
|
||||
// std::string func =
|
||||
// " sll r0, r0, 0\n"
|
||||
// " daddiu sp, sp, -64\n"
|
||||
// " daddiu sp, sp, -16\n"
|
||||
// " sd ra, 0(sp)\n"
|
||||
// " sd fp, 8(sp)\n"
|
||||
// " or fp, t9, r0\n"
|
||||
// " sq s4, 16(sp)\n"
|
||||
// " sq s5, 32(sp)\n"
|
||||
// " sq gp, 48(sp)\n"
|
||||
//
|
||||
// " or s5, a0, r0\n"
|
||||
// " or gp, a1, r0\n"
|
||||
// " lwu s4, 16(s5)\n"
|
||||
// " bnel s7, s4, L223\n"
|
||||
//
|
||||
// " or v1, s7, r0\n"
|
||||
//
|
||||
// " lw v1, *debug-segment*(s7)\n"
|
||||
// " beql s7, v1, L223\n"
|
||||
//
|
||||
// " or v1, v1, r0\n"
|
||||
//
|
||||
// " lw v1, *debug-dead-pool*(s7)\n"
|
||||
// " dsubu a0, s5, v1\n"
|
||||
// " daddiu v1, s7, 8\n"
|
||||
// " movz v1, s7, a0\n"
|
||||
//
|
||||
// "L223:\n"
|
||||
// " beq s7, v1, L225\n"
|
||||
// " or v1, s7, r0\n"
|
||||
//
|
||||
// " lw a0, *debug-dead-pool*(s7)\n"
|
||||
// " lwu v1, -4(a0)\n"
|
||||
// " lwu t9, 72(v1)\n"
|
||||
// " or a1, gp, r0\n"
|
||||
// " jalr ra, t9\n"
|
||||
// " sll v0, ra, 0\n"
|
||||
//
|
||||
// " or s4, v0, r0\n"
|
||||
// " beq s7, s4, L225\n"
|
||||
// " or v1, s7, r0\n"
|
||||
//
|
||||
// " lw t9, format(s7)\n"
|
||||
// " addiu a0, r0, 0\n"
|
||||
// " daddiu a1, fp, L315\n"
|
||||
// " or a2, gp, r0\n"
|
||||
// " or v1, s4, r0\n"
|
||||
// " beq s7, v1, L224\n"
|
||||
// " or a3, s7, r0\n"
|
||||
//
|
||||
// " lwu v1, 0(v1)\n"
|
||||
// " lwu a3, 24(v1)\n"
|
||||
//
|
||||
// "L224:\n"
|
||||
// " lwu t0, 0(s5)\n"
|
||||
// " jalr ra, t9\n"
|
||||
// " sll v0, ra, 0\n"
|
||||
// "\n"
|
||||
// " or v1, v0, r0\n"
|
||||
//
|
||||
// "L225:\n"
|
||||
// " beq s7, s4, L226\n"
|
||||
// " lwu v1, 64(a0)\n"
|
||||
// " lwu a1, 80(a0)\n"
|
||||
// " beq s7, a1, L191\n"
|
||||
// " sll r0, r0, 0\n"
|
||||
//
|
||||
// " lwu v1, 0(s4)\n"
|
||||
// " sw gp, -4(v1)\n"
|
||||
// " lwu v0, 0(s4)\n"
|
||||
// " beq r0, r0, L228\n"
|
||||
// " sll r0, r0, 0\n"
|
||||
//
|
||||
// "L226:\n"
|
||||
// " lw t9, format(s7)\n"
|
||||
// " addiu a0, r0, 0\n"
|
||||
// " daddiu a1, fp, L314\n"
|
||||
// " beq s7, s4, L227\n"
|
||||
// " or a3, s7, r0\n"
|
||||
//
|
||||
// " lwu v1, 0(s4)\n"
|
||||
// " lwu a3, 24(v1)\n"
|
||||
//
|
||||
// "L227:\n"
|
||||
// " lwu t0, 0(s5)\n"
|
||||
// " or a2, gp, r0\n"
|
||||
// " or v1, a0, r0\n"
|
||||
// " lwu a1, -4(v1)\n"
|
||||
// " lwu t9, 100(a1)\n"
|
||||
// " lwu a1, 80(a0)\n"
|
||||
// " or a0, v1, r0\n" - is eliminated falsely because of the virtual method thing.
|
||||
// " jalr ra, t9\n"
|
||||
// " sll v0, ra, 0\n"
|
||||
//
|
||||
// " or v0, s7, r0\n"
|
||||
// " beq r0, r0, L192\n"
|
||||
// " sll r0, r0, 0\n"
|
||||
//
|
||||
// "L228:\n"
|
||||
// "L191:\n"
|
||||
// " lwu a0, 60(a0)\n"
|
||||
// " dsubu v0, v1, a0\n"
|
||||
//
|
||||
// "L192:\n"
|
||||
// " ld ra, 0(sp)\n"
|
||||
// " ld fp, 8(sp)\n"
|
||||
// " lq gp, 48(sp)\n"
|
||||
// " lq s5, 32(sp)\n"
|
||||
// " lq s4, 16(sp)\n"
|
||||
// " jr ra\n"
|
||||
// " daddiu sp, sp, 64";
|
||||
// std::string type = "(function dead-pool type int process)";
|
||||
// std::string expected =
|
||||
// "(begin\n"
|
||||
// " (set! s4-0 (-> arg0 child))\n"
|
||||
// " (when\n"
|
||||
// " (and (not s4-0) *debug-segment* (!= arg0 *debug-dead-pool*))\n"
|
||||
// " (set! s4-0 (get-process *debug-dead-pool* arg1 arg2))\n"
|
||||
// " (when\n"
|
||||
// " s4-0\n"
|
||||
// " (set! t9-1 format)\n"
|
||||
// " (set! a0-2 0)\n"
|
||||
// " (set!\n"
|
||||
// " a1-2\n"
|
||||
// " \"WARNING: ~A ~A had to be allocated from the debug pool, because ~A was empty.~%\"\n"
|
||||
// " )\n"
|
||||
// " (set! a2-1 arg1)\n"
|
||||
// " (set! v1-6 s4-0)\n"
|
||||
// " (t9-1 a0-2 a1-2 a2-1 (if v1-6 (-> v1-6 name 6)) (-> arg0 name))\n"
|
||||
// " )\n"
|
||||
// " )\n"
|
||||
// " (the-as\n"
|
||||
// " process\n"
|
||||
// " (cond\n"
|
||||
// " (s4-0\n"
|
||||
// " (set! (-> (the-as (pointer process-tree) s4-0) 0 type) arg1)\n"
|
||||
// " (-> s4-0 0)\n"
|
||||
// " )\n"
|
||||
// " (else\n"
|
||||
// " (format\n"
|
||||
// " 0\n"
|
||||
// " \"WARNING: ~A ~A could not be allocated, because ~A was empty.~%\"\n"
|
||||
// " arg1\n"
|
||||
// " (if s4-0 (-> s4-0 0 self))\n"
|
||||
// " (-> arg0 name)\n"
|
||||
// " )\n"
|
||||
// " (quote #f)\n"
|
||||
// " )\n"
|
||||
// " )\n"
|
||||
// " )\n"
|
||||
// " )";
|
||||
// test_with_expr(
|
||||
// func, type, expected, false, "dead-pool",
|
||||
// {{"L315", "WARNING: ~A ~A had to be allocated from the debug pool, because ~A was
|
||||
// empty.~%"},
|
||||
// {"L314", "WARNING: ~A ~A could not be allocated, because ~A was empty.~%"}},
|
||||
// parse_hint_json("[\t\t[25, [\"v1\", \"(pointer process-tree)\"]],\n"
|
||||
// "\t\t[30, [\"s4\", \"(pointer process-tree)\"]]]"));
|
||||
// " daddiu sp, sp, 16";
|
||||
// std::string type = "(function dead-pool-heap int)";
|
||||
// std::string expected = "blah";
|
||||
// test_with_expr(func, type, expected);
|
||||
//}
|
Loading…
Reference in a new issue