compiler

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

commit 576a00c0478ebabefdfbaa57ff7deb5d72207f79
parent f9791dc75627127d59280d457c359413dc5724f3
Author: Brian Swetland <swetland@frotz.net>
Date:   Fri,  6 Mar 2020 00:12:34 -0800

more compiler progress

- scopes and scope stack
- more prologue generation
- common find() routine for identifiers in scopes
- make_global() to publish into the global scope
- code generation for i32 addition and subtraction
- loading from parameters
- get_reg_tmp() / put_reg() allocator
- emit_opi_n() to non-move immediate ops with arbitrary immediates
- gen_load() now allocates a temporary register
- gen_load_reg() uses an indicated register
- patch_last_load() helper allows gen_load*() to patch the
  destination of the previous instruction (when sensible)
  instead of issuing an additional mov instruction

Diffstat:
Msrc/risc5ins.txt | 2+-
Msrc/tlc.c | 309+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mtest/minimal.tl | 7++++++-
3 files changed, 232 insertions(+), 86 deletions(-)

diff --git a/src/risc5ins.txt b/src/risc5ins.txt @@ -1,7 +1,7 @@ # Copyright 2020, Brian Swetland <swetland@frotz.net> # Licensed under the Apache License, Version 2.0. -0000aaaa----0000------------cccc mov %a, c +0000aaaa----0000------------cccc mov %a, %c 0010aaaa----0000---------------- mov %a, h 0011aaaa----0000---------------- mov %a, nzcv 0010aaaabbbb1000------------cccc adc %a, %b, %c diff --git a/src/tlc.c b/src/tlc.c @@ -53,12 +53,14 @@ char *tnames[] = { typedef struct StringRec* String; typedef struct ObjectRec* Object; +typedef struct ScopeRec* Scope; typedef struct TypeRec* Type; typedef struct ItemRec* Item; typedef struct CtxRec* Ctx; typedef struct StringRec StringRec; typedef struct ObjectRec ObjectRec; +typedef struct ScopeRec ScopeRec; typedef struct TypeRec TypeRec; typedef struct ItemRec ItemRec; typedef struct CtxRec CtxRec; @@ -69,6 +71,12 @@ struct StringRec { char text[0]; }; +struct ScopeRec { + Scope next; // next in scope stack + Object first; // first object in this scope + u32 level; // height in stack (0 == globals, ...) +}; + // ------------------------------------------------------------------ struct ObjectRec { @@ -86,7 +94,7 @@ enum { // value oConst, // const value oGlobal, // global offset oVar, // frame offset - oParam, // frame offset + oParam, // param slot oField, // record offset oType, // type-desc-ptr oFunc, // address @@ -163,8 +171,8 @@ struct CtxRec { String strtab; // TODO: hashtable Object typetab; // TODO: hashtable - Object symtab; // TODO: hashtable, globals, functions - Object scope; + Scope scope; // scope stack + ScopeRec global; Type type_void; Type type_byte; @@ -184,6 +192,7 @@ struct CtxRec { void gen_prologue(Ctx ctx, Object fn); void gen_epilogue(Ctx ctx, Object fn); void gen_return(Ctx ctx, Item x); +void gen_add_op(Ctx ctx, u32 op, Item x, Item y); String mkstring(Ctx ctx, const char* text, u32 len) { String str = ctx->strtab; @@ -240,6 +249,8 @@ void init_ctx(Ctx ctx) { ctx->type_int32 = mktype(ctx, "i32", 3, tInt32, 4); ctx->type_nil = mktype(ctx, "nil", 3, tNil, 4); ctx->type_string = mktype(ctx, "str", 3, tString, 8); + + ctx->scope = &(ctx->global); } bool sametype(Type a, Type b) { @@ -565,16 +576,46 @@ void require(Ctx ctx, token_t tok) { next(ctx); } +// Look for an identifier in the scope stack Object find(Ctx ctx, String str) { - Object obj = ctx->symtab; // scope? - while (obj != nil) { - if (obj->name == str) { - return obj; + Scope scope = ctx->scope; + while (scope != nil) { + Object obj = scope->first; + while (obj != nil) { + if (obj->name == str) { + return obj; + } + obj = obj->next; } + scope = scope->next; } return nil; } +void make_global(Ctx ctx, Object obj) { + obj->next = ctx->global.first; + ctx->global.first = 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) { + Scope scope = malloc(sizeof(ScopeRec)); + scope->level = ctx->scope->level + 1; + scope->next = ctx->scope; + scope->first = obj; + ctx->scope = scope; + // XXX lazy scopes +} + +void pop_scope(Ctx ctx) { + if (ctx->scope->level == 0) { + error(ctx, "cannot pop the global scope"); + } + ctx->scope = ctx->scope->next; + // XXX delete? +} + void setitem(Item itm, u32 kind, Type type, u32 r, u32 a, u32 b) { itm->kind = kind; itm->flags = 0; @@ -610,11 +651,15 @@ void parse_factor(Ctx ctx, Item x) { if (obj == nil) { error(ctx, "unknown identifier '%s'", str->text); } - error(ctx, "unsupported identifier"); + if (obj->kind == oParam) { + setitem(x, iParam, obj->type, 0, obj->value, 0); + } else { + error(ctx, "unsupported identifier"); // .ident .ident ... // [ explist ] (array) // ( explist ) (fncall) // const + } } next(ctx); } @@ -635,13 +680,21 @@ void parse_simple_expr(Ctx ctx, Item x) { next(ctx); } else if (ctx->tok == tMINUS) { negate = true; + error(ctx, "unsupported"); } parse_term(ctx, x); - while ((ctx->tok == tPLUS) || (ctx->tok == tMINUS) || (ctx->tok == tOR)) { - u32 op = ctx->tok; - next(ctx); - ItemRec y; - parse_term(ctx, &y); + while (true) { + if ((ctx->tok == tPLUS) || (ctx->tok == tMINUS)) { + u32 op = ctx->tok; + next(ctx); + ItemRec y; + parse_term(ctx, &y); + gen_add_op(ctx, op, x, &y); + } else if (ctx->tok == tOR) { + error(ctx, "unsupported"); + } else { + break; + } } } @@ -681,9 +734,11 @@ Type parse_type(Ctx ctx) { void parse_function_body(Ctx ctx, Object fn) { gen_prologue(ctx, fn); + push_scope(ctx, fn->first); while (true) { if (ctx->tok == tCBRACE) { next(ctx); + pop_scope(ctx); gen_epilogue(ctx, fn); return; } else if (ctx->tok == tRETURN) { @@ -776,42 +831,34 @@ void parse_function(Ctx ctx) { } // Look for an existing declaration or definintion of this function - // and if it exists, ensure that we are in argeement with it - Object obj = ctx->symtab; - while (obj != nil) { - if (obj->name == fname) { - if (obj->kind != tFunc) { - error(ctx, "redefining '%s' as function", fname->text); - } - if (!isdef) { - error(ctx, "redeclared function '%s'", fname->text); - } - if (obj->flags & ofDefined) { - error(ctx, "redefined function '%s'", fname->text); - } - if (ftype != obj->type->base) { - error(ctx, "function definition mismatch for '%s' (return type)", fname->text); - } - if (obj->type->len != n) { - error(ctx, "function definition mismatch for '%s' (parameter count)", fname->text); - } - Object pa = first; - Object pb = obj->type->first; - u32 i = 1; - while ((pa != nil) && (pb != nil)) { - if (!sametype(pa->type, pb->type)) { - error(ctx, "function definition mismatch for '%s' (parameter #%u)", fname->text, i); - } - pa = pa->next; - pb = pb->next; + Object obj = find(ctx, fname); + if (obj != nil) { + // such a named identifier exists + // check to see if we are in agreement with it + if (obj->kind != oFunc) { + error(ctx, "redefining '%s' as function", fname->text); + } + if (isdef && (obj->flags & ofDefined)) { + error(ctx, "redefined function '%s'", fname->text); + } + if (ftype != obj->type->base) { + error(ctx, "func '%s' return type differs from decl", fname->text); + } + if (obj->type->len != n) { + error(ctx, "func '%s' parameter count differs from decl", fname->text); + } + Object pa = first; + Object pb = obj->type->first; + u32 i = 1; + while ((pa != nil) && (pb != nil)) { + if (!sametype(pa->type, pb->type)) { + error(ctx, "func '%s' param %u differs from decl", fname->text, i); } - break; + pa = pa->next; + pb = pb->next; } - obj = obj->next; - } - - // if there was no existing record of this function, create one now - if (obj == nil) { + } else { + // if there was no existing record of this function, create one now Type type = malloc(sizeof(TypeRec)); obj = malloc(sizeof(ObjectRec)); @@ -822,7 +869,7 @@ void parse_function(Ctx ctx) { type->len = n; type->size = 0; - obj->kind = oType; //?? + obj->kind = oFunc; obj->flags = 0; obj->value = 0; obj->next = nil; @@ -830,8 +877,7 @@ void parse_function(Ctx ctx) { obj->type = type; obj->name = fname; - obj->next = ctx->symtab; - ctx->symtab = obj; + make_global(ctx, obj); } // handle definition if it is one @@ -866,6 +912,27 @@ void parse_program(Ctx ctx) { } // ================================================================ +u32 get_reg_tmp(Ctx ctx) { + u32 n = 8; + while (n < 12) { + if (!(ctx->regbits & (1 << n))) { + ctx->regbits |= (1 << n); + printf("GET REG %u\n", n); + return n; + } + n++; + } + error(ctx, "cannot allocate register"); + return 0; +} + +void put_reg(Ctx ctx, u32 r) { + printf("PUT REG %u\n", r); + if (!(ctx->regbits & (1 << r))) { + error(ctx, "freeing non-allocated register %u\n", r); + } + ctx->regbits = ctx->regbits & (~(1 << r)); +} void emit(Ctx ctx, u32 ins) { ctx->code[ctx->pc / 4] = ins; @@ -875,6 +942,7 @@ void emit(Ctx ctx, u32 ins) { enum { R0 = 0, R1 = 1, R2 = 2, R3 = 3, R4 = 4, R5 = 5, R6 = 6, R7 = 7, R8 = 9, R9 = 9, R10 = 10, R11 = 11, MT = 12, SB = 13, SP = 14, LR = 15, + TMP = 16 }; enum { MOV = 0x0000, LSL = 0x0001, ASR = 0x0002, ROR = 0x0003, @@ -892,6 +960,9 @@ void emit_op(Ctx ctx, u32 op, u32 a, u32 b, u32 c) { void emit_opi(Ctx ctx, u32 op, u32 a, u32 b, u32 n) { emit(ctx, ((0x4000 | op) << 16) | (a << 24) | (b << 20) | (n & 0xffff)); } + +// mov (load immediate) using the appropriate one or two +// instruction form for the immediate argument void emit_mov(Ctx ctx, u32 a, u32 n) { u32 m = n >> 16; if (m == 0) { @@ -906,6 +977,25 @@ void emit_mov(Ctx ctx, u32 a, u32 n) { } } +// immediate op, using a temporary register and register op if the +// immediate argument does not fit the single instruction form +void emit_opi_n(Ctx ctx, u32 op, u32 a, u32 b, u32 n) { + u32 m = n >> 16; + if (m == 0) { + emit_opi(ctx, op, a, b, n); + } else if (m == 0xFFFF) { + emit_opi(ctx, op | 0x1000, a, b, n); + } else { + u32 t0 = get_reg_tmp(ctx); + emit_opi(ctx, MHI, t0, 0, m); + if ((n & 0xFFFF) != 0) { + emit_opi(ctx, IOR, t0, t0, n); + } + emit_op(ctx, op, a, b, t0); + put_reg(ctx, t0); + } +} + enum { LDW = 8, LDB = 9, STW = 10, STB = 11 }; @@ -950,51 +1040,105 @@ void print_item(Item x) { x->type, x->r, x->a, x->b); } -u32 get_reg_tmp(Ctx ctx) { - u32 n = 8; - while (n < 12) { - if (!(ctx->regbits & (1 << n))) { - ctx->regbits |= (1 << n); - return n; - } - n++; +// check to see if the last emitted instruction +// loaded a particular register and if so, patch +// it to load a different register +bool patch_last_load(Ctx ctx, u32 oldr, u32 newr) { + u32 ins = ctx->code[(ctx->pc - 4) / 4]; + u32 n = ins >> 29; + if ((n == 0b101) || (n == 0b110) || (n == 0b111)) { + // store and branch instructions can be ignored + return false; } - error(ctx, "cannot allocate register"); - return 0; -} - -void put_reg(Ctx ctx, u32 r) { - ctx->regbits = ctx->regbits & (~(1 << r)); + if (((ins >> 24) & 15) != oldr) { + return false; + } + ctx->code[(ctx->pc - 4) / 4] = (ins & 0xF0FFFFFF) | (newr << 24); + return true; } -void gen_load(Ctx ctx, Item x, u32 r) { +// load the value of an item into a specific register +void gen_load_reg(Ctx ctx, Item x, u32 r) { if (x->kind == iReg) { if (x->r != r) { - emit_op(ctx, MOV, r, 0, x->r); + if (patch_last_load(ctx, x->r, r)) { + put_reg(ctx, x->r); + } else { + emit_op(ctx, MOV, r, 0, x->r); + put_reg(ctx, x->r); + } } } else if (x->kind == iConst) { emit_mov(ctx, r, x->a); + } else if (x->kind == iParam) { + emit_mem(ctx, LDW, r, SP, 4 + x->a * 4); } else { error(ctx, "gen_load failed"); } + x->kind = iReg; + x->r = r; +} + +// convert an item to value-in-register format +// if it's not already in that format +void gen_load(Ctx ctx, Item x) { + if (x->kind != iReg) { + gen_load_reg(ctx, x, get_reg_tmp(ctx)); + } } void gen_return(Ctx ctx, Item x) { if (x->type != ctx->type_void) { - gen_load(ctx, x, R0); + gen_load_reg(ctx, x, R0); } // XXX: branch to epilogue } +void gen_add_op(Ctx ctx, u32 op, Item x, Item y) { + if (op == tPLUS) { + op = ADD; + } else { + op = SUB; + } + if ((x->kind == iConst) && (y->kind == iConst)) { + // XC = XC op YC + if (op == ADD) { + x->a = x->a + y->a; + } else { + x->a = x->a - y->a; + } + } else if (y->kind == iConst) { + // XR = XR op YC + gen_load(ctx, x); + if (y->a != 0) { + emit_opi_n(ctx, op, x->r, x->r, y->a); + } + } else { + // XR = XR op YR + gen_load(ctx, x); + gen_load(ctx, y); + emit_op(ctx, op, x->r, x->r, y->r); + put_reg(ctx, y->r); + } +} + void gen_prologue(Ctx ctx, Object fn) { fn->value = ctx->pc; - emit_opi(ctx, SUB, SP, SP, 4 + fn->type->size); + emit_opi(ctx, SUB, SP, SP, 4 + fn->type->len * 4); emit_mem(ctx, STW, LR, SP, 0); + + Object param = fn->first; + u32 r = 0; + while (param != nil) { + emit_mem(ctx, STW, r, SP, (r + 1) * 4); + r++; + param = param->next; + } } void gen_epilogue(Ctx ctx, Object fn) { emit_mem(ctx, LDW, LR, SP, 0); - emit_opi(ctx, ADD, SP, SP, 4 + fn->type->size); + emit_opi(ctx, ADD, SP, SP, 4 + fn->type->len * 4); emit_br(ctx, AL, LR); } @@ -1008,23 +1152,20 @@ void gen_end(Ctx ctx) { ctx->code[0] |= (ctx->pc - 4) >> 2; // patch branch at 0 String str = mkstring(ctx, "start", 5); - Object obj = ctx->symtab; + Object obj = find(ctx, str); while (obj != nil) { - if (obj->name == str) { - if (obj->type->kind != tFunc) { - error(ctx, "'start' is not a function\n"); - } - if (obj->first != nil) { - error(ctx, "'start' must have no parameters\n"); - } - emit_mov(ctx, 14, 0x100000); // MOV SP, RAMTOP - emit_bi(ctx, AL|L, -((ctx->pc + 4 - obj->value) >> 2)); // BL start - emit_mov(ctx, 1, 0xFFFF0000); // MOV R1, IOBASE - emit_mem(ctx, STW, 0, 1, 0x100); // SW R0, [R1, 0x100] - emit_br(ctx, AL, -1); // B . - return; + if (obj->type->kind != tFunc) { + error(ctx, "'start' is not a function\n"); } - obj = obj->next; + if (obj->first != nil) { + error(ctx, "'start' must have no parameters\n"); + } + emit_mov(ctx, 14, 0x100000); // MOV SP, RAMTOP + emit_bi(ctx, AL|L, -((ctx->pc + 4 - obj->value) >> 2)); // BL start + emit_mov(ctx, 1, 0xFFFF0000); // MOV R1, IOBASE + emit_mem(ctx, STW, 0, 1, 0x100); // SW R0, [R1, 0x100] + emit_br(ctx, AL, -1); // B . + return; } error(ctx, "no 'start' function\n"); } diff --git a/test/minimal.tl b/test/minimal.tl @@ -1,5 +1,10 @@ + +func test(a i32, b i32, c i32) i32 { + return a + b + c + 13 + a + 17 + 0x31337AAA; +} + func start() i32 { - return 42; + return 42 + 1; }