commit d6104b30f8dd959bde6187ba08ad7a362b9b495b
parent 726eade9e9697bf0e5393ec9323a47d80099521c
Author: Drew DeVault <sir@cmpwn.com>
Date: Mon, 26 Jul 2021 17:40:09 +0200
gen: implement constants
Diffstat:
M | include/gen.h | | | 25 | ++++++++++++++++--------- |
M | src/gen.c | | | 112 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- |
M | src/genutil.c | | | 22 | ++++++++++++++++++++++ |
3 files changed, 144 insertions(+), 15 deletions(-)
diff --git a/include/gen.h b/include/gen.h
@@ -13,15 +13,22 @@ enum fixed_aborts {
ABORT_STATIC_EXCEEDED = 3,
};
-// A gen temporary is a reference to a qbe temporary by name, and the
-// corresponding Hare type. If indirect is true, the qbe temporary is a pointer
-// to the actual storage; otherwise the type will be representable as a qbe
-// primitive.
-struct gen_temp {
- char *name;
+enum gen_value_kind {
+ GV_CONST,
+ GV_GLOBAL,
+ GV_TEMP,
+};
+
+struct gen_value {
+ enum gen_value_kind kind;
const struct type *type;
- bool indirect;
- bool is_global;
+ union {
+ char *name;
+ uint32_t wval;
+ uint64_t lval;
+ float sval;
+ double dval;
+ };
};
struct gen_arch {
@@ -40,7 +47,6 @@ struct gen_context {
struct qbe_func *current;
const struct type *functype;
const char *end;
- struct gen_temp *rval;
};
struct unit;
@@ -51,6 +57,7 @@ void gen(const struct unit *unit,
// genutil.c
char *gen_name(struct gen_context *ctx, const char *fmt);
+struct qbe_value mkqval(struct gen_context *ctx, struct gen_value *value);
// qinstr.c
enum qbe_instr alloc_for_align(size_t align);
diff --git a/src/gen.c b/src/gen.c
@@ -8,6 +8,99 @@
#include "types.h"
#include "util.h"
+static struct gen_value
+gen_expr_const(struct gen_context *ctx, const struct expression *expr)
+{
+ struct gen_value val = {
+ .kind = GV_CONST,
+ .type = expr->result,
+ };
+
+ // Special cases
+ switch (expr->result->storage) {
+ case STORAGE_BOOL:
+ val.wval = expr->constant.bval ? 1 : 0;
+ return val;
+ case STORAGE_VOID:
+ return val;
+ case STORAGE_NULL:
+ val.lval = 0;
+ return val;
+ case STORAGE_ARRAY:
+ assert(0); // TODO
+ case STORAGE_STRING:
+ assert(0); // TODO
+ default:
+ // Moving right along
+ break;
+ }
+
+ const struct qbe_type *qtype = qtype_lookup(ctx, expr->result, false);
+ switch (qtype->stype) {
+ case Q_BYTE:
+ case Q_HALF:
+ case Q_WORD:
+ val.wval = (uint32_t)expr->constant.uval;
+ return val;
+ case Q_LONG:
+ val.lval = expr->constant.uval;
+ return val;
+ case Q_SINGLE:
+ val.sval = (float)expr->constant.fval;
+ return val;
+ case Q_DOUBLE:
+ val.dval = expr->constant.fval;
+ return val;
+ case Q__VOID:
+ return val;
+ case Q__AGGREGATE:
+ assert(0); // Invariant
+ }
+
+ abort(); // Invariant
+}
+
+static struct gen_value
+gen_expr(struct gen_context *ctx, const struct expression *expr)
+{
+ switch (expr->type) {
+ case EXPR_ACCESS:
+ case EXPR_ALLOC:
+ case EXPR_APPEND:
+ case EXPR_ASSERT:
+ case EXPR_ASSIGN:
+ case EXPR_BINARITHM:
+ case EXPR_BINDING:
+ case EXPR_BREAK:
+ case EXPR_CALL:
+ case EXPR_CAST:
+ assert(0); // TODO
+ case EXPR_CONSTANT:
+ return gen_expr_const(ctx, expr);
+ case EXPR_CONTINUE:
+ case EXPR_DEFER:
+ case EXPR_DELETE:
+ case EXPR_FOR:
+ case EXPR_FREE:
+ case EXPR_IF:
+ case EXPR_INSERT:
+ case EXPR_LIST:
+ case EXPR_MATCH:
+ case EXPR_MEASURE:
+ assert(0); // TODO
+ case EXPR_PROPAGATE:
+ assert(0); // Lowered in check (for now?)
+ case EXPR_RETURN:
+ case EXPR_SLICE:
+ case EXPR_STRUCT:
+ case EXPR_SWITCH:
+ case EXPR_TUPLE:
+ case EXPR_UNARITHM:
+ assert(0); // TODO
+ }
+ abort(); // Unreachable
+}
+
static void
gen_function_decl(struct gen_context *ctx, const struct declaration *decl)
{
@@ -32,18 +125,25 @@ gen_function_decl(struct gen_context *ctx, const struct declaration *decl)
ctx->end = genl(&end_label, &ctx->id, "end.%d");
push(&qdef->func.prelude, &start_label);
- assert(type_dealias(fntype->func.result)->storage == STORAGE_VOID);
- // TODO: non-void return type
- qdef->func.returns = &qbe_void;
+ if (type_dealias(fntype->func.result)->storage != STORAGE_VOID) {
+ qdef->func.returns = qtype_lookup(
+ ctx, fntype->func.result, false);
+ } else {
+ qdef->func.returns = &qbe_void;
+ }
assert(!decl->func.scope->objects); // TODO: Parameters
pushl(&qdef->func, &ctx->id, "body.%d");
- // TODO: Generate body
+ struct gen_value ret = gen_expr(ctx, decl->func.body);
push(&qdef->func.body, &end_label);
- // TODO: non-void return value
- pushi(ctx->current, NULL, Q_RET, NULL);
+ if (type_dealias(fntype->func.result)->storage != STORAGE_VOID) {
+ struct qbe_value qret = mkqval(ctx, &ret);
+ pushi(ctx->current, NULL, Q_RET, &qret, NULL);
+ } else {
+ pushi(ctx->current, NULL, Q_RET, NULL);
+ }
qbe_append_def(ctx->out, qdef);
ctx->current = NULL;
diff --git a/src/genutil.c b/src/genutil.c
@@ -13,3 +13,25 @@ gen_name(struct gen_context *ctx, const char *fmt)
++ctx->id;
return str;
}
+
+struct qbe_value
+mkqval(struct gen_context *ctx, struct gen_value *value)
+{
+ struct qbe_value qval = {0};
+ switch (value->kind) {
+ case GV_CONST:
+ qval.kind = QV_CONST;
+ qval.lval = value->lval; // XXX: Kind of hacky
+ break;
+ case GV_GLOBAL:
+ qval.kind = QV_GLOBAL;
+ qval.name = value->name;
+ break;
+ case GV_TEMP:
+ qval.kind = QV_TEMPORARY;
+ qval.name = value->name;
+ break;
+ }
+ qval.type = qtype_lookup(ctx, value->type, true);
+ return qval;
+}