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