harec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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:
Minclude/types.h | 4++--
Minclude/util.h | 8++++----
Msrc/gen.c | 24++++++++++++------------
Msrc/qtype.c | 6+++---
Msrc/type_store.c | 55++++++++++++++++++++++++++++++++++++++++---------------
Msrc/types.c | 24++++++++++++------------
Msrc/util.c | 20++++++++++----------
Mtests/13-tagged.ha | 23++++++++++++++---------
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); };