harec

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

commit b321f939e42565cc8a1c77753d9aa6bb101a43e6
parent 92289addea6bb2766b7ee652aae93bbe26a1783b
Author: Drew DeVault <sir@cmpwn.com>
Date:   Fri, 11 Mar 2022 16:20:58 +0100

all: implement packed structs

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Minclude/ast.h | 11++++++++---
Minclude/lex.h | 1+
Msrc/check.c | 12++++++------
Msrc/lex.c | 1+
Msrc/parse.c | 16+++++++++++++---
Msrc/type_store.c | 45++++++++++++++++++++++++++-------------------
Mtests/06-structs.ha | 15++++++++++++++-
7 files changed, 69 insertions(+), 32 deletions(-)

diff --git a/include/ast.h b/include/ast.h @@ -70,11 +70,16 @@ struct ast_tuple_type { struct ast_tuple_type *next; }; -struct ast_struct_union_type { - struct ast_struct_union_type *next; - struct ast_expression *offset; +struct ast_struct_union_field { char *name; struct ast_type *type; + struct ast_expression *offset; + struct ast_struct_union_field *next; +}; + +struct ast_struct_union_type { + struct ast_struct_union_field fields; + bool packed; }; struct ast_type { diff --git a/include/lex.h b/include/lex.h @@ -10,6 +10,7 @@ enum lexical_token { T_ATTR_INIT, T_ATTR_NORETURN, T_ATTR_OFFSET, + T_ATTR_PACKED, T_ATTR_SYMBOL, T_ATTR_TEST, T_UNDERSCORE, diff --git a/src/check.c b/src/check.c @@ -2289,8 +2289,8 @@ check_expr_struct(struct context *ctx, .storage = STORAGE_STRUCT, .flags = TYPE_CONST, }; - struct ast_struct_union_type *tfield = &satype.struct_union; - struct ast_struct_union_type **tnext = &tfield->next; + struct ast_struct_union_field *tfield = &satype.struct_union.fields; + struct ast_struct_union_field **tnext = &tfield->next; struct expr_struct_field *sexpr, **snext = &expr->_struct.fields; expr->_struct.autofill = aexpr->_struct.autofill; if (stype == NULL && expr->_struct.autofill) { @@ -2319,7 +2319,7 @@ check_expr_struct(struct context *ctx, sexpr->value, ftype); if (afield->next) { *tnext = tfield = xcalloc( - 1, sizeof(struct ast_struct_union_type)); + 1, sizeof(struct ast_struct_union_field)); tnext = &tfield->next; } } else { @@ -2360,7 +2360,7 @@ check_expr_struct(struct context *ctx, } else { expr->result = type_store_lookup_atype(ctx->store, &satype); - tfield = &satype.struct_union; + tfield = &satype.struct_union.fields; sexpr = expr->_struct.fields; while (tfield) { const struct struct_field *field = type_get_field( @@ -2380,8 +2380,8 @@ check_expr_struct(struct context *ctx, sexpr->field = field; sexpr->value = lower_implicit_cast(field->type, sexpr->value); - struct ast_struct_union_type *next = tfield->next; - if (tfield != &satype.struct_union) { + struct ast_struct_union_field *next = tfield->next; + if (tfield != &satype.struct_union.fields) { free(tfield); } tfield = next; diff --git a/src/lex.c b/src/lex.c @@ -18,6 +18,7 @@ static const char *tokens[] = { [T_ATTR_INIT] = "@init", [T_ATTR_NORETURN] = "@noreturn", [T_ATTR_OFFSET] = "@offset", + [T_ATTR_PACKED] = "@packed", [T_ATTR_SYMBOL] = "@symbol", [T_ATTR_TEST] = "@test", [T_UNDERSCORE] = "_", diff --git a/src/parse.c b/src/parse.c @@ -454,7 +454,7 @@ parse_struct_union_type(struct lexer *lexer) { struct token tok = {0}; struct ast_type *type = mktype(&lexer->loc); - struct ast_struct_union_type *next = &type->struct_union; + struct ast_struct_union_field *next = &type->struct_union.fields; switch (lex(lexer, &tok)) { case T_STRUCT: type->storage = STORAGE_STRUCT; @@ -466,7 +466,17 @@ parse_struct_union_type(struct lexer *lexer) synassert(false, &tok, T_STRUCT, T_UNION, T_EOF); break; } - want(lexer, T_LBRACE, NULL); + switch (lex(lexer, &tok)) { + case T_ATTR_PACKED: + type->struct_union.packed = true; + want(lexer, T_LBRACE, NULL); + break; + case T_LBRACE: + break; + default: + synassert(false, &tok, T_LBRACE, T_ATTR_PACKED, T_EOF); + } + while (tok.token != T_RBRACE) { if (lex(lexer, &tok) == T_ATTR_OFFSET) { want(lexer, T_LPAREN, NULL); @@ -521,7 +531,7 @@ parse_struct_union_type(struct lexer *lexer) if (lex(lexer, &tok) != T_RBRACE) { unlex(lexer, &tok); next->next = xcalloc(1, - sizeof(struct ast_struct_union_type)); + sizeof(struct ast_struct_union_field)); next = next->next; } break; diff --git a/src/type_store.c b/src/type_store.c @@ -142,15 +142,16 @@ builtin_for_type(const struct type *type) static struct struct_field * 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, bool *ccompat, bool size_only) + const struct ast_struct_union_field *afield, + bool *ccompat, bool size_only, bool packed) { - while (*fields && (!atype->name || !(*fields)->name || strcmp((*fields)->name, atype->name) < 0)) { + while (*fields && (!afield->name || !(*fields)->name || strcmp((*fields)->name, afield->name) < 0)) { fields = &(*fields)->next; } struct struct_field *field = *fields; - if (field != NULL && atype->name && field->name && strcmp(field->name, atype->name) == 0) { - error(store->check_context, atype->type->loc, - "Duplicate struct/union member '%s'", atype->name); + if (field != NULL && afield->name && field->name && strcmp(field->name, afield->name) == 0) { + error(store->check_context, afield->type->loc, + "Duplicate struct/union member '%s'", afield->name); return NULL; } // XXX: leaks if size_only @@ -158,25 +159,25 @@ struct_insert_field(struct type_store *store, struct struct_field **fields, (*fields)->next = field; field = *fields; - if (atype->name) { - field->name = strdup(atype->name); + if (afield->name) { + field->name = strdup(afield->name); } struct dimensions dim = {0}; if (size_only) { - dim = _type_store_lookup_atype(store, NULL, atype->type); + dim = _type_store_lookup_atype(store, NULL, afield->type); } else { - dim = _type_store_lookup_atype(store, &field->type, atype->type); + dim = _type_store_lookup_atype(store, &field->type, afield->type); } if (dim.size == 0) { - error(store->check_context, atype->type->loc, + error(store->check_context, afield->type->loc, "Struct field size cannot be zero"); return NULL; } - if (atype->offset) { + if (afield->offset) { *ccompat = false; struct expression in, out; - check_expression(store->check_context, atype->offset, &in, NULL); + check_expression(store->check_context, afield->offset, &in, NULL); field->offset = 0; enum eval_result r = eval_expr(store->check_context, &in, &out); if (r != EVAL_OK) { @@ -191,6 +192,9 @@ struct_insert_field(struct type_store *store, struct struct_field **fields, } else { field->offset = (size_t)out.constant.uval; } + } else if (packed) { + size_t offs = *size; + field->offset = offs; } else { size_t offs = *size; if (offs % dim.align) { @@ -272,14 +276,16 @@ shift_fields(struct type_store *store, struct struct_field *parent) 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, bool *ccompat, bool size_only) + const struct ast_struct_union_field *afield, + bool *ccompat, bool size_only, bool packed) { // TODO: fields with size SIZE_UNDEFINED size_t usize = 0; assert(storage == STORAGE_STRUCT || storage == STORAGE_UNION); - while (atype) { + while (afield) { struct struct_field *field = struct_insert_field(store, fields, - storage, size, &usize, align, atype, ccompat, size_only); + storage, size, &usize, align, afield, + ccompat, size_only, packed); if (field == NULL) { return; } @@ -292,7 +298,7 @@ struct_init_from_atype(struct type_store *store, enum type_storage storage, // there for sorting fields. shift_fields(store, field); } - atype = atype->next; + afield = afield->next; } if (storage == STORAGE_UNION) { @@ -816,11 +822,12 @@ type_init_from_atype(struct type_store *store, break; case STORAGE_STRUCT: case STORAGE_UNION: - type->struct_union.c_compat = true; + type->struct_union.c_compat = !atype->struct_union.packed; struct_init_from_atype(store, type->storage, &type->size, &type->align, &type->struct_union.fields, - &atype->struct_union, &type->struct_union.c_compat, - size_only); + &atype->struct_union.fields, + &type->struct_union.c_compat, + size_only, atype->struct_union.packed); if (!type->struct_union.c_compat) { // Recompute size type->size = 0; diff --git a/tests/06-structs.ha b/tests/06-structs.ha @@ -225,6 +225,19 @@ fn fields() void = { assert(&sp.c: uintptr == &n: uintptr + 2); }; +type packed_struct = struct @packed { + x: u8, + y: u32, + z: u64, +}; + +fn packed() void = { + let p = packed_struct { ... }; + assert(offset(p.x) == 0); + assert(offset(p.y) == 1); + assert(offset(p.z) == 5); +}; + export fn main() void = { padding(); storage(); @@ -235,8 +248,8 @@ export fn main() void = { autofill(); invariants(); fields(); + packed(); // TODO: // - Union tests // - Embedded structs - // - offset() };