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:
M | include/qbe.h | | | 5 | +---- |
M | src/emit.c | | | 19 | ++++++++++--------- |
M | src/gen.c | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ |
M | src/qbe.c | | | 44 | -------------------------------------------- |
M | src/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: