diff options
Diffstat (limited to 'src/compiler.c')
-rw-r--r-- | src/compiler.c | 136 |
1 files changed, 127 insertions, 9 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 | } |