harec

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

commit 8e01dffc981c8f1061ba4669b7390bf4623b7a42
parent de6020676a978c9ca1f101408d70e62bcfa09aa6
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sun, 20 Dec 2020 12:21:07 -0500

Implement return values properly

Diffstat:
Minclude/gen.h | 5++++-
Minclude/qbe.h | 3+++
Minclude/types.h | 1+
Msrc/emit.c | 13+++++++------
Msrc/gen.c | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/qbe.c | 24++++++++++++++++++++++++
Msrc/qinstr.c | 49++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/types.c | 37+++++++++++++++++++++++++++++++++++++
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,