compiler

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

commit 15461e954e6002136911b11b673498d24bc7e781
parent 491ad7f5ea782f032514aa0216b9c7ffc2d1a15d
Author: Brian Swetland <swetland@frotz.net>
Date:   Sun,  8 Mar 2020 21:12:11 -0700

compiler: function calls and test framework

- parse function calls, gen_param(), gen_call()
- improve argument handling (-o bin, -l lst, -A abort-on-error)
- fixup list for objects to support forward decl funcs
- codegen for modulus
- a simple test framework
- two initial tests

Diffstat:
MMakefile | 13++++++++++++-
Aruntest.sh | 46++++++++++++++++++++++++++++++++++++++++++++++
Msrc/tlc.c | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Atest/1000-return-42.log | 1+
Atest/1000-return-42.src | 3+++
Atest/1010-func-call.log | 1+
Atest/1010-func-call.src | 7+++++++
Dtest/hello.tl | 18------------------
Dtest/minimal.tl | 10----------
9 files changed, 219 insertions(+), 55 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,5 +1,5 @@ -all: bin/tlc bin/fs bin/r5d bin/r5e bin/mkinstab +all: bin/tlc bin/fs bin/r5d bin/r5e bin/mkinstab out/test/summary.txt clean: rm -rf bin out @@ -31,3 +31,14 @@ out/risc5ins.h: src/risc5ins.txt bin/mkinstab @mkdir -p out bin/mkinstab < src/risc5ins.txt > $@ +out/test/%.txt: test/%.src bin/tlc bin/r5d ./runtest.sh + @mkdir -p out/test + @rm -f $@ + @./runtest.sh $< $@ + +SRCTESTS := $(sort $(wildcard test/*.src)) +ALLTESTS := $(patsubst test/%.src,out/test/%.txt,$(SRCTESTS)) + +out/test/summary.txt: $(ALLTESTS) + @cat $(ALLTESTS) > out/test/summary.txt + diff --git a/runtest.sh b/runtest.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +## Copyright 2020, Brian Swetland <swetland@frotz.net> +## Licensed under the Apache License, Version 2.0. + +src="$1" +txt="$2" +bin="${txt%.txt}.bin" +lst="${txt%.txt}.lst" +log="${txt%.txt}.log" +gold="${src%.src}.log" + +echo "RUNTEST: $src: compiling..." +#echo "bin/tlc -o $bin -l $lst $src" +if bin/tlc -o "$bin" -l "$lst" "$src"; then + # success! + if [[ "$txt" == *"-err"* ]]; then + # but this was an error test, so... + echo "RUNTEST: $src: FAIL: compiler did not detect error" + echo "FAIL: $src" > "$txt" + else + echo "RUNTEST: $src: running..." + if bin/r5e "$bin" > "$log"; then + if diff "$log" "$gold"; then + echo "RUNTEST: $src: PASS" + echo "PASS: $src" > "$txt" + else + echo "RUNTEST: $src: FAIL: output differs from expected" + echo "FAIL: %src" > "$txt" + fi + else + echo "RUNTEST: $src: FAIL: emulator crashed" + echo "FAIL: %src" > "$txt" + fi + fi +else + # failure + if [[ "$txt" == *"-err"* ]]; then + # but this was an error test, so... + echo "PASS: $src" > "$txt" + else + echo "RUNTEST: $src: FAIL: compiler error" + echo "FAIL: $src" > "$txt" + fi +fi + diff --git a/src/tlc.c b/src/tlc.c @@ -112,6 +112,7 @@ struct ObjectRec { Object first; // list of... Type type; String name; + Fixup fixups; // forward func refs }; // Object Kind IDs @@ -175,6 +176,7 @@ enum { // r a b iReg, // regno iRegInd, // regno offset iCond, // ccode f-chain t-chain + iFunc, }; // Item Flags @@ -215,6 +217,9 @@ struct CtxRec { u32 xref[8192]; }; +#define cfVisibleEOL 1 +#define cfAbortOnError 2 + void gen_prologue(Ctx ctx, Object fn); void gen_epilogue(Ctx ctx, Object fn); void gen_return(Ctx ctx, Item x); @@ -224,6 +229,8 @@ 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); +void gen_param(Ctx ctx, u32 n, Item val); +void gen_call(Ctx, Item func); String mkstring(Ctx ctx, const char* text, u32 len) { String str = ctx->strtab; @@ -282,6 +289,7 @@ void init_ctx(Ctx ctx) { ctx->type_string = mktype(ctx, "str", 3, tString, 8); ctx->scope = &(ctx->global); + ctx->line = ""; } bool sametype(Type a, Type b) { @@ -326,8 +334,11 @@ void error(Ctx ctx, const char *fmt, ...) { va_end(ap); fprintf(stderr,"\n%.*s\n", len, ctx->line); - abort(); - //exit(1); + if (ctx->flags & cfAbortOnError) { + abort(); + } else { + exit(1); + } } void load(Ctx ctx, const char* filename) { @@ -466,7 +477,7 @@ token_t _next(Ctx ctx) { ctx->sptr++; ctx->line = ctx->sptr; ctx->xref[ctx->pc / 4] = ctx->linenumber; - if (ctx->flags & 1) return ctx->tok = tEOL; + if (ctx->flags & cfVisibleEOL) return ctx->tok = tEOL; continue; case ' ': case '\t': @@ -681,6 +692,13 @@ void add_scope_fixup(Ctx ctx, Scope scope) { scope->fixups = fixup; } +void add_object_fixup(Ctx ctx, Object obj) { + Fixup fixup = malloc(sizeof(FixupRec)); + fixup->next = obj->fixups; + fixup->pc = ctx->pc - 4; + obj->fixups = fixup; +} + void setitem(Item itm, u32 kind, Type type, u32 r, u32 a, u32 b) { itm->kind = kind; itm->flags = 0; @@ -694,7 +712,7 @@ void setitem(Item itm, u32 kind, Type type, u32 r, u32 a, u32 b) { void parse_expr(Ctx ctx, Item x); -void parse_primary_expr(Ctx ctx, Item x) { +void parse_operand(Ctx ctx, Item x) { if (ctx->tok == tNUMBER) { setitem(x, iConst, ctx->type_int32, 0, ctx->num, 0); } else if (ctx->tok == tSTRING) { @@ -718,17 +736,48 @@ void parse_primary_expr(Ctx ctx, Item x) { } if (obj->kind == oParam) { setitem(x, iParam, obj->type, 0, obj->value, 0); + } else if (obj->kind == oFunc) { + setitem(x, iFunc, obj->type, 0, 0, 0); } else { error(ctx, "unsupported identifier"); - // .ident .ident ... - // [ explist ] (array) - // ( explist ) (fncall) - // const } } next(ctx); } +void parse_primary_expr(Ctx ctx, Item x) { + parse_operand(ctx, x); + while (true) { + if (ctx->tok == tOPAREN) { + next(ctx); + if (x->kind != iFunc) { + error(ctx, "cannot call non-function"); + } + // TODO ptr-to-func + u32 n = 0; + Object param = x->type->first; + while (param != nil) { + if (n != 0) { + require(ctx, tCOMMA); + } + ItemRec y; + parse_expr(ctx, &y); + gen_param(ctx, n, &y); + param = param->next; + n++; + } + require(ctx, tCPAREN); + gen_call(ctx, x); + } else if (ctx->tok == tDOT) { + error(ctx, "unsupported field deref"); + } else if (ctx->tok == tOBRACK) { + error(ctx, "unsupported array deref"); + } else { + break; + } + } +} + void parse_unary_expr(Ctx ctx, Item x) { if (ctx->tok == tPLUS) { next(ctx); @@ -935,6 +984,7 @@ Object parse_param(Ctx ctx, String fname, u32 n, Object first, Object last) { param->first = nil; param->name = parse_name(ctx, "parameter name"); param->type = parse_type(ctx); + param->fixups = nil; Object obj = first; while (obj != nil) { @@ -970,7 +1020,6 @@ void parse_function(Ctx ctx) { n++; } } - require(ctx, tCPAREN); if ((ctx->tok != tSEMI) && (ctx->tok != tOBRACE)) { @@ -1035,13 +1084,19 @@ void parse_function(Ctx ctx) { obj->first = first; obj->type = type; obj->name = fname; + obj->fixups = nil; make_global(ctx, obj); } // handle definition if it is one if (isdef) { + // patch any forward references + gen_fixups(ctx, obj->fixups); + + // mark as defined and save entry address obj->flags |= ofDefined; + obj->value = ctx->pc; parse_function_body(ctx, obj); } } @@ -1087,6 +1142,11 @@ u32 get_reg_tmp(Ctx ctx) { void put_reg(Ctx ctx, u32 r) { //printf("PUT REG %u\n", r); + if (r < 8) { + // currently we don't strictly track r0..r7 + // they are used for function calls and returns + return; + } if (!(ctx->regbits & (1 << r))) { error(ctx, "freeing non-allocated register %u\n", r); } @@ -1112,6 +1172,7 @@ enum { MHI = 0x2000, MOV_H = 0x2000, MOV_CC = 0x3000, + MOD = 0x001B, // fake op for plumbing (DIV+MOV_H) }; void emit_op(Ctx ctx, u32 op, u32 a, u32 b, u32 c) { @@ -1211,11 +1272,11 @@ u32 add_op_to_ins(Ctx ctx, u32 op) { u32 mul_op_to_ins(Ctx ctx, u32 op) { if (op == tSTAR) { return MUL; } if (op == tSLASH) { return DIV; } + if (op == tPERCENT) { return MOD; } if (op == tLEFT) { return LSL; } if (op == tRIGHT) { return ASR; } if (op == tAMP) { return AND; } if (op == tANDNOT) { return ANN; } - // XXX tPERCENT error(ctx, "invalid mul-op"); return 0; } @@ -1285,6 +1346,29 @@ void gen_return(Ctx ctx, Item x) { add_scope_fixup(ctx, find_scope(ctx, sFunc)); } +void gen_param(Ctx ctx, u32 n, Item val) { + if (n > 7) { + error(ctx, "gen_param - too many parameters"); + } + gen_load_reg(ctx, val, n); +} + +void gen_call(Ctx ctx, Item x) { + if (x->type->obj->flags & ofDefined) { + u32 fnpc = x->type->obj->value; + emit_bi(ctx, AL|L, (fnpc - ctx->pc - 4) >> 2); + } else { + add_object_fixup(ctx, x->type->obj); + emit_bi(ctx, AL|L, 0); + } + // item becomes the return value + x->type = x->type->base; + x->kind = iReg; + x->r = R0; + x->a = 0; + x->b = 0; +} + void gen_add_op(Ctx ctx, u32 op, Item x, Item y) { op = add_op_to_ins(ctx, op); if ((x->kind == iConst) && (y->kind == iConst)) { @@ -1315,9 +1399,6 @@ void gen_add_op(Ctx ctx, u32 op, Item x, Item y) { } void gen_mul_op(Ctx ctx, u32 op, Item x, Item y) { - if (op == tPERCENT) { - error(ctx, "mod unsupported"); - } op = mul_op_to_ins(ctx, op); if ((x->kind == iConst) && (y->kind == iConst)) { // XC = XC op YC @@ -1325,6 +1406,8 @@ void gen_mul_op(Ctx ctx, u32 op, Item x, Item y) { x->a = x->a * y->a; } else if (op == DIV) { x->a = x->a / y->a; + } else if (op == MOD) { + x->a = x->a % y->a; } else if (op == LSL) { x->a = x->a << y->a; } else if (op == ASR) { @@ -1337,20 +1420,30 @@ void gen_mul_op(Ctx ctx, u32 op, Item x, Item y) { } else if (y->kind == iConst) { // XR = XR op YC gen_load(ctx, x); - if ((op == DIV) && (y->a == 0)) { + if (((op == DIV) || (op == MOD)) && (y->a == 0)) { error(ctx, "divide by zero"); } else if ((op == MUL) && (y->a == 1)) { return; // x * 1 = x } else if (((op == LSL) || (op == ASR)) && (y->a == 0)) { return; // shift-by-zero } - emit_opi_n(ctx, op, x->r, x->r, y->a); + if (op == MOD) { + emit_opi_n(ctx, DIV, x->r, x->r, y->a); + emit_op(ctx, MOV_H, x->r, 0, 0); + } else { + emit_opi_n(ctx, op, x->r, x->r, y->a); + } } else { // TODO runtime div-by-zero check // XR = XR op YR gen_load(ctx, x); gen_load(ctx, y); - emit_op(ctx, op, x->r, x->r, y->r); + if (op == MOD) { + emit_op(ctx, op, x->r, x->r, y->r); + emit_op(ctx, MOV_H, x->r, 0, 0); + } else { + emit_op(ctx, op, x->r, x->r, y->r); + } put_reg(ctx, y->r); } } @@ -1512,21 +1605,49 @@ void gen_listing(Ctx ctx, const char* listfn, const char* srcfn) { int main(int argc, char **argv) { const char *outname = "out.bin"; - const char *listname = "out.lst"; + const char *lstname = nil; + const char *srcname = nil; CtxRec ctx; init_ctx(&ctx); - - if (argc < 2) { - ctx.filename = "<commandline>"; - error(&ctx, "no file specified"); + ctx.filename = "<commandline>"; + + while (argc > 1) { + if (!strcmp(argv[1],"-o")) { + if (argc < 2) { + error(&ctx, "option -o requires argument"); + } + outname = argv[2]; + argc--; + argv++; + } else if (!strcmp(argv[1], "-l")) { + if (argc < 2) { + error(&ctx, "option -l requires argument"); + } + lstname = argv[2]; + argc--; + argv++; + } else if (!strcmp(argv[1], "-A")) { + ctx.flags |= cfAbortOnError; + } else if (argv[1][0] == '-') { + error(&ctx, "unknown option: %s", argv[1]); + } else { + if (srcname != nil) { + error(&ctx, "multiple source files disallowed"); + } else { + srcname = argv[1]; + } + } + argc--; + argv++; } - ctx.filename = argv[1]; - if (argc == 3) - outname = argv[2]; + if (srcname == nil) { + error(&ctx, "no file specified"); + } + ctx.filename = srcname; - load(&ctx, argv[1]); + load(&ctx, srcname); ctx.line = ctx.sptr; ctx.linenumber = 1; @@ -1542,7 +1663,9 @@ int main(int argc, char **argv) { parse_program(&ctx); gen_end(&ctx); gen_write(&ctx, outname); - gen_listing(&ctx, listname, ctx.filename); + if (lstname != nil) { + gen_listing(&ctx, lstname, ctx.filename); + } #endif return 0; diff --git a/test/1000-return-42.log b/test/1000-return-42.log @@ -0,0 +1 @@ +X 0000002a diff --git a/test/1000-return-42.src b/test/1000-return-42.src @@ -0,0 +1,3 @@ +func start() i32 { + return 42; +} diff --git a/test/1010-func-call.log b/test/1010-func-call.log @@ -0,0 +1 @@ +X 0000002a diff --git a/test/1010-func-call.src b/test/1010-func-call.src @@ -0,0 +1,7 @@ +func add(a i32, b i32) i32 { + return a + b; +} + +func start() i32 { + return add(20, 22); +} diff --git a/test/hello.tl b/test/hello.tl @@ -1,18 +0,0 @@ - -func boring() ; - -func add(a i32, b i32) i32; - -func add(a i32, b i32) i32 { - return a + b; -} - -func main(argc i32, argv string) { - var n i32 = 0; - - while n < 100 { - n = n + 1; - } - "hello cruel\n world!"; - return n - 1; -} diff --git a/test/minimal.tl b/test/minimal.tl @@ -1,10 +0,0 @@ - - -func test(a i32, b i32, c i32) i32 { - return a + b + c + 13 + a + 17 + 0x31337AAA; -} - -func start() i32 { - return 42 + 1; -} -