aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2024-06-20 14:20:07 +0200
committerBad Diode <bd@badd10de.dev>2024-06-20 14:20:07 +0200
commit972c453a085bedb3de8d598cc4ae4486e24f0144 (patch)
tree02503240867fc8e77fbca4f7c900192e9a7873be
parentc0faac681a32ffc2e323917f8b54f33558b391a5 (diff)
downloadbdl-972c453a085bedb3de8d598cc4ae4486e24f0144.tar.gz
bdl-972c453a085bedb3de8d598cc4ae4486e24f0144.zip
Move vm/chunk compiler to separate file
-rw-r--r--Makefile2
-rw-r--r--src/badlib.h4
-rw-r--r--src/main.c444
-rw-r--r--src/vm.c379
-rw-r--r--tests/functions.bad29
5 files changed, 452 insertions, 406 deletions
diff --git a/Makefile b/Makefile
index dd3f3ae..3153058 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ BUILD_DIR := build
7TESTS_DIR := tests 7TESTS_DIR := tests
8TEST_FILES := $(wildcard $(TESTS_DIR)/*.bad) 8TEST_FILES := $(wildcard $(TESTS_DIR)/*.bad)
9SRC_MAIN := $(SRC_DIR)/main.c 9SRC_MAIN := $(SRC_DIR)/main.c
10SRC_RUN := tests/expressions.bad 10SRC_RUN := tests/functions.bad
11WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h") 11WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h")
12INC_DIRS := $(shell find $(SRC_DIR) -type d) 12INC_DIRS := $(shell find $(SRC_DIR) -type d)
13INC_FLAGS := $(addprefix -I,$(INC_DIRS)) 13INC_FLAGS := $(addprefix -I,$(INC_DIRS))
diff --git a/src/badlib.h b/src/badlib.h
index 522e4bb..674045a 100644
--- a/src/badlib.h
+++ b/src/badlib.h
@@ -156,7 +156,9 @@ arena_create(sz cap, Allocator allocator) {
156 156
157void 157void
158arena_destroy(Arena *arena, Allocator allocator) { 158arena_destroy(Arena *arena, Allocator allocator) {
159 allocator.free(arena->beg, allocator.ctx); 159 if (arena) {
160 allocator.free(arena->beg, allocator.ctx);
161 }
160} 162}
161 163
162void 164void
diff --git a/src/main.c b/src/main.c
index 598a93f..909caa4 100644
--- a/src/main.c
+++ b/src/main.c
@@ -5,6 +5,7 @@
5#include "badlib.h" 5#include "badlib.h"
6#include "lexer.c" 6#include "lexer.c"
7#include "parser.c" 7#include "parser.c"
8#include "vm.c"
8 9
9typedef enum ExecMode { 10typedef enum ExecMode {
10 RUN_NORMAL, 11 RUN_NORMAL,
@@ -21,379 +22,13 @@ init(void) {
21 log_init_default(); 22 log_init_default();
22} 23}
23 24
24typedef struct Instruction {
25 u8 dst;
26 u8 a;
27 u8 b;
28 u8 op;
29} Instruction;
30
31typedef union Constant {
32 s64 i;
33 u64 u;
34 double f;
35 ptrsize ptr;
36} Constant;
37
38typedef struct Chunk {
39 Instruction *code;
40 Constant *constants;
41 Str file_name;
42 sz reg_idx;
43 sz const_idx;
44 Arena *storage;
45 // TODO: line/col info for debugging.
46} Chunk;
47
48typedef enum OpCode {
49 // OP DST A B
50 // ---------------------------------------------------------------
51 OP_HALT, // halt
52 OP_LD8K, // ld8k rx, ca -> u8 rx = ca
53 OP_LD16K, // ld16k rx, ca -> u16 rx = ca
54 OP_LD32K, // ld32k rx, ca -> u32 rx = ca
55 OP_LD64K, // ld64k rx, ca -> u64 rx = ca
56 OP_LD8I, // ld8i rx, ra, cb -> u8 *p; rx = p[ra + cb]
57 OP_LD16I, // ld16i rx, ra, cb -> u16 *p; rx = p[ra + cb]
58 OP_LD32I, // ld32i rx, ra, cb -> u32 *p; rx = p[ra + cb]
59 OP_LD64I, // ld64i rx, ra, cb -> u64 *p; rx = p[ra + cb]
60 OP_LD8, // ld8 rx, ra, rb -> u8 *p; rx = p[ra + rb]
61 OP_LD16, // ld16 rx, ra, rb -> u16 *p; rx = p[ra + rb]
62 OP_LD32, // ld32 rx, ra, rb -> u32 *p; rx = p[ra + rb]
63 OP_LD64, // ld64 rx, ra, rb -> u64 *p; rx = p[ra + rb]
64 OP_ST8I, // st8i rx, ra, cb -> u8 *p; p[ra + cb] = rx
65 OP_ST16I, // st16i rx, ra, cb -> u16 *p; p[ra + cb] = rx
66 OP_ST32I, // st32i rx, ra, cb -> u32 *p; p[ra + cb] = rx
67 OP_ST64I, // st64i rx, ra, cb -> u64 *p; p[ra + cb] = rx
68 OP_ST8, // st8 rx, ra, rb -> u8 *p; p[ra + rb] = rx
69 OP_ST16, // st16 rx, ra, rb -> u16 *p; p[ra + rb] = rx
70 OP_ST32, // st32 rx, ra, rb -> u32 *p; p[ra + rb] = rx
71 OP_ST64, // st64 rx, ra, rb -> u64 *p; p[ra + rb] = rx
72 OP_ADDI, // addk rx, ra, cb
73 OP_SUBI, // subk rx, ra, cb
74 OP_MULI, // mulk rx, ra, cb
75 OP_DIVI, // divk rx, ra, cb
76 OP_MODI, // modk rx, ra, cb
77 OP_ADD, // add rx, ra, rb
78 OP_SUB, // sub rx, ra, rb
79 OP_MUL, // mul rx, ra, rb
80 OP_DIV, // div rx, ra, rb
81 OP_MOD, // mod rx, ra, rb
82 OP_ADDFI, // addk rx, ra, cb
83 OP_SUBFI, // subk rx, ra, cb
84 OP_MULFI, // mulk rx, ra, cb
85 OP_DIVFI, // divk rx, ra, cb
86 OP_MODFI, // modk rx, ra, cb
87 OP_ADDF, // add rx, ra, rb
88 OP_SUBF, // sub rx, ra, rb
89 OP_MULF, // mul rx, ra, rb
90 OP_DIVF, // div rx, ra, rb
91 OP_MODF, // mod rx, ra, rb
92 OP_MOV8, // mov8 rx, ra -> rx = ra & 0xFF
93 OP_MOV16, // mov16 rx, ra -> rx = ra & 0xFFFF
94 OP_MOV32, // mov32 rx, ra -> rx = ra & 0xFFFFFFFF
95 OP_MOV64, // mov64 rx, ra -> rx = ra & 0xFFFFFFFFFFFFFFFF
96} OpCode;
97
98Str op_str[] = {
99 [OP_HALT] = cstr("HALT "),
100 // Load ops.
101 [OP_LD8K] = cstr("LD8K "),
102 [OP_LD16K] = cstr("LD16K "),
103 [OP_LD32K] = cstr("LD32K "),
104 [OP_LD64K] = cstr("LD64K "),
105 [OP_LD8I] = cstr("LD8I "),
106 [OP_LD16I] = cstr("LD16I "),
107 [OP_LD32I] = cstr("LD32I "),
108 [OP_LD64I] = cstr("LD64I "),
109 [OP_LD8] = cstr("LD8 "),
110 [OP_LD16] = cstr("LD16 "),
111 [OP_LD32] = cstr("LD32 "),
112 [OP_LD64] = cstr("LD64 "),
113 // Store ops.
114 [OP_ST8I] = cstr("ST8I "),
115 [OP_ST16I] = cstr("ST16I "),
116 [OP_ST32I] = cstr("ST32I "),
117 [OP_ST64I] = cstr("ST64I "),
118 [OP_ST8] = cstr("ST8 "),
119 [OP_ST16] = cstr("ST16 "),
120 [OP_ST32] = cstr("ST32 "),
121 [OP_ST64] = cstr("ST64 "),
122 // Arithmetic.
123 [OP_ADDI] = cstr("ADDI "),
124 [OP_SUBI] = cstr("SUBI "),
125 [OP_MULI] = cstr("MULI "),
126 [OP_DIVI] = cstr("DIVI "),
127 [OP_MODI] = cstr("MODI "),
128 [OP_ADD] = cstr("ADD "),
129 [OP_SUB] = cstr("SUB "),
130 [OP_MUL] = cstr("MUL "),
131 [OP_DIV] = cstr("DIV "),
132 [OP_MOD] = cstr("MOD "),
133 [OP_ADDFI] = cstr("ADDFI "),
134 [OP_SUBFI] = cstr("SUBFI "),
135 [OP_MULFI] = cstr("MULFI "),
136 [OP_DIVFI] = cstr("DIVFI "),
137 [OP_MODFI] = cstr("MODFI "),
138 [OP_ADDF] = cstr("ADDF "),
139 [OP_SUBF] = cstr("SUBF "),
140 [OP_MULF] = cstr("MULF "),
141 [OP_DIVF] = cstr("DIVF "),
142 // Reg copy/move.
143 [OP_MODF] = cstr("MODF "),
144 [OP_MOV8] = cstr("MOV8 "),
145 [OP_MOV16] = cstr("MOV16 "),
146 [OP_MOV32] = cstr("MOV32 "),
147 [OP_MOV64] = cstr("MOV64 "),
148};
149
150void
151disassemble_instruction(Instruction instruction) {
152 switch (instruction.op) {
153 case OP_MOV8:
154 case OP_MOV16:
155 case OP_MOV32:
156 case OP_MOV64:
157 println("%s r%d, r%d", op_str[instruction.op], instruction.dst,
158 instruction.a, instruction.b);
159 break;
160 case OP_LD8K:
161 case OP_LD16K:
162 case OP_LD32K:
163 case OP_LD64K:
164 println("%s r%d, c%d", op_str[instruction.op], instruction.dst,
165 instruction.a, instruction.b);
166 break;
167 case OP_LD8I:
168 case OP_LD16I:
169 case OP_LD32I:
170 case OP_LD64I:
171 case OP_ST8I:
172 case OP_ST16I:
173 case OP_ST32I:
174 case OP_ST64I:
175 case OP_ADDI:
176 case OP_SUBI:
177 case OP_MULI:
178 case OP_DIVI:
179 case OP_MODI:
180 case OP_ADDFI:
181 case OP_SUBFI:
182 case OP_MULFI:
183 case OP_DIVFI:
184 case OP_MODFI:
185 println("%s r%d, r%d, c%d", op_str[instruction.op], instruction.dst,
186 instruction.a, instruction.b);
187 break;
188 case OP_LD8:
189 case OP_LD16:
190 case OP_LD32:
191 case OP_LD64:
192 case OP_ST8:
193 case OP_ST16:
194 case OP_ST32:
195 case OP_ST64:
196 case OP_ADD:
197 case OP_SUB:
198 case OP_MUL:
199 case OP_DIV:
200 case OP_MOD:
201 case OP_ADDF:
202 case OP_SUBF:
203 case OP_MULF:
204 case OP_DIVF:
205 case OP_MODF:
206 println("%s r%d, r%d, r%d", op_str[instruction.op], instruction.dst,
207 instruction.a, instruction.b);
208 break;
209 case OP_HALT: println("%s", op_str[instruction.op]); break;
210 default: println("Unknown opcode %d", instruction.op); break;
211 }
212}
213
214void
215disassemble_chunk(Chunk chunk) {
216 println("%s: ========= code =========", chunk.file_name);
217 for (sz i = 0; i < array_size(chunk.code); i++) {
218 print("%s: %x{4}: ", chunk.file_name, i);
219 disassemble_instruction(chunk.code[i]);
220 }
221 if (array_size(chunk.constants) > 0) {
222 println("%s: ======= constants ======", chunk.file_name);
223 for (sz i = 0; i < array_size(chunk.constants); i++) {
224 println("%s: %x{2}: %x{8}", chunk.file_name, i, chunk.constants[i]);
225 }
226 }
227}
228
229#define N_CONST 256
230typedef struct VM {
231 Chunk *chunk;
232 Constant regs[N_CONST];
233 Instruction *ip;
234} VM;
235
236void
237vm_init(VM *vm, Chunk *chunk) {
238 assert(vm);
239 assert(chunk);
240 assert(chunk->code);
241 vm->chunk = chunk;
242 vm->ip = vm->chunk->code;
243}
244
245#define OP_ARITHMETIC(OP, TYPE) \
246 do { \
247 u8 dst = instruction.dst; \
248 u8 src_a = instruction.a; \
249 u8 src_b = instruction.b; \
250 vm->regs[dst].TYPE = vm->regs[src_a].TYPE OP vm->regs[src_b].TYPE; \
251 } while (0);
252
253#define OP_ARITHMETIC_CONST(OP, TYPE) \
254 do { \
255 u8 dst = instruction.dst; \
256 u8 src_a = instruction.a; \
257 u8 src_b = instruction.b; \
258 vm->regs[dst].TYPE = \
259 vm->regs[src_a].TYPE OP vm->chunk->constants[src_b].TYPE; \
260 } while (0);
261
262#include <math.h>
263
264void
265vm_run(VM *vm) {
266 assert(vm);
267 assert(vm->chunk);
268 assert(vm->ip);
269 println("VM running...");
270 while (true) {
271 Instruction instruction = *vm->ip++;
272#if DEBUG == 1
273 print("IP: %d -> ", vm->ip - vm->chunk->code - 1);
274 disassemble_instruction(instruction);
275#endif
276
277 switch (instruction.op) {
278 case OP_LD64K: {
279 u8 dst = instruction.dst;
280 u8 src_a = instruction.a;
281 vm->regs[dst].i = vm->chunk->constants[src_a].i;
282 } break;
283 case OP_ADD: OP_ARITHMETIC(+, i) break;
284 case OP_SUB: OP_ARITHMETIC(-, i) break;
285 case OP_MUL: OP_ARITHMETIC(*, i) break;
286 case OP_DIV: OP_ARITHMETIC(/, i) break;
287 case OP_MOD: OP_ARITHMETIC(%, i) break;
288 case OP_ADDF: OP_ARITHMETIC(+, f) break;
289 case OP_SUBF: OP_ARITHMETIC(-, f) break;
290 case OP_MULF: OP_ARITHMETIC(*, f) break;
291 case OP_DIVF: OP_ARITHMETIC(/, f) break;
292 case OP_MODF: {
293 u8 dst = instruction.dst;
294 u8 src_a = instruction.a;
295 u8 src_b = instruction.b;
296 vm->regs[dst].f =
297 fmod(vm->regs[src_a].f, vm->chunk->constants[src_b].f);
298 } break;
299 case OP_HALT: {
300 println("VM HALT -> %d", vm->regs[instruction.dst]);
301 return;
302 }
303
304 default: {
305 eprintln("unimplemented OP code: %d", instruction.op);
306 return;
307 }
308 }
309 }
310}
311
312typedef enum {
313 COMP_CONST,
314 COMP_REG,
315 COMP_ERR,
316} CompResultType;
317
318typedef struct CompResult {
319 sz idx;
320 CompResultType type;
321} CompResult;
322
323CompResult compile_expr(Chunk *chunk, Node *node);
324
325// #define EMIT_OP(OP, CHUNK, ARENA)
326
327CompResult
328compile_binary(OpCode op, Chunk *chunk, Node *node) {
329 sz reg_dst = chunk->reg_idx++;
330 CompResult comp_a = compile_expr(chunk, node->left);
331 CompResult comp_b = compile_expr(chunk, node->right);
332 sz reg_a;
333 sz reg_b;
334 switch (comp_a.type) {
335 case COMP_CONST: {
336 reg_a = chunk->reg_idx++;
337 Instruction inst =
338 (Instruction){.op = OP_LD64K, .dst = reg_a, .a = comp_a.idx};
339 array_push(chunk->code, inst, chunk->storage);
340 } break;
341 case COMP_REG: {
342 reg_a = comp_a.idx;
343 } break;
344 default: {
345 return (CompResult){.type = COMP_ERR};
346 } break;
347 }
348 switch (comp_b.type) {
349 case COMP_CONST: {
350 reg_b = chunk->reg_idx++;
351 Instruction inst =
352 (Instruction){.op = OP_LD64K, .dst = reg_b, .a = comp_b.idx};
353 array_push(chunk->code, inst, chunk->storage);
354 } break;
355 case COMP_REG: {
356 reg_b = comp_b.idx;
357 } break;
358 default: {
359 return (CompResult){.type = COMP_ERR};
360 } break;
361 }
362 Instruction inst =
363 (Instruction){.op = op, .dst = reg_dst, .a = reg_a, .b = reg_b};
364 array_push(chunk->code, inst, chunk->storage);
365 return (CompResult){.type = COMP_REG, .idx = reg_dst};
366}
367
368CompResult
369compile_expr(Chunk *chunk, Node *node) {
370 switch (node->kind) {
371 case NODE_ADD: return compile_binary(OP_ADD, chunk, node); break;
372 case NODE_SUB: return compile_binary(OP_SUB, chunk, node); break;
373 case NODE_MUL: return compile_binary(OP_MUL, chunk, node); break;
374 case NODE_DIV: return compile_binary(OP_DIV, chunk, node); break;
375 case NODE_MOD: return compile_binary(OP_MOD, chunk, node); break;
376 case NODE_NUM_FLOAT:
377 case NODE_NUM_INT: {
378 Constant c = (Constant){.i = node->value.i};
379 array_push(chunk->constants, c, chunk->storage);
380 return (CompResult){
381 .type = COMP_CONST,
382 .idx = chunk->const_idx++,
383 };
384 } break;
385 default: break;
386 }
387 return (CompResult){.type = COMP_ERR};
388}
389
390void 25void
391process_file(Str path) { 26process_file(Str path) {
392 Arena lexer_arena = arena_create(LEXER_MEM, os_allocator); 27 Arena lexer_arena = arena_create(LEXER_MEM, os_allocator);
393 28
394 FileContents file = platform_read_file(path, &lexer_arena); 29 FileContents file = platform_read_file(path, &lexer_arena);
395 if (file.err) { 30 if (file.err) {
396 eprintln("%s: error: %s", path, cstr("WOT")); 31 eprintln("%s: error: %s", path, cstr("couldn't read the file"));
397 exit(EXIT_FAILURE); 32 exit(EXIT_FAILURE);
398 } 33 }
399 sz errors = 0; 34 sz errors = 0;
@@ -432,7 +67,7 @@ process_file(Str path) {
432 while (parser.current.kind != TOK_EOF) { 67 while (parser.current.kind != TOK_EOF) {
433#if DEBUG == 1 68#if DEBUG == 1
434 static sz ctr = 0; 69 static sz ctr = 0;
435 println("parsing root: %d", ctr++); 70 println("ROOT: %d", ctr++);
436#endif 71#endif
437 parse_expr(&parser, PREC_LOW); 72 parse_expr(&parser, PREC_LOW);
438 if (parser.panic) { 73 if (parser.panic) {
@@ -451,44 +86,45 @@ process_file(Str path) {
451 // TODO: Type checking. 86 // TODO: Type checking.
452 87
453 // Compile roots. 88 // Compile roots.
454 Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator); 89 // Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator);
455 Chunk chunk = {.file_name = path, .storage = &bytecode_arena}; 90 // Chunk chunk = {.file_name = path, .storage = &bytecode_arena};
456 array_zero(chunk.constants, 256, &bytecode_arena); 91 // array_zero(chunk.constants, 256, &bytecode_arena);
457 array_zero(chunk.code, 0xffff, &bytecode_arena); 92 // array_zero(chunk.code, 0xffff, &bytecode_arena);
458 sz n_roots = array_size(parser.nodes); 93 // sz n_roots = array_size(parser.nodes);
459 CompResult res; 94 // CompResult res;
460 for (sz i = 0; i < n_roots; i++) { 95 // for (sz i = 0; i < n_roots; i++) {
461 // The parser stores the root nodes as a stack. 96 // // The parser stores the root nodes as a stack.
462 Node *root = parser.nodes[i]; 97 // Node *root = parser.nodes[i];
463 res = compile_expr(&chunk, root); 98 // res = compile_expr(&chunk, root);
464 } 99 // }
465 sz res_reg = 0; 100 // sz res_reg = 0;
466 switch (res.type) { 101 // switch (res.type) {
467 case COMP_CONST: { 102 // case COMP_CONST: {
468 res_reg = chunk.reg_idx++; 103 // res_reg = chunk.reg_idx++;
469 Instruction inst = 104 // Instruction inst =
470 (Instruction){.op = OP_LD64K, .dst = res_reg, .a = res.idx}; 105 // (Instruction){.op = OP_LD64K, .dst = res_reg, .a = res.idx};
471 array_push(chunk.code, inst, chunk.storage); 106 // array_push(chunk.code, inst, chunk.storage);
472 } break; 107 // } break;
473 case COMP_REG: { 108 // case COMP_REG: {
474 res_reg = res.idx; 109 // res_reg = res.idx;
475 } break; 110 // } break;
476 default: break; 111 // default: break;
477 } 112 // }
478 // After we are done move the last result to r0 for printing. 113 // // After we are done move the last result to r0 for printing.
479 Instruction halt = (Instruction){.op = OP_HALT, .dst = res_reg}; 114 // Instruction halt = (Instruction){.op = OP_HALT, .dst = res_reg};
480 array_push(chunk.code, halt, &bytecode_arena); 115 // array_push(chunk.code, halt, &bytecode_arena);
481 116
482 // Run bytecode on VM. 117 // // Run bytecode on VM.
483 VM vm = {0}; 118 // VM vm = {0};
484 vm_init(&vm, &chunk); 119 // vm_init(&vm, &chunk);
485 // println("VM REGISTERS BEFORE:\n%{Mem}", 120 // // println("VM REGISTERS BEFORE:\n%{Mem}",
121 // // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)});
122 // vm_run(&vm);
123 // println("VM REGISTERS AFTER:\n%{Mem}",
486 // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); 124 // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)});
487 vm_run(&vm); 125 // disassemble_chunk(chunk);
488 println("VM REGISTERS AFTER:\n%{Mem}",
489 &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)});
490 disassemble_chunk(chunk);
491 126
127 // arena_destroy(&bytecode_arena, os_allocator);
492stop: 128stop:
493 // Free up resources. 129 // Free up resources.
494 arena_destroy(&lexer_arena, os_allocator); 130 arena_destroy(&lexer_arena, os_allocator);
diff --git a/src/vm.c b/src/vm.c
new file mode 100644
index 0000000..574f4fa
--- /dev/null
+++ b/src/vm.c
@@ -0,0 +1,379 @@
1#include "badlib.h"
2#include "parser.c"
3
4typedef struct Instruction {
5 u8 dst;
6 u8 a;
7 u8 b;
8 u8 op;
9} Instruction;
10
11typedef union Constant {
12 s64 i;
13 u64 u;
14 double f;
15 ptrsize ptr;
16} Constant;
17
18typedef struct Chunk {
19 Instruction *code;
20 Constant *constants;
21 Str file_name;
22 sz reg_idx;
23 sz const_idx;
24 Arena *storage;
25 // TODO: line/col info for debugging.
26} Chunk;
27
28typedef enum OpCode {
29 // OP DST A B
30 // ---------------------------------------------------------------
31 OP_HALT, // halt
32 OP_LD8K, // ld8k rx, ca -> u8 rx = ca
33 OP_LD16K, // ld16k rx, ca -> u16 rx = ca
34 OP_LD32K, // ld32k rx, ca -> u32 rx = ca
35 OP_LD64K, // ld64k rx, ca -> u64 rx = ca
36 OP_LD8I, // ld8i rx, ra, cb -> u8 *p; rx = p[ra + cb]
37 OP_LD16I, // ld16i rx, ra, cb -> u16 *p; rx = p[ra + cb]
38 OP_LD32I, // ld32i rx, ra, cb -> u32 *p; rx = p[ra + cb]
39 OP_LD64I, // ld64i rx, ra, cb -> u64 *p; rx = p[ra + cb]
40 OP_LD8, // ld8 rx, ra, rb -> u8 *p; rx = p[ra + rb]
41 OP_LD16, // ld16 rx, ra, rb -> u16 *p; rx = p[ra + rb]
42 OP_LD32, // ld32 rx, ra, rb -> u32 *p; rx = p[ra + rb]
43 OP_LD64, // ld64 rx, ra, rb -> u64 *p; rx = p[ra + rb]
44 OP_ST8I, // st8i rx, ra, cb -> u8 *p; p[ra + cb] = rx
45 OP_ST16I, // st16i rx, ra, cb -> u16 *p; p[ra + cb] = rx
46 OP_ST32I, // st32i rx, ra, cb -> u32 *p; p[ra + cb] = rx
47 OP_ST64I, // st64i rx, ra, cb -> u64 *p; p[ra + cb] = rx
48 OP_ST8, // st8 rx, ra, rb -> u8 *p; p[ra + rb] = rx
49 OP_ST16, // st16 rx, ra, rb -> u16 *p; p[ra + rb] = rx
50 OP_ST32, // st32 rx, ra, rb -> u32 *p; p[ra + rb] = rx
51 OP_ST64, // st64 rx, ra, rb -> u64 *p; p[ra + rb] = rx
52 OP_ADDI, // addk rx, ra, cb
53 OP_SUBI, // subk rx, ra, cb
54 OP_MULI, // mulk rx, ra, cb
55 OP_DIVI, // divk rx, ra, cb
56 OP_MODI, // modk rx, ra, cb
57 OP_ADD, // add rx, ra, rb
58 OP_SUB, // sub rx, ra, rb
59 OP_MUL, // mul rx, ra, rb
60 OP_DIV, // div rx, ra, rb
61 OP_MOD, // mod rx, ra, rb
62 OP_ADDFI, // addk rx, ra, cb
63 OP_SUBFI, // subk rx, ra, cb
64 OP_MULFI, // mulk rx, ra, cb
65 OP_DIVFI, // divk rx, ra, cb
66 OP_MODFI, // modk rx, ra, cb
67 OP_ADDF, // add rx, ra, rb
68 OP_SUBF, // sub rx, ra, rb
69 OP_MULF, // mul rx, ra, rb
70 OP_DIVF, // div rx, ra, rb
71 OP_MODF, // mod rx, ra, rb
72 OP_MOV8, // mov8 rx, ra -> rx = ra & 0xFF
73 OP_MOV16, // mov16 rx, ra -> rx = ra & 0xFFFF
74 OP_MOV32, // mov32 rx, ra -> rx = ra & 0xFFFFFFFF
75 OP_MOV64, // mov64 rx, ra -> rx = ra & 0xFFFFFFFFFFFFFFFF
76} OpCode;
77
78Str op_str[] = {
79 [OP_HALT] = cstr("HALT "),
80 // Load ops.
81 [OP_LD8K] = cstr("LD8K "),
82 [OP_LD16K] = cstr("LD16K "),
83 [OP_LD32K] = cstr("LD32K "),
84 [OP_LD64K] = cstr("LD64K "),
85 [OP_LD8I] = cstr("LD8I "),
86 [OP_LD16I] = cstr("LD16I "),
87 [OP_LD32I] = cstr("LD32I "),
88 [OP_LD64I] = cstr("LD64I "),
89 [OP_LD8] = cstr("LD8 "),
90 [OP_LD16] = cstr("LD16 "),
91 [OP_LD32] = cstr("LD32 "),
92 [OP_LD64] = cstr("LD64 "),
93 // Store ops.
94 [OP_ST8I] = cstr("ST8I "),
95 [OP_ST16I] = cstr("ST16I "),
96 [OP_ST32I] = cstr("ST32I "),
97 [OP_ST64I] = cstr("ST64I "),
98 [OP_ST8] = cstr("ST8 "),
99 [OP_ST16] = cstr("ST16 "),
100 [OP_ST32] = cstr("ST32 "),
101 [OP_ST64] = cstr("ST64 "),
102 // Arithmetic.
103 [OP_ADDI] = cstr("ADDI "),
104 [OP_SUBI] = cstr("SUBI "),
105 [OP_MULI] = cstr("MULI "),
106 [OP_DIVI] = cstr("DIVI "),
107 [OP_MODI] = cstr("MODI "),
108 [OP_ADD] = cstr("ADD "),
109 [OP_SUB] = cstr("SUB "),
110 [OP_MUL] = cstr("MUL "),
111 [OP_DIV] = cstr("DIV "),
112 [OP_MOD] = cstr("MOD "),
113 [OP_ADDFI] = cstr("ADDFI "),
114 [OP_SUBFI] = cstr("SUBFI "),
115 [OP_MULFI] = cstr("MULFI "),
116 [OP_DIVFI] = cstr("DIVFI "),
117 [OP_MODFI] = cstr("MODFI "),
118 [OP_ADDF] = cstr("ADDF "),
119 [OP_SUBF] = cstr("SUBF "),
120 [OP_MULF] = cstr("MULF "),
121 [OP_DIVF] = cstr("DIVF "),
122 // Reg copy/move.
123 [OP_MODF] = cstr("MODF "),
124 [OP_MOV8] = cstr("MOV8 "),
125 [OP_MOV16] = cstr("MOV16 "),
126 [OP_MOV32] = cstr("MOV32 "),
127 [OP_MOV64] = cstr("MOV64 "),
128};
129
130void
131disassemble_instruction(Instruction instruction) {
132 switch (instruction.op) {
133 case OP_MOV8:
134 case OP_MOV16:
135 case OP_MOV32:
136 case OP_MOV64:
137 println("%s r%d, r%d", op_str[instruction.op], instruction.dst,
138 instruction.a, instruction.b);
139 break;
140 case OP_LD8K:
141 case OP_LD16K:
142 case OP_LD32K:
143 case OP_LD64K:
144 println("%s r%d, c%d", op_str[instruction.op], instruction.dst,
145 instruction.a, instruction.b);
146 break;
147 case OP_LD8I:
148 case OP_LD16I:
149 case OP_LD32I:
150 case OP_LD64I:
151 case OP_ST8I:
152 case OP_ST16I:
153 case OP_ST32I:
154 case OP_ST64I:
155 case OP_ADDI:
156 case OP_SUBI:
157 case OP_MULI:
158 case OP_DIVI:
159 case OP_MODI:
160 case OP_ADDFI:
161 case OP_SUBFI:
162 case OP_MULFI:
163 case OP_DIVFI:
164 case OP_MODFI:
165 println("%s r%d, r%d, c%d", op_str[instruction.op], instruction.dst,
166 instruction.a, instruction.b);
167 break;
168 case OP_LD8:
169 case OP_LD16:
170 case OP_LD32:
171 case OP_LD64:
172 case OP_ST8:
173 case OP_ST16:
174 case OP_ST32:
175 case OP_ST64:
176 case OP_ADD:
177 case OP_SUB:
178 case OP_MUL:
179 case OP_DIV:
180 case OP_MOD:
181 case OP_ADDF:
182 case OP_SUBF:
183 case OP_MULF:
184 case OP_DIVF:
185 case OP_MODF:
186 println("%s r%d, r%d, r%d", op_str[instruction.op], instruction.dst,
187 instruction.a, instruction.b);
188 break;
189 case OP_HALT: println("%s", op_str[instruction.op]); break;
190 default: println("Unknown opcode %d", instruction.op); break;
191 }
192}
193
194void
195disassemble_chunk(Chunk chunk) {
196 println("%s: =========== code ===========", chunk.file_name);
197 for (sz i = 0; i < array_size(chunk.code); i++) {
198 print("%s: %x{4}: ", chunk.file_name, i);
199 disassemble_instruction(chunk.code[i]);
200 }
201 if (array_size(chunk.constants) > 0) {
202 println("%s: ========= constants ========", chunk.file_name);
203 for (sz i = 0; i < array_size(chunk.constants); i++) {
204 println("%s: %x{2}: %x{8}", chunk.file_name, i,
205 chunk.constants[i]);
206 }
207 }
208}
209
210#define N_CONST 256
211typedef struct VM {
212 Chunk *chunk;
213 Constant regs[N_CONST];
214 Instruction *ip;
215} VM;
216
217void
218vm_init(VM *vm, Chunk *chunk) {
219 assert(vm);
220 assert(chunk);
221 assert(chunk->code);
222 vm->chunk = chunk;
223 vm->ip = vm->chunk->code;
224}
225
226#define OP_ARITHMETIC(OP, TYPE) \
227 do { \
228 u8 dst = instruction.dst; \
229 u8 src_a = instruction.a; \
230 u8 src_b = instruction.b; \
231 vm->regs[dst].TYPE = vm->regs[src_a].TYPE OP vm->regs[src_b].TYPE; \
232 } while (0);
233
234#define OP_ARITHMETIC_CONST(OP, TYPE) \
235 do { \
236 u8 dst = instruction.dst; \
237 u8 src_a = instruction.a; \
238 u8 src_b = instruction.b; \
239 vm->regs[dst].TYPE = \
240 vm->regs[src_a].TYPE OP vm->chunk->constants[src_b].TYPE; \
241 } while (0);
242
243#include <math.h>
244
245void
246vm_run(VM *vm) {
247 assert(vm);
248 assert(vm->chunk);
249 assert(vm->ip);
250 println("VM running...");
251 while (true) {
252 Instruction instruction = *vm->ip++;
253#if DEBUG == 1
254 print("IP: %d -> ", vm->ip - vm->chunk->code - 1);
255 disassemble_instruction(instruction);
256#endif
257
258 switch (instruction.op) {
259 case OP_LD64K: {
260 u8 dst = instruction.dst;
261 u8 src_a = instruction.a;
262 vm->regs[dst].i = vm->chunk->constants[src_a].i;
263 } break;
264 case OP_ADD: OP_ARITHMETIC(+, i) break;
265 case OP_SUB: OP_ARITHMETIC(-, i) break;
266 case OP_MUL: OP_ARITHMETIC(*, i) break;
267 case OP_DIV: OP_ARITHMETIC(/, i) break;
268 case OP_MOD: OP_ARITHMETIC(%, i) break;
269 case OP_ADDF: OP_ARITHMETIC(+, f) break;
270 case OP_SUBF: OP_ARITHMETIC(-, f) break;
271 case OP_MULF: OP_ARITHMETIC(*, f) break;
272 case OP_DIVF: OP_ARITHMETIC(/, f) break;
273 case OP_MODF: {
274 u8 dst = instruction.dst;
275 u8 src_a = instruction.a;
276 u8 src_b = instruction.b;
277 vm->regs[dst].f =
278 fmod(vm->regs[src_a].f, vm->chunk->constants[src_b].f);
279 } break;
280 case OP_HALT: {
281 println("VM HALT -> %d", vm->regs[instruction.dst]);
282 return;
283 }
284
285 default: {
286 eprintln("unimplemented OP code: %d", instruction.op);
287 return;
288 }
289 }
290 }
291}
292
293typedef enum {
294 COMP_CONST,
295 COMP_REG,
296 COMP_ERR,
297} CompResultType;
298
299typedef struct CompResult {
300 sz idx;
301 CompResultType type;
302} CompResult;
303
304CompResult compile_expr(Chunk *chunk, Node *node);
305
306// #define EMIT_OP(OP, CHUNK, ARENA)
307
308CompResult
309compile_binary(OpCode op, Chunk *chunk, Node *node) {
310 sz reg_dst = chunk->reg_idx++;
311 CompResult comp_a = compile_expr(chunk, node->left);
312 CompResult comp_b = compile_expr(chunk, node->right);
313 sz reg_a;
314 sz reg_b;
315 switch (comp_a.type) {
316 case COMP_CONST: {
317 reg_a = chunk->reg_idx++;
318 Instruction inst =
319 (Instruction){.op = OP_LD64K, .dst = reg_a, .a = comp_a.idx};
320 array_push(chunk->code, inst, chunk->storage);
321 } break;
322 case COMP_REG: {
323 reg_a = comp_a.idx;
324 } break;
325 default: {
326 return (CompResult){.type = COMP_ERR};
327 } break;
328 }
329 switch (comp_b.type) {
330 case COMP_CONST: {
331 reg_b = chunk->reg_idx++;
332 Instruction inst =
333 (Instruction){.op = OP_LD64K, .dst = reg_b, .a = comp_b.idx};
334 array_push(chunk->code, inst, chunk->storage);
335 } break;
336 case COMP_REG: {
337 reg_b = comp_b.idx;
338 } break;
339 default: {
340 return (CompResult){.type = COMP_ERR};
341 } break;
342 }
343 Instruction inst =
344 (Instruction){.op = op, .dst = reg_dst, .a = reg_a, .b = reg_b};
345 array_push(chunk->code, inst, chunk->storage);
346 return (CompResult){.type = COMP_REG, .idx = reg_dst};
347}
348
349CompResult
350compile_expr(Chunk *chunk, Node *node) {
351 switch (node->kind) {
352 case NODE_ADD: return compile_binary(OP_ADD, chunk, node); break;
353 case NODE_SUB: return compile_binary(OP_SUB, chunk, node); break;
354 case NODE_MUL: return compile_binary(OP_MUL, chunk, node); break;
355 case NODE_DIV: return compile_binary(OP_DIV, chunk, node); break;
356 case NODE_MOD: return compile_binary(OP_MOD, chunk, node); break;
357 case NODE_NUM_FLOAT:
358 case NODE_NUM_INT: {
359 // Make sure we don't have duplicated constants.
360 for (sz i = 0; i < chunk->const_idx; i++) {
361 if (node->value.i == chunk->constants[i].i) {
362 return (CompResult){
363 .type = COMP_CONST,
364 .idx = i,
365 };
366 }
367 }
368 Constant c = (Constant){.i = node->value.i};
369 array_push(chunk->constants, c, chunk->storage);
370 return (CompResult){
371 .type = COMP_CONST,
372 .idx = chunk->const_idx++,
373 };
374 } break;
375 default: break;
376 }
377 return (CompResult){.type = COMP_ERR};
378}
379
diff --git a/tests/functions.bad b/tests/functions.bad
new file mode 100644
index 0000000..0d2e2d0
--- /dev/null
+++ b/tests/functions.bad
@@ -0,0 +1,29 @@
1; Simple function declarations don't need a block.
2fun add_two(a: int, b: int): int a + b
3
4; Functions return the result of the last expression implicitly.
5fun add_three(a: int, b: int, c: int): int {
6 a + b + c
7}
8
9; Functions that don't return anything must explicitly state it as returning
10; nil.
11fun foo(): nil {
12 write("hello buddy")
13}
14
15; An alternate form for empty return values.
16fun bar(): () {
17 write("hello world")
18}
19
20; We support multiple return values, in this case the "return" keyword must be
21; explict.
22fun baz(a: int, b: str): (int, str) {
23 return(1, "hello")
24}
25
26; Make sure we can use pointer types on params and return values.
27fun test(a: @int[256], b: @str): @int {
28 a
29}