diff --git a/common/type_system/Enum.h b/common/type_system/Enum.h deleted file mode 100644 index caedddd06c..0000000000 --- a/common/type_system/Enum.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include -#include "common/common_types.h" -#include "common/type_system/TypeSpec.h" - -struct GoalEnum { - std::string name; - TypeSpec base_type; - bool is_bitfield = false; - std::unordered_map entries; - - bool operator==(const GoalEnum& other) const; - bool operator!=(const GoalEnum& other) const; -}; diff --git a/common/type_system/Type.cpp b/common/type_system/Type.cpp index 9b06e8bd79..2b0318613f 100644 --- a/common/type_system/Type.cpp +++ b/common/type_system/Type.cpp @@ -516,15 +516,6 @@ bool StructureType::operator==(const Type& other) const { // clang-format on } -bool BitFieldType::operator==(const Type& other) const { - if (typeid(*this) != typeid(other)) { - return false; - } - - auto* p_other = dynamic_cast(&other); - return other.is_equal(*this) && m_fields == p_other->m_fields; -} - int StructureType::get_size_in_memory() const { return m_size_in_mem; } @@ -669,4 +660,44 @@ std::string BitFieldType::print() const { result += fmt::format("Mem size: {}, load size: {}, signed {}, align {}\n", get_size_in_memory(), get_load_size(), get_load_signed(), get_in_memory_alignment()); return result; +} + +bool BitFieldType::operator==(const Type& other) const { + if (typeid(*this) != typeid(other)) { + return false; + } + + auto* p_other = dynamic_cast(&other); + return other.is_equal(*this) && m_fields == p_other->m_fields; +} + +///////////////// +// Enum +///////////////// + +EnumType::EnumType(const ValueType* parent, + std::string name, + bool is_bitfield, + const std::unordered_map& entries) + : ValueType(parent->get_name(), + std::move(name), + parent->is_boxed(), + parent->get_load_size(), + parent->get_load_signed(), + parent->get_preferred_reg_class()), + m_is_bitfield(is_bitfield), + m_entries(entries) {} + +std::string EnumType::print() const { + return fmt::format("Enum Type {}", m_name); +} + +bool EnumType::operator==(const Type& other) const { + if (typeid(*this) != typeid(other)) { + return false; + } + + auto* p_other = dynamic_cast(&other); + return other.is_equal(*this) && (m_entries == p_other->m_entries) && + (m_is_bitfield == p_other->m_is_bitfield); } \ No newline at end of file diff --git a/common/type_system/Type.h b/common/type_system/Type.h index 09afee0cb9..ac69c795c9 100644 --- a/common/type_system/Type.h +++ b/common/type_system/Type.h @@ -5,11 +5,11 @@ * Representation of a GOAL type in the type system. */ -#ifndef JAK_TYPE_H -#define JAK_TYPE_H +#pragma once #include #include +#include #include "common/goal_constants.h" #include "TypeSpec.h" @@ -91,6 +91,8 @@ class Type { } } + bool is_boxed() const { return m_is_boxed; } + protected: Type(std::string parent, std::string name, bool is_boxed); @@ -312,4 +314,19 @@ class BitFieldType : public ValueType { std::vector m_fields; }; -#endif // JAK_TYPE_H +class EnumType : public ValueType { + public: + EnumType(const ValueType* parent, + std::string name, + bool is_bitfield, + const std::unordered_map& entries); + std::string print() const override; + bool operator==(const Type& other) const override; + const std::unordered_map& entries() const { return m_entries; } + bool is_bitfield() const { return m_is_bitfield; } + + private: + friend class TypeSystem; + bool m_is_bitfield = false; + std::unordered_map m_entries; +}; diff --git a/common/type_system/TypeSystem.cpp b/common/type_system/TypeSystem.cpp index e6e77356b5..aa5c8138dd 100644 --- a/common/type_system/TypeSystem.cpp +++ b/common/type_system/TypeSystem.cpp @@ -10,7 +10,6 @@ #include #include "TypeSystem.h" #include "common/util/math_util.h" -#include "deftype.h" TypeSystem::TypeSystem() { // the "none" and "_type_" types are included by default. @@ -70,37 +69,6 @@ Type* TypeSystem::add_type(const std::string& name, std::unique_ptr type) return m_types[name].get(); } -/*! - * Add a new 'enum type'. This maps enum names to the their type's name, allowing the enum name to - * be used as if it were a type name. - */ -Type* TypeSystem::add_enum_type(const std::string& name, const std::string& type) { - auto kv = m_enum_types.find(name); - if (kv != m_enum_types.end()) { - // exists already - - if (kv->second != type) { - // exists, and we are trying to change it! - fmt::print("[TypeSystem] Enum {} was originally\n{}\nand is redefined as\n{}\n", name, - kv->second, type); - - throw std::runtime_error("Enum type was redefined."); - } - } else { - // an enum has been forward declared, which is only allowed for types - if (m_forward_declared_types.find(name) != m_forward_declared_types.end()) { - fmt::print("[TypeSystem] Enum {} was forward-declared, enums cannot be forward-declared\n", - name); - - throw std::runtime_error("Enum was forward-declared."); - } - - m_enum_types[name] = type; - } - - return lookup_type(m_enum_types[name]); -} - /*! * Inform the type system that there will eventually be a type named "name". * This will allow the type system to generate TypeSpecs for this type, but not access detailed @@ -216,9 +184,6 @@ TypeSpec TypeSystem::make_typespec(const std::string& name) const { if (m_types.find(name) != m_types.end() || m_forward_declared_types.find(name) != m_forward_declared_types.end()) { return TypeSpec(name); - } else if (m_enum_types.find(name) != m_enum_types.end()) { - // simply return the enum's type instead - return TypeSpec(m_enum_types.at(name)); } else { fmt::print("[TypeSystem] The type {} is unknown.\n", name); throw std::runtime_error("make_typespec failed"); @@ -233,10 +198,6 @@ bool TypeSystem::partially_defined_type_exists(const std::string& name) const { return m_forward_declared_types.find(name) != m_forward_declared_types.end(); } -bool TypeSystem::enum_type_exists(const std::string& name) const { - return m_enum_types.find(name) != m_enum_types.end(); -} - TypeSpec TypeSystem::make_array_typespec(const TypeSpec& element_type) const { return TypeSpec("array", {element_type}); } @@ -359,17 +320,6 @@ MethodInfo TypeSystem::add_method(const std::string& type_name, return add_method(lookup_type(make_typespec(type_name)), method_name, ts, allow_new_method); } -/*! - * Return the type name of an enum. Throws if the enum does not exist. - */ -std::string TypeSystem::get_enum_type_name(const std::string& name) const { - if (m_enum_types.find(name) != m_enum_types.end()) { - return m_enum_types.at(name); - } else { - throw std::runtime_error("get_enum_type_name failed"); - } -} - /*! * Add a method, if it doesn't exist. If the method already exists (possibly in a parent), checks to * see if this is an identical definition. If not, it's an error, and if so, nothing happens. @@ -1152,6 +1102,18 @@ bool TypeSystem::typecheck_base_types(const std::string& expected, return false; } +EnumType* TypeSystem::try_enum_lookup(const std::string& type_name) const { + auto it = m_types.find(type_name); + if (it != m_types.end()) { + return dynamic_cast(it->second.get()); + } + return nullptr; +} + +EnumType* TypeSystem::try_enum_lookup(const TypeSpec& type) const { + return try_enum_lookup(type.base_type()); +} + /*! * Get a path from type to object. */ @@ -1246,6 +1208,9 @@ TypeSpec TypeSystem::lowest_common_ancestor(const std::vector& types) return result; } +/*! + * Converts a type in memory to the type you'll get in a register after loading it. + */ TypeSpec coerce_to_reg_type(const TypeSpec& in) { if (in.arg_count() == 0) { if (in.base_type() == "int8" || in.base_type() == "int16" || in.base_type() == "int32" || diff --git a/common/type_system/TypeSystem.h b/common/type_system/TypeSystem.h index 6229ed6dea..2d3fb339c6 100644 --- a/common/type_system/TypeSystem.h +++ b/common/type_system/TypeSystem.h @@ -91,7 +91,6 @@ class TypeSystem { TypeSystem(); Type* add_type(const std::string& name, std::unique_ptr type); - Type* add_enum_type(const std::string& name, const std::string& type); void forward_declare_type(const std::string& name); void forward_declare_type_as_basic(const std::string& name); void forward_declare_type_as_structure(const std::string& name); @@ -102,7 +101,6 @@ class TypeSystem { bool fully_defined_type_exists(const std::string& name) const; bool partially_defined_type_exists(const std::string& name) const; - bool enum_type_exists(const std::string& name) const; TypeSpec make_typespec(const std::string& name) const; TypeSpec make_array_typespec(const TypeSpec& element_type) const; TypeSpec make_function_typespec(const std::vector& arg_types, @@ -119,8 +117,6 @@ class TypeSystem { Type* lookup_type_allow_partial_def(const TypeSpec& ts) const; Type* lookup_type_allow_partial_def(const std::string& name) const; - std::string get_enum_type_name(const std::string& name) const; - MethodInfo add_method(const std::string& type_name, const std::string& method_name, const TypeSpec& ts, @@ -187,6 +183,8 @@ class TypeSystem { return result; } + EnumType* try_enum_lookup(const std::string& type_name) const; + EnumType* try_enum_lookup(const TypeSpec& type) const; TypeSpec lowest_common_ancestor(const TypeSpec& a, const TypeSpec& b) const; TypeSpec lowest_common_ancestor_reg(const TypeSpec& a, const TypeSpec& b) const; TypeSpec lowest_common_ancestor(const std::vector& types) const; @@ -233,7 +231,6 @@ class TypeSystem { enum ForwardDeclareKind { TYPE, STRUCTURE, BASIC }; std::unordered_map> m_types; - std::unordered_map m_enum_types; std::unordered_map m_forward_declared_types; std::vector> m_old_types; diff --git a/common/type_system/defenum.cpp b/common/type_system/defenum.cpp index fc1bb69a60..287d4eefed 100644 --- a/common/type_system/defenum.cpp +++ b/common/type_system/defenum.cpp @@ -4,7 +4,7 @@ * This is used both in the compiler and in the decompiler for the type definition file. */ -#include "Enum.h" +#include "common/goos/ParseHelpers.h" #include "defenum.h" #include "deftype.h" #include "third-party/fmt/core.h" @@ -58,20 +58,6 @@ bool integer_fits(s64 in, int size, bool is_signed) { } } -template -void for_each_in_list(const goos::Object& list, T f) { - const goos::Object* iter = &list; - while (iter->is_pair()) { - auto lap = iter->as_pair(); - f(lap->car); - iter = &lap->cdr; - } - - if (!iter->is_empty_list()) { - throw std::runtime_error("invalid list in for_each_in_list: " + list.print()); - } -} - std::string symbol_string(const goos::Object& obj) { if (obj.is_symbol()) { return obj.as_symbol()->name; @@ -81,10 +67,11 @@ std::string symbol_string(const goos::Object& obj) { } // namespace -void parse_defenum(const goos::Object& defenum, TypeSystem* ts, GoalEnum& goalenum) { +EnumType* parse_defenum(const goos::Object& defenum, TypeSystem* ts) { // default enum type will be int32. - goalenum.base_type = ts->make_typespec("int32"); - goalenum.is_bitfield = false; + TypeSpec base_type = ts->make_typespec("int32"); + bool is_bitfield = false; + std::unordered_map entries; auto iter = &defenum; @@ -94,7 +81,7 @@ void parse_defenum(const goos::Object& defenum, TypeSystem* ts, GoalEnum& goalen if (!enum_name_obj.is_symbol()) { throw std::runtime_error("defenum must be given a symbol as its name"); } - goalenum.name = enum_name_obj.as_symbol()->name; + std::string name = enum_name_obj.as_symbol()->name; auto current = car(iter); while (current.is_symbol() && symbol_string(current).at(0) == ':') { @@ -105,12 +92,12 @@ void parse_defenum(const goos::Object& defenum, TypeSystem* ts, GoalEnum& goalen current = car(iter); if (option_name == ":type") { - goalenum.base_type = parse_typespec(ts, option_value); + base_type = parse_typespec(ts, option_value); } else if (option_name == ":bitfield") { if (symbol_string(option_value) == "#t") { - goalenum.is_bitfield = true; + is_bitfield = true; } else if (symbol_string(option_value) == "#f") { - goalenum.is_bitfield = false; + is_bitfield = false; } else { fmt::print("Invalid option {} to :bitfield option.\n", option_value.print()); throw std::runtime_error("invalid bitfield option"); @@ -121,10 +108,10 @@ void parse_defenum(const goos::Object& defenum, TypeSystem* ts, GoalEnum& goalen } } - auto type = ts->lookup_type(goalenum.base_type); + auto type = ts->lookup_type(base_type); while (!iter->is_empty_list()) { auto field = car(iter); - auto name = symbol_string(car(&field)); + auto entry_name = symbol_string(car(&field)); auto rest = cdr(&field); auto& value = car(rest); if (!value.is_int()) { @@ -138,17 +125,20 @@ void parse_defenum(const goos::Object& defenum, TypeSystem* ts, GoalEnum& goalen rest = cdr(rest); if (!rest->is_empty_list()) { - fmt::print("Got too many items in defenum {} entry {}\n", goalenum.name, name); + fmt::print("Got too many items in defenum {} entry {}\n", name, entry_name); } - goalenum.entries[name] = entry_val; + entries[entry_name] = entry_val; iter = cdr(iter); } - if (is_type("integer", goalenum.base_type, ts)) { - ts->add_enum_type(goalenum.name, goalenum.base_type.base_type()); + if (is_type("integer", base_type, ts)) { + auto parent = ts->get_type_of_type(base_type.base_type()); + auto new_type = std::make_unique(parent, name, is_bitfield, entries); + new_type->set_runtime_type(parent->get_runtime_name()); + return dynamic_cast(ts->add_type(name, std::move(new_type))); } else { - throw std::runtime_error("Creating an enum with type " + goalenum.base_type.print() + + throw std::runtime_error("Creating an enum with type " + base_type.print() + " is not allowed or not supported yet."); } } diff --git a/common/type_system/defenum.h b/common/type_system/defenum.h index 4c5d329576..3650ee6d92 100644 --- a/common/type_system/defenum.h +++ b/common/type_system/defenum.h @@ -7,7 +7,6 @@ */ #include "TypeSystem.h" -#include "Enum.h" #include "common/goos/Object.h" -void parse_defenum(const goos::Object& defenum, TypeSystem* ts, GoalEnum& goalenum); +EnumType* parse_defenum(const goos::Object& defenum, TypeSystem* ts); diff --git a/common/type_system/deftype.cpp b/common/type_system/deftype.cpp index 54fda703fc..8df4753d78 100644 --- a/common/type_system/deftype.cpp +++ b/common/type_system/deftype.cpp @@ -4,6 +4,7 @@ * This is used both in the compiler and in the decompiler for the type definition file. */ +#include "common/goos/ParseHelpers.h" #include "deftype.h" #include "third-party/fmt/core.h" @@ -56,20 +57,6 @@ bool is_type(const std::string& expected, const TypeSpec& actual, const TypeSyst return ts->tc(ts->make_typespec(expected), actual); } -template -void for_each_in_list(const goos::Object& list, T f) { - const goos::Object* iter = &list; - while (iter->is_pair()) { - auto lap = iter->as_pair(); - f(lap->car); - iter = &lap->cdr; - } - - if (!iter->is_empty_list()) { - throw std::runtime_error("invalid list in for_each_in_list: " + list.print()); - } -} - std::string symbol_string(const goos::Object& obj) { if (obj.is_symbol()) { return obj.as_symbol()->name; @@ -486,6 +473,7 @@ DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts) { assert(pto); auto new_type = std::make_unique( parent_type_name, name, pto->get_size_in_memory(), pto->get_load_signed()); + new_type->set_runtime_type(pto->get_runtime_name()); auto sr = parse_bitfield_type_def(new_type.get(), ts, field_list_obj, options_obj); result.flags = sr.flags; result.create_runtime_type = sr.generate_runtime_type; diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index c580e65076..56e344df7c 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -216,6 +216,11 @@ Form* cast_form(Form* in, const TypeSpec& new_type, FormPool& pool, const Env& e return cast_to_bitfield(bitfield_info, new_type, pool, env, in); } + auto enum_info = dynamic_cast(type_info); + if (enum_info && enum_info->is_bitfield()) { + return cast_to_bitfield_enum(enum_info, new_type, pool, env, in); + } + return pool.alloc_single_element_form(nullptr, new_type, in); } @@ -843,17 +848,27 @@ void SimpleExpressionElement::update_from_stack_copy_first_int_2(const Env& env, FormStack& stack, std::vector* result, bool allow_side_effects) { + auto arg0_type = env.get_variable_type(m_expr.get_arg(0).var(), true); auto arg0_i = is_int_type(env, m_my_idx, m_expr.get_arg(0).var()); auto arg0_u = is_uint_type(env, m_my_idx, m_expr.get_arg(0).var()); if (!m_expr.get_arg(1).is_var()) { auto args = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects); if (!arg0_i && !arg0_u) { - auto new_form = pool.alloc_element( - GenericOperator::make_fixed(kind), - pool.alloc_single_element_form(nullptr, TypeSpec("int"), args.at(0)), - pool.alloc_single_element_form(nullptr, m_expr.get_arg(1))); - result->push_back(new_form); + auto bti = dynamic_cast(env.dts->ts.lookup_type(arg0_type)); + if (bti) { + auto new_form = pool.alloc_element( + GenericOperator::make_fixed(kind), args.at(0), + cast_form(pool.alloc_single_element_form(nullptr, m_expr.get_arg(1)), + arg0_type, pool, env)); + result->push_back(new_form); + } else { + auto new_form = pool.alloc_element( + GenericOperator::make_fixed(kind), + pool.alloc_single_element_form(nullptr, TypeSpec("int"), args.at(0)), + pool.alloc_single_element_form(nullptr, m_expr.get_arg(1))); + result->push_back(new_form); + } } else { auto new_form = pool.alloc_element( GenericOperator::make_fixed(kind), args.at(0), @@ -993,8 +1008,38 @@ void SimpleExpressionElement::update_from_stack_logor_or_logand(const Env& env, auto new_form = pool.alloc_element(GenericOperator::make_fixed(kind), args.at(0), args.at(1)); result->push_back(new_form); - } else { // types bad, insert cast. + } else { + // this is an ugly hack to make (logand (lognot (enum-bitfield xxxx)) work. + // I have only one example for this, so I think this unlikely to work in all cases. + if (m_expr.get_arg(1).is_var()) { + auto arg1_type = env.get_variable_type(m_expr.get_arg(1).var(), true); + auto eti = env.dts->ts.try_enum_lookup(arg1_type.base_type()); + if (eti) { + auto integer = get_goal_integer_constant(args.at(0), env); + if (integer && ((s64)*integer) < 0) { + // clearing a bitfield. + auto elts = decompile_bitfield_enum_from_int(arg1_type, env.dts->ts, ~*integer); + auto oper = + GenericOperator::make_function(pool.alloc_single_element_form( + nullptr, arg1_type.base_type())); + std::vector form_elts; + for (auto& x : elts) { + form_elts.push_back(pool.alloc_single_element_form(nullptr, x)); + } + auto inverted = + pool.alloc_single_element_form(nullptr, oper, form_elts); + auto normal = pool.alloc_single_element_form( + nullptr, GenericOperator::make_fixed(FixedOperatorKind::LOGNOT), inverted); + auto new_form = pool.alloc_element(GenericOperator::make_fixed(kind), + normal, args.at(1)); + result->push_back(new_form); + // assert(false); + return; + } + } + } + auto cast = pool.alloc_single_element_form( nullptr, TypeSpec(arg0_i ? "int" : "uint"), args.at(1)); auto new_form = diff --git a/decompiler/IR2/bitfields.cpp b/decompiler/IR2/bitfields.cpp index 8e728ed9ce..19f11cd65f 100644 --- a/decompiler/IR2/bitfields.cpp +++ b/decompiler/IR2/bitfields.cpp @@ -380,9 +380,10 @@ std::vector compact_nested_logiors(GenericElement* input, const Env&) { return result; } +} // namespace + /*! * If this could be an integer constant, figure out what the value is. - * TODO move this somewhere more general. */ std::optional get_goal_integer_constant(Form* in, const Env&) { auto as_atom = form_as_atom(in); @@ -405,8 +406,6 @@ std::optional get_goal_integer_constant(Form* in, const Env&) { return {}; } -} // namespace - BitFieldDef BitFieldDef::from_constant(const BitFieldConstantDef& constant, FormPool& pool) { BitFieldDef bfd; bfd.field_name = constant.field_name; @@ -474,4 +473,26 @@ Form* cast_to_bitfield(const BitFieldType* type_info, return pool.alloc_single_element_form(nullptr, typespec, in); } +Form* cast_to_bitfield_enum(const EnumType* type_info, + const TypeSpec& typespec, + FormPool& pool, + const Env& env, + Form* in) { + 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); + } else { + // all failed, just return whatever. + return pool.alloc_single_element_form(nullptr, typespec, in); + } +} + } // namespace decompiler \ No newline at end of file diff --git a/decompiler/IR2/bitfields.h b/decompiler/IR2/bitfields.h index f13c415508..f8331e4f67 100644 --- a/decompiler/IR2/bitfields.h +++ b/decompiler/IR2/bitfields.h @@ -138,4 +138,11 @@ Form* cast_to_bitfield(const BitFieldType* type_info, const Env& env, Form* in); +Form* cast_to_bitfield_enum(const EnumType* type_info, + const TypeSpec& typespec, + FormPool& pool, + const Env& env, + Form* in); + +std::optional get_goal_integer_constant(Form* in, const Env&); } // namespace decompiler diff --git a/decompiler/config/all-types.gc b/decompiler/config/all-types.gc index 8fb6a44e1d..1be1d1c9a5 100644 --- a/decompiler/config/all-types.gc +++ b/decompiler/config/all-types.gc @@ -183,19 +183,48 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; +(defenum process-mask + :bitfield #t :type uint32 + (execute 0) ;; 1 + (draw 1) ;; 2 + (pause 2) ;; 4 + (menu 3) ;; 8 + (progress 4) ;; 16 + (actor-pause 5) ;; 32 + (sleep 6) ;; 64 + (sleep-code 7) ;; 128 + (process-tree 8) ;; 256 not an actual process, just a "tree node" for organization + (heap-shrunk 9) ;; 512 + (going 10) ;; 1024 + (movie 11) ;; 2048 + (movie-subject 12) ;; 4096 + (target 13) ;; 8192 + (sidekick 14) ;; 16384 + (crate 15) ;; 32768 + (collectable 16) ;; 65536 + (enemy 17) ;; 131072 + (camera 18) ;; 262144 + (platform 19) ;; 524288 + (ambient 20) ;; 1048576 + (entity 21) ;; 2097152 + (projectile 22) ;; 4194304 + (attackable 23) ;; 8388608 + (death 24) ;; 16777216 + ) + ;; gkernel-h (deftype kernel-context (basic) - ((prevent-from-run uint32 :offset-assert 4) - (require-for-run uint32 :offset-assert 8) - (allow-to-run uint32 :offset-assert 12) - (next-pid int32 :offset-assert 16) - (fast-stack-top pointer :offset-assert 20) - (current-process basic :offset-assert 24) - (relocating-process basic :offset-assert 28) - (relocating-min int32 :offset-assert 32) - (relocating-max int32 :offset-assert 36) - (relocating-offset int32 :offset-assert 40) - (low-memory-message basic :offset-assert 44) + ((prevent-from-run process-mask :offset-assert 4) + (require-for-run process-mask :offset-assert 8) + (allow-to-run process-mask :offset-assert 12) + (next-pid int32 :offset-assert 16) + (fast-stack-top pointer :offset-assert 20) + (current-process basic :offset-assert 24) + (relocating-process basic :offset-assert 28) + (relocating-min int32 :offset-assert 32) + (relocating-max int32 :offset-assert 36) + (relocating-offset int32 :offset-assert 40) + (low-memory-message basic :offset-assert 44) ) :method-count-assert 9 :size-assert #x30 @@ -258,7 +287,7 @@ ;; gkernel-h (deftype process-tree (basic) ((name basic :offset-assert 4) - (mask uint32 :offset-assert 8) + (mask process-mask :offset-assert 8) (parent (pointer process-tree) :offset-assert 12) (brother (pointer process-tree) :offset-assert 16) (child (pointer process-tree) :offset-assert 20) @@ -2933,6 +2962,7 @@ ) :flag-assert #x900000008 ) + (defenum gs-prim-type :type uint8 (point 0) @@ -2947,14 +2977,14 @@ ;; initializes the contents of the vertex queue. (deftype gs-prim (uint64) ((prim gs-prim-type :offset 0 :size 3) - (iip uint8 :offset 3 :size 1) - (tme uint8 :offset 4 :size 1) - (fge uint8 :offset 5 :size 1) - (abe uint8 :offset 6 :size 1) - (aa1 uint8 :offset 7 :size 1) - (fst uint8 :offset 8 :size 1) - (ctxt uint8 :offset 9 :size 1) - (fix uint8 :offset 10 :size 1) + (iip uint8 :offset 3 :size 1) + (tme uint8 :offset 4 :size 1) + (fge uint8 :offset 5 :size 1) + (abe uint8 :offset 6 :size 1) + (aa1 uint8 :offset 7 :size 1) + (fst uint8 :offset 8 :size 1) + (ctxt uint8 :offset 9 :size 1) + (fix uint8 :offset 10 :size 1) ) :flag-assert #x900000008 ) diff --git a/decompiler/config/jak1_ntsc_black_label.jsonc b/decompiler/config/jak1_ntsc_black_label.jsonc index 199fbd0319..403ab1a98f 100644 --- a/decompiler/config/jak1_ntsc_black_label.jsonc +++ b/decompiler/config/jak1_ntsc_black_label.jsonc @@ -51,7 +51,7 @@ "STR/GRSOBBB.STR","STR/SA3INTRO.STR" ], "str_file_names_":[], - "allowed_objects":["gcommon"], + "allowed_objects":["gstate"], "type_casts_file":"decompiler/config/jak1_ntsc_black_label/type_casts.jsonc", "anonymous_function_types_file":"decompiler/config/jak1_ntsc_black_label/anonymous_function_types.jsonc", diff --git a/decompiler/util/DecompilerTypeSystem.cpp b/decompiler/util/DecompilerTypeSystem.cpp index 6a4f56d4c5..470b7bf1dc 100644 --- a/decompiler/util/DecompilerTypeSystem.cpp +++ b/decompiler/util/DecompilerTypeSystem.cpp @@ -3,7 +3,6 @@ #include "common/type_system/defenum.h" #include "common/type_system/deftype.h" #include "decompiler/Disasm/Register.h" -#include "common/type_system/Enum.h" #include "common/log/log.h" #include "TP_Type.h" @@ -63,7 +62,10 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector& file_ } else if (car(o).as_symbol()->name == "deftype") { auto dtr = parse_deftype(cdr(o), &ts); - add_symbol(dtr.type.base_type(), "type"); + if (dtr.create_runtime_type) { + add_symbol(dtr.type.base_type(), "type"); + } + } else if (car(o).as_symbol()->name == "declare-type") { auto* rest = &cdr(o); auto type_name = car(*rest); @@ -80,9 +82,8 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector& file_ throw std::runtime_error("bad declare-type"); } } else if (car(o).as_symbol()->name == "defenum") { - GoalEnum new_enum; - parse_defenum(cdr(o), &ts, new_enum); - // TODO we do nothing with the enum for now + parse_defenum(cdr(o), &ts); + // so far, enums are never runtime types so there's no symbol for them. } else { throw std::runtime_error("Decompiler cannot parse " + car(o).print()); } diff --git a/decompiler/util/data_decompile.cpp b/decompiler/util/data_decompile.cpp index 5614ab6bf3..9bb1c6f007 100644 --- a/decompiler/util/data_decompile.cpp +++ b/decompiler/util/data_decompile.cpp @@ -1,3 +1,5 @@ +#include + #include "data_decompile.h" #include "third-party/fmt/core.h" #include "common/goos/PrettyPrinter.h" @@ -511,8 +513,6 @@ goos::Object decompile_structure(const TypeSpec& type, std::vector result_def = { pretty_print::to_symbol(fmt::format("new 'static '{}", actual_type.print()))}; - // pretty_print::to_symbol("new"), pretty_print::to_symbol("'static"), - // pretty_print::to_symbol(fmt::format("'{}", actual_type.print()))}; for (auto& f : field_defs_out) { auto str = f.second.print(); if (str.length() < 40) { @@ -529,6 +529,20 @@ goos::Object decompile_structure(const TypeSpec& type, goos::Object decompile_value(const TypeSpec& type, const std::vector& bytes, const TypeSystem& ts) { + auto bitfield_enum = ts.try_enum_lookup(type); + if (bitfield_enum) { + assert((int)bytes.size() == bitfield_enum->get_load_size()); + assert(bytes.size() <= 8); + u64 value = 0; + memcpy(&value, bytes.data(), bytes.size()); + auto defs = decompile_bitfield_enum_from_int(type, ts, value); + std::vector result_def = {pretty_print::to_symbol(type.print())}; + for (auto& x : defs) { + result_def.push_back(pretty_print::to_symbol(x)); + } + return pretty_print::build_list(result_def); + } + // try as common integer types: if (ts.tc(TypeSpec("uint32"), type)) { assert(bytes.size() == 4); @@ -849,4 +863,35 @@ std::vector decompile_bitfield_from_int(const TypeSpec& typ return result; } +std::vector decompile_bitfield_enum_from_int(const TypeSpec& type, + const TypeSystem& ts, + u64 value) { + u64 reconstructed = 0; + std::vector result; + auto type_info = ts.try_enum_lookup(type.base_type()); + assert(type_info); + assert(type_info->is_bitfield()); + + for (auto& field : type_info->entries()) { + u64 mask = ((u64)1) << field.second; + if (value & mask) { + reconstructed |= mask; + result.push_back(field.first); + } + } + + if (reconstructed != value) { + throw std::runtime_error( + fmt::format("Failed to decompile bitfield enum. Original value is 0x{:x} but we could only " + "make 0x{:x} using the available fields.", + value, reconstructed)); + } + + // unordered map will give us these fields in a weird order, let's order them explicitly. + std::sort(result.begin(), result.end(), [&](const std::string& a, const std::string& b) { + return type_info->entries().at(a) < type_info->entries().at(b); + }); + return result; +} + } // namespace decompiler \ No newline at end of file diff --git a/decompiler/util/data_decompile.h b/decompiler/util/data_decompile.h index f3c25657fb..ad2f66cadc 100644 --- a/decompiler/util/data_decompile.h +++ b/decompiler/util/data_decompile.h @@ -76,4 +76,8 @@ std::vector decompile_bitfield_from_int(const TypeSpec& typ const TypeSystem& ts, u64 value); +std::vector decompile_bitfield_enum_from_int(const TypeSpec& type, + const TypeSystem& ts, + u64 value); + } // namespace decompiler diff --git a/goal_src/kernel-defs.gc b/goal_src/kernel-defs.gc index 8f880067fe..fb4afada27 100644 --- a/goal_src/kernel-defs.gc +++ b/goal_src/kernel-defs.gc @@ -169,9 +169,38 @@ ;; - not set up in GOAL code ;; So these deftypes generate no code. +(defenum process-mask + :bitfield #t :type uint32 + (execute 0) ;; 1 + (draw 1) ;; 2 + (pause 2) ;; 4 + (menu 3) ;; 8 + (progress 4) ;; 16 + (actor-pause 5) ;; 32 + (sleep 6) ;; 64 + (sleep-code 7) ;; 128 + (process-tree 8) ;; 256 not an actual process, just a "tree node" for organization + (heap-shrunk 9) ;; 512 + (going 10) ;; 1024 + (movie 11) ;; 2048 + (movie-subject 12) ;; 4096 + (target 13) ;; 8192 + (sidekick 14) ;; 16384 + (crate 15) ;; 32768 + (collectable 16) ;; 65536 + (enemy 17) ;; 131072 + (camera 18) ;; 262144 + (platform 19) ;; 524288 + (ambient 20) ;; 1048576 + (entity 21) ;; 2097152 + (projectile 22) ;; 4194304 + (attackable 23) ;; 8388608 + (death 24) ;; 16777216 + ) + (deftype process-tree (basic) ((name basic :offset-assert 4) - (mask uint32 :offset-assert 8) + (mask process-mask :offset-assert 8) (parent (pointer process-tree) :offset-assert 12) (brother (pointer process-tree) :offset-assert 16) (child (pointer process-tree) :offset-assert 20) @@ -193,6 +222,7 @@ :no-runtime-type ;; already defined by kscheme. Don't do it again. ) + (deftype stack-frame (basic) ((name basic :offset 4) (next stack-frame :offset 8) ;; which way does this point? diff --git a/goal_src/kernel/gkernel-h.gc b/goal_src/kernel/gkernel-h.gc index 68a5578f48..106f74ec17 100644 --- a/goal_src/kernel/gkernel-h.gc +++ b/goal_src/kernel/gkernel-h.gc @@ -56,7 +56,7 @@ ;; bitfield enum to indicate proprties about a process-tree (defenum process-mask - :bitfield #t :type int32 + :bitfield #t :type uint32 (execute 0) ;; 1 (draw 1) ;; 2 (pause 2) ;; 4 @@ -112,17 +112,17 @@ ;; this stores the current state of the kernel. (deftype kernel-context (basic) - ((prevent-from-run uint32 :offset-assert 4) - (require-for-run uint32 :offset-assert 8) - (allow-to-run uint32 :offset-assert 12) - (next-pid int32 :offset-assert 16) - (fast-stack-top pointer :offset-assert 20) - (current-process basic :offset-assert 24) - (relocating-process basic :offset-assert 28) - (relocating-min int32 :offset-assert 32) - (relocating-max int32 :offset-assert 36) - (relocating-offset int32 :offset-assert 40) - (low-memory-message basic :offset-assert 44) + ((prevent-from-run process-mask :offset-assert 4) + (require-for-run process-mask :offset-assert 8) + (allow-to-run process-mask :offset-assert 12) + (next-pid int32 :offset-assert 16) + (fast-stack-top pointer :offset-assert 20) + (current-process basic :offset-assert 24) + (relocating-process basic :offset-assert 28) + (relocating-min int32 :offset-assert 32) + (relocating-max int32 :offset-assert 36) + (relocating-offset int32 :offset-assert 40) + (low-memory-message basic :offset-assert 44) ) :size-assert #x30 @@ -199,7 +199,7 @@ ;; (except GOAL is old and it looks like they called them left-child right-brother trees back then) (deftype process-tree (basic) ((name basic :offset-assert 4) - (mask uint32 :offset-assert 8) + (mask process-mask :offset-assert 8) (parent (pointer process-tree) :offset-assert 12) (brother (pointer process-tree) :offset-assert 16) (child (pointer process-tree) :offset-assert 20) diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 26112efb22..a7cfd79185 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -1,8 +1,5 @@ #pragma once -#ifndef JAK_COMPILER_H -#define JAK_COMPILER_H - #include #include #include "common/type_system/TypeSystem.h" @@ -17,7 +14,6 @@ #include "third-party/fmt/color.h" #include "CompilerException.h" #include "goalc/compiler/SymbolInfo.h" -#include "common/type_system/Enum.h" #include "common/goos/ReplUtils.h" enum MathMode { MATH_INT, MATH_BINT, MATH_FLOAT, MATH_INVALID }; @@ -216,7 +212,6 @@ class Compiler { Debugger m_debugger; goos::Interpreter m_goos; std::unordered_map m_symbol_types; - std::unordered_map m_enums; std::unordered_map, goos::Object> m_global_constants; std::unordered_map, LambdaVal*> m_inlineable_functions; CompilerSettings m_settings; @@ -237,12 +232,12 @@ class Compiler { bool is_none(Val* in); emitter::Register parse_register(const goos::Object& code); u64 enum_lookup(const goos::Object& form, - const GoalEnum& e, + const EnumType* e, const goos::Object& rest, bool throw_on_error, bool* success); Val* compile_enum_lookup(const goos::Object& form, - const GoalEnum& e, + const EnumType* e, const goos::Object& rest, Env* env); @@ -547,5 +542,3 @@ extern const std::unordered_map< std::string, Val* (Compiler::*)(const goos::Object& form, const goos::Object& rest, Env* env)> g_goal_forms; - -#endif // JAK_COMPILER_H diff --git a/goalc/compiler/Util.cpp b/goalc/compiler/Util.cpp index 67b2000902..1b5b4a7781 100644 --- a/goalc/compiler/Util.cpp +++ b/goalc/compiler/Util.cpp @@ -205,10 +205,10 @@ bool Compiler::try_getting_constant_integer(const goos::Object& in, int64_t* out auto head = in.as_pair()->car; if (head.is_symbol()) { auto head_sym = head.as_symbol(); - auto enum_kv = m_enums.find(head_sym->name); - if (enum_kv != m_enums.end()) { + auto enum_type = m_ts.try_enum_lookup(head_sym->name); + if (enum_type) { bool success; - u64 as_enum = enum_lookup(in, enum_kv->second, in.as_pair()->cdr, false, &success); + u64 as_enum = enum_lookup(in, enum_type, in.as_pair()->cdr, false, &success); if (success) { *out = as_enum; return true; diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 5f731309be..139eb8a308 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -272,9 +272,10 @@ Val* Compiler::compile_pair(const goos::Object& code, Env* env) { return compile_goos_macro(code, macro_obj, rest, env); } - auto enum_kv = m_enums.find(head_sym->name); - if (enum_kv != m_enums.end()) { - return compile_enum_lookup(code, enum_kv->second, rest, env); + // next try as an enum + auto enum_type = m_ts.try_enum_lookup(head_sym->name); + if (enum_type) { + return compile_enum_lookup(code, enum_type, rest, env); } } diff --git a/goalc/compiler/compilation/Static.cpp b/goalc/compiler/compilation/Static.cpp index 09e5a7d423..10dc5b9e5d 100644 --- a/goalc/compiler/compilation/Static.cpp +++ b/goalc/compiler/compilation/Static.cpp @@ -718,7 +718,8 @@ StaticResult Compiler::fill_static_array(const goos::Object& form, // 8 - 12 allocated length memcpy(obj->data.data() + 8, &length, 4); // 12 - 16 content type - obj->add_type_record(content_type.base_type(), 12); + auto runtime_type = m_ts.lookup_type(content_type.base_type())->get_runtime_name(); + obj->add_type_record(runtime_type, 12); } // now add arguments: diff --git a/goalc/compiler/compilation/Type.cpp b/goalc/compiler/compilation/Type.cpp index c5239563f3..75be7c5751 100644 --- a/goalc/compiler/compilation/Type.cpp +++ b/goalc/compiler/compilation/Type.cpp @@ -2,7 +2,6 @@ #include "third-party/fmt/core.h" #include "common/type_system/defenum.h" #include "common/type_system/deftype.h" -#include "common/type_system/Enum.h" namespace { @@ -995,34 +994,24 @@ Val* Compiler::compile_none(const goos::Object& form, const goos::Object& rest, } Val* Compiler::compile_defenum(const goos::Object& form, const goos::Object& rest, Env* env) { - // format is (defenum name [options] [entries]) (void)form; (void)env; - GoalEnum new_enum; - parse_defenum(rest, &m_ts, new_enum); - - auto existing_kv = m_enums.find(new_enum.name); - if (existing_kv != m_enums.end() && existing_kv->second != new_enum) { - print_compiler_warning("defenum changes the definition of existing enum {}", - new_enum.name.c_str()); - } - m_enums[new_enum.name] = new_enum; - + parse_defenum(rest, &m_ts); return get_none(); } u64 Compiler::enum_lookup(const goos::Object& form, - const GoalEnum& e, + const EnumType* e, const goos::Object& rest, bool throw_on_error, bool* success) { *success = true; - if (e.is_bitfield) { + if (e->is_bitfield()) { uint64_t value = 0; for_each_in_list(rest, [&](const goos::Object& o) { - auto kv = e.entries.find(symbol_string(o)); - if (kv == e.entries.end()) { + auto kv = e->entries().find(symbol_string(o)); + if (kv == e->entries().end()) { if (throw_on_error) { throw_compiler_error(form, "The value {} was not found in enum.", o.print()); } else { @@ -1046,8 +1035,8 @@ u64 Compiler::enum_lookup(const goos::Object& form, return; } } - auto kv = e.entries.find(symbol_string(o)); - if (kv == e.entries.end()) { + auto kv = e->entries().find(symbol_string(o)); + if (kv == e->entries().end()) { if (throw_on_error) { throw_compiler_error(form, "The value {} was not found in enum.", o.print()); } else { @@ -1072,34 +1061,22 @@ u64 Compiler::enum_lookup(const goos::Object& form, } Val* Compiler::compile_enum_lookup(const goos::Object& form, - const GoalEnum& e, + const EnumType* e, const goos::Object& rest, Env* env) { bool success; u64 value = enum_lookup(form, e, rest, true, &success); assert(success); auto result = compile_integer(value, env); - result->set_type(e.base_type); + result->set_type(TypeSpec(e->get_name())); return result; } -bool GoalEnum::operator==(const GoalEnum& other) const { - return base_type == other.base_type && is_bitfield == other.is_bitfield && - entries == other.entries; -} - -bool GoalEnum::operator!=(const GoalEnum& other) const { - return !(*this == other); -} - int Compiler::get_size_for_size_of(const goos::Object& form, const goos::Object& rest) { auto args = get_va(form, rest); va_check(form, args, {goos::ObjectType::SYMBOL}, {}); auto type_to_look_for = args.unnamed.at(0).as_symbol()->name; - if (m_ts.enum_type_exists(type_to_look_for)) { - type_to_look_for = m_ts.get_enum_type_name(type_to_look_for); - } if (!m_ts.fully_defined_type_exists(type_to_look_for)) { throw_compiler_error( diff --git a/test/decompiler/reference/all_forward_declarations.gc b/test/decompiler/reference/all_forward_declarations.gc index bf482f438b..1bf2b7026e 100644 --- a/test/decompiler/reference/all_forward_declarations.gc +++ b/test/decompiler/reference/all_forward_declarations.gc @@ -122,6 +122,13 @@ `(none)) ;; timer-h +(defenum timer-clock-selection + :type uint8 + (busclk 0) + (busclk/16 1) + (busclk/256 2) + (hblank 3) + ) (declare-type dma-buffer basic) ;; display-h diff --git a/test/decompiler/reference/gkernel-h_REF.gc b/test/decompiler/reference/gkernel-h_REF.gc index 7c215a264c..ff274c3a7c 100644 --- a/test/decompiler/reference/gkernel-h_REF.gc +++ b/test/decompiler/reference/gkernel-h_REF.gc @@ -3,17 +3,17 @@ ;; definition of type kernel-context (deftype kernel-context (basic) - ((prevent-from-run uint32 :offset-assert 4) - (require-for-run uint32 :offset-assert 8) - (allow-to-run uint32 :offset-assert 12) - (next-pid int32 :offset-assert 16) - (fast-stack-top pointer :offset-assert 20) - (current-process basic :offset-assert 24) - (relocating-process basic :offset-assert 28) - (relocating-min int32 :offset-assert 32) - (relocating-max int32 :offset-assert 36) - (relocating-offset int32 :offset-assert 40) - (low-memory-message basic :offset-assert 44) + ((prevent-from-run process-mask :offset-assert 4) + (require-for-run process-mask :offset-assert 8) + (allow-to-run process-mask :offset-assert 12) + (next-pid int32 :offset-assert 16) + (fast-stack-top pointer :offset-assert 20) + (current-process basic :offset-assert 24) + (relocating-process basic :offset-assert 28) + (relocating-min int32 :offset-assert 32) + (relocating-max int32 :offset-assert 36) + (relocating-offset int32 :offset-assert 40) + (low-memory-message basic :offset-assert 44) ) :method-count-assert 9 :size-assert #x30 diff --git a/test/decompiler/reference/gkernel_REF.gc b/test/decompiler/reference/gkernel_REF.gc index 7ceb4052da..d3167b3994 100644 --- a/test/decompiler/reference/gkernel_REF.gc +++ b/test/decompiler/reference/gkernel_REF.gc @@ -58,7 +58,7 @@ (define *kernel-context* (new 'static 'kernel-context - :prevent-from-run #x41 + :prevent-from-run (process-mask execute sleep) :next-pid 2 :current-process #f :relocating-process #f @@ -317,7 +317,7 @@ ) ) (set! (-> v0-0 name) arg0) - (set! (-> v0-0 mask) (the-as uint 256)) + (set! (-> v0-0 mask) (process-mask process-tree)) (set! (-> v0-0 parent) (the-as (pointer process-tree) #f)) (set! (-> v0-0 brother) (the-as (pointer process-tree) #f)) (set! (-> v0-0 child) (the-as (pointer process-tree) #f)) @@ -551,7 +551,7 @@ ) ) (set! (-> s3-0 name) arg2) - (set! (-> s3-0 mask) (the-as uint 256)) + (set! (-> s3-0 mask) (process-mask process-tree)) (set! (-> s3-0 parent) (the-as (pointer process-tree) #f)) (set! (-> s3-0 brother) (the-as (pointer process-tree) #f)) (set! (-> s3-0 child) (the-as (pointer process-tree) #f)) @@ -658,7 +658,7 @@ ) ) (set! (-> obj name) arg0) - (set! (-> obj mask) (the-as uint 256)) + (set! (-> obj mask) (process-mask process-tree)) (set! (-> obj allocated-length) arg1) (set! (-> obj parent) (the-as (pointer process-tree) #f)) (set! (-> obj brother) (the-as (pointer process-tree) #f)) @@ -979,7 +979,7 @@ ) (set! (-> s5-1 1 parent) (the-as (pointer process-tree) (-> s5-1 2))) (if (-> s5-1 2) - (set! (-> s5-1 2 mask) (the-as uint (-> s5-1 1))) + (set! (-> s5-1 2 mask) (the-as process-mask (-> s5-1 1))) (set! (-> obj alive-list prev) (the-as dead-pool-heap-rec (-> s5-1 1))) ) (set! (-> s5-1 2) (the-as process-tree (-> obj dead-list next))) @@ -998,7 +998,7 @@ (when (not (or - (nonzero? (logand (-> arg0 mask) 512)) + (nonzero? (logand (-> arg0 mask) (process-mask heap-shrunk))) (and (not (-> arg0 next-state)) (not (-> arg0 state))) ) ) @@ -1011,7 +1011,7 @@ (< (the-as int arg0) (the-as int (gap-location obj (-> obj first-gap)))) (set! (-> obj first-gap) (find-gap obj (the-as dead-pool-heap-rec s5-0))) ) - (set! (-> arg0 mask) (logior (-> arg0 mask) 512)) + (set! (-> arg0 mask) (logior (-> arg0 mask) (process-mask heap-shrunk))) ) (if (= (-> obj first-shrink) s5-0) (set! (-> obj first-shrink) (the-as dead-pool-heap-rec (-> s5-0 2))) @@ -1256,7 +1256,14 @@ (defun iterate-process-tree ((arg0 process-tree) (arg1 (function object object)) (arg2 kernel-context)) - (let ((s4-0 (or (nonzero? (logand (-> arg0 mask) 256)) (arg1 arg0)))) + (let + ((s4-0 + (or + (nonzero? (logand (-> arg0 mask) (process-mask process-tree))) + (arg1 arg0) + ) + ) + ) (cond ((= s4-0 'dead) ) @@ -1282,10 +1289,10 @@ (let ((s3-0 (or - (nonzero? (logand (-> arg0 mask) 256)) + (nonzero? (logand (-> arg0 mask) (process-mask process-tree))) (not (and - (zero? (logand (-> arg2 prevent-from-run) (-> arg0 mask))) + (zero? (logand (-> arg2 prevent-from-run) (the-as uint (-> arg0 mask)))) (run-logic? arg0) ) ) @@ -1316,7 +1323,7 @@ (defun search-process-tree ((arg0 process-tree) (arg1 (function process-tree object))) - (if (zero? (logand (-> arg0 mask) 256)) + (if (zero? (logand (-> arg0 mask) (process-mask process-tree))) (if (arg1 arg0) (return arg0) ) @@ -1361,7 +1368,9 @@ ((or (= v1-0 'waiting-to-run) (= v1-0 'suspended)) (set! (-> s5-0 current-process) a0-0) (cond - ((nonzero? (logand (-> a0-0 mask) 4)) + ((nonzero? + (logand (-> a0-0 mask) (process-mask pause)) + ) (set! *stdcon* *stdcon1*) (set! *debug-draw-pauseable* #t) ) @@ -1399,7 +1408,13 @@ ) ) ) - (if (nonzero? (logand (-> a0-0 mask) 128)) + (if + (nonzero? + (logand + (-> a0-0 mask) + (process-mask sleep-code) + ) + ) (set! (-> a0-0 status) 'suspended) ((-> a0-0 main-thread resume-hook) (-> a0-0 main-thread) @@ -1700,7 +1715,13 @@ activate process ((obj process) (arg0 process-tree) (arg1 basic) (arg2 pointer)) - (set! (-> obj mask) (logand -961 (the-as int (-> arg0 mask)))) + (set! + (-> obj mask) + (logand + (lognot (process-mask sleep sleep-code process-tree heap-shrunk)) + (-> arg0 mask) + ) + ) (set! (-> obj status) 'ready) (let ((v1-4 (-> *kernel-context* next-pid))) (set! (-> obj pid) v1-4) @@ -1721,7 +1742,7 @@ (set! (-> obj state) #f) (set! (-> obj next-state) #f) (cond - ((nonzero? (logand (-> arg0 mask) 256)) + ((nonzero? (logand (-> arg0 mask) (process-mask process-tree))) (set! (-> obj entity) #f) ) (else @@ -1952,7 +1973,7 @@ (let ((gp-2 change-parent) (a0-57 (new 'global 'process-tree 'camera-pool)) ) - (set! (-> a0-57 mask) (the-as uint #x4011c)) + (set! (-> a0-57 mask) (process-mask pause menu progress process-tree camera)) (set! *camera-pool* a0-57) (gp-2 a0-57 *active-pool*) ) @@ -1961,7 +1982,7 @@ (let ((gp-3 change-parent) (a0-59 (new 'global 'process-tree 'target-pool)) ) - (set! (-> a0-59 mask) (the-as uint 284)) + (set! (-> a0-59 mask) (process-mask pause menu progress process-tree)) (set! *target-pool* a0-59) (gp-3 a0-59 *active-pool*) ) @@ -1970,7 +1991,7 @@ (let ((gp-4 change-parent) (a0-61 (new 'global 'process-tree 'entity-pool)) ) - (set! (-> a0-61 mask) (the-as uint #x20011c)) + (set! (-> a0-61 mask) (process-mask pause menu progress process-tree entity)) (set! *entity-pool* a0-61) (gp-4 a0-61 *active-pool*) ) @@ -1979,7 +2000,7 @@ (let ((gp-5 change-parent) (a0-63 (new 'global 'process-tree 'default-pool)) ) - (set! (-> a0-63 mask) (the-as uint 284)) + (set! (-> a0-63 mask) (process-mask pause menu progress process-tree)) (set! *default-pool* a0-63) (gp-5 a0-63 *active-pool*) ) diff --git a/test/decompiler/reference/gstate_REF.gc b/test/decompiler/reference/gstate_REF.gc index f159827565..6102722c45 100644 --- a/test/decompiler/reference/gstate_REF.gc +++ b/test/decompiler/reference/gstate_REF.gc @@ -63,8 +63,11 @@ (arg5 object) ) (local-vars (pp process) (s7-0 none) (sp-0 none) (sp-1 int) (ra-0 int)) - (set! (-> pp mask) (logand -193 (the-as int (-> pp mask)))) - (set! (-> pp mask) (logior (-> pp mask) 1024)) + (set! + (-> pp mask) + (logand (lognot (process-mask sleep sleep-code)) (-> pp mask)) + ) + (set! (-> pp mask) (logior (-> pp mask) (process-mask going))) (cond ((= (-> pp status) 'initialize) (set! (-> pp trans-hook) #f) @@ -93,7 +96,7 @@ (set! s0-1 (-> s0-1 next)) ) ) - (set! (-> pp mask) (logand -1025 (the-as int (-> pp mask)))) + (set! (-> pp mask) (logand (lognot (process-mask going)) (-> pp mask))) (let ((s0-2 (-> pp state))) (set! (-> pp event-hook) (-> s0-2 event)) (cond diff --git a/test/decompiler/reference/timer-h_REF.gc b/test/decompiler/reference/timer-h_REF.gc index 6aca9d5a5c..9e88ef07aa 100644 --- a/test/decompiler/reference/timer-h_REF.gc +++ b/test/decompiler/reference/timer-h_REF.gc @@ -3,16 +3,16 @@ ;; definition of type timer-mode (deftype timer-mode (uint32) - ((clks uint8 :offset 0 :size 2) - (gate uint8 :offset 2 :size 1) - (gats uint8 :offset 3 :size 1) - (gatm uint8 :offset 4 :size 2) - (zret uint8 :offset 6 :size 1) - (cue uint8 :offset 7 :size 1) - (cmpe uint8 :offset 8 :size 1) - (ovfe uint8 :offset 9 :size 1) - (equf uint8 :offset 10 :size 1) - (ovff uint8 :offset 11 :size 1) + ((clks timer-clock-selection :offset 0 :size 2) + (gate uint8 :offset 2 :size 1) + (gats uint8 :offset 3 :size 1) + (gatm uint8 :offset 4 :size 2) + (zret uint8 :offset 6 :size 1) + (cue uint8 :offset 7 :size 1) + (cmpe uint8 :offset 8 :size 1) + (ovfe uint8 :offset 9 :size 1) + (equf uint8 :offset 10 :size 1) + (ovff uint8 :offset 11 :size 1) ) :method-count-assert 9 :size-assert #x4 diff --git a/test/decompiler/test_DataParser.cpp b/test/decompiler/test_DataParser.cpp index 0806e85478..1a34191b42 100644 --- a/test/decompiler/test_DataParser.cpp +++ b/test/decompiler/test_DataParser.cpp @@ -20,7 +20,7 @@ class DataDecompTest : public ::testing::Test { static void TearDownTestCase() { dts.reset(); } - void check_forms_equal(const std::string& expected, const std::string& actual) { + void check_forms_equal(const std::string& actual, const std::string& expected) { auto expected_form = pretty_print::get_pretty_printer_reader().read_from_string(expected, false).as_pair()->car; auto actual_form = @@ -356,4 +356,31 @@ TEST_F(DataDecompTest, Bitfield) { auto decomp = decompile_bitfield(typespec, info, parsed.label("L80"), parsed.labels, {parsed.words}, ts); check_forms_equal(decomp.print(), "(new 'static 'rgba :r #x40 :b #x40 :a #x80)"); +} + +TEST_F(DataDecompTest, KernelContext) { + std::string input = + " .type kernel-context\n" + "L345:\n" + " .word 0x41\n" + " .word 0x0\n" + " .word 0x0\n" + " .word 0x2\n" + " .word 0x0\n" + " .symbol #f\n" + " .symbol #f\n" + " .word 0x0\n" + " .word 0x0\n" + " .word 0x0\n" + " .symbol #t\n"; + auto parsed = parse_data(input); + auto decomp = + decompile_at_label_guess_type(parsed.label("L345"), parsed.labels, {parsed.words}, dts->ts); + check_forms_equal(decomp.print(), + "(new 'static 'kernel-context\n" + " :prevent-from-run (process-mask execute sleep)\n" + " :next-pid 2\n" + " :current-process #f\n" + " :relocating-process #f\n" + " :low-memory-message #t)\n"); } \ No newline at end of file diff --git a/test/decompiler/test_FormExpressionBuild2.cpp b/test/decompiler/test_FormExpressionBuild2.cpp index c4d17131b2..f816ebc894 100644 --- a/test/decompiler/test_FormExpressionBuild2.cpp +++ b/test/decompiler/test_FormExpressionBuild2.cpp @@ -307,7 +307,8 @@ TEST_F(FormRegressionTest, IterateProcessTree) { " daddiu sp, sp, 80"; std::string type = "(function process-tree (function object object) kernel-context object)"; std::string expected = - "(let ((s4-0 (or (nonzero? (logand (-> arg0 mask) 256)) (arg1 arg0))))\n" + "(let ((s4-0 (or (nonzero? (logand (-> arg0 mask) (process-mask process-tree))) (arg1 " + "arg0))))\n" " (cond\n" " ((= s4-0 (quote dead))\n" " )\n" diff --git a/test/decompiler/test_gkernel_decomp.cpp b/test/decompiler/test_gkernel_decomp.cpp index a48052e3ce..a6bb328330 100644 --- a/test/decompiler/test_gkernel_decomp.cpp +++ b/test/decompiler/test_gkernel_decomp.cpp @@ -452,7 +452,7 @@ TEST_F(FormRegressionTest, RemoveMethod0ProcessTree) { "(let\n" " ((v0-0 (object-new arg0 arg1 (the-as int (-> arg1 size)))))\n" " (set! (-> v0-0 name) arg2)\n" - " (set! (-> v0-0 mask) (the-as uint 256))\n" + " (set! (-> v0-0 mask) (process-mask process-tree))\n" " (set! (-> v0-0 parent) (the-as (pointer process-tree) #f))\n" " (set! (-> v0-0 brother) (the-as (pointer process-tree) #f))\n" " (set! (-> v0-0 child) (the-as (pointer process-tree) #f))\n" @@ -948,7 +948,7 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPool) { "(let\n" " ((s3-0 (object-new arg0 arg1 (the-as int (-> arg1 size)))))\n" " (set! (-> s3-0 name) arg4)\n" - " (set! (-> s3-0 mask) (the-as uint 256))\n" + " (set! (-> s3-0 mask) (process-mask process-tree))\n" " (set! (-> s3-0 parent) (the-as (pointer process-tree) #f))\n" " (set! (-> s3-0 brother) (the-as (pointer process-tree) #f))\n" " (set! (-> s3-0 child) (the-as (pointer process-tree) #f))\n" @@ -1263,7 +1263,7 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPoolHeap) { " )\n" " )\n" " (set! (-> obj name) arg2)\n" - " (set! (-> obj mask) (the-as uint 256))\n" + " (set! (-> obj mask) (process-mask process-tree))\n" " (set! (-> obj allocated-length) arg3)\n" " (set! (-> obj parent) (the-as (pointer process-tree) #f))\n" " (set! (-> obj brother) (the-as (pointer process-tree) #f))\n" @@ -2241,7 +2241,7 @@ TEST_F(FormRegressionTest, ExprMethod15DeadPoolHeap) { " )\n" " (set! (-> s5-1 1 parent) (the-as (pointer process-tree) (-> s5-1 2)))\n" " (if (-> s5-1 2)\n" - " (set! (-> s5-1 2 mask) (the-as uint (-> s5-1 1)))\n" + " (set! (-> s5-1 2 mask) (the-as process-mask (-> s5-1 1)))\n" " (set! (-> arg0 alive-list prev) (the-as dead-pool-heap-rec (-> s5-1 1)))\n" " )\n" " (set! (-> s5-1 2) (the-as process-tree (-> arg0 dead-list next)))\n" @@ -2352,7 +2352,7 @@ TEST_F(FormRegressionTest, ExprMethod17DeadPoolHeap) { " (when\n" " (not\n" " (or\n" - " (nonzero? (logand (-> arg1 mask) 512))\n" + " (nonzero? (logand (-> arg1 mask) (process-mask heap-shrunk)))\n" " (and (not (-> arg1 next-state)) (not (-> arg1 state)))\n" " )\n" " )\n" @@ -2371,7 +2371,7 @@ TEST_F(FormRegressionTest, ExprMethod17DeadPoolHeap) { " )\n" " (set! (-> arg0 first-gap) (find-gap arg0 (the-as dead-pool-heap-rec s5-0)))\n" " )\n" - " (set! (-> arg1 mask) (logior (-> arg1 mask) 512))\n" + " (set! (-> arg1 mask) (logior (-> arg1 mask) (process-mask heap-shrunk)))\n" " )\n" " (if (= (-> arg0 first-shrink) s5-0)\n" " (set!\n" diff --git a/test/goalc/source_templates/variables/bitfield-enums.gc b/test/goalc/source_templates/variables/bitfield-enums.gc index 7453bb6eb0..e86930bad9 100644 --- a/test/goalc/source_templates/variables/bitfield-enums.gc +++ b/test/goalc/source_templates/variables/bitfield-enums.gc @@ -1,4 +1,4 @@ -(defenum test-bitfield :bitfield #t +(defenum test-bitfield2 :bitfield #t (four 2) (one 0) (two 1) @@ -6,11 +6,11 @@ (deftype type-with-bitfield (basic) ((name basic) - (thing int32) + (thing test-bitfield2) ) ) (let ((obj (new 'global 'type-with-bitfield))) - (set! (-> obj thing) (test-bitfield one four)) + (set! (-> obj thing) (test-bitfield2 one four)) (the uint (-> obj thing)) ) diff --git a/test/goalc/source_templates/variables/integer-enums.gc b/test/goalc/source_templates/variables/integer-enums.gc index 5d4506888c..9b56f68716 100644 --- a/test/goalc/source_templates/variables/integer-enums.gc +++ b/test/goalc/source_templates/variables/integer-enums.gc @@ -7,8 +7,8 @@ (deftype type-with-bitfield2 (basic) ((name basic) - (thing1 int32) - (thing2 int32) + (thing1 test-int-enum) + (thing2 test-int-enum) ) ) diff --git a/test/goalc/source_templates/with_game/test-bitfield-and-enum-types.gc b/test/goalc/source_templates/with_game/test-bitfield-and-enum-types.gc new file mode 100644 index 0000000000..6f735913f1 --- /dev/null +++ b/test/goalc/source_templates/with_game/test-bitfield-and-enum-types.gc @@ -0,0 +1,80 @@ +(defenum test-enum + :bitfield #f + :type uint16 + (one 1) + (two 2) + ) + +(deftype test-bitfield (uint16) + ((f1 uint8 :size 1 :offset 0) + (f2 uint8 :size 7 :offset 1)) + ) + + +(defun print-test-bitfield ((obj test-bitfield)) + (format #t "f1: ~d~%" (-> obj f1)) + (format #t "f2: ~d~%" (-> obj f2)) + obj + ) + +(defun print-int ((obj int)) + (format #t "int: ~d~%" obj) + obj) + +(let ((test-arr (new 'static 'boxed-array test-bitfield 12))) + (format #t "content type: ~A~%" (-> test-arr content-type)) + ) + +(let ((test-arr (new 'static 'boxed-array test-enum 12))) + (format #t "content type: ~A~%" (-> test-arr content-type)) + ) + +(deftype enum-bitfield-test-type (structure) + ((bf0 test-bitfield) + (bf1 test-bitfield) + (en0 test-enum) + (en1 test-enum) + (bfs test-bitfield 12) + (ens test-enum 12)) + ) + +(let ((obj (new 'stack 'enum-bitfield-test-type))) + (format #t "bitfield spacing: ~d~%" (&- (&-> obj bf1) (&-> obj bf0))) + (format #t "enum spacing: ~d~%" (&- (&-> obj en1) (&-> obj en0))) + (format #t "bitfield array spacing: ~d~%" (&- (&-> obj bfs 1) (&-> obj bfs 0))) + (format #t "enum array spacing: ~d~%" (&- (&-> obj ens 1) (&-> obj ens 0))) + ) + +(defun function-enum-test ((x test-enum)) + (let ((obj (new 'stack 'enum-bitfield-test-type))) + (set! (-> obj en0) x) + (set! (-> obj ens 1) x) + ) + (test-enum one) + ) + +(defun function-bitfield-test ((x test-bitfield)) + (let ((obj (new 'stack 'enum-bitfield-test-type))) + (set! (-> obj bf0) x) + (set! (-> obj bfs 1) x) + ) + (new 'static 'test-bitfield :f1 1) + ) + +(let ((obj (new 'stack 'enum-bitfield-test-type)) + (mem (new 'stack 'array 'uint8 12)) + ) + (set! (-> obj bf0) (function-bitfield-test (new 'static 'test-bitfield :f1 1))) + (set! (-> obj bf0) (function-bitfield-test (the test-bitfield 1))) + (set! (-> obj en0) (function-enum-test (test-enum one))) + (set! (-> obj en0) (function-enum-test (the test-enum 1))) + (set! (-> mem 2) 4) + (set! (-> mem 3) 5) + + (set! (-> (the (pointer test-enum) mem)) (test-enum one)) + (set! (-> (the (pointer test-bitfield) mem)) (new 'static 'test-bitfield :f1 1)) + (format #t "~d~%" (+ (-> mem 2) (-> mem 3))) + + ) + +(format #t "sizes: ~d ~d~%" (size-of test-enum) (size-of test-bitfield)) \ No newline at end of file diff --git a/test/goalc/test_with_game.cpp b/test/goalc/test_with_game.cpp index 5c29be4707..4529efe398 100644 --- a/test/goalc/test_with_game.cpp +++ b/test/goalc/test_with_game.cpp @@ -401,6 +401,19 @@ TEST_F(WithGameTests, SizeOf) { "size of stack array is 16\n0\n"}); } +TEST_F(WithGameTests, EnumAndBitfieldTypes) { + runner.run_static_test(env, testCategory, "test-bitfield-and-enum-types.gc", + {"content type: uint16\n" // runtime type is u16 + "content type: uint16\n" + "bitfield spacing: 2\n" // u16 spacing + "enum spacing: 2\n" // u16 spacing + "bitfield array spacing: 2\n" // u16 spacing + "enum array spacing: 2\n" // u16 spacing + "9\n" // 4 + 5 + "sizes: 2 2\n" // size-of should work + "0\n"}); +} + TEST_F(WithGameTests, Trig) { runner.run_static_test(env, testCategory, "test-trig.gc", {"2.0000\n" // 2 deg