harec

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

commit aba6bcec52f191372537f40d10d4c12a5888bdc8
parent 556ae5103bc7a5ee0f11ee275c22ebba1dcc67df
Author: Drew DeVault <sir@cmpwn.com>
Date:   Tue, 21 Dec 2021 12:56:07 +0100

Overhaul expandable arrays

This improves the design of expandable arrays somewhat so that we are
able to use them in a broader set of contexts, notably for the coming
allocation improvements.

Essentially, this adds the "expandable" tag to the array type itself, so
that an expression of [1, 2, 3...] becomes a [3]int with the expandable
tag set, rather than the previous behavior of looking into the type hint
to find a suitable length.

This is an ABI breaking change.

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Minclude/expr.h | 1-
Minclude/type_store.h | 2+-
Minclude/types.h | 1+
Msrc/check.c | 57++++++++++++++++++++++++++-------------------------------
Msrc/eval.c | 35++++++++++++++++++++++++++++++++---
Msrc/gen.c | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/type_store.c | 4+++-
Msrc/types.c | 15++++++++++++---
8 files changed, 132 insertions(+), 67 deletions(-)

diff --git a/include/expr.h b/include/expr.h @@ -168,7 +168,6 @@ struct expression_compound { struct array_constant { struct expression *value; struct array_constant *next; - bool expand; }; // Invariant: these are sorted by field offset diff --git a/include/type_store.h b/include/type_store.h @@ -39,7 +39,7 @@ const struct type *type_store_lookup_pointer(struct type_store *store, const struct type *referent, unsigned int ptrflags); const struct type *type_store_lookup_array(struct type_store *store, - const struct type *members, size_t len); + const struct type *members, size_t len, bool expandable); const struct type *type_store_lookup_slice(struct type_store *store, const struct type *members); diff --git a/include/types.h b/include/types.h @@ -56,6 +56,7 @@ struct type_alias { struct type_array { size_t length; // SIZE_UNDEFINED for [*] and slices const struct type *members; + bool expandable; }; struct type_enum_value { diff --git a/src/check.c b/src/check.c @@ -995,7 +995,7 @@ lower_vaargs(struct context *ctx, // XXX: This error handling is minimum-effort and bad const struct type *hint = type_store_lookup_array( - ctx->store, type, SIZE_UNDEFINED); + ctx->store, type, SIZE_UNDEFINED, false); check_expression(ctx, &val, vaargs, hint); if (vaargs->result->storage != STORAGE_ARRAY || vaargs->result->array.members != type) { @@ -1162,17 +1162,20 @@ check_expr_array(struct context *ctx, const struct type *hint) { size_t len = 0; - bool expandable = false; + bool expand = false; struct ast_array_constant *item = aexpr->constant.array; struct array_constant *cur, **next = &expr->constant.array; const struct type *type = NULL; if (hint) { hint = type_dealias(hint); - if (hint->storage == STORAGE_ARRAY - || hint->storage == STORAGE_SLICE) { + + size_t narray = 0; + switch (hint->storage) { + case STORAGE_ARRAY: + case STORAGE_SLICE: type = hint->array.members; - } else if (hint->storage == STORAGE_TAGGED) { - size_t narray = 0; + break; + case STORAGE_TAGGED: for (const struct type_tagged_union *tu = &hint->tagged; tu; tu = tu->next) { const struct type *t = type_dealias(tu->type); @@ -1186,8 +1189,10 @@ check_expr_array(struct context *ctx, if (narray != 1) { type = hint = NULL; } - } else { + break; + default: hint = NULL; + break; } } @@ -1209,8 +1214,7 @@ check_expr_array(struct context *ctx, } if (item->expand) { - expandable = true; - expr->constant.array->expand = true; + expand = true; assert(!item->next); } @@ -1219,29 +1223,11 @@ check_expr_array(struct context *ctx, ++len; } - if (expandable) { - if (hint == NULL) { - error(ctx, aexpr->loc, expr, - "Cannot expand array for inferred type"); - return; - } - if (hint->storage != STORAGE_ARRAY - || hint->array.length == SIZE_UNDEFINED - || hint->array.length < len) { - error(ctx, aexpr->loc, expr, - "Cannot expand array into destination type"); - return; - } - expr->result = type_store_lookup_array(ctx->store, - type, hint->array.length); - } else { - if (type == NULL) { - error(ctx, aexpr->loc, expr, - "Cannot infer array type from context, try casting it to the desired type"); - return; - } - expr->result = type_store_lookup_array(ctx->store, type, len); + if (type == NULL) { + error(ctx, aexpr->loc, expr, "Cannot infer array type from context, try casting it to the desired type"); + return; } + expr->result = type_store_lookup_array(ctx->store, type, len, expand); } static const struct type * @@ -3012,6 +2998,15 @@ check_global(struct context *ctx, expect(&adecl->init->loc, type_is_assignable(type, initializer->result), "Constant type is not assignable from initializer type"); + + bool context = adecl->type + && adecl->type->storage == STORAGE_ARRAY + && adecl->type->array.contextual; + if (context) { + // XXX: Do we need to do anything more here + type = initializer->result; + } + initializer = lower_implicit_cast(type, initializer); struct expression *value = diff --git a/src/eval.c b/src/eval.c @@ -310,7 +310,6 @@ eval_const(struct context *ctx, struct expression *in, struct expression *out) arr = arr->next) { struct array_constant *aconst = *next = xcalloc(sizeof(struct array_constant), 1); - aconst->expand = arr->expand; aconst->value = xcalloc(sizeof(struct expression), 1); eval_expr(ctx, arr->value, aconst->value); next = &aconst->next; @@ -359,6 +358,28 @@ eval_const(struct context *ctx, struct expression *in, struct expression *out) return EVAL_OK; } +static void +eval_expand_array(struct context *ctx, + const struct type *intype, const struct type *outtype, + struct expression *in, struct expression *out) +{ + assert(in->type == EXPR_CONSTANT); + assert(out->type == EXPR_CONSTANT); + assert(intype->storage == STORAGE_ARRAY); + assert(outtype->storage == STORAGE_ARRAY); + struct array_constant *array_in = in->constant.array; + struct array_constant **next = &out->constant.array; + for (size_t i = 0; i < outtype->array.length; i++) { + struct array_constant *item = *next = + xcalloc(1, sizeof(struct array_constant)); + item->value = array_in->value; + next = &item->next; + if (array_in->next) { + array_in = array_in->next; + } + } +} + enum eval_result eval_cast(struct context *ctx, struct expression *in, struct expression *out) { @@ -370,7 +391,9 @@ eval_cast(struct context *ctx, struct expression *in, struct expression *out) const struct type *to = type_dealias(in->result), *from = type_dealias(val.result); - if (to->storage == from->storage) { + // The STORAGE_ARRAY exception is to make sure we handle expandable + // arrays at this point. + if (to->storage == from->storage && to->storage != STORAGE_ARRAY) { *out = val; return EVAL_OK; } @@ -416,6 +439,13 @@ eval_cast(struct context *ctx, struct expression *in, struct expression *out) } return EVAL_OK; case STORAGE_ARRAY: + assert(from->storage == STORAGE_ARRAY); + if (from->array.expandable) { + eval_expand_array(ctx, from, to, &val, out); + } else { + out->constant = val.constant; + } + return EVAL_OK; case STORAGE_SLICE: assert(val.result->storage == STORAGE_ARRAY); out->constant = val.constant; @@ -532,7 +562,6 @@ constant_default(struct context *ctx, struct expression *v) case STORAGE_ARRAY: case STORAGE_SLICE: v->constant.array = xcalloc(1, sizeof(struct array_constant)); - v->constant.array->expand = true; v->constant.array->value = xcalloc(1, sizeof(struct expression)); v->constant.array->value->type = EXPR_CONSTANT; v->constant.array->value->result = diff --git a/src/gen.c b/src/gen.c @@ -1141,6 +1141,11 @@ cast_prefers_at(const struct expression *expr) if (type_dealias(to)->storage == STORAGE_TAGGED) { return true; } + // array => array + if (type_dealias(to)->storage == STORAGE_ARRAY + && type_dealias(from)->storage == STORAGE_ARRAY) { + return true; + } // array => slice if (type_dealias(from)->storage == STORAGE_ARRAY && type_dealias(to)->storage == STORAGE_SLICE) { @@ -1150,6 +1155,52 @@ cast_prefers_at(const struct expression *expr) } static void +gen_expr_cast_array_at(struct gen_context *ctx, + const struct expression *expr, struct gen_value out) +{ + const struct type *typeout = type_dealias(expr->result); + const struct type *typein = type_dealias(expr->cast.value->result); + gen_expr_at(ctx, expr->cast.value, out); + if (!typein->array.expandable) { + return; + } + + assert(typein->array.length != SIZE_UNDEFINED + && typeout->array.length != SIZE_UNDEFINED); + assert(typeout->array.length >= typein->array.length); + + const struct type *membtype = typein->array.members; + size_t remain = typeout->array.length - typein->array.length; + + struct qbe_value base = mkqval(ctx, &out); + struct qbe_value offs = constl((typein->array.length - 1) * membtype->size); + struct gen_value next = mktemp(ctx, membtype, ".%d"); + struct qbe_value ptr = mklval(ctx, &next); + struct gen_value item = mktemp(ctx, membtype, "item.%d"); + struct qbe_value qitem = mklval(ctx, &item); + pushi(ctx->current, &qitem, Q_ADD, &base, &offs, NULL); + + if (remain * membtype->size <= 128) { + struct gen_value last = gen_load(ctx, item); + for (size_t n = typein->array.length; n < typeout->array.length; ++n) { + struct qbe_value offs = constl(n * membtype->size); + pushi(ctx->current, &ptr, Q_ADD, &base, &offs, NULL); + gen_store(ctx, next, last); + } + return; + } + + offs = constl(typein->array.length * membtype->size); + pushi(ctx->current, &ptr, Q_ADD, &base, &offs, NULL); + + struct qbe_value rtfunc = mkrtfunc(ctx, "rt.memcpy"); + struct qbe_value dtemp = mklval(ctx, &next); + struct qbe_value stemp = mklval(ctx, &item); + struct qbe_value sz = constl(remain * membtype->size); + pushi(ctx->current, NULL, Q_CALL, &rtfunc, &dtemp, &stemp, &sz, NULL); +} + +static void gen_expr_cast_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { @@ -1173,6 +1224,9 @@ gen_expr_cast_at(struct gen_context *ctx, case STORAGE_TAGGED: gen_expr_cast_tagged_at(ctx, expr, out); break; + case STORAGE_ARRAY: + gen_expr_cast_array_at(ctx, expr, out); + break; default: abort(); // Invariant } } @@ -1386,7 +1440,8 @@ gen_expr_compound_with(struct gen_context *ctx, static void gen_const_array_at(struct gen_context *ctx, - const struct expression *expr, struct gen_value out) + const struct expression *expr, + struct gen_value out) { struct array_constant *aexpr = expr->constant.array; struct qbe_value base = mkqval(ctx, &out); @@ -1402,32 +1457,7 @@ gen_const_array_at(struct gen_context *ctx, ++n; } - if (!aexpr || !aexpr->expand) { - return; - } - - struct gen_value next = mktemp(ctx, atype->array.members, ".%d"); - struct qbe_value ptr = mklval(ctx, &next); - - size_t remain = atype->array.length - n; - if (remain * atype->array.members->size <= 128) { - struct gen_value last = gen_load(ctx, item); - for (; n < atype->array.length; ++n) { - struct qbe_value offs = constl(n * atype->array.members->size); - pushi(ctx->current, &ptr, Q_ADD, &base, &offs, NULL); - gen_store(ctx, next, last); - } - return; - } - - struct qbe_value offs = constl(n * atype->array.members->size); - pushi(ctx->current, &ptr, Q_ADD, &base, &offs, NULL); - - struct qbe_value rtfunc = mkrtfunc(ctx, "rt.memcpy"); - struct qbe_value dtemp = mklval(ctx, &next); - struct qbe_value stemp = mklval(ctx, &item); - struct qbe_value sz = constl(remain * atype->array.members->size); - pushi(ctx->current, NULL, Q_CALL, &rtfunc, &dtemp, &stemp, &sz, NULL); + assert(n == atype->array.length); } static void diff --git a/src/type_store.c b/src/type_store.c @@ -976,13 +976,15 @@ type_store_lookup_pointer(struct type_store *store, const struct type * type_store_lookup_array(struct type_store *store, - const struct type *members, size_t len) + const struct type *members, size_t len, bool expandable) { struct type array = { .storage = STORAGE_ARRAY, .array = { .members = members, .length = len, + // TODO: Define expandable semantics better in spec + .expandable = expandable, }, .size = len == SIZE_UNDEFINED ? SIZE_UNDEFINED : members->size * len, diff --git a/src/types.c b/src/types.c @@ -397,6 +397,7 @@ type_hash(const struct type *type) case STORAGE_ARRAY: hash = fnv1a_u32(hash, type_hash(type->array.members)); hash = fnv1a_size(hash, type->array.length); + hash = fnv1a_u32(hash, type->array.expandable); break; case STORAGE_FUNCTION: hash = fnv1a_u32(hash, type_hash(type->func.result)); @@ -650,9 +651,17 @@ type_is_assignable(const struct type *to, const struct type *from) } return to_secondary->id == from_secondary->id; case STORAGE_ARRAY: - return from->storage == STORAGE_ARRAY - && to->array.length == SIZE_UNDEFINED - && from->array.length != SIZE_UNDEFINED; + if (from->storage != STORAGE_ARRAY) { + return false; + } + if (from->array.expandable) { + return to->array.length != SIZE_UNDEFINED + && to->array.length >= from->array.length + && to->array.members == from->array.members; + } else { + return to->array.length == SIZE_UNDEFINED + && from->array.length != SIZE_UNDEFINED; + } case STORAGE_TAGGED: return tagged_select_subtype(to, from_orig) != NULL || tagged_subset_compat(to, from);