From c95e501cec55dc88a223bed5d137462819fca768 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Fri, 14 May 2021 20:12:59 -0400 Subject: [PATCH] handle zero case in enum comparison (#484) --- decompiler/IR2/FormExpressionAnalysis.cpp | 50 +++++++++++++++++-- decompiler/IR2/bitfields.cpp | 23 +++++---- decompiler/IR2/bitfields.h | 1 + .../reference/engine/dma/dma-disasm_REF.gc | 4 +- .../reference/engine/gfx/hw/gs_REF.gc | 2 +- .../reference/engine/load/file-io_REF.gc | 8 +-- 6 files changed, 67 insertions(+), 21 deletions(-) diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index bd6fa9c053..c9a5a7ff11 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -2371,6 +2371,40 @@ Matcher make_int_uint_cast_matcher(const Matcher& thing) { // ConditionElement /////////////////// +namespace { +/*! + * Try to make a pretty looking constant out of value for comparing to something of type. + * If we can't do anything nice, return nullptr. + */ +Form* try_make_constant_for_compare(Form* value, + const TypeSpec& type, + FormPool& pool, + const Env& env) { + if (get_goal_integer_constant(value, env) && env.dts->ts.try_enum_lookup(type)) { + return cast_form(value, type, pool, env); + } + return nullptr; +} + +Form* try_make_constant_from_int_for_compare(s64 value, + const TypeSpec& type, + FormPool& pool, + const Env& env) { + auto enum_type_info = env.dts->ts.try_enum_lookup(type); + if (enum_type_info) { + if (enum_type_info->is_bitfield()) { + if (value != 0) { + // prefer (zero? x) for bitfield enums. + return cast_to_bitfield_enum(enum_type_info, pool, env, value); + } + } else { + return cast_to_int_enum(enum_type_info, pool, env, value); + } + } + return nullptr; +} +} // namespace + FormElement* ConditionElement::make_zero_check_generic(const Env& env, FormPool& pool, const std::vector& source_forms, @@ -2405,6 +2439,13 @@ FormElement* ConditionElement::make_zero_check_generic(const Env& env, } } + auto nice_constant = try_make_constant_from_int_for_compare(0, source_types.at(0), pool, env); + if (nice_constant) { + return pool.alloc_element( + GenericOperator::make_fixed(FixedOperatorKind::EQ), + std::vector{source_forms.at(0), nice_constant}); + } + return pool.alloc_element(GenericOperator::make_compare(m_kind), source_forms); } @@ -2442,12 +2483,11 @@ FormElement* ConditionElement::make_equal_check_generic(const Env& env, return pool.alloc_element(GenericOperator::make_fixed(FixedOperatorKind::NULLP), source_forms.at(0)); } else { - auto int_val = get_goal_integer_constant(source_forms.at(1), env); - auto src0_as_enum = env.dts->ts.try_enum_lookup(source_types.at(0)); - if (src0_as_enum && int_val) { - // if comparing an enum against a constant integer, rewrite the enum. + auto nice_constant = + try_make_constant_for_compare(source_forms.at(1), source_types.at(0), pool, env); + if (nice_constant) { auto forms_with_cast = source_forms; - forms_with_cast.at(1) = cast_form(source_forms.at(1), source_types.at(0), pool, env); + forms_with_cast.at(1) = nice_constant; return pool.alloc_element(GenericOperator::make_fixed(FixedOperatorKind::EQ), forms_with_cast); } else { diff --git a/decompiler/IR2/bitfields.cpp b/decompiler/IR2/bitfields.cpp index d59a6606a2..4d1e45126b 100644 --- a/decompiler/IR2/bitfields.cpp +++ b/decompiler/IR2/bitfields.cpp @@ -563,15 +563,7 @@ Form* cast_to_bitfield_enum(const EnumType* type_info, assert(type_info->is_bitfield()); auto integer = get_goal_integer_constant(strip_int_or_uint_cast(in), env); if (integer) { - auto elts = - decompile_bitfield_enum_from_int(TypeSpec(type_info->get_name()), env.dts->ts, *integer); - auto oper = GenericOperator::make_function( - pool.alloc_single_element_form(nullptr, type_info->get_name())); - std::vector form_elts; - for (auto& x : elts) { - form_elts.push_back(pool.alloc_single_element_form(nullptr, x)); - } - return pool.alloc_single_element_form(nullptr, oper, form_elts); + return cast_to_bitfield_enum(type_info, pool, env, *integer); } else { // all failed, just return whatever. return pool.alloc_single_element_form(nullptr, typespec, in); @@ -594,6 +586,7 @@ Form* cast_to_int_enum(const EnumType* type_info, } Form* cast_to_int_enum(const EnumType* type_info, FormPool& pool, const Env& env, s64 in) { + assert(!type_info->is_bitfield()); auto entry = decompile_int_enum_from_int(TypeSpec(type_info->get_name()), env.dts->ts, in); auto oper = GenericOperator::make_function( pool.alloc_single_element_form(nullptr, type_info->get_name())); @@ -601,4 +594,16 @@ Form* cast_to_int_enum(const EnumType* type_info, FormPool& pool, const Env& env nullptr, oper, pool.alloc_single_element_form(nullptr, entry)); } +Form* cast_to_bitfield_enum(const EnumType* type_info, FormPool& pool, const Env& env, s64 in) { + assert(type_info->is_bitfield()); + auto elts = decompile_bitfield_enum_from_int(TypeSpec(type_info->get_name()), env.dts->ts, in); + auto oper = GenericOperator::make_function( + pool.alloc_single_element_form(nullptr, type_info->get_name())); + std::vector form_elts; + for (auto& x : elts) { + form_elts.push_back(pool.alloc_single_element_form(nullptr, x)); + } + return pool.alloc_single_element_form(nullptr, oper, form_elts); +} + } // namespace decompiler \ No newline at end of file diff --git a/decompiler/IR2/bitfields.h b/decompiler/IR2/bitfields.h index fd0011afc8..efb28993ff 100644 --- a/decompiler/IR2/bitfields.h +++ b/decompiler/IR2/bitfields.h @@ -178,6 +178,7 @@ Form* cast_to_int_enum(const EnumType* type_info, Form* in); Form* cast_to_int_enum(const EnumType* type_info, FormPool& pool, const Env& env, s64 in); +Form* cast_to_bitfield_enum(const EnumType* type_info, FormPool& pool, const Env& env, s64 in); std::optional get_goal_integer_constant(Form* in, const Env&); diff --git a/test/decompiler/reference/engine/dma/dma-disasm_REF.gc b/test/decompiler/reference/engine/dma/dma-disasm_REF.gc index d04f578858..e67856bce2 100644 --- a/test/decompiler/reference/engine/dma/dma-disasm_REF.gc +++ b/test/decompiler/reference/engine/dma/dma-disasm_REF.gc @@ -613,7 +613,7 @@ ((= v1-1 (dma-tag-id cnt)) "cnt" ) - ((zero? v1-1) + ((= v1-1 (dma-tag-id refe)) "refe" ) (else @@ -754,7 +754,7 @@ data-2 (the-as dma-packet (&-> (the-as (pointer uint64) data-2) 2)) ) - (if (zero? (-> current-tag id)) + (if (= (-> current-tag id) (dma-tag-id refe)) (set! end-condition #t) ) ) diff --git a/test/decompiler/reference/engine/gfx/hw/gs_REF.gc b/test/decompiler/reference/engine/gfx/hw/gs_REF.gc index 94087a30fd..7a8f5405a4 100644 --- a/test/decompiler/reference/engine/gfx/hw/gs_REF.gc +++ b/test/decompiler/reference/engine/gfx/hw/gs_REF.gc @@ -115,7 +115,7 @@ ((= v1-0 (gs-psm ct24)) "ct24" ) - ((zero? v1-0) + ((= v1-0 (gs-psm ct32)) "ct32" ) (else diff --git a/test/decompiler/reference/engine/load/file-io_REF.gc b/test/decompiler/reference/engine/load/file-io_REF.gc index f3c8665e74..2999e78387 100644 --- a/test/decompiler/reference/engine/load/file-io_REF.gc +++ b/test/decompiler/reference/engine/load/file-io_REF.gc @@ -105,7 +105,7 @@ ((= kind (file-kind tpage)) (format *file-temp-string* "texture-page~D/tpage-~S" 7 name) ) - ((zero? kind) + ((= kind (file-kind level-bt)) (format *file-temp-string* "level~D/~S-bt" 30 name) ) ((= kind (file-kind tx)) @@ -138,7 +138,7 @@ (defun make-vfile-name ((kind file-kind) (name string)) (clear *file-temp-string*) (cond - ((zero? kind) + ((= kind (file-kind level-bt)) (format *file-temp-string* "$LEVEL/~S" name) ) ((= kind (file-kind art-group)) @@ -163,7 +163,7 @@ ) 7 ) - ((zero? v1-0) + ((= v1-0 (file-kind level-bt)) 30 ) ((= v1-0 (file-kind art-group)) @@ -182,7 +182,7 @@ ((= v1-1 (file-kind tpage)) "texture-page" ) - ((zero? v1-1) + ((= v1-1 (file-kind level-bt)) "bsp-header" ) ((= v1-1 (file-kind art-group))