diff options
Diffstat (limited to 'src/vm.c')
-rw-r--r-- | src/vm.c | 90 |
1 files changed, 65 insertions, 25 deletions
@@ -17,10 +17,22 @@ typedef union Constant { | |||
17 | 17 | ||
18 | typedef struct Chunk { | 18 | typedef struct Chunk { |
19 | Instruction *code; | 19 | Instruction *code; |
20 | |||
21 | // Constant values that fit in 64 bits. | ||
20 | Constant *constants; | 22 | Constant *constants; |
21 | Str file_name; | 23 | IntIntMap *intmap; |
22 | sz reg_idx; | ||
23 | sz const_idx; | 24 | sz const_idx; |
25 | |||
26 | // Constant strings. | ||
27 | Str *strings; | ||
28 | StrIntMap *strmap; | ||
29 | sz str_idx; | ||
30 | |||
31 | // Number of registers currently used in this chunk. | ||
32 | sz reg_idx; | ||
33 | |||
34 | // Debugging. | ||
35 | Str file_name; | ||
24 | Arena *storage; | 36 | Arena *storage; |
25 | // TODO: line/col info for debugging. | 37 | // TODO: line/col info for debugging. |
26 | } Chunk; | 38 | } Chunk; |
@@ -205,6 +217,12 @@ disassemble_chunk(Chunk chunk) { | |||
205 | chunk.constants[i]); | 217 | chunk.constants[i]); |
206 | } | 218 | } |
207 | } | 219 | } |
220 | if (array_size(chunk.strings) > 0) { | ||
221 | println("%s: ========== strings =========", chunk.file_name); | ||
222 | for (sz i = 0; i < array_size(chunk.strings); i++) { | ||
223 | println("%s: %x{2}: %s", chunk.file_name, i, chunk.strings[i]); | ||
224 | } | ||
225 | } | ||
208 | } | 226 | } |
209 | 227 | ||
210 | #define N_CONST 256 | 228 | #define N_CONST 256 |
@@ -292,6 +310,7 @@ vm_run(VM *vm) { | |||
292 | 310 | ||
293 | typedef enum { | 311 | typedef enum { |
294 | COMP_CONST, | 312 | COMP_CONST, |
313 | COMP_STRING, | ||
295 | COMP_REG, | 314 | COMP_REG, |
296 | COMP_ERR, | 315 | COMP_ERR, |
297 | } CompResultType; | 316 | } CompResultType; |
@@ -303,11 +322,19 @@ typedef struct CompResult { | |||
303 | 322 | ||
304 | CompResult compile_expr(Chunk *chunk, Node *node); | 323 | CompResult compile_expr(Chunk *chunk, Node *node); |
305 | 324 | ||
306 | // #define EMIT_OP(OP, CHUNK, ARENA) | 325 | #define EMIT_OP(OP, DST, A, B, NODE, CHUNK) \ |
326 | do { \ | ||
327 | Instruction inst = (Instruction){ \ | ||
328 | .op = (OP), \ | ||
329 | .dst = (DST), \ | ||
330 | .a = (A), \ | ||
331 | .b = (B), \ | ||
332 | }; \ | ||
333 | array_push((CHUNK)->code, inst, (CHUNK)->storage); \ | ||
334 | } while (0) | ||
307 | 335 | ||
308 | CompResult | 336 | CompResult |
309 | compile_binary(OpCode op, Chunk *chunk, Node *node) { | 337 | compile_binary(OpCode op, Chunk *chunk, Node *node) { |
310 | sz reg_dst = chunk->reg_idx++; | ||
311 | CompResult comp_a = compile_expr(chunk, node->left); | 338 | CompResult comp_a = compile_expr(chunk, node->left); |
312 | CompResult comp_b = compile_expr(chunk, node->right); | 339 | CompResult comp_b = compile_expr(chunk, node->right); |
313 | sz reg_a; | 340 | sz reg_a; |
@@ -315,9 +342,7 @@ compile_binary(OpCode op, Chunk *chunk, Node *node) { | |||
315 | switch (comp_a.type) { | 342 | switch (comp_a.type) { |
316 | case COMP_CONST: { | 343 | case COMP_CONST: { |
317 | reg_a = chunk->reg_idx++; | 344 | reg_a = chunk->reg_idx++; |
318 | Instruction inst = | 345 | EMIT_OP(OP_LD64K, reg_a, comp_a.idx, 0, node, chunk); |
319 | (Instruction){.op = OP_LD64K, .dst = reg_a, .a = comp_a.idx}; | ||
320 | array_push(chunk->code, inst, chunk->storage); | ||
321 | } break; | 346 | } break; |
322 | case COMP_REG: { | 347 | case COMP_REG: { |
323 | reg_a = comp_a.idx; | 348 | reg_a = comp_a.idx; |
@@ -329,9 +354,7 @@ compile_binary(OpCode op, Chunk *chunk, Node *node) { | |||
329 | switch (comp_b.type) { | 354 | switch (comp_b.type) { |
330 | case COMP_CONST: { | 355 | case COMP_CONST: { |
331 | reg_b = chunk->reg_idx++; | 356 | reg_b = chunk->reg_idx++; |
332 | Instruction inst = | 357 | EMIT_OP(OP_LD64K, reg_b, comp_b.idx, 0, node, chunk); |
333 | (Instruction){.op = OP_LD64K, .dst = reg_b, .a = comp_b.idx}; | ||
334 | array_push(chunk->code, inst, chunk->storage); | ||
335 | } break; | 358 | } break; |
336 | case COMP_REG: { | 359 | case COMP_REG: { |
337 | reg_b = comp_b.idx; | 360 | reg_b = comp_b.idx; |
@@ -340,9 +363,9 @@ compile_binary(OpCode op, Chunk *chunk, Node *node) { | |||
340 | return (CompResult){.type = COMP_ERR}; | 363 | return (CompResult){.type = COMP_ERR}; |
341 | } break; | 364 | } break; |
342 | } | 365 | } |
343 | Instruction inst = | 366 | sz reg_dst = comp_a.idx; // Less registers |
344 | (Instruction){.op = op, .dst = reg_dst, .a = reg_a, .b = reg_b}; | 367 | // sz reg_dst = chunk->reg_idx++; // Better for optimization |
345 | array_push(chunk->code, inst, chunk->storage); | 368 | EMIT_OP(op, reg_dst, reg_a, reg_b, node, chunk); |
346 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 369 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
347 | } | 370 | } |
348 | 371 | ||
@@ -354,26 +377,43 @@ compile_expr(Chunk *chunk, Node *node) { | |||
354 | case NODE_MUL: return compile_binary(OP_MUL, chunk, node); break; | 377 | case NODE_MUL: return compile_binary(OP_MUL, chunk, node); break; |
355 | case NODE_DIV: return compile_binary(OP_DIV, chunk, node); break; | 378 | case NODE_DIV: return compile_binary(OP_DIV, chunk, node); break; |
356 | case NODE_MOD: return compile_binary(OP_MOD, chunk, node); break; | 379 | case NODE_MOD: return compile_binary(OP_MOD, chunk, node); break; |
380 | case NODE_TRUE: | ||
381 | case NODE_FALSE: | ||
357 | case NODE_NUM_FLOAT: | 382 | case NODE_NUM_FLOAT: |
358 | case NODE_NUM_INT: { | 383 | case NODE_NUM_INT: { |
384 | sz value = node->value.i; | ||
359 | // Make sure we don't have duplicated constants. | 385 | // Make sure we don't have duplicated constants. |
360 | for (sz i = 0; i < chunk->const_idx; i++) { | 386 | IntIntMap *map = intintmap_lookup(&chunk->intmap, value); |
361 | if (node->value.i == chunk->constants[i].i) { | 387 | if (!map) { |
362 | return (CompResult){ | 388 | map = intintmap_insert(&chunk->intmap, value, |
363 | .type = COMP_CONST, | 389 | chunk->const_idx++, chunk->storage); |
364 | .idx = i, | 390 | Constant c = (Constant){.i = node->value.i}; |
365 | }; | 391 | array_push(chunk->constants, c, chunk->storage); |
366 | } | ||
367 | } | 392 | } |
368 | Constant c = (Constant){.i = node->value.i}; | ||
369 | array_push(chunk->constants, c, chunk->storage); | ||
370 | return (CompResult){ | 393 | return (CompResult){ |
371 | .type = COMP_CONST, | 394 | .type = COMP_CONST, |
372 | .idx = chunk->const_idx++, | 395 | .idx = map->val, |
396 | }; | ||
397 | } break; | ||
398 | case NODE_STRING: { | ||
399 | Str string = node->value.str; | ||
400 | // Make sure we don't have duplicated strings. | ||
401 | StrIntMap *map = strintmap_lookup(&chunk->strmap, string); | ||
402 | if (!map) { | ||
403 | map = strintmap_insert(&chunk->strmap, string, chunk->str_idx++, | ||
404 | chunk->storage); | ||
405 | array_push(chunk->strings, string, chunk->storage); | ||
406 | } | ||
407 | return (CompResult){ | ||
408 | .type = COMP_STRING, | ||
409 | .idx = map->val, | ||
373 | }; | 410 | }; |
374 | } break; | 411 | } break; |
375 | default: break; | 412 | default: { |
413 | eprintln("error: compilation not implemented for node %s", | ||
414 | node_str[node->kind]); | ||
415 | exit(EXIT_FAILURE); | ||
416 | } break; | ||
376 | } | 417 | } |
377 | return (CompResult){.type = COMP_ERR}; | 418 | return (CompResult){.type = COMP_ERR}; |
378 | } | 419 | } |
379 | |||