harec

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

commit 65a8d225e0098a23f8edd929d8b5256add800eda
parent a78994ee7377ec20b4de56432bde3113d205d684
Author: Drew DeVault <sir@cmpwn.com>
Date:   Fri, 15 Jan 2021 20:20:45 -0500

gen: implement array/slice bounds test

Diffstat:
Minclude/gen.h | 4++++
Mrt/abort.ha | 7+++++++
Msrc/eval.c | 1+
Msrc/gen.c | 123+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
4 files changed, 113 insertions(+), 22 deletions(-)

diff --git a/include/gen.h b/include/gen.h @@ -7,6 +7,10 @@ #include "identifier.h" #include "qbe.h" +enum fixed_aborts { + ABORT_OOB = 0, +}; + struct unit; void gen(const struct unit *unit, struct qbe_program *out); diff --git a/rt/abort.ha b/rt/abort.ha @@ -5,3 +5,10 @@ export @noreturn @symbol("rt.abort") fn _abort(msg: str) void = { write(2, "\n": *const char, 1z); kill(getpid(), SIGABRT); }; + +// See include/gen.h +const reasons: [1]str = [ + "slice or array access out of bounds", // 0 +]; + +export @noreturn fn abort_fixed(i: int) void = _abort(reasons[i]); diff --git a/src/eval.c b/src/eval.c @@ -9,6 +9,7 @@ enum eval_result eval_const(struct context *ctx, struct expression *in, struct expression *out) { + // TODO: Update for aggregate types out->type = EXPR_CONSTANT; out->result = in->result; out->constant = in->constant; diff --git a/src/gen.c b/src/gen.c @@ -31,18 +31,23 @@ ident_to_sym(const struct identifier *ident) return strdup(ident->name); } -static void -gen_temp(struct gen_context *ctx, struct qbe_value *val, - const struct qbe_type *type, const char *fmt) +static char * +gen_name(struct gen_context *ctx, const char *fmt) { int n = snprintf(NULL, 0, fmt, ctx->id); char *str = xcalloc(1, n + 1); snprintf(str, n + 1, fmt, ctx->id); ++ctx->id; + return str; +} +static void +gen_temp(struct gen_context *ctx, struct qbe_value *val, + const struct qbe_type *type, const char *fmt) +{ val->kind = QV_TEMPORARY; val->type = type; - val->name = str; + val->name = gen_name(ctx, fmt); val->indirect = false; } @@ -320,15 +325,52 @@ address_index(struct gen_context *ctx, pushi(ctx->current, out, Q_LOADL, out, NULL); atype = type_dealias(atype->pointer.referent); } - if (atype->storage == TYPE_STORAGE_SLICE) { - pushi(ctx->current, out, Q_LOADL, out, NULL); - } struct qbe_value index = {0}; gen_temp(ctx, &index, &qbe_long, "index.%d"); gen_expression(ctx, expr->access.index, &index); - // TODO: Check if offset is in bounds + // XXX: ARCH + struct qbe_value length = {0}; + if (atype->storage == TYPE_STORAGE_ARRAY) { + constl(&length, atype->array.length); + } else if (atype->storage == TYPE_STORAGE_SLICE) { + struct qbe_value ptr = {0}; + gen_temp(ctx, &ptr, &qbe_long, "ptr.%d"); + constl(&length, 8); + pushi(ctx->current, &ptr, Q_ADD, out, &length, NULL); + gen_temp(ctx, &length, &qbe_long, "length.%d"); + pushi(ctx->current, &length, Q_LOADL, &ptr, NULL); + } + + if (atype->storage != TYPE_STORAGE_ARRAY + || atype->array.length != SIZE_UNDEFINED) { + struct qbe_statement validl = {0}, invalidl = {0}; + struct qbe_value bvalid = {0}, binvalid = {0}; + bvalid.kind = QV_LABEL; + bvalid.name = strdup(genl(&validl, &ctx->id, "bounds.valid.%d")); + binvalid.kind = QV_LABEL; + binvalid.name = strdup(genl(&invalidl, &ctx->id, "bounds.invalid.%d")); + + struct qbe_value valid = {0}; + gen_temp(ctx, &valid, &qbe_word, "valid.%d"); + pushi(ctx->current, &valid, Q_CULTL, &index, &length, NULL); + pushi(ctx->current, NULL, Q_JNZ, &valid, &bvalid, &binvalid, NULL); + push(&ctx->current->body, &invalidl); + + struct qbe_value rtfunc = {0}; + rtfunc.kind = QV_GLOBAL; + rtfunc.name = strdup("rt.abort_fixed"); + rtfunc.type = &qbe_long; + constl(&valid, ABORT_OOB); + pushi(ctx->current, NULL, Q_CALL, &rtfunc, &valid, NULL); + + push(&ctx->current->body, &validl); + } + + if (atype->storage == TYPE_STORAGE_SLICE) { + pushi(ctx->current, out, Q_LOADL, out, NULL); + } struct qbe_value temp = {0}; constl(&temp, atype->array.members->size); @@ -1552,20 +1594,14 @@ gen_function_decl(struct gen_context *ctx, const struct declaration *decl) } static void -gen_global_decl(struct gen_context *ctx, const struct declaration *decl) +gen_data_item(struct gen_context *ctx, struct expression *expr, + struct qbe_data_item *item) { - assert(decl->type == DECL_GLOBAL); - const struct global_decl *global = &decl->global; + assert(expr->type == EXPR_CONSTANT); - struct qbe_def *qdef = xcalloc(1, sizeof(struct qbe_def)); - qdef->kind = Q_DATA; - qdef->exported = decl->exported; - qdef->name = ident_to_sym(&decl->ident); - - const union expression_constant *constant = &global->value->constant; - struct qbe_data_item *item = &qdef->data.items; - - const struct type *type = type_dealias(global->type); + struct qbe_def *def; + const union expression_constant *constant = &expr->constant; + const struct type *type = type_dealias(expr->result); switch (type->storage) { case TYPE_STORAGE_I8: case TYPE_STORAGE_U8: @@ -1603,14 +1639,46 @@ gen_global_decl(struct gen_context *ctx, const struct declaration *decl) case TYPE_STORAGE_UINTPTR: assert(0); // TODO: What are the semantics for this? case TYPE_STORAGE_POINTER: - assert(global->value->type == EXPR_CONSTANT); // TODO? + assert(expr->type == EXPR_CONSTANT); // TODO? item->type = QD_VALUE; constl(&item->value, (uint64_t)constant->uval); // XXX: ARCH break; case TYPE_STORAGE_ARRAY: + assert(type->array.length != SIZE_UNDEFINED); + assert(!type->array.expandable); // TODO + for (struct array_constant *c = constant->array; c; c = c->next) { + gen_data_item(ctx, c->value, item); + if (c->next) { + item->next = xcalloc(1, + sizeof(struct qbe_data_item)); + item = item->next; + } + } + break; + case TYPE_STORAGE_STRING: + def = xcalloc(1, sizeof(struct qbe_def)); + def->name = gen_name(ctx, "strdata.%d"); + def->kind = Q_DATA; + def->data.items.type = QD_STRING; + def->data.items.str = strdup(expr->constant.string.value); + def->data.items.sz = expr->constant.string.len; + qbe_append_def(ctx->out, def); + + item->type = QD_VALUE; + item->value.kind = QV_GLOBAL; + item->value.type = &qbe_long; + item->value.name = strdup(def->name); + item->next = xcalloc(1, sizeof(struct qbe_data_item)); + item = item->next; + item->type = QD_VALUE; + constl(&item->value, expr->constant.string.len); + item->next = xcalloc(1, sizeof(struct qbe_data_item)); + item = item->next; + item->type = QD_VALUE; + constl(&item->value, expr->constant.string.len); + break; case TYPE_STORAGE_ENUM: case TYPE_STORAGE_SLICE: - case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED_UNION: case TYPE_STORAGE_UNION: @@ -1622,7 +1690,18 @@ gen_global_decl(struct gen_context *ctx, const struct declaration *decl) case TYPE_STORAGE_VOID: assert(0); // Invariant } +} +static void +gen_global_decl(struct gen_context *ctx, const struct declaration *decl) +{ + assert(decl->type == DECL_GLOBAL); + const struct global_decl *global = &decl->global; + struct qbe_def *qdef = xcalloc(1, sizeof(struct qbe_def)); + qdef->kind = Q_DATA; + qdef->exported = decl->exported; + qdef->name = ident_to_sym(&decl->ident); + gen_data_item(ctx, global->value, &qdef->data.items); qbe_append_def(ctx->out, qdef); }