harec

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

commit a5d269eb7ca97a40903ee5f6993035909131538a
parent 048eb9c5954c8d505c8775da6aff6d1b276d37bd
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 28 Apr 2021 10:07:56 -0400

Finish (non-variadic) insert implementation

Diffstat:
Minclude/expr.h | 7+++++++
Mrt/Makefile | 1+
Art/memmove.ha | 16++++++++++++++++
Msrc/check.c | 62+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/gen.c | 90++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Atests/28-insert.ha | 30++++++++++++++++++++++++++++++
Mtests/configure | 3++-
7 files changed, 206 insertions(+), 3 deletions(-)

diff --git a/include/expr.h b/include/expr.h @@ -218,6 +218,12 @@ struct expression_if { struct expression *true_branch, *false_branch; }; +struct expression_insert { + struct expression *expr; + struct expression *variadic; + struct append_values *values; +}; + struct expressions { struct expression *expr; struct expressions *next; @@ -337,6 +343,7 @@ struct expression { struct expression_for _for; struct expression_free free; struct expression_if _if; + struct expression_insert insert; struct expression_list list; struct expression_match match; struct expression_measure measure; diff --git a/rt/Makefile b/rt/Makefile @@ -5,6 +5,7 @@ libhart_srcs+=\ rt/ensure.ha \ rt/malloc.ha \ rt/memcpy.ha \ + rt/memmove.ha \ rt/memset.ha \ rt/rtmain.ha \ rt/strcmp.ha diff --git a/rt/memmove.ha b/rt/memmove.ha @@ -0,0 +1,16 @@ +export fn memmove(dest: *void, src: *void, n: size) void = { + let d = dest: *[*]u8, s = src: *[*]u8; + if (d: uintptr == s: uintptr) { + return; + }; + + if (d: uintptr < s: uintptr) { + for (let i = 0z; i < n; i += 1) { + d[i] = s[i]; + }; + } else { + for (let i = 0z; i < n; i += 1) { + d[n - i - 1] = s[n - i - 1]; + }; + }; +}; diff --git a/src/check.c b/src/check.c @@ -1589,6 +1589,65 @@ check_expr_if(struct context *ctx, } static struct errors * +check_expr_insert(struct context *ctx, + const struct ast_expression *aexpr, + struct expression *expr, + const struct type *hint, + struct errors *errors) +{ + assert(aexpr->type == EXPR_INSERT); + assert(!aexpr->insert.is_static); // TODO + expr->type = EXPR_INSERT; + expr->result = &builtin_type_void; + expr->insert.expr = xcalloc(sizeof(struct expression), 1); + errors = check_expression(ctx, aexpr->insert.expr, + expr->insert.expr, NULL, errors); + // TODO: Handle dereferences + assert(expr->insert.expr->type == EXPR_ACCESS + && expr->insert.expr->access.type == ACCESS_INDEX); + const struct type *sltype = expr->insert.expr->access.array->result; + if (type_dealias(sltype)->storage != STORAGE_SLICE) { + return error(aexpr->insert.expr->loc, expr, errors, + "cannot insert into non-slice type %s", + type_storage_unparse(type_dealias(sltype)->storage)); + } + if (type_dealias(sltype)->flags & TYPE_CONST) { + return error(aexpr->insert.expr->loc, expr, errors, + "insert must operate on a mutable slice"); + } + const struct type *memb = type_dealias(sltype)->array.members; + struct append_values **next = &expr->insert.values; + for (struct ast_append_values *avalue = aexpr->insert.values; avalue; + avalue = avalue->next) { + struct append_values *value = *next = + xcalloc(sizeof(struct append_values), 1); + value->expr = + xcalloc(sizeof(struct expression), 1); + errors = check_expression(ctx, avalue->expr, + value->expr, memb, errors); + if (!type_is_assignable(memb, value->expr->result)) { + return error(avalue->expr->loc, expr, errors, + "inserted value must be assignable to member type"); + } + value->expr = lower_implicit_cast(memb, value->expr); + next = &value->next; + } + if (aexpr->insert.variadic != NULL) { + const struct type *type = expr->insert.expr->result; + expr->insert.variadic = xcalloc(sizeof(struct expression), 1); + errors = check_expression(ctx, aexpr->insert.variadic, + expr->insert.variadic, type, errors); + if (!type_is_assignable(type, expr->insert.variadic->result)) { + return error(aexpr->insert.variadic->loc, expr, errors, + "inserted slice must be assignable to slice type"); + } + expr->insert.variadic = + lower_implicit_cast(type, expr->insert.variadic); + } + return errors; +} + +static struct errors * check_expr_list(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, @@ -2513,7 +2572,8 @@ check_expression(struct context *ctx, errors = check_expr_if(ctx, aexpr, expr, hint, errors); break; case EXPR_INSERT: - assert(0); // TODO + errors = check_expr_insert(ctx, aexpr, expr, hint, errors); + break; case EXPR_LIST: errors = check_expr_list(ctx, aexpr, expr, hint, errors); break; diff --git a/src/gen.c b/src/gen.c @@ -1916,6 +1916,93 @@ gen_expr_if(struct gen_context *ctx, } static void +gen_expr_insert(struct gen_context *ctx, + const struct expression *expr, + const struct qbe_value *out) +{ + assert(expr->type == EXPR_INSERT); + assert(!expr->insert.variadic); // TODO + + struct qbe_value val = {0}, temp = {0}; + gen_temp(ctx, &val, &qbe_long, "insert.val.%d"); + assert(expr->insert.expr->type == EXPR_ACCESS + && expr->insert.expr->access.type == ACCESS_INDEX); + + // TODO: Automatic dereference here + const struct expression *expr_array = expr->insert.expr->access.array; + struct qbe_value addr = {0}; + address_object(ctx, expr_array, &addr); + qval_address(&addr); + gen_store(ctx, &val, &addr); + + const struct expression *expr_index = expr->insert.expr->access.index; + struct qbe_value index = {0}; + gen_temp(ctx, &index, &qbe_long, "insert.index.%d"); + gen_expression(ctx, expr_index, &index); + + const struct type *sltype = expr->insert.expr->access.array->result; + const struct type *mtype = type_dealias(sltype)->array.members; + + struct qbe_value len = {0}, newlen = {0}, lenptr = {0}, nadd = {0}; + gen_temp(ctx, &lenptr, &qbe_long, "insert.lenptr.%d"); + gen_temp(ctx, &newlen, &qbe_long, "insert.newlen.%d"); + gen_loadtemp(ctx, &lenptr, &val, &qbe_long, false); + constl(&temp, builtin_type_size.size); + pushi(ctx->current, &lenptr, Q_ADD, &lenptr, &temp, NULL); + qval_deref(&lenptr); + gen_loadtemp(ctx, &len, &lenptr, &qbe_long, false); + size_t args = 0; + for (struct append_values *value = expr->insert.values; + value; value = value->next) { + args++; + } + constl(&nadd, args); + pushi(ctx->current, &newlen, Q_ADD, &len, &nadd, NULL); + gen_store(ctx, &lenptr, &newlen); + + struct qbe_value rtfunc = {0}, membsz = {0}; + constl(&membsz, mtype->size); + rtfunc.kind = QV_GLOBAL; + rtfunc.name = strdup("rt.ensure"); + rtfunc.type = &qbe_long; + pushi(ctx->current, NULL, Q_CALL, &rtfunc, &val, &membsz, NULL); + + struct qbe_value ptr = {0}; + const struct qbe_type *type = qtype_for_type(ctx, mtype, true); + qval_deref(&val); + gen_loadtemp(ctx, &ptr, &val, &qbe_long, "insert.ptr.%d"); + qval_address(&ptr); + + pushi(ctx->current, &index, Q_MUL, &index, &membsz, NULL); + pushi(ctx->current, &ptr, Q_ADD, &ptr, &index, NULL); + + struct qbe_value dest = {0}, ncopy = {0}, nbytes = {0}; + gen_temp(ctx, &dest, &qbe_long, "insert.dest.%d"); + gen_temp(ctx, &ncopy, &qbe_long, "insert.ncopy.%d"); + gen_temp(ctx, &nbytes, &qbe_long, "insert.ninsert.%d"); + pushi(ctx->current, &dest, Q_COPY, &ptr, NULL); + pushi(ctx->current, &ncopy, Q_MUL, &len, &membsz, NULL); + pushi(ctx->current, &ncopy, Q_SUB, &ncopy, &index, NULL); + pushi(ctx->current, &nbytes, Q_MUL, &nadd, &membsz, NULL); + pushi(ctx->current, &dest, Q_ADD, &dest, &nbytes, NULL); + rtfunc.name = strdup("rt.memmove"); + pushi(ctx->current, NULL, Q_CALL, &rtfunc, &dest, &ptr, &ncopy, NULL); + + for (struct append_values *value = expr->insert.values; value; + value = value->next) { + struct qbe_value v = {0}; + alloc_temp(ctx, &v, value->expr->result, "insert.value.%d"); + qval_deref(&v); + gen_expression(ctx, value->expr, &v); + v.indirect = false; + ptr.type = type; + gen_copy(ctx, &ptr, &v); + ptr.type = &qbe_long; + pushi(ctx->current, &ptr, Q_ADD, &ptr, &membsz, NULL); + } +} + +static void gen_expr_list(struct gen_context *ctx, const struct expression *expr, const struct qbe_value *out) @@ -2650,7 +2737,8 @@ gen_expression(struct gen_context *ctx, gen_expr_if(ctx, expr, out); break; case EXPR_INSERT: - assert(0); // TODO + gen_expr_insert(ctx, expr, out); + break; case EXPR_LIST: gen_expr_list(ctx, expr, out); break; diff --git a/tests/28-insert.ha b/tests/28-insert.ha @@ -0,0 +1,30 @@ +fn sleq(a: []int, b: []int) bool = { + if (len(a) != len(b)) { + return false; + }; + for (let i = 0z; i < len(a); i += 1) { + if (a[i] != b[i]) { + return false; + }; + }; + return true; +}; + +fn basics() void = { + let x: []int = []; + defer free(x); + append(x, 1, 2, 3, 4, 5, 6); + insert(x[2], 7, 8, 9); + assert(sleq(x, [1, 2, 7, 8, 9, 3, 4, 5, 6])); + + let x: []int = alloc([], 9); + defer free(x); + append(x, 1, 2, 3, 4, 5, 6); + insert(x[2], 7, 8, 9); + assert(sleq(x, [1, 2, 7, 8, 9, 3, 4, 5, 6])); +}; + +export fn main() void = { + basics(); + // TODO: Variadic insert +}; diff --git a/tests/configure b/tests/configure @@ -30,7 +30,8 @@ tests() { 24-imports \ 25-promotion \ 26-gen \ - 27-rt + 27-rt \ + 28-insert do cat <<EOF tests/$t: libhart.a tests/$t.ha