harec

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

commit 2d0d38b7f505114ef37c527a02493c22a1070786
parent 46837f82bb0ff467c78e8ee450a0302997e842b1
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 23 Dec 2020 09:32:58 -0500

gen: implement binary arithmetic

This also adds an "indirect" flag to qbe_value, which is used to
indicate that a value stores its result "indirectly". This is used by
gen_store to choose between copy and store instructions.

I suspected this would be necessary at some point, but I'm not yet sure
how exactly it's going to hold up as gen expands. Keeping an eye on it.

Diffstat:
Minclude/gen.h | 2++
Minclude/qbe.h | 1+
Msrc/check.c | 4++--
Msrc/gen.c | 68+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/parse.c | 1+
Msrc/qinstr.c | 41+++++++++++++++++++++++++++++++++++++++++
6 files changed, 100 insertions(+), 17 deletions(-)

diff --git a/include/gen.h b/include/gen.h @@ -3,6 +3,7 @@ #include <stdbool.h> #include <stdio.h> #include <stdint.h> +#include "expr.h" #include "identifier.h" #include "qbe.h" @@ -39,5 +40,6 @@ bool type_is_aggregate(const struct type *type); 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); +enum qbe_instr binarithm_for_op(enum binarithm_operator op); #endif diff --git a/include/qbe.h b/include/qbe.h @@ -42,6 +42,7 @@ enum qbe_value_kind { struct qbe_value { enum qbe_value_kind kind; const struct qbe_type *type; + bool indirect; union { char *name; uint32_t wval; diff --git a/src/check.c b/src/check.c @@ -61,8 +61,8 @@ check_expr_binarithm(struct context *ctx, expr->type = EXPR_BINARITHM; expr->binarithm.op = aexpr->binarithm.op; - struct expression *lvalue = calloc(1, sizeof(struct expression *)), - *rvalue = calloc(1, sizeof(struct expression *)); + struct expression *lvalue = calloc(1, sizeof(struct expression)), + *rvalue = calloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->binarithm.lvalue, lvalue); check_expression(ctx, aexpr->binarithm.rvalue, rvalue); expr->binarithm.lvalue = lvalue; diff --git a/src/gen.c b/src/gen.c @@ -62,6 +62,7 @@ binding_alloc(struct gen_context *ctx, const struct scope_object *obj, struct qbe_value *val, const char *fmt) { alloc_temp(ctx, val, obj->type, fmt); + val->indirect = true; struct gen_binding *binding = calloc(1, sizeof(struct gen_binding)); binding->name = strdup(val->name); @@ -106,7 +107,14 @@ gen_store(struct gen_context *ctx, const struct qbe_type *qtype = src->type; assert(qtype->stype != Q__VOID); // Invariant assert(qtype->stype != Q__AGGREGATE); // TODO - pushi(ctx->current, NULL, store_for_type(qtype->stype), src, dest, NULL); + + if (dest->indirect) { + assert(!src->indirect); // XXX: Correct? + assert(dest->type == &qbe_long); // XXX: ARCH + pushi(ctx->current, NULL, store_for_type(qtype->stype), src, dest, NULL); + } else { + pushi(ctx->current, dest, Q_COPY, src, NULL); + } } // Given value src of type pointer to A, and value dest of type A, load dest @@ -138,6 +146,25 @@ static void gen_expression(struct gen_context *ctx, const struct expression *expr, const struct qbe_value *out); static void +gen_access(struct gen_context *ctx, + const struct expression *expr, + const struct qbe_value *out) +{ + if (out == NULL) { + pushc(ctx->current, "useless access expression discarded"); + return; + } + + const struct scope_object *obj = expr->access.object; + struct qbe_value src; + qval_for_object(ctx, &src, obj); + + struct qbe_value temp; + gen_loadtemp(ctx, &temp, &src, src.type, type_is_signed(obj->type)); + gen_store(ctx, out, &temp); +} + +static void gen_binding(struct gen_context *ctx, const struct expression *expr, const struct qbe_value *out) @@ -158,22 +185,30 @@ gen_binding(struct gen_context *ctx, } static void -gen_access(struct gen_context *ctx, +gen_binarithm(struct gen_context *ctx, const struct expression *expr, const struct qbe_value *out) { - if (out == NULL) { - pushc(ctx->current, "useless access expression discarded"); - return; - } - - const struct scope_object *obj = expr->access.object; - struct qbe_value src; - qval_for_object(ctx, &src, obj); - - struct qbe_value temp; - gen_loadtemp(ctx, &temp, &src, src.type, type_is_signed(obj->type)); - gen_store(ctx, out, &temp); + const struct qbe_type *ltype = + qtype_for_type(ctx, expr->binarithm.lvalue->result, false); + const struct qbe_type *rtype = + qtype_for_type(ctx, expr->binarithm.rvalue->result, false); + const struct qbe_type *etype = qtype_for_type(ctx, expr->result, false); + assert(etype == ltype && ltype == rtype); // TODO: Type promotion + + assert(expr->result != &builtin_type_bool); // TODO: Logical arithmetic + + struct qbe_value lvalue = {0}, rvalue = {0}, result = {0}; + gen_temp(ctx, &lvalue, ltype, "lvalue.%d"); + gen_temp(ctx, &rvalue, rtype, "rvalue.%d"); + gen_temp(ctx, &result, etype, "result.%d"); + + gen_expression(ctx, expr->binarithm.lvalue, &lvalue); + gen_expression(ctx, expr->binarithm.rvalue, &rvalue); + + pushi(ctx->current, &result, binarithm_for_op(expr->binarithm.op), + &lvalue, &rvalue, NULL); + gen_store(ctx, out, &result); } static void @@ -251,8 +286,10 @@ gen_expression(struct gen_context *ctx, break; case EXPR_ASSERT: case EXPR_ASSIGN: - case EXPR_BINARITHM: assert(0); // TODO + case EXPR_BINARITHM: + gen_binarithm(ctx, expr, out); + break; case EXPR_BINDING: gen_binding(ctx, expr, out); break; @@ -340,6 +377,7 @@ 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, "ret.%d"); + rval.indirect = true; ctx->return_value = &rval; pushl(&qdef->func, &ctx->id, "body.%d"); diff --git a/src/parse.c b/src/parse.c @@ -587,6 +587,7 @@ binop_for_token(enum lexical_token tok) static struct ast_expression * parse_bin_expression(struct parser *par, struct ast_expression *lvalue, int i) { + trace(TR_PARSE, "bin-arithm"); if (!lvalue) { lvalue = parse_cast_expression(par); } diff --git a/src/qinstr.c b/src/qinstr.c @@ -1,4 +1,5 @@ #include <assert.h> +#include "expr.h" #include "gen.h" #include "qbe.h" @@ -60,3 +61,43 @@ load_for_type(enum qbe_stype stype, bool is_signed) } assert(0); } + +enum qbe_instr +binarithm_for_op(enum binarithm_operator op) +{ + // TODO: udiv et al + switch (op) { + case BIN_PLUS: + return Q_ADD; + case BIN_BAND: + return Q_AND; + case BIN_DIV: + return Q_DIV; + case BIN_MINUS: + return Q_SUB; + case BIN_TIMES: + return Q_MUL; + case BIN_MODULO: + return Q_REM; + case BIN_BOR: + return Q_OR; + case BIN_BXOR: + return Q_XOR; + case BIN_LSHIFT: + return Q_SHL; + case BIN_RSHIFT: + return Q_SHR; + case BIN_BNOT: + case BIN_GREATER: + case BIN_GREATEREQ: + case BIN_LAND: + case BIN_LEQUAL: + case BIN_LESS: + case BIN_LESSEQ: + case BIN_LOR: + case BIN_LXOR: + case BIN_NEQUAL: + assert(0); // Invariant + } + assert(0); // Unreachable +}