diff options
author | Bad Diode <bd@badd10de.dev> | 2024-06-20 14:20:07 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-06-20 14:20:07 +0200 |
commit | 972c453a085bedb3de8d598cc4ae4486e24f0144 (patch) | |
tree | 02503240867fc8e77fbca4f7c900192e9a7873be /src/vm.c | |
parent | c0faac681a32ffc2e323917f8b54f33558b391a5 (diff) | |
download | bdl-972c453a085bedb3de8d598cc4ae4486e24f0144.tar.gz bdl-972c453a085bedb3de8d598cc4ae4486e24f0144.zip |
Move vm/chunk compiler to separate file
Diffstat (limited to 'src/vm.c')
-rw-r--r-- | src/vm.c | 379 |
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 | |||
4 | typedef struct Instruction { | ||
5 | u8 dst; | ||
6 | u8 a; | ||
7 | u8 b; | ||
8 | u8 op; | ||
9 | } Instruction; | ||
10 | |||
11 | typedef union Constant { | ||
12 | s64 i; | ||
13 | u64 u; | ||
14 | double f; | ||
15 | ptrsize ptr; | ||
16 | } Constant; | ||
17 | |||
18 | typedef 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 | |||
28 | typedef 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 | |||
78 | Str 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 | |||
130 | void | ||
131 | disassemble_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 | |||
194 | void | ||
195 | disassemble_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 | ||
211 | typedef struct VM { | ||
212 | Chunk *chunk; | ||
213 | Constant regs[N_CONST]; | ||
214 | Instruction *ip; | ||
215 | } VM; | ||
216 | |||
217 | void | ||
218 | vm_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 | |||
245 | void | ||
246 | vm_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 | |||
293 | typedef enum { | ||
294 | COMP_CONST, | ||
295 | COMP_REG, | ||
296 | COMP_ERR, | ||
297 | } CompResultType; | ||
298 | |||
299 | typedef struct CompResult { | ||
300 | sz idx; | ||
301 | CompResultType type; | ||
302 | } CompResult; | ||
303 | |||
304 | CompResult compile_expr(Chunk *chunk, Node *node); | ||
305 | |||
306 | // #define EMIT_OP(OP, CHUNK, ARENA) | ||
307 | |||
308 | CompResult | ||
309 | compile_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 | |||
349 | CompResult | ||
350 | compile_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 | |||