harec

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

commit 4e8cc2de3ce1764671fab1c29d64cf1bb686ab34
parent ce4f47284a6be1724af247120b6142f40affc749
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sun, 20 Dec 2020 11:50:05 -0500

gen: implement some basic instruction handling

Diffstat:
Mconfigure | 1+
Minclude/gen.h | 5+++++
Minclude/qbe.h | 14++++++++++----
Msrc/emit.c | 74+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/gen.c | 45++++++++++++++++++++++++++++++++++++++++++++-
Msrc/qbe.c | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Asrc/qinstr.c | 15+++++++++++++++
7 files changed, 221 insertions(+), 10 deletions(-)

diff --git a/configure b/configure @@ -12,6 +12,7 @@ harec() { src/main.c \ src/parse.c \ src/qbe.c \ + src/qinstr.c \ src/qtype.c \ src/trace.c \ src/type_store.c \ diff --git a/include/gen.h b/include/gen.h @@ -12,14 +12,19 @@ void gen(const struct unit *unit, struct qbe_program *out); struct gen_context { struct qbe_program *out; struct identifier *ns; + struct qbe_func *current; uint64_t id; }; struct type; +// qtype.c enum qbe_stype qstype_for_type(const struct type *type); enum qbe_stype qxtype_for_type(const struct type *type); 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); + #endif diff --git a/include/qbe.h b/include/qbe.h @@ -5,6 +5,7 @@ #include <stdint.h> enum qbe_stype { + Q__VOID = 0, Q_BYTE = 'b', Q_HALF = 'h', Q_WORD = 'w', @@ -12,7 +13,6 @@ enum qbe_stype { Q_SINGLE = 's', Q_DOUBLE = 'd', Q__AGGREGATE, - Q__VOID, }; struct qbe_type { @@ -44,7 +44,10 @@ struct qbe_value { const struct qbe_type *type; union { char *name; - // TODO: const storage + uint32_t wval; + uint64_t lval; + float sval; + double dval; }; }; @@ -152,6 +155,7 @@ struct qbe_statement { union { struct { enum qbe_instr instr; + struct qbe_value *out; struct qbe_arguments *args; }; char *label; @@ -191,9 +195,11 @@ void qbe_append_def(struct qbe_program *prog, struct qbe_def *def); // provide to the given instruction. The "l" family takes a printf-compatible // set of values to match with fmt to produce a label (plus the ID postfix, // which is added for you). -void geni(struct qbe_statement *stmt, enum qbe_instr instr, ...); +void geni(struct qbe_statement *stmt, enum qbe_instr instr, const struct qbe_value *out, ...); void genl(struct qbe_statement *stmt, uint64_t *id, const char *fmt, ...); -void pushi(struct qbe_func *func, enum qbe_instr instr, ...); +void pushi(struct qbe_func *func, enum qbe_instr instr, const struct qbe_value *out, ...); void pushl(struct qbe_func *func, uint64_t *id, const char *fmt, ...); +void constl(struct qbe_value *val, uint64_t l); + #endif diff --git a/src/emit.c b/src/emit.c @@ -23,6 +23,73 @@ emit_qtype(const struct qbe_type *type, FILE *out) } static void +emit_const(struct qbe_value *val, FILE *out) +{ + switch (val->type->stype) { + case Q_BYTE: + case Q_HALF: + case Q_WORD: + fprintf(out, "%u ", val->wval); + break; + case Q_LONG: + fprintf(out, "%lu ", val->lval); + break; + case Q_SINGLE: + fprintf(out, "%f ", val->sval); + break; + case Q_DOUBLE: + fprintf(out, "%f ", val->dval); + break; + case Q__VOID: + case Q__AGGREGATE: + assert(0); // Invariant + } +} + +static void +emit_value(struct qbe_value *val, FILE *out) +{ + switch (val->kind) { + case QV_CONST: + emit_const(val, out); + break; + case QV_GLOBAL: + assert(0); // TODO + case QV_LABEL: + assert(0); // TODO + case QV_TEMPORARY: + fprintf(out, "%%%s", val->name); + break; + } +} + +static void +emit_stmt(struct qbe_statement *stmt, FILE *out) +{ + switch (stmt->type) { + case Q_INSTR: + fprintf(out, "\t"); + if (stmt->out != NULL) { + emit_value(stmt->out, out); + fprintf(out, " ="); + assert(stmt->out->type->stype != Q__AGGREGATE); // TODO + emit_qtype(stmt->out->type, out); + } + fprintf(out, "%s", qbe_instr[stmt->instr]); + struct qbe_arguments *arg = stmt->args; + while (arg) { + fprintf(out, " "); + emit_value(&arg->value, out); + arg = arg->next; + } + fprintf(out, "\n"); + break; + case Q_LABEL: + assert(0); // TODO + } +} + +static void emit_func(struct qbe_def *def, FILE *out) { assert(def->type == Q_FUNC); @@ -30,7 +97,12 @@ emit_func(struct qbe_def *def, FILE *out) fprintf(out, "%sfunction ", def->exported ? "export " : ""); emit_qtype(def->func.returns, out); fprintf(out, "$%s() {\n", def->name); // TODO: Parameters - // TODO: Body + + for (size_t i = 0; i < def->func.blen; ++i) { + struct qbe_statement *stmt = &def->func.body[i]; + emit_stmt(stmt, out); + } + fprintf(out, "}\n\n"); } diff --git a/src/gen.c b/src/gen.c @@ -31,6 +31,43 @@ ident_to_sym(const struct identifier *ident) } static void +gen_temp(struct gen_context *ctx, struct qbe_value *val, + const struct qbe_type *type, char *fmt) +{ + val->kind = QV_TEMPORARY; + val->type = type; + + int n = snprintf(NULL, 0, fmt, ctx->id); + char *str = calloc(1, n + 1); + snprintf(str, n + 1, fmt, ctx->id); + ++ctx->id; + + val->name = str; +} + +static void +alloc_temp(struct gen_context *ctx, struct qbe_value *val, + const struct type *type, char *fmt) +{ + const struct qbe_type *qtype = qtype_for_type(ctx, type, false); + gen_temp(ctx, val, qtype, fmt); + + struct qbe_value size; + constl(&size, type->size); + pushi(ctx->current, alignment_to_qbe_alloc(type->align), + val, &size, NULL); +} + +static void +gen_expression(struct gen_context *ctx, + struct qbe_func *body, + const struct expression *expr, + struct qbe_value *out) +{ + //assert(0); // TODO +} + +static void gen_function_decl(struct gen_context *ctx, const struct declaration *decl) { assert(decl->type == DECL_FUNC); @@ -44,12 +81,18 @@ gen_function_decl(struct gen_context *ctx, const struct declaration *decl) qdef->name = func->symbol ? strdup(func->symbol) : ident_to_sym(&decl->ident); qdef->func.returns = qtype_for_type(ctx, fntype->func.result, true); + ctx->current = &qdef->func; assert(fntype->func.params == NULL); // TODO - // TODO: Gen function body + // TODO: Update for void type + struct qbe_value rval; + alloc_temp(ctx, &rval, fntype->func.result, "return.%d"); + gen_expression(ctx, &qdef->func, func->body, &rval); + pushi(&qdef->func, Q_RET, NULL, &rval, NULL); qbe_append_def(ctx->out, qdef); + ctx->current = NULL; } static void diff --git a/src/qbe.c b/src/qbe.c @@ -1,6 +1,7 @@ #include <assert.h> #include <stdarg.h> #include <stdlib.h> +#include <string.h> #include "qbe.h" // Simple type singletons @@ -143,10 +144,46 @@ qbe_append_def(struct qbe_program *prog, struct qbe_def *def) prog->defs = def; } +static struct qbe_value * +qval_dup(const struct qbe_value *val) +{ + struct qbe_value *new = calloc(1, sizeof(struct qbe_value)); + *new = *val; + if (val->kind != QV_CONST) { + new->name = strdup(val->name); + } + return new; +} + +static void +va_geni(struct qbe_statement *stmt, enum qbe_instr instr, + const struct qbe_value *out, va_list ap) +{ + stmt->type = Q_INSTR; + stmt->instr = instr; + + if (out) { + stmt->out = qval_dup(out); + } + + struct qbe_arguments **next = &stmt->args; + struct qbe_value *val; + while ((val = va_arg(ap, struct qbe_value *))) { + struct qbe_arguments *arg = calloc(1, sizeof(struct qbe_arguments)); + arg->value = *val; + *next = arg; + next = &arg->next; + } +} + void -geni(struct qbe_statement *stmt, enum qbe_instr instr, ...) +geni(struct qbe_statement *stmt, enum qbe_instr instr, + const struct qbe_value *out, ...) { - assert(0); // TODO + va_list ap; + va_start(ap, out); + va_geni(stmt, instr, out, ap); + va_end(ap); } void @@ -155,10 +192,34 @@ genl(struct qbe_statement *stmt, uint64_t *id, const char *fmt, ...) assert(0); // TODO } +static void +push(struct qbe_func *func, struct qbe_statement *stmt) +{ + if (!func->body) { + func->bsiz = 256; + func->blen = 0; + func->body = calloc(1, sizeof(struct qbe_statement) * func->bsiz); + assert(func->body); + } + if (func->blen + 1 < func->bsiz) { + func->bsiz *= 2; + struct qbe_statement *new = realloc(func->body, func->bsiz); + func->body = new; + assert(func->body); + } + func->body[func->blen++] = *stmt; +} + void -pushi(struct qbe_func *func, enum qbe_instr instr, ...) +pushi(struct qbe_func *func, enum qbe_instr instr, + const struct qbe_value *out, ...) { - assert(0); // TODO + struct qbe_statement stmt = {0}; + va_list ap; + va_start(ap, out); + va_geni(&stmt, instr, out, ap); + va_end(ap); + push(func, &stmt); } void @@ -166,3 +227,11 @@ pushl(struct qbe_func *func, uint64_t *id, const char *fmt, ...) { assert(0); // TODO } + +void +constl(struct qbe_value *val, uint64_t l) +{ + val->kind = QV_CONST; + val->type = &qbe_long; + val->lval = l; +} diff --git a/src/qinstr.c b/src/qinstr.c @@ -0,0 +1,15 @@ +#include "gen.h" +#include "qbe.h" + +enum qbe_instr +alignment_to_qbe_alloc(size_t align) +{ + switch (align) { + case 4: + return Q_ALLOC4; + case 8: + return Q_ALLOC8; + default: + return Q_ALLOC16; + } +}