harec

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

commit 63a3174a7099949ce62e28a15b0774930a6ef77b
parent cdae8e4489fb1a60243afff3ee6597500668180a
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 20 Jan 2021 15:03:42 -0500

gen: implement defer

Diffstat:
Minclude/gen.h | 6++++++
Msrc/gen.c | 75++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Atests/16-defer.ha | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtests/configure | 3++-
4 files changed, 126 insertions(+), 10 deletions(-)

diff --git a/include/gen.h b/include/gen.h @@ -28,11 +28,17 @@ enum scope_class { SCOPE_OTHER, // expression lists, etc }; +struct gen_deferred { + const struct expression *expr; + struct gen_deferred *next; +}; + struct gen_scope_context { const char *label; enum scope_class class; struct qbe_value *after; struct qbe_value *end; + struct gen_deferred *defers, **next_defer; struct gen_scope_context *parent; }; diff --git a/src/gen.c b/src/gen.c @@ -14,6 +14,9 @@ #include "types.h" #include "util.h" +static void gen_expression(struct gen_context *ctx, + const struct expression *expr, const struct qbe_value *out); + static char * ident_to_sym(const struct identifier *ident) { @@ -41,6 +44,7 @@ push_scope(struct gen_context *ctx, scope->class = class; scope->end = end; scope->parent = ctx->scope; + scope->next_defer = &scope->defers; ctx->scope = scope; return scope; } @@ -50,10 +54,26 @@ pop_scope(struct gen_context *ctx) { struct gen_scope_context *scope = ctx->scope; assert(scope); + struct gen_deferred *d = scope->defers; + while (d) { + struct gen_deferred *n = d->next; + free(d); + d = n; + } ctx->scope = ctx->scope->parent; free(scope); } +static void +gen_defers(struct gen_context *ctx, struct gen_scope_context *scope) +{ + struct gen_deferred *d = scope->defers; + while (d) { + gen_expression(ctx, d->expr, NULL); + d = d->next; + } +} + static char * gen_name(struct gen_context *ctx, const char *fmt) { @@ -321,9 +341,6 @@ gen_loadtemp(struct gen_context *ctx, gen_load(ctx, dest, src, is_signed); } -static void gen_expression(struct gen_context *ctx, - const struct expression *expr, const struct qbe_value *out); - static void address_ident(struct gen_context *ctx, const struct expression *expr, @@ -1158,10 +1175,13 @@ gen_expr_control(struct gen_context *ctx, { assert(out == NULL); // Invariant struct gen_scope_context *scope = ctx->scope; - while (expr->control.label != NULL && scope != NULL) { - if (scope->label - && strcmp(expr->control.label, scope->label) == 0 - && scope->class == SCOPE_LOOP) { + while (scope != NULL) { + gen_defers(ctx, scope); + if (expr->control.label && scope->label) { + if (strcmp(expr->control.label, scope->label) == 0) { + break; + } + } else if (scope->class == SCOPE_LOOP) { break; } scope = scope->parent; @@ -1175,6 +1195,17 @@ gen_expr_control(struct gen_context *ctx, } static void +gen_expr_defer(struct gen_context *ctx, + const struct expression *expr, + const struct qbe_value *out) +{ + struct gen_deferred *d = xcalloc(1, sizeof(struct gen_deferred)); + d->expr = expr->defer.deferred; + *ctx->scope->next_defer = d; + ctx->scope->next_defer = &d->next; +} + +static void gen_expr_for(struct gen_context *ctx, const struct expression *expr, const struct qbe_value *out) @@ -1215,6 +1246,7 @@ gen_expr_for(struct gen_context *ctx, gen_expression(ctx, expr->_for.afterthought, NULL); } + gen_defers(ctx, ctx->scope); pop_scope(ctx); pushi(ctx->current, NULL, Q_JMP, &loop, NULL); @@ -1264,6 +1296,12 @@ gen_expr_list(struct gen_context *ctx, const struct expression *expr, const struct qbe_value *out) { + struct qbe_statement endl = {0}; + struct qbe_value end = {0}; + end.kind = QV_LABEL; + end.name = strdup(genl(&endl, &ctx->id, "expr_list_end.%d")); + push_scope(ctx, SCOPE_OTHER, &end); + const struct expressions *exprs = &expr->list.exprs; while (exprs) { const struct qbe_value *dest = NULL; @@ -1273,6 +1311,10 @@ gen_expr_list(struct gen_context *ctx, gen_expression(ctx, exprs->expr, dest); exprs = exprs->next; } + + gen_defers(ctx, ctx->scope); + pop_scope(ctx); + push(&ctx->current->body, &endl); } static void @@ -1324,7 +1366,18 @@ gen_expr_return(struct gen_context *ctx, if (expr->_return.value) { gen_expression(ctx, expr->_return.value, ctx->return_value); } - pushi(ctx->current, NULL, Q_JMP, ctx->end_label, NULL); + + struct gen_scope_context *scope = ctx->scope; + while (scope) { + gen_defers(ctx, scope); + if (scope->class == SCOPE_FUNC) { + break; + } + scope = scope->parent; + } + assert(scope); + + pushi(ctx->current, NULL, Q_JMP, scope->end, NULL); } static void @@ -1584,7 +1637,8 @@ gen_expression(struct gen_context *ctx, gen_expr_constant(ctx, expr, out); break; case EXPR_DEFER: - assert(0); // TODO + gen_expr_defer(ctx, expr, out); + break; case EXPR_FOR: gen_expr_for(ctx, expr, out); break; @@ -1681,9 +1735,12 @@ gen_function_decl(struct gen_context *ctx, const struct declaration *decl) ctx->return_value = NULL; } + push_scope(ctx, SCOPE_FUNC, &end_label_v); pushl(&qdef->func, &ctx->id, "body.%d"); gen_expression(ctx, func->body, ctx->return_value); + gen_defers(ctx, ctx->scope); push(&qdef->func.body, &end_label); + pop_scope(ctx); if (fntype->func.result->storage != TYPE_STORAGE_VOID) { struct qbe_value load = {0}; diff --git a/tests/16-defer.ha b/tests/16-defer.ha @@ -0,0 +1,52 @@ +let x: int = 10; + +fn basics() void = { + assert(x == 10); + defer x = 20; + assert(x == 10); +}; + +fn scope() void = { + let x = 10; + { + defer x = 20; + assert(x == 10); + }; + assert(x == 20); +}; + +fn loops() void = { + let x = 0; + for (let i = 0; i < 5; i += 1) { + defer x += 1; + assert(x == i); + }; + assert(x == 5); +}; + +fn control() void = { + let x = 0; + for (let i = 0; i < 5; i += 1) { + if (true) { + continue; + }; + defer x += 1; + }; + assert(x == 0); + + for (let i = 0; i < 5; i += 1) { + defer x += 1; + if (true) { + break; + }; + }; + assert(x == 1); +}; + +export fn main() void = { + basics(); + assert(x == 20); + scope(); + loops(); + control(); +}; diff --git a/tests/configure b/tests/configure @@ -18,7 +18,8 @@ tests() { 12-loops \ 13-tagged \ 14-switch \ - 15-enums + 15-enums \ + 16-defer do cat <<EOF tests/$t: libhart.a tests/$t.ha