diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler.c | 136 | ||||
-rw-r--r-- | src/vm.c | 60 |
2 files changed, 185 insertions, 11 deletions
diff --git a/src/compiler.c b/src/compiler.c index 84cc99a..5bddcb6 100644 --- a/src/compiler.c +++ b/src/compiler.c | |||
@@ -148,6 +148,13 @@ typedef enum OpCode { | |||
148 | OP_BITAND, // band rx, ra, rb | 148 | OP_BITAND, // band rx, ra, rb |
149 | OP_BITOR, // bor rx, ra, rb | 149 | OP_BITOR, // bor rx, ra, rb |
150 | OP_BITNOT, // bnot rx, ra | 150 | OP_BITNOT, // bnot rx, ra |
151 | // Jump instructions. | ||
152 | OP_JMPI, // jmp cx ; cx := signed offset | ||
153 | OP_JMPFI, // jmpf cx, ca ; rx := condition, ca := offset | ||
154 | OP_JMPTI, // jmpt cx, ca ; rx := condition, ca := offset | ||
155 | OP_JMP, // jmp rx ; rx := signed offset | ||
156 | OP_JMPF, // jmpf rx, ca ; rx := condition, ca := offset | ||
157 | OP_JMPT, // jmpt rx, ca ; rx := condition, ca := offset | ||
151 | } OpCode; | 158 | } OpCode; |
152 | 159 | ||
153 | Str op_str[] = { | 160 | Str op_str[] = { |
@@ -233,6 +240,13 @@ Str op_str[] = { | |||
233 | [OP_BITAND] = cstr("BAND "), | 240 | [OP_BITAND] = cstr("BAND "), |
234 | [OP_BITOR] = cstr("BOR "), | 241 | [OP_BITOR] = cstr("BOR "), |
235 | [OP_BITNOT] = cstr("BNOT "), | 242 | [OP_BITNOT] = cstr("BNOT "), |
243 | // Jump instructions. | ||
244 | [OP_JMPI] = cstr("JMPI "), | ||
245 | [OP_JMPFI] = cstr("JMPFI "), | ||
246 | [OP_JMPTI] = cstr("JMPTI "), | ||
247 | [OP_JMP] = cstr("JMP "), | ||
248 | [OP_JMPF] = cstr("JMPF "), | ||
249 | [OP_JMPT] = cstr("JMPT "), | ||
236 | }; | 250 | }; |
237 | 251 | ||
238 | typedef enum { | 252 | typedef enum { |
@@ -434,9 +448,105 @@ compile_unary(Chunk *chunk, Node *node) { | |||
434 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 448 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
435 | } | 449 | } |
436 | 450 | ||
451 | sz | ||
452 | add_constant(Chunk *chunk, sz value) { | ||
453 | IntIntMap *map = intintmap_lookup(&chunk->intmap, value); | ||
454 | // Make sure we don't have duplicated constants. | ||
455 | if (!map) { | ||
456 | map = intintmap_insert(&chunk->intmap, value, chunk->const_idx++, | ||
457 | chunk->storage); | ||
458 | Constant c = (Constant){.i = value}; | ||
459 | array_push(chunk->constants, c, chunk->storage); | ||
460 | } | ||
461 | return map->val; | ||
462 | } | ||
463 | |||
464 | CompResult | ||
465 | compile_if(Chunk *chunk, Node *node) { | ||
466 | CompResult cond = compile_expr(chunk, node->cond_if); | ||
467 | OpCode jmpop; | ||
468 | switch (cond.type) { | ||
469 | case COMP_CONST: { | ||
470 | jmpop = OP_JMPFI; | ||
471 | } break; | ||
472 | case COMP_REG: { | ||
473 | jmpop = OP_JMPF; | ||
474 | } break; | ||
475 | default: { | ||
476 | return (CompResult){.type = COMP_ERR}; | ||
477 | } break; | ||
478 | } | ||
479 | sz jump_a = array_size(chunk->code); | ||
480 | sz reg_dst = 255; | ||
481 | bool has_value = !str_eq(node->type, cstr("nil")); | ||
482 | if (has_value) { | ||
483 | reg_dst = chunk->reg_idx++; | ||
484 | } | ||
485 | |||
486 | // Jump to the `false` branch. | ||
487 | EMIT_OP(jmpop, cond.idx, 0xff, 0, node->cond_if, chunk); | ||
488 | |||
489 | // Condition is true. | ||
490 | CompResult then_expr = compile_expr(chunk, node->cond_expr); | ||
491 | if (has_value) { | ||
492 | switch (then_expr.type) { | ||
493 | case COMP_CONST: { | ||
494 | EMIT_OP(OP_LD64K, reg_dst, then_expr.idx, 0, node->cond_if, | ||
495 | chunk); | ||
496 | } break; | ||
497 | case COMP_REG: { | ||
498 | EMIT_OP(OP_MOV64, reg_dst, then_expr.idx, 0, node->cond_if, | ||
499 | chunk); | ||
500 | } break; | ||
501 | default: { | ||
502 | return (CompResult){.type = COMP_ERR}; | ||
503 | } break; | ||
504 | } | ||
505 | } | ||
506 | |||
507 | // Jump to the end of the expression. | ||
508 | sz jump_b = array_size(chunk->code); | ||
509 | EMIT_OP(OP_JMPI, 0xff, 0, 0, node->cond_if, chunk); | ||
510 | |||
511 | // Else expression. | ||
512 | CompResult else_expr = compile_expr(chunk, node->cond_else); | ||
513 | if (has_value) { | ||
514 | switch (else_expr.type) { | ||
515 | case COMP_CONST: { | ||
516 | EMIT_OP(OP_LD64K, reg_dst, else_expr.idx, 0, node->cond_if, | ||
517 | chunk); | ||
518 | } break; | ||
519 | case COMP_REG: { | ||
520 | EMIT_OP(OP_MOV64, reg_dst, else_expr.idx, 0, node->cond_if, | ||
521 | chunk); | ||
522 | } break; | ||
523 | default: { | ||
524 | return (CompResult){.type = COMP_ERR}; | ||
525 | } break; | ||
526 | } | ||
527 | } | ||
528 | sz end_expr = array_size(chunk->code); | ||
529 | |||
530 | // Backpatch jumps. | ||
531 | sz const_a = add_constant(chunk, jump_b + 1 - jump_a); | ||
532 | sz const_b = add_constant(chunk, end_expr - jump_b); | ||
533 | chunk->code[jump_a].a = const_a; | ||
534 | chunk->code[jump_b].dst = const_b; | ||
535 | // TODO: does it has an else or not? Moreover, should we enforce on the | ||
536 | // semantic level that if the `if` expression returns a value we must add an | ||
537 | // else? | ||
538 | |||
539 | // Return. | ||
540 | if (has_value) { | ||
541 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
542 | } | ||
543 | return (CompResult){.type = COMP_NIL}; | ||
544 | } | ||
545 | |||
437 | CompResult | 546 | CompResult |
438 | compile_expr(Chunk *chunk, Node *node) { | 547 | compile_expr(Chunk *chunk, Node *node) { |
439 | switch (node->kind) { | 548 | switch (node->kind) { |
549 | case NODE_IF: return compile_if(chunk, node); | ||
440 | // Logic. | 550 | // Logic. |
441 | // case NODE_XOR: | 551 | // case NODE_XOR: |
442 | case NODE_BITNOT: | 552 | case NODE_BITNOT: |
@@ -466,17 +576,10 @@ compile_expr(Chunk *chunk, Node *node) { | |||
466 | case NODE_NUM_UINT: | 576 | case NODE_NUM_UINT: |
467 | case NODE_NUM_INT: { | 577 | case NODE_NUM_INT: { |
468 | sz value = node->value.i; | 578 | sz value = node->value.i; |
469 | // Make sure we don't have duplicated constants. | 579 | sz const_idx = add_constant(chunk, value); |
470 | IntIntMap *map = intintmap_lookup(&chunk->intmap, value); | ||
471 | if (!map) { | ||
472 | map = intintmap_insert(&chunk->intmap, value, | ||
473 | chunk->const_idx++, chunk->storage); | ||
474 | Constant c = (Constant){.i = node->value.i}; | ||
475 | array_push(chunk->constants, c, chunk->storage); | ||
476 | } | ||
477 | return (CompResult){ | 580 | return (CompResult){ |
478 | .type = COMP_CONST, | 581 | .type = COMP_CONST, |
479 | .idx = map->val, | 582 | .idx = const_idx, |
480 | }; | 583 | }; |
481 | } break; | 584 | } break; |
482 | case NODE_STRING: { | 585 | case NODE_STRING: { |
@@ -577,6 +680,8 @@ disassemble_instruction(Instruction instruction) { | |||
577 | case OP_LD16K: | 680 | case OP_LD16K: |
578 | case OP_LD32K: | 681 | case OP_LD32K: |
579 | case OP_LD64K: | 682 | case OP_LD64K: |
683 | case OP_JMPF: | ||
684 | case OP_JMPT: | ||
580 | println("%s r%d, c%d", op_str[instruction.op], instruction.dst, | 685 | println("%s r%d, c%d", op_str[instruction.op], instruction.dst, |
581 | instruction.a, instruction.b); | 686 | instruction.a, instruction.b); |
582 | break; | 687 | break; |
@@ -668,6 +773,19 @@ disassemble_instruction(Instruction instruction) { | |||
668 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, | 773 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, |
669 | instruction.a, instruction.b); | 774 | instruction.a, instruction.b); |
670 | break; | 775 | break; |
776 | case OP_JMPI: | ||
777 | println("%s c%d", op_str[instruction.op], instruction.dst, | ||
778 | instruction.a, instruction.b); | ||
779 | break; | ||
780 | case OP_JMP: | ||
781 | println("%s r%d", op_str[instruction.op], instruction.dst, | ||
782 | instruction.a, instruction.b); | ||
783 | break; | ||
784 | case OP_JMPFI: | ||
785 | case OP_JMPTI: | ||
786 | println("%s c%d, c%d", op_str[instruction.op], instruction.dst, | ||
787 | instruction.a, instruction.b); | ||
788 | break; | ||
671 | case OP_HALT: println("%s", op_str[instruction.op]); break; | 789 | case OP_HALT: println("%s", op_str[instruction.op]); break; |
672 | default: println("Unknown opcode %d", instruction.op); break; | 790 | default: println("Unknown opcode %d", instruction.op); break; |
673 | } | 791 | } |
@@ -137,7 +137,6 @@ vm_run(VM *vm) { | |||
137 | case OP_LDGVAR: { | 137 | case OP_LDGVAR: { |
138 | u8 dst = instruction.dst; | 138 | u8 dst = instruction.dst; |
139 | u8 src = instruction.a; | 139 | u8 src = instruction.a; |
140 | println("dst: %d src: %d", dst, src); | ||
141 | Variable var = vm->chunk->vars[src]; | 140 | Variable var = vm->chunk->vars[src]; |
142 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 141 | s64 *stack = (s64 *)&vm->stack[var.offset]; |
143 | vm->regs[dst].i = *stack; | 142 | vm->regs[dst].i = *stack; |
@@ -156,6 +155,62 @@ vm_run(VM *vm) { | |||
156 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 155 | s64 *stack = (s64 *)&vm->stack[var.offset]; |
157 | *stack = vm->chunk->constants[src].i; | 156 | *stack = vm->chunk->constants[src].i; |
158 | } break; | 157 | } break; |
158 | case OP_JMPI: { | ||
159 | sz offset = vm->chunk->constants[instruction.dst].i; | ||
160 | vm->ip += offset - 1; | ||
161 | } break; | ||
162 | case OP_JMPFI: { | ||
163 | bool cond = vm->chunk->constants[instruction.dst].i; | ||
164 | sz offset = vm->chunk->constants[instruction.a].i; | ||
165 | if (!cond) { | ||
166 | vm->ip += offset - 1; | ||
167 | } | ||
168 | } break; | ||
169 | case OP_JMPTI: { | ||
170 | bool cond = vm->chunk->constants[instruction.dst].i; | ||
171 | sz offset = vm->chunk->constants[instruction.a].i; | ||
172 | if (cond) { | ||
173 | vm->ip += offset - 1; | ||
174 | } | ||
175 | } break; | ||
176 | case OP_JMP: { | ||
177 | sz offset = vm->chunk->constants[instruction.dst].i; | ||
178 | vm->ip += offset - 1; | ||
179 | } break; | ||
180 | case OP_JMPF: { | ||
181 | bool cond = vm->regs[instruction.dst].i; | ||
182 | sz offset = vm->chunk->constants[instruction.a].i; | ||
183 | if (!cond) { | ||
184 | vm->ip += offset - 1; | ||
185 | } | ||
186 | } break; | ||
187 | case OP_JMPT: { | ||
188 | bool cond = vm->regs[instruction.dst].i; | ||
189 | sz offset = vm->chunk->constants[instruction.a].i; | ||
190 | if (cond) { | ||
191 | vm->ip += offset - 1; | ||
192 | } | ||
193 | } break; | ||
194 | case OP_MOV64: { | ||
195 | u8 dst = instruction.dst; | ||
196 | u8 src = instruction.a; | ||
197 | vm->regs[dst] = vm->regs[src]; | ||
198 | } break; | ||
199 | case OP_MOV32: { | ||
200 | u8 dst = instruction.dst; | ||
201 | u8 src = instruction.a; | ||
202 | vm->regs[dst].i = vm->regs[src].i & 0xFFFFFFFF; | ||
203 | } break; | ||
204 | case OP_MOV16: { | ||
205 | u8 dst = instruction.dst; | ||
206 | u8 src = instruction.a; | ||
207 | vm->regs[dst].i = vm->regs[src].i & 0xFFFF; | ||
208 | } break; | ||
209 | case OP_MOV8: { | ||
210 | u8 dst = instruction.dst; | ||
211 | u8 src = instruction.a; | ||
212 | vm->regs[dst].i = vm->regs[src].i & 0xFF; | ||
213 | } break; | ||
159 | case OP_HALT: { | 214 | case OP_HALT: { |
160 | println("VM HALT (int) -> %d", vm->regs[instruction.dst]); | 215 | println("VM HALT (int) -> %d", vm->regs[instruction.dst]); |
161 | println("VM HALT (float) -> %f", vm->regs[instruction.dst]); | 216 | println("VM HALT (float) -> %f", vm->regs[instruction.dst]); |
@@ -163,7 +218,8 @@ vm_run(VM *vm) { | |||
163 | return; | 218 | return; |
164 | } | 219 | } |
165 | default: { | 220 | default: { |
166 | eprintln("unimplemented OP code: %d", instruction.op); | 221 | // eprintln("unimplemented OP code: %d", instruction.op); |
222 | eprintln("unimplemented OP code: %s", op_str[instruction.op]); | ||
167 | return; | 223 | return; |
168 | } | 224 | } |
169 | } | 225 | } |