diff --git a/stage1/spl_ast2ir.c b/stage1/spl_ast2ir.c index 2aa0b62..9c8cdcd 100644 --- a/stage1/spl_ast2ir.c +++ b/stage1/spl_ast2ir.c @@ -28,6 +28,26 @@ static void emit_break_stmt(spl_ast2ir_t *a, spl_ast_node_t *n); static spl_ir_node_ref_t emit_expr(spl_ast2ir_t *a, spl_ast_node_ref_t ref); static spl_ir_node_ref_t emit_lvalue(spl_ast2ir_t *a, spl_ast_node_ref_t ref); static void emit_stmt(spl_ast2ir_t *a, spl_ast_node_ref_t ref); +/* Emit one exit path without consuming the lexical registrations needed by + * sibling paths. A private stack also allows cleanup bodies to register defer. */ +static void emit_deferred_until(spl_ast2ir_t *a, usize base) { + if (vec_size(a->defer_stack) <= base) + return; + + spl_defer_stack_t saved = a->defer_stack; + vec_init(a->defer_stack); + for (usize i = 0; i < vec_size(saved); ++i) + vec_push(a->defer_stack, vec_at(saved, i)); + + while (!a->ir->terminated && vec_size(a->defer_stack) > base) { + spl_ast_node_ref_t body = vec_pop(a->defer_stack); + emit_stmt(a, body); + } + + vec_free(a->defer_stack); + a->defer_stack = saved; +} + static void emit_decl(spl_ast2ir_t *a, spl_ast_node_ref_t ref); static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n); static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, @@ -1105,11 +1125,16 @@ static spl_ir_node_ref_t emit_expr_raw(spl_ast2ir_t *a, spl_ast_node_ref_t ref) return node; } case SPL_AST_EXPR_BLOCK: { - for (usize i = 0; i < vec_size(n->block.stmts); ++i) + usize defer_base = vec_size(a->defer_stack); + for (usize i = 0; i < vec_size(n->block.stmts) && !a->ir->terminated; ++i) emit_stmt(a, vec_at(n->block.stmts, i)); - if (n->block.tail_expr) - return emit_expr(a, n->block.tail_expr); - return 0; + spl_ir_node_ref_t value = 0; + if (!a->ir->terminated && n->block.tail_expr) + value = emit_expr(a, n->block.tail_expr); + if (!a->ir->terminated) + emit_deferred_until(a, defer_base); + a->defer_stack.size = defer_base; + return value; } case SPL_AST_EXPR_IF: case SPL_AST_EXPR_IFVAR: @@ -1422,6 +1447,7 @@ static spl_ir_node_ref_t emit_loop_like(spl_ast2ir_t *a, spl_ast_node_ref_t ref) spl_loop_ctx_t ctx = {0}; ctx.cont_blk = cont_blk; + ctx.defer_base = vec_size(a->defer_stack); ctx.label = NULL; ctx.result_slot = result_slot; ctx.result_tid = result_t; @@ -1480,29 +1506,34 @@ static void emit_break_stmt(spl_ast2ir_t *a, spl_ast_node_t *n) { lbl = ln->label.name; } - spl_loop_ctx_t *target = NULL; + usize target_index = vec_size(a->loop_stack); if (lbl) { for (usize i = vec_size(a->loop_stack); i > 0; --i) { spl_loop_ctx_t *c = &vec_at(a->loop_stack, i - 1); if (c->label && strcmp(c->label, lbl) == 0) { - target = c; + target_index = i - 1; break; } } } else { - target = &vec_at(a->loop_stack, vec_size(a->loop_stack) - 1); + target_index = vec_size(a->loop_stack) - 1; } - if (!target) + if (target_index == vec_size(a->loop_stack)) return; - if (n->statement_break.expr && target->result_slot) { + /* Emitting a value or a cleanup can grow/reallocate loop_stack. */ + spl_loop_ctx_t target = vec_at(a->loop_stack, target_index); + if (n->statement_break.expr && target.result_slot) { spl_ir_node_ref_t bv = emit_expr(a, n->statement_break.expr); if (bv) - spl_ir_builder_mem_store(a->ir, target->result_tid, target->result_slot, bv); + spl_ir_builder_mem_store(a->ir, target.result_tid, target.result_slot, bv); } + emit_deferred_until(a, target.defer_base); + if (a->ir->terminated) + return; spl_ir_node_ref_t jmp = spl_ir_builder_control_jmp(a->ir); - vec_push(target->breaks, jmp); + vec_push(vec_at(a->loop_stack, target_index).breaks, jmp); } static void emit_stmt(spl_ast2ir_t *a, spl_ast_node_ref_t ref) { @@ -1518,12 +1549,10 @@ static void emit_stmt(spl_ast2ir_t *a, spl_ast_node_ref_t ref) { if (n->statement_ret.expr) v = emit_expr(a, n->statement_ret.expr); - while (vec_size(a->defer_stack) > a->cur_defer_base) { - spl_ast_node_ref_t d = vec_pop(a->defer_stack); - emit_stmt(a, d); - } + emit_deferred_until(a, a->cur_defer_base); - spl_ir_builder_control_ret(a->ir, ret_ir, v); + if (!a->ir->terminated) + spl_ir_builder_control_ret(a->ir, ret_ir, v); return; } case SPL_AST_STATEMENT_DEFER: @@ -1566,21 +1595,26 @@ static void emit_stmt(spl_ast2ir_t *a, spl_ast_node_ref_t ref) { lbl = ln->label.name; } - spl_ir_node_ref_t jmp = spl_ir_builder_control_jmp(a->ir); - + usize target_index = vec_size(a->loop_stack) - 1; if (lbl) { + target_index = vec_size(a->loop_stack); for (usize i = vec_size(a->loop_stack); i > 0; --i) { spl_loop_ctx_t *c = &vec_at(a->loop_stack, i - 1); if (c->label && strcmp(c->label, lbl) == 0) { - spl_ir_builder_control_jmp_fixup(a->ir, jmp, c->cont_blk); - return; + target_index = i - 1; + break; } } - return; } + if (target_index == vec_size(a->loop_stack)) + return; - usize cont = vec_at(a->loop_stack, vec_size(a->loop_stack) - 1).cont_blk; - spl_ir_builder_control_jmp_fixup(a->ir, jmp, cont); + spl_loop_ctx_t target = vec_at(a->loop_stack, target_index); + emit_deferred_until(a, target.defer_base); + if (!a->ir->terminated) { + spl_ir_node_ref_t jmp = spl_ir_builder_control_jmp(a->ir); + spl_ir_builder_control_jmp_fixup(a->ir, jmp, target.cont_blk); + } return; } case SPL_AST_EXPR_BLOCK: @@ -1691,12 +1725,10 @@ static void emit_fn_body(spl_ast2ir_t *a, spl_ast_node_t *n) { if (n->fn_decl.body) emit_expr(a, n->fn_decl.body); - if (!a->ir->terminated) { - while (vec_size(a->defer_stack) > defer_base) { - spl_ast_node_ref_t d = vec_pop(a->defer_stack); - emit_stmt(a, d); - } + if (!a->ir->terminated) + emit_deferred_until(a, defer_base); + if (!a->ir->terminated) { if (ret_is_unit) { spl_ir_builder_control_ret(a->ir, ret_ir, 0); } else if (ret_ir) { @@ -1707,10 +1739,7 @@ static void emit_fn_body(spl_ast2ir_t *a, spl_ast_node_t *n) { } } - while (vec_size(a->defer_stack) > defer_base) { - spl_ast_node_ref_t d = vec_pop(a->defer_stack); - (void)d; - } + a->defer_stack.size = defer_base; while (vec_size(a->loop_stack) > loop_base) { spl_loop_ctx_t *top = &vec_at(a->loop_stack, vec_size(a->loop_stack) - 1); vec_free(top->breaks); diff --git a/stage1/spl_ast2ir.h b/stage1/spl_ast2ir.h index de07780..a7c5b08 100644 --- a/stage1/spl_ast2ir.h +++ b/stage1/spl_ast2ir.h @@ -9,6 +9,7 @@ typedef MAP(spl_def_id_t, spl_ir_node_ref_t) spl_def2ir_map_t; typedef struct { usize cont_blk; + usize defer_base; const char *label; spl_ir_node_ref_vec_t breaks; spl_ir_node_ref_t result_slot;