harec

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

commit cfad556ac17877415110e0ae809af7edc71824bc
parent b2f81197761d64a5f53d95fe4b38bfffd67b7591
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sun, 14 Feb 2021 12:37:50 -0500

all: implement explicit struct offsets

Diffstat:
Minclude/ast.h | 1+
Minclude/lex.h | 1+
Msrc/emit.c | 3++-
Msrc/lex.c | 1+
Msrc/parse.c | 8++++++++
Msrc/qtype.c | 7++++++-
Msrc/type_store.c | 52+++++++++++++++++++++++++++++++++++++++++-----------
Msrc/typedef.c | 9+++++----
8 files changed, 65 insertions(+), 17 deletions(-)

diff --git a/include/ast.h b/include/ast.h @@ -79,6 +79,7 @@ enum struct_union_member_type { struct ast_struct_union_type { enum struct_union_member_type member_type; struct ast_struct_union_type *next; + struct ast_expression *offset; union { struct { char *name; diff --git a/include/lex.h b/include/lex.h @@ -9,6 +9,7 @@ enum lexical_token { T_ATTR_FINI, T_ATTR_INIT, T_ATTR_NORETURN, + T_ATTR_OFFSET, T_ATTR_SYMBOL, T_ATTR_TEST, T_UNDERSCORE, diff --git a/src/emit.c b/src/emit.c @@ -5,6 +5,7 @@ #include "emit.h" #include "qbe.h" #include "typedef.h" +#include "types.h" static void emit_qtype(const struct qbe_type *type, bool aggr, FILE *out) @@ -48,7 +49,7 @@ qemit_type(const struct qbe_def *def, FILE *out) assert(def->kind == Q_TYPE); if (def->type.base) { char *tn = gen_typename(def->type.base); - fprintf(out, "# %s\n", tn); + fprintf(out, "# %s [id: %u]\n", tn, def->type.base->id); free(tn); } fprintf(out, "type :%s =", def->name); diff --git a/src/lex.c b/src/lex.c @@ -18,6 +18,7 @@ static const char *tokens[] = { [T_ATTR_FINI] = "@fini", [T_ATTR_INIT] = "@init", [T_ATTR_NORETURN] = "@noreturn", + [T_ATTR_OFFSET] = "@offset", [T_ATTR_SYMBOL] = "@symbol", [T_ATTR_TEST] = "@test", [T_UNDERSCORE] = "_", diff --git a/src/parse.c b/src/parse.c @@ -463,6 +463,14 @@ parse_struct_union_type(struct lexer *lexer) } want(lexer, T_LBRACE, NULL); while (tok.token != T_RBRACE) { + if (lex(lexer, &tok) == T_ATTR_OFFSET) { + want(lexer, T_LPAREN, NULL); + next->offset = parse_simple_expression(lexer); + want(lexer, T_RPAREN, NULL); + } else { + unlex(lexer, &tok); + } + char *name; switch (lex(lexer, &tok)) { case T_NAME: diff --git a/src/qtype.c b/src/qtype.c @@ -184,7 +184,12 @@ lookup_aggregate(struct gen_context *ctx, const struct type *type) break; case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_UNION: - assert(type->struct_union.c_compat); // TODO + if (!type->struct_union.c_compat) { + def->type.align = type->align; + field->type = NULL; + field->count = type->size; + break; + } for (struct struct_field *tfield = type->struct_union.fields; tfield; tfield = tfield->next) { field->type = qtype_for_type(ctx, tfield->type, true); diff --git a/src/type_store.c b/src/type_store.c @@ -106,7 +106,7 @@ builtin_for_type(const struct type *type) static void struct_insert_field(struct type_store *store, struct struct_field **fields, enum type_storage storage, size_t *size, size_t *usize, size_t *align, - const struct ast_struct_union_type *atype) + const struct ast_struct_union_type *atype, bool *ccompat) { assert(atype->member_type == MEMBER_TYPE_FIELD); while (fields && *fields && strcmp((*fields)->name, atype->field.name) < 0) { @@ -126,8 +126,27 @@ struct_insert_field(struct type_store *store, struct struct_field **fields, field->name = strdup(atype->field.name); field->type = type_store_lookup_atype(store, atype->field.type); - *size += *size % field->type->align; - field->offset = *size; + + if (atype->offset) { + *ccompat = false; + assert(storage == TYPE_STORAGE_STRUCT); // TODO: Bubble up + struct expression in, out; + check_expression(store->check_context, atype->offset, &in, NULL); + enum eval_result r = eval_expr(store->check_context, &in, &out); + // TODO: Bubble up + assert(r == EVAL_OK); + assert(type_is_integer(out.result)); + if (type_is_signed(out.result)) { + assert(out.constant.ival >= 0); + } + size_t offs = (size_t)out.constant.uval; + field->offset = offs; + assert(offs % field->type->align == 0); // TODO? + } else { + *size += *size % field->type->align; + field->offset = *size; + } + if (storage == TYPE_STORAGE_STRUCT) { *size += field->type->size; } else { @@ -139,7 +158,7 @@ struct_insert_field(struct type_store *store, struct struct_field **fields, static void struct_init_from_atype(struct type_store *store, enum type_storage storage, size_t *size, size_t *align, struct struct_field **fields, - const struct ast_struct_union_type *atype) + const struct ast_struct_union_type *atype, bool *ccompat) { // TODO: fields with size SIZE_UNDEFINED size_t usize = 0; @@ -149,10 +168,11 @@ struct_init_from_atype(struct type_store *store, enum type_storage storage, switch (atype->member_type) { case MEMBER_TYPE_FIELD: struct_insert_field(store, fields, storage, - size, &usize, align, atype); + size, &usize, align, atype, ccompat); break; case MEMBER_TYPE_EMBEDDED: if (atype->embedded->storage == TYPE_STORAGE_UNION) { + *ccompat = false; // 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 @@ -160,18 +180,18 @@ struct_init_from_atype(struct type_store *store, enum type_storage storage, size_t offs = 0, align_1 = 0; struct_init_from_atype(store, TYPE_STORAGE_UNION, &offs, &align_1, NULL, - &atype->embedded->struct_union); + &atype->embedded->struct_union, ccompat); // 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); + &atype->embedded->struct_union, ccompat); } else { struct_init_from_atype(store, TYPE_STORAGE_STRUCT, &sub, align, fields, - &atype->embedded->struct_union); + &atype->embedded->struct_union, ccompat); } if (storage == TYPE_STORAGE_UNION) { @@ -185,6 +205,7 @@ struct_init_from_atype(struct type_store *store, enum type_storage storage, } atype = atype->next; } + if (storage == TYPE_STORAGE_UNION) { *size = usize; } @@ -513,12 +534,21 @@ type_init_from_atype(struct type_store *store, type->array.length = SIZE_UNDEFINED; break; case TYPE_STORAGE_STRUCT: - type->struct_union.c_compat = true; - // Fallthrough case TYPE_STORAGE_UNION: + type->struct_union.c_compat = true; struct_init_from_atype(store, type->storage, &type->size, &type->align, &type->struct_union.fields, - &atype->struct_union); + &atype->struct_union, &type->struct_union.c_compat); + if (!type->struct_union.c_compat) { + // Recompute size + type->size = 0; + for (struct struct_field *f = type->struct_union.fields; + f; f = f->next) { + if (f->offset + f->type->size > type->size) { + type->size = f->offset + f->type->size; + } + } + } break; case TYPE_STORAGE_TAGGED: tagged_init_from_atype(store, type, atype); diff --git a/src/typedef.c b/src/typedef.c @@ -120,8 +120,6 @@ field_compar(const void *_a, const void *_b) static void emit_struct(const struct type *type, FILE *out) { - // TODO: This can be greatly simplified when we have explicit field - // offsets for structs. size_t n = 0; for (const struct struct_field *f = type->struct_union.fields; f; f = f->next) { @@ -137,12 +135,15 @@ emit_struct(const struct type *type, FILE *out) qsort(fields, n, sizeof(fields[0]), field_compar); - assert(type->struct_union.c_compat); // TODO fprintf(out, "%s { ", type->storage == TYPE_STORAGE_STRUCT ? "struct" : "union"); for (size_t i = 0; i < n; ++i) { const struct struct_field *f = fields[i]; - fprintf(out, "%s: ", f->name); + if (!type->struct_union.c_compat) { + fprintf(out, "@offset(%zd) %s: ", f->offset, f->name); + } else { + fprintf(out, "%s: ", f->name); + } emit_type(f->type, out); fprintf(out, ", "); }