commit f9791dc75627127d59280d457c359413dc5724f3
parent 57175a27ad794a246459b553a67f153384079ed4
Author: Brian Swetland <swetland@frotz.net>
Date: Thu, 5 Mar 2020 20:12:54 -0800
compiler progress
Diffstat:
6 files changed, 408 insertions(+), 37 deletions(-)
diff --git a/Makefile b/Makefile
@@ -7,15 +7,15 @@ clean:
CFLAGS := -Wall -O2 -g
CC := gcc
-bin/tlc: src/tlc.c
+bin/tlc: src/tlc.c src/risc5dis.c out/risc5ins.h
@mkdir -p bin
- $(CC) -o $@ $(CFLAGS) src/tlc.c
+ $(CC) -o $@ $(CFLAGS) src/tlc.c src/risc5dis.c
bin/fs: src/fs.c src/fs.h
@mkdir -p bin
$(CC) -o $@ $(CFLAGS) src/fs.c
-bin/r5d: src/r5d.c src/risc5dis.c
+bin/r5d: src/r5d.c src/risc5dis.c out/risc5ins.h
@mkdir -p bin
$(CC) -o $@ $(CFLAGS) src/r5d.c src/risc5dis.c
@@ -31,9 +31,3 @@ out/risc5ins.h: src/risc5ins.txt bin/mkinstab
@mkdir -p out
bin/mkinstab < src/risc5ins.txt > $@
-src/risc5dis.c: src/risc5.h out/risc5ins.h
-src/risc5emu.c: src/risc5emu.h src/risc5.h
-src/risc5emu-fp.c: src/risc5emu-fp.h
-src/r5d.c: src/risc5.h
-src/r5e.c: src/risc5emu.h
-
diff --git a/docs/tlc.bnf b/docs/tlc.bnf
@@ -61,7 +61,7 @@ ident := ( letter | "_" ) | { letter | digit | "_" }
letter := "A" .. "Z" | "a" .. "z"
-statement := [ assignment | if | while | for | break | return ]
+statement := [ assignment | if | while | for | break | return | expr ";" | ";" ]
assignment := designator "=" expr ";"
diff --git a/src/r5e.c b/src/r5e.c
@@ -9,7 +9,7 @@ uint32_t data[] = {
0x4000FFD2, // mov r0, 0xFFD2
0x51000100, // mov r1, 0xffff0100
0xA0100000, // sw r0, [r1, 0]
- 0xE7FFFFFE, // b -3
+ 0xE7FFFFFF, // b -1
};
int main(int argc, char** argv) {
diff --git a/src/risc5ins.txt b/src/risc5ins.txt
@@ -20,10 +20,10 @@
0111aaaabbbb1001ssssssssssssssss sbc %a, %b, %s
0110aaaabbbb1010nnnnnnnnnnnnnnnn uml %a, %b, %n
0111aaaabbbb1010ssssssssssssssss uml %%a, %b, %s
-1000aaaabbbbmmmmmmmmmmmmmmmmmmmm lw %a, [%b, %m]
-1001aaaabbbbmmmmmmmmmmmmmmmmmmmm lb %a, [%b, %m]
-1010aaaabbbbmmmmmmmmmmmmmmmmmmmm sw %a, [%b, %m]
-1011aaaabbbbmmmmmmmmmmmmmmmmmmmm sb %a, [%b, %m]
+1000aaaabbbbmmmmmmmmmmmmmmmmmmmm ldw %a, [%b, %m]
+1001aaaabbbbmmmmmmmmmmmmmmmmmmmm ldb %a, [%b, %m]
+1010aaaabbbbmmmmmmmmmmmmmmmmmmmm stw %a, [%b, %m]
+1011aaaabbbbmmmmmmmmmmmmmmmmmmmm stb %a, [%b, %m]
11000111-------------------1---- rti
11001111------------------1----0 sti
11001111------------------1----1 cli
diff --git a/src/tlc.c b/src/tlc.c
@@ -1,4 +1,4 @@
-// Copyright 2020, Brian Swetland <swetland@frotz.net>
+// Copyright 2020, Brian Swetland <swetland@frotz.net>
// Licensed under the Apache License, Version 2.0.
#include <stdio.h>
@@ -24,11 +24,14 @@ typedef enum {
tEOF, tEOL,
tDOT, tCOMMA, tCOLON, tSEMI, tBANG, tOBRACK, tCBRACK,
tOPAREN, tCPAREN, tOBRACE, tCBRACE, tASSIGN,
- tPLUS, tMINUS, tSTAR, tSLASH, tAMP, tPIPE, tCARET,
- tAND, tOR, tEQ, tGT, tLT, tGE, tLE, tNE,
+ tPLUS, tMINUS, tSTAR, tSLASH, tPERCENT, tAMP, tPIPE, tCARET,
+ tAND, tOR,
+ // comparisons (keep EQ/NE first/last)
+ tEQ, tGT, tGE, tLT, tLE, tNE,
tINCR, tDECR,
tVAR, tSTRUCT, tFUNC, tRETURN, tIF, tELSE,
tWHILE, tFOR, tBREAK, tCONTINUE, tSWITCH, tCASE,
+ tTRUE, tFALSE, tNIL,
tNAME, tNUMBER, tSTRING,
NUMTOKENS,
} token_t;
@@ -37,11 +40,13 @@ char *tnames[] = {
"<EOF>", "<EOL>",
".", ",", ":", ";", "!", "[", "]",
"(", ")","{", "}", "=",
- "+", "-", "*", "/", "&", "|", "^",
- "&&", "||", "==", ">", "<", ">=", "<=", "!=",
+ "+", "-", "*", "/", "%", "&", "|", "^",
+ "&&", "||",
+ "==", ">", ">=", "<", "<=", "!=",
"++", "--",
"var", "struct", "func", "return", "if", "else",
"while", "for", "break", "switch", "case",
+ "true", "false", "nil",
"<NAME>", "<NUMBER>", "<STRING>",
};
@@ -77,12 +82,15 @@ struct ObjectRec {
};
// Object Kind IDs
-enum {
- oConst,
- oVar,
- oParam,
- oField,
- oType,
+enum { // value
+ oConst, // const value
+ oGlobal, // global offset
+ oVar, // frame offset
+ oParam, // frame offset
+ oField, // record offset
+ oType, // type-desc-ptr
+ oFunc, // address
+ oScope, // scope depth
};
// Object Flags
@@ -97,7 +105,7 @@ struct TypeRec {
Object obj; // if we're non-anonymous
Object first; // list of Params or Fields
Type base; // Pointer-to, Func-return, or Array-elem
- u32 len; // of Array
+ u32 len; // of Array, num of Params
u32 size; // of Type in Memory
};
@@ -164,8 +172,18 @@ struct CtxRec {
Type type_int32;
Type type_nil;
Type type_string;
+
+ u32 code[8192];
+ u32 pc;
+
+ u32 regbits;
+
+ u32 xref[8192];
};
+void gen_prologue(Ctx ctx, Object fn);
+void gen_epilogue(Ctx ctx, Object fn);
+void gen_return(Ctx ctx, Item x);
String mkstring(Ctx ctx, const char* text, u32 len) {
String str = ctx->strtab;
@@ -365,15 +383,18 @@ token_t next_word(Ctx ctx, const char* str, size_t len) {
case 3:
if (streq(str, len, "for", 3)) return ctx->tok = tFOR;
if (streq(str, len, "var", 3)) return ctx->tok = tVAR;
+ if (streq(str, len, "nil", 3)) return ctx->tok = tNIL;
break;
case 4:
if (streq(str, len, "case", 4)) return ctx->tok = tCASE;
if (streq(str, len, "func", 4)) return ctx->tok = tFUNC;
if (streq(str, len, "else", 4)) return ctx->tok = tELSE;
+ if (streq(str, len, "true", 4)) return ctx->tok = tTRUE;
break;
case 5:
if (streq(str, len, "break", 5)) return ctx->tok = tBREAK;
if (streq(str, len, "while", 5)) return ctx->tok = tWHILE;
+ if (streq(str, len, "false", 5)) return ctx->tok = tFALSE;
break;
case 6:
if (streq(str, len, "switch", 6)) return ctx->tok = tSWITCH;
@@ -401,6 +422,7 @@ token_t _next(Ctx ctx) {
ctx->linenumber++;
ctx->sptr++;
ctx->line = ctx->sptr;
+ ctx->xref[ctx->pc / 4] = ctx->linenumber;
if (ctx->flags & 1) return ctx->tok = tEOL;
continue;
case ' ':
@@ -469,6 +491,7 @@ token_t _next(Ctx ctx) {
case '+': if (s[1] == '+') TOKEN2(tINCR) else TOKEN(tPLUS);
case '-': if (s[1] == '-') TOKEN2(tDECR) else TOKEN(tMINUS);
case '*': TOKEN(tSTAR);
+ case '%': TOKEN(tPERCENT);
case '^': TOKEN(tCARET);
case '=': if (s[1] == '=') TOKEN2(tEQ) else TOKEN(tASSIGN);
case '&': if (s[1] == '&') TOKEN2(tAND) else TOKEN(tAMP);
@@ -542,6 +565,98 @@ void require(Ctx ctx, token_t tok) {
next(ctx);
}
+Object find(Ctx ctx, String str) {
+ Object obj = ctx->symtab; // scope?
+ while (obj != nil) {
+ if (obj->name == str) {
+ return obj;
+ }
+ }
+ return nil;
+}
+
+void setitem(Item itm, u32 kind, Type type, u32 r, u32 a, u32 b) {
+ itm->kind = kind;
+ itm->flags = 0;
+ itm->type = type;
+ itm->r = r;
+ itm->a = a;
+ itm->b = b;
+}
+
+// ================================================================
+
+void parse_expr(Ctx ctx, Item x);
+
+void parse_factor(Ctx ctx, Item x) {
+ if (ctx->tok == tNUMBER) {
+ setitem(x, iConst, ctx->type_int32, 0, ctx->num, 0);
+ } else if (ctx->tok == tSTRING) {
+ error(ctx, "unsupported string const");
+ } else if (ctx->tok == tTRUE) {
+ setitem(x, iConst, ctx->type_bool, 0, 1, 0);
+ } else if (ctx->tok == tFALSE) {
+ setitem(x, iConst, ctx->type_bool, 0, 0, 0);
+ } else if (ctx->tok == tNIL) {
+ setitem(x, iConst, ctx->type_nil, 0, 0, 0);
+ } else if (ctx->tok == tOPAREN) {
+ next(ctx);
+ parse_expr(ctx, x);
+ require(ctx, tCPAREN);
+ return;
+ } else if (ctx->tok == tNAME) {
+ String str = mkstring(ctx, ctx->tmp, strlen(ctx->tmp));
+ Object obj = find(ctx, str);
+ if (obj == nil) {
+ error(ctx, "unknown identifier '%s'", str->text);
+ }
+ error(ctx, "unsupported identifier");
+ // .ident .ident ...
+ // [ explist ] (array)
+ // ( explist ) (fncall)
+ // const
+ }
+ next(ctx);
+}
+
+void parse_term(Ctx ctx, Item x) {
+ parse_factor(ctx, x);
+ while ((ctx->tok == tSTAR) || (ctx->tok == tSLASH) || (ctx->tok == tPERCENT) || (ctx->tok == tOR)) {
+ u32 op = ctx->tok;
+ next(ctx);
+ ItemRec y;
+ parse_factor(ctx, &y);
+ }
+}
+
+void parse_simple_expr(Ctx ctx, Item x) {
+ bool negate = false;
+ if (ctx->tok == tPLUS) {
+ next(ctx);
+ } else if (ctx->tok == tMINUS) {
+ negate = true;
+ }
+ 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);
+ }
+}
+
+void parse_expr(Ctx ctx, Item x) {
+ parse_simple_expr(ctx, x);
+ while ((ctx->tok >= tEQ) && (ctx->tok <= tNE)) {
+ u32 op = ctx->tok;
+ next(ctx);
+ error(ctx, "unsupported relop");
+ ItemRec y;
+ parse_simple_expr(ctx, &y);
+ }
+}
+
+
String parse_name(Ctx ctx, const char* what) {
if (ctx->tok != tNAME) {
error(ctx, "expected %s, found %s", what, tnames[ctx->tok]);
@@ -564,8 +679,34 @@ Type parse_type(Ctx ctx) {
return nil;
}
-void parse_function_body(Ctx ctx) {
- error(ctx, "unsupported");
+void parse_function_body(Ctx ctx, Object fn) {
+ gen_prologue(ctx, fn);
+ while (true) {
+ if (ctx->tok == tCBRACE) {
+ next(ctx);
+ gen_epilogue(ctx, fn);
+ return;
+ } else if (ctx->tok == tRETURN) {
+ next(ctx);
+ ItemRec x;
+ if (ctx->tok == tSEMI) {
+ if (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(fn->type->base, x.type)) {
+ error(ctx, "return types do not match");
+ }
+ require(ctx, tSEMI);
+ }
+ gen_return(ctx, &x);
+ } else {
+ expected(ctx, "statement");
+ }
+ }
}
Object parse_param(Ctx ctx, String fname, u32 n, Object first, Object last) {
@@ -639,7 +780,7 @@ void parse_function(Ctx ctx) {
Object obj = ctx->symtab;
while (obj != nil) {
if (obj->name == fname) {
- if (obj->type->kind != tFunc) {
+ if (obj->kind != tFunc) {
error(ctx, "redefining '%s' as function", fname->text);
}
if (!isdef) {
@@ -651,6 +792,9 @@ void parse_function(Ctx ctx) {
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;
@@ -661,9 +805,6 @@ void parse_function(Ctx ctx) {
pa = pa->next;
pb = pb->next;
}
- if ((pa != nil) || (pb != nil)) {
- error(ctx, "function definition mismatch for '%s' (parameter count mismatch)", fname->text);
- }
break;
}
obj = obj->next;
@@ -678,7 +819,7 @@ void parse_function(Ctx ctx) {
type->obj = obj;
type->first = first;
type->base = ftype;
- type->len = 0;
+ type->len = n;
type->size = 0;
obj->kind = oType; //??
@@ -696,7 +837,7 @@ void parse_function(Ctx ctx) {
// handle definition if it is one
if (isdef) {
obj->flags |= ofDefined;
- parse_function_body(ctx);
+ parse_function_body(ctx, obj);
}
}
@@ -717,15 +858,242 @@ void parse_program(Ctx ctx) {
parse_global_var(ctx);
break;
case tEOF:
- break;
+ return;
default:
expected(ctx, "func or var");
}
}
}
+// ================================================================
+
+void emit(Ctx ctx, u32 ins) {
+ ctx->code[ctx->pc / 4] = ins;
+ ctx->pc = ctx->pc + 4;
+}
+
+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,
+};
+enum {
+ MOV = 0x0000, LSL = 0x0001, ASR = 0x0002, ROR = 0x0003,
+ AND = 0x0004, ANN = 0x0005, IOR = 0x0006, XOR = 0x0007,
+ ADD = 0x0008, SUB = 0x0009, MUL = 0x000A, DIV = 0x000B,
+ FAD = 0x000C, FSB = 0x000D, FML = 0x000E, FDV = 0x000F,
+ ADC = 0x2008, SBC = 0x2009, UML = 0x200A,
+ MHI = 0x2000,
+ MOV_H = 0x2000,
+ MOV_CC = 0x3000,
+};
+void emit_op(Ctx ctx, u32 op, u32 a, u32 b, u32 c) {
+ emit(ctx, (op << 16) | (a << 24) | (b << 20) | 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));
+}
+void emit_mov(Ctx ctx, u32 a, u32 n) {
+ u32 m = n >> 16;
+ if (m == 0) {
+ emit_opi(ctx, MOV, a, 0, n);
+ } else if (m == 0xFFFF) {
+ emit_opi(ctx, MOV | 0x1000, a, 0, n);
+ } else {
+ emit_opi(ctx, MHI, a, 0, m);
+ if ((n & 0xFFFF) != 0) {
+ emit_opi(ctx, IOR, a, a, n);
+ }
+ }
+}
+
+enum {
+ LDW = 8, LDB = 9, STW = 10, STB = 11
+};
+void emit_mem(Ctx ctx, u32 op, u32 a, u32 b, u32 off) {
+ emit(ctx, (op << 28) | (a << 24) | (b << 20) | (off & 0xfffff));
+}
+
+enum {
+ MI = 0, EQ = 1, CS = 2, VS = 3, LS = 4, LT = 5, LE = 6, AL = 7,
+ PL = 8, NE = 9, CC = 10, VC = 11, HI = 12, GE = 13, GT = 14, NV = 15,
+ L = 0x10,
+};
+void emit_br(Ctx ctx, u32 op, u32 r) {
+ emit(ctx, ((0xC0 | op) << 24) | r);
+}
+void emit_bi(Ctx ctx, u32 op, u32 off) {
+ emit(ctx, ((0xE0 | op) << 24) | (off & 0xffffff));
+}
+
+// ================================================================
+
+const char* item_kind(u32 n) {
+ if (n == iConst) {
+ return "const";
+ } else if (n == iVar) {
+ return "var";
+ } else if (n == iParam) {
+ return "param";
+ } else if (n == iReg) {
+ return "reg";
+ } else if (n == iRegInd) {
+ return "regind";
+ } else if (n == iCond) {
+ return "cond";
+ } else {
+ return "???";
+ }
+}
+void print_item(Item x) {
+ fprintf(stderr, "ITEM(%s)%s t=%p r=%u a=%08x b=%08x\n",
+ item_kind(x->kind), (x->flags & ifReadOnly) ? " RO" : "",
+ 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++;
+ }
+ error(ctx, "cannot allocate register");
+ return 0;
+}
+
+void put_reg(Ctx ctx, u32 r) {
+ ctx->regbits = ctx->regbits & (~(1 << r));
+}
+
+void gen_load(Ctx ctx, Item x, u32 r) {
+ if (x->kind == iReg) {
+ if (x->r != r) {
+ emit_op(ctx, MOV, r, 0, x->r);
+ }
+ } else if (x->kind == iConst) {
+ emit_mov(ctx, r, x->a);
+ } else {
+ error(ctx, "gen_load failed");
+ }
+}
+
+void gen_return(Ctx ctx, Item x) {
+ if (x->type != ctx->type_void) {
+ gen_load(ctx, x, R0);
+ }
+ // XXX: branch to epilogue
+}
+
+void gen_prologue(Ctx ctx, Object fn) {
+ fn->value = ctx->pc;
+ emit_opi(ctx, SUB, SP, SP, 4 + fn->type->size);
+ emit_mem(ctx, STW, LR, SP, 0);
+}
+
+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_br(ctx, AL, LR);
+}
+
+
+void gen_start(Ctx ctx) {
+ // placeholder branch to init
+ emit_bi(ctx, AL, 0);
+}
+
+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;
+ 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;
+ }
+ obj = obj->next;
+ }
+ error(ctx, "no 'start' function\n");
+}
+
+void gen_write(Ctx ctx, const char* outname) {
+ int fd = open(outname, O_CREAT | O_TRUNC | O_WRONLY, 0644);
+ if (fd < 0) {
+ error(ctx, "cannot open '%s' to write", outname);
+ }
+ u32 n = 0;
+ while (n < ctx->pc) {
+ if (write(fd, ctx->code + (n/4), sizeof(u32)) != sizeof(u32)) {
+ error(ctx, "error writing '%s'", outname);
+ }
+ n += 4;
+ }
+ close(fd);
+}
+
+#include "risc5.h"
+
+void gen_listing(Ctx ctx, const char* listfn, const char* srcfn) {
+ FILE* fin = fopen(srcfn, "r");
+ if (fin == NULL) {
+ error(ctx, "cannot re-read '%s'\n", srcfn);
+ }
+ FILE* fout = fopen(listfn, "w");
+ if (fout == NULL) {
+ error(ctx, "cannot write '%s'\n", listfn);
+ }
+ u32 n = 0;
+ u32 line = 1;
+ char buf[1024];
+ while (n < ctx->pc) {
+ u32 ins = ctx->code[n/4];
+ if ((line < ctx->xref[n/4]) && fin) {
+ fprintf(fout, "\n");
+ while (line < ctx->xref[n/4]) {
+ if (fgets(buf, sizeof(buf), fin) == nil) {
+ fin = nil;
+ break;
+ }
+ u32 i = 0;
+ while (buf[i] != 0) {
+ if (buf[i] > ' ') {
+ fprintf(fout,"%s", buf);
+ break;
+ }
+ i++;
+ }
+ line++;
+ }
+ fprintf(fout, "\n");
+ }
+ risc5dis(n, ins, buf);
+ fprintf(fout, "%08x: %08x %s\n", n, ins, buf);
+ n += 4;
+ }
+ fclose(fout);
+ if (fin) {
+ fclose(fin);
+ }
+}
+
+// ================================================================
+
int main(int argc, char **argv) {
- const char *outname = "out.hex";
+ const char *outname = "out.bin";
+ const char *listname = "out.lst";
CtxRec ctx;
init_ctx(&ctx);
@@ -751,7 +1119,11 @@ int main(int argc, char **argv) {
} while (ctx.tok != tEOF);
printf("\n");
#else
+ gen_start(&ctx);
parse_program(&ctx);
+ gen_end(&ctx);
+ gen_write(&ctx, outname);
+ gen_listing(&ctx, listname, ctx.filename);
#endif
return 0;
diff --git a/test/minimal.tl b/test/minimal.tl
@@ -0,0 +1,5 @@
+
+func start() i32 {
+ return 42;
+}
+