[Compiler/Decompiler] Better support for Bitfield and Enum types (#374)

* compiler fixes, a decent amount of decompiler stuff is working too

* more support in decompiler, fix some casts

* decompile static data too
This commit is contained in:
water111 2021-04-22 19:08:58 -04:00 committed by GitHub
parent 26accb8714
commit 060b125324
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 561 additions and 283 deletions

View file

@ -1,16 +0,0 @@
#pragma once
#include <string>
#include <unordered_map>
#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<std::string, s64> entries;
bool operator==(const GoalEnum& other) const;
bool operator!=(const GoalEnum& other) const;
};

View file

@ -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<const BitFieldType*>(&other);
return other.is_equal(*this) && m_fields == p_other->m_fields;
}
int StructureType::get_size_in_memory() const {
return m_size_in_mem;
}
@ -670,3 +661,43 @@ std::string BitFieldType::print() const {
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<const BitFieldType*>(&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<std::string, s64>& 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<const EnumType*>(&other);
return other.is_equal(*this) && (m_entries == p_other->m_entries) &&
(m_is_bitfield == p_other->m_is_bitfield);
}

View file

@ -5,11 +5,11 @@
* Representation of a GOAL type in the type system.
*/
#ifndef JAK_TYPE_H
#define JAK_TYPE_H
#pragma once
#include <string>
#include <cassert>
#include <unordered_map>
#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<BitField> 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<std::string, s64>& entries);
std::string print() const override;
bool operator==(const Type& other) const override;
const std::unordered_map<std::string, s64>& 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<std::string, s64> m_entries;
};

View file

@ -10,7 +10,6 @@
#include <third-party/fmt/core.h>
#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> 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<EnumType*>(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<TypeSpec>& 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" ||

View file

@ -91,7 +91,6 @@ class TypeSystem {
TypeSystem();
Type* add_type(const std::string& name, std::unique_ptr<Type> 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<std::string>& 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<TypeSpec>& types) const;
@ -233,7 +231,6 @@ class TypeSystem {
enum ForwardDeclareKind { TYPE, STRUCTURE, BASIC };
std::unordered_map<std::string, std::unique_ptr<Type>> m_types;
std::unordered_map<std::string, std::string> m_enum_types;
std::unordered_map<std::string, ForwardDeclareKind> m_forward_declared_types;
std::vector<std::unique_ptr<Type>> m_old_types;

View file

@ -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 <typename T>
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<std::string, s64> 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<ValueType>(base_type.base_type());
auto new_type = std::make_unique<EnumType>(parent, name, is_bitfield, entries);
new_type->set_runtime_type(parent->get_runtime_name());
return dynamic_cast<EnumType*>(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.");
}
}

View file

@ -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);

View file

@ -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 <typename T>
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<BitFieldType>(
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;

View file

@ -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<EnumType*>(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<CastElement>(nullptr, new_type, in);
}
@ -843,17 +848,27 @@ void SimpleExpressionElement::update_from_stack_copy_first_int_2(const Env& env,
FormStack& stack,
std::vector<FormElement*>* 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<GenericElement>(
GenericOperator::make_fixed(kind),
pool.alloc_single_element_form<CastElement>(nullptr, TypeSpec("int"), args.at(0)),
pool.alloc_single_element_form<SimpleAtomElement>(nullptr, m_expr.get_arg(1)));
result->push_back(new_form);
auto bti = dynamic_cast<EnumType*>(env.dts->ts.lookup_type(arg0_type));
if (bti) {
auto new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(kind), args.at(0),
cast_form(pool.alloc_single_element_form<SimpleAtomElement>(nullptr, m_expr.get_arg(1)),
arg0_type, pool, env));
result->push_back(new_form);
} else {
auto new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_fixed(kind),
pool.alloc_single_element_form<CastElement>(nullptr, TypeSpec("int"), args.at(0)),
pool.alloc_single_element_form<SimpleAtomElement>(nullptr, m_expr.get_arg(1)));
result->push_back(new_form);
}
} else {
auto new_form = pool.alloc_element<GenericElement>(
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<GenericElement>(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<ConstantTokenElement>(
nullptr, arg1_type.base_type()));
std::vector<Form*> form_elts;
for (auto& x : elts) {
form_elts.push_back(pool.alloc_single_element_form<ConstantTokenElement>(nullptr, x));
}
auto inverted =
pool.alloc_single_element_form<GenericElement>(nullptr, oper, form_elts);
auto normal = pool.alloc_single_element_form<GenericElement>(
nullptr, GenericOperator::make_fixed(FixedOperatorKind::LOGNOT), inverted);
auto new_form = pool.alloc_element<GenericElement>(GenericOperator::make_fixed(kind),
normal, args.at(1));
result->push_back(new_form);
// assert(false);
return;
}
}
}
auto cast = pool.alloc_single_element_form<CastElement>(
nullptr, TypeSpec(arg0_i ? "int" : "uint"), args.at(1));
auto new_form =

View file

@ -380,9 +380,10 @@ std::vector<Form*> 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<u64> get_goal_integer_constant(Form* in, const Env&) {
auto as_atom = form_as_atom(in);
@ -405,8 +406,6 @@ std::optional<u64> 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<CastElement>(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<ConstantTokenElement>(nullptr, type_info->get_name()));
std::vector<Form*> form_elts;
for (auto& x : elts) {
form_elts.push_back(pool.alloc_single_element_form<ConstantTokenElement>(nullptr, x));
}
return pool.alloc_single_element_form<GenericElement>(nullptr, oper, form_elts);
} else {
// all failed, just return whatever.
return pool.alloc_single_element_form<CastElement>(nullptr, typespec, in);
}
}
} // namespace decompiler

View file

@ -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<u64> get_goal_integer_constant(Form* in, const Env&);
} // namespace decompiler

View file

@ -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
)

View file

@ -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",

View file

@ -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<std::string>& 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<std::string>& 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());
}

View file

@ -1,3 +1,5 @@
#include <algorithm>
#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<goos::Object> 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<u8>& 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<goos::Object> 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<BitFieldConstantDef> decompile_bitfield_from_int(const TypeSpec& typ
return result;
}
std::vector<std::string> decompile_bitfield_enum_from_int(const TypeSpec& type,
const TypeSystem& ts,
u64 value) {
u64 reconstructed = 0;
std::vector<std::string> 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

View file

@ -76,4 +76,8 @@ std::vector<BitFieldConstantDef> decompile_bitfield_from_int(const TypeSpec& typ
const TypeSystem& ts,
u64 value);
std::vector<std::string> decompile_bitfield_enum_from_int(const TypeSpec& type,
const TypeSystem& ts,
u64 value);
} // namespace decompiler

View file

@ -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?

View file

@ -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)

View file

@ -1,8 +1,5 @@
#pragma once
#ifndef JAK_COMPILER_H
#define JAK_COMPILER_H
#include <functional>
#include <optional>
#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<std::string, TypeSpec> m_symbol_types;
std::unordered_map<std::string, GoalEnum> m_enums;
std::unordered_map<std::shared_ptr<goos::SymbolObject>, goos::Object> m_global_constants;
std::unordered_map<std::shared_ptr<goos::SymbolObject>, 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

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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:

View file

@ -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(

View file

@ -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

View file

@ -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

View file

@ -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*)
)

View file

@ -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

View file

@ -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

View file

@ -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 =
@ -357,3 +357,30 @@ TEST_F(DataDecompTest, Bitfield) {
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");
}

View file

@ -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"

View file

@ -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"

View file

@ -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))
)

View file

@ -7,8 +7,8 @@
(deftype type-with-bitfield2 (basic)
((name basic)
(thing1 int32)
(thing2 int32)
(thing1 test-int-enum)
(thing2 test-int-enum)
)
)

View file

@ -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))

View file

@ -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