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