aboutsummaryrefslogtreecommitdiffstats
path: root/src/vm.c
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 /src/vm.c
parentc0faac681a32ffc2e323917f8b54f33558b391a5 (diff)
downloadbdl-972c453a085bedb3de8d598cc4ae4486e24f0144.tar.gz
bdl-972c453a085bedb3de8d598cc4ae4486e24f0144.zip
Move vm/chunk compiler to separate file
Diffstat (limited to 'src/vm.c')
-rw-r--r--src/vm.c379
1 files changed, 379 insertions, 0 deletions
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