diff options
Diffstat (limited to 'src/compiler.c')
-rw-r--r-- | src/compiler.c | 1440 |
1 files changed, 1320 insertions, 120 deletions
diff --git a/src/compiler.c b/src/compiler.c index 04afd5d..c2b48b3 100644 --- a/src/compiler.c +++ b/src/compiler.c | |||
@@ -10,6 +10,7 @@ typedef struct Variable { | |||
10 | Str type; | 10 | Str type; |
11 | sz size; | 11 | sz size; |
12 | sz offset; | 12 | sz offset; |
13 | sz idx; | ||
13 | } Variable; | 14 | } Variable; |
14 | 15 | ||
15 | MAPDEF(StrVarMap, varmap, Str, Variable, str_hash, str_eq) | 16 | MAPDEF(StrVarMap, varmap, Str, Variable, str_hash, str_eq) |
@@ -24,13 +25,32 @@ typedef struct Instruction { | |||
24 | typedef union Constant { | 25 | typedef union Constant { |
25 | s64 i; | 26 | s64 i; |
26 | u64 u; | 27 | u64 u; |
27 | double f; | 28 | f64 f; |
28 | ptrsize ptr; | 29 | ptrsize ptr; |
29 | } Constant; | 30 | } Constant; |
30 | 31 | ||
32 | typedef struct LineCol { | ||
33 | sz line; | ||
34 | sz col; | ||
35 | } LineCol; | ||
36 | |||
37 | typedef struct Function { | ||
38 | Str name; | ||
39 | sz param_arity; | ||
40 | sz return_arity; | ||
41 | sz index; | ||
42 | } Function; | ||
43 | |||
44 | MAPDEF(FunctionMap, funcmap, Str, Function, str_hash, str_eq) | ||
45 | |||
31 | typedef struct Chunk { | 46 | typedef struct Chunk { |
32 | sz id; | 47 | sz id; |
48 | Str name; | ||
49 | struct Chunk *parent; | ||
50 | |||
33 | Instruction *code; | 51 | Instruction *code; |
52 | IntIntMap *labels; // label -> chunk_index | ||
53 | sz labels_idx; | ||
34 | 54 | ||
35 | // Constant values that fit in 64 bits. | 55 | // Constant values that fit in 64 bits. |
36 | Constant *constants; | 56 | Constant *constants; |
@@ -46,44 +66,91 @@ typedef struct Chunk { | |||
46 | Variable *vars; | 66 | Variable *vars; |
47 | StrVarMap *varmap; | 67 | StrVarMap *varmap; |
48 | sz var_off; | 68 | sz var_off; |
69 | sz param_off; | ||
49 | 70 | ||
50 | // Number of registers currently used in this chunk. | 71 | // Number of registers currently used in this chunk. |
51 | sz reg_idx; | 72 | sz reg_idx; |
52 | 73 | ||
74 | // Number of functions currently used in this chunk. | ||
75 | struct Chunk **functions; | ||
76 | FunctionMap *funmap; | ||
77 | sz fun_idx; | ||
78 | |||
53 | // Debugging. | 79 | // Debugging. |
54 | Str file_name; | 80 | Str file_name; |
55 | Arena *storage; | 81 | Arena *storage; |
56 | // TODO: line/col info for debugging. | 82 | LineCol *linecol; |
57 | } Chunk; | 83 | } Chunk; |
58 | 84 | ||
85 | typedef struct Compiler { | ||
86 | Chunk main_chunk; | ||
87 | Str file_name; | ||
88 | Arena *storage; | ||
89 | |||
90 | // Tables. | ||
91 | StrSet *integer_types; | ||
92 | StrSet *numeric_types; | ||
93 | |||
94 | // Destinations. | ||
95 | sz lab_pre; | ||
96 | sz lab_post; | ||
97 | } Compiler; | ||
98 | |||
59 | typedef enum OpCode { | 99 | typedef enum OpCode { |
60 | // OP DST A B | 100 | // OP DST A B |
61 | // --------------------------------------------------------------- | 101 | // --------------------------------------------------------------- |
62 | // VM/high level instructions. | 102 | // VM/high level instructions. |
63 | OP_HALT, // halt | 103 | OP_HALT, // halt |
64 | OP_STVARI, // stvari vx, ca | 104 | // NOTE: LDGVAR/STGVAR* could be obtained in terms of LDGADDR. |
65 | OP_STVAR, // stvar vx, ra | 105 | OP_STGVARI, // stgvari vx, ca |
106 | OP_STGVAR, // stgvar vx, ra | ||
107 | OP_LDGVAR, // ldgvar rx, va | ||
108 | OP_LDGADDR, // ldgaddr rx, va | ||
109 | OP_STLVARI, // stlvari vx, ca | ||
110 | OP_STLVAR, // stlvar vx, ra | ||
111 | OP_LDLVAR, // ldlvar rx, va | ||
112 | OP_LDLADDR, // ldladdr rx, va | ||
113 | OP_LDSTR, // ldstr rx, sa ; Stores the address of the string sa into rx | ||
114 | // Functions. | ||
115 | OP_CALL, // call fx ; Call the function fx | ||
116 | OP_RECUR, // recur ; Jump to the beginning of the function. | ||
117 | OP_RET, // ret ; Returns from current function | ||
118 | OP_RESERVE, // reserve cx ; Increments the stack pointer by cx bytes | ||
119 | OP_POP, // pop rx ; Pops the last value of the stack into rx. | ||
120 | OP_PUSH, // push rx ; Push the rx value to the stack. | ||
121 | OP_PUSHI, // pushi cx ; Push the cx value to the stack. | ||
122 | OP_PUTRET, // putret rx ; Put rx into the return value memory. | ||
123 | OP_PUTRETI, // putreti cx ; Put cx into the return value memory. | ||
124 | // Printing values with builtin print/println functions. | ||
125 | OP_PRINTSTR, // p rx | ||
126 | OP_PRINTSTRI, // p cx | ||
127 | OP_PRINTS64, // p rx | ||
128 | OP_PRINTS64I, // p cx | ||
129 | OP_PRINTF64, // p rx | ||
130 | OP_PRINTF64I, // p cx | ||
131 | OP_PRINTBOOL, // p rx | ||
132 | OP_PRINTBOOLI, // p cx | ||
66 | // Load/Store instructions. | 133 | // Load/Store instructions. |
67 | OP_LD8K, // ld8k rx, ca -> u8 rx = ca | 134 | OP_LD8K, // ld8k rx, ca -> u8 rx = ca |
68 | OP_LD16K, // ld16k rx, ca -> u16 rx = ca | 135 | OP_LD16K, // ld16k rx, ca -> u16 rx = ca |
69 | OP_LD32K, // ld32k rx, ca -> u32 rx = ca | 136 | OP_LD32K, // ld32k rx, ca -> u32 rx = ca |
70 | OP_LD64K, // ld64k rx, ca -> u64 rx = ca | 137 | OP_LD64K, // ld64k rx, ca -> u64 rx = ca |
71 | OP_LD8I, // ld8i rx, ra, cb -> u8 *p; rx = p[ra + cb] | 138 | OP_LD8I, // ld8i rx, ra, cb -> u8 *p = ra; rx = p[cb] |
72 | OP_LD16I, // ld16i rx, ra, cb -> u16 *p; rx = p[ra + cb] | 139 | OP_LD16I, // ld16i rx, ra, cb -> u16 *p = ra; rx = p[cb] |
73 | OP_LD32I, // ld32i rx, ra, cb -> u32 *p; rx = p[ra + cb] | 140 | OP_LD32I, // ld32i rx, ra, cb -> u32 *p = ra; rx = p[cb] |
74 | OP_LD64I, // ld64i rx, ra, cb -> u64 *p; rx = p[ra + cb] | 141 | OP_LD64I, // ld64i rx, ra, cb -> u64 *p = ra; rx = p[cb] |
75 | OP_LD8, // ld8 rx, ra, rb -> u8 *p; rx = p[ra + rb] | 142 | OP_LD8, // ld8 rx, ra, rb -> u8 *p = ra; rx = p[rb] |
76 | OP_LD16, // ld16 rx, ra, rb -> u16 *p; rx = p[ra + rb] | 143 | OP_LD16, // ld16 rx, ra, rb -> u16 *p = ra; rx = p[rb] |
77 | OP_LD32, // ld32 rx, ra, rb -> u32 *p; rx = p[ra + rb] | 144 | OP_LD32, // ld32 rx, ra, rb -> u32 *p = ra; rx = p[rb] |
78 | OP_LD64, // ld64 rx, ra, rb -> u64 *p; rx = p[ra + rb] | 145 | OP_LD64, // ld64 rx, ra, rb -> u64 *p = ra; rx = p[rb] |
79 | OP_ST8I, // st8i rx, ra, cb -> u8 *p; p[ra + cb] = rx | 146 | OP_ST8I, // st8i rx, ra, cb -> u8 *p = ra; p[cb] = rx |
80 | OP_ST16I, // st16i rx, ra, cb -> u16 *p; p[ra + cb] = rx | 147 | OP_ST16I, // st16i rx, ra, cb -> u16 *p = ra; p[cb] = rx |
81 | OP_ST32I, // st32i rx, ra, cb -> u32 *p; p[ra + cb] = rx | 148 | OP_ST32I, // st32i rx, ra, cb -> u32 *p = ra; p[cb] = rx |
82 | OP_ST64I, // st64i rx, ra, cb -> u64 *p; p[ra + cb] = rx | 149 | OP_ST64I, // st64i rx, ra, cb -> u64 *p = ra; p[cb] = rx |
83 | OP_ST8, // st8 rx, ra, rb -> u8 *p; p[ra + rb] = rx | 150 | OP_ST8, // st8 rx, ra, rb -> u8 *p = ra; p[rb] = rx |
84 | OP_ST16, // st16 rx, ra, rb -> u16 *p; p[ra + rb] = rx | 151 | OP_ST16, // st16 rx, ra, rb -> u16 *p = ra; p[rb] = rx |
85 | OP_ST32, // st32 rx, ra, rb -> u32 *p; p[ra + rb] = rx | 152 | OP_ST32, // st32 rx, ra, rb -> u32 *p = ra; p[rb] = rx |
86 | OP_ST64, // st64 rx, ra, rb -> u64 *p; p[ra + rb] = rx | 153 | OP_ST64, // st64 rx, ra, rb -> u64 *p = ra; p[rb] = rx |
87 | // Integer arithmetic (only int/s64 for now). | 154 | // Integer arithmetic (only int/s64 for now). |
88 | OP_ADDI, // addk rx, ra, cb | 155 | OP_ADDI, // addk rx, ra, cb |
89 | OP_SUBI, // subk rx, ra, cb | 156 | OP_SUBI, // subk rx, ra, cb |
@@ -135,18 +202,52 @@ typedef enum OpCode { | |||
135 | OP_BITRSHIFTI, // shri rx, ra, cb | 202 | OP_BITRSHIFTI, // shri rx, ra, cb |
136 | OP_BITANDI, // bandi rx, ra, cb | 203 | OP_BITANDI, // bandi rx, ra, cb |
137 | OP_BITORI, // bori rx, ra, cb | 204 | OP_BITORI, // bori rx, ra, cb |
205 | OP_BITXORI, // bxor rx, ra, cb | ||
138 | OP_BITNOTI, // bnoti rx, ca | 206 | OP_BITNOTI, // bnoti rx, ca |
139 | OP_BITLSHIFT, // shl rx, ra, rb | 207 | OP_BITLSHIFT, // shl rx, ra, rb |
140 | OP_BITRSHIFT, // shr rx, ra, rb | 208 | OP_BITRSHIFT, // shr rx, ra, rb |
141 | OP_BITAND, // band rx, ra, rb | 209 | OP_BITAND, // band rx, ra, rb |
142 | OP_BITOR, // bor rx, ra, rb | 210 | OP_BITOR, // bor rx, ra, rb |
211 | OP_BITXOR, // bxor rx, ra, rb | ||
143 | OP_BITNOT, // bnot rx, ra | 212 | OP_BITNOT, // bnot rx, ra |
213 | // Jump instructions. | ||
214 | OP_JMP, // jmp lx ; jmp to label lx | ||
215 | OP_JMPF, // jmpf lx, rx ; jmp to label lx if rx is false | ||
216 | OP_JMPT, // jmpt lx, rx ; jmp to label lx if rx is true | ||
217 | OP_JMPFI, // jmpf lx, cx ; jmp to label lx if rx is false | ||
218 | OP_JMPTI, // jmpt lx, cx ; jmp to label lx if rx is true | ||
144 | } OpCode; | 219 | } OpCode; |
145 | 220 | ||
146 | Str op_str[] = { | 221 | Str op_str[] = { |
222 | // High level ops. | ||
147 | [OP_HALT] = cstr("HALT "), | 223 | [OP_HALT] = cstr("HALT "), |
148 | [OP_STVAR] = cstr("STVAR "), | 224 | [OP_STGVAR] = cstr("STGVAR "), |
149 | [OP_STVARI] = cstr("STVARI "), | 225 | [OP_STGVARI] = cstr("STGVARI "), |
226 | [OP_LDGVAR] = cstr("LDGVAR "), | ||
227 | [OP_LDGADDR] = cstr("LDGADDR "), | ||
228 | [OP_STLVAR] = cstr("STLVAR "), | ||
229 | [OP_STLVARI] = cstr("STLVARI "), | ||
230 | [OP_LDLVAR] = cstr("LDLVAR "), | ||
231 | [OP_LDLADDR] = cstr("LDLADDR "), | ||
232 | [OP_LDSTR] = cstr("LDSTR "), | ||
233 | [OP_PRINTSTR] = cstr("PRNSTR "), | ||
234 | [OP_PRINTSTRI] = cstr("PRNSTRI "), | ||
235 | [OP_PRINTS64] = cstr("PRNS64 "), | ||
236 | [OP_PRINTS64I] = cstr("PRNS64I "), | ||
237 | [OP_PRINTF64] = cstr("PRNF64 "), | ||
238 | [OP_PRINTF64I] = cstr("PRNF64I "), | ||
239 | [OP_PRINTBOOL] = cstr("PRNBOOL "), | ||
240 | [OP_PRINTBOOLI] = cstr("PRNBOOLI"), | ||
241 | [OP_PUTRET] = cstr("PUTRET "), | ||
242 | [OP_PUTRETI] = cstr("PUTRETI "), | ||
243 | // Functions. | ||
244 | [OP_CALL] = cstr("CALL "), | ||
245 | [OP_RECUR] = cstr("RECUR "), | ||
246 | [OP_RET] = cstr("RET "), | ||
247 | [OP_RESERVE] = cstr("RESERVE "), | ||
248 | [OP_POP] = cstr("POP "), | ||
249 | [OP_PUSH] = cstr("PUSH "), | ||
250 | [OP_PUSHI] = cstr("PUSHI "), | ||
150 | // Load ops. | 251 | // Load ops. |
151 | [OP_LD8K] = cstr("LD8K "), | 252 | [OP_LD8K] = cstr("LD8K "), |
152 | [OP_LD16K] = cstr("LD16K "), | 253 | [OP_LD16K] = cstr("LD16K "), |
@@ -219,12 +320,20 @@ Str op_str[] = { | |||
219 | [OP_BITRSHIFTI] = cstr("RSHI "), | 320 | [OP_BITRSHIFTI] = cstr("RSHI "), |
220 | [OP_BITANDI] = cstr("BANDI "), | 321 | [OP_BITANDI] = cstr("BANDI "), |
221 | [OP_BITORI] = cstr("BORI "), | 322 | [OP_BITORI] = cstr("BORI "), |
323 | [OP_BITXORI] = cstr("BXORI "), | ||
222 | [OP_BITNOTI] = cstr("BNOTI "), | 324 | [OP_BITNOTI] = cstr("BNOTI "), |
223 | [OP_BITLSHIFT] = cstr("LSH "), | 325 | [OP_BITLSHIFT] = cstr("LSH "), |
224 | [OP_BITRSHIFT] = cstr("RSH "), | 326 | [OP_BITRSHIFT] = cstr("RSH "), |
225 | [OP_BITAND] = cstr("BAND "), | 327 | [OP_BITAND] = cstr("BAND "), |
226 | [OP_BITOR] = cstr("BOR "), | 328 | [OP_BITOR] = cstr("BOR "), |
329 | [OP_BITXOR] = cstr("XBOR "), | ||
227 | [OP_BITNOT] = cstr("BNOT "), | 330 | [OP_BITNOT] = cstr("BNOT "), |
331 | // Jump instructions. | ||
332 | [OP_JMP] = cstr("JMP "), | ||
333 | [OP_JMPF] = cstr("JMPF "), | ||
334 | [OP_JMPT] = cstr("JMPT "), | ||
335 | [OP_JMPFI] = cstr("JMPFI "), | ||
336 | [OP_JMPTI] = cstr("JMPTI "), | ||
228 | }; | 337 | }; |
229 | 338 | ||
230 | typedef enum { | 339 | typedef enum { |
@@ -232,6 +341,7 @@ typedef enum { | |||
232 | COMP_CONST, | 341 | COMP_CONST, |
233 | COMP_STRING, | 342 | COMP_STRING, |
234 | COMP_REG, | 343 | COMP_REG, |
344 | COMP_RET, | ||
235 | COMP_ERR, | 345 | COMP_ERR, |
236 | } CompResultType; | 346 | } CompResultType; |
237 | 347 | ||
@@ -240,21 +350,108 @@ typedef struct CompResult { | |||
240 | CompResultType type; | 350 | CompResultType type; |
241 | } CompResult; | 351 | } CompResult; |
242 | 352 | ||
243 | CompResult compile_expr(Chunk *chunk, Node *node); | 353 | CompResult compile_expr(Compiler *compiler, Chunk *chunk, Node *node); |
354 | |||
355 | sz | ||
356 | add_constant(Chunk *chunk, sz value) { | ||
357 | IntIntMap *map = intintmap_lookup(&chunk->intmap, value); | ||
358 | // Make sure we don't have duplicated constants. | ||
359 | if (!map) { | ||
360 | map = intintmap_insert(&chunk->intmap, value, chunk->const_idx++, | ||
361 | chunk->storage); | ||
362 | Constant c = (Constant){.i = value}; | ||
363 | array_push(chunk->constants, c, chunk->storage); | ||
364 | } | ||
365 | return map->val; | ||
366 | } | ||
244 | 367 | ||
245 | #define EMIT_OP(OP, DST, A, B, NODE, CHUNK) \ | 368 | sz |
246 | do { \ | 369 | add_string(Chunk *chunk, Str string) { |
247 | Instruction inst = (Instruction){ \ | 370 | // Make sure we don't have duplicated string. |
248 | .op = (OP), \ | 371 | StrIntMap *map = strintmap_lookup(&chunk->strmap, string); |
249 | .dst = (DST), \ | 372 | if (!map) { |
250 | .a = (A), \ | 373 | map = strintmap_insert(&chunk->strmap, string, chunk->str_idx++, |
251 | .b = (B), \ | 374 | chunk->storage); |
252 | }; \ | 375 | array_push(chunk->strings, string, chunk->storage); |
253 | array_push((CHUNK)->code, inst, (CHUNK)->storage); \ | 376 | } |
254 | } while (0) | 377 | return map->val; |
378 | } | ||
379 | |||
380 | sz | ||
381 | add_variable(Chunk *chunk, Str name, Str type, sz arr_size) { | ||
382 | sz idx = array_size(chunk->vars); | ||
383 | sz size = 8; | ||
384 | // TODO: get type storage from a table to consider all the basic | ||
385 | // types as well as user defined ones. | ||
386 | if (str_eq(type, cstr("str"))) { | ||
387 | size = 16; | ||
388 | } | ||
389 | if (arr_size) { | ||
390 | // TODO: get the proper storage size for the multiplication. | ||
391 | size *= arr_size; | ||
392 | // FIXME: this should be done on the static analysis, plus, | ||
393 | // we shouldn't be checking all these types by hand, but | ||
394 | // using the symbol tables. | ||
395 | type = str_remove_prefix(type, cstr("@")); | ||
396 | type = str_concat(cstr("[]"), type, chunk->storage); | ||
397 | } | ||
398 | Variable var = (Variable){ | ||
399 | .name = name, | ||
400 | .type = type, | ||
401 | .size = size, | ||
402 | .offset = chunk->var_off, | ||
403 | .idx = idx, | ||
404 | }; | ||
405 | varmap_insert(&chunk->varmap, name, var, chunk->storage); | ||
406 | array_push(chunk->vars, var, chunk->storage); | ||
407 | chunk->var_off += size; | ||
408 | return idx; | ||
409 | } | ||
410 | |||
411 | void | ||
412 | emit_op(OpCode op, sz dst, sz a, sz b, Node *node, Chunk *chunk) { | ||
413 | Instruction inst = (Instruction){ | ||
414 | .op = op, | ||
415 | .dst = dst, | ||
416 | .a = a, | ||
417 | .b = b, | ||
418 | }; | ||
419 | array_push(chunk->code, inst, chunk->storage); | ||
420 | LineCol linecol = (LineCol){0}; | ||
421 | if (node) { | ||
422 | linecol = (LineCol){.line = node->line, .col = node->col}; | ||
423 | } | ||
424 | array_push(chunk->linecol, linecol, chunk->storage); | ||
425 | } | ||
426 | |||
427 | void | ||
428 | emit_fat_copy(Chunk *chunk, Node *node, sz dst_addr, sz src_addr) { | ||
429 | sz reg_dst = chunk->reg_idx++; | ||
430 | |||
431 | // Store the fat string pointer into the variable. | ||
432 | sz zero = add_constant(chunk, 0); | ||
433 | sz one = add_constant(chunk, 1); | ||
434 | |||
435 | // Get the value for the first word of the string | ||
436 | // pointer. | ||
437 | emit_op(OP_LD64I, reg_dst, src_addr, zero, node, chunk); | ||
438 | emit_op(OP_ST64I, reg_dst, dst_addr, zero, node, chunk); | ||
439 | emit_op(OP_LD64I, reg_dst, src_addr, one, node, chunk); | ||
440 | emit_op(OP_ST64I, reg_dst, dst_addr, one, node, chunk); | ||
441 | } | ||
442 | |||
443 | void disassemble_chunk(Chunk chunk); | ||
444 | |||
445 | void | ||
446 | emit_compile_err(Compiler *compiler, Chunk *chunk, Node *node) { | ||
447 | disassemble_chunk(*chunk); | ||
448 | eprintln("%s:%d:%d: error: compilation error on: %s", compiler->file_name, | ||
449 | node->line, node->col, node_str[node->kind]); | ||
450 | exit(EXIT_FAILURE); | ||
451 | } | ||
255 | 452 | ||
256 | CompResult | 453 | CompResult |
257 | compile_binary(Chunk *chunk, Node *node) { | 454 | compile_binary(Compiler *compiler, Chunk *chunk, Node *node) { |
258 | OpCode op = OP_HALT; | 455 | OpCode op = OP_HALT; |
259 | OpCode opi = OP_HALT; | 456 | OpCode opi = OP_HALT; |
260 | OpCode ldop = OP_LD64K; | 457 | OpCode ldop = OP_LD64K; |
@@ -343,6 +540,10 @@ compile_binary(Chunk *chunk, Node *node) { | |||
343 | op = OP_BITOR; | 540 | op = OP_BITOR; |
344 | opi = OP_BITORI; | 541 | opi = OP_BITORI; |
345 | } break; | 542 | } break; |
543 | case NODE_BITXOR: { | ||
544 | op = OP_BITXOR; | ||
545 | opi = OP_BITXORI; | ||
546 | } break; | ||
346 | case NODE_BITAND: { | 547 | case NODE_BITAND: { |
347 | op = OP_BITAND; | 548 | op = OP_BITAND; |
348 | opi = OP_BITANDI; | 549 | opi = OP_BITANDI; |
@@ -357,19 +558,20 @@ compile_binary(Chunk *chunk, Node *node) { | |||
357 | } break; | 558 | } break; |
358 | default: break; | 559 | default: break; |
359 | } | 560 | } |
360 | CompResult comp_a = compile_expr(chunk, node->left); | 561 | CompResult comp_a = compile_expr(compiler, chunk, node->binary.left); |
361 | CompResult comp_b = compile_expr(chunk, node->right); | 562 | CompResult comp_b = compile_expr(compiler, chunk, node->binary.right); |
362 | sz reg_a; | 563 | sz reg_a; |
363 | sz reg_b; | 564 | sz reg_b; |
364 | switch (comp_a.type) { | 565 | switch (comp_a.type) { |
365 | case COMP_CONST: { | 566 | case COMP_CONST: { |
366 | reg_a = chunk->reg_idx++; | 567 | reg_a = chunk->reg_idx++; |
367 | EMIT_OP(ldop, reg_a, comp_a.idx, 0, node, chunk); | 568 | emit_op(ldop, reg_a, comp_a.idx, 0, node, chunk); |
368 | } break; | 569 | } break; |
369 | case COMP_REG: { | 570 | case COMP_REG: { |
370 | reg_a = comp_a.idx; | 571 | reg_a = comp_a.idx; |
371 | } break; | 572 | } break; |
372 | default: { | 573 | default: { |
574 | emit_compile_err(compiler, chunk, node); | ||
373 | return (CompResult){.type = COMP_ERR}; | 575 | return (CompResult){.type = COMP_ERR}; |
374 | } break; | 576 | } break; |
375 | } | 577 | } |
@@ -382,16 +584,17 @@ compile_binary(Chunk *chunk, Node *node) { | |||
382 | reg_b = comp_b.idx; | 584 | reg_b = comp_b.idx; |
383 | } break; | 585 | } break; |
384 | default: { | 586 | default: { |
587 | emit_compile_err(compiler, chunk, node); | ||
385 | return (CompResult){.type = COMP_ERR}; | 588 | return (CompResult){.type = COMP_ERR}; |
386 | } break; | 589 | } break; |
387 | } | 590 | } |
388 | sz reg_dst = chunk->reg_idx++; // Better for optimization | 591 | sz reg_dst = chunk->reg_idx++; // Better for optimization |
389 | EMIT_OP(op, reg_dst, reg_a, reg_b, node, chunk); | 592 | emit_op(op, reg_dst, reg_a, reg_b, node, chunk); |
390 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 593 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
391 | } | 594 | } |
392 | 595 | ||
393 | CompResult | 596 | CompResult |
394 | compile_unary(Chunk *chunk, Node *node) { | 597 | compile_unary(Compiler *compiler, Chunk *chunk, Node *node) { |
395 | OpCode op = OP_HALT; | 598 | OpCode op = OP_HALT; |
396 | OpCode opi = OP_HALT; | 599 | OpCode opi = OP_HALT; |
397 | switch (node->kind) { | 600 | switch (node->kind) { |
@@ -405,7 +608,7 @@ compile_unary(Chunk *chunk, Node *node) { | |||
405 | } break; | 608 | } break; |
406 | default: break; | 609 | default: break; |
407 | } | 610 | } |
408 | CompResult comp_a = compile_expr(chunk, node->left); | 611 | CompResult comp_a = compile_expr(compiler, chunk, node->binary.left); |
409 | sz reg_a; | 612 | sz reg_a; |
410 | switch (comp_a.type) { | 613 | switch (comp_a.type) { |
411 | case COMP_CONST: { | 614 | case COMP_CONST: { |
@@ -416,21 +619,925 @@ compile_unary(Chunk *chunk, Node *node) { | |||
416 | reg_a = comp_a.idx; | 619 | reg_a = comp_a.idx; |
417 | } break; | 620 | } break; |
418 | default: { | 621 | default: { |
622 | emit_compile_err(compiler, chunk, node); | ||
623 | return (CompResult){.type = COMP_ERR}; | ||
624 | } break; | ||
625 | } | ||
626 | sz reg_dst = chunk->reg_idx++; | ||
627 | emit_op(op, reg_dst, reg_a, 0, node, chunk); | ||
628 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
629 | } | ||
630 | |||
631 | CompResult | ||
632 | compile_if(Compiler *compiler, Chunk *chunk, Node *node) { | ||
633 | CompResult cond = compile_expr(compiler, chunk, node->ifelse.cond); | ||
634 | OpCode jmpop; | ||
635 | switch (cond.type) { | ||
636 | case COMP_CONST: { | ||
637 | jmpop = OP_JMPFI; | ||
638 | } break; | ||
639 | case COMP_REG: { | ||
640 | jmpop = OP_JMPF; | ||
641 | } break; | ||
642 | default: { | ||
643 | emit_compile_err(compiler, chunk, node); | ||
419 | return (CompResult){.type = COMP_ERR}; | 644 | return (CompResult){.type = COMP_ERR}; |
420 | } break; | 645 | } break; |
421 | } | 646 | } |
647 | |||
648 | if (!str_eq(node->type, cstr("nil")) && | ||
649 | !str_has_prefix(node->type, cstr("ret:")) && | ||
650 | !str_has_prefix(node->type, cstr("flow:"))) { | ||
651 | sz reg_dst = chunk->reg_idx++; | ||
652 | |||
653 | // Jump to the `false` branch. | ||
654 | sz lab0 = chunk->labels_idx++; | ||
655 | emit_op(jmpop, lab0, cond.idx, 0, node->ifelse.cond, chunk); | ||
656 | |||
657 | // Condition is true. | ||
658 | CompResult then_expr = | ||
659 | compile_expr(compiler, chunk, node->ifelse.expr_true); | ||
660 | switch (then_expr.type) { | ||
661 | case COMP_CONST: { | ||
662 | emit_op(OP_LD64K, reg_dst, then_expr.idx, 0, node->ifelse.cond, | ||
663 | chunk); | ||
664 | } break; | ||
665 | case COMP_REG: { | ||
666 | emit_op(OP_MOV64, reg_dst, then_expr.idx, 0, node->ifelse.cond, | ||
667 | chunk); | ||
668 | } break; | ||
669 | case COMP_RET: break; | ||
670 | default: { | ||
671 | emit_compile_err(compiler, chunk, node); | ||
672 | return (CompResult){.type = COMP_ERR}; | ||
673 | } break; | ||
674 | } | ||
675 | |||
676 | // Jump to the end of the expression. | ||
677 | sz pos0 = array_size(chunk->code); | ||
678 | sz lab1 = chunk->labels_idx++; | ||
679 | emit_op(OP_JMP, lab1, 0, 0, node->ifelse.expr_else, chunk); | ||
680 | |||
681 | // Else expression. | ||
682 | CompResult else_expr = | ||
683 | compile_expr(compiler, chunk, node->ifelse.expr_else); | ||
684 | switch (else_expr.type) { | ||
685 | case COMP_CONST: { | ||
686 | emit_op(OP_LD64K, reg_dst, else_expr.idx, 0, | ||
687 | node->ifelse.expr_else, chunk); | ||
688 | } break; | ||
689 | case COMP_REG: { | ||
690 | emit_op(OP_MOV64, reg_dst, else_expr.idx, 0, | ||
691 | node->ifelse.expr_else, chunk); | ||
692 | } break; | ||
693 | case COMP_RET: break; | ||
694 | default: { | ||
695 | emit_compile_err(compiler, chunk, node); | ||
696 | return (CompResult){.type = COMP_ERR}; | ||
697 | } break; | ||
698 | } | ||
699 | sz pos1 = array_size(chunk->code); | ||
700 | |||
701 | // Update labels. | ||
702 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage); | ||
703 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
704 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
705 | } | ||
706 | |||
707 | // Jump to the `false` branch. | ||
708 | sz lab0 = chunk->labels_idx++; | ||
709 | emit_op(jmpop, lab0, cond.idx, 0, node->ifelse.cond, chunk); | ||
710 | |||
711 | // Condition is true. | ||
712 | compile_expr(compiler, chunk, node->ifelse.expr_true); | ||
713 | |||
714 | // Jump to the end of the expression. | ||
715 | sz pos0 = array_size(chunk->code); | ||
716 | sz lab1 = chunk->labels_idx++; | ||
717 | emit_op(OP_JMP, lab1, 0, 0, node->ifelse.expr_else, chunk); | ||
718 | |||
719 | // Else expression. | ||
720 | if (node->ifelse.expr_else) { | ||
721 | compile_expr(compiler, chunk, node->ifelse.expr_else); | ||
722 | } | ||
723 | sz pos1 = array_size(chunk->code); | ||
724 | |||
725 | // Update labels. | ||
726 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage); | ||
727 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
728 | |||
729 | return (CompResult){.type = COMP_NIL}; | ||
730 | } | ||
731 | |||
732 | CompResult | ||
733 | compile_cond(Compiler *compiler, Chunk *chunk, Node *node) { | ||
734 | if (str_eq(node->type, cstr("nil"))) { | ||
735 | sz lab1 = chunk->labels_idx++; | ||
736 | for (sz i = 0; i < array_size(node->match.cases); i++) { | ||
737 | // condition = expression | ||
738 | Node *expr = node->match.cases[i]; | ||
739 | if (expr->case_entry.cond) { | ||
740 | CompResult cond = | ||
741 | compile_expr(compiler, chunk, expr->case_entry.cond); | ||
742 | OpCode jmpop; | ||
743 | switch (cond.type) { | ||
744 | case COMP_CONST: { | ||
745 | jmpop = OP_JMPFI; | ||
746 | } break; | ||
747 | case COMP_REG: { | ||
748 | jmpop = OP_JMPF; | ||
749 | } break; | ||
750 | default: { | ||
751 | emit_compile_err(compiler, chunk, node); | ||
752 | return (CompResult){.type = COMP_ERR}; | ||
753 | } break; | ||
754 | } | ||
755 | // Jump to the `next` branch. | ||
756 | sz lab0 = chunk->labels_idx++; | ||
757 | emit_op(jmpop, lab0, cond.idx, 0, expr->case_entry.expr, chunk); | ||
758 | |||
759 | // Condition is true. | ||
760 | compile_expr(compiler, chunk, expr->case_entry.expr); | ||
761 | if (i != array_size(node->match.cases) - 1) { | ||
762 | // Jump to the end of the expression. | ||
763 | sz pos0 = array_size(chunk->code); | ||
764 | emit_op(OP_JMP, lab1, 0, 0, node->ifelse.expr_else, chunk); | ||
765 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, | ||
766 | chunk->storage); | ||
767 | } | ||
768 | } else { | ||
769 | compile_expr(compiler, chunk, expr->case_entry.expr); | ||
770 | break; | ||
771 | } | ||
772 | } | ||
773 | sz pos1 = array_size(chunk->code); | ||
774 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
775 | return (CompResult){.type = COMP_NIL}; | ||
776 | } | ||
777 | |||
422 | sz reg_dst = chunk->reg_idx++; | 778 | sz reg_dst = chunk->reg_idx++; |
423 | EMIT_OP(op, reg_dst, reg_a, 0, node, chunk); | 779 | sz lab1 = chunk->labels_idx++; |
780 | for (sz i = 0; i < array_size(node->match.cases); i++) { | ||
781 | // condition = expression | ||
782 | Node *expr = node->match.cases[i]; | ||
783 | if (expr->case_entry.cond) { | ||
784 | CompResult cond = | ||
785 | compile_expr(compiler, chunk, expr->case_entry.cond); | ||
786 | OpCode jmpop; | ||
787 | switch (cond.type) { | ||
788 | case COMP_CONST: { | ||
789 | jmpop = OP_JMPFI; | ||
790 | } break; | ||
791 | case COMP_REG: { | ||
792 | jmpop = OP_JMPF; | ||
793 | } break; | ||
794 | default: { | ||
795 | emit_compile_err(compiler, chunk, node); | ||
796 | return (CompResult){.type = COMP_ERR}; | ||
797 | } break; | ||
798 | } | ||
799 | // Jump to the `next` branch. | ||
800 | sz lab0 = chunk->labels_idx++; | ||
801 | emit_op(jmpop, lab0, cond.idx, 0, expr->case_entry.expr, chunk); | ||
802 | |||
803 | // Condition is true. | ||
804 | CompResult then_expr = | ||
805 | compile_expr(compiler, chunk, expr->case_entry.expr); | ||
806 | switch (then_expr.type) { | ||
807 | case COMP_CONST: { | ||
808 | emit_op(OP_LD64K, reg_dst, then_expr.idx, 0, | ||
809 | expr->case_entry.expr, chunk); | ||
810 | } break; | ||
811 | case COMP_REG: { | ||
812 | emit_op(OP_MOV64, reg_dst, then_expr.idx, 0, | ||
813 | expr->case_entry.expr, chunk); | ||
814 | } break; | ||
815 | case COMP_RET: break; | ||
816 | default: { | ||
817 | emit_compile_err(compiler, chunk, node); | ||
818 | return (CompResult){.type = COMP_ERR}; | ||
819 | } break; | ||
820 | } | ||
821 | if (i != array_size(node->match.cases) - 1) { | ||
822 | // Jump to the end of the expression. | ||
823 | sz pos0 = array_size(chunk->code); | ||
824 | emit_op(OP_JMP, lab1, 0, 0, node->ifelse.expr_else, chunk); | ||
825 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, | ||
826 | chunk->storage); | ||
827 | } | ||
828 | } else { | ||
829 | CompResult then_expr = | ||
830 | compile_expr(compiler, chunk, expr->case_entry.expr); | ||
831 | switch (then_expr.type) { | ||
832 | case COMP_CONST: { | ||
833 | emit_op(OP_LD64K, reg_dst, then_expr.idx, 0, | ||
834 | expr->case_entry.expr, chunk); | ||
835 | } break; | ||
836 | case COMP_REG: { | ||
837 | emit_op(OP_MOV64, reg_dst, then_expr.idx, 0, | ||
838 | expr->case_entry.expr, chunk); | ||
839 | } break; | ||
840 | case COMP_RET: break; | ||
841 | default: { | ||
842 | emit_compile_err(compiler, chunk, node); | ||
843 | return (CompResult){.type = COMP_ERR}; | ||
844 | } break; | ||
845 | } | ||
846 | break; | ||
847 | } | ||
848 | } | ||
849 | sz pos1 = array_size(chunk->code); | ||
850 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
851 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
852 | } | ||
853 | |||
854 | CompResult | ||
855 | compile_break(Compiler *compiler, Chunk *chunk, Node *node) { | ||
856 | emit_op(OP_JMP, compiler->lab_post, 0, 0, node, chunk); | ||
857 | return (CompResult){.type = COMP_NIL}; | ||
858 | } | ||
859 | |||
860 | CompResult | ||
861 | compile_continue(Compiler *compiler, Chunk *chunk, Node *node) { | ||
862 | emit_op(OP_JMP, compiler->lab_pre, 0, 0, node, chunk); | ||
863 | return (CompResult){.type = COMP_NIL}; | ||
864 | } | ||
865 | |||
866 | CompResult | ||
867 | compile_while(Compiler *compiler, Chunk *chunk, Node *node) { | ||
868 | sz lab0 = chunk->labels_idx++; | ||
869 | sz lab1 = chunk->labels_idx++; | ||
870 | sz pos1 = array_size(chunk->code); | ||
871 | CompResult cond = compile_expr(compiler, chunk, node->loop.cond); | ||
872 | OpCode jmpop; | ||
873 | switch (cond.type) { | ||
874 | case COMP_CONST: { | ||
875 | jmpop = OP_JMPFI; | ||
876 | } break; | ||
877 | case COMP_REG: { | ||
878 | jmpop = OP_JMPF; | ||
879 | } break; | ||
880 | default: { | ||
881 | emit_compile_err(compiler, chunk, node); | ||
882 | return (CompResult){.type = COMP_ERR}; | ||
883 | } break; | ||
884 | } | ||
885 | |||
886 | // Jump to the `end of the loop` branch. | ||
887 | emit_op(jmpop, lab0, cond.idx, 0, node->loop.cond, chunk); | ||
888 | |||
889 | // Condition is true. | ||
890 | compiler->lab_pre = lab1; | ||
891 | compiler->lab_post = lab0; | ||
892 | compile_expr(compiler, chunk, node->loop.expr); | ||
893 | sz pos0 = array_size(chunk->code); | ||
894 | emit_op(OP_JMP, lab1, 0, 0, node, chunk); | ||
895 | |||
896 | // Update labels. | ||
897 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage); | ||
898 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
899 | |||
900 | // Return. | ||
901 | return (CompResult){.type = COMP_NIL}; | ||
902 | } | ||
903 | |||
904 | CompResult | ||
905 | compile_tail_call(Compiler *compiler, Chunk *chunk, Node *node) { | ||
906 | // Update the local parameters. | ||
907 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
908 | Node *expr = node->elements[i]; | ||
909 | CompResult result = compile_expr(compiler, chunk, expr); | ||
910 | switch (result.type) { | ||
911 | case COMP_CONST: { | ||
912 | emit_op(OP_STLVARI, i, result.idx, 0, node, chunk); | ||
913 | } break; | ||
914 | case COMP_REG: { | ||
915 | if (str_eq(expr->type, cstr("str"))) { | ||
916 | sz var_addr = chunk->reg_idx++; | ||
917 | sz str_addr = result.idx; | ||
918 | emit_op(OP_LDLADDR, var_addr, i, 0, node, chunk); | ||
919 | emit_fat_copy(chunk, node, var_addr, str_addr); | ||
920 | } else { | ||
921 | emit_op(OP_STLVAR, i, result.idx, 0, node, chunk); | ||
922 | } | ||
923 | } break; | ||
924 | case COMP_STRING: { | ||
925 | sz var_addr = chunk->reg_idx++; | ||
926 | sz str_addr = chunk->reg_idx++; | ||
927 | emit_op(OP_LDLADDR, var_addr, i, 0, node, chunk); | ||
928 | emit_op(OP_LDSTR, str_addr, result.idx, 0, node, chunk); | ||
929 | emit_fat_copy(chunk, node, var_addr, str_addr); | ||
930 | } break; | ||
931 | default: { | ||
932 | emit_compile_err(compiler, chunk, node); | ||
933 | return (CompResult){.type = COMP_ERR}; | ||
934 | } break; | ||
935 | } | ||
936 | } | ||
937 | |||
938 | emit_op(OP_RECUR, 0, 0, 0, node, chunk); | ||
939 | return (CompResult){.type = COMP_NIL}; | ||
940 | } | ||
941 | |||
942 | CompResult | ||
943 | compile_funcall(Compiler *compiler, Chunk *chunk, Node *node) { | ||
944 | Str name = node->value.str; | ||
945 | |||
946 | // Builtins. | ||
947 | if (str_eq(name, cstr("print")) || str_eq(name, cstr("println"))) { | ||
948 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
949 | Node *expr = node->elements[i]; | ||
950 | CompResult result = compile_expr(compiler, chunk, expr); | ||
951 | if (str_eq(expr->type, cstr("int"))) { | ||
952 | switch (result.type) { | ||
953 | case COMP_CONST: { | ||
954 | emit_op(OP_PRINTS64I, result.idx, 0, 0, expr, chunk); | ||
955 | } break; | ||
956 | case COMP_REG: { | ||
957 | emit_op(OP_PRINTS64, result.idx, 0, 0, expr, chunk); | ||
958 | } break; | ||
959 | default: { | ||
960 | emit_compile_err(compiler, chunk, node); | ||
961 | return (CompResult){.type = COMP_ERR}; | ||
962 | } break; | ||
963 | } | ||
964 | } else if (str_eq(expr->type, cstr("f64"))) { | ||
965 | switch (result.type) { | ||
966 | case COMP_CONST: { | ||
967 | emit_op(OP_PRINTF64I, result.idx, 0, 0, expr, chunk); | ||
968 | } break; | ||
969 | case COMP_REG: { | ||
970 | emit_op(OP_PRINTF64, result.idx, 0, 0, expr, chunk); | ||
971 | } break; | ||
972 | default: { | ||
973 | emit_compile_err(compiler, chunk, node); | ||
974 | return (CompResult){.type = COMP_ERR}; | ||
975 | } break; | ||
976 | } | ||
977 | } else if (str_eq(expr->type, cstr("str"))) { | ||
978 | switch (result.type) { | ||
979 | case COMP_STRING: { | ||
980 | emit_op(OP_PRINTSTRI, result.idx, 0, 0, expr, chunk); | ||
981 | } break; | ||
982 | case COMP_REG: { | ||
983 | emit_op(OP_PRINTSTR, result.idx, 0, 0, expr, chunk); | ||
984 | } break; | ||
985 | default: { | ||
986 | emit_compile_err(compiler, chunk, node); | ||
987 | return (CompResult){.type = COMP_ERR}; | ||
988 | } break; | ||
989 | } | ||
990 | } else if (str_eq(expr->type, cstr("bool"))) { | ||
991 | switch (result.type) { | ||
992 | case COMP_CONST: { | ||
993 | emit_op(OP_PRINTBOOLI, result.idx, 0, 0, expr, chunk); | ||
994 | } break; | ||
995 | case COMP_REG: { | ||
996 | emit_op(OP_PRINTBOOL, result.idx, 0, 0, expr, chunk); | ||
997 | } break; | ||
998 | default: { | ||
999 | emit_compile_err(compiler, chunk, node); | ||
1000 | return (CompResult){.type = COMP_ERR}; | ||
1001 | } break; | ||
1002 | } | ||
1003 | } | ||
1004 | } | ||
1005 | if (str_eq(name, cstr("println"))) { | ||
1006 | sz idx = add_string(chunk, cstr("\n")); | ||
1007 | emit_op(OP_PRINTSTRI, idx, 0, 0, node, chunk); | ||
1008 | } | ||
1009 | return (CompResult){.type = COMP_NIL}; | ||
1010 | } | ||
1011 | |||
1012 | FunctionMap *map = | ||
1013 | funcmap_lookup(&compiler->main_chunk.funmap, node->unique_name); | ||
1014 | if (!map) { | ||
1015 | emit_compile_err(compiler, chunk, node); | ||
1016 | return (CompResult){.type = COMP_ERR}; | ||
1017 | } | ||
1018 | Function fun = map->val; | ||
1019 | |||
1020 | // Check for tail recursive opportunities. | ||
1021 | if (str_eq(fun.name, node->unique_name) && | ||
1022 | str_eq(chunk->name, node->unique_name)) { | ||
1023 | Node *parent = node->parent; | ||
1024 | Node *current = node; | ||
1025 | bool tail_recursive = true; | ||
1026 | while (parent != NULL) { | ||
1027 | switch (parent->kind) { | ||
1028 | case NODE_BLOCK: { | ||
1029 | sz idx = array_size(parent->statements) - 1; | ||
1030 | if (parent->statements[idx] != node) { | ||
1031 | tail_recursive = false; | ||
1032 | break; | ||
1033 | } | ||
1034 | } break; | ||
1035 | case NODE_WHILE: { | ||
1036 | if (current == parent->loop.cond) { | ||
1037 | tail_recursive = false; | ||
1038 | break; | ||
1039 | } | ||
1040 | } break; | ||
1041 | case NODE_IF: { | ||
1042 | if (current == parent->ifelse.cond) { | ||
1043 | tail_recursive = false; | ||
1044 | break; | ||
1045 | } | ||
1046 | } break; | ||
1047 | case NODE_FUN: { | ||
1048 | sz idx = array_size(parent->func.body->statements) - 1; | ||
1049 | if (parent->func.body->statements[idx] != current) { | ||
1050 | tail_recursive = false; | ||
1051 | break; | ||
1052 | } | ||
1053 | break; | ||
1054 | } break; | ||
1055 | case NODE_MATCH: { | ||
1056 | if (current == parent->match.expr) { | ||
1057 | tail_recursive = false; | ||
1058 | break; | ||
1059 | } | ||
1060 | } break; | ||
1061 | case NODE_COND: break; | ||
1062 | case NODE_CASE_COND: { | ||
1063 | if (current == parent->case_entry.cond) { | ||
1064 | tail_recursive = false; | ||
1065 | break; | ||
1066 | } | ||
1067 | } break; | ||
1068 | default: { | ||
1069 | tail_recursive = false; | ||
1070 | break; | ||
1071 | } break; | ||
1072 | } | ||
1073 | parent = parent->parent; | ||
1074 | current = current->parent; | ||
1075 | } | ||
1076 | if (tail_recursive) { | ||
1077 | return compile_tail_call(compiler, chunk, node); | ||
1078 | } | ||
1079 | } | ||
1080 | |||
1081 | // Reserve space for the return value if needed. | ||
1082 | if (fun.return_arity > 0) { | ||
1083 | // Put the return data into a register | ||
1084 | sz ret_size = add_constant(chunk, 8); | ||
1085 | emit_op(OP_RESERVE, ret_size, 0, 0, node, chunk); | ||
1086 | } | ||
1087 | |||
1088 | // Send parameters to the stack. | ||
1089 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
1090 | Node *expr = node->elements[i]; | ||
1091 | CompResult result = compile_expr(compiler, chunk, expr); | ||
1092 | switch (result.type) { | ||
1093 | case COMP_CONST: { | ||
1094 | emit_op(OP_PUSHI, result.idx, 0, 0, expr, chunk); | ||
1095 | } break; | ||
1096 | case COMP_REG: { | ||
1097 | if (str_eq(expr->type, cstr("str"))) { | ||
1098 | sz str_addr = result.idx; | ||
1099 | // Store the fat string pointer into the stack. | ||
1100 | sz reg_dst = chunk->reg_idx++; | ||
1101 | sz zero = add_constant(chunk, 0); | ||
1102 | sz one = add_constant(chunk, 1); | ||
1103 | emit_op(OP_LD64I, reg_dst, str_addr, zero, node, chunk); | ||
1104 | emit_op(OP_PUSH, reg_dst, 0, 0, expr, chunk); | ||
1105 | emit_op(OP_LD64I, reg_dst, str_addr, one, node, chunk); | ||
1106 | emit_op(OP_PUSH, reg_dst, 0, 0, expr, chunk); | ||
1107 | } else { | ||
1108 | emit_op(OP_PUSH, result.idx, 0, 0, expr, chunk); | ||
1109 | } | ||
1110 | } break; | ||
1111 | case COMP_STRING: { | ||
1112 | // Get the address for the string value variable. | ||
1113 | sz str_addr = chunk->reg_idx++; | ||
1114 | emit_op(OP_LDSTR, str_addr, result.idx, 0, node->var.val, | ||
1115 | chunk); | ||
1116 | |||
1117 | // Store the fat string pointer into the stack. | ||
1118 | sz reg_dst = chunk->reg_idx++; | ||
1119 | sz zero = add_constant(chunk, 0); | ||
1120 | sz one = add_constant(chunk, 1); | ||
1121 | emit_op(OP_LD64I, reg_dst, str_addr, zero, node, chunk); | ||
1122 | emit_op(OP_PUSH, reg_dst, 0, 0, expr, chunk); | ||
1123 | emit_op(OP_LD64I, reg_dst, str_addr, one, node, chunk); | ||
1124 | emit_op(OP_PUSH, reg_dst, 0, 0, expr, chunk); | ||
1125 | } break; | ||
1126 | default: { | ||
1127 | emit_compile_err(compiler, chunk, node); | ||
1128 | return (CompResult){.type = COMP_ERR}; | ||
1129 | } break; | ||
1130 | } | ||
1131 | } | ||
1132 | |||
1133 | emit_op(OP_CALL, fun.index, 0, 0, node, chunk); | ||
1134 | |||
1135 | // Only one return parameter for now. | ||
1136 | if (fun.return_arity > 0) { | ||
1137 | // Put the return data into a register | ||
1138 | sz reg_dst = chunk->reg_idx++; | ||
1139 | emit_op(OP_POP, reg_dst, 0, 0, node, chunk); | ||
1140 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
1141 | } | ||
1142 | |||
1143 | return (CompResult){.type = COMP_NIL}; | ||
1144 | } | ||
1145 | |||
1146 | CompResult | ||
1147 | compile_return(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1148 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
1149 | Node *expr = node->elements[i]; | ||
1150 | CompResult res = compile_expr(compiler, chunk, expr); | ||
1151 | |||
1152 | // TODO: Only one return field for now, but this is the idea. | ||
1153 | // Put return values into memory. | ||
1154 | switch (res.type) { | ||
1155 | case COMP_CONST: { | ||
1156 | emit_op(OP_PUTRETI, res.idx, 0, 0, node, chunk); | ||
1157 | } break; | ||
1158 | case COMP_REG: { | ||
1159 | emit_op(OP_PUTRET, res.idx, 0, 0, node, chunk); | ||
1160 | } break; | ||
1161 | case COMP_NIL: break; | ||
1162 | default: { | ||
1163 | emit_compile_err(compiler, chunk, node); | ||
1164 | return (CompResult){.type = COMP_ERR}; | ||
1165 | } break; | ||
1166 | } | ||
1167 | break; | ||
1168 | } | ||
1169 | |||
1170 | emit_op(OP_RET, 0, 0, 0, node, chunk); | ||
1171 | return (CompResult){.type = COMP_RET}; | ||
1172 | } | ||
1173 | |||
1174 | Chunk * | ||
1175 | chunk_alloc(Chunk *parent) { | ||
1176 | static sz chunk_idx = 1; | ||
1177 | Chunk *chunk = arena_calloc((sz)sizeof(Chunk), parent->storage); | ||
1178 | chunk->parent = parent; | ||
1179 | chunk->id = chunk_idx++; | ||
1180 | chunk->storage = parent->storage; | ||
1181 | chunk->file_name = parent->file_name; | ||
1182 | return chunk; | ||
1183 | } | ||
1184 | |||
1185 | void | ||
1186 | verify_chunk(Chunk *chunk) { | ||
1187 | if (chunk->const_idx >= 256) { | ||
1188 | eprintln("too many constants on chunk %s", chunk->id); | ||
1189 | exit(EXIT_FAILURE); | ||
1190 | } | ||
1191 | if (chunk->str_idx >= 256) { | ||
1192 | eprintln("too many strings on chunk %s", chunk->id); | ||
1193 | exit(EXIT_FAILURE); | ||
1194 | } | ||
1195 | if (chunk->reg_idx >= 256) { | ||
1196 | eprintln("too many registers used on chunk %s", chunk->id); | ||
1197 | exit(EXIT_FAILURE); | ||
1198 | } | ||
1199 | if (chunk->labels_idx >= 256) { | ||
1200 | eprintln("too many labels used on chunk %s", chunk->id); | ||
1201 | exit(EXIT_FAILURE); | ||
1202 | } | ||
1203 | if (chunk->fun_idx >= 256) { | ||
1204 | eprintln("too many functions on chunk %s", chunk->id); | ||
1205 | exit(EXIT_FAILURE); | ||
1206 | } | ||
1207 | } | ||
1208 | |||
1209 | void | ||
1210 | declare_function(Chunk *chunk, Node *node) { | ||
1211 | Str name = node->unique_name; | ||
1212 | FunctionMap *map = funcmap_lookup(&chunk->funmap, node->unique_name); | ||
1213 | if (map) { | ||
1214 | return; | ||
1215 | } | ||
1216 | Function fun = (Function){ | ||
1217 | .name = name, | ||
1218 | .index = chunk->fun_idx++, | ||
1219 | .param_arity = array_size(node->func.params), | ||
1220 | .return_arity = array_size(node->func.ret), | ||
1221 | }; | ||
1222 | funcmap_insert(&chunk->funmap, node->unique_name, fun, chunk->storage); | ||
1223 | } | ||
1224 | |||
1225 | CompResult | ||
1226 | compile_function(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1227 | // The current activation record procedure for the VM is as follows: | ||
1228 | // | ||
1229 | // [caller][callee ] | ||
1230 | // [ .... ][ RET VAL ][ PARAMS ][ LOCALS ][ REGISTERS ][ RET META ] | ||
1231 | // ^ | ||
1232 | // frame pointer | ||
1233 | // | ||
1234 | // The caller is responsible for allocating the return memory and the | ||
1235 | // parameter memory and filling the param data before OP_CALL. | ||
1236 | // | ||
1237 | chunk = &compiler->main_chunk; | ||
1238 | Chunk *func = chunk_alloc(chunk); | ||
1239 | func->name = node->unique_name; | ||
1240 | declare_function(chunk, node); | ||
1241 | array_push(chunk->functions, func, chunk->storage); | ||
1242 | |||
1243 | // Push arguments as locals. | ||
1244 | for (sz i = 0; i < array_size(node->func.params); i++) { | ||
1245 | Node *param = node->func.params[i]; | ||
1246 | Str name = param->unique_name; | ||
1247 | Str type = param->type; | ||
1248 | sz arr_size = 0; | ||
1249 | if (str_has_prefix(type, cstr("@"))) { | ||
1250 | if (param->var.type && param->var.type->kind == NODE_ARR_TYPE && | ||
1251 | param->var.type->sym.arr_size->value.i > 0) { | ||
1252 | arr_size = param->var.type->sym.arr_size->value.i; | ||
1253 | } | ||
1254 | } | ||
1255 | add_variable(func, name, type, arr_size); | ||
1256 | } | ||
1257 | func->param_off = func->var_off; | ||
1258 | |||
1259 | // Compiling the body. | ||
1260 | CompResult res = compile_expr(compiler, func, node->func.body); | ||
1261 | |||
1262 | // Put return values into memory. | ||
1263 | switch (res.type) { | ||
1264 | case COMP_CONST: { | ||
1265 | emit_op(OP_PUTRETI, res.idx, 0, 0, node, func); | ||
1266 | } break; | ||
1267 | case COMP_REG: { | ||
1268 | emit_op(OP_PUTRET, res.idx, 0, 0, node, func); | ||
1269 | } break; | ||
1270 | default: break; | ||
1271 | } | ||
1272 | |||
1273 | emit_op(OP_RET, 0, 0, 0, node, func); | ||
1274 | verify_chunk(func); | ||
1275 | return (CompResult){.type = COMP_NIL}; | ||
1276 | } | ||
1277 | |||
1278 | CompResult | ||
1279 | compile_let(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1280 | sz op_ldaddr = OP_LDLADDR; | ||
1281 | sz op_stvari = OP_STLVARI; | ||
1282 | sz op_stvar = OP_STLVAR; | ||
1283 | if (chunk == &compiler->main_chunk) { | ||
1284 | op_ldaddr = OP_LDGADDR; | ||
1285 | op_stvari = OP_STGVARI; | ||
1286 | op_stvar = OP_STGVAR; | ||
1287 | } | ||
1288 | Str name = node->unique_name; | ||
1289 | Str type = node->var.name->type; | ||
1290 | sz arr_size = 0; | ||
1291 | if (str_has_prefix(type, cstr("@"))) { | ||
1292 | if (node->var.type && node->var.type->kind == NODE_ARR_TYPE && | ||
1293 | node->var.type->sym.arr_size->value.i > 0) { | ||
1294 | arr_size = node->var.type->sym.arr_size->value.i; | ||
1295 | } | ||
1296 | } | ||
1297 | |||
1298 | sz idx = add_variable(chunk, name, type, arr_size); | ||
1299 | |||
1300 | // Value. | ||
1301 | if (node->var.val) { | ||
1302 | CompResult res = compile_expr(compiler, chunk, node->var.val); | ||
1303 | switch (res.type) { | ||
1304 | case COMP_CONST: { | ||
1305 | emit_op(op_stvari, idx, res.idx, 0, node->var.val, chunk); | ||
1306 | } break; | ||
1307 | case COMP_REG: { | ||
1308 | if (str_eq(node->var.val->type, cstr("str"))) { | ||
1309 | // Get the address for the local/global storage | ||
1310 | // variable. | ||
1311 | sz var_addr = chunk->reg_idx++; | ||
1312 | emit_op(op_ldaddr, var_addr, idx, 0, node, chunk); | ||
1313 | |||
1314 | // Copy the fat pointer. | ||
1315 | emit_fat_copy(chunk, node, var_addr, res.idx); | ||
1316 | } else { | ||
1317 | emit_op(op_stvar, idx, res.idx, 0, node->var.val, chunk); | ||
1318 | } | ||
1319 | } break; | ||
1320 | case COMP_STRING: { | ||
1321 | // Get the address for the string value variable. | ||
1322 | sz str_addr = chunk->reg_idx++; | ||
1323 | emit_op(OP_LDSTR, str_addr, res.idx, 0, node->var.val, chunk); | ||
1324 | |||
1325 | // Get the address for the local/global storage | ||
1326 | // variable. | ||
1327 | sz var_addr = chunk->reg_idx++; | ||
1328 | emit_op(op_ldaddr, var_addr, idx, 0, node, chunk); | ||
1329 | |||
1330 | // Copy the fat pointer. | ||
1331 | emit_fat_copy(chunk, node, var_addr, str_addr); | ||
1332 | } break; | ||
1333 | default: { | ||
1334 | emit_compile_err(compiler, chunk, node); | ||
1335 | return (CompResult){.type = COMP_ERR}; | ||
1336 | } break; | ||
1337 | } | ||
1338 | } | ||
1339 | |||
1340 | return (CompResult){.type = COMP_NIL}; | ||
1341 | } | ||
1342 | |||
1343 | CompResult | ||
1344 | compile_set(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1345 | Str name = node->unique_name; | ||
1346 | StrVarMap *map = NULL; | ||
1347 | Chunk *next = chunk; | ||
1348 | while (next) { | ||
1349 | map = varmap_lookup(&next->varmap, name); | ||
1350 | if (map) { | ||
1351 | break; | ||
1352 | } | ||
1353 | next = chunk->parent; | ||
1354 | } | ||
1355 | if (!map) { | ||
1356 | emit_compile_err(compiler, chunk, node); | ||
1357 | return (CompResult){.type = COMP_ERR}; | ||
1358 | } | ||
1359 | sz op_ldaddr = OP_LDLADDR; | ||
1360 | sz op_ldvar = OP_LDLVAR; | ||
1361 | sz op_stvari = OP_STLVARI; | ||
1362 | sz op_stvar = OP_STLVAR; | ||
1363 | if (next == &compiler->main_chunk) { | ||
1364 | op_ldaddr = OP_LDGADDR; | ||
1365 | op_ldvar = OP_LDGVAR; | ||
1366 | op_stvari = OP_STGVARI; | ||
1367 | op_stvar = OP_STGVAR; | ||
1368 | } | ||
1369 | CompResult res = compile_expr(compiler, chunk, node->var.val); | ||
1370 | if (node->var.name->kind == NODE_SYMBOL_IDX) { | ||
1371 | // Value. | ||
1372 | sz reg_val; | ||
1373 | switch (res.type) { | ||
1374 | case COMP_CONST: { | ||
1375 | reg_val = chunk->reg_idx++; | ||
1376 | emit_op(OP_LD64K, reg_val, res.idx, 0, node, chunk); | ||
1377 | } break; | ||
1378 | case COMP_REG: { | ||
1379 | reg_val = res.idx; | ||
1380 | } break; | ||
1381 | default: { | ||
1382 | emit_compile_err(compiler, chunk, node); | ||
1383 | return (CompResult){.type = COMP_ERR}; | ||
1384 | } break; | ||
1385 | } | ||
1386 | |||
1387 | // Address. | ||
1388 | sz reg_addr = chunk->reg_idx++; | ||
1389 | // Is this a pointer access or an array access? | ||
1390 | if (str_has_prefix(map->val.type, cstr("[]"))) { | ||
1391 | emit_op(op_ldaddr, reg_addr, map->val.idx, 0, node->var.val, chunk); | ||
1392 | } else { | ||
1393 | emit_op(op_ldvar, reg_addr, map->val.idx, 0, node->var.val, chunk); | ||
1394 | } | ||
1395 | |||
1396 | // Index. | ||
1397 | CompResult idx = | ||
1398 | compile_expr(compiler, chunk, node->var.name->sym.arr_size); | ||
1399 | switch (idx.type) { | ||
1400 | case COMP_CONST: { | ||
1401 | emit_op(OP_ST64I, reg_val, reg_addr, idx.idx, node, chunk); | ||
1402 | } break; | ||
1403 | case COMP_REG: { | ||
1404 | emit_op(OP_ST64, reg_val, reg_addr, idx.idx, node, chunk); | ||
1405 | } break; | ||
1406 | default: { | ||
1407 | emit_compile_err(compiler, chunk, node); | ||
1408 | return (CompResult){.type = COMP_ERR}; | ||
1409 | } break; | ||
1410 | } | ||
1411 | // TODO: offset should be in bytes, in this case we are assuming | ||
1412 | // 64bit types, hence ST64 | ||
1413 | return (CompResult){.type = COMP_NIL}; | ||
1414 | } | ||
1415 | switch (res.type) { | ||
1416 | case COMP_CONST: { | ||
1417 | emit_op(op_stvari, map->val.idx, res.idx, 0, node->var.val, chunk); | ||
1418 | } break; | ||
1419 | case COMP_REG: { | ||
1420 | if (str_eq(node->var.val->type, cstr("str"))) { | ||
1421 | // Get the address for the local/global storage | ||
1422 | // variable. | ||
1423 | sz var_addr = chunk->reg_idx++; | ||
1424 | emit_op(op_ldaddr, var_addr, map->val.idx, 0, node, chunk); | ||
1425 | |||
1426 | // Copy the fat pointer. | ||
1427 | emit_fat_copy(chunk, node, var_addr, res.idx); | ||
1428 | } else { | ||
1429 | emit_op(op_stvar, map->val.idx, res.idx, 0, node->var.val, | ||
1430 | chunk); | ||
1431 | } | ||
1432 | } break; | ||
1433 | default: { | ||
1434 | emit_compile_err(compiler, chunk, node); | ||
1435 | return (CompResult){.type = COMP_ERR}; | ||
1436 | } break; | ||
1437 | } | ||
1438 | return (CompResult){.type = COMP_NIL}; | ||
1439 | } | ||
1440 | |||
1441 | CompResult | ||
1442 | compile_symbol(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1443 | Str name = node->unique_name; | ||
1444 | StrVarMap *map = NULL; | ||
1445 | Chunk *next = chunk; | ||
1446 | while (next) { | ||
1447 | map = varmap_lookup(&next->varmap, name); | ||
1448 | if (map) { | ||
1449 | break; | ||
1450 | } | ||
1451 | next = next->parent; | ||
1452 | } | ||
1453 | if (!map) { | ||
1454 | emit_compile_err(compiler, chunk, node); | ||
1455 | return (CompResult){.type = COMP_ERR}; | ||
1456 | } | ||
1457 | sz op_ldaddr = OP_LDLADDR; | ||
1458 | sz op_ldvar = OP_LDLVAR; | ||
1459 | if (next == &compiler->main_chunk) { | ||
1460 | op_ldaddr = OP_LDGADDR; | ||
1461 | op_ldvar = OP_LDGVAR; | ||
1462 | } | ||
1463 | Variable var = map->val; | ||
1464 | u8 reg_dst = chunk->reg_idx++; | ||
1465 | if (node->is_ptr || str_has_prefix(var.type, cstr("[]")) || | ||
1466 | str_eq(var.type, cstr("str"))) { | ||
1467 | emit_op(op_ldaddr, reg_dst, var.idx, 0, node, chunk); | ||
1468 | } else { | ||
1469 | emit_op(op_ldvar, reg_dst, var.idx, 0, node, chunk); | ||
1470 | } | ||
424 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 1471 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
425 | } | 1472 | } |
426 | 1473 | ||
427 | CompResult | 1474 | CompResult |
428 | compile_expr(Chunk *chunk, Node *node) { | 1475 | compile_symbol_idx(Compiler *compiler, Chunk *chunk, Node *node) { |
1476 | Str name = node->unique_name; | ||
1477 | StrVarMap *map = NULL; | ||
1478 | Chunk *next = chunk; | ||
1479 | while (next) { | ||
1480 | map = varmap_lookup(&next->varmap, name); | ||
1481 | if (map) { | ||
1482 | break; | ||
1483 | } | ||
1484 | next = next->parent; | ||
1485 | } | ||
1486 | if (!map) { | ||
1487 | eprintln("couldn't resolve symbol name: %s", name); | ||
1488 | emit_compile_err(compiler, chunk, node); | ||
1489 | return (CompResult){.type = COMP_ERR}; | ||
1490 | } | ||
1491 | sz op_ldaddr = OP_LDLADDR; | ||
1492 | sz op_ldvar = OP_LDLVAR; | ||
1493 | if (next == &compiler->main_chunk) { | ||
1494 | op_ldaddr = OP_LDGADDR; | ||
1495 | op_ldvar = OP_LDGVAR; | ||
1496 | } | ||
1497 | |||
1498 | // Destination. | ||
1499 | u8 reg_dst = chunk->reg_idx++; | ||
1500 | |||
1501 | // Address. | ||
1502 | sz reg_addr = chunk->reg_idx++; | ||
1503 | if (str_has_prefix(map->val.type, cstr("[]"))) { | ||
1504 | emit_op(op_ldaddr, reg_addr, map->val.idx, 0, node->var.val, chunk); | ||
1505 | } else { | ||
1506 | emit_op(op_ldvar, reg_addr, map->val.idx, 0, node->var.val, chunk); | ||
1507 | } | ||
1508 | |||
1509 | // Index. | ||
1510 | CompResult idx = compile_expr(compiler, chunk, node->sym.arr_size); | ||
1511 | switch (idx.type) { | ||
1512 | case COMP_CONST: { | ||
1513 | emit_op(OP_LD64I, reg_dst, reg_addr, idx.idx, node, chunk); | ||
1514 | } break; | ||
1515 | case COMP_REG: { | ||
1516 | emit_op(OP_LD64, reg_dst, reg_addr, idx.idx, node, chunk); | ||
1517 | } break; | ||
1518 | default: { | ||
1519 | emit_compile_err(compiler, chunk, node); | ||
1520 | return (CompResult){.type = COMP_ERR}; | ||
1521 | } break; | ||
1522 | } | ||
1523 | // TODO: hardcoding the type size for now (LD64/LD64I). | ||
1524 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
1525 | } | ||
1526 | |||
1527 | CompResult | ||
1528 | compile_expr(Compiler *compiler, Chunk *chunk, Node *node) { | ||
429 | switch (node->kind) { | 1529 | switch (node->kind) { |
1530 | case NODE_BREAK: return compile_break(compiler, chunk, node); | ||
1531 | case NODE_CONTINUE: return compile_continue(compiler, chunk, node); | ||
1532 | case NODE_RETURN: return compile_return(compiler, chunk, node); | ||
1533 | case NODE_FUN: return compile_function(compiler, chunk, node); | ||
1534 | case NODE_FUNCALL: return compile_funcall(compiler, chunk, node); | ||
1535 | case NODE_WHILE: return compile_while(compiler, chunk, node); | ||
1536 | case NODE_IF: return compile_if(compiler, chunk, node); | ||
1537 | case NODE_COND: return compile_cond(compiler, chunk, node); | ||
430 | // Logic. | 1538 | // Logic. |
431 | // case NODE_XOR: | ||
432 | case NODE_BITNOT: | 1539 | case NODE_BITNOT: |
433 | case NODE_NOT: return compile_unary(chunk, node); break; | 1540 | case NODE_NOT: return compile_unary(compiler, chunk, node); break; |
434 | case NODE_AND: | 1541 | case NODE_AND: |
435 | case NODE_OR: | 1542 | case NODE_OR: |
436 | case NODE_EQ: | 1543 | case NODE_EQ: |
@@ -441,6 +1548,7 @@ compile_expr(Chunk *chunk, Node *node) { | |||
441 | // Bitwise ops. | 1548 | // Bitwise ops. |
442 | case NODE_BITAND: | 1549 | case NODE_BITAND: |
443 | case NODE_BITOR: | 1550 | case NODE_BITOR: |
1551 | case NODE_BITXOR: | ||
444 | case NODE_BITLSHIFT: | 1552 | case NODE_BITLSHIFT: |
445 | case NODE_BITRSHIFT: | 1553 | case NODE_BITRSHIFT: |
446 | // Arithmetic. | 1554 | // Arithmetic. |
@@ -449,92 +1557,45 @@ compile_expr(Chunk *chunk, Node *node) { | |||
449 | case NODE_SUB: | 1557 | case NODE_SUB: |
450 | case NODE_MUL: | 1558 | case NODE_MUL: |
451 | case NODE_DIV: | 1559 | case NODE_DIV: |
452 | case NODE_MOD: return compile_binary(chunk, node); break; | 1560 | case NODE_MOD: return compile_binary(compiler, chunk, node); break; |
453 | case NODE_TRUE: | 1561 | case NODE_TRUE: |
454 | case NODE_FALSE: | 1562 | case NODE_FALSE: |
455 | case NODE_NUM_FLOAT: | 1563 | case NODE_NUM_FLOAT: |
456 | case NODE_NUM_UINT: | 1564 | case NODE_NUM_UINT: |
457 | case NODE_NUM_INT: { | 1565 | case NODE_NUM_INT: { |
458 | sz value = node->value.i; | 1566 | sz value = node->value.i; |
459 | // Make sure we don't have duplicated constants. | 1567 | sz const_idx = add_constant(chunk, value); |
460 | IntIntMap *map = intintmap_lookup(&chunk->intmap, value); | ||
461 | if (!map) { | ||
462 | map = intintmap_insert(&chunk->intmap, value, | ||
463 | chunk->const_idx++, chunk->storage); | ||
464 | Constant c = (Constant){.i = node->value.i}; | ||
465 | array_push(chunk->constants, c, chunk->storage); | ||
466 | } | ||
467 | return (CompResult){ | 1568 | return (CompResult){ |
468 | .type = COMP_CONST, | 1569 | .type = COMP_CONST, |
469 | .idx = map->val, | 1570 | .idx = const_idx, |
470 | }; | 1571 | }; |
471 | } break; | 1572 | } break; |
472 | case NODE_STRING: { | 1573 | case NODE_STRING: { |
473 | Str string = node->value.str; | 1574 | Str string = node->value.str; |
474 | // Make sure we don't have duplicated strings. | 1575 | sz str_idx = add_string(chunk, string); |
475 | StrIntMap *map = strintmap_lookup(&chunk->strmap, string); | ||
476 | if (!map) { | ||
477 | map = strintmap_insert(&chunk->strmap, string, chunk->str_idx++, | ||
478 | chunk->storage); | ||
479 | array_push(chunk->strings, string, chunk->storage); | ||
480 | } | ||
481 | return (CompResult){ | 1576 | return (CompResult){ |
482 | .type = COMP_STRING, | 1577 | .type = COMP_STRING, |
483 | .idx = map->val, | 1578 | .idx = str_idx, |
484 | }; | 1579 | }; |
485 | } break; | 1580 | } break; |
486 | case NODE_LET: { | 1581 | case NODE_LET: return compile_let(compiler, chunk, node); |
487 | sz idx = array_size(chunk->vars); | 1582 | case NODE_SET: return compile_set(compiler, chunk, node); |
488 | Str name = node->unique_name; | 1583 | case NODE_SYMBOL: return compile_symbol(compiler, chunk, node); |
489 | Str type = node->var_name->type; | 1584 | case NODE_SYMBOL_IDX: return compile_symbol_idx(compiler, chunk, node); |
490 | sz size = 8; | ||
491 | // TODO: get type storage from a table to consider all the basic | ||
492 | // types as well as user defined ones. | ||
493 | if (str_eq(type, cstr("str"))) { | ||
494 | size = 16; | ||
495 | } | ||
496 | Variable var = (Variable){ | ||
497 | .name = name, | ||
498 | .type = type, | ||
499 | .size = size, | ||
500 | .offset = chunk->var_off, | ||
501 | }; | ||
502 | varmap_insert(&chunk->varmap, name, var, chunk->storage); | ||
503 | array_push(chunk->vars, var, chunk->storage); | ||
504 | chunk->var_off += size; | ||
505 | |||
506 | // Value. | ||
507 | if (node->var_val) { | ||
508 | CompResult res = compile_expr(chunk, node->var_val); | ||
509 | switch (res.type) { | ||
510 | case COMP_CONST: { | ||
511 | EMIT_OP(OP_STVARI, idx, res.idx, 0, node->var_val, | ||
512 | chunk); | ||
513 | } break; | ||
514 | case COMP_REG: { | ||
515 | EMIT_OP(OP_STVAR, idx, res.idx, 0, node->var_val, | ||
516 | chunk); | ||
517 | } break; | ||
518 | default: { | ||
519 | return (CompResult){.type = COMP_ERR}; | ||
520 | } break; | ||
521 | } | ||
522 | } | ||
523 | |||
524 | return (CompResult){.type = COMP_NIL}; | ||
525 | } break; | ||
526 | case NODE_BLOCK: { | 1585 | case NODE_BLOCK: { |
527 | CompResult res; | 1586 | CompResult res; |
528 | for (sz i = 0; i < array_size(node->elements); i++) { | 1587 | for (sz i = 0; i < array_size(node->elements); i++) { |
529 | Node *root = node->elements[i]; | 1588 | Node *root = node->elements[i]; |
530 | res = compile_expr(chunk, root); | 1589 | res = compile_expr(compiler, chunk, root); |
531 | } | 1590 | } |
532 | return res; | 1591 | return res; |
533 | } break; | 1592 | } break; |
1593 | case NODE_NIL: return (CompResult){.type = COMP_NIL}; | ||
534 | default: { | 1594 | default: { |
535 | eprintln("error: compilation not implemented for node %s", | 1595 | eprintln("error: compilation not implemented for node %s", |
536 | node_str[node->kind]); | 1596 | node_str[node->kind]); |
537 | exit(EXIT_FAILURE); | 1597 | emit_compile_err(compiler, chunk, node); |
1598 | return (CompResult){.type = COMP_ERR}; | ||
538 | } break; | 1599 | } break; |
539 | } | 1600 | } |
540 | return (CompResult){.type = COMP_ERR}; | 1601 | return (CompResult){.type = COMP_ERR}; |
@@ -543,6 +1604,10 @@ compile_expr(Chunk *chunk, Node *node) { | |||
543 | void | 1604 | void |
544 | disassemble_instruction(Instruction instruction) { | 1605 | disassemble_instruction(Instruction instruction) { |
545 | switch (instruction.op) { | 1606 | switch (instruction.op) { |
1607 | case OP_CALL: | ||
1608 | println("%s f%d", op_str[instruction.op], instruction.dst, | ||
1609 | instruction.a, instruction.b); | ||
1610 | break; | ||
546 | case OP_MOV8: | 1611 | case OP_MOV8: |
547 | case OP_MOV16: | 1612 | case OP_MOV16: |
548 | case OP_MOV32: | 1613 | case OP_MOV32: |
@@ -550,6 +1615,11 @@ disassemble_instruction(Instruction instruction) { | |||
550 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, | 1615 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, |
551 | instruction.a, instruction.b); | 1616 | instruction.a, instruction.b); |
552 | break; | 1617 | break; |
1618 | case OP_JMPF: | ||
1619 | case OP_JMPT: | ||
1620 | println("%s l%d, r%d", op_str[instruction.op], instruction.dst, | ||
1621 | instruction.a, instruction.b); | ||
1622 | break; | ||
553 | case OP_LD8K: | 1623 | case OP_LD8K: |
554 | case OP_LD16K: | 1624 | case OP_LD16K: |
555 | case OP_LD32K: | 1625 | case OP_LD32K: |
@@ -587,6 +1657,7 @@ disassemble_instruction(Instruction instruction) { | |||
587 | case OP_BITRSHIFTI: | 1657 | case OP_BITRSHIFTI: |
588 | case OP_BITANDI: | 1658 | case OP_BITANDI: |
589 | case OP_BITORI: | 1659 | case OP_BITORI: |
1660 | case OP_BITXORI: | ||
590 | println("%s r%d, r%d, c%d", op_str[instruction.op], instruction.dst, | 1661 | println("%s r%d, r%d, c%d", op_str[instruction.op], instruction.dst, |
591 | instruction.a, instruction.b); | 1662 | instruction.a, instruction.b); |
592 | break; | 1663 | break; |
@@ -620,14 +1691,28 @@ disassemble_instruction(Instruction instruction) { | |||
620 | case OP_BITRSHIFT: | 1691 | case OP_BITRSHIFT: |
621 | case OP_BITAND: | 1692 | case OP_BITAND: |
622 | case OP_BITOR: | 1693 | case OP_BITOR: |
1694 | case OP_BITXOR: | ||
623 | println("%s r%d, r%d, r%d", op_str[instruction.op], instruction.dst, | 1695 | println("%s r%d, r%d, r%d", op_str[instruction.op], instruction.dst, |
624 | instruction.a, instruction.b); | 1696 | instruction.a, instruction.b); |
625 | break; | 1697 | break; |
626 | case OP_STVAR: | 1698 | case OP_LDGVAR: |
1699 | case OP_LDGADDR: | ||
1700 | case OP_LDLVAR: | ||
1701 | case OP_LDLADDR: | ||
1702 | println("%s r%d, v%d", op_str[instruction.op], instruction.dst, | ||
1703 | instruction.a, instruction.b); | ||
1704 | break; | ||
1705 | case OP_LDSTR: | ||
1706 | println("%s r%d, s%d", op_str[instruction.op], instruction.dst, | ||
1707 | instruction.a, instruction.b); | ||
1708 | break; | ||
1709 | case OP_STGVAR: | ||
1710 | case OP_STLVAR: | ||
627 | println("%s v%d, r%d", op_str[instruction.op], instruction.dst, | 1711 | println("%s v%d, r%d", op_str[instruction.op], instruction.dst, |
628 | instruction.a, instruction.b); | 1712 | instruction.a, instruction.b); |
629 | break; | 1713 | break; |
630 | case OP_STVARI: | 1714 | case OP_STGVARI: |
1715 | case OP_STLVARI: | ||
631 | println("%s v%d, c%d", op_str[instruction.op], instruction.dst, | 1716 | println("%s v%d, c%d", op_str[instruction.op], instruction.dst, |
632 | instruction.a, instruction.b); | 1717 | instruction.a, instruction.b); |
633 | break; | 1718 | break; |
@@ -641,6 +1726,37 @@ disassemble_instruction(Instruction instruction) { | |||
641 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, | 1726 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, |
642 | instruction.a, instruction.b); | 1727 | instruction.a, instruction.b); |
643 | break; | 1728 | break; |
1729 | case OP_JMP: | ||
1730 | println("%s l%d", op_str[instruction.op], instruction.dst, | ||
1731 | instruction.a, instruction.b); | ||
1732 | break; | ||
1733 | case OP_JMPFI: | ||
1734 | case OP_JMPTI: | ||
1735 | println("%s l%d, c%d", op_str[instruction.op], instruction.dst, | ||
1736 | instruction.a, instruction.b); | ||
1737 | break; | ||
1738 | case OP_PRINTS64: | ||
1739 | case OP_PRINTF64: | ||
1740 | case OP_PRINTSTR: | ||
1741 | case OP_PRINTBOOL: | ||
1742 | case OP_PUSH: | ||
1743 | case OP_POP: | ||
1744 | case OP_PUTRET: | ||
1745 | println("%s r%d", op_str[instruction.op], instruction.dst, | ||
1746 | instruction.a, instruction.b); | ||
1747 | break; | ||
1748 | case OP_PRINTSTRI: | ||
1749 | case OP_PRINTS64I: | ||
1750 | case OP_PRINTF64I: | ||
1751 | case OP_PRINTBOOLI: | ||
1752 | case OP_RESERVE: | ||
1753 | case OP_PUSHI: | ||
1754 | case OP_PUTRETI: | ||
1755 | println("%s c%d", op_str[instruction.op], instruction.dst, | ||
1756 | instruction.a, instruction.b); | ||
1757 | break; | ||
1758 | case OP_RET: | ||
1759 | case OP_RECUR: | ||
644 | case OP_HALT: println("%s", op_str[instruction.op]); break; | 1760 | case OP_HALT: println("%s", op_str[instruction.op]); break; |
645 | default: println("Unknown opcode %d", instruction.op); break; | 1761 | default: println("Unknown opcode %d", instruction.op); break; |
646 | } | 1762 | } |
@@ -648,36 +1764,120 @@ disassemble_instruction(Instruction instruction) { | |||
648 | 1764 | ||
649 | void | 1765 | void |
650 | disassemble_chunk(Chunk chunk) { | 1766 | disassemble_chunk(Chunk chunk) { |
651 | println("%s: =========== code ===========", chunk.file_name); | 1767 | println("CHUNK %d: %s%s", chunk.id, chunk.file_name, chunk.name); |
1768 | println("n_regs: %d, n_vars: %d, n_strings: %d, n_consts: %d", | ||
1769 | chunk.reg_idx, array_size(chunk.vars), chunk.str_idx, | ||
1770 | chunk.const_idx); | ||
1771 | println("================== code =================="); | ||
1772 | println(" LINE:COL INUM LABELS OP OPERANDS "); | ||
1773 | println("------------------------------------------"); | ||
652 | for (sz i = 0; i < array_size(chunk.code); i++) { | 1774 | for (sz i = 0; i < array_size(chunk.code); i++) { |
653 | print("%s: %x{4}: ", chunk.file_name, i); | 1775 | printf(" %.4ld:%.4ld %.4lx ", chunk.linecol[i].line, |
1776 | chunk.linecol[i].col, i); | ||
1777 | IntIntMapIter lab_it = intintmap_iterator(chunk.labels, chunk.storage); | ||
1778 | IntIntMap *m = intintmap_next(&lab_it, chunk.storage); | ||
1779 | Str labs = cstr(""); | ||
1780 | while (m) { | ||
1781 | if (m->val == i) { | ||
1782 | labs = str_concat(labs, cstr(".L"), chunk.storage); | ||
1783 | labs = str_concat(labs, str_from_int(m->key, chunk.storage), | ||
1784 | chunk.storage); | ||
1785 | break; | ||
1786 | } | ||
1787 | m = intintmap_next(&lab_it, chunk.storage); | ||
1788 | } | ||
1789 | if (labs.size) { | ||
1790 | print("%s", labs); | ||
1791 | for (sz i = 0; i < 7 - labs.size; i++) { | ||
1792 | print(" "); | ||
1793 | } | ||
1794 | } else { | ||
1795 | printf(" "); | ||
1796 | } | ||
654 | disassemble_instruction(chunk.code[i]); | 1797 | disassemble_instruction(chunk.code[i]); |
655 | } | 1798 | } |
656 | if (array_size(chunk.constants) > 0) { | 1799 | if (array_size(chunk.constants) > 0) { |
657 | println("%s: ========= constants ========", chunk.file_name); | 1800 | println("================ constants ===============", chunk.file_name); |
658 | for (sz i = 0; i < array_size(chunk.constants); i++) { | 1801 | for (sz i = 0; i < array_size(chunk.constants); i++) { |
659 | println("%s: %x{2}: %x{8}", chunk.file_name, i, | 1802 | println(" %x{2}: %x{8}", i, chunk.constants[i]); |
660 | chunk.constants[i]); | ||
661 | } | 1803 | } |
662 | } | 1804 | } |
663 | if (array_size(chunk.strings) > 0) { | 1805 | if (array_size(chunk.strings) > 0) { |
664 | println("%s: ========== strings =========", chunk.file_name); | 1806 | println("================= strings ================", chunk.file_name); |
665 | for (sz i = 0; i < array_size(chunk.strings); i++) { | 1807 | for (sz i = 0; i < array_size(chunk.strings); i++) { |
666 | println("%s: %x{2}: %s", chunk.file_name, i, chunk.strings[i]); | 1808 | println(" %x{2}: %s", i, chunk.strings[i]); |
667 | } | 1809 | } |
668 | } | 1810 | } |
669 | if (array_size(chunk.vars) > 0) { | 1811 | if (array_size(chunk.vars) > 0) { |
670 | println("%s: ========= variables ========", chunk.file_name); | 1812 | println("================ variables ===============", chunk.file_name); |
671 | for (sz i = 0; i < array_size(chunk.vars); i++) { | 1813 | for (sz i = 0; i < array_size(chunk.vars); i++) { |
672 | println("%s: %x{2}: [%x{4}:%x{4}] %s: %s", chunk.file_name, i, | 1814 | println(" %x{2}: [%x{4}:%x{4}] %s: %s", i, chunk.vars[i].offset, |
673 | chunk.vars[i].offset, | ||
674 | chunk.vars[i].offset + chunk.vars[i].size, | 1815 | chunk.vars[i].offset + chunk.vars[i].size, |
675 | chunk.vars[i].name, chunk.vars[i].type); | 1816 | chunk.vars[i].name, chunk.vars[i].type); |
676 | } | 1817 | } |
677 | } | 1818 | } |
678 | println("n_regs: %d, n_vars: %d, n_strings: %d, n_consts: %d", | 1819 | if (array_size(chunk.functions) > 0) { |
679 | chunk.reg_idx, array_size(chunk.vars), chunk.str_idx, | 1820 | println("================ functions ===============", chunk.file_name); |
680 | chunk.const_idx); | 1821 | for (sz i = 0; i < array_size(chunk.functions); i++) { |
1822 | Chunk *func = chunk.functions[i]; | ||
1823 | println(" %x{2}: func%d: %s", i, func->id, func->name); | ||
1824 | } | ||
1825 | } | ||
1826 | println("=========================================="); | ||
1827 | for (sz i = 0; i < array_size(chunk.functions); i++) { | ||
1828 | Chunk *func = chunk.functions[i]; | ||
1829 | disassemble_chunk(*func); | ||
1830 | } | ||
1831 | } | ||
1832 | |||
1833 | void | ||
1834 | bytecode_compiler(Compiler *compiler, Parser parser) { | ||
1835 | compiler->main_chunk = (Chunk){ | ||
1836 | .file_name = compiler->file_name, | ||
1837 | .storage = compiler->storage, | ||
1838 | .name = cstr(".main"), | ||
1839 | }; | ||
1840 | // TODO: Fill up builtin types and tables. | ||
1841 | array_zero(compiler->main_chunk.constants, 256, compiler->storage); | ||
1842 | array_zero(compiler->main_chunk.code, 0xffff, compiler->storage); | ||
1843 | sz n_roots = array_size(parser.nodes); | ||
1844 | CompResult res = {0}; | ||
1845 | |||
1846 | // Do a first pass to setup the function declarations on the main scope. | ||
1847 | Chunk *chunk = &compiler->main_chunk; | ||
1848 | for (sz i = 0; i < n_roots; i++) { | ||
1849 | Node *root = parser.nodes[i]; | ||
1850 | if (root->kind == NODE_FUN) { | ||
1851 | declare_function(chunk, root); | ||
1852 | } | ||
1853 | } | ||
1854 | |||
1855 | // Compile all root expressions. | ||
1856 | for (sz i = 0; i < n_roots; i++) { | ||
1857 | Node *root = parser.nodes[i]; | ||
1858 | res = compile_expr(compiler, chunk, root); | ||
1859 | } | ||
1860 | |||
1861 | // Make sure the last result is on r0. | ||
1862 | sz res_reg = 0; | ||
1863 | bool is_nil = false; | ||
1864 | switch (res.type) { | ||
1865 | case COMP_CONST: { | ||
1866 | res_reg = chunk->reg_idx++; | ||
1867 | Instruction inst = | ||
1868 | (Instruction){.op = OP_LD64K, .dst = res_reg, .a = res.idx}; | ||
1869 | array_push(chunk->code, inst, chunk->storage); | ||
1870 | } break; | ||
1871 | case COMP_REG: { | ||
1872 | res_reg = res.idx; | ||
1873 | } break; | ||
1874 | case COMP_NIL: { | ||
1875 | is_nil = true; | ||
1876 | } break; | ||
1877 | default: break; | ||
1878 | } | ||
1879 | emit_op(OP_HALT, res_reg, !is_nil, 0, NULL, chunk); | ||
1880 | verify_chunk(chunk); | ||
681 | } | 1881 | } |
682 | 1882 | ||
683 | #endif // COMPILER_C | 1883 | #endif // COMPILER_C |