commit 479073a9f953caa4d3abd670215862b6cd2c0a0b
parent 62cbb7d6195369bbe8e2fb95fd47ad44cbf670f8
Author: Drew DeVault <sir@cmpwn.com>
Date: Sun, 17 Jan 2021 11:25:25 -0500
Use uint for tagged union tag value
Also fixes issues with padding and embedded unions which were discovered
while implementing this change.
Diffstat:
8 files changed, 97 insertions(+), 67 deletions(-)
diff --git a/include/types.h b/include/types.h
@@ -113,7 +113,7 @@ enum type_flags {
struct type {
enum type_storage storage;
- uint64_t id;
+ uint32_t id;
unsigned int flags;
size_t size, align;
union {
@@ -137,7 +137,7 @@ bool type_is_integer(const struct type *type);
bool type_is_numeric(const struct type *type);
bool type_is_float(const struct type *type);
-uint64_t type_hash(const struct type *type);
+uint32_t type_hash(const struct type *type);
void builtin_types_init();
diff --git a/include/util.h b/include/util.h
@@ -3,11 +3,11 @@
#include <assert.h>
#include <stdint.h>
-#define FNV1A_INIT 14695981039346656037UL
+#define FNV1A_INIT 2166136261u
-uint64_t fnv1a(uint64_t hash, unsigned char c);
-uint64_t fnv1a_u64(uint64_t hash, uint64_t c);
-uint64_t fnv1a_s(uint64_t hash, const char *str);
+uint32_t fnv1a(uint32_t hash, unsigned char c);
+uint32_t fnv1a_u32(uint32_t hash, uint32_t c);
+uint32_t fnv1a_s(uint32_t hash, const char *str);
void *xcalloc(size_t n, size_t s);
void *xrealloc(void *p, size_t s);
diff --git a/src/gen.c b/src/gen.c
@@ -741,10 +741,10 @@ gen_cast_to_tagged(struct gen_context *ctx,
struct qbe_value tag = {0}, ptr = {0}, offs = {0};
gen_temp(ctx, &ptr, &qbe_long, "ptr.%d");
- constl(&offs, 8);
- constl(&tag, expr->cast.value->result->id);
+ constl(&offs, expr->result->align);
+ constw(&tag, expr->cast.value->result->id);
pushi(ctx->current, &ptr, Q_COPY, out, NULL);
- pushi(ctx->current, NULL, Q_STOREL, &tag, &ptr, NULL);
+ pushi(ctx->current, NULL, Q_STOREW, &tag, &ptr, NULL);
pushi(ctx->current, &ptr, Q_ADD, &ptr, &offs, NULL);
ptr.type = qtype_for_type(ctx, expr->cast.value->result, false);
ptr.indirect = !type_is_aggregate(expr->cast.value->result);
@@ -764,8 +764,7 @@ gen_cast_from_tagged(struct gen_context *ctx,
struct qbe_value ptr = {0}, offs = {0}, temp = {0};
gen_temp(ctx, &ptr, &qbe_long, "tagged.%d");
gen_expression(ctx, expr->cast.value, &ptr);
-
- constl(&offs, 8);
+ constl(&offs, expr->cast.value->result->align);
pushi(ctx->current, &ptr, Q_ADD, &ptr, &offs, NULL);
ptr.type = qtype_for_type(ctx, expr->result, false);
qval_deref(&ptr);
@@ -788,13 +787,13 @@ gen_expr_type_test(struct gen_context *ctx,
const struct type *want = type_dealias(expr->cast.secondary),
*tagged = type_dealias(expr->cast.value->result);
struct qbe_value tag = {0}, in = {0}, id = {0};
- gen_temp(ctx, &tag, &qbe_long, "tag.%d");
+ gen_temp(ctx, &tag, &qbe_word, "tag.%d");
gen_temp(ctx, &in, qtype_for_type(ctx, tagged, false), "cast.in.%d");
qval_address(&in);
gen_expression(ctx, expr->cast.value, &in);
- pushi(ctx->current, &tag, Q_LOADL, &in, NULL);
+ pushi(ctx->current, &tag, Q_LOADUW, &in, NULL);
constl(&id, want->id);
- pushi(ctx->current, out, Q_CEQL, &tag, &id, NULL);
+ pushi(ctx->current, out, Q_CEQW, &tag, &id, NULL);
}
static void
@@ -806,14 +805,14 @@ gen_expr_type_assertion(struct gen_context *ctx,
const struct type *want = type_dealias(expr->cast.secondary),
*tagged = type_dealias(expr->cast.value->result);
struct qbe_value tag = {0}, in = {0}, id = {0}, result = {0};
- gen_temp(ctx, &tag, &qbe_long, "tag.%d");
+ gen_temp(ctx, &tag, &qbe_word, "tag.%d");
gen_temp(ctx, &in, qtype_for_type(ctx, tagged, false), "cast.in.%d");
qval_address(&in);
gen_expression(ctx, expr->cast.value, &in);
- pushi(ctx->current, &tag, Q_LOADL, &in, NULL);
- constl(&id, want->id);
+ pushi(ctx->current, &tag, Q_LOADUW, &in, NULL);
+ constw(&id, want->id);
gen_temp(ctx, &result, &qbe_word, "valid.%d");
- pushi(ctx->current, &result, Q_CEQL, &tag, &id, NULL);
+ pushi(ctx->current, &result, Q_CEQW, &tag, &id, NULL);
struct qbe_statement validl = {0}, invalidl = {0};
struct qbe_value bvalid = {0}, binvalid = {0};
@@ -1041,6 +1040,7 @@ gen_string(struct gen_context *ctx,
struct qbe_value size = {0};
constl(&size, expr->constant.string.len); // XXX: ARCH
+ // TODO: generate string members as declaration
gen_store(ctx, &str, &temp);
constl(&temp, 8); // XXX: ARCH
pushi(ctx->current, &str, Q_ADD, &str, &temp, NULL);
diff --git a/src/qtype.c b/src/qtype.c
@@ -110,7 +110,7 @@ tagged_qtype(struct gen_context *ctx, const struct type *type)
def->type.name = name;
def->type.align = SIZE_UNDEFINED;
def->type.is_union = true;
- def->type.size = type->size - builtin_type_size.size;
+ def->type.size = type->size - builtin_type_uint.size;
struct qbe_field *field = &def->type.fields;
for (const struct type_tagged_union *tu = &type->tagged;
@@ -187,9 +187,9 @@ lookup_aggregate(struct gen_context *ctx, const struct type *type)
break;
case TYPE_STORAGE_TAGGED_UNION:
def->type.align = type->align;
- field->type = &qbe_long; // XXX: ARCH
+ field->type = &qbe_word; // XXX: ARCH
field->count = 1;
- if (type->size != builtin_type_size.size) {
+ if (type->size != builtin_type_uint.size) {
field->next = xcalloc(1, sizeof(struct qbe_field));
field = field->next;
field->type = tagged_qtype(ctx, type);
diff --git a/src/type_store.c b/src/type_store.c
@@ -357,15 +357,20 @@ struct_insert_field(struct type_store *store, struct struct_field **fields,
const struct ast_struct_union_type *atype)
{
assert(atype->member_type == MEMBER_TYPE_FIELD);
- while (*fields && strcmp((*fields)->name, atype->field.name) < 0) {
+ while (fields && *fields && strcmp((*fields)->name, atype->field.name) < 0) {
fields = &(*fields)->next;
}
- struct struct_field *field = *fields;
+ struct struct_field *field, _temp = {0};
+ if (fields != NULL) {
+ field = *fields;
+ assert(field == NULL || strcmp(field->name, atype->field.name) != 0);
+ *fields = xcalloc(1, sizeof(struct struct_field));
+ (*fields)->next = field;
+ field = *fields;
+ } else {
+ field = &_temp;
+ }
// TODO: Bubble this error up
- assert(field == NULL || strcmp(field->name, atype->field.name) != 0);
- *fields = xcalloc(1, sizeof(struct struct_field));
- (*fields)->next = field;
- field = *fields;
field->name = strdup(atype->field.name);
field->type = type_store_lookup_atype(store, atype->field.type);
@@ -391,13 +396,32 @@ struct_init_from_atype(struct type_store *store, enum type_storage storage,
size_t sub = *size;
switch (atype->member_type) {
case MEMBER_TYPE_FIELD:
- struct_insert_field(store, fields, storage, size, &usize,
- align, atype);
+ struct_insert_field(store, fields, storage,
+ size, &usize, align, atype);
break;
case MEMBER_TYPE_EMBEDDED:
- struct_init_from_atype(store, atype->embedded->storage,
- &sub, align, fields,
- &atype->embedded->struct_union);
+ if (atype->embedded->storage == TYPE_STORAGE_UNION) {
+ // We need to set the offset of all union
+ // members to the maximum alignment of the union
+ // members, so first we do a dry run to compute
+ // it:
+ size_t offs = 0, align_1 = 0;
+ struct_init_from_atype(store, TYPE_STORAGE_UNION,
+ &offs, &align_1, NULL,
+ &atype->embedded->struct_union);
+ // Insert padding per the results:
+ *size += *size % align_1;
+ // Then insert the fields for real:
+ sub = *size;
+ struct_init_from_atype(store, TYPE_STORAGE_UNION,
+ &sub, align, fields,
+ &atype->embedded->struct_union);
+ } else {
+ struct_init_from_atype(store, TYPE_STORAGE_STRUCT,
+ &sub, align, fields,
+ &atype->embedded->struct_union);
+ }
+
if (storage == TYPE_STORAGE_UNION) {
usize = sub > usize ? sub : usize;
} else {
@@ -538,9 +562,10 @@ tagged_init_from_atype(struct type_store *store,
next = &tu[i]->next;
}
- type->size += builtin_type_size.size;
- if (builtin_type_size.align > type->align) {
- type->align = builtin_type_size.align;
+ type->size += builtin_type_uint.size % type->align
+ + builtin_type_uint.align;
+ if (type->align < builtin_type_uint.align) {
+ type->align = builtin_type_uint.align;
}
}
@@ -659,7 +684,7 @@ type_store_lookup_type(struct type_store *store, const struct type *type)
return builtin;
}
- uint64_t hash = type_hash(type);
+ uint32_t hash = type_hash(type);
struct type_bucket **next = &store->buckets[hash % TYPE_STORE_BUCKETS],
*bucket = NULL;
diff --git a/src/types.c b/src/types.c
@@ -237,11 +237,11 @@ type_is_signed(const struct type *type)
assert(0); // Unreachable
}
-uint64_t
+uint32_t
type_hash(const struct type *type)
{
// XXX: ARCH
- uint64_t hash = FNV1A_INIT;
+ uint32_t hash = FNV1A_INIT;
hash = fnv1a(hash, type->storage);
hash = fnv1a(hash, type->flags);
switch (type->storage) {
@@ -272,37 +272,37 @@ type_hash(const struct type *type)
hash = fnv1a_s(hash, ident->name);
hash = fnv1a(hash, 0);
}
- hash = fnv1a_u64(hash, type_hash(type->alias.type));
+ hash = fnv1a_u32(hash, type_hash(type->alias.type));
break;
case TYPE_STORAGE_ARRAY:
- hash = fnv1a_u64(hash, type_hash(type->array.members));
- hash = fnv1a_u64(hash, type->array.length);
+ hash = fnv1a_u32(hash, type_hash(type->array.members));
+ hash = fnv1a_u32(hash, type->array.length);
break;
case TYPE_STORAGE_FUNCTION:
- hash = fnv1a_u64(hash, type_hash(type->func.result));
+ hash = fnv1a_u32(hash, type_hash(type->func.result));
hash = fnv1a(hash, type->func.variadism);
hash = fnv1a(hash, type->func.flags);
for (struct type_func_param *param = type->func.params;
param; param = param->next) {
- hash = fnv1a_u64(hash, type_hash(param->type));
+ hash = fnv1a_u32(hash, type_hash(param->type));
}
break;
case TYPE_STORAGE_ENUM:
assert(0); // TODO
case TYPE_STORAGE_POINTER:
hash = fnv1a(hash, type->pointer.flags);
- hash = fnv1a_u64(hash, type_hash(type->pointer.referent));
+ hash = fnv1a_u32(hash, type_hash(type->pointer.referent));
break;
case TYPE_STORAGE_SLICE:
- hash = fnv1a_u64(hash, type_hash(type->array.members));
+ hash = fnv1a_u32(hash, type_hash(type->array.members));
break;
case TYPE_STORAGE_STRUCT:
case TYPE_STORAGE_UNION:
for (const struct struct_field *field = type->struct_union.fields;
field; field = field->next) {
hash = fnv1a_s(hash, field->name);
- hash = fnv1a_u64(hash, type_hash(field->type));
- hash = fnv1a_u64(hash, field->offset);
+ hash = fnv1a_u32(hash, type_hash(field->type));
+ hash = fnv1a_u32(hash, field->offset);
}
break;
case TYPE_STORAGE_TAGGED_UNION:
@@ -310,7 +310,7 @@ type_hash(const struct type *type)
// any other tagged union types, nor any duplicates.
for (const struct type_tagged_union *tu = &type->tagged;
tu; tu = tu->next) {
- hash = fnv1a_u64(hash, type_hash(tu->type));
+ hash = fnv1a_u32(hash, type_hash(tu->type));
}
break;
}
diff --git a/src/util.c b/src/util.c
@@ -3,24 +3,24 @@
// Do not include this header:
//#include "util.h"
-uint64_t
-fnv1a(uint64_t hash, unsigned char c)
+uint32_t
+fnv1a(uint32_t hash, unsigned char c)
{
return (hash ^ c) * 1099511628211;
}
-uint64_t
-fnv1a_u64(uint64_t hash, uint64_t u64)
+uint32_t
+fnv1a_u32(uint32_t hash, uint32_t u32)
{
- hash = fnv1a(hash, (u64) & 0xFF);
- hash = fnv1a(hash, (u64 >> 8) & 0xFF);
- hash = fnv1a(hash, (u64 >> 16) & 0xFF);
- hash = fnv1a(hash, (u64 >> 24) & 0xFF);
+ hash = fnv1a(hash, (u32) & 0xFF);
+ hash = fnv1a(hash, (u32 >> 8) & 0xFF);
+ hash = fnv1a(hash, (u32 >> 16) & 0xFF);
+ hash = fnv1a(hash, (u32 >> 24) & 0xFF);
return hash;
}
-uint64_t
-fnv1a_s(uint64_t hash, const char *str)
+uint32_t
+fnv1a_s(uint32_t hash, const char *str)
{
unsigned char c;
while ((c = *str++)) {
diff --git a/tests/13-tagged.ha b/tests/13-tagged.ha
@@ -1,33 +1,38 @@
fn rt::compile(src: str) int;
fn measurements() void = {
- let x: (u8 | u16 | u32 | u64) = 1337u16;
- assert(size((u8 | u16 | u32 | u64)) == size(u64) + size(size));
+ let x: (u8 | u16 | u32 | u64) = 1337u16; // With padding
const align: size =
- if (size(u64) < size(size)) size(size)
+ if (size(u64) < size(uint)) size(uint)
else size(u64);
- assert(&x: uintptr: size % align == 0z);
+ assert(size((u8 | u16 | u32 | u64)) == align * 2z);
+ assert(&x: uintptr: size % size(uint) == 0z);
+ assert(&x: uintptr: size % size(u64) == 0z);
+
+ let y: (u8 | u16) = 1337u16; // No padding
+ assert(&x: uintptr: size % size(uint) == 0z);
+ assert(&x: uintptr: size % size(u16) == 0z);
};
fn storage() void = {
let x: (u8 | u16 | u32 | u64) = 42u8;
let y = &x: *struct {
- tag: size,
+ tag: uint,
union { _u8: u8, _u16: u16, _u32: u32, _u64: u64 },
};
- assert(y.tag == 605989269682102909z); // u8 type ID
+ assert(y.tag == 1228088861u); // u8 type ID
assert(y._u8 == 42u8);
x = 1337u16;
- assert(y.tag == 593553793169496424z); // u16 type ID
+ assert(y.tag == 1225628936u); // u16 type ID
assert(y._u16 == 1337u16);
x = 0xCAFEBABEu32;
- assert(y.tag == 596423518518559459z); // u32 type ID
+ assert(y.tag == 1226196611u); // u32 type ID
assert(y._u32 == 0xCAFEBABEu32);
x = 0xCAFEBABEDEADBEEFu64;
- assert(y.tag == 595466943402205114z); // u64 type ID
+ assert(y.tag == 1226007386u); // u64 type ID
assert(y._u64 == 0xCAFEBABEDEADBEEFu64);
};