diff --git a/decompiler/IR2/AtomicOp.h b/decompiler/IR2/AtomicOp.h index 3bf097d933..bf477d21c9 100644 --- a/decompiler/IR2/AtomicOp.h +++ b/decompiler/IR2/AtomicOp.h @@ -175,6 +175,9 @@ class SimpleAtom { void get_regs(std::vector* out) const; SimpleExpression as_expr() const; TP_Type get_type(const TypeState& input, const Env& env, const DecompilerTypeSystem& dts) const; + RegisterTypeState get_type(InstrTypeState& input, + const Env& env, + const DecompilerTypeSystem& dts) const; const std::string& get_str() const { assert(is_sym_ptr() || is_sym_val()); return m_string; @@ -259,6 +262,10 @@ class SimpleExpression { } void get_regs(std::vector* out) const; TP_Type get_type(const TypeState& input, const Env& env, const DecompilerTypeSystem& dts) const; + RegisterTypeState get_type(InstrTypeState& input, + const Env& env, + const DecompilerTypeSystem& dts) const; + TP_Type get_type_int2(const TypeState& input, const Env& env, const DecompilerTypeSystem& dts) const; @@ -298,10 +305,18 @@ class SetVarOp : public AtomicOp { const RegisterAccess& dst() const { return m_dst; } const SimpleExpression& src() const { return m_src; } + void multi_types_internal(InstrTypeState* output, + InstrTypeState& input, + const Env& env, + DecompilerTypeSystem& dts) override; + private: RegisterAccess m_dst; SimpleExpression m_src; + + // depending on if we use the new or old type pass. std::optional m_source_type; + RegisterTypeState* m_source_type_new = nullptr; }; /*! @@ -725,6 +740,11 @@ class FunctionEndOp : public AtomicOp { return m_return_reg; } + void multi_types_internal(InstrTypeState* output, + InstrTypeState& input, + const Env& env, + DecompilerTypeSystem& dts) override; + private: bool m_function_has_return_value = true; RegisterAccess m_return_reg; diff --git a/decompiler/IR2/AtomicOpForm.cpp b/decompiler/IR2/AtomicOpForm.cpp index 614cf37786..cc893a6aa8 100644 --- a/decompiler/IR2/AtomicOpForm.cpp +++ b/decompiler/IR2/AtomicOpForm.cpp @@ -65,6 +65,7 @@ FormElement* SetVarOp::get_as_form(FormPool& pool, const Env& env) const { } } else { // access a field + // TODO: rework for new type pass. auto arg0_type = env.get_types_before_op(m_my_idx).get(m_src.get_arg(0).var().reg()); if (arg0_type.kind == TP_Type::Kind::TYPESPEC) { FieldReverseLookupInput rd_in; @@ -93,6 +94,7 @@ FormElement* SetVarOp::get_as_form(FormPool& pool, const Env& env) const { // create element auto source = pool.alloc_single_element_form(nullptr, m_src, m_my_idx); + // TODO: rework m_source_type for new type pass. auto result = pool.alloc_element(m_dst, source, is_sequence_point(), m_source_type.value_or(TypeSpec("object"))); diff --git a/decompiler/IR2/MultiTypeAnalysis.cpp b/decompiler/IR2/MultiTypeAnalysis.cpp index 1bfeb37a36..075852fc46 100644 --- a/decompiler/IR2/MultiTypeAnalysis.cpp +++ b/decompiler/IR2/MultiTypeAnalysis.cpp @@ -4,6 +4,8 @@ * register, due to overlapping fields in types. When it encounters a function call, set, or * certain math operation, it will attempt to prune the decision tree to remove incompatible types. * + * Compared to + * * When there are multiple ways to get the same type, or the type is ambiguous, it will use the one * with the highest score. * @@ -614,7 +616,6 @@ std::vector convert_to_old_format(const std::vector& return result; } - bool dbg_types = true; } // namespace @@ -807,4 +808,60 @@ void AtomicOp::multi_types_internal(InstrTypeState*, fmt::format("multi_type_internal not yet implemented for {}", typeid(*this).name())); } +RegisterTypeState SimpleAtom::get_type(InstrTypeState& input, + const Env& env, + const DecompilerTypeSystem& dts) const { + switch (m_kind) { + case Kind::VARIABLE: + // just get the type in the variable. + return input.get_state(var().reg()); + default: + throw std::runtime_error("Simple atom cannot get_type (multi types): " + to_string(env)); + } +} + +RegisterTypeState SimpleExpression::get_type(InstrTypeState& input, + const Env& env, + const DecompilerTypeSystem& dts) const { + switch (m_kind) { + case Kind::IDENTITY: + // this expression is just an atom, so return the atom's type. + return m_args[0].get_type(input, env, dts); + default: + throw std::runtime_error("Simple expression cannot get_type (multi types): " + + to_string(env)); + } +} + +void SetVarOp::multi_types_internal(InstrTypeState* output, + InstrTypeState& input, + const Env& env, + DecompilerTypeSystem& dts) { + // we have special cases where we can infer something about the source type from the dest + + // GOAL will use mfc, fX, r0 to set a float to 0. + if (m_dst.reg().get_kind() == Reg::FPR && m_src.is_identity() && m_src.get_arg(0).is_int() && + m_src.get_arg(0).get_int() == 0) { + output->assign(m_dst.reg(), RegisterTypeState("float")); + } else { + output->assign(m_dst.reg(), m_src.get_type(input, env, dts)); + } + + // it's safe to do this, though a little confusing. + // if this type is based on a cast, we can't have the possibility of referencing the temporary + // cast. Instead we copy the cast (or the result of using the temporary cast) to the output. + // If the next op casts this, it won't touch this because it will use its own temporary cast. + + // In the final cast application, this is also okay because this node will be kept, but replaced + // in the main graph. This will refer to the type without the cast, which is what we want. + auto& out_node = output->get(m_dst.reg()); + assert(out_node.is_alloc_point() && !out_node.is_clobber() && !out_node.is_cast()); + m_source_type_new = &output->get_state(m_dst.reg()); +} + +void FunctionEndOp::multi_types_internal(InstrTypeState*, + InstrTypeState&, + const Env&, + DecompilerTypeSystem&) {} + } // namespace decompiler \ No newline at end of file diff --git a/decompiler/IR2/MultiTypeAnalysis.h b/decompiler/IR2/MultiTypeAnalysis.h index 97f233ab4e..7f02c638f2 100644 --- a/decompiler/IR2/MultiTypeAnalysis.h +++ b/decompiler/IR2/MultiTypeAnalysis.h @@ -81,9 +81,15 @@ struct RegisterTypeState { bool is_temp_node = false; RegisterTypeState() = default; - RegisterTypeState(const PossibleType& single_type) : possible_types({single_type}) { + explicit RegisterTypeState(const PossibleType& single_type) : possible_types({single_type}) { single_type_cache = 0; } + explicit RegisterTypeState(const TP_Type& single_type) + : RegisterTypeState(PossibleType(single_type)) {} + explicit RegisterTypeState(const TypeSpec& type) + : RegisterTypeState(TP_Type::make_from_ts(type)) {} + explicit RegisterTypeState(const std::string& type) + : RegisterTypeState(TP_Type::make_from_ts(type)) {} void reduce_to_single_best_type(DecompWarnings* warnings, int op_idx, const DerefHint* hint); bool is_single_type() const; const PossibleType& get_single_type_decision() const; @@ -119,6 +125,7 @@ struct RegisterNode { } bool is_alloc_point() const { return m_flags & FLAG_ALLOC_POINT; } bool is_clobber() const { return m_flags & FLAG_CLOBBER; } + bool is_cast() const { return m_flags & FLAG_CAST_TEMP; } s64 uid() const { return m_uid; } void set_uid(s64 val) { m_uid = val; }