aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--bench/fib.bad12
-rw-r--r--bench/fib.py5
-rw-r--r--bench/life.bad77
-rw-r--r--bench/rule110.bad39
-rw-r--r--bench/rule110.py29
-rw-r--r--src/badlib.h28
-rw-r--r--src/compiler.c828
-rw-r--r--src/main.c35
-rw-r--r--src/semantic.c45
-rw-r--r--src/vm.c213
-rw-r--r--tests/compilation.bad121
-rw-r--r--tests/conditionals.bad4
-rw-r--r--tests/semantics.bad7
14 files changed, 1306 insertions, 141 deletions
diff --git a/Makefile b/Makefile
index c3ab930..83dc56d 100644
--- a/Makefile
+++ b/Makefile
@@ -26,9 +26,9 @@ BIN := $(BUILD_DIR)/$(TARGET)
26 26
27# Compiler and linker configuration. 27# Compiler and linker configuration.
28CC := cc 28CC := cc
29CFLAGS := -Wall -Wextra -pedantic -std=c11 -DBIN_NAME=\"$(TARGET)\" 29CFLAGS := -Wall -Wextra -std=c11 -DBIN_NAME=\"$(TARGET)\"
30CFLAGS += $(INC_FLAGS) 30CFLAGS += $(INC_FLAGS)
31LDFLAGS := 31LDFLAGS := -lm
32LDLIBS := 32LDLIBS :=
33RELEASE_CFLAGS := -DNDEBUG -O2 33RELEASE_CFLAGS := -DNDEBUG -O2
34DEBUG_CFLAGS := -DDEBUG -g -fsanitize=address,undefined 34DEBUG_CFLAGS := -DDEBUG -g -fsanitize=address,undefined
diff --git a/bench/fib.bad b/bench/fib.bad
new file mode 100644
index 0000000..ae7c729
--- /dev/null
+++ b/bench/fib.bad
@@ -0,0 +1,12 @@
1let n = 1000
2let a = 0.0
3let b = 1.0
4let i = 0
5while i < n {
6 let tmp = a + b
7 set a = b
8 set b = tmp
9 set i = i + 1
10}
11
12a
diff --git a/bench/fib.py b/bench/fib.py
new file mode 100644
index 0000000..ceb7406
--- /dev/null
+++ b/bench/fib.py
@@ -0,0 +1,5 @@
1n = 1000
2a, b = 0.0, 1.0
3for i in range(0, n):
4 a, b = b, a + b
5print(a)
diff --git a/bench/life.bad b/bench/life.bad
new file mode 100644
index 0000000..3ca0697
--- /dev/null
+++ b/bench/life.bad
@@ -0,0 +1,77 @@
1; This board is 8 * 8 = 64 cells.
2; let n_cells = 64
3; let board: int[64]
4; let new_board: int[64]
5; let stride = 8
6
7; This board is 32 * 32 = 1024 cells.
8let n_cells = 1024
9let board: int[1024]
10let new_board: int[1024]
11let stride = 32
12
13; Initialize glider
14set board[stride * 2 + 5] = 1
15set board[stride * 3 + 6] = 1
16set board[stride * 4 + 4] = 1
17set board[stride * 4 + 5] = 1
18set board[stride * 4 + 6] = 1
19
20let n_iter = 1000
21while n_iter > 0 {
22 set n_iter = n_iter - 1
23 ; Print the board.
24 {
25 let i = 0
26 while i < n_cells {
27 if i % stride == 0 {
28 println("")
29 }
30 if board[i] == 1 print("■ ")
31 else print("· ")
32 set i = i + 1
33 }
34 println("")
35 }
36
37 ; Update the board.
38 {
39 let i = 0
40 while i < n_cells {
41 let left = if i > 0 board[i - 1] else 0
42 let right = if i < n_cells - 1 board[i + 1] else 0
43 let top = if i >= stride board[i - stride] else 0
44 let topleft = if i >= stride board[i - stride - 1] else 0
45 let topright = if i >= stride board[i - stride + 1] else 0
46 let bot = if i <= n_cells - stride board[i + stride] else 0
47 let botleft = if i <= n_cells - stride board[i + stride - 1] else 0
48 let botright = if i <= n_cells - stride board[i + stride + 1] else 0
49
50 let neig = left
51 + right
52 + top
53 + bot
54 + topleft
55 + topright
56 + botleft
57 + botright
58
59 cond {
60 board[i] == 0 && neig == 3 = set new_board[i] = 1
61 board[i] == 1 && (neig == 2 || neig == 3) = set new_board[i] = 1
62 else = set new_board[i] = 0
63 }
64
65 set i = i + 1
66 }
67
68 ; Copy the new board.
69 {
70 let i = 0
71 while i < n_cells {
72 set board[i] = new_board[i]
73 set i = i + 1
74 }
75 }
76 }
77}
diff --git a/bench/rule110.bad b/bench/rule110.bad
new file mode 100644
index 0000000..14aa00e
--- /dev/null
+++ b/bench/rule110.bad
@@ -0,0 +1,39 @@
1; Parameters.
2let line = 0b00000000000000000000000000000001
3let max_iter = 30000
4
5let iter = 0
6while iter < max_iter {
7 ; Print current line.
8 let i = 0
9 while i < 64 {
10 let val = line >> (63 - i) & 0b1
11 if val == 0b1 {
12 print("▀ ")
13 } else {
14 print(" ")
15 }
16 set i = i + 1
17 }
18 println("")
19
20 ; Update next line
21 let next = 0
22 let j = 0
23 while j < 61 {
24 let val = line >> 60 - j & 0b111
25 set val = cond {
26 val == 1 = 1
27 val == 2 = 1
28 val == 3 = 1
29 val == 5 = 1
30 val == 6 = 1
31 else = 0
32 }
33 set next = next | val << 61 - j
34 set j = j + 1
35 }
36 set line = next | 1
37
38 set iter = iter + 1
39}
diff --git a/bench/rule110.py b/bench/rule110.py
new file mode 100644
index 0000000..a4fed06
--- /dev/null
+++ b/bench/rule110.py
@@ -0,0 +1,29 @@
1line = 0b00000000000000000000000000000001
2max_iter = 30000
3
4for iter in range(0, max_iter):
5 for i in range(0, 64):
6 val = line >> (63 - i) & 0b1
7 if val == 0b1:
8 print("▀", end=" ")
9 else:
10 print(".", end=" ")
11 print("")
12
13 next = 0
14 for j in range(0, 61):
15 val = (line >> (61 - j - 1)) & 0b111
16 if val == 1:
17 val = 1
18 elif val == 2:
19 val = 1
20 elif val == 3:
21 val = 1
22 elif val == 5:
23 val = 1
24 elif val == 6:
25 val = 1
26 else:
27 val = 0
28 next = next | (val << (61 - j))
29 line = next | 1
diff --git a/src/badlib.h b/src/badlib.h
index 25e4914..c73b57f 100644
--- a/src/badlib.h
+++ b/src/badlib.h
@@ -20,7 +20,9 @@
20#include <stdint.h> 20#include <stdint.h>
21#include <stdio.h> 21#include <stdio.h>
22#include <stdlib.h> 22#include <stdlib.h>
23#include <strings.h> 23#include <string.h>
24#include <time.h>
25#include <unistd.h>
24 26
25// 27//
26// Basic types. 28// Basic types.
@@ -43,7 +45,7 @@ typedef ptrdiff_t sz;
43typedef uintptr_t ptrsize; 45typedef uintptr_t ptrsize;
44typedef size_t usize; 46typedef size_t usize;
45 47
46#define KB(N) ((sz)(N)*1024) 48#define KB(N) ((sz)(N) * 1024)
47#define MB(N) ((sz)KB(N) * 1024) 49#define MB(N) ((sz)KB(N) * 1024)
48#define GB(N) ((sz)MB(N) * 1024) 50#define GB(N) ((sz)MB(N) * 1024)
49#define TB(N) ((sz)GB(N) * 1024) 51#define TB(N) ((sz)GB(N) * 1024)
@@ -287,9 +289,9 @@ buf_pop(Buf *buf, void *dst, sz size) {
287typedef Array Str; 289typedef Array Str;
288 290
289// Create a string object from a C literal. 291// Create a string object from a C literal.
290#define cstr(s) \ 292#define cstr(s) \
291 (Str) { \ 293 (Str) { \
292 (u8 *)(s), LEN(s) - 1 \ 294 .mem = (u8 *)(s), .size = LEN(s) - 1, \
293 } 295 }
294 296
295// Create a string object from a char* array. 297// Create a string object from a char* array.
@@ -345,7 +347,9 @@ str_split_fn(Str *a, StrSplitFn split_fn) {
345void 347void
346str_replace(Str *a, Str from, Str to) { 348str_replace(Str *a, Str from, Str to) {
347 assert(a != NULL); 349 assert(a != NULL);
348 assert(from.size == to.size); 350 if (from.size != to.size) {
351 return;
352 }
349 SearchResult res = array_find_next(*a, from); 353 SearchResult res = array_find_next(*a, from);
350 if (res.found) { 354 if (res.found) {
351 memcpy(a->mem + res.pos, to.mem, res.matched); 355 memcpy(a->mem + res.pos, to.mem, res.matched);
@@ -945,7 +949,7 @@ _int_eq(sz a, sz b) {
945 return a == b; 949 return a == b;
946} 950}
947 951
948bool 952u64
949_int_hash(sz a) { 953_int_hash(sz a) {
950 return a * UINT64_C(11400714819323198485); 954 return a * UINT64_C(11400714819323198485);
951} 955}
@@ -1235,14 +1239,6 @@ Allocator os_allocator = {
1235 .free = platform_free, 1239 .free = platform_free,
1236}; 1240};
1237 1241
1238#include <time.h>
1239#include <unistd.h>
1240
1241void
1242platform_sleep(size_t microseconds) {
1243 usleep(microseconds);
1244}
1245
1246sz 1242sz
1247platform_time(void) { 1243platform_time(void) {
1248 struct timespec ts; 1244 struct timespec ts;
@@ -1504,7 +1500,7 @@ void
1504log_func_memory(Logger *l, void *in) { 1500log_func_memory(Logger *l, void *in) {
1505 assert(l); 1501 assert(l);
1506 Array *val = in; 1502 Array *val = in;
1507 for (sz i = 0; i < MIN(64, val->size); i++) { 1503 for (sz i = 0; i < MIN(256, val->size); i++) {
1508 Arena scratch = l->storage; 1504 Arena scratch = l->storage;
1509 log_str(l, str_from_hex(val->mem[i], 2, &scratch)); 1505 log_str(l, str_from_hex(val->mem[i], 2, &scratch));
1510 if ((i + 1) % 16 == 0) { 1506 if ((i + 1) % 16 == 0) {
diff --git a/src/compiler.c b/src/compiler.c
index bd50955..98a5e7b 100644
--- a/src/compiler.c
+++ b/src/compiler.c
@@ -25,13 +25,33 @@ typedef struct Instruction {
25typedef union Constant { 25typedef union Constant {
26 s64 i; 26 s64 i;
27 u64 u; 27 u64 u;
28 double f; 28 f64 f;
29 ptrsize ptr; 29 ptrsize ptr;
30} Constant; 30} Constant;
31 31
32typedef struct LineCol {
33 sz line;
34 sz col;
35} LineCol;
36
37typedef struct Function {
38 Str name;
39 sz param_arity;
40 sz return_arity;
41 sz index;
42} Function;
43
44MAPDEF(FunctionMap, funcmap, Str, Function, str_hash, str_eq)
45
32typedef struct Chunk { 46typedef struct Chunk {
33 sz id; 47 sz id;
48 Str name;
49 struct Chunk *parent;
50
34 Instruction *code; 51 Instruction *code;
52 IntIntMap *labels; // label -> chunk_index
53 IntIntMap *labels_rev; // chunk_index -> label
54 sz labels_idx;
35 55
36 // Constant values that fit in 64 bits. 56 // Constant values that fit in 64 bits.
37 Constant *constants; 57 Constant *constants;
@@ -51,41 +71,63 @@ typedef struct Chunk {
51 // Number of registers currently used in this chunk. 71 // Number of registers currently used in this chunk.
52 sz reg_idx; 72 sz reg_idx;
53 73
74 // Number of functions currently used in this chunk.
75 struct Chunk **functions;
76 FunctionMap *funmap;
77 sz fun_idx;
78
54 // Debugging. 79 // Debugging.
55 Str file_name; 80 Str file_name;
56 Arena *storage; 81 Arena *storage;
57 // TODO: line/col info for debugging. 82 LineCol *linecol;
58} Chunk; 83} Chunk;
59 84
60typedef enum OpCode { 85typedef enum OpCode {
61 // OP DST A B 86 // OP DST A B
62 // --------------------------------------------------------------- 87 // ---------------------------------------------------------------
63 // VM/high level instructions. 88 // VM/high level instructions.
64 OP_HALT, // halt 89 OP_HALT, // halt
65 OP_STVARI, // stvari vx, ca 90 // NOTE: LDGVAR/STGVAR* could be obtained in terms of LDGADDR.
66 OP_STVAR, // stvar vx, ra 91 OP_STGVARI, // stgvari vx, ca
67 OP_LDVAR, // ldvar rx, vx 92 OP_STGVAR, // stgvar vx, ra
93 OP_LDGVAR, // ldgvar rx, va
94 OP_LDGADDR, // ldgaddr rx, va
95 // Functions.
96 OP_CALL, // call fx ; Bumps the stack pointer by cx
97 OP_RET, // ret ; Returns from current function
98 OP_RESERVE, // reserve cx ; Increments the stack pointer by cx bytes
99 OP_POP, // pop rx ; Pops the last value of the stack into rx.
100 OP_PUSH, // push rx ; Push the rx value to the stack.
101 OP_PUSHI, // pushi cx ; Push the cx value to the stack.
102 OP_PUTRET, // putret rx ; Put rx into the return value memory.
103 OP_PUTRETI, // putreti cx ; Put cx into the return value memory.
104 // Printing values with builtin print/println functions.
105 OP_PRINTSTR, // p rx
106 OP_PRINTS64, // p rx
107 OP_PRINTF64, // p rx
108 OP_PRINTS64I, // p cx
109 OP_PRINTF64I, // p cx
68 // Load/Store instructions. 110 // Load/Store instructions.
69 OP_LD8K, // ld8k rx, ca -> u8 rx = ca 111 OP_LD8K, // ld8k rx, ca -> u8 rx = ca
70 OP_LD16K, // ld16k rx, ca -> u16 rx = ca 112 OP_LD16K, // ld16k rx, ca -> u16 rx = ca
71 OP_LD32K, // ld32k rx, ca -> u32 rx = ca 113 OP_LD32K, // ld32k rx, ca -> u32 rx = ca
72 OP_LD64K, // ld64k rx, ca -> u64 rx = ca 114 OP_LD64K, // ld64k rx, ca -> u64 rx = ca
73 OP_LD8I, // ld8i rx, ra, cb -> u8 *p; rx = p[ra + cb] 115 OP_LD8I, // ld8i rx, ra, cb -> u8 *p = ra; rx = p[cb]
74 OP_LD16I, // ld16i rx, ra, cb -> u16 *p; rx = p[ra + cb] 116 OP_LD16I, // ld16i rx, ra, cb -> u16 *p = ra; rx = p[cb]
75 OP_LD32I, // ld32i rx, ra, cb -> u32 *p; rx = p[ra + cb] 117 OP_LD32I, // ld32i rx, ra, cb -> u32 *p = ra; rx = p[cb]
76 OP_LD64I, // ld64i rx, ra, cb -> u64 *p; rx = p[ra + cb] 118 OP_LD64I, // ld64i rx, ra, cb -> u64 *p = ra; rx = p[cb]
77 OP_LD8, // ld8 rx, ra, rb -> u8 *p; rx = p[ra + rb] 119 OP_LD8, // ld8 rx, ra, rb -> u8 *p = ra; rx = p[rb]
78 OP_LD16, // ld16 rx, ra, rb -> u16 *p; rx = p[ra + rb] 120 OP_LD16, // ld16 rx, ra, rb -> u16 *p = ra; rx = p[rb]
79 OP_LD32, // ld32 rx, ra, rb -> u32 *p; rx = p[ra + rb] 121 OP_LD32, // ld32 rx, ra, rb -> u32 *p = ra; rx = p[rb]
80 OP_LD64, // ld64 rx, ra, rb -> u64 *p; rx = p[ra + rb] 122 OP_LD64, // ld64 rx, ra, rb -> u64 *p = ra; rx = p[rb]
81 OP_ST8I, // st8i rx, ra, cb -> u8 *p; p[ra + cb] = rx 123 OP_ST8I, // st8i rx, ra, cb -> u8 *p = ra; p[cb] = rx
82 OP_ST16I, // st16i rx, ra, cb -> u16 *p; p[ra + cb] = rx 124 OP_ST16I, // st16i rx, ra, cb -> u16 *p = ra; p[cb] = rx
83 OP_ST32I, // st32i rx, ra, cb -> u32 *p; p[ra + cb] = rx 125 OP_ST32I, // st32i rx, ra, cb -> u32 *p = ra; p[cb] = rx
84 OP_ST64I, // st64i rx, ra, cb -> u64 *p; p[ra + cb] = rx 126 OP_ST64I, // st64i rx, ra, cb -> u64 *p = ra; p[cb] = rx
85 OP_ST8, // st8 rx, ra, rb -> u8 *p; p[ra + rb] = rx 127 OP_ST8, // st8 rx, ra, rb -> u8 *p = ra; p[rb] = rx
86 OP_ST16, // st16 rx, ra, rb -> u16 *p; p[ra + rb] = rx 128 OP_ST16, // st16 rx, ra, rb -> u16 *p = ra; p[rb] = rx
87 OP_ST32, // st32 rx, ra, rb -> u32 *p; p[ra + rb] = rx 129 OP_ST32, // st32 rx, ra, rb -> u32 *p = ra; p[rb] = rx
88 OP_ST64, // st64 rx, ra, rb -> u64 *p; p[ra + rb] = rx 130 OP_ST64, // st64 rx, ra, rb -> u64 *p = ra; p[rb] = rx
89 // Integer arithmetic (only int/s64 for now). 131 // Integer arithmetic (only int/s64 for now).
90 OP_ADDI, // addk rx, ra, cb 132 OP_ADDI, // addk rx, ra, cb
91 OP_SUBI, // subk rx, ra, cb 133 OP_SUBI, // subk rx, ra, cb
@@ -143,13 +185,35 @@ typedef enum OpCode {
143 OP_BITAND, // band rx, ra, rb 185 OP_BITAND, // band rx, ra, rb
144 OP_BITOR, // bor rx, ra, rb 186 OP_BITOR, // bor rx, ra, rb
145 OP_BITNOT, // bnot rx, ra 187 OP_BITNOT, // bnot rx, ra
188 // Jump instructions.
189 OP_JMP, // jmp lx ; jmp to label lx
190 OP_JMPF, // jmpf lx, rx ; jmp to label lx if rx is false
191 OP_JMPT, // jmpt lx, rx ; jmp to label lx if rx is true
192 OP_JMPFI, // jmpf lx, cx ; jmp to label lx if rx is false
193 OP_JMPTI, // jmpt lx, cx ; jmp to label lx if rx is true
146} OpCode; 194} OpCode;
147 195
148Str op_str[] = { 196Str op_str[] = {
197 // High level ops.
149 [OP_HALT] = cstr("HALT "), 198 [OP_HALT] = cstr("HALT "),
150 [OP_STVAR] = cstr("STVAR "), 199 [OP_STGVAR] = cstr("STGVAR "),
151 [OP_STVARI] = cstr("STVARI "), 200 [OP_STGVARI] = cstr("STGVARI "),
152 [OP_LDVAR] = cstr("LDVAR "), 201 [OP_LDGVAR] = cstr("LDGVAR "),
202 [OP_LDGADDR] = cstr("LDGADDR "),
203 [OP_PRINTSTR] = cstr("PRNTSTR "),
204 [OP_PRINTS64] = cstr("PRNTS64 "),
205 [OP_PRINTF64] = cstr("PRNTF64 "),
206 [OP_PRINTS64I] = cstr("PRNTS64I"),
207 [OP_PRINTF64I] = cstr("PRNTF64I"),
208 [OP_PUTRET] = cstr("PUTRET "),
209 [OP_PUTRETI] = cstr("PUTRETI "),
210 // Functions.
211 [OP_CALL] = cstr("CALL "),
212 [OP_RET] = cstr("RET "),
213 [OP_RESERVE] = cstr("RESERVE "),
214 [OP_POP] = cstr("POP "),
215 [OP_PUSH] = cstr("PUSH "),
216 [OP_PUSHI] = cstr("PUSHI "),
153 // Load ops. 217 // Load ops.
154 [OP_LD8K] = cstr("LD8K "), 218 [OP_LD8K] = cstr("LD8K "),
155 [OP_LD16K] = cstr("LD16K "), 219 [OP_LD16K] = cstr("LD16K "),
@@ -228,6 +292,12 @@ Str op_str[] = {
228 [OP_BITAND] = cstr("BAND "), 292 [OP_BITAND] = cstr("BAND "),
229 [OP_BITOR] = cstr("BOR "), 293 [OP_BITOR] = cstr("BOR "),
230 [OP_BITNOT] = cstr("BNOT "), 294 [OP_BITNOT] = cstr("BNOT "),
295 // Jump instructions.
296 [OP_JMP] = cstr("JMP "),
297 [OP_JMPF] = cstr("JMPF "),
298 [OP_JMPT] = cstr("JMPT "),
299 [OP_JMPFI] = cstr("JMPFI "),
300 [OP_JMPTI] = cstr("JMPTI "),
231}; 301};
232 302
233typedef enum { 303typedef enum {
@@ -245,15 +315,21 @@ typedef struct CompResult {
245 315
246CompResult compile_expr(Chunk *chunk, Node *node); 316CompResult compile_expr(Chunk *chunk, Node *node);
247 317
248#define EMIT_OP(OP, DST, A, B, NODE, CHUNK) \ 318#define EMIT_OP(OP, DST, A, B, NODE, CHUNK) \
249 do { \ 319 do { \
250 Instruction inst = (Instruction){ \ 320 Instruction inst = (Instruction){ \
251 .op = (OP), \ 321 .op = (OP), \
252 .dst = (DST), \ 322 .dst = (DST), \
253 .a = (A), \ 323 .a = (A), \
254 .b = (B), \ 324 .b = (B), \
255 }; \ 325 }; \
256 array_push((CHUNK)->code, inst, (CHUNK)->storage); \ 326 array_push((CHUNK)->code, inst, (CHUNK)->storage); \
327 LineCol linecol = (LineCol){0}; \
328 if (NODE) { \
329 Node *_node = (NODE); \
330 linecol = (LineCol){.line = _node->line, .col = _node->col}; \
331 } \
332 array_push((CHUNK)->linecol, linecol, (CHUNK)->storage); \
257 } while (0) 333 } while (0)
258 334
259CompResult 335CompResult
@@ -427,9 +503,456 @@ compile_unary(Chunk *chunk, Node *node) {
427 return (CompResult){.type = COMP_REG, .idx = reg_dst}; 503 return (CompResult){.type = COMP_REG, .idx = reg_dst};
428} 504}
429 505
506sz
507add_constant(Chunk *chunk, sz value) {
508 IntIntMap *map = intintmap_lookup(&chunk->intmap, value);
509 // Make sure we don't have duplicated constants.
510 if (!map) {
511 map = intintmap_insert(&chunk->intmap, value, chunk->const_idx++,
512 chunk->storage);
513 Constant c = (Constant){.i = value};
514 array_push(chunk->constants, c, chunk->storage);
515 }
516 return map->val;
517}
518
519sz
520add_string(Chunk *chunk, Str string) {
521 // Make sure we don't have duplicated string.
522 StrIntMap *map = strintmap_lookup(&chunk->strmap, string);
523 if (!map) {
524 map = strintmap_insert(&chunk->strmap, string, chunk->str_idx++,
525 chunk->storage);
526 array_push(chunk->strings, string, chunk->storage);
527 }
528 return map->val;
529}
530
531CompResult
532compile_if(Chunk *chunk, Node *node) {
533 CompResult cond = compile_expr(chunk, node->cond_if);
534 OpCode jmpop;
535 switch (cond.type) {
536 case COMP_CONST: {
537 jmpop = OP_JMPFI;
538 } break;
539 case COMP_REG: {
540 jmpop = OP_JMPF;
541 } break;
542 default: {
543 return (CompResult){.type = COMP_ERR};
544 } break;
545 }
546
547 if (!str_eq(node->type, cstr("nil"))) {
548 sz reg_dst = chunk->reg_idx++;
549
550 // Jump to the `false` branch.
551 sz lab0 = chunk->labels_idx++;
552 EMIT_OP(jmpop, lab0, cond.idx, 0, node->cond_if, chunk);
553
554 // Condition is true.
555 CompResult then_expr = compile_expr(chunk, node->cond_expr);
556 switch (then_expr.type) {
557 case COMP_CONST: {
558 EMIT_OP(OP_LD64K, reg_dst, then_expr.idx, 0, node->cond_if,
559 chunk);
560 } break;
561 case COMP_REG: {
562 EMIT_OP(OP_MOV64, reg_dst, then_expr.idx, 0, node->cond_if,
563 chunk);
564 } break;
565 default: {
566 return (CompResult){.type = COMP_ERR};
567 } break;
568 }
569
570 // Jump to the end of the expression.
571 sz pos0 = array_size(chunk->code);
572 sz lab1 = chunk->labels_idx++;
573 EMIT_OP(OP_JMP, lab1, 0, 0, node->cond_else, chunk);
574
575 // Else expression.
576 CompResult else_expr = compile_expr(chunk, node->cond_else);
577 switch (else_expr.type) {
578 case COMP_CONST: {
579 EMIT_OP(OP_LD64K, reg_dst, else_expr.idx, 0, node->cond_else,
580 chunk);
581 } break;
582 case COMP_REG: {
583 EMIT_OP(OP_MOV64, reg_dst, else_expr.idx, 0, node->cond_else,
584 chunk);
585 } break;
586 default: {
587 return (CompResult){.type = COMP_ERR};
588 } break;
589 }
590 sz pos1 = array_size(chunk->code);
591
592 // Update labels.
593 intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage);
594 intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage);
595 intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, chunk->storage);
596 intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage);
597 return (CompResult){.type = COMP_REG, .idx = reg_dst};
598 }
599
600 // Jump to the `false` branch.
601 sz lab0 = chunk->labels_idx++;
602 EMIT_OP(jmpop, lab0, cond.idx, 0, node->cond_if, chunk);
603
604 // Condition is true.
605 compile_expr(chunk, node->cond_expr);
606
607 // Jump to the end of the expression.
608 sz pos0 = array_size(chunk->code);
609 sz lab1 = chunk->labels_idx++;
610 EMIT_OP(OP_JMP, lab1, 0, 0, node->cond_else, chunk);
611
612 // Else expression.
613 if (node->cond_else) {
614 compile_expr(chunk, node->cond_else);
615 }
616 sz pos1 = array_size(chunk->code);
617
618 // Update labels.
619 intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage);
620 intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, chunk->storage);
621 intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage);
622 intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage);
623
624 return (CompResult){.type = COMP_NIL};
625}
626
627CompResult
628compile_cond(Chunk *chunk, Node *node) {
629 if (str_eq(node->type, cstr("nil"))) {
630 sz lab1 = chunk->labels_idx++;
631 for (sz i = 0; i < array_size(node->match_cases); i++) {
632 // condition = expression
633 Node *expr = node->match_cases[i];
634 if (expr->case_value) {
635 CompResult cond = compile_expr(chunk, expr->case_value);
636 OpCode jmpop;
637 switch (cond.type) {
638 case COMP_CONST: {
639 jmpop = OP_JMPFI;
640 } break;
641 case COMP_REG: {
642 jmpop = OP_JMPF;
643 } break;
644 default: {
645 return (CompResult){.type = COMP_ERR};
646 } break;
647 }
648 // Jump to the `next` branch.
649 sz lab0 = chunk->labels_idx++;
650 EMIT_OP(jmpop, lab0, cond.idx, 0, expr->case_expr, chunk);
651
652 // Condition is true.
653 compile_expr(chunk, expr->case_expr);
654 if (i != array_size(node->match_cases) - 1) {
655 // Jump to the end of the expression.
656 sz pos0 = array_size(chunk->code);
657 EMIT_OP(OP_JMP, lab1, 0, 0, node->cond_else, chunk);
658 intintmap_insert(&chunk->labels, lab0, pos0 + 1,
659 chunk->storage);
660 intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0,
661 chunk->storage);
662 }
663 } else {
664 compile_expr(chunk, expr->case_expr);
665 break;
666 }
667 }
668 sz pos1 = array_size(chunk->code);
669 intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage);
670 intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage);
671 return (CompResult){.type = COMP_NIL};
672 }
673
674 sz reg_dst = chunk->reg_idx++;
675 sz lab1 = chunk->labels_idx++;
676 for (sz i = 0; i < array_size(node->match_cases); i++) {
677 // condition = expression
678 Node *expr = node->match_cases[i];
679 if (expr->case_value) {
680 CompResult cond = compile_expr(chunk, expr->case_value);
681 OpCode jmpop;
682 switch (cond.type) {
683 case COMP_CONST: {
684 jmpop = OP_JMPFI;
685 } break;
686 case COMP_REG: {
687 jmpop = OP_JMPF;
688 } break;
689 default: {
690 return (CompResult){.type = COMP_ERR};
691 } break;
692 }
693 // Jump to the `next` branch.
694 sz lab0 = chunk->labels_idx++;
695 EMIT_OP(jmpop, lab0, cond.idx, 0, expr->case_expr, chunk);
696
697 // Condition is true.
698 CompResult then_expr = compile_expr(chunk, expr->case_expr);
699 switch (then_expr.type) {
700 case COMP_CONST: {
701 EMIT_OP(OP_LD64K, reg_dst, then_expr.idx, 0,
702 expr->case_expr, chunk);
703 } break;
704 case COMP_REG: {
705 EMIT_OP(OP_MOV64, reg_dst, then_expr.idx, 0,
706 expr->case_expr, chunk);
707 } break;
708 default: {
709 return (CompResult){.type = COMP_ERR};
710 } break;
711 }
712 if (i != array_size(node->match_cases) - 1) {
713 // Jump to the end of the expression.
714 sz pos0 = array_size(chunk->code);
715 EMIT_OP(OP_JMP, lab1, 0, 0, node->cond_else, chunk);
716 intintmap_insert(&chunk->labels, lab0, pos0 + 1,
717 chunk->storage);
718 intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0,
719 chunk->storage);
720 }
721 } else {
722 CompResult then_expr = compile_expr(chunk, expr->case_expr);
723 switch (then_expr.type) {
724 case COMP_CONST: {
725 EMIT_OP(OP_LD64K, reg_dst, then_expr.idx, 0,
726 expr->case_expr, chunk);
727 } break;
728 case COMP_REG: {
729 EMIT_OP(OP_MOV64, reg_dst, then_expr.idx, 0,
730 expr->case_expr, chunk);
731 } break;
732 default: {
733 return (CompResult){.type = COMP_ERR};
734 } break;
735 }
736 break;
737 }
738 }
739 sz pos1 = array_size(chunk->code);
740 intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage);
741 intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage);
742 return (CompResult){.type = COMP_REG, .idx = reg_dst};
743}
744
745CompResult
746compile_while(Chunk *chunk, Node *node) {
747 sz pos1 = array_size(chunk->code);
748 CompResult cond = compile_expr(chunk, node->while_cond);
749 OpCode jmpop;
750 switch (cond.type) {
751 case COMP_CONST: {
752 jmpop = OP_JMPFI;
753 } break;
754 case COMP_REG: {
755 jmpop = OP_JMPF;
756 } break;
757 default: {
758 return (CompResult){.type = COMP_ERR};
759 } break;
760 }
761
762 // Jump to the `end of the loop` branch.
763 sz lab0 = chunk->labels_idx++;
764 EMIT_OP(jmpop, lab0, cond.idx, 0, node->while_cond, chunk);
765
766 // Condition is true.
767 compile_expr(chunk, node->while_expr);
768 sz pos0 = array_size(chunk->code);
769 sz lab1 = chunk->labels_idx++;
770 EMIT_OP(OP_JMP, lab1, 0, 0, node, chunk);
771
772 // Update labels.
773 intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage);
774 intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, chunk->storage);
775 intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage);
776 intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage);
777
778 // Return.
779 return (CompResult){.type = COMP_NIL};
780}
781
782CompResult
783compile_funcall(Chunk *chunk, Node *node) {
784 Str name = node->value.str;
785
786 // Builtins.
787 if (str_eq(name, cstr("print")) || str_eq(name, cstr("println"))) {
788 for (sz i = 0; i < array_size(node->elements); i++) {
789 Node *expr = node->elements[i];
790 CompResult result = compile_expr(chunk, expr);
791 if (str_eq(expr->type, cstr("int"))) {
792 switch (result.type) {
793 case COMP_CONST: {
794 EMIT_OP(OP_PRINTS64I, result.idx, 0, 0, expr, chunk);
795 } break;
796 case COMP_REG: {
797 EMIT_OP(OP_PRINTS64, result.idx, 0, 0, expr, chunk);
798 } break;
799 default: {
800 return (CompResult){.type = COMP_ERR};
801 } break;
802 }
803 } else if (str_eq(expr->type, cstr("f64"))) {
804 switch (result.type) {
805 case COMP_CONST: {
806 EMIT_OP(OP_PRINTF64I, result.idx, 0, 0, expr, chunk);
807 } break;
808 case COMP_REG: {
809 EMIT_OP(OP_PRINTF64, result.idx, 0, 0, expr, chunk);
810 } break;
811 default: {
812 return (CompResult){.type = COMP_ERR};
813 } break;
814 }
815 } else if (str_eq(expr->type, cstr("str"))) {
816 switch (result.type) {
817 case COMP_STRING: {
818 EMIT_OP(OP_PRINTSTR, result.idx, 0, 0, expr, chunk);
819 } break;
820 default: {
821 return (CompResult){.type = COMP_ERR};
822 } break;
823 }
824 }
825 }
826 if (str_eq(name, cstr("println"))) {
827 sz idx = add_string(chunk, cstr("\n"));
828 EMIT_OP(OP_PRINTSTR, idx, 0, 0, node, chunk);
829 }
830 return (CompResult){.type = COMP_NIL};
831 }
832
833 // TODO: need to find this on the parents, not just in the current chunk.
834 FunctionMap *map = funcmap_lookup(&chunk->funmap, node->unique_name);
835 if (!map) {
836 println("how come?");
837 }
838 Function fun = map->val;
839
840 // Reserve space for the return value if needed.
841 if (fun.return_arity > 0) {
842 // Put the return data into a register
843 sz ret_size = add_constant(chunk, 8);
844 EMIT_OP(OP_RESERVE, ret_size, 0, 0, node, chunk);
845 }
846
847 // Send parameters to the stack.
848 for (sz i = 0; i < array_size(node->elements); i++) {
849 Node *expr = node->elements[i];
850 CompResult result = compile_expr(chunk, expr);
851 // TODO: Assuming all values are 8 bytes... again.
852 switch (result.type) {
853 case COMP_CONST: {
854 EMIT_OP(OP_PUSHI, result.idx, 0, 0, expr, chunk);
855 } break;
856 case COMP_REG: {
857 EMIT_OP(OP_PUSH, result.idx, 0, 0, expr, chunk);
858 } break;
859 default: {
860 return (CompResult){.type = COMP_ERR};
861 } break;
862 }
863 }
864
865 // // TODO: prologue (how much space do we actually need)? we need the
866 // symbol
867 // // table, arity and return parameters boy.
868 // // FIXME: assuming all values are 8 bytes for now...
869 // sz alloc_size = (fun.param_arity + fun.return_arity) * 8;
870 // sz alloc_const = add_constant(chunk, alloc_size);
871 // if (alloc_size > 0) {
872 // // EMIT_OP(OP_RESERVE, alloc_const, 0, 0, node, chunk);
873 // }
874
875 // // println("FUNCALL: unique name: %s", node->unique_name);
876 // // println("FUN: param_arity: %d", fun.param_arity);
877 // // println("FUN: return_arity: %d", fun.return_arity);
878 EMIT_OP(OP_CALL, fun.index, 0, 0, node, chunk);
879
880 // Only one return parameter for now.
881 if (fun.return_arity > 0) {
882 // Put the return data into a register
883 sz reg_dst = chunk->reg_idx++;
884 EMIT_OP(OP_POP, reg_dst, 0, 0, node, chunk);
885 return (CompResult){.type = COMP_REG, .idx = reg_dst};
886 }
887
888 // if (alloc_size > 0) {
889 // // EMIT_OP(OP_POP, alloc_const, 0, 0, node, chunk);
890 // }
891 // TODO: epilogue (how much space do we actually need)? we need to remove
892 // the AR data here after using it. If we only have one return parameter we
893 // can put it in a register but what if that's not actually the case?
894 return (CompResult){.type = COMP_NIL};
895}
896
897Chunk *
898chunk_alloc(Chunk *parent) {
899 static sz chunk_idx = 1;
900 Chunk *chunk = arena_calloc((sz)sizeof(Chunk), parent->storage);
901 chunk->parent = parent;
902 chunk->id = chunk_idx++;
903 chunk->storage = parent->storage;
904 chunk->file_name = parent->file_name;
905 return chunk;
906}
907
908CompResult
909compile_function(Chunk *chunk, Node *node) {
910 // The current activation record procedure for the VM is as follows:
911 //
912 // [caller][callee ]
913 // [ .... ][ RET VAL ][ PARAMS ][ LOCALS ][ REGISTERS ][ RET META ]
914 // ^
915 // frame pointer
916 //
917 // The caller is responsible for allocating the return memory and the
918 // parameter memory and filling the param data before OP_CALL.
919 //
920 Chunk *func = chunk_alloc(chunk);
921 func->name = node->unique_name;
922 Function fun = (Function){
923 .name = func->name,
924 .index = chunk->fun_idx++,
925 .param_arity = array_size(node->func_params),
926 .return_arity = array_size(node->func_ret),
927 };
928 funcmap_insert(&chunk->funmap, func->name, fun, chunk->storage);
929 array_push(chunk->functions, func, chunk->storage);
930
931 // Put return values into memory.
932 CompResult res = compile_expr(func, node->func_body);
933 switch (res.type) {
934 case COMP_CONST: {
935 EMIT_OP(OP_PUTRETI, res.idx, 0, 0, node, func);
936 } break;
937 case COMP_REG: {
938 EMIT_OP(OP_PUTRET, res.idx, 0, 0, node, func);
939 } break;
940 default: break;
941 }
942
943 // TODO: handle captured locals/globals?
944 EMIT_OP(OP_RET, 0, 0, 0, node, func);
945 return (CompResult){.type = COMP_NIL};
946}
947
430CompResult 948CompResult
431compile_expr(Chunk *chunk, Node *node) { 949compile_expr(Chunk *chunk, Node *node) {
432 switch (node->kind) { 950 switch (node->kind) {
951 case NODE_FUN: return compile_function(chunk, node);
952 case NODE_FUNCALL: return compile_funcall(chunk, node);
953 case NODE_WHILE: return compile_while(chunk, node);
954 case NODE_IF: return compile_if(chunk, node);
955 case NODE_COND: return compile_cond(chunk, node);
433 // Logic. 956 // Logic.
434 // case NODE_XOR: 957 // case NODE_XOR:
435 case NODE_BITNOT: 958 case NODE_BITNOT:
@@ -459,31 +982,18 @@ compile_expr(Chunk *chunk, Node *node) {
459 case NODE_NUM_UINT: 982 case NODE_NUM_UINT:
460 case NODE_NUM_INT: { 983 case NODE_NUM_INT: {
461 sz value = node->value.i; 984 sz value = node->value.i;
462 // Make sure we don't have duplicated constants. 985 sz const_idx = add_constant(chunk, value);
463 IntIntMap *map = intintmap_lookup(&chunk->intmap, value);
464 if (!map) {
465 map = intintmap_insert(&chunk->intmap, value,
466 chunk->const_idx++, chunk->storage);
467 Constant c = (Constant){.i = node->value.i};
468 array_push(chunk->constants, c, chunk->storage);
469 }
470 return (CompResult){ 986 return (CompResult){
471 .type = COMP_CONST, 987 .type = COMP_CONST,
472 .idx = map->val, 988 .idx = const_idx,
473 }; 989 };
474 } break; 990 } break;
475 case NODE_STRING: { 991 case NODE_STRING: {
476 Str string = node->value.str; 992 Str string = node->value.str;
477 // Make sure we don't have duplicated strings. 993 sz str_idx = add_string(chunk, string);
478 StrIntMap *map = strintmap_lookup(&chunk->strmap, string);
479 if (!map) {
480 map = strintmap_insert(&chunk->strmap, string, chunk->str_idx++,
481 chunk->storage);
482 array_push(chunk->strings, string, chunk->storage);
483 }
484 return (CompResult){ 994 return (CompResult){
485 .type = COMP_STRING, 995 .type = COMP_STRING,
486 .idx = map->val, 996 .idx = str_idx,
487 }; 997 };
488 } break; 998 } break;
489 case NODE_LET: { 999 case NODE_LET: {
@@ -496,6 +1006,18 @@ compile_expr(Chunk *chunk, Node *node) {
496 if (str_eq(type, cstr("str"))) { 1006 if (str_eq(type, cstr("str"))) {
497 size = 16; 1007 size = 16;
498 } 1008 }
1009 if (str_has_prefix(type, cstr("@"))) {
1010 if (node->var_type && node->var_type->kind == NODE_ARR_TYPE &&
1011 node->var_type->arr_size->value.i > 0) {
1012 // TODO: get the proper storage size for the multiplication.
1013 size *= node->var_type->arr_size->value.i;
1014 // FIXME: this should be done on the static analysis, plus,
1015 // we shouldn't be checking all these types by hand, but
1016 // using the symbol tables.
1017 type = str_remove_prefix(type, cstr("@"));
1018 type = str_concat(cstr("[]"), type, chunk->storage);
1019 }
1020 }
499 Variable var = (Variable){ 1021 Variable var = (Variable){
500 .name = name, 1022 .name = name,
501 .type = type, 1023 .type = type,
@@ -512,11 +1034,11 @@ compile_expr(Chunk *chunk, Node *node) {
512 CompResult res = compile_expr(chunk, node->var_val); 1034 CompResult res = compile_expr(chunk, node->var_val);
513 switch (res.type) { 1035 switch (res.type) {
514 case COMP_CONST: { 1036 case COMP_CONST: {
515 EMIT_OP(OP_STVARI, idx, res.idx, 0, node->var_val, 1037 EMIT_OP(OP_STGVARI, idx, res.idx, 0, node->var_val,
516 chunk); 1038 chunk);
517 } break; 1039 } break;
518 case COMP_REG: { 1040 case COMP_REG: {
519 EMIT_OP(OP_STVAR, idx, res.idx, 0, node->var_val, 1041 EMIT_OP(OP_STGVAR, idx, res.idx, 0, node->var_val,
520 chunk); 1042 chunk);
521 } break; 1043 } break;
522 default: { 1044 default: {
@@ -527,6 +1049,74 @@ compile_expr(Chunk *chunk, Node *node) {
527 1049
528 return (CompResult){.type = COMP_NIL}; 1050 return (CompResult){.type = COMP_NIL};
529 } break; 1051 } break;
1052 case NODE_SET: {
1053 Str name = node->unique_name;
1054 StrVarMap *map = varmap_lookup(&chunk->varmap, name);
1055 if (!map) {
1056 eprintln("error: node_set: symbol name not found: %s", name);
1057 }
1058 CompResult res = compile_expr(chunk, node->var_val);
1059 if (node->var_name->kind == NODE_SYMBOL_IDX) {
1060 // Value.
1061 sz reg_val;
1062 switch (res.type) {
1063 case COMP_CONST: {
1064 reg_val = chunk->reg_idx++;
1065 EMIT_OP(OP_LD64K, reg_val, res.idx, 0, node, chunk);
1066 } break;
1067 case COMP_REG: {
1068 reg_val = res.idx;
1069 } break;
1070 default: {
1071 return (CompResult){.type = COMP_ERR};
1072 } break;
1073 }
1074
1075 // Address.
1076 sz reg_addr = chunk->reg_idx++;
1077 // Is this a pointer access or an array access?
1078 if (str_has_prefix(map->val.type, cstr("[]"))) {
1079 EMIT_OP(OP_LDGADDR, reg_addr, map->val.idx, 0,
1080 node->var_val, chunk);
1081 } else {
1082 EMIT_OP(OP_LDGVAR, reg_addr, map->val.idx, 0, node->var_val,
1083 chunk);
1084 }
1085
1086 // Index.
1087 CompResult idx = compile_expr(chunk, node->var_name->arr_size);
1088 switch (idx.type) {
1089 case COMP_CONST: {
1090 EMIT_OP(OP_ST64I, reg_val, reg_addr, idx.idx, node,
1091 chunk);
1092 } break;
1093 case COMP_REG: {
1094 EMIT_OP(OP_ST64, reg_val, reg_addr, idx.idx, node,
1095 chunk);
1096 } break;
1097 default: {
1098 return (CompResult){.type = COMP_ERR};
1099 } break;
1100 }
1101 // TODO: offset should be in bytes, in this case we are assuming
1102 // 64bit types, hence ST64
1103 return (CompResult){.type = COMP_NIL};
1104 }
1105 switch (res.type) {
1106 case COMP_CONST: {
1107 EMIT_OP(OP_STGVARI, map->val.idx, res.idx, 0, node->var_val,
1108 chunk);
1109 } break;
1110 case COMP_REG: {
1111 EMIT_OP(OP_STGVAR, map->val.idx, res.idx, 0, node->var_val,
1112 chunk);
1113 } break;
1114 default: {
1115 return (CompResult){.type = COMP_ERR};
1116 } break;
1117 }
1118 return (CompResult){.type = COMP_NIL};
1119 } break;
530 case NODE_SYMBOL: { 1120 case NODE_SYMBOL: {
531 Str name = node->unique_name; 1121 Str name = node->unique_name;
532 StrVarMap *map = varmap_lookup(&chunk->varmap, name); 1122 StrVarMap *map = varmap_lookup(&chunk->varmap, name);
@@ -536,7 +1126,48 @@ compile_expr(Chunk *chunk, Node *node) {
536 } 1126 }
537 Variable var = map->val; 1127 Variable var = map->val;
538 u8 reg_dst = chunk->reg_idx++; 1128 u8 reg_dst = chunk->reg_idx++;
539 EMIT_OP(OP_LDVAR, reg_dst, var.idx, 0, node->var_val, chunk); 1129 if (node->is_ptr) {
1130 EMIT_OP(OP_LDGADDR, reg_dst, var.idx, 0, node, chunk);
1131 } else {
1132 EMIT_OP(OP_LDGVAR, reg_dst, var.idx, 0, node, chunk);
1133 }
1134 return (CompResult){.type = COMP_REG, .idx = reg_dst};
1135 } break;
1136 case NODE_SYMBOL_IDX: {
1137 Str name = node->unique_name;
1138 StrVarMap *map = varmap_lookup(&chunk->varmap, name);
1139 if (!map) {
1140 println("error: unreachable... name: %s", name);
1141 exit(EXIT_FAILURE);
1142 }
1143
1144 // Destination.
1145 u8 reg_dst = chunk->reg_idx++;
1146
1147 // Address.
1148 sz reg_addr = chunk->reg_idx++;
1149 if (str_has_prefix(map->val.type, cstr("[]"))) {
1150 EMIT_OP(OP_LDGADDR, reg_addr, map->val.idx, 0, node->var_val,
1151 chunk);
1152 } else {
1153 EMIT_OP(OP_LDGVAR, reg_addr, map->val.idx, 0, node->var_val,
1154 chunk);
1155 }
1156
1157 // Index.
1158 CompResult idx = compile_expr(chunk, node->arr_size);
1159 switch (idx.type) {
1160 case COMP_CONST: {
1161 EMIT_OP(OP_LD64I, reg_dst, reg_addr, idx.idx, node, chunk);
1162 } break;
1163 case COMP_REG: {
1164 EMIT_OP(OP_LD64, reg_dst, reg_addr, idx.idx, node, chunk);
1165 } break;
1166 default: {
1167 return (CompResult){.type = COMP_ERR};
1168 } break;
1169 }
1170 // TODO: hardcoding the type size for now (LD64/LD64I).
540 return (CompResult){.type = COMP_REG, .idx = reg_dst}; 1171 return (CompResult){.type = COMP_REG, .idx = reg_dst};
541 } break; 1172 } break;
542 case NODE_BLOCK: { 1173 case NODE_BLOCK: {
@@ -559,6 +1190,10 @@ compile_expr(Chunk *chunk, Node *node) {
559void 1190void
560disassemble_instruction(Instruction instruction) { 1191disassemble_instruction(Instruction instruction) {
561 switch (instruction.op) { 1192 switch (instruction.op) {
1193 case OP_CALL:
1194 println("%s f%d", op_str[instruction.op], instruction.dst,
1195 instruction.a, instruction.b);
1196 break;
562 case OP_MOV8: 1197 case OP_MOV8:
563 case OP_MOV16: 1198 case OP_MOV16:
564 case OP_MOV32: 1199 case OP_MOV32:
@@ -566,6 +1201,11 @@ disassemble_instruction(Instruction instruction) {
566 println("%s r%d, r%d", op_str[instruction.op], instruction.dst, 1201 println("%s r%d, r%d", op_str[instruction.op], instruction.dst,
567 instruction.a, instruction.b); 1202 instruction.a, instruction.b);
568 break; 1203 break;
1204 case OP_JMPF:
1205 case OP_JMPT:
1206 println("%s l%d, r%d", op_str[instruction.op], instruction.dst,
1207 instruction.a, instruction.b);
1208 break;
569 case OP_LD8K: 1209 case OP_LD8K:
570 case OP_LD16K: 1210 case OP_LD16K:
571 case OP_LD32K: 1211 case OP_LD32K:
@@ -639,15 +1279,16 @@ disassemble_instruction(Instruction instruction) {
639 println("%s r%d, r%d, r%d", op_str[instruction.op], instruction.dst, 1279 println("%s r%d, r%d, r%d", op_str[instruction.op], instruction.dst,
640 instruction.a, instruction.b); 1280 instruction.a, instruction.b);
641 break; 1281 break;
642 case OP_LDVAR: 1282 case OP_LDGVAR:
1283 case OP_LDGADDR:
643 println("%s r%d, v%d", op_str[instruction.op], instruction.dst, 1284 println("%s r%d, v%d", op_str[instruction.op], instruction.dst,
644 instruction.a, instruction.b); 1285 instruction.a, instruction.b);
645 break; 1286 break;
646 case OP_STVAR: 1287 case OP_STGVAR:
647 println("%s v%d, r%d", op_str[instruction.op], instruction.dst, 1288 println("%s v%d, r%d", op_str[instruction.op], instruction.dst,
648 instruction.a, instruction.b); 1289 instruction.a, instruction.b);
649 break; 1290 break;
650 case OP_STVARI: 1291 case OP_STGVARI:
651 println("%s v%d, c%d", op_str[instruction.op], instruction.dst, 1292 println("%s v%d, c%d", op_str[instruction.op], instruction.dst,
652 instruction.a, instruction.b); 1293 instruction.a, instruction.b);
653 break; 1294 break;
@@ -661,6 +1302,33 @@ disassemble_instruction(Instruction instruction) {
661 println("%s r%d, r%d", op_str[instruction.op], instruction.dst, 1302 println("%s r%d, r%d", op_str[instruction.op], instruction.dst,
662 instruction.a, instruction.b); 1303 instruction.a, instruction.b);
663 break; 1304 break;
1305 case OP_JMP:
1306 println("%s l%d", op_str[instruction.op], instruction.dst,
1307 instruction.a, instruction.b);
1308 break;
1309 case OP_JMPFI:
1310 case OP_JMPTI:
1311 println("%s l%d, c%d", op_str[instruction.op], instruction.dst,
1312 instruction.a, instruction.b);
1313 break;
1314 case OP_PRINTS64:
1315 case OP_PRINTF64:
1316 case OP_PRINTSTR:
1317 case OP_PUSH:
1318 case OP_POP:
1319 case OP_PUTRET:
1320 println("%s r%d", op_str[instruction.op], instruction.dst,
1321 instruction.a, instruction.b);
1322 break;
1323 case OP_PRINTS64I:
1324 case OP_PRINTF64I:
1325 case OP_RESERVE:
1326 case OP_PUSHI:
1327 case OP_PUTRETI:
1328 println("%s c%d", op_str[instruction.op], instruction.dst,
1329 instruction.a, instruction.b);
1330 break;
1331 case OP_RET:
664 case OP_HALT: println("%s", op_str[instruction.op]); break; 1332 case OP_HALT: println("%s", op_str[instruction.op]); break;
665 default: println("Unknown opcode %d", instruction.op); break; 1333 default: println("Unknown opcode %d", instruction.op); break;
666 } 1334 }
@@ -668,36 +1336,56 @@ disassemble_instruction(Instruction instruction) {
668 1336
669void 1337void
670disassemble_chunk(Chunk chunk) { 1338disassemble_chunk(Chunk chunk) {
671 println("%s: =========== code ===========", chunk.file_name); 1339 println("CHUNK %d: %s%s", chunk.id, chunk.file_name, chunk.name);
1340 println("n_regs: %d, n_vars: %d, n_strings: %d, n_consts: %d",
1341 chunk.reg_idx, array_size(chunk.vars), chunk.str_idx,
1342 chunk.const_idx);
1343 println("================== code ==================");
1344 println(" LINE:COL INUM LABELS OP OPERANDS ");
1345 println("------------------------------------------");
672 for (sz i = 0; i < array_size(chunk.code); i++) { 1346 for (sz i = 0; i < array_size(chunk.code); i++) {
673 print("%s: %x{4}: ", chunk.file_name, i); 1347 printf(" %.4ld:%.4ld %.4lx ", chunk.linecol[i].line,
1348 chunk.linecol[i].col, i);
1349 IntIntMap *label = intintmap_lookup(&chunk.labels_rev, i);
1350 if (label) {
1351 printf(".L%.2ld ", label->val);
1352 } else {
1353 printf(" %2s ", "");
1354 }
674 disassemble_instruction(chunk.code[i]); 1355 disassemble_instruction(chunk.code[i]);
675 } 1356 }
676 if (array_size(chunk.constants) > 0) { 1357 if (array_size(chunk.constants) > 0) {
677 println("%s: ========= constants ========", chunk.file_name); 1358 println("================ constants ===============", chunk.file_name);
678 for (sz i = 0; i < array_size(chunk.constants); i++) { 1359 for (sz i = 0; i < array_size(chunk.constants); i++) {
679 println("%s: %x{2}: %x{8}", chunk.file_name, i, 1360 println(" %x{2}: %x{8}", i, chunk.constants[i]);
680 chunk.constants[i]);
681 } 1361 }
682 } 1362 }
683 if (array_size(chunk.strings) > 0) { 1363 if (array_size(chunk.strings) > 0) {
684 println("%s: ========== strings =========", chunk.file_name); 1364 println("================= strings ================", chunk.file_name);
685 for (sz i = 0; i < array_size(chunk.strings); i++) { 1365 for (sz i = 0; i < array_size(chunk.strings); i++) {
686 println("%s: %x{2}: %s", chunk.file_name, i, chunk.strings[i]); 1366 println(" %x{2}: %s", i, chunk.strings[i]);
687 } 1367 }
688 } 1368 }
689 if (array_size(chunk.vars) > 0) { 1369 if (array_size(chunk.vars) > 0) {
690 println("%s: ========= variables ========", chunk.file_name); 1370 println("================ variables ===============", chunk.file_name);
691 for (sz i = 0; i < array_size(chunk.vars); i++) { 1371 for (sz i = 0; i < array_size(chunk.vars); i++) {
692 println("%s: %x{2}: [%x{4}:%x{4}] %s: %s", chunk.file_name, i, 1372 println(" %x{2}: [%x{4}:%x{4}] %s: %s", i, chunk.vars[i].offset,
693 chunk.vars[i].offset,
694 chunk.vars[i].offset + chunk.vars[i].size, 1373 chunk.vars[i].offset + chunk.vars[i].size,
695 chunk.vars[i].name, chunk.vars[i].type); 1374 chunk.vars[i].name, chunk.vars[i].type);
696 } 1375 }
697 } 1376 }
698 println("n_regs: %d, n_vars: %d, n_strings: %d, n_consts: %d", 1377 if (array_size(chunk.functions) > 0) {
699 chunk.reg_idx, array_size(chunk.vars), chunk.str_idx, 1378 println("================ functions ===============", chunk.file_name);
700 chunk.const_idx); 1379 for (sz i = 0; i < array_size(chunk.functions); i++) {
1380 Chunk *func = chunk.functions[i];
1381 println(" %x{2}: func%d: %s", i, func->id, func->name);
1382 }
1383 }
1384 println("==========================================");
1385 for (sz i = 0; i < array_size(chunk.functions); i++) {
1386 Chunk *func = chunk.functions[i];
1387 disassemble_chunk(*func);
1388 }
701} 1389}
702 1390
703#endif // COMPILER_C 1391#endif // COMPILER_C
diff --git a/src/main.c b/src/main.c
index 618d325..3c3dd29 100644
--- a/src/main.c
+++ b/src/main.c
@@ -13,6 +13,18 @@
13// TODO: embed (binary file) and include (source file) 13// TODO: embed (binary file) and include (source file)
14// TODO: revisit ast parsing for pointers and arrays (I think I'm missing corner 14// TODO: revisit ast parsing for pointers and arrays (I think I'm missing corner
15// cases). 15// cases).
16// TODO: for loops?
17// for i = 0 , i < 10 , i++ {
18//
19// }
20// TODO: fix semantics for the following expression:
21// let b = int
22// a type shouldn't be a valid symbol name
23// TODO: fix semantics for arrays: static arrays can't have size 0.
24// TODO: consider making all tye types PascalCase: Int F64 Str...
25// TODO: add a `const` keyword that can only take literals or constexpr values.
26// TODO: add assignment operators instead of `set` :=, +=, -=
27// TODO: add mifix operators ++ and --
16 28
17typedef enum ExecMode { 29typedef enum ExecMode {
18 RUN_NORMAL, 30 RUN_NORMAL,
@@ -167,11 +179,15 @@ process_file(Str path) {
167 179
168 // Compile roots. 180 // Compile roots.
169 Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator); 181 Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator);
170 Chunk chunk = {.file_name = path, .storage = &bytecode_arena}; 182 Chunk chunk = {
183 .file_name = path,
184 .storage = &bytecode_arena,
185 .name = cstr(".main"),
186 };
171 array_zero(chunk.constants, 256, &bytecode_arena); 187 array_zero(chunk.constants, 256, &bytecode_arena);
172 array_zero(chunk.code, 0xffff, &bytecode_arena); 188 array_zero(chunk.code, 0xffff, &bytecode_arena);
173 sz n_roots = array_size(parser.nodes); 189 sz n_roots = array_size(parser.nodes);
174 CompResult res; 190 CompResult res = {0};
175 for (sz i = 0; i < n_roots; i++) { 191 for (sz i = 0; i < n_roots; i++) {
176 // The parser stores the root nodes as a stack. 192 // The parser stores the root nodes as a stack.
177 Node *root = parser.nodes[i]; 193 Node *root = parser.nodes[i];
@@ -181,7 +197,9 @@ process_file(Str path) {
181 exit(EXIT_FAILURE); 197 exit(EXIT_FAILURE);
182 } 198 }
183 } 199 }
200 // Make sure the last result is on r0.
184 sz res_reg = 0; 201 sz res_reg = 0;
202 bool is_nil = false;
185 switch (res.type) { 203 switch (res.type) {
186 case COMP_CONST: { 204 case COMP_CONST: {
187 res_reg = chunk.reg_idx++; 205 res_reg = chunk.reg_idx++;
@@ -192,11 +210,12 @@ process_file(Str path) {
192 case COMP_REG: { 210 case COMP_REG: {
193 res_reg = res.idx; 211 res_reg = res.idx;
194 } break; 212 } break;
213 case COMP_NIL: {
214 is_nil = true;
215 } break;
195 default: break; 216 default: break;
196 } 217 }
197 // After we are done move the last result to r0 for printing. 218 EMIT_OP(OP_HALT, res_reg, !is_nil, 0, NULL, &chunk);
198 Instruction halt = (Instruction){.op = OP_HALT, .dst = res_reg};
199 array_push(chunk.code, halt, &bytecode_arena);
200 219
201 if (chunk.const_idx >= 256) { 220 if (chunk.const_idx >= 256) {
202 eprintln("too many constants on chunk %s", chunk.id); 221 eprintln("too many constants on chunk %s", chunk.id);
@@ -219,10 +238,10 @@ process_file(Str path) {
219 // println("VM REGISTERS BEFORE:\n%{Mem}", 238 // println("VM REGISTERS BEFORE:\n%{Mem}",
220 // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); 239 // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)});
221 vm_run(&vm); 240 vm_run(&vm);
222 println("VM REGISTERS AFTER:\n%{Mem}", 241#if DEBUG == 1
223 &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); 242 println("MEMORY:\n%{Mem}",
224 println("VM MEMORY AFTER:\n%{Mem}",
225 &(Array){.mem = (u8 *)&vm.stack, sizeof(vm.stack)}); 243 &(Array){.mem = (u8 *)&vm.stack, sizeof(vm.stack)});
244#endif
226 245
227#if DEBUG == 1 246#if DEBUG == 1
228 println("Space used: %{Arena}", &lexer_arena); 247 println("Space used: %{Arena}", &lexer_arena);
diff --git a/src/semantic.c b/src/semantic.c
index d782cac..a0231d5 100644
--- a/src/semantic.c
+++ b/src/semantic.c
@@ -521,6 +521,11 @@ type_inference(Analyzer *a, Node *node, Scope *scope) {
521 .kind = SYM_VAR, 521 .kind = SYM_VAR,
522 }, 522 },
523 a->storage); 523 a->storage);
524 node->var_name->type = type_name;
525 symbol = str_concat(cstr("."), symbol, a->storage);
526 symbol = str_concat(symbol, str_from_int(scope->id, a->storage),
527 a->storage);
528 node->unique_name = symbol;
524 return node->type; 529 return node->type;
525 } 530 }
526 531
@@ -550,6 +555,12 @@ type_inference(Analyzer *a, Node *node, Scope *scope) {
550 a->err = true; 555 a->err = true;
551 return cstr(""); 556 return cstr("");
552 } 557 }
558 Str symbol = node->var_name->value.str;
559 FindSymbolResult sym = find_symbol(scope, symbol);
560 node->unique_name = str_concat(cstr("."), symbol, a->storage);
561 node->unique_name =
562 str_concat(node->unique_name,
563 str_from_int(sym.scope->id, a->storage), a->storage);
553 node->type = cstr("nil"); 564 node->type = cstr("nil");
554 return node->type; 565 return node->type;
555 } break; 566 } break;
@@ -653,6 +664,15 @@ type_inference(Analyzer *a, Node *node, Scope *scope) {
653 return cstr(""); 664 return cstr("");
654 } 665 }
655 } 666 }
667 // If it returns a value, verify it contains an `else` statement.
668 if (!str_eq(node->type, cstr("nil"))) {
669 if (!node->cond_else) {
670 emit_semantic_error(
671 a, node,
672 cstr("missing else statement in if expression"));
673 return cstr("");
674 }
675 }
656 return node->type; 676 return node->type;
657 } break; 677 } break;
658 case NODE_WHILE: { 678 case NODE_WHILE: {
@@ -672,6 +692,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) {
672 } break; 692 } break;
673 case NODE_COND: { 693 case NODE_COND: {
674 Str previous = cstr(""); 694 Str previous = cstr("");
695 bool has_else = false;
675 for (sz i = 0; i < array_size(node->match_cases); i++) { 696 for (sz i = 0; i < array_size(node->match_cases); i++) {
676 Node *expr = node->match_cases[i]; 697 Node *expr = node->match_cases[i];
677 Str next = type_inference(a, expr, scope); 698 Str next = type_inference(a, expr, scope);
@@ -681,8 +702,20 @@ type_inference(Analyzer *a, Node *node, Scope *scope) {
681 cstr("non-matching types for cond expressions")); 702 cstr("non-matching types for cond expressions"));
682 return cstr(""); 703 return cstr("");
683 } 704 }
705 if (!expr->case_value) {
706 has_else = true;
707 }
684 previous = next; 708 previous = next;
685 } 709 }
710 // If it returns a value, verify it contains an `else` statement.
711 if (!str_eq(previous, cstr("nil"))) {
712 if (!has_else) {
713 emit_semantic_error(
714 a, node,
715 cstr("missing else statement in cond expression"));
716 return cstr("");
717 }
718 }
686 node->type = previous; 719 node->type = previous;
687 return node->type; 720 return node->type;
688 } break; 721 } break;
@@ -850,7 +883,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) {
850 return node->type; 883 return node->type;
851 } break; 884 } break;
852 case NODE_NUM_UINT: { 885 case NODE_NUM_UINT: {
853 node->type = cstr("uint"); 886 node->type = cstr("int");
854 return node->type; 887 return node->type;
855 } break; 888 } break;
856 case NODE_NUM_INT: { 889 case NODE_NUM_INT: {
@@ -1025,6 +1058,12 @@ type_inference(Analyzer *a, Node *node, Scope *scope) {
1025 a->err = true; 1058 a->err = true;
1026 return cstr(""); 1059 return cstr("");
1027 } 1060 }
1061 FindSymbolResult sym = find_symbol(scope, symbol);
1062 node->unique_name = str_concat(cstr("."), symbol, a->storage);
1063 node->unique_name =
1064 str_concat(node->unique_name,
1065 str_from_int(sym.scope->id, a->storage), a->storage);
1066
1028 // Check that actual parameters typecheck 1067 // Check that actual parameters typecheck
1029 Str args = cstr(""); 1068 Str args = cstr("");
1030 for (sz i = 0; i < array_size(node->elements); i++) { 1069 for (sz i = 0; i < array_size(node->elements); i++) {
@@ -1148,6 +1187,10 @@ type_inference(Analyzer *a, Node *node, Scope *scope) {
1148 .param_type = param_type, 1187 .param_type = param_type,
1149 .return_type = ret_type}, 1188 .return_type = ret_type},
1150 a->storage); 1189 a->storage);
1190 symbol = str_concat(cstr("."), symbol, a->storage);
1191 symbol = str_concat(symbol, str_from_int(prev_scope->id, a->storage),
1192 a->storage);
1193 node->unique_name = symbol;
1151 1194
1152 if (node->func_body->kind == NODE_BLOCK) { 1195 if (node->func_body->kind == NODE_BLOCK) {
1153 Str type; 1196 Str type;
diff --git a/src/vm.c b/src/vm.c
index fac477a..2992a18 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -8,9 +8,11 @@
8#define STACK_SIZE KB(64) 8#define STACK_SIZE KB(64)
9typedef struct VM { 9typedef struct VM {
10 Chunk *chunk; 10 Chunk *chunk;
11 Constant regs[N_CONST];
12 u8 stack[STACK_SIZE]; 11 u8 stack[STACK_SIZE];
13 Instruction *ip; 12 Instruction *ip;
13 u8 *sp;
14 u64 *fp;
15 Constant *regs;
14} VM; 16} VM;
15 17
16void 18void
@@ -20,6 +22,10 @@ vm_init(VM *vm, Chunk *chunk) {
20 assert(chunk->code); 22 assert(chunk->code);
21 vm->chunk = chunk; 23 vm->chunk = chunk;
22 vm->ip = vm->chunk->code; 24 vm->ip = vm->chunk->code;
25 vm->fp = (u64 *)vm->stack;
26 vm->sp = vm->stack + chunk->var_off;
27 vm->regs = (Constant *)vm->sp;
28 vm->sp += sizeof(Constant) * chunk->reg_idx;
23} 29}
24 30
25#define OP_UNARY(OP, TYPE) \ 31#define OP_UNARY(OP, TYPE) \
@@ -134,36 +140,225 @@ vm_run(VM *vm) {
134 vm->regs[dst].f = 140 vm->regs[dst].f =
135 fmod(vm->regs[src_a].f, vm->chunk->constants[src_b].f); 141 fmod(vm->regs[src_a].f, vm->chunk->constants[src_b].f);
136 } break; 142 } break;
137 case OP_LDVAR: { 143 case OP_LDGVAR: {
138 u8 dst = instruction.dst; 144 u8 dst = instruction.dst;
139 u8 src = instruction.a; 145 u8 src = instruction.a;
140 println("dst: %d src: %d", dst, src);
141 Variable var = vm->chunk->vars[src]; 146 Variable var = vm->chunk->vars[src];
142 s64 *stack = (s64 *)&vm->stack[var.offset]; 147 s64 *stack = (s64 *)&vm->stack[var.offset];
143 vm->regs[dst].i = *stack; 148 vm->regs[dst].i = *stack;
144 } break; 149 } break;
145 case OP_STVAR: { 150 case OP_LDGADDR: {
151 u8 dst = instruction.dst;
152 u8 src = instruction.a;
153 Variable var = vm->chunk->vars[src];
154 s64 *stack = (s64 *)&vm->stack[var.offset];
155 vm->regs[dst].ptr = (ptrsize)stack;
156 } break;
157 case OP_ST64I: {
158 sz value = vm->regs[instruction.dst].i;
159 s64 *addr = (s64 *)vm->regs[instruction.a].ptr;
160 sz offset = vm->chunk->constants[instruction.b].i;
161 addr[offset] = value;
162 } break;
163 case OP_ST64: {
164 sz value = vm->regs[instruction.dst].i;
165 s64 *addr = (s64 *)vm->regs[instruction.a].ptr;
166 sz offset = vm->regs[instruction.b].i;
167 addr[offset] = value;
168 } break;
169 case OP_LD64I: {
170 s64 *addr = (s64 *)vm->regs[instruction.a].ptr;
171 sz offset = vm->chunk->constants[instruction.b].i;
172 vm->regs[instruction.dst].i = addr[offset];
173 } break;
174 case OP_LD64: {
175 s64 *addr = (s64 *)vm->regs[instruction.a].ptr;
176 sz offset = vm->regs[instruction.b].i;
177 vm->regs[instruction.dst].i = addr[offset];
178 } break;
179 case OP_STGVAR: {
146 u8 dst = instruction.dst; 180 u8 dst = instruction.dst;
147 u8 src = instruction.a; 181 u8 src = instruction.a;
148 Variable var = vm->chunk->vars[dst]; 182 Variable var = vm->chunk->vars[dst];
149 s64 *stack = (s64 *)&vm->stack[var.offset]; 183 s64 *stack = (s64 *)&vm->stack[var.offset];
150 *stack = vm->regs[src].i; 184 *stack = vm->regs[src].i;
151 } break; 185 } break;
152 case OP_STVARI: { 186 case OP_STGVARI: {
153 u8 dst = instruction.dst; 187 u8 dst = instruction.dst;
154 u8 src = instruction.a; 188 u8 src = instruction.a;
155 Variable var = vm->chunk->vars[dst]; 189 Variable var = vm->chunk->vars[dst];
156 s64 *stack = (s64 *)&vm->stack[var.offset]; 190 s64 *stack = (s64 *)&vm->stack[var.offset];
157 *stack = vm->chunk->constants[src].i; 191 *stack = vm->chunk->constants[src].i;
158 } break; 192 } break;
193 case OP_JMP: {
194 u8 dst = instruction.dst;
195 sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val;
196 vm->ip = vm->chunk->code + pos;
197 } break;
198 case OP_JMPFI: {
199 u8 dst = instruction.dst;
200 sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val;
201 bool cond = vm->chunk->constants[instruction.a].i;
202 if (!cond) {
203 vm->ip = vm->chunk->code + pos;
204 }
205 } break;
206 case OP_JMPTI: {
207 u8 dst = instruction.dst;
208 sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val;
209 bool cond = vm->chunk->constants[instruction.a].i;
210 if (cond) {
211 vm->ip = vm->chunk->code + pos;
212 }
213 } break;
214 case OP_JMPF: {
215 u8 dst = instruction.dst;
216 sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val;
217 bool cond = vm->regs[instruction.a].i;
218 if (!cond) {
219 vm->ip = vm->chunk->code + pos;
220 }
221 } break;
222 case OP_JMPT: {
223 u8 dst = instruction.dst;
224 sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val;
225 bool cond = vm->regs[instruction.a].i;
226 if (cond) {
227 vm->ip = vm->chunk->code + pos;
228 }
229 } break;
230 case OP_MOV64: {
231 u8 dst = instruction.dst;
232 u8 src = instruction.a;
233 vm->regs[dst] = vm->regs[src];
234 } break;
235 case OP_MOV32: {
236 u8 dst = instruction.dst;
237 u8 src = instruction.a;
238 vm->regs[dst].i = vm->regs[src].i & 0xFFFFFFFF;
239 } break;
240 case OP_MOV16: {
241 u8 dst = instruction.dst;
242 u8 src = instruction.a;
243 vm->regs[dst].i = vm->regs[src].i & 0xFFFF;
244 } break;
245 case OP_MOV8: {
246 u8 dst = instruction.dst;
247 u8 src = instruction.a;
248 vm->regs[dst].i = vm->regs[src].i & 0xFF;
249 } break;
250 case OP_PRINTS64: {
251 u8 idx = instruction.dst;
252 print("%d", vm->regs[idx].i);
253 } break;
254 case OP_PRINTS64I: {
255 u8 idx = instruction.dst;
256 print("%d", vm->chunk->constants[idx].i);
257 } break;
258 case OP_PRINTF64: {
259 u8 idx = instruction.dst;
260 printf("%f", vm->regs[idx].f);
261 } break;
262 case OP_PRINTF64I: {
263 u8 idx = instruction.dst;
264 printf("%f", vm->chunk->constants[idx].f);
265 } break;
266 case OP_PRINTSTR: {
267 u8 idx = instruction.dst;
268 Str string = vm->chunk->strings[idx];
269 print("%s", string);
270 } break;
271 case OP_RESERVE: {
272 sz offset = vm->chunk->constants[instruction.dst].i;
273 vm->sp += offset;
274 } break;
275 case OP_PUSH: {
276 sz val = vm->regs[instruction.dst].i;
277 u64 *p = (u64 *)vm->sp;
278 *p = val;
279 vm->sp += sizeof(ptrsize);
280 } break;
281 case OP_PUSHI: {
282 sz val = vm->chunk->constants[instruction.dst].i;
283 u64 *p = (u64 *)vm->sp;
284 *p = val;
285 vm->sp += sizeof(ptrsize);
286 } break;
287 case OP_POP: {
288 vm->sp -= sizeof(ptrsize);
289 u64 *p = (u64 *)vm->sp;
290 vm->regs[instruction.dst].i = *p;
291 } break;
292 case OP_PUTRET: {
293 sz val = vm->regs[instruction.dst].i;
294 vm->fp[-1] = val;
295 } break;
296 case OP_PUTRETI: {
297 sz val = vm->chunk->constants[instruction.dst].i;
298 vm->fp[-1] = val;
299 } break;
300 case OP_CALL: {
301 u8 dst = instruction.dst;
302 Chunk *func = vm->chunk->functions[dst];
303
304 ptrsize chunk_addr = (ptrsize)vm->chunk;
305 ptrsize ip_addr = (ptrsize)vm->ip;
306 ptrsize reg_addr = (ptrsize)vm->regs;
307 ptrsize old_fp = (ptrsize)vm->fp;
308
309 // Allocate space for the locals.
310 vm->fp = (u64 *)vm->sp;
311 vm->sp += func->var_off; // FIXME: - func->n_params!
312 vm->chunk = func;
313 vm->ip = func->code;
314 vm->regs = (Constant *)vm->sp;
315
316 // Allocate registers.
317 vm->sp += sizeof(Constant) * func->reg_idx;
318
319 // Store memory addresses we need to return to this function.
320 u64 *p = (u64 *)vm->sp;
321 p[0] = chunk_addr;
322 p[1] = ip_addr;
323 p[2] = reg_addr;
324 p[3] = old_fp;
325 vm->sp += sizeof(ptrsize) * 4;
326 } break;
327 case OP_RET: {
328 u64 *p = (u64 *)vm->sp;
329 ptrsize chunk_addr = p[-4];
330 ptrsize ip_addr = p[-3];
331 ptrsize reg_addr = p[-2];
332 ptrsize old_fp = p[-1];
333 vm->sp -= sizeof(ptrsize) * 4;
334
335 // Deallocate registers.
336 vm->sp -= sizeof(Constant) * vm->chunk->reg_idx;
337
338 // Deallocate locals.
339 vm->sp -= vm->chunk->var_off;
340
341 // Restore previous activation record.
342 vm->regs = (Constant *)reg_addr;
343 vm->ip = (Instruction *)ip_addr;
344 vm->chunk = (Chunk *)chunk_addr;
345 vm->fp = (u64 *)old_fp;
346 } break;
159 case OP_HALT: { 347 case OP_HALT: {
160 println("VM HALT (int) -> %d", vm->regs[instruction.dst]); 348 println("VM halt...");
161 println("VM HALT (float) -> %f", vm->regs[instruction.dst]); 349 if (instruction.a != 0) {
162 println("VM HALT (hex) -> %x", vm->regs[instruction.dst]); 350 println("Result:");
351 Constant result = vm->regs[instruction.dst];
352 printf("\tint -> %lld\n", result.i);
353 printf("\tfloat -> %.10e\n", result.f);
354 printf("\thex -> %llx\n", (u64)result.i);
355 println("\tbinary -> %b", result.i);
356 }
163 return; 357 return;
164 } 358 }
165 default: { 359 default: {
166 eprintln("unimplemented OP code: %d", instruction.op); 360 // eprintln("unimplemented OP code: %d", instruction.op);
361 eprintln("unimplemented OP code: %s", op_str[instruction.op]);
167 return; 362 return;
168 } 363 }
169 } 364 }
diff --git a/tests/compilation.bad b/tests/compilation.bad
index 0ffbb06..ea467d2 100644
--- a/tests/compilation.bad
+++ b/tests/compilation.bad
@@ -1,30 +1,93 @@
1let b = 8 1fun adder(a: int): int {
2let a = 8 2 ; a + 1
3; { 3 4
4; let a = 1 + 2 * 4 4}
5; let b = 3.0 5adder(2)
6; } 6; let a = 1
7 7; let b = 8
81 + a 8
9; 0xf | 0xf0 9; a + b
10; 0xf & 0xff
11; 0x1 << 2
12; 0x1 << 2
13; (~0xf & 0xfff << 8) == 0xfff00
14; 0xf << 4 | 0xf
15; 1 + 2 10; 1 + 2
16; 1 < 2 11; fun greet(name: str): nil {
17; !(1 == 0 || 1 <= 1) 12; fun greet(name: str): nil {
18; 1 == 1 && 1 <= 1 13; println("hello ")
19; !((1 * 2 * 3) * (1 * 2 * 3) >= 3 && 4 != 3 + 2) 14; }
20; 1 + 2 * 3 15
21; 1.0 + 2.0 * 3.0 16; greet("alex")
22; true 17; greet("ding")
23; false 18; greet()
24; 0 1 19; if true {
25; "hello" 20; 1
26; "world" 21; nil
27; fun foo(): int { 22; }
28; 32 23; let a = 1
29; } 24; let p = @a
30; 1 + 2 + foo() 25; let arr: int[8]
26; set p[0] = 2
27
28; let b = p[0]
29; println("a: " a)
30; println("b: " b)
31; let i = 0
32; while i < 8 {
33; set arr[i] = i
34; set i = i + 1
35; }
36; println("arr[0]: " arr[0])
37; println("arr[1]: " arr[1])
38
39; let arr: int[8]
40; set arr[1] = 2
41
42; let idx = 3
43; set arr[idx] = 3
44
45; println("arr[0]: " arr[0])
46; println("arr[1]: " arr[1])
47; println("arr[2]: " arr[2])
48; println("arr[3]: " arr[3])
49
50; arr[0] + arr[1] + arr[2] + arr[3]
51
52; set a = 2
53
54; let a = cond {
55; 1 == 1 = 42
56; 1 == 3 = 55
57; 1 == 1 = 66
58; else = 10
59; }
60
61; a
62
63; let a = 8
64; let b = 16
65; let c = {
66; let a = 32
67; a + 8
68; }
69
70; a + b + c
71
72; let a = 1
73; if false {
74; set a = 2
75; }
76; a
77
78; if true {
79; 1 + 2
80; } else {
81; 3 + 4
82; }
83
84; let a = 0
85; while a < 10 {
86; set a = a + 1
87; }
88
89; a
90
91; print(1)
92; println(1.0 + 2.0)
93; println("hello world " 1 " " 2.0 " wow!")
diff --git a/tests/conditionals.bad b/tests/conditionals.bad
index aca5c36..85de62a 100644
--- a/tests/conditionals.bad
+++ b/tests/conditionals.bad
@@ -1,8 +1,8 @@
1; Basic if expressions. 1; Basic if expressions.
2if true "hello" 2if true "hello" else "world"
3 3
4; These can produce values and the result bound to a name. 4; These can produce values and the result bound to a name.
5let a = if (2 + 2 >= 4) 42 5let a = if (2 + 2 >= 4) 42 else 0
6 6
7; We support a single if expression. 7; We support a single if expression.
8let b = if (0xff == 0x32) "hello" else "world" 8let b = if (0xff == 0x32) "hello" else "world"
diff --git a/tests/semantics.bad b/tests/semantics.bad
index acf4316..befbfab 100644
--- a/tests/semantics.bad
+++ b/tests/semantics.bad
@@ -42,6 +42,7 @@ match a {
42cond { 42cond {
43 1 == 1 = "ha" 43 1 == 1 = "ha"
44 2 != 2 = "ho" 44 2 != 2 = "ho"
45 else = "let's go"
45} 46}
46 47
47struct vec { 48struct vec {
@@ -160,9 +161,7 @@ let maybe = if (1 == 2) {
160 44 161 44
161} 162}
162 163
163let single = if (true) { 164let single = if (true) 123 else 0
164 123
165}
166 165
167while (!true) { 166while (!true) {
168 println("asjdflasdjf") 167 println("asjdflasdjf")
@@ -185,5 +184,5 @@ fun nested(): int {
185 let c = 32 184 let c = 32
186 5 + c 185 5 + c
187 } 186 }
188 } 187 } else 0
189} 188}