compiler

Unnamed Compiled Systems Language Project
git clone http://frotz.net/git/compiler.git
Log | Files | Refs

commit 9f953f1ad6f4b72ac2065369591a57342b0ec1f9
parent b1028ac73d20cb957c3bb5adb2ae246363b82bc8
Author: Brian Swetland <swetland@frotz.net>
Date:   Fri,  6 Mar 2020 22:14:32 -0800

more compiler work

- split block parsing out from func body parsing
- improved scope management (including tracking exit fixups)
- return statements now branch to epilogue
- while and if parsing (but no codegen)
- assignment statements (for param types)
- micro optimization: drop b <nextinst> in fixups

Diffstat:
Msrc/tlc.c | 188++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 162 insertions(+), 26 deletions(-)

diff --git a/src/tlc.c b/src/tlc.c @@ -63,6 +63,7 @@ char *tnames[] = { typedef struct StringRec* String; typedef struct ObjectRec* Object; typedef struct ScopeRec* Scope; +typedef struct FixupRec* Fixup; typedef struct TypeRec* Type; typedef struct ItemRec* Item; typedef struct CtxRec* Ctx; @@ -70,6 +71,7 @@ typedef struct CtxRec* Ctx; typedef struct StringRec StringRec; typedef struct ObjectRec ObjectRec; typedef struct ScopeRec ScopeRec; +typedef struct FixupRec FixupRec; typedef struct TypeRec TypeRec; typedef struct ItemRec ItemRec; typedef struct CtxRec CtxRec; @@ -81,9 +83,23 @@ struct StringRec { }; struct ScopeRec { + u32 kind; Scope next; // next in scope stack Object first; // first object in this scope u32 level; // height in stack (0 == globals, ...) + Fixup fixups; +}; + +struct FixupRec { + Fixup next; + u32 pc; +}; + +// Scope Kind IDs +enum { + sBlock, + sFunc, + sLoop, }; // ------------------------------------------------------------------ @@ -182,6 +198,7 @@ struct CtxRec { Object typetab; // TODO: hashtable Scope scope; // scope stack ScopeRec global; + Object fn; // function being compiled if non-nil Type type_void; Type type_byte; @@ -205,6 +222,8 @@ void gen_add_op(Ctx ctx, u32 op, Item x, Item y); void gen_mul_op(Ctx ctx, u32 op, Item x, Item y); void gen_rel_op(Ctx ctx, u32 op, Item x, Item y); void gen_unary_op(Ctx ctx, u32 op, Item x); +void gen_fixups(Ctx ctx, Fixup fixup); +void gen_store(Ctx ctx, Item val, Item var); String mkstring(Ctx ctx, const char* text, u32 len) { String str = ctx->strtab; @@ -622,11 +641,14 @@ void make_global(Ctx ctx, Object obj) { // push a scope on the scope stack // if obj is non-nil, it is the first of a list of items in that scope -void push_scope(Ctx ctx, Object obj) { +void push_scope(Ctx ctx, u32 kind, Object obj) { Scope scope = malloc(sizeof(ScopeRec)); - scope->level = ctx->scope->level + 1; + scope->kind = kind; scope->next = ctx->scope; scope->first = obj; + scope->level = ctx->scope->level + 1; + scope->fixups = nil; + ctx->scope = scope; // XXX lazy scopes } @@ -635,8 +657,28 @@ void pop_scope(Ctx ctx) { if (ctx->scope->level == 0) { error(ctx, "cannot pop the global scope"); } - ctx->scope = ctx->scope->next; + gen_fixups(ctx, ctx->scope->fixups); // XXX delete? + ctx->scope = ctx->scope->next; +} + +// find the first surrounding scope of a specified kind +Scope find_scope(Ctx ctx, u32 scope_kind) { + Scope scope = ctx->scope; + while (scope != nil) { + if (scope->kind == scope_kind) { + return scope; + } + scope = scope->next; + } + return nil; +} + +void add_scope_fixup(Ctx ctx, Scope scope) { + Fixup fixup = malloc(sizeof(FixupRec)); + fixup->next = scope->fixups; + fixup->pc = ctx->pc - 4; + scope->fixups = fixup; } void setitem(Item itm, u32 kind, Type type, u32 r, u32 a, u32 b) { @@ -706,7 +748,6 @@ void parse_unary_expr(Ctx ctx, Item x) { void parse_mul_expr(Ctx ctx, Item x) { parse_unary_expr(ctx, x); - printf("mul-expr %x\n", ctx->tok); while ((ctx->tok >> 8) == tcMULOP) { u32 op = ctx->tok; next(ctx); @@ -718,7 +759,6 @@ void parse_mul_expr(Ctx ctx, Item x) { void parse_add_expr(Ctx ctx, Item x) { parse_mul_expr(ctx, x); - printf("add-expr %x\n", ctx->tok); while ((ctx->tok >> 8) == tcADDOP) { u32 op = ctx->tok; next(ctx); @@ -781,38 +821,108 @@ Type parse_type(Ctx ctx) { return nil; } -void parse_function_body(Ctx ctx, Object fn) { - gen_prologue(ctx, fn); - push_scope(ctx, fn->first); +void parse_block(Ctx ctx); + +void parse_while(Ctx ctx) { + ItemRec x; + parse_expr(ctx, &x); + require(ctx, tOBRACE); + push_scope(ctx, sLoop, nil); + parse_block(ctx); + pop_scope(ctx); +} + +void parse_if(Ctx ctx) { + ItemRec x; + parse_expr(ctx, &x); + require(ctx, tOBRACE); + push_scope(ctx, sBlock, nil); + parse_block(ctx); + pop_scope(ctx); + while (ctx->tok == tELSE) { + next(ctx); + if (ctx->tok == tIF) { + next(ctx); + parse_expr(ctx, &x); + require(ctx, tOBRACE); + push_scope(ctx, sBlock, nil); + parse_block(ctx); + pop_scope(ctx); + } else { + require(ctx, tOBRACE); + push_scope(ctx, sBlock, nil); + parse_block(ctx); + pop_scope(ctx); + break; + } + } +} + +void parse_return(Ctx ctx) { + ItemRec x; + if (ctx->tok == tSEMI) { + if (ctx->fn->type->base != ctx->type_void) { + error(ctx, "function requires return type"); + } + next(ctx); + x.type = ctx->type_void; + } else { + parse_expr(ctx, &x); + if (!sametype(ctx->fn->type->base, x.type)) { + error(ctx, "return types do not match"); + } + require(ctx, tSEMI); + } + gen_return(ctx, &x); +} + +void parse_block(Ctx ctx) { while (true) { if (ctx->tok == tCBRACE) { next(ctx); - pop_scope(ctx); - gen_epilogue(ctx, fn); - return; + break; } else if (ctx->tok == tRETURN) { next(ctx); + parse_return(ctx); + } else if (ctx->tok == tWHILE) { + next(ctx); + parse_while(ctx); + } else if (ctx->tok == tIF) { + next(ctx); + parse_if(ctx); + } else if (ctx->tok == tSEMI) { + next(ctx); + // empty statement + } else { ItemRec x; - if (ctx->tok == tSEMI) { - if (fn->type->base != ctx->type_void) { - error(ctx, "function requires return type"); - } + parse_expr(ctx, &x); + if (ctx->tok == tASSIGN) { + next(ctx); + ItemRec y; + parse_expr(ctx, &y); + gen_store(ctx, &y, &x); + } else if ((ctx->tok == tINC) || (ctx->tok == tDEC)) { + ItemRec y; + setitem(&y, iConst, ctx->type_int32, 0, 1, 0); next(ctx); - x.type = ctx->type_void; - } else { - parse_expr(ctx, &x); - if (!sametype(fn->type->base, x.type)) { - error(ctx, "return types do not match"); - } - require(ctx, tSEMI); } - gen_return(ctx, &x); - } else { - expected(ctx, "statement"); + require(ctx, tSEMI); } } } +void parse_function_body(Ctx ctx, Object fn) { + ctx->fn = fn; + gen_prologue(ctx, fn); + push_scope(ctx, sFunc, fn->first); // scope for parameters + push_scope(ctx, sBlock, nil); // top scope for function body + parse_block(ctx); + pop_scope(ctx); + pop_scope(ctx); + gen_epilogue(ctx, fn); +} + + Object parse_param(Ctx ctx, String fname, u32 n, Object first, Object last) { if (n == FNMAXARGS) { error(ctx, "too many parameters (%d) for '%s'", FNMAXARGS, fname->text); @@ -1157,11 +1267,21 @@ void gen_load(Ctx ctx, Item x) { } } +void gen_store(Ctx ctx, Item val, Item var) { + gen_load(ctx, val); + if (var->kind == iParam) { + emit_mem(ctx, STW, val->r, SP, 4 + var->a * 4); + } else { + error(ctx, "gen_store: invalid target"); + } +} + void gen_return(Ctx ctx, Item x) { if (x->type != ctx->type_void) { gen_load_reg(ctx, x, R0); } - // XXX: branch to epilogue + emit_bi(ctx, AL, 0); + add_scope_fixup(ctx, find_scope(ctx, sFunc)); } void gen_add_op(Ctx ctx, u32 op, Item x, Item y) { @@ -1263,6 +1383,22 @@ void gen_unary_op(Ctx ctx, u32 op, Item x) { put_reg(ctx, t0); } +void gen_fixups(Ctx ctx, Fixup fixup) { + if ((fixup != nil) && (fixup->pc == ctx->pc - 4)) { + // if the most recent branch is the + // previously emitted instruction, we + // can just erase it. + ctx->pc -= 4; + fixup = fixup->next; + } + while (fixup != nil) { + u32 off = (ctx->pc - fixup->pc - 4) >> 2; + u32 ins = ctx->code[fixup->pc >> 2] & 0xFF000000; + ctx->code[fixup->pc >> 2] = ins | (off & 0x00FFFFFF); + fixup = fixup->next; + } +} + void gen_prologue(Ctx ctx, Object fn) { fn->value = ctx->pc; emit_opi(ctx, SUB, SP, SP, 4 + fn->type->len * 4);