harec

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

commit e182a979899e7b964064c4e0c51029a616c91d32
parent 757e74c31678cc1ed88e9e0249113d2bef312c60
Author: Drew DeVault <sir@cmpwn.com>
Date:   Thu,  1 Jul 2021 14:32:05 -0400

gen: implement struct constants

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

Diffstat:
Minclude/qbe.h | 5+----
Msrc/emit.c | 19++++++++++---------
Msrc/gen.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/qbe.c | 44--------------------------------------------
Msrc/qtype.c | 103++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 174 insertions(+), 64 deletions(-)

diff --git a/include/qbe.h b/include/qbe.h @@ -27,10 +27,9 @@ struct qbe_field { struct qbe_type { enum qbe_stype stype; size_t size; + // Aggregate types only: char *name; - size_t align; - bool is_union, is_signed; struct qbe_field fields; const struct type *base; }; @@ -46,8 +45,6 @@ extern const struct qbe_type qbe_void, qbe_aggregate; -const struct qbe_type *qtype_for_xtype(enum qbe_stype type, bool is_signed); - enum qbe_value_kind { QV_CONST, QV_GLOBAL, diff --git a/src/emit.c b/src/emit.c @@ -48,20 +48,21 @@ static void 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 [id: %u]\n", tn, def->type.base->id); - free(tn); - } + assert(def->type.base); + const struct type *base = def->type.base; + char *tn = gen_typename(def->type.base); + fprintf(out, "# %s [id: %u]\n", tn, def->type.base->id); + free(tn); fprintf(out, "type :%s =", def->name); - if (def->type.align != (size_t)-1) { - fprintf(out, " align %zu", def->type.align); + if (base->align != (size_t)-1) { + fprintf(out, " align %zu", base->align); } fprintf(out, " {"); + bool is_union = type_dealias(base)->storage == STORAGE_UNION; const struct qbe_field *field = &def->type.fields; while (field) { - if (def->type.is_union) { + if (is_union) { fprintf(out, " {"); } if (field->type) { @@ -71,7 +72,7 @@ qemit_type(const struct qbe_def *def, FILE *out) if (field->count) { fprintf(out, " %zu", field->count); } - if (def->type.is_union) { + if (is_union) { fprintf(out, " }"); } else if (field->next) { fprintf(out, ","); diff --git a/src/gen.c b/src/gen.c @@ -25,17 +25,26 @@ qval_temp(struct gen_context *ctx, const struct gen_temp *temp) { out->kind = QV_TEMPORARY; - out->type = qtype_lookup(ctx, temp->type, false); + out->type = qtype_lookup(ctx, temp->type, true); out->name = temp->name; } -// Allocates a temporary of the given type on the stack in this function's -// preamble. +// Initializes a qbe_value as a qbe temporary for the given qbe type. +static void +gen_qtemp(struct gen_context *ctx, struct qbe_value *out, + const struct qbe_type *type, const char *fmt) +{ + out->kind = QV_TEMPORARY; + out->type = type; + out->name = gen_name(ctx, fmt); +} + +// Allocates a temporary of the given type on the stack in the current +// function's preamble. static struct gen_temp * alloc_temp(struct gen_context *ctx, const struct type *type, const char *fmt) { assert(type->size != 0 && type->size != SIZE_UNDEFINED); - struct gen_temp *temp = xcalloc(1, sizeof(struct gen_temp)); temp->type = type; temp->name = gen_name(ctx, fmt); @@ -132,10 +141,8 @@ gen_expr_constant(struct gen_context *ctx, case STORAGE_NULL: case STORAGE_SLICE: case STORAGE_STRING: - case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: - case STORAGE_UNION: assert(0); // TODO case STORAGE_ICONST: case STORAGE_FCONST: @@ -143,6 +150,8 @@ gen_expr_constant(struct gen_context *ctx, case STORAGE_VOID: case STORAGE_ALIAS: case STORAGE_FUNCTION: + case STORAGE_STRUCT: + case STORAGE_UNION: abort(); // Invariant } @@ -181,6 +190,50 @@ gen_expr_return(struct gen_context *ctx, } static void +gen_expr_struct(struct gen_context *ctx, + const struct expression *expr, + struct gen_temp *out) +{ + if (!out) { + pushc(ctx->current, "Useless struct expression dropped"); + return; + } + struct qbe_value base = {0}, ptr = {0}, offs = {0}; + qval_temp(ctx, &base, out); + gen_qtemp(ctx, &ptr, ctx->arch.ptr, "offset.%d"); + + if (expr->_struct.autofill) { + struct qbe_value rtfunc = {0}, size = {0}, zero = {0}; + rtfunc.kind = QV_GLOBAL; + rtfunc.name = strdup("rt.memset"); + rtfunc.type = &qbe_long; + constl(&size, expr->result->size); + constl(&zero, 0); + pushi(ctx->current, NULL, Q_CALL, &rtfunc, + &base, &zero, &size, NULL); + } + + const struct expr_struct_field *field = &expr->_struct.fields; + while (field) { + if (!field->value) { + assert(expr->_struct.autofill); + field = field->next; + continue; + } + + constl(&offs, field->field->offset); + pushi(ctx->current, &ptr, Q_ADD, &base, &offs, NULL); + + struct gen_temp temp = { + .name = ptr.name, + .type = field->field->type, + }; + gen_expr(ctx, field->value, &temp); + field = field->next; + } +} + +static void gen_expr(struct gen_context *ctx, const struct expression *expr, struct gen_temp *out) @@ -222,6 +275,8 @@ gen_expr(struct gen_context *ctx, case EXPR_SLICE: assert(0); // TODO case EXPR_STRUCT: + gen_expr_struct(ctx, expr, out); + break; case EXPR_SWITCH: case EXPR_TUPLE: case EXPR_UNARITHM: diff --git a/src/qbe.c b/src/qbe.c @@ -11,78 +11,34 @@ const struct qbe_type qbe_byte = { .stype = Q_BYTE, .size = 1, - .align = 1, -}, -qbe_byte_s = { - .stype = Q_BYTE, - .size = 1, - .align = 1, - .is_signed = true, }, qbe_half = { .stype = Q_HALF, .size = 2, - .align = 2, -}, -qbe_half_s = { - .stype = Q_HALF, - .size = 2, - .align = 2, - .is_signed = true, }, qbe_word = { .stype = Q_WORD, .size = 4, - .align = 4, }, qbe_long = { .stype = Q_LONG, .size = 8, - .align = 8, }, qbe_single = { .stype = Q_SINGLE, .size = 4, - .align = 4, }, qbe_double = { .stype = Q_DOUBLE, .size = 8, - .align = 8, }, qbe_void = { .stype = Q__VOID, }, -// Used for some types which are unrepresentible in the qbe type system, but -// still representable as values (e.g. functions) qbe_aggregate = { .stype = Q__AGGREGATE, }; -const struct qbe_type * -qtype_for_xtype(enum qbe_stype type, bool is_signed) -{ - switch (type) { - case Q_BYTE: - return is_signed ? &qbe_byte_s : &qbe_byte; - case Q_HALF: - return is_signed ? &qbe_half_s : &qbe_half; - case Q_WORD: - return &qbe_word; - case Q_LONG: - return &qbe_long; - case Q_SINGLE: - return &qbe_single; - case Q_DOUBLE: - return &qbe_double; - case Q__VOID: - return &qbe_void; - case Q__AGGREGATE: - return &qbe_aggregate; - } - assert(0); // Unreachable -} - const char *qbe_instr[Q_LAST_INSTR] = { [Q_ADD] = "add", [Q_ALLOC16] = "alloc16", diff --git a/src/qtype.c b/src/qtype.c @@ -1,9 +1,109 @@ #include <assert.h> #include <stdlib.h> +#include <stdio.h> #include "gen.h" #include "qbe.h" #include "types.h" #include "type_store.h" +#include "util.h" + +static int +sf_compar(const void *_a, const void *_b) +{ + const struct struct_field **a = (const struct struct_field **)_a; + const struct struct_field **b = (const struct struct_field **)_b; + return (int)(*a)->offset - (int)(*b)->offset; +} + +static const struct qbe_type * +aggregate_lookup(struct gen_context *ctx, const struct type *type) +{ + for (struct qbe_def *def = ctx->out->defs; def; def = def->next) { + if (def->kind == Q_TYPE && def->type.base == type) { + return &def->type; + } + } + + int n = snprintf(NULL, 0, "type.%zd", ctx->id); + char *name = xcalloc(1, n + 1); + snprintf(name, n + 1, "type.%zd", ctx->id); + ++ctx->id; + + struct qbe_def *def = xcalloc(1, sizeof(struct qbe_def)); + def->kind = Q_TYPE; + def->name = name; + def->type.stype = Q__AGGREGATE; + def->type.base = type; + def->type.name = name; + + struct qbe_field *field = &def->type.fields; + switch (type->storage) { + case STORAGE_ARRAY: + case STORAGE_STRING: + assert(0); // TODO + case STORAGE_STRUCT: + case STORAGE_UNION: + assert(type->struct_union.c_compat); // TODO + size_t n = 0; + for (struct struct_field *tfield = type->struct_union.fields; + tfield; tfield = tfield->next) { + ++n; + } + struct struct_field **tfields = + xcalloc(sizeof(struct struct_field *), n); + size_t i = 0; + for (struct struct_field *tfield = type->struct_union.fields; + tfield; tfield = tfield->next, ++i) { + tfields[i] = tfield; + } + qsort(tfields, n, sizeof(struct struct_field *), sf_compar); + for (size_t i = 0; i < n; ++i) { + struct struct_field *tfield = tfields[i]; + field->type = qtype_lookup(ctx, tfield->type, true); + field->count = 1; + + if (i + 1 < n) { + field->next = xcalloc(1, sizeof(struct qbe_field)); + field = field->next; + } + } + free(tfields); + break; + case STORAGE_SLICE: + case STORAGE_TAGGED: + case STORAGE_TUPLE: + assert(0); // TODO + case STORAGE_ENUM: + case STORAGE_ALIAS: + case STORAGE_CHAR: + case STORAGE_I8: + case STORAGE_U8: + case STORAGE_I16: + case STORAGE_U16: + case STORAGE_BOOL: + case STORAGE_I32: + case STORAGE_U32: + case STORAGE_RUNE: + case STORAGE_INT: + case STORAGE_UINT: + case STORAGE_I64: + case STORAGE_U64: + case STORAGE_ICONST: + case STORAGE_SIZE: + case STORAGE_UINTPTR: + case STORAGE_POINTER: + case STORAGE_NULL: + case STORAGE_F32: + case STORAGE_F64: + case STORAGE_FCONST: + case STORAGE_VOID: + case STORAGE_FUNCTION: + abort(); // Invariant + } + + qbe_append_def(ctx->out, def); + return &def->type; +} const struct qbe_type *qtype_lookup( struct gen_context *ctx, @@ -49,7 +149,8 @@ const struct qbe_type *qtype_lookup( case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: - assert(0); // TODO + assert(xtype); + return aggregate_lookup(ctx, type); case STORAGE_VOID: case STORAGE_FUNCTION: case STORAGE_NULL: