Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Apr 22, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions common/type_system/Enum.h

This file was deleted.

49 changes: 40 additions & 9 deletions common/type_system/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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<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);
}
23 changes: 20 additions & 3 deletions common/type_system/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
};
61 changes: 11 additions & 50 deletions common/type_system/TypeSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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");
Expand All @@ -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});
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -1152,6 +1102,14 @@ 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;
}

/*!
* Get a path from type to object.
*/
Expand Down Expand Up @@ -1246,6 +1204,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" ||
Expand Down
7 changes: 2 additions & 5 deletions common/type_system/TypeSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -187,6 +183,8 @@ class TypeSystem {
return result;
}

EnumType* try_enum_lookup(const std::string& type_name) 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;
Expand Down Expand Up @@ -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;

Expand Down
48 changes: 19 additions & 29 deletions common/type_system/defenum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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) == ':') {
Expand All @@ -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");
Expand All @@ -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()) {
Expand All @@ -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.");
}
}
3 changes: 1 addition & 2 deletions common/type_system/defenum.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Loading