diff --git a/src/ir.c b/src/ir.c index 2699010..f89cb42 100644 --- a/src/ir.c +++ b/src/ir.c @@ -119,6 +119,13 @@ var lit_string(char* str) { exit(1); } +var add(var addend1, var addend2) { + load_var(AX, addend1); + load_var(BX, addend2); + x86_inst_add_r64_r64(AX, BX); + return push_var(AX); +} + var sub(var subtrahend, var minuend) { // TODO: use modr/m load_var(AX, subtrahend); diff --git a/src/ir.h b/src/ir.h index 3548e54..9f8034e 100644 --- a/src/ir.h +++ b/src/ir.h @@ -70,6 +70,9 @@ var lit(uint64_t lit); /// String literal. var lit_string(char* str); +/// Addition. +var add(var addend1, var addend2); + /// Subtraction. var sub(var subtrahend, var minuend); diff --git a/src/lang.c b/src/lang.c index d824f45..99bf983 100644 --- a/src/lang.c +++ b/src/lang.c @@ -66,6 +66,13 @@ struct expr_crumb { enum operator_ operators[MAX_OPERATORS]; }; +struct jump_crumb { + label label; + uint32_t arity; + uint32_t argument_count; + var arguments[MAX_ARGUMENTS]; +}; + enum crumb_type { BLOCK_CRUMB, IF_CRUMB, @@ -79,7 +86,7 @@ union crumb_data { struct if_crumb if_; struct loop_crumb loop; struct expr_crumb expr; - label jump; + struct jump_crumb jump; }; struct crumb { @@ -132,9 +139,16 @@ static void push_new_expr(void) { push(crumb); } -static void push_new_jump(label label) { +struct label_and_arity { + label label; + uint32_t arity; +}; + +static void push_new_jump(struct label_and_arity label) { union crumb_data data; - data.jump = label; + data.jump.label = label.label; + data.jump.arity = label.arity; + data.jump.argument_count = 0; struct crumb crumb = { .type = JUMP_CRUMB, .data = data, @@ -241,17 +255,22 @@ static const char* label_type_name(enum label_type type) { } } -static label lookup_label(enum label_type type, char* name) { +static struct label_and_arity lookup_label(enum label_type type, char* name) { for (uint32_t i = context_depth; i > 0; i--) { struct crumb ctx = context[i - 1]; switch (ctx.type) { case LOOP_CRUMB: if (name == NULL || strcmp(name, ctx.data.loop.label_name) == 0) { + struct label_and_arity label; if (type == NEXT_LABEL) { - return ctx.data.loop.next; + label.label = ctx.data.loop.next; + label.arity = ctx.data.loop.assignment_count; + return label; } if (type == EXIT_LABEL) { - return ctx.data.loop.exit; + label.label = ctx.data.loop.exit; + label.arity = 1; + return label; } } break; @@ -267,11 +286,37 @@ static label lookup_label(enum label_type type, char* name) { exit(1); } +void reduce_expression_binop(struct expr_crumb* exprc, var (*emit)(var arg1, var arg2)) { + assert(exprc->argument_count >= 2); + var arg1 = exprc->arguments[0]; + var arg2 = exprc->arguments[1]; + exprc->arguments[0] = emit(arg1, arg2); + memmove(&exprc->arguments[1], &exprc->arguments[2], exprc->argument_count - 2); + exprc->argument_count--; +} - -void reduce_expression(struct expr_crumb* exprc) { - // TODO FIXME - return; +var reduce_expression(struct expr_crumb* exprc) { + // TODO FIXME: operator precedence + if (exprc->operator_count > 0 || exprc->argument_count > 1) { + fprintf(stderr, "warning: expression reduction may be incorrect\n"); + //exit(1); + } + for (uint32_t op_ix = 0; op_ix < exprc->operator_count; op_ix++) { + switch (exprc->operators[op_ix]) { + case OP_ADD: + reduce_expression_binop(exprc, add); + break; + case OP_SUB: + reduce_expression_binop(exprc, sub); + break; + default: + fprintf(stderr, "error: operator not implemented: %i", exprc->operators[op_ix]); + exit(1); + } + } + exprc->operator_count = 0; + assert(exprc->argument_count == 1); + return exprc->arguments[0]; } void enter_block(void) { @@ -371,7 +416,7 @@ void exit_block(void) { case LOOP_CRUMB: { // unlike with `if`, there is no `exit_loop`, so we do clean-up here. struct loop_crumb loopc = ctx.data.loop; - assert(loopc.state == LOOP_CLEAN); + assert(loopc.state == LOOP_BODY); jump(loopc.exit, &ret); context_depth--; for (uint32_t i = 0; i < loopc.assignment_count; i++) { @@ -396,10 +441,6 @@ void exit_expr(void) { struct expr_crumb* exprc = &ctx->data.expr; assert(exprc->argument_count > 0); reduce_expression(exprc); - if (exprc->operator_count > 0 || exprc->argument_count > 1) { - fprintf(stderr, "error: failed to reduce expression\n"); - exit(1); - } var ret = exprc->arguments[0]; context_depth--; ctx = &context[context_depth - 1]; @@ -441,9 +482,13 @@ void exit_expr(void) { } case JUMP_CRUMB: { // TODO FIXME: this is *completely wrong* for `next`! - label label = ctx->data.jump; - jump(label, &ret); + struct jump_crumb jumpc = ctx->data.jump; + fprintf(stderr, "args: %i, arity: %i\n", jumpc.argument_count, jumpc.arity); + assert(jumpc.argument_count + 1 == jumpc.arity); + jumpc.arguments[jumpc.argument_count] = ret; + jump(jumpc.label, jumpc.arguments); // TODO: better way to handle returning impossible value + context_depth--; push_argument(ret); break; } @@ -491,6 +536,7 @@ void exit_if(void) { } var ret; leave(&ret); + context_depth--; push_argument(ret); } @@ -558,10 +604,24 @@ void exit_group(void) { } void expr_op(enum operator_ op) { - printf("** expr_op %ir\n", op); + printf("** expr_op %i\n", op); struct crumb* ctx = &context[context_depth - 1]; assert(ctx->type == EXPR_CRUMB); struct expr_crumb* exprc = &ctx->data.expr; + if (op == OP_JUXT && context_depth > 1) { + // HACK: should handle continuations separately from expressions + struct crumb* ctx2 = &context[context_depth - 2]; + if (ctx2->type == JUMP_CRUMB) { + struct jump_crumb* jumpc = &ctx2->data.jump; + var ret = reduce_expression(exprc); + assert(jumpc->argument_count < MAX_ARGUMENTS); + jumpc->arguments[jumpc->argument_count] = ret; + jumpc->argument_count++; + context_depth--; + push_new_expr(); + return; + } + } if (exprc->operator_count > MAX_OPERATORS) { fprintf(stderr, "error: exceeded maximum number of operators in expression\n"); exit(1); diff --git a/src/main.c b/src/main.c index 8d51d29..5530d50 100644 --- a/src/main.c +++ b/src/main.c @@ -21,6 +21,7 @@ symbol compile(void) { var sys_exit = lit(60); var args[2] = { sys_exit, exit_code }; syscall(2, args); + parse(); return entry_point; } @@ -31,8 +32,6 @@ int main(int argc, char** argv) { } open_files(argv[2], argv[1]); - //parse(); - elf_executable(); symbol entry_point = compile(); finish_executable(entry_point); diff --git a/src/parse.c b/src/parse.c index b1bc112..0775b40 100644 --- a/src/parse.c +++ b/src/parse.c @@ -192,7 +192,7 @@ void parse(void) { } break; case TOK_NEXT: - push(ST_EXPR_CONT); + push(ST_EXPR); if (nxt.type == TOK_LABEL) { next(); expr_next(nxt.data.label); diff --git a/src/x86encode.c b/src/x86encode.c index c67895c..f806ec4 100644 --- a/src/x86encode.c +++ b/src/x86encode.c @@ -269,6 +269,10 @@ void x86_inst_add_r64_imm8(reg dest, int8_t imm) { x86_enc_rexw_modxm_imm8(0x83, 0, dest, (uint8_t) imm); } +void x86_inst_add_r64_r64(reg dest, reg src) { + x86_enc_rexw_modrr(0x0, src, dest); +} + void x86_inst_syscall(void) { const uint8_t buf[2] = { 0x0f, 0x05 }; append_data(2, buf); diff --git a/src/x86encode.h b/src/x86encode.h index de87ac4..0ff4570 100644 --- a/src/x86encode.h +++ b/src/x86encode.h @@ -55,6 +55,7 @@ void x86_inst_sub_r64_imm(reg dest, int32_t imm); void x86_inst_sub_r64_r64(reg dest, reg src); void x86_inst_add_r64_imm8(reg dest, int8_t imm); +void x86_inst_add_r64_r64(reg dest, reg src); void x86_inst_syscall(void);