From d9edbab10c05e51aa727d147207c72ea5a9fcc1f Mon Sep 17 00:00:00 2001 From: James Martin Date: Wed, 19 Oct 2022 15:42:04 -0700 Subject: [PATCH] Fix calculation of labels and jump destinations. Previously, all calculations regarding stack depth and label depth were broken. *All* of them. Off-by-ones, the logic was wrong, etc. Variables are still screwed up, but I'm *almost* there, where "there" is being able to generate functioning programs, hopefully? --- src/format.c | 1 + src/ir.c | 35 +++++++++++++++-------------------- src/lang.c | 32 +++++++++++++++++++++----------- 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/format.c b/src/format.c index 4840410..1e5bc9b 100644 --- a/src/format.c +++ b/src/format.c @@ -120,6 +120,7 @@ symbol new_symbol(void) { void define_executable_symbol(symbol s) { struct symbol* sym = &symbols[s]; + assert(sym->vaddr == (uint64_t) -1); sym->vaddr = file_here; } diff --git a/src/ir.c b/src/ir.c index 73c064f..0d5e7d0 100644 --- a/src/ir.c +++ b/src/ir.c @@ -38,10 +38,7 @@ static uint32_t label_depth = 0; static struct label labels[MAX_LABELS]; void init_ir(var* argc, var* argv, var* env) { - assert(stack_depth == 0); - // seems like this should be necessary. it really feels like there's some - // off-by-one arrows going on around here, but I can't figure it out. - //enter(); + assert(stack_depth == 0 && stack_frame == 0); x86_inst_mov_r64_r64(BP, SP); x86_inst_add_r64_imm8(BP, 8 * 3); *env = stack_depth++; @@ -51,7 +48,8 @@ void init_ir(var* argc, var* argv, var* env) { void enter(void) { assert(stack_frame < MAX_STACK_FRAMES); - struct stack_frame frame = { stack_depth, label_depth }; + printf("ENTERING: %i, %i\n", stack_depth, label_depth); + struct stack_frame frame = { .depth = stack_depth, .label_depth = label_depth }; stack_frames[stack_frame] = frame; stack_frame++; // exit label @@ -61,15 +59,16 @@ void enter(void) { void leave(var* args) { assert(stack_frame > 0); struct stack_frame frame = stack_frames[stack_frame - 1]; - stack_frame--; stack_depth = frame.depth; + label_depth = frame.label_depth; define(frame.label_depth, args); + stack_frame--; } label declare(uint32_t argc) { assert(label_depth < MAX_LABELS); symbol sym = new_symbol(); - struct label label = { stack_frame, argc, sym }; + struct label label = { .frame = stack_frame, .argc = argc, .symbol = sym }; labels[label_depth] = label; return label_depth++; } @@ -82,18 +81,13 @@ label declare_exit(uint32_t argc) { void define(label l, var* args) { struct label* label = &labels[l]; + printf("DEFINING %i (%i)\n", l, label->argc); define_executable_symbol(label->symbol); - // possibly wrong. do I need to do any clean-up of the old frame here? - stack_frame = label->frame; - struct stack_frame* frame = &stack_frames[stack_frame - 1]; - label_depth = frame->label_depth; + assert(label->frame == stack_frame); for (uint32_t i = 0; i < label->argc; i++) { - args[i] = frame->depth + i; + args[i] = stack_depth + i; } - // probably wrong. seems like I ought to create a new frame or something? - // wouldn't this make the old frame too deep? - // but on the other hand, if I enter a new frame, how do I decide when to leave it? - frame->depth += label->argc; + stack_depth += label->argc; } void load_var(reg reg, var var) { @@ -109,13 +103,11 @@ var push_var(reg reg) { } void load_args(struct label* label, var* args) { - struct stack_frame* cur_frame = &stack_frames[stack_frame - 1]; struct stack_frame* dest_frame = &stack_frames[label->frame]; - uint32_t depth_diff = cur_frame->depth - dest_frame->depth; + uint32_t depth_diff = stack_depth - dest_frame->depth; if (depth_diff > 0) { // FIXME: should be immX!!! - // FIXME: this should be necessary! stack depth is never getting decreased! - //x86_inst_add_r64_imm8(SP, depth_diff); + x86_inst_add_r64_imm8(SP, depth_diff); } for (uint32_t arg = 0; arg < label->argc; arg++) { load_var(AX, args[arg]); @@ -125,6 +117,7 @@ void load_args(struct label* label, var* args) { void jump(label l, var* args) { struct label* label = &labels[l]; + printf("JUMP %i (%i)\n", l, label->argc); load_args(label, args); inst_jump(label->symbol); } @@ -135,6 +128,7 @@ void jump_table(size_t branches, label* labels, var index, var* args) { void jump_if(label l, var cond, var* args) { struct label* label = &labels[l]; + printf("JUMP_IF %i (%i)\n", l, label->argc); load_var(BX, cond); load_args(label, args); inst_jump_if_not_zero(label->symbol, BX); @@ -142,6 +136,7 @@ void jump_if(label l, var cond, var* args) { void jump_unless(label l, var cond, var* args) { struct label* label = &labels[l]; + printf("JUMP_UNLESS %i (%i)\n", l, label->argc); load_var(BX, cond); load_args(label, args); inst_jump_if_zero(label->symbol, BX); diff --git a/src/lang.c b/src/lang.c index 9337271..bd15142 100644 --- a/src/lang.c +++ b/src/lang.c @@ -34,6 +34,7 @@ enum if_state { IF_COND, IF_THEN, IF_ELSE, + IF_END, }; struct if_crumb { @@ -336,6 +337,7 @@ void enter_block(void) { struct if_crumb ifc = ctx->data.if_; switch (ifc.state) { case IF_COND: + case IF_END: assert(0); case IF_THEN: define(ifc.then, NULL); @@ -351,6 +353,7 @@ void enter_block(void) { assert(loopc->state == LOOP_CLEAN); loopc->state = LOOP_BODY; loopc->next = declare(loopc->assignment_count); + printf("LOOP %i END %i\n", loopc->next, loopc->exit); var args[MAX_ASSIGNMENTS]; define(loopc->next, args); // TODO NOTE: is this the correct order? @@ -392,9 +395,9 @@ void stmt_expr(void) { void exit_block(void) { printf("** exit_block\n"); - struct crumb ctx = context[context_depth - 1]; - assert(ctx.type == BLOCK_CRUMB); - struct block_crumb blockc = ctx.data.block; + struct crumb* ctx = &context[context_depth - 1]; + assert(ctx->type == BLOCK_CRUMB); + struct block_crumb blockc = ctx->data.block; assert(blockc.state == BLOCK_CLEAN); var ret = blockc.final; if (ret == (var) -1) { @@ -402,21 +405,26 @@ void exit_block(void) { ret = lit(0); } context_depth--; - ctx = context[context_depth - 1]; - switch (ctx.type) { + ctx = &context[context_depth - 1]; + switch (ctx->type) { case EXPR_CRUMB: { push_argument(ret); break; } case IF_CRUMB: { - struct if_crumb ifc = ctx.data.if_; - assert(ifc.state != IF_COND); - jump(ifc.end, &ret); + struct if_crumb* ifc = &ctx->data.if_; + assert(ifc->state == IF_THEN || ifc->state == IF_ELSE); + jump(ifc->end, &ret); + if (ifc->state == IF_THEN) { + ifc->state = IF_ELSE; + } else if (ifc->state == IF_ELSE) { + ifc->state = IF_END; + } break; } case LOOP_CRUMB: { // unlike with `if`, there is no `exit_loop`, so we do clean-up here. - struct loop_crumb loopc = ctx.data.loop; + struct loop_crumb loopc = ctx->data.loop; assert(loopc.state == LOOP_BODY); jump(loopc.exit, &ret); context_depth--; @@ -501,6 +509,7 @@ void enter_if(void) { label then = declare(0); label else_ = declare(0); label end = declare_exit(1); + printf("IF THEN %i ELSE %i EXIT %i\n", then, else_, end); struct if_crumb ifc = { .state = IF_COND, .then = then, @@ -524,14 +533,15 @@ void exit_if(void) { struct if_crumb ifc = ctx.data.if_; switch (ifc.state) { case IF_COND: + case IF_THEN: assert(0); - case IF_THEN: { + case IF_ELSE: { define(ifc.else_, NULL); var ret = lit(0); jump(ifc.end, &ret); break; } - case IF_ELSE: + case IF_END: break; } var ret;