commit 8e01dffc981c8f1061ba4669b7390bf4623b7a42
parent de6020676a978c9ca1f101408d70e62bcfa09aa6
Author: Drew DeVault <sir@cmpwn.com>
Date: Sun, 20 Dec 2020 12:21:07 -0500
Implement return values properly
Diffstat:
8 files changed, 199 insertions(+), 12 deletions(-)
diff --git a/include/gen.h b/include/gen.h
@@ -1,5 +1,6 @@
#ifndef HAREC_GEN_H
#define HAREC_GEN_H
+#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include "identifier.h"
@@ -25,6 +26,8 @@ const struct qbe_type *qtype_for_type(struct gen_context *ctx,
const struct type *type, bool extended);
// qinstr.c
-enum qbe_instr alignment_to_qbe_alloc(size_t align);
+enum qbe_instr alloc_for_align(size_t align);
+enum qbe_instr store_for_type(enum qbe_stype stype);
+enum qbe_instr load_for_type(enum qbe_stype stype, bool is_signed);
#endif
diff --git a/include/qbe.h b/include/qbe.h
@@ -196,6 +196,9 @@ const char *genl(struct qbe_statement *stmt, uint64_t *id, const char *fmt);
void pushi(struct qbe_func *func, enum qbe_instr instr, const struct qbe_value *out, ...);
const char *pushl(struct qbe_func *func, uint64_t *id, const char *fmt);
+void constw(struct qbe_value *val, uint32_t l);
void constl(struct qbe_value *val, uint64_t l);
+void consts(struct qbe_value *val, float l);
+void constd(struct qbe_value *val, double l);
#endif
diff --git a/include/types.h b/include/types.h
@@ -86,6 +86,7 @@ struct type {
};
const char *type_storage_unparse(enum type_storage storage);
+bool type_is_signed(const struct type *type);
// Built-in type singletons
extern const struct type
diff --git a/src/emit.c b/src/emit.c
@@ -29,16 +29,16 @@ emit_const(struct qbe_value *val, FILE *out)
case Q_BYTE:
case Q_HALF:
case Q_WORD:
- fprintf(out, "%u ", val->wval);
+ fprintf(out, "%u", val->wval);
break;
case Q_LONG:
- fprintf(out, "%lu ", val->lval);
+ fprintf(out, "%lu", val->lval);
break;
case Q_SINGLE:
- fprintf(out, "%f ", val->sval);
+ fprintf(out, "%f", val->sval);
break;
case Q_DOUBLE:
- fprintf(out, "%f ", val->dval);
+ fprintf(out, "%f", val->dval);
break;
case Q__VOID:
case Q__AGGREGATE:
@@ -75,10 +75,11 @@ emit_stmt(struct qbe_statement *stmt, FILE *out)
assert(stmt->out->type->stype != Q__AGGREGATE); // TODO
emit_qtype(stmt->out->type, out);
}
- fprintf(out, "%s", qbe_instr[stmt->instr]);
+ fprintf(out, "%s%s", qbe_instr[stmt->instr],
+ stmt->args ? " " : "");
struct qbe_arguments *arg = stmt->args;
while (arg) {
- fprintf(out, " ");
+ fprintf(out, "%s", arg == stmt->args ? "" : ", ");
emit_value(&arg->value, out);
arg = arg->next;
}
diff --git a/src/gen.c b/src/gen.c
@@ -53,8 +53,40 @@ alloc_temp(struct gen_context *ctx, struct qbe_value *val,
struct qbe_value size;
constl(&size, type->size);
- pushi(ctx->current, alignment_to_qbe_alloc(type->align),
- val, &size, NULL);
+ pushi(ctx->current, alloc_for_align(type->align), val, &size, NULL);
+}
+
+static void
+gen_constant(struct gen_context *ctx,
+ struct qbe_func *body,
+ const struct expression *expr,
+ struct qbe_value *out)
+{
+ const struct qbe_type *qtype = qtype_for_type(ctx, expr->result, false);
+
+ struct qbe_value val = {0};
+ switch (qtype->stype) {
+ case Q_BYTE:
+ case Q_HALF:
+ case Q_WORD:
+ constw(&val, (uint32_t)expr->constant.uval);
+ break;
+ case Q_LONG:
+ constl(&val, (uint64_t)expr->constant.uval);
+ break;
+ case Q_SINGLE:
+ consts(&val, (float)expr->constant.fval);
+ break;
+ case Q_DOUBLE:
+ constd(&val, expr->constant.fval);
+ break;
+ case Q__AGGREGATE:
+ assert(0); // TODO: General-purpose store
+ case Q__VOID:
+ assert(0); // Invariant
+ }
+
+ pushi(ctx->current, store_for_type(qtype->stype), NULL, &val, out, NULL);
}
static void
@@ -63,7 +95,34 @@ gen_expression(struct gen_context *ctx,
const struct expression *expr,
struct qbe_value *out)
{
- assert(0); // TODO
+ switch (expr->type) {
+ case EXPR_ACCESS:
+ case EXPR_ASSERT:
+ case EXPR_ASSIGN:
+ case EXPR_BINARITHM:
+ case EXPR_BINDING_LIST:
+ case EXPR_CALL:
+ case EXPR_CAST:
+ assert(0); // TODO
+ case EXPR_CONSTANT:
+ gen_constant(ctx, body, expr, out);
+ break;
+ case EXPR_CONTROL:
+ case EXPR_FOR:
+ case EXPR_FREE:
+ case EXPR_FUNC:
+ case EXPR_IF:
+ case EXPR_INDEX:
+ case EXPR_LIST:
+ case EXPR_MATCH:
+ case EXPR_MEASURE:
+ case EXPR_SLICE:
+ case EXPR_STRUCT:
+ case EXPR_SWITCH:
+ case EXPR_UNARITHM:
+ case EXPR_WHILE:
+ assert(0); // TODO
+ }
}
static void
@@ -89,8 +148,20 @@ gen_function_decl(struct gen_context *ctx, const struct declaration *decl)
// TODO: Update for void type
struct qbe_value rval;
alloc_temp(ctx, &rval, fntype->func.result, "return.%d");
+
+ // XXX: Does this change if we have an expression list (with an explicit
+ // return statement) here?
+ pushl(&qdef->func, &ctx->id, "body.%d");
gen_expression(ctx, &qdef->func, func->body, &rval);
- pushi(&qdef->func, Q_RET, NULL, &rval, NULL);
+ pushl(&qdef->func, &ctx->id, "end.%d");
+
+ struct qbe_value load;
+ gen_temp(ctx, &load, qdef->func.returns, "return.%d");
+ pushi(&qdef->func,
+ // TODO: Update for aggregate types
+ load_for_type(qdef->func.returns->stype, type_is_signed(fntype->func.result)),
+ &load, &rval, NULL);
+ pushi(&qdef->func, Q_RET, NULL, &load, NULL);
qbe_append_def(ctx->out, qdef);
ctx->current = NULL;
diff --git a/src/qbe.c b/src/qbe.c
@@ -239,9 +239,33 @@ pushl(struct qbe_func *func, uint64_t *id, const char *fmt)
}
void
+constw(struct qbe_value *val, uint32_t w)
+{
+ val->kind = QV_CONST;
+ val->type = &qbe_word;
+ val->wval = w;
+}
+
+void
constl(struct qbe_value *val, uint64_t l)
{
val->kind = QV_CONST;
val->type = &qbe_long;
val->lval = l;
}
+
+void
+consts(struct qbe_value *val, float s)
+{
+ val->kind = QV_CONST;
+ val->type = &qbe_single;
+ val->sval = s;
+}
+
+void
+constd(struct qbe_value *val, double d)
+{
+ val->kind = QV_CONST;
+ val->type = &qbe_double;
+ val->dval = d;
+}
diff --git a/src/qinstr.c b/src/qinstr.c
@@ -1,8 +1,9 @@
+#include <assert.h>
#include "gen.h"
#include "qbe.h"
enum qbe_instr
-alignment_to_qbe_alloc(size_t align)
+alloc_for_align(size_t align)
{
switch (align) {
case 4:
@@ -13,3 +14,49 @@ alignment_to_qbe_alloc(size_t align)
return Q_ALLOC16;
}
}
+
+enum qbe_instr
+store_for_type(enum qbe_stype stype)
+{
+ switch (stype) {
+ case Q_BYTE:
+ return Q_STOREB;
+ case Q_HALF:
+ return Q_STOREH;
+ case Q_WORD:
+ return Q_STOREW;
+ case Q_LONG:
+ return Q_STOREL;
+ case Q_SINGLE:
+ return Q_STORES;
+ case Q_DOUBLE:
+ return Q_STORED;
+ case Q__VOID:
+ case Q__AGGREGATE:
+ assert(0); // Invariant
+ }
+ assert(0);
+}
+
+enum qbe_instr
+load_for_type(enum qbe_stype stype, bool is_signed)
+{
+ switch (stype) {
+ case Q_BYTE:
+ return is_signed ? Q_LOADSB : Q_LOADUB;
+ case Q_HALF:
+ return is_signed ? Q_LOADSH : Q_LOADUH;
+ case Q_WORD:
+ return is_signed ? Q_LOADSW : Q_LOADUW;
+ case Q_LONG:
+ return Q_LOADL;
+ case Q_SINGLE:
+ return Q_LOADS;
+ case Q_DOUBLE:
+ return Q_LOADD;
+ case Q__VOID:
+ case Q__AGGREGATE:
+ assert(0); // Invariant
+ }
+ assert(0);
+}
diff --git a/src/types.c b/src/types.c
@@ -64,6 +64,43 @@ type_storage_unparse(enum type_storage storage)
assert(0);
}
+bool
+type_is_signed(const struct type *type)
+{
+ switch (type->storage) {
+ case TYPE_STORAGE_VOID:
+ case TYPE_STORAGE_ALIAS:
+ case TYPE_STORAGE_ARRAY:
+ case TYPE_STORAGE_FUNCTION:
+ case TYPE_STORAGE_POINTER:
+ case TYPE_STORAGE_SLICE:
+ case TYPE_STORAGE_STRING:
+ case TYPE_STORAGE_STRUCT:
+ case TYPE_STORAGE_TAGGED_UNION:
+ case TYPE_STORAGE_UNION:
+ case TYPE_STORAGE_BOOL:
+ case TYPE_STORAGE_CHAR:
+ case TYPE_STORAGE_RUNE:
+ case TYPE_STORAGE_SIZE:
+ case TYPE_STORAGE_U8:
+ case TYPE_STORAGE_U16:
+ case TYPE_STORAGE_U32:
+ case TYPE_STORAGE_U64:
+ case TYPE_STORAGE_UINT:
+ case TYPE_STORAGE_UINTPTR:
+ return false;
+ case TYPE_STORAGE_I8:
+ case TYPE_STORAGE_I16:
+ case TYPE_STORAGE_I32:
+ case TYPE_STORAGE_I64:
+ case TYPE_STORAGE_INT:
+ case TYPE_STORAGE_F32:
+ case TYPE_STORAGE_F64:
+ return true;
+ }
+ assert(0); // Unreachable
+}
+
// Built-in type singletons
const struct type builtin_type_bool = {
.storage = TYPE_STORAGE_BOOL,