aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-05-21 13:42:33 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-05-21 13:42:33 -0400
commit760f46e71c2a430c5824e79539af4b3537f307f0 (patch)
treed42cf5bc5cad9f7035ae30fe15fef22fa8bb66d1
parentc5c3be9e5d74cda08df2c36412e0a652f1433d4f (diff)
Reduce codegen output for enums without data attached to any tags
-rw-r--r--builtins/functions.c4
-rw-r--r--builtins/types.h5
-rw-r--r--enums.c60
-rw-r--r--structs.c63
4 files changed, 92 insertions, 40 deletions
diff --git a/builtins/functions.c b/builtins/functions.c
index b2808c5a..bed3dbfd 100644
--- a/builtins/functions.c
+++ b/builtins/functions.c
@@ -81,6 +81,7 @@ public uint32_t generic_hash(const void *obj, const TypeInfo *type)
case TextInfo: return Text$hash(obj);
case ArrayInfo: return Array$hash(obj, type);
case TableInfo: return Table$hash(obj, type);
+ case EmptyStruct: return 0;
case CustomInfo:
if (!type->CustomInfo.hash)
goto hash_data;
@@ -101,6 +102,7 @@ public int32_t generic_compare(const void *x, const void *y, const TypeInfo *typ
case TextInfo: return Text$compare(x, y);
case ArrayInfo: return Array$compare(x, y, type);
case TableInfo: return Table$compare(x, y, type);
+ case EmptyStruct: return 0;
case CustomInfo:
if (!type->CustomInfo.compare)
goto compare_data;
@@ -118,6 +120,7 @@ public bool generic_equal(const void *x, const void *y, const TypeInfo *type)
case TextInfo: return Text$equal(x, y);
case ArrayInfo: return Array$equal(x, y, type);
case TableInfo: return Table$equal(x, y, type);
+ case EmptyStruct: return true;
case CustomInfo:
if (!type->CustomInfo.equal)
goto use_generic_compare;
@@ -137,6 +140,7 @@ public CORD generic_as_text(const void *obj, bool colorize, const TypeInfo *type
case ArrayInfo: return Array$as_text(obj, colorize, type);
case TableInfo: return Table$as_text(obj, colorize, type);
case TypeInfoInfo: return Type$as_text(obj, colorize, type);
+ case EmptyStruct: return colorize ? CORD_all("\x1b[0;1m", type->EmptyStruct.name, "\x1b[m()") : CORD_all(type->EmptyStruct.name, "()");
case CustomInfo:
if (!type->CustomInfo.as_text)
fail("No cord function provided for type!\n");
diff --git a/builtins/types.h b/builtins/types.h
index 3b517fd3..ac2166f0 100644
--- a/builtins/types.h
+++ b/builtins/types.h
@@ -18,7 +18,7 @@ typedef CORD (*str_fn_t)(const void*, bool, const struct TypeInfo*);
typedef struct TypeInfo {
int64_t size, align;
struct { // Anonymous tagged union for convenience
- enum { CustomInfo, PointerInfo, TextInfo, ArrayInfo, TableInfo, FunctionInfo, TypeInfoInfo, OpaqueInfo, } tag;
+ enum { CustomInfo, PointerInfo, TextInfo, ArrayInfo, TableInfo, FunctionInfo, TypeInfoInfo, OpaqueInfo, EmptyStruct } tag;
union {
struct {
equal_fn_t equal;
@@ -47,6 +47,9 @@ typedef struct TypeInfo {
const char *type_str;
} TypeInfoInfo;
struct {} OpaqueInfo;
+ struct {
+ const char *name;
+ } EmptyStruct;
};
};
} TypeInfo;
diff --git a/enums.c b/enums.c
index 63df7644..9d33c5c5 100644
--- a/enums.c
+++ b/enums.c
@@ -12,6 +12,14 @@
#include "typecheck.h"
#include "builtins/util.h"
+static bool has_extra_data(tag_ast_t *tags)
+{
+ for (tag_ast_t *tag = tags; tag; tag = tag->next) {
+ if (tag->fields) return true;
+ }
+ return false;
+}
+
static CORD compile_str_method(env_t *env, ast_t *ast)
{
auto def = Match(ast, EnumDef);
@@ -50,6 +58,14 @@ static CORD compile_compare_method(env_t *env, ast_t *ast)
{
auto def = Match(ast, EnumDef);
CORD full_name = CORD_cat(env->file_prefix, def->name);
+ if (!has_extra_data(def->tags)) {
+ // Comparisons are simpler if there is only a tag, no tagged data:
+ return CORD_all("static int ", full_name, "$compare(const ", full_name, "_t *x, const ", full_name,
+ "_t *y, const TypeInfo *info) {\n"
+ "(void)info;\n"
+ "return (x->$tag - y->$tag);\n"
+ "}\n");
+ }
CORD cmp_func = CORD_all("static int ", full_name, "$compare(const ", full_name, "_t *x, const ", full_name,
"_t *y, const TypeInfo *info) {\n"
"(void)info;\n"
@@ -73,6 +89,14 @@ static CORD compile_equals_method(env_t *env, ast_t *ast)
{
auto def = Match(ast, EnumDef);
CORD full_name = CORD_cat(env->file_prefix, def->name);
+ if (!has_extra_data(def->tags)) {
+ // Equality is simpler if there is only a tag, no tagged data:
+ return CORD_all("static bool ", full_name, "$equal(const ", full_name, "_t *x, const ", full_name,
+ "_t *y, const TypeInfo *info) {\n"
+ "(void)info;\n"
+ "return (x->$tag == y->$tag);\n"
+ "}\n");
+ }
CORD eq_func = CORD_all("static bool ", full_name, "$equal(const ", full_name, "_t *x, const ", full_name,
"_t *y, const TypeInfo *info) {\n"
"(void)info;\n"
@@ -95,6 +119,15 @@ static CORD compile_hash_method(env_t *env, ast_t *ast)
{
auto def = Match(ast, EnumDef);
CORD full_name = CORD_cat(env->file_prefix, def->name);
+ if (!has_extra_data(def->tags)) {
+ // Hashing is simpler if there is only a tag, no tagged data:
+ return CORD_all("static uint32_t ", full_name, "$hash(const ", full_name, "_t *obj, const TypeInfo *info) {\n"
+ "(void)info;\n"
+ "uint32_t hash;\n"
+ "halfsiphash(&obj->$tag, sizeof(obj->$tag), TOMO_HASH_VECTOR, (uint8_t*)&hash, sizeof(hash));\n"
+ "return hash;"
+ "\n}\n");
+ }
CORD hash_func = CORD_all("static uint32_t ", full_name, "$hash(const ", full_name, "_t *obj, const TypeInfo *info) {\n"
"(void)info;\n"
"uint32_t hashes[2] = {(uint32_t)obj->$tag, 0};\n"
@@ -167,17 +200,22 @@ void compile_enum_def(env_t *env, ast_t *ast)
CORD typeinfo = CORD_asprintf("public const TypeInfo %s = {%zu, %zu, {.tag=CustomInfo, .CustomInfo={",
full_name, type_size(t), type_align(t));
- env->code->funcs = CORD_all(
- env->code->funcs,
- compile_str_method(env, ast),
- compile_equals_method(env, ast), compile_compare_method(env, ast),
- compile_hash_method(env, ast));
- typeinfo = CORD_all(
- typeinfo,
- ".as_text=(void*)", full_name, "$as_text, "
- ".equal=(void*)", full_name, "$equal, "
- ".hash=(void*)", full_name, "$hash, "
- ".compare=(void*)", full_name, "$compare");
+ env->code->funcs = CORD_all(env->code->funcs, compile_str_method(env, ast));
+ if (has_extra_data(def->tags)) {
+ env->code->funcs = CORD_all(
+ env->code->funcs,
+ compile_equals_method(env, ast), compile_compare_method(env, ast),
+ compile_hash_method(env, ast));
+ typeinfo = CORD_all(
+ typeinfo,
+ ".as_text=(void*)", full_name, "$as_text, "
+ ".equal=(void*)", full_name, "$equal, "
+ ".hash=(void*)", full_name, "$hash, "
+ ".compare=(void*)", full_name, "$compare");
+ } else {
+ // No need for custom methods if there is no tagged data
+ typeinfo = CORD_all(typeinfo, ".as_text=(void*)", full_name, "$as_text");
+ }
typeinfo = CORD_cat(typeinfo, "}}};\n");
env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo);
diff --git a/structs.c b/structs.c
index 48cc4d8a..730d8aec 100644
--- a/structs.c
+++ b/structs.c
@@ -133,38 +133,45 @@ void compile_struct_def(env_t *env, ast_t *ast)
type_t *t = Table$str_get(*env->types, def->name);
assert(t && t->tag == StructType);
auto struct_ = Match(t, StructType);
- CORD typeinfo = CORD_asprintf("public const TypeInfo %r = {%zu, %zu, {.tag=CustomInfo, .CustomInfo={",
- full_name, type_size(t), type_align(t));
+ if (def->fields) {
+ CORD typeinfo = CORD_asprintf("public const TypeInfo %r = {%zu, %zu, {.tag=CustomInfo, .CustomInfo={",
+ full_name, type_size(t), type_align(t));
- typeinfo = CORD_all(typeinfo, ".as_text=(void*)", full_name, "$as_text, ");
- env->code->funcs = CORD_all(env->code->funcs, compile_str_method(env, ast));
+ typeinfo = CORD_all(typeinfo, ".as_text=(void*)", full_name, "$as_text, ");
+ env->code->funcs = CORD_all(env->code->funcs, compile_str_method(env, ast));
- if (struct_->fields && !struct_->fields->next) { // Single member, can just use its methods
- type_t *member_t = struct_->fields->type;
- switch (member_t->tag) {
- case TextType:
- typeinfo = CORD_all(typeinfo, ".hash=(void*)", type_to_cord(member_t), "$hash", ", ");
- // fallthrough
- case IntType: case NumType:
- typeinfo = CORD_all(typeinfo, ".compare=(void*)", type_to_cord(member_t), "$compare, "
- ".equal=(void*)", type_to_cord(member_t), "$equal, ");
- // fallthrough
- case BoolType: goto got_methods;
- default: break;
+ if (struct_->fields && !struct_->fields->next) { // Single member, can just use its methods
+ type_t *member_t = struct_->fields->type;
+ switch (member_t->tag) {
+ case TextType:
+ typeinfo = CORD_all(typeinfo, ".hash=(void*)", type_to_cord(member_t), "$hash", ", ");
+ // fallthrough
+ case IntType: case NumType:
+ typeinfo = CORD_all(typeinfo, ".compare=(void*)", type_to_cord(member_t), "$compare, "
+ ".equal=(void*)", type_to_cord(member_t), "$equal, ");
+ // fallthrough
+ case BoolType: goto got_methods;
+ default: break;
+ }
}
+ if (struct_->fields) {
+ env->code->funcs = CORD_all(env->code->funcs, compile_compare_method(env, ast),
+ compile_equals_method(env, ast), compile_hash_method(env, ast));
+ typeinfo = CORD_all(
+ typeinfo,
+ ".compare=(void*)", full_name, "$compare, "
+ ".equal=(void*)", full_name, "$equal, "
+ ".hash=(void*)", full_name, "$hash");
+ }
+ got_methods:;
+ typeinfo = CORD_cat(typeinfo, "}}};\n");
+ env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo);
+ } else {
+ // If there are no fields, we can use an EmptyStruct typeinfo, which generates less code:
+ CORD typeinfo = CORD_asprintf("public const TypeInfo %r = {%zu, %zu, {.tag=EmptyStruct, .EmptyStruct.name=%r}};\n",
+ full_name, type_size(t), type_align(t), Text$quoted(def->name, false));
+ env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo);
}
- if (struct_->fields) {
- env->code->funcs = CORD_all(env->code->funcs, compile_compare_method(env, ast),
- compile_equals_method(env, ast), compile_hash_method(env, ast));
- typeinfo = CORD_all(
- typeinfo,
- ".compare=(void*)", full_name, "$compare, "
- ".equal=(void*)", full_name, "$equal, "
- ".hash=(void*)", full_name, "$hash");
- }
- got_methods:;
- typeinfo = CORD_cat(typeinfo, "}}};\n");
- env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo);
compile_namespace(env, def->name, def->namespace);
}