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:
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()
};