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:
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
+}