diff options
Diffstat (limited to 'src/compiler.c')
-rw-r--r-- | src/compiler.c | 1969 |
1 files changed, 1724 insertions, 245 deletions
diff --git a/src/compiler.c b/src/compiler.c index 0f9607e..65429b0 100644 --- a/src/compiler.c +++ b/src/compiler.c | |||
@@ -5,27 +5,64 @@ | |||
5 | 5 | ||
6 | #include "parser.c" | 6 | #include "parser.c" |
7 | 7 | ||
8 | typedef struct Type { | ||
9 | Str name; | ||
10 | Str unique_name; | ||
11 | // Size of the type in bytes or 0 if it's target dependant. | ||
12 | sz size; | ||
13 | } Type; | ||
14 | |||
8 | typedef struct Variable { | 15 | typedef struct Variable { |
9 | Str name; | 16 | Str name; |
10 | Str type; | 17 | Str type_name; |
11 | sz size; | 18 | sz size; |
12 | sz offset; | 19 | sz offset; |
13 | sz idx; | 20 | sz idx; |
21 | Type type; | ||
14 | } Variable; | 22 | } Variable; |
15 | 23 | ||
24 | #define WORD_SIZE 8 | ||
25 | |||
26 | Type builtin_types[] = { | ||
27 | // Nil. | ||
28 | {cstr("nil"), cstr("nil"), 0}, | ||
29 | |||
30 | // Architecture dependant. | ||
31 | {cstr("Int"), cstr("Int"), 8}, | ||
32 | {cstr("UInt"), cstr("UInt"), 8}, | ||
33 | {cstr("Ptr"), cstr("Ptr"), 8}, | ||
34 | {cstr("Str"), cstr("Str"), 16}, | ||
35 | |||
36 | // Fixed integer types. | ||
37 | {cstr("Bool"), cstr("Bool"), 1}, | ||
38 | {cstr("U8"), cstr("U8"), 1}, | ||
39 | {cstr("S8"), cstr("S8"), 1}, | ||
40 | {cstr("U16"), cstr("U16"), 2}, | ||
41 | {cstr("S16"), cstr("S16"), 2}, | ||
42 | {cstr("U32"), cstr("U32"), 4}, | ||
43 | {cstr("S32"), cstr("S32"), 4}, | ||
44 | {cstr("U64"), cstr("U64"), 8}, | ||
45 | {cstr("S64"), cstr("S64"), 8}, | ||
46 | |||
47 | // Fixed float types. | ||
48 | {cstr("F32"), cstr("F32"), 4}, | ||
49 | {cstr("F64"), cstr("F64"), 8}, | ||
50 | }; | ||
51 | |||
16 | MAPDEF(StrVarMap, varmap, Str, Variable, str_hash, str_eq) | 52 | MAPDEF(StrVarMap, varmap, Str, Variable, str_hash, str_eq) |
53 | MAPDEF(StrTypeMap, strtype, Str, Type, str_hash, str_eq) | ||
17 | 54 | ||
18 | typedef struct Instruction { | 55 | typedef struct Instruction { |
19 | u8 dst; | 56 | u16 dst; |
20 | u8 a; | 57 | u16 a; |
21 | u8 b; | 58 | u16 b; |
22 | u8 op; | 59 | u16 op; |
23 | } Instruction; | 60 | } Instruction; |
24 | 61 | ||
25 | typedef union Constant { | 62 | typedef union Constant { |
26 | s64 i; | 63 | s64 i; |
27 | u64 u; | 64 | u64 u; |
28 | double f; | 65 | f64 f; |
29 | ptrsize ptr; | 66 | ptrsize ptr; |
30 | } Constant; | 67 | } Constant; |
31 | 68 | ||
@@ -34,9 +71,23 @@ typedef struct LineCol { | |||
34 | sz col; | 71 | sz col; |
35 | } LineCol; | 72 | } LineCol; |
36 | 73 | ||
74 | typedef struct Function { | ||
75 | Str name; | ||
76 | sz param_arity; | ||
77 | sz return_arity; | ||
78 | sz index; | ||
79 | } Function; | ||
80 | |||
81 | MAPDEF(FunctionMap, funcmap, Str, Function, str_hash, str_eq) | ||
82 | |||
37 | typedef struct Chunk { | 83 | typedef struct Chunk { |
38 | sz id; | 84 | sz id; |
85 | Str name; | ||
86 | struct Chunk *parent; | ||
87 | |||
39 | Instruction *code; | 88 | Instruction *code; |
89 | IntIntMap *labels; // label -> chunk_index | ||
90 | sz labels_idx; | ||
40 | 91 | ||
41 | // Constant values that fit in 64 bits. | 92 | // Constant values that fit in 64 bits. |
42 | Constant *constants; | 93 | Constant *constants; |
@@ -52,45 +103,116 @@ typedef struct Chunk { | |||
52 | Variable *vars; | 103 | Variable *vars; |
53 | StrVarMap *varmap; | 104 | StrVarMap *varmap; |
54 | sz var_off; | 105 | sz var_off; |
106 | sz param_off; | ||
55 | 107 | ||
56 | // Number of registers currently used in this chunk. | 108 | // Number of registers currently used in this chunk. |
57 | sz reg_idx; | 109 | sz reg_idx; |
58 | 110 | ||
111 | // Number of functions currently used in this chunk. | ||
112 | struct Chunk **functions; | ||
113 | FunctionMap *funmap; | ||
114 | sz fun_idx; | ||
115 | |||
59 | // Debugging. | 116 | // Debugging. |
60 | Str file_name; | 117 | Str file_name; |
61 | Arena *storage; | 118 | Arena *storage; |
62 | LineCol *linecol; | 119 | LineCol *linecol; |
63 | } Chunk; | 120 | } Chunk; |
64 | 121 | ||
122 | typedef struct Compiler { | ||
123 | Chunk main_chunk; | ||
124 | Str file_name; | ||
125 | Arena *storage; | ||
126 | |||
127 | // Tables. | ||
128 | StrSet *integer_types; | ||
129 | StrSet *signed_ints; | ||
130 | StrSet *unsigned_ints; | ||
131 | StrSet *float_types; | ||
132 | StrSet *numeric_types; | ||
133 | StrTypeMap *type_map; | ||
134 | |||
135 | // Destinations. | ||
136 | sz lab_pre; | ||
137 | sz lab_post; | ||
138 | } Compiler; | ||
139 | |||
65 | typedef enum OpCode { | 140 | typedef enum OpCode { |
66 | // OP DST A B | 141 | // OP DST A B |
67 | // --------------------------------------------------------------- | 142 | // --------------------------------------------------------------- |
68 | // VM/high level instructions. | 143 | // VM/high level instructions. |
69 | OP_HALT, // halt | 144 | OP_HALT, // halt |
145 | // NOTE: LDGVAR/STGVAR* could be obtained in terms of LDGADDR. | ||
70 | OP_STGVARI, // stgvari vx, ca | 146 | OP_STGVARI, // stgvari vx, ca |
71 | OP_STGVAR, // stgvar vx, ra | 147 | OP_STGVAR, // stgvar vx, ra |
72 | OP_LDGVAR, // ldgvar rx, vx | 148 | OP_LDGVAR, // ldgvar rx, va |
149 | OP_LDGADDR, // ldgaddr rx, va | ||
150 | OP_STLVARI, // stlvari vx, ca | ||
151 | OP_STLVAR, // stlvar vx, ra | ||
152 | OP_LDLVAR, // ldlvar rx, va | ||
153 | OP_LDLADDR, // ldladdr rx, va | ||
154 | OP_LDSTR, // ldstr rx, sa ; Stores the address of the string sa into rx | ||
155 | // Functions. | ||
156 | OP_CALL, // call fx ; Call the function fx | ||
157 | OP_RECUR, // recur ; Jump to the beginning of the function. | ||
158 | OP_RET, // ret ; Returns from current function | ||
159 | OP_RESERVE, // reserve cx ; Increments the stack pointer by cx bytes | ||
160 | OP_POP, // pop rx ; Pops the last value of the stack into rx. | ||
161 | OP_PUSH, // push rx ; Push the rx value to the stack. | ||
162 | OP_PUSHI, // pushi cx ; Push the cx value to the stack. | ||
163 | OP_PUTRET, // putret rx ; Put rx into the return value memory. | ||
164 | OP_PUTRETI, // putreti cx ; Put cx into the return value memory. | ||
165 | // Printing values with builtin print/println functions. | ||
166 | OP_PRINTSTR, // p rx | ||
167 | OP_PRINTSTRI, // p cx | ||
168 | OP_PRINTS8, // p rx | ||
169 | OP_PRINTS8I, // p cx | ||
170 | OP_PRINTS16, // p rx | ||
171 | OP_PRINTS16I, // p cx | ||
172 | OP_PRINTS32, // p rx | ||
173 | OP_PRINTS32I, // p cx | ||
174 | OP_PRINTS64, // p rx | ||
175 | OP_PRINTS64I, // p cx | ||
176 | OP_PRINTU8, // p rx | ||
177 | OP_PRINTU8I, // p cx | ||
178 | OP_PRINTU16, // p rx | ||
179 | OP_PRINTU16I, // p cx | ||
180 | OP_PRINTU32, // p rx | ||
181 | OP_PRINTU32I, // p cx | ||
182 | OP_PRINTU64, // p rx | ||
183 | OP_PRINTU64I, // p cx | ||
184 | OP_PRINTF64, // p rx | ||
185 | OP_PRINTF64I, // p cx | ||
186 | OP_PRINTF32, // p cx | ||
187 | OP_PRINTF32I, // p cx | ||
188 | OP_PRINTBOOL, // p rx | ||
189 | OP_PRINTBOOLI, // p cx | ||
73 | // Load/Store instructions. | 190 | // Load/Store instructions. |
74 | OP_LD8K, // ld8k rx, ca -> u8 rx = ca | 191 | OP_LDCONST, // ldconst rx, ca -> u64 rx = ca |
75 | OP_LD16K, // ld16k rx, ca -> u16 rx = ca | 192 | OP_LD8K, // ld8k rx, ra -> u8 *p = ra; rx = *p |
76 | OP_LD32K, // ld32k rx, ca -> u32 rx = ca | 193 | OP_LD16K, // ld16k rx, ra -> u16 *p = ra; rx = *p |
77 | OP_LD64K, // ld64k rx, ca -> u64 rx = ca | 194 | OP_LD32K, // ld32k rx, ra -> u32 *p = ra; rx = *p |
78 | OP_LD8I, // ld8i rx, ra, cb -> u8 *p; rx = p[ra + cb] | 195 | OP_LD64K, // ld64k rx, ra -> u64 *p = ra; rx = *p |
79 | OP_LD16I, // ld16i rx, ra, cb -> u16 *p; rx = p[ra + cb] | 196 | OP_ST8K, // ld8k rx, ra -> u8 *p = ra; *p = rx |
80 | OP_LD32I, // ld32i rx, ra, cb -> u32 *p; rx = p[ra + cb] | 197 | OP_ST16K, // ld16k rx, ra -> u16 *p = ra; *p = rx |
81 | OP_LD64I, // ld64i rx, ra, cb -> u64 *p; rx = p[ra + cb] | 198 | OP_ST32K, // ld32k rx, ra -> u32 *p = ra; *p = rx |
82 | OP_LD8, // ld8 rx, ra, rb -> u8 *p; rx = p[ra + rb] | 199 | OP_ST64K, // ld64k rx, ra -> u64 *p = ra; *p = rx |
83 | OP_LD16, // ld16 rx, ra, rb -> u16 *p; rx = p[ra + rb] | 200 | OP_LD8I, // ld8i rx, ra, cb -> u8 *p = ra; rx = p[cb] |
84 | OP_LD32, // ld32 rx, ra, rb -> u32 *p; rx = p[ra + rb] | 201 | OP_LD16I, // ld16i rx, ra, cb -> u16 *p = ra; rx = p[cb] |
85 | OP_LD64, // ld64 rx, ra, rb -> u64 *p; rx = p[ra + rb] | 202 | OP_LD32I, // ld32i rx, ra, cb -> u32 *p = ra; rx = p[cb] |
86 | OP_ST8I, // st8i rx, ra, cb -> u8 *p; p[ra + cb] = rx | 203 | OP_LD64I, // ld64i rx, ra, cb -> u64 *p = ra; rx = p[cb] |
87 | OP_ST16I, // st16i rx, ra, cb -> u16 *p; p[ra + cb] = rx | 204 | OP_LD8, // ld8 rx, ra, rb -> u8 *p = ra; rx = p[rb] |
88 | OP_ST32I, // st32i rx, ra, cb -> u32 *p; p[ra + cb] = rx | 205 | OP_LD16, // ld16 rx, ra, rb -> u16 *p = ra; rx = p[rb] |
89 | OP_ST64I, // st64i rx, ra, cb -> u64 *p; p[ra + cb] = rx | 206 | OP_LD32, // ld32 rx, ra, rb -> u32 *p = ra; rx = p[rb] |
90 | OP_ST8, // st8 rx, ra, rb -> u8 *p; p[ra + rb] = rx | 207 | OP_LD64, // ld64 rx, ra, rb -> u64 *p = ra; rx = p[rb] |
91 | OP_ST16, // st16 rx, ra, rb -> u16 *p; p[ra + rb] = rx | 208 | OP_ST8I, // st8i rx, ra, cb -> u8 *p = ra; p[cb] = rx |
92 | OP_ST32, // st32 rx, ra, rb -> u32 *p; p[ra + rb] = rx | 209 | OP_ST16I, // st16i rx, ra, cb -> u16 *p = ra; p[cb] = rx |
93 | OP_ST64, // st64 rx, ra, rb -> u64 *p; p[ra + rb] = rx | 210 | OP_ST32I, // st32i rx, ra, cb -> u32 *p = ra; p[cb] = rx |
211 | OP_ST64I, // st64i rx, ra, cb -> u64 *p = ra; p[cb] = rx | ||
212 | OP_ST8, // st8 rx, ra, rb -> u8 *p = ra; p[rb] = rx | ||
213 | OP_ST16, // st16 rx, ra, rb -> u16 *p = ra; p[rb] = rx | ||
214 | OP_ST32, // st32 rx, ra, rb -> u32 *p = ra; p[rb] = rx | ||
215 | OP_ST64, // st64 rx, ra, rb -> u64 *p = ra; p[rb] = rx | ||
94 | // Integer arithmetic (only int/s64 for now). | 216 | // Integer arithmetic (only int/s64 for now). |
95 | OP_ADDI, // addk rx, ra, cb | 217 | OP_ADDI, // addk rx, ra, cb |
96 | OP_SUBI, // subk rx, ra, cb | 218 | OP_SUBI, // subk rx, ra, cb |
@@ -142,31 +264,79 @@ typedef enum OpCode { | |||
142 | OP_BITRSHIFTI, // shri rx, ra, cb | 264 | OP_BITRSHIFTI, // shri rx, ra, cb |
143 | OP_BITANDI, // bandi rx, ra, cb | 265 | OP_BITANDI, // bandi rx, ra, cb |
144 | OP_BITORI, // bori rx, ra, cb | 266 | OP_BITORI, // bori rx, ra, cb |
267 | OP_BITXORI, // bxor rx, ra, cb | ||
145 | OP_BITNOTI, // bnoti rx, ca | 268 | OP_BITNOTI, // bnoti rx, ca |
146 | OP_BITLSHIFT, // shl rx, ra, rb | 269 | OP_BITLSHIFT, // shl rx, ra, rb |
147 | OP_BITRSHIFT, // shr rx, ra, rb | 270 | OP_BITRSHIFT, // shr rx, ra, rb |
148 | OP_BITAND, // band rx, ra, rb | 271 | OP_BITAND, // band rx, ra, rb |
149 | OP_BITOR, // bor rx, ra, rb | 272 | OP_BITOR, // bor rx, ra, rb |
273 | OP_BITXOR, // bxor rx, ra, rb | ||
150 | OP_BITNOT, // bnot rx, ra | 274 | OP_BITNOT, // bnot rx, ra |
151 | // Jump instructions. | 275 | // Jump instructions. |
152 | OP_JMPI, // jmp cx ; cx := signed offset | 276 | OP_JMP, // jmp lx ; jmp to label lx |
153 | OP_JMPFI, // jmpf cx, ca ; rx := condition, ca := offset | 277 | OP_JMPF, // jmpf lx, rx ; jmp to label lx if rx is false |
154 | OP_JMPTI, // jmpt cx, ca ; rx := condition, ca := offset | 278 | OP_JMPT, // jmpt lx, rx ; jmp to label lx if rx is true |
155 | OP_JMP, // jmp rx ; rx := signed offset | 279 | OP_JMPFI, // jmpf lx, cx ; jmp to label lx if rx is false |
156 | OP_JMPF, // jmpf rx, ca ; rx := condition, ca := offset | 280 | OP_JMPTI, // jmpt lx, cx ; jmp to label lx if rx is true |
157 | OP_JMPT, // jmpt rx, ca ; rx := condition, ca := offset | 281 | _OP_NUM, |
158 | } OpCode; | 282 | } OpCode; |
159 | 283 | ||
160 | Str op_str[] = { | 284 | Str op_str[] = { |
285 | // High level ops. | ||
161 | [OP_HALT] = cstr("HALT "), | 286 | [OP_HALT] = cstr("HALT "), |
162 | [OP_STGVAR] = cstr("STGVAR "), | 287 | [OP_STGVAR] = cstr("STGVAR "), |
163 | [OP_STGVARI] = cstr("STGVARI "), | 288 | [OP_STGVARI] = cstr("STGVARI "), |
164 | [OP_LDGVAR] = cstr("LDGVAR "), | 289 | [OP_LDGVAR] = cstr("LDGVAR "), |
290 | [OP_LDGADDR] = cstr("LDGADDR "), | ||
291 | [OP_STLVAR] = cstr("STLVAR "), | ||
292 | [OP_STLVARI] = cstr("STLVARI "), | ||
293 | [OP_LDLVAR] = cstr("LDLVAR "), | ||
294 | [OP_LDLADDR] = cstr("LDLADDR "), | ||
295 | [OP_LDSTR] = cstr("LDSTR "), | ||
296 | [OP_PRINTSTR] = cstr("PRNSTR "), | ||
297 | [OP_PRINTSTRI] = cstr("PRNSTRI "), | ||
298 | [OP_PRINTS8] = cstr("PRNS8 "), | ||
299 | [OP_PRINTS8I] = cstr("PRNS8I "), | ||
300 | [OP_PRINTS16] = cstr("PRNS16 "), | ||
301 | [OP_PRINTS16I] = cstr("PRNS16I "), | ||
302 | [OP_PRINTS32] = cstr("PRNS32 "), | ||
303 | [OP_PRINTS32I] = cstr("PRNS32I "), | ||
304 | [OP_PRINTS64] = cstr("PRNS64 "), | ||
305 | [OP_PRINTS64I] = cstr("PRNS64I "), | ||
306 | [OP_PRINTU8] = cstr("PRNU8 "), | ||
307 | [OP_PRINTU8I] = cstr("PRNU8I "), | ||
308 | [OP_PRINTU16] = cstr("PRNU16 "), | ||
309 | [OP_PRINTU16I] = cstr("PRNU16I "), | ||
310 | [OP_PRINTU32] = cstr("PRNU32 "), | ||
311 | [OP_PRINTU32I] = cstr("PRNU32I "), | ||
312 | [OP_PRINTU64] = cstr("PRNU64 "), | ||
313 | [OP_PRINTU64I] = cstr("PRNU64I "), | ||
314 | [OP_PRINTF32] = cstr("PRNF32 "), | ||
315 | [OP_PRINTF32I] = cstr("PRNF32I "), | ||
316 | [OP_PRINTF64] = cstr("PRNF64 "), | ||
317 | [OP_PRINTF64I] = cstr("PRNF64I "), | ||
318 | [OP_PRINTBOOL] = cstr("PRNBOOL "), | ||
319 | [OP_PRINTBOOLI] = cstr("PRNBOOLI"), | ||
320 | [OP_PUTRET] = cstr("PUTRET "), | ||
321 | [OP_PUTRETI] = cstr("PUTRETI "), | ||
322 | // Functions. | ||
323 | [OP_CALL] = cstr("CALL "), | ||
324 | [OP_RECUR] = cstr("RECUR "), | ||
325 | [OP_RET] = cstr("RET "), | ||
326 | [OP_RESERVE] = cstr("RESERVE "), | ||
327 | [OP_POP] = cstr("POP "), | ||
328 | [OP_PUSH] = cstr("PUSH "), | ||
329 | [OP_PUSHI] = cstr("PUSHI "), | ||
165 | // Load ops. | 330 | // Load ops. |
331 | [OP_LDCONST] = cstr("LDCONST "), | ||
166 | [OP_LD8K] = cstr("LD8K "), | 332 | [OP_LD8K] = cstr("LD8K "), |
167 | [OP_LD16K] = cstr("LD16K "), | 333 | [OP_LD16K] = cstr("LD16K "), |
168 | [OP_LD32K] = cstr("LD32K "), | 334 | [OP_LD32K] = cstr("LD32K "), |
169 | [OP_LD64K] = cstr("LD64K "), | 335 | [OP_LD64K] = cstr("LD64K "), |
336 | [OP_ST8K] = cstr("ST8K "), | ||
337 | [OP_ST16K] = cstr("ST6K "), | ||
338 | [OP_ST32K] = cstr("ST32K "), | ||
339 | [OP_ST64K] = cstr("ST64K "), | ||
170 | [OP_LD8I] = cstr("LD8I "), | 340 | [OP_LD8I] = cstr("LD8I "), |
171 | [OP_LD16I] = cstr("LD16I "), | 341 | [OP_LD16I] = cstr("LD16I "), |
172 | [OP_LD32I] = cstr("LD32I "), | 342 | [OP_LD32I] = cstr("LD32I "), |
@@ -234,19 +404,20 @@ Str op_str[] = { | |||
234 | [OP_BITRSHIFTI] = cstr("RSHI "), | 404 | [OP_BITRSHIFTI] = cstr("RSHI "), |
235 | [OP_BITANDI] = cstr("BANDI "), | 405 | [OP_BITANDI] = cstr("BANDI "), |
236 | [OP_BITORI] = cstr("BORI "), | 406 | [OP_BITORI] = cstr("BORI "), |
407 | [OP_BITXORI] = cstr("BXORI "), | ||
237 | [OP_BITNOTI] = cstr("BNOTI "), | 408 | [OP_BITNOTI] = cstr("BNOTI "), |
238 | [OP_BITLSHIFT] = cstr("LSH "), | 409 | [OP_BITLSHIFT] = cstr("LSH "), |
239 | [OP_BITRSHIFT] = cstr("RSH "), | 410 | [OP_BITRSHIFT] = cstr("RSH "), |
240 | [OP_BITAND] = cstr("BAND "), | 411 | [OP_BITAND] = cstr("BAND "), |
241 | [OP_BITOR] = cstr("BOR "), | 412 | [OP_BITOR] = cstr("BOR "), |
413 | [OP_BITXOR] = cstr("XBOR "), | ||
242 | [OP_BITNOT] = cstr("BNOT "), | 414 | [OP_BITNOT] = cstr("BNOT "), |
243 | // Jump instructions. | 415 | // Jump instructions. |
244 | [OP_JMPI] = cstr("JMPI "), | ||
245 | [OP_JMPFI] = cstr("JMPFI "), | ||
246 | [OP_JMPTI] = cstr("JMPTI "), | ||
247 | [OP_JMP] = cstr("JMP "), | 416 | [OP_JMP] = cstr("JMP "), |
248 | [OP_JMPF] = cstr("JMPF "), | 417 | [OP_JMPF] = cstr("JMPF "), |
249 | [OP_JMPT] = cstr("JMPT "), | 418 | [OP_JMPT] = cstr("JMPT "), |
419 | [OP_JMPFI] = cstr("JMPFI "), | ||
420 | [OP_JMPTI] = cstr("JMPTI "), | ||
250 | }; | 421 | }; |
251 | 422 | ||
252 | typedef enum { | 423 | typedef enum { |
@@ -254,6 +425,7 @@ typedef enum { | |||
254 | COMP_CONST, | 425 | COMP_CONST, |
255 | COMP_STRING, | 426 | COMP_STRING, |
256 | COMP_REG, | 427 | COMP_REG, |
428 | COMP_RET, | ||
257 | COMP_ERR, | 429 | COMP_ERR, |
258 | } CompResultType; | 430 | } CompResultType; |
259 | 431 | ||
@@ -262,69 +434,195 @@ typedef struct CompResult { | |||
262 | CompResultType type; | 434 | CompResultType type; |
263 | } CompResult; | 435 | } CompResult; |
264 | 436 | ||
265 | CompResult compile_expr(Chunk *chunk, Node *node); | 437 | CompResult compile_expr(Compiler *compiler, Chunk *chunk, Node *node); |
266 | 438 | ||
267 | #define EMIT_OP(OP, DST, A, B, NODE, CHUNK) \ | 439 | sz |
268 | do { \ | 440 | add_constant(Chunk *chunk, sz value) { |
269 | Instruction inst = (Instruction){ \ | 441 | IntIntMap *map = intintmap_lookup(&chunk->intmap, value); |
270 | .op = (OP), \ | 442 | // Make sure we don't have duplicated constants. |
271 | .dst = (DST), \ | 443 | if (!map) { |
272 | .a = (A), \ | 444 | map = intintmap_insert(&chunk->intmap, value, chunk->const_idx++, |
273 | .b = (B), \ | 445 | chunk->storage); |
274 | }; \ | 446 | Constant c = (Constant){.i = value}; |
275 | array_push((CHUNK)->code, inst, (CHUNK)->storage); \ | 447 | array_push(chunk->constants, c, chunk->storage); |
276 | LineCol linecol = (LineCol){.line = (NODE)->line, .col = (NODE)->col}; \ | 448 | } |
277 | array_push((CHUNK)->linecol, linecol, (CHUNK)->storage); \ | 449 | return map->val; |
278 | } while (0) | 450 | } |
451 | |||
452 | sz | ||
453 | add_string(Chunk *chunk, Str string) { | ||
454 | // Make sure we don't have duplicated string. | ||
455 | StrIntMap *map = strintmap_lookup(&chunk->strmap, string); | ||
456 | if (!map) { | ||
457 | map = strintmap_insert(&chunk->strmap, string, chunk->str_idx++, | ||
458 | chunk->storage); | ||
459 | array_push(chunk->strings, string, chunk->storage); | ||
460 | } | ||
461 | return map->val; | ||
462 | } | ||
463 | |||
464 | sz | ||
465 | add_variable(Compiler *compiler, | ||
466 | Chunk *chunk, | ||
467 | Str name, | ||
468 | Str type_name, | ||
469 | sz arr_size) { | ||
470 | sz idx = array_size(chunk->vars); | ||
471 | Str base_type = type_name; | ||
472 | if (str_has_prefix(base_type, cstr("@"))) { | ||
473 | base_type = str_remove_prefix(base_type, cstr("@")); | ||
474 | if (str_has_prefix(base_type, cstr("@"))) { | ||
475 | base_type = cstr("Ptr"); | ||
476 | } else if (str_has_prefix(base_type, cstr("["))) { | ||
477 | base_type = cstr("Ptr"); | ||
478 | } | ||
479 | } else if (str_has_prefix(base_type, cstr("["))) { | ||
480 | str_split(&base_type, cstr("]")); | ||
481 | if (str_has_prefix(base_type, cstr("@"))) { | ||
482 | base_type = cstr("Ptr"); | ||
483 | } else if (str_has_prefix(base_type, cstr("["))) { | ||
484 | base_type = cstr("Ptr"); | ||
485 | } | ||
486 | } | ||
487 | StrTypeMap *t = strtype_lookup(&compiler->type_map, base_type); | ||
488 | sz size = t->val.size; | ||
489 | |||
490 | // An array. | ||
491 | if (arr_size) { | ||
492 | size *= arr_size; | ||
493 | } else if (str_has_prefix(type_name, cstr("@"))) { | ||
494 | StrTypeMap *tp = strtype_lookup(&compiler->type_map, cstr("Ptr")); | ||
495 | size = tp->val.size; | ||
496 | } | ||
497 | |||
498 | sz padding = -size & (WORD_SIZE - 1); | ||
499 | size += padding; | ||
500 | Variable var = (Variable){ | ||
501 | .name = name, | ||
502 | .type = t->val, | ||
503 | .type_name = type_name, | ||
504 | .size = size, | ||
505 | .offset = chunk->var_off, | ||
506 | .idx = idx, | ||
507 | }; | ||
508 | varmap_insert(&chunk->varmap, name, var, chunk->storage); | ||
509 | array_push(chunk->vars, var, chunk->storage); | ||
510 | chunk->var_off += size; | ||
511 | return idx; | ||
512 | } | ||
513 | |||
514 | void | ||
515 | emit_op(OpCode op, sz dst, sz a, sz b, Node *node, Chunk *chunk) { | ||
516 | Instruction inst = (Instruction){ | ||
517 | .op = op, | ||
518 | .dst = dst, | ||
519 | .a = a, | ||
520 | .b = b, | ||
521 | }; | ||
522 | array_push(chunk->code, inst, chunk->storage); | ||
523 | LineCol linecol = (LineCol){0}; | ||
524 | if (node) { | ||
525 | linecol = (LineCol){.line = node->line, .col = node->col}; | ||
526 | } | ||
527 | array_push(chunk->linecol, linecol, chunk->storage); | ||
528 | } | ||
529 | |||
530 | void | ||
531 | emit_sized_op(sz size, | ||
532 | OpCode op64, | ||
533 | OpCode op32, | ||
534 | OpCode op16, | ||
535 | OpCode op8, | ||
536 | sz dst, | ||
537 | sz a, | ||
538 | sz b, | ||
539 | Node *node, | ||
540 | Chunk *chunk) { | ||
541 | if (size == 8) { | ||
542 | emit_op(op64, dst, a, b, node, chunk); | ||
543 | } else if (size == 4) { | ||
544 | emit_op(op32, dst, a, b, node, chunk); | ||
545 | } else if (size == 2) { | ||
546 | emit_op(op16, dst, a, b, node, chunk); | ||
547 | } else if (size == 1) { | ||
548 | emit_op(op8, dst, a, b, node, chunk); | ||
549 | } | ||
550 | } | ||
551 | |||
552 | void | ||
553 | emit_fat_copy(Chunk *chunk, Node *node, sz dst_addr, sz src_addr) { | ||
554 | sz reg_dst = chunk->reg_idx++; | ||
555 | |||
556 | // Store the fat string pointer into the variable. | ||
557 | sz zero = add_constant(chunk, 0); | ||
558 | sz one = add_constant(chunk, 1); | ||
559 | |||
560 | // Get the value for the first word of the string | ||
561 | // pointer. | ||
562 | emit_op(OP_LD64I, reg_dst, src_addr, zero, node, chunk); | ||
563 | emit_op(OP_ST64I, reg_dst, dst_addr, zero, node, chunk); | ||
564 | emit_op(OP_LD64I, reg_dst, src_addr, one, node, chunk); | ||
565 | emit_op(OP_ST64I, reg_dst, dst_addr, one, node, chunk); | ||
566 | } | ||
567 | |||
568 | void disassemble_chunk(Chunk chunk); | ||
569 | |||
570 | void | ||
571 | emit_compile_err(Compiler *compiler, Chunk *chunk, Node *node) { | ||
572 | disassemble_chunk(*chunk); | ||
573 | eprintln("%s:%d:%d: error: compilation error on: %s", compiler->file_name, | ||
574 | node->line, node->col, node_str[node->kind]); | ||
575 | exit(EXIT_FAILURE); | ||
576 | } | ||
279 | 577 | ||
280 | CompResult | 578 | CompResult |
281 | compile_binary(Chunk *chunk, Node *node) { | 579 | compile_binary(Compiler *compiler, Chunk *chunk, Node *node) { |
282 | OpCode op = OP_HALT; | 580 | OpCode op = OP_HALT; |
283 | OpCode opi = OP_HALT; | 581 | OpCode opi = OP_HALT; |
284 | OpCode ldop = OP_LD64K; | 582 | OpCode ldop = OP_LDCONST; |
285 | switch (node->kind) { | 583 | switch (node->kind) { |
286 | // Arithmetic. | 584 | // Arithmetic. |
287 | case NODE_ADD: { | 585 | case NODE_ADD: { |
288 | if (str_eq(node->type, cstr("int"))) { | 586 | if (strset_lookup(&compiler->integer_types, node->type)) { |
289 | op = OP_ADD; | 587 | op = OP_ADD; |
290 | opi = OP_ADDI; | 588 | opi = OP_ADDI; |
291 | } else if (str_eq(node->type, cstr("f64"))) { | 589 | } else if (strset_lookup(&compiler->float_types, node->type)) { |
292 | op = OP_ADDF; | 590 | op = OP_ADDF; |
293 | opi = OP_ADDFI; | 591 | opi = OP_ADDFI; |
294 | } | 592 | } |
295 | } break; | 593 | } break; |
296 | case NODE_SUB: { | 594 | case NODE_SUB: { |
297 | if (str_eq(node->type, cstr("int"))) { | 595 | if (strset_lookup(&compiler->integer_types, node->type)) { |
298 | op = OP_SUB; | 596 | op = OP_SUB; |
299 | opi = OP_SUBI; | 597 | opi = OP_SUBI; |
300 | } else if (str_eq(node->type, cstr("f64"))) { | 598 | } else if (strset_lookup(&compiler->float_types, node->type)) { |
301 | op = OP_SUBF; | 599 | op = OP_SUBF; |
302 | opi = OP_SUBFI; | 600 | opi = OP_SUBFI; |
303 | } | 601 | } |
304 | } break; | 602 | } break; |
305 | case NODE_MUL: { | 603 | case NODE_MUL: { |
306 | if (str_eq(node->type, cstr("int"))) { | 604 | if (strset_lookup(&compiler->integer_types, node->type)) { |
307 | op = OP_MUL; | 605 | op = OP_MUL; |
308 | opi = OP_MULI; | 606 | opi = OP_MULI; |
309 | } else if (str_eq(node->type, cstr("f64"))) { | 607 | } else if (strset_lookup(&compiler->float_types, node->type)) { |
310 | op = OP_MULF; | 608 | op = OP_MULF; |
311 | opi = OP_MULFI; | 609 | opi = OP_MULFI; |
312 | } | 610 | } |
313 | } break; | 611 | } break; |
314 | case NODE_DIV: { | 612 | case NODE_DIV: { |
315 | if (str_eq(node->type, cstr("int"))) { | 613 | if (strset_lookup(&compiler->integer_types, node->type)) { |
316 | op = OP_DIV; | 614 | op = OP_DIV; |
317 | opi = OP_DIVI; | 615 | opi = OP_DIVI; |
318 | } else if (str_eq(node->type, cstr("f64"))) { | 616 | } else if (strset_lookup(&compiler->float_types, node->type)) { |
319 | op = OP_DIVF; | 617 | op = OP_DIVF; |
320 | opi = OP_DIVFI; | 618 | opi = OP_DIVFI; |
321 | } | 619 | } |
322 | } break; | 620 | } break; |
323 | case NODE_MOD: { | 621 | case NODE_MOD: { |
324 | if (str_eq(node->type, cstr("int"))) { | 622 | if (strset_lookup(&compiler->integer_types, node->type)) { |
325 | op = OP_MOD; | 623 | op = OP_MOD; |
326 | opi = OP_MODI; | 624 | opi = OP_MODI; |
327 | } else if (str_eq(node->type, cstr("f64"))) { | 625 | } else if (strset_lookup(&compiler->float_types, node->type)) { |
328 | op = OP_MODF; | 626 | op = OP_MODF; |
329 | opi = OP_MODFI; | 627 | opi = OP_MODFI; |
330 | } | 628 | } |
@@ -354,19 +652,15 @@ compile_binary(Chunk *chunk, Node *node) { | |||
354 | op = OP_GE; | 652 | op = OP_GE; |
355 | opi = OP_GEI; | 653 | opi = OP_GEI; |
356 | } break; | 654 | } break; |
357 | case NODE_AND: { | ||
358 | op = OP_AND; | ||
359 | opi = OP_ANDI; | ||
360 | } break; | ||
361 | case NODE_OR: { | ||
362 | op = OP_OR; | ||
363 | opi = OP_ORI; | ||
364 | } break; | ||
365 | // Bitwise. | 655 | // Bitwise. |
366 | case NODE_BITOR: { | 656 | case NODE_BITOR: { |
367 | op = OP_BITOR; | 657 | op = OP_BITOR; |
368 | opi = OP_BITORI; | 658 | opi = OP_BITORI; |
369 | } break; | 659 | } break; |
660 | case NODE_BITXOR: { | ||
661 | op = OP_BITXOR; | ||
662 | opi = OP_BITXORI; | ||
663 | } break; | ||
370 | case NODE_BITAND: { | 664 | case NODE_BITAND: { |
371 | op = OP_BITAND; | 665 | op = OP_BITAND; |
372 | opi = OP_BITANDI; | 666 | opi = OP_BITANDI; |
@@ -381,19 +675,20 @@ compile_binary(Chunk *chunk, Node *node) { | |||
381 | } break; | 675 | } break; |
382 | default: break; | 676 | default: break; |
383 | } | 677 | } |
384 | CompResult comp_a = compile_expr(chunk, node->left); | 678 | CompResult comp_a = compile_expr(compiler, chunk, node->binary.left); |
385 | CompResult comp_b = compile_expr(chunk, node->right); | 679 | CompResult comp_b = compile_expr(compiler, chunk, node->binary.right); |
386 | sz reg_a; | 680 | sz reg_a; |
387 | sz reg_b; | 681 | sz reg_b; |
388 | switch (comp_a.type) { | 682 | switch (comp_a.type) { |
389 | case COMP_CONST: { | 683 | case COMP_CONST: { |
390 | reg_a = chunk->reg_idx++; | 684 | reg_a = chunk->reg_idx++; |
391 | EMIT_OP(ldop, reg_a, comp_a.idx, 0, node, chunk); | 685 | emit_op(ldop, reg_a, comp_a.idx, 0, node, chunk); |
392 | } break; | 686 | } break; |
393 | case COMP_REG: { | 687 | case COMP_REG: { |
394 | reg_a = comp_a.idx; | 688 | reg_a = comp_a.idx; |
395 | } break; | 689 | } break; |
396 | default: { | 690 | default: { |
691 | emit_compile_err(compiler, chunk, node); | ||
397 | return (CompResult){.type = COMP_ERR}; | 692 | return (CompResult){.type = COMP_ERR}; |
398 | } break; | 693 | } break; |
399 | } | 694 | } |
@@ -406,16 +701,99 @@ compile_binary(Chunk *chunk, Node *node) { | |||
406 | reg_b = comp_b.idx; | 701 | reg_b = comp_b.idx; |
407 | } break; | 702 | } break; |
408 | default: { | 703 | default: { |
704 | emit_compile_err(compiler, chunk, node); | ||
409 | return (CompResult){.type = COMP_ERR}; | 705 | return (CompResult){.type = COMP_ERR}; |
410 | } break; | 706 | } break; |
411 | } | 707 | } |
412 | sz reg_dst = chunk->reg_idx++; // Better for optimization | 708 | sz reg_dst = chunk->reg_idx++; // Better for optimization |
413 | EMIT_OP(op, reg_dst, reg_a, reg_b, node, chunk); | 709 | emit_op(op, reg_dst, reg_a, reg_b, node, chunk); |
414 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 710 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
415 | } | 711 | } |
416 | 712 | ||
417 | CompResult | 713 | CompResult |
418 | compile_unary(Chunk *chunk, Node *node) { | 714 | compile_binary_logic(Compiler *compiler, Chunk *chunk, Node *node) { |
715 | // Logical functions have to shortcircuit once the answer is known. | ||
716 | OpCode op = OP_HALT; | ||
717 | OpCode opi = OP_HALT; | ||
718 | OpCode ldop = OP_LDCONST; | ||
719 | OpCode jmpop = OP_HALT; | ||
720 | OpCode jmpopi = OP_HALT; | ||
721 | bool default_value = false; | ||
722 | switch (node->kind) { | ||
723 | case NODE_AND: { | ||
724 | op = OP_AND; | ||
725 | opi = OP_ANDI; | ||
726 | jmpop = OP_JMPF; | ||
727 | jmpopi = OP_JMPFI; | ||
728 | default_value = false; | ||
729 | } break; | ||
730 | case NODE_OR: { | ||
731 | op = OP_OR; | ||
732 | opi = OP_ORI; | ||
733 | jmpop = OP_JMPT; | ||
734 | jmpopi = OP_JMPTI; | ||
735 | default_value = true; | ||
736 | } break; | ||
737 | default: break; | ||
738 | } | ||
739 | |||
740 | sz lab0 = chunk->labels_idx++; | ||
741 | sz lab1 = chunk->labels_idx++; | ||
742 | |||
743 | CompResult comp_a = compile_expr(compiler, chunk, node->binary.left); | ||
744 | sz reg_a; | ||
745 | switch (comp_a.type) { | ||
746 | case COMP_CONST: { | ||
747 | emit_op(jmpopi, lab0, comp_a.idx, 0, node->binary.left, chunk); | ||
748 | reg_a = chunk->reg_idx++; | ||
749 | emit_op(ldop, reg_a, comp_a.idx, 0, node, chunk); | ||
750 | } break; | ||
751 | case COMP_REG: { | ||
752 | emit_op(jmpop, lab0, comp_a.idx, 0, node->binary.left, chunk); | ||
753 | reg_a = comp_a.idx; | ||
754 | } break; | ||
755 | default: { | ||
756 | emit_compile_err(compiler, chunk, node); | ||
757 | return (CompResult){.type = COMP_ERR}; | ||
758 | } break; | ||
759 | } | ||
760 | |||
761 | CompResult comp_b = compile_expr(compiler, chunk, node->binary.right); | ||
762 | sz reg_b; | ||
763 | switch (comp_b.type) { | ||
764 | case COMP_CONST: { | ||
765 | reg_b = comp_b.idx; | ||
766 | op = opi; | ||
767 | } break; | ||
768 | case COMP_REG: { | ||
769 | reg_b = comp_b.idx; | ||
770 | } break; | ||
771 | default: { | ||
772 | emit_compile_err(compiler, chunk, node); | ||
773 | return (CompResult){.type = COMP_ERR}; | ||
774 | } break; | ||
775 | } | ||
776 | sz reg_dst = chunk->reg_idx++; | ||
777 | emit_op(op, reg_dst, reg_a, reg_b, node, chunk); | ||
778 | |||
779 | // Jump to the end of this comparison. | ||
780 | emit_op(OP_JMP, lab1, 0, 0, node, chunk); | ||
781 | sz pos0 = array_size(chunk->code); | ||
782 | |||
783 | // Load default value. | ||
784 | sz defaul_const = add_constant(chunk, default_value); | ||
785 | emit_op(ldop, reg_dst, defaul_const, 0, node, chunk); | ||
786 | sz pos1 = array_size(chunk->code); | ||
787 | |||
788 | // Register labels. | ||
789 | intintmap_insert(&chunk->labels, lab0, pos0, chunk->storage); | ||
790 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
791 | |||
792 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
793 | } | ||
794 | |||
795 | CompResult | ||
796 | compile_unary(Compiler *compiler, Chunk *chunk, Node *node) { | ||
419 | OpCode op = OP_HALT; | 797 | OpCode op = OP_HALT; |
420 | OpCode opi = OP_HALT; | 798 | OpCode opi = OP_HALT; |
421 | switch (node->kind) { | 799 | switch (node->kind) { |
@@ -429,7 +807,7 @@ compile_unary(Chunk *chunk, Node *node) { | |||
429 | } break; | 807 | } break; |
430 | default: break; | 808 | default: break; |
431 | } | 809 | } |
432 | CompResult comp_a = compile_expr(chunk, node->left); | 810 | CompResult comp_a = compile_expr(compiler, chunk, node->binary.left); |
433 | sz reg_a; | 811 | sz reg_a; |
434 | switch (comp_a.type) { | 812 | switch (comp_a.type) { |
435 | case COMP_CONST: { | 813 | case COMP_CONST: { |
@@ -440,30 +818,18 @@ compile_unary(Chunk *chunk, Node *node) { | |||
440 | reg_a = comp_a.idx; | 818 | reg_a = comp_a.idx; |
441 | } break; | 819 | } break; |
442 | default: { | 820 | default: { |
821 | emit_compile_err(compiler, chunk, node); | ||
443 | return (CompResult){.type = COMP_ERR}; | 822 | return (CompResult){.type = COMP_ERR}; |
444 | } break; | 823 | } break; |
445 | } | 824 | } |
446 | sz reg_dst = chunk->reg_idx++; | 825 | sz reg_dst = chunk->reg_idx++; |
447 | EMIT_OP(op, reg_dst, reg_a, 0, node, chunk); | 826 | emit_op(op, reg_dst, reg_a, 0, node, chunk); |
448 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 827 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
449 | } | 828 | } |
450 | 829 | ||
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 | 830 | CompResult |
465 | compile_if(Chunk *chunk, Node *node) { | 831 | compile_if(Compiler *compiler, Chunk *chunk, Node *node) { |
466 | CompResult cond = compile_expr(chunk, node->cond_if); | 832 | CompResult cond = compile_expr(compiler, chunk, node->ifelse.cond); |
467 | OpCode jmpop; | 833 | OpCode jmpop; |
468 | switch (cond.type) { | 834 | switch (cond.type) { |
469 | case COMP_CONST: { | 835 | case COMP_CONST: { |
@@ -473,101 +839,1076 @@ compile_if(Chunk *chunk, Node *node) { | |||
473 | jmpop = OP_JMPF; | 839 | jmpop = OP_JMPF; |
474 | } break; | 840 | } break; |
475 | default: { | 841 | default: { |
842 | emit_compile_err(compiler, chunk, node); | ||
476 | return (CompResult){.type = COMP_ERR}; | 843 | return (CompResult){.type = COMP_ERR}; |
477 | } break; | 844 | } break; |
478 | } | 845 | } |
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 | 846 | ||
486 | // Jump to the `false` branch. | 847 | if (!str_eq(node->type, cstr("nil")) && |
487 | EMIT_OP(jmpop, cond.idx, 0xff, 0, node->cond_if, chunk); | 848 | !str_has_prefix(node->type, cstr("ret:")) && |
849 | !str_has_prefix(node->type, cstr("flow:"))) { | ||
850 | sz reg_dst = chunk->reg_idx++; | ||
488 | 851 | ||
489 | // Condition is true. | 852 | // Jump to the `false` branch. |
490 | CompResult then_expr = compile_expr(chunk, node->cond_expr); | 853 | sz lab0 = chunk->labels_idx++; |
491 | if (has_value) { | 854 | emit_op(jmpop, lab0, cond.idx, 0, node->ifelse.cond, chunk); |
855 | |||
856 | // Condition is true. | ||
857 | CompResult then_expr = | ||
858 | compile_expr(compiler, chunk, node->ifelse.expr_true); | ||
492 | switch (then_expr.type) { | 859 | switch (then_expr.type) { |
493 | case COMP_CONST: { | 860 | case COMP_CONST: { |
494 | EMIT_OP(OP_LD64K, reg_dst, then_expr.idx, 0, node->cond_if, | 861 | emit_op(OP_LDCONST, reg_dst, then_expr.idx, 0, |
495 | chunk); | 862 | node->ifelse.cond, chunk); |
496 | } break; | 863 | } break; |
497 | case COMP_REG: { | 864 | case COMP_REG: { |
498 | EMIT_OP(OP_MOV64, reg_dst, then_expr.idx, 0, node->cond_if, | 865 | emit_op(OP_MOV64, reg_dst, then_expr.idx, 0, node->ifelse.cond, |
499 | chunk); | 866 | chunk); |
500 | } break; | 867 | } break; |
868 | case COMP_RET: break; | ||
501 | default: { | 869 | default: { |
870 | emit_compile_err(compiler, chunk, node); | ||
502 | return (CompResult){.type = COMP_ERR}; | 871 | return (CompResult){.type = COMP_ERR}; |
503 | } break; | 872 | } break; |
504 | } | 873 | } |
505 | } | ||
506 | 874 | ||
507 | if (node->cond_else) { | ||
508 | // Jump to the end of the expression. | 875 | // Jump to the end of the expression. |
509 | sz jump_b = array_size(chunk->code); | 876 | sz pos0 = array_size(chunk->code); |
510 | EMIT_OP(OP_JMPI, 0xff, 0, 0, node->cond_else, chunk); | 877 | sz lab1 = chunk->labels_idx++; |
878 | emit_op(OP_JMP, lab1, 0, 0, node->ifelse.expr_else, chunk); | ||
511 | 879 | ||
512 | // Else expression. | 880 | // Else expression. |
513 | CompResult else_expr = compile_expr(chunk, node->cond_else); | 881 | CompResult else_expr = |
514 | if (has_value) { | 882 | compile_expr(compiler, chunk, node->ifelse.expr_else); |
515 | switch (else_expr.type) { | 883 | switch (else_expr.type) { |
884 | case COMP_CONST: { | ||
885 | emit_op(OP_LDCONST, reg_dst, else_expr.idx, 0, | ||
886 | node->ifelse.expr_else, chunk); | ||
887 | } break; | ||
888 | case COMP_REG: { | ||
889 | emit_op(OP_MOV64, reg_dst, else_expr.idx, 0, | ||
890 | node->ifelse.expr_else, chunk); | ||
891 | } break; | ||
892 | case COMP_RET: break; | ||
893 | default: { | ||
894 | emit_compile_err(compiler, chunk, node); | ||
895 | return (CompResult){.type = COMP_ERR}; | ||
896 | } break; | ||
897 | } | ||
898 | sz pos1 = array_size(chunk->code); | ||
899 | |||
900 | // Update labels. | ||
901 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage); | ||
902 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
903 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
904 | } | ||
905 | |||
906 | // Jump to the `false` branch. | ||
907 | sz lab0 = chunk->labels_idx++; | ||
908 | emit_op(jmpop, lab0, cond.idx, 0, node->ifelse.cond, chunk); | ||
909 | |||
910 | // Condition is true. | ||
911 | compile_expr(compiler, chunk, node->ifelse.expr_true); | ||
912 | |||
913 | // Jump to the end of the expression. | ||
914 | sz pos0 = array_size(chunk->code); | ||
915 | sz lab1 = chunk->labels_idx++; | ||
916 | emit_op(OP_JMP, lab1, 0, 0, node->ifelse.expr_else, chunk); | ||
917 | |||
918 | // Else expression. | ||
919 | if (node->ifelse.expr_else) { | ||
920 | compile_expr(compiler, chunk, node->ifelse.expr_else); | ||
921 | } | ||
922 | sz pos1 = array_size(chunk->code); | ||
923 | |||
924 | // Update labels. | ||
925 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage); | ||
926 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
927 | |||
928 | return (CompResult){.type = COMP_NIL}; | ||
929 | } | ||
930 | |||
931 | CompResult | ||
932 | compile_cond(Compiler *compiler, Chunk *chunk, Node *node) { | ||
933 | if (str_eq(node->type, cstr("nil"))) { | ||
934 | sz lab1 = chunk->labels_idx++; | ||
935 | for (sz i = 0; i < array_size(node->match.cases); i++) { | ||
936 | // condition = expression | ||
937 | Node *expr = node->match.cases[i]; | ||
938 | if (expr->case_entry.cond) { | ||
939 | CompResult cond = | ||
940 | compile_expr(compiler, chunk, expr->case_entry.cond); | ||
941 | OpCode jmpop; | ||
942 | switch (cond.type) { | ||
943 | case COMP_CONST: { | ||
944 | jmpop = OP_JMPFI; | ||
945 | } break; | ||
946 | case COMP_REG: { | ||
947 | jmpop = OP_JMPF; | ||
948 | } break; | ||
949 | default: { | ||
950 | emit_compile_err(compiler, chunk, node); | ||
951 | return (CompResult){.type = COMP_ERR}; | ||
952 | } break; | ||
953 | } | ||
954 | // Jump to the `next` branch. | ||
955 | sz lab0 = chunk->labels_idx++; | ||
956 | emit_op(jmpop, lab0, cond.idx, 0, expr->case_entry.expr, chunk); | ||
957 | |||
958 | // Condition is true. | ||
959 | compile_expr(compiler, chunk, expr->case_entry.expr); | ||
960 | if (i != array_size(node->match.cases) - 1) { | ||
961 | // Jump to the end of the expression. | ||
962 | sz pos0 = array_size(chunk->code); | ||
963 | emit_op(OP_JMP, lab1, 0, 0, node->ifelse.expr_else, chunk); | ||
964 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, | ||
965 | chunk->storage); | ||
966 | } | ||
967 | } else { | ||
968 | compile_expr(compiler, chunk, expr->case_entry.expr); | ||
969 | break; | ||
970 | } | ||
971 | } | ||
972 | sz pos1 = array_size(chunk->code); | ||
973 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
974 | return (CompResult){.type = COMP_NIL}; | ||
975 | } | ||
976 | |||
977 | sz reg_dst = chunk->reg_idx++; | ||
978 | sz lab1 = chunk->labels_idx++; | ||
979 | for (sz i = 0; i < array_size(node->match.cases); i++) { | ||
980 | // condition = expression | ||
981 | Node *expr = node->match.cases[i]; | ||
982 | if (expr->case_entry.cond) { | ||
983 | CompResult cond = | ||
984 | compile_expr(compiler, chunk, expr->case_entry.cond); | ||
985 | OpCode jmpop; | ||
986 | switch (cond.type) { | ||
516 | case COMP_CONST: { | 987 | case COMP_CONST: { |
517 | EMIT_OP(OP_LD64K, reg_dst, else_expr.idx, 0, | 988 | jmpop = OP_JMPFI; |
518 | node->cond_else, chunk); | ||
519 | } break; | 989 | } break; |
520 | case COMP_REG: { | 990 | case COMP_REG: { |
521 | EMIT_OP(OP_MOV64, reg_dst, else_expr.idx, 0, | 991 | jmpop = OP_JMPF; |
522 | node->cond_else, chunk); | ||
523 | } break; | 992 | } break; |
524 | default: { | 993 | default: { |
994 | emit_compile_err(compiler, chunk, node); | ||
525 | return (CompResult){.type = COMP_ERR}; | 995 | return (CompResult){.type = COMP_ERR}; |
526 | } break; | 996 | } break; |
527 | } | 997 | } |
528 | } | 998 | // Jump to the `next` branch. |
529 | sz end_expr = array_size(chunk->code); | 999 | sz lab0 = chunk->labels_idx++; |
1000 | emit_op(jmpop, lab0, cond.idx, 0, expr->case_entry.expr, chunk); | ||
530 | 1001 | ||
531 | // Backpatch jumps. | 1002 | // Condition is true. |
532 | sz const_a = add_constant(chunk, jump_b + 1 - jump_a); | 1003 | CompResult then_expr = |
533 | sz const_b = add_constant(chunk, end_expr - jump_b); | 1004 | compile_expr(compiler, chunk, expr->case_entry.expr); |
534 | chunk->code[jump_a].a = const_a; | 1005 | switch (then_expr.type) { |
535 | chunk->code[jump_b].dst = const_b; | 1006 | case COMP_CONST: { |
536 | } else { | 1007 | emit_op(OP_LDCONST, reg_dst, then_expr.idx, 0, |
537 | sz end_expr = array_size(chunk->code); | 1008 | expr->case_entry.expr, chunk); |
538 | if (has_value) { | 1009 | } break; |
539 | sz const_a = add_constant(chunk, end_expr + 1 - jump_a); | 1010 | case COMP_REG: { |
540 | chunk->code[jump_a].a = const_a; | 1011 | emit_op(OP_MOV64, reg_dst, then_expr.idx, 0, |
541 | sz zero = add_constant(chunk, 0); | 1012 | expr->case_entry.expr, chunk); |
542 | sz end = add_constant(chunk, 2); | 1013 | } break; |
543 | EMIT_OP(OP_JMPI, end, 0, 0, node, chunk); | 1014 | case COMP_RET: break; |
544 | EMIT_OP(OP_LD64K, reg_dst, zero, 0, node, chunk); | 1015 | default: { |
1016 | emit_compile_err(compiler, chunk, node); | ||
1017 | return (CompResult){.type = COMP_ERR}; | ||
1018 | } break; | ||
1019 | } | ||
1020 | if (i != array_size(node->match.cases) - 1) { | ||
1021 | // Jump to the end of the expression. | ||
1022 | sz pos0 = array_size(chunk->code); | ||
1023 | emit_op(OP_JMP, lab1, 0, 0, node->ifelse.expr_else, chunk); | ||
1024 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, | ||
1025 | chunk->storage); | ||
1026 | } | ||
545 | } else { | 1027 | } else { |
546 | sz const_a = add_constant(chunk, end_expr - jump_a); | 1028 | CompResult then_expr = |
547 | chunk->code[jump_a].a = const_a; | 1029 | compile_expr(compiler, chunk, expr->case_entry.expr); |
1030 | switch (then_expr.type) { | ||
1031 | case COMP_CONST: { | ||
1032 | emit_op(OP_LDCONST, reg_dst, then_expr.idx, 0, | ||
1033 | expr->case_entry.expr, chunk); | ||
1034 | } break; | ||
1035 | case COMP_REG: { | ||
1036 | emit_op(OP_MOV64, reg_dst, then_expr.idx, 0, | ||
1037 | expr->case_entry.expr, chunk); | ||
1038 | } break; | ||
1039 | case COMP_RET: break; | ||
1040 | default: { | ||
1041 | emit_compile_err(compiler, chunk, node); | ||
1042 | return (CompResult){.type = COMP_ERR}; | ||
1043 | } break; | ||
1044 | } | ||
1045 | break; | ||
548 | } | 1046 | } |
549 | } | 1047 | } |
550 | // TODO: does it has an else or not? Moreover, should we enforce on the | 1048 | sz pos1 = array_size(chunk->code); |
551 | // semantic level that if the `if` expression returns a value we must add an | 1049 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); |
552 | // else? | 1050 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
1051 | } | ||
1052 | |||
1053 | CompResult | ||
1054 | compile_break(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1055 | emit_op(OP_JMP, compiler->lab_post, 0, 0, node, chunk); | ||
1056 | return (CompResult){.type = COMP_NIL}; | ||
1057 | } | ||
1058 | |||
1059 | CompResult | ||
1060 | compile_continue(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1061 | emit_op(OP_JMP, compiler->lab_pre, 0, 0, node, chunk); | ||
1062 | return (CompResult){.type = COMP_NIL}; | ||
1063 | } | ||
1064 | |||
1065 | CompResult | ||
1066 | compile_while(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1067 | sz lab0 = chunk->labels_idx++; | ||
1068 | sz lab1 = chunk->labels_idx++; | ||
1069 | sz pos1 = array_size(chunk->code); | ||
1070 | CompResult cond = compile_expr(compiler, chunk, node->loop.cond); | ||
1071 | OpCode jmpop; | ||
1072 | switch (cond.type) { | ||
1073 | case COMP_CONST: { | ||
1074 | jmpop = OP_JMPFI; | ||
1075 | } break; | ||
1076 | case COMP_REG: { | ||
1077 | jmpop = OP_JMPF; | ||
1078 | } break; | ||
1079 | default: { | ||
1080 | emit_compile_err(compiler, chunk, node); | ||
1081 | return (CompResult){.type = COMP_ERR}; | ||
1082 | } break; | ||
1083 | } | ||
1084 | |||
1085 | // Jump to the `end of the loop` branch. | ||
1086 | emit_op(jmpop, lab0, cond.idx, 0, node->loop.cond, chunk); | ||
1087 | |||
1088 | // Condition is true. | ||
1089 | compiler->lab_pre = lab1; | ||
1090 | compiler->lab_post = lab0; | ||
1091 | compile_expr(compiler, chunk, node->loop.expr); | ||
1092 | sz pos0 = array_size(chunk->code); | ||
1093 | emit_op(OP_JMP, lab1, 0, 0, node, chunk); | ||
1094 | |||
1095 | // Update labels. | ||
1096 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage); | ||
1097 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
553 | 1098 | ||
554 | // Return. | 1099 | // Return. |
555 | if (has_value) { | 1100 | return (CompResult){.type = COMP_NIL}; |
1101 | } | ||
1102 | |||
1103 | CompResult | ||
1104 | compile_tail_call(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1105 | // Update the local parameters. | ||
1106 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
1107 | Node *expr = node->elements[i]; | ||
1108 | CompResult result = compile_expr(compiler, chunk, expr); | ||
1109 | switch (result.type) { | ||
1110 | case COMP_CONST: { | ||
1111 | emit_op(OP_STLVARI, i, result.idx, 0, node, chunk); | ||
1112 | } break; | ||
1113 | case COMP_REG: { | ||
1114 | if (str_eq(expr->type, cstr("Str"))) { | ||
1115 | sz var_addr = chunk->reg_idx++; | ||
1116 | sz str_addr = result.idx; | ||
1117 | emit_op(OP_LDLADDR, var_addr, i, 0, node, chunk); | ||
1118 | emit_fat_copy(chunk, node, var_addr, str_addr); | ||
1119 | } else { | ||
1120 | emit_op(OP_STLVAR, i, result.idx, 0, node, chunk); | ||
1121 | } | ||
1122 | } break; | ||
1123 | case COMP_STRING: { | ||
1124 | sz var_addr = chunk->reg_idx++; | ||
1125 | sz str_addr = chunk->reg_idx++; | ||
1126 | emit_op(OP_LDLADDR, var_addr, i, 0, node, chunk); | ||
1127 | emit_op(OP_LDSTR, str_addr, result.idx, 0, node, chunk); | ||
1128 | emit_fat_copy(chunk, node, var_addr, str_addr); | ||
1129 | } break; | ||
1130 | default: { | ||
1131 | emit_compile_err(compiler, chunk, node); | ||
1132 | return (CompResult){.type = COMP_ERR}; | ||
1133 | } break; | ||
1134 | } | ||
1135 | } | ||
1136 | |||
1137 | emit_op(OP_RECUR, 0, 0, 0, node, chunk); | ||
1138 | return (CompResult){.type = COMP_NIL}; | ||
1139 | } | ||
1140 | |||
1141 | CompResult | ||
1142 | compile_funcall(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1143 | Str name = node->value.str; | ||
1144 | |||
1145 | // Builtins. | ||
1146 | if (str_eq(name, cstr("print")) || str_eq(name, cstr("println"))) { | ||
1147 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
1148 | Node *expr = node->elements[i]; | ||
1149 | Str type_name = expr->type; | ||
1150 | if (str_has_prefix(type_name, cstr("@")) || | ||
1151 | str_has_prefix(type_name, cstr("["))) { | ||
1152 | type_name = cstr("Ptr"); | ||
1153 | } | ||
1154 | StrTypeMap *t = strtype_lookup(&compiler->type_map, type_name); | ||
1155 | Str type = t->val.unique_name; | ||
1156 | sz size = t->val.size; | ||
1157 | CompResult result = compile_expr(compiler, chunk, expr); | ||
1158 | if (strset_lookup(&compiler->signed_ints, type)) { | ||
1159 | switch (result.type) { | ||
1160 | case COMP_CONST: { | ||
1161 | emit_sized_op(size, OP_PRINTS64I, OP_PRINTS32I, | ||
1162 | OP_PRINTS16I, OP_PRINTS8I, result.idx, 0, | ||
1163 | 0, expr, chunk); | ||
1164 | } break; | ||
1165 | case COMP_REG: { | ||
1166 | emit_sized_op(size, OP_PRINTS64, OP_PRINTS32, | ||
1167 | OP_PRINTS16, OP_PRINTS8, result.idx, 0, 0, | ||
1168 | expr, chunk); | ||
1169 | } break; | ||
1170 | default: { | ||
1171 | emit_compile_err(compiler, chunk, node); | ||
1172 | return (CompResult){.type = COMP_ERR}; | ||
1173 | } break; | ||
1174 | } | ||
1175 | } else if (strset_lookup(&compiler->unsigned_ints, type)) { | ||
1176 | switch (result.type) { | ||
1177 | case COMP_CONST: { | ||
1178 | emit_sized_op(size, OP_PRINTU64I, OP_PRINTU32I, | ||
1179 | OP_PRINTU16I, OP_PRINTU8I, result.idx, 0, | ||
1180 | 0, expr, chunk); | ||
1181 | } break; | ||
1182 | case COMP_REG: { | ||
1183 | emit_sized_op(size, OP_PRINTU64, OP_PRINTU32, | ||
1184 | OP_PRINTU16, OP_PRINTU8, result.idx, 0, 0, | ||
1185 | expr, chunk); | ||
1186 | } break; | ||
1187 | default: { | ||
1188 | emit_compile_err(compiler, chunk, node); | ||
1189 | return (CompResult){.type = COMP_ERR}; | ||
1190 | } break; | ||
1191 | } | ||
1192 | } else if (strset_lookup(&compiler->float_types, type)) { | ||
1193 | switch (result.type) { | ||
1194 | case COMP_CONST: { | ||
1195 | if (size == 8) { | ||
1196 | emit_op(OP_PRINTF64I, result.idx, 0, 0, expr, | ||
1197 | chunk); | ||
1198 | } else { | ||
1199 | emit_op(OP_PRINTF32I, result.idx, 0, 0, expr, | ||
1200 | chunk); | ||
1201 | } | ||
1202 | } break; | ||
1203 | case COMP_REG: { | ||
1204 | if (size == 8) { | ||
1205 | emit_op(OP_PRINTF64, result.idx, 0, 0, expr, chunk); | ||
1206 | } else { | ||
1207 | emit_op(OP_PRINTF32, result.idx, 0, 0, expr, chunk); | ||
1208 | } | ||
1209 | } break; | ||
1210 | default: { | ||
1211 | emit_compile_err(compiler, chunk, node); | ||
1212 | return (CompResult){.type = COMP_ERR}; | ||
1213 | } break; | ||
1214 | } | ||
1215 | } else if (str_eq(type, cstr("Str"))) { | ||
1216 | switch (result.type) { | ||
1217 | case COMP_STRING: { | ||
1218 | emit_op(OP_PRINTSTRI, result.idx, 0, 0, expr, chunk); | ||
1219 | } break; | ||
1220 | case COMP_REG: { | ||
1221 | emit_op(OP_PRINTSTR, result.idx, 0, 0, expr, chunk); | ||
1222 | } break; | ||
1223 | default: { | ||
1224 | emit_compile_err(compiler, chunk, node); | ||
1225 | return (CompResult){.type = COMP_ERR}; | ||
1226 | } break; | ||
1227 | } | ||
1228 | } else if (str_eq(type, cstr("Bool"))) { | ||
1229 | switch (result.type) { | ||
1230 | case COMP_CONST: { | ||
1231 | emit_op(OP_PRINTBOOLI, result.idx, 0, 0, expr, chunk); | ||
1232 | } break; | ||
1233 | case COMP_REG: { | ||
1234 | emit_op(OP_PRINTBOOL, result.idx, 0, 0, expr, chunk); | ||
1235 | } break; | ||
1236 | default: { | ||
1237 | emit_compile_err(compiler, chunk, node); | ||
1238 | return (CompResult){.type = COMP_ERR}; | ||
1239 | } break; | ||
1240 | } | ||
1241 | } | ||
1242 | } | ||
1243 | if (str_eq(name, cstr("println"))) { | ||
1244 | sz idx = add_string(chunk, cstr("\n")); | ||
1245 | emit_op(OP_PRINTSTRI, idx, 0, 0, node, chunk); | ||
1246 | } | ||
1247 | return (CompResult){.type = COMP_NIL}; | ||
1248 | } else if (str_eq(name, cstr("sizeof"))) { | ||
1249 | // Find size of the given type. | ||
1250 | Node *expr = node->elements[0]; | ||
1251 | Str type_name = expr->value.str; | ||
1252 | // Try to find the type on the table. | ||
1253 | StrTypeMap *t = strtype_lookup(&compiler->type_map, type_name); | ||
1254 | sz size = 0; | ||
1255 | if (t) { | ||
1256 | size = t->val.size; | ||
1257 | } else { | ||
1258 | // If that's not possible, try resolving the symbol. | ||
1259 | Str name = expr->unique_name; | ||
1260 | StrVarMap *map = NULL; | ||
1261 | Chunk *next = chunk; | ||
1262 | while (next) { | ||
1263 | map = varmap_lookup(&next->varmap, name); | ||
1264 | if (map) { | ||
1265 | break; | ||
1266 | } | ||
1267 | next = next->parent; | ||
1268 | } | ||
1269 | if (!map) { | ||
1270 | emit_compile_err(compiler, chunk, expr); | ||
1271 | return (CompResult){.type = COMP_ERR}; | ||
1272 | } | ||
1273 | Variable var = map->val; | ||
1274 | size = var.size; | ||
1275 | } | ||
1276 | |||
1277 | // FIXME: type size and tables is better done on semantic analyzer, | ||
1278 | // enough bloat here already. | ||
1279 | sz reg_dst = chunk->reg_idx++; | ||
1280 | sz const_idx = add_constant(chunk, size); | ||
1281 | emit_op(OP_LDCONST, reg_dst, const_idx, 0, node, chunk); | ||
556 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 1282 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
557 | } | 1283 | } |
1284 | |||
1285 | FunctionMap *map = | ||
1286 | funcmap_lookup(&compiler->main_chunk.funmap, node->unique_name); | ||
1287 | if (!map) { | ||
1288 | emit_compile_err(compiler, chunk, node); | ||
1289 | return (CompResult){.type = COMP_ERR}; | ||
1290 | } | ||
1291 | Function fun = map->val; | ||
1292 | |||
1293 | // Check for tail recursive opportunities. | ||
1294 | if (str_eq(fun.name, node->unique_name) && | ||
1295 | str_eq(chunk->name, node->unique_name)) { | ||
1296 | Node *parent = node->parent; | ||
1297 | Node *current = node; | ||
1298 | bool tail_recursive = true; | ||
1299 | while (parent != NULL) { | ||
1300 | switch (parent->kind) { | ||
1301 | case NODE_BLOCK: { | ||
1302 | sz idx = array_size(parent->statements) - 1; | ||
1303 | if (parent->statements[idx] != node) { | ||
1304 | tail_recursive = false; | ||
1305 | break; | ||
1306 | } | ||
1307 | } break; | ||
1308 | case NODE_WHILE: { | ||
1309 | if (current == parent->loop.cond) { | ||
1310 | tail_recursive = false; | ||
1311 | break; | ||
1312 | } | ||
1313 | } break; | ||
1314 | case NODE_IF: { | ||
1315 | if (current == parent->ifelse.cond) { | ||
1316 | tail_recursive = false; | ||
1317 | break; | ||
1318 | } | ||
1319 | } break; | ||
1320 | case NODE_FUN: { | ||
1321 | sz idx = array_size(parent->func.body->statements) - 1; | ||
1322 | if (parent->func.body->statements[idx] != current) { | ||
1323 | tail_recursive = false; | ||
1324 | break; | ||
1325 | } | ||
1326 | break; | ||
1327 | } break; | ||
1328 | case NODE_MATCH: { | ||
1329 | if (current == parent->match.expr) { | ||
1330 | tail_recursive = false; | ||
1331 | break; | ||
1332 | } | ||
1333 | } break; | ||
1334 | case NODE_COND: break; | ||
1335 | case NODE_CASE_COND: { | ||
1336 | if (current == parent->case_entry.cond) { | ||
1337 | tail_recursive = false; | ||
1338 | break; | ||
1339 | } | ||
1340 | } break; | ||
1341 | default: { | ||
1342 | tail_recursive = false; | ||
1343 | break; | ||
1344 | } break; | ||
1345 | } | ||
1346 | parent = parent->parent; | ||
1347 | current = current->parent; | ||
1348 | } | ||
1349 | if (tail_recursive) { | ||
1350 | return compile_tail_call(compiler, chunk, node); | ||
1351 | } | ||
1352 | } | ||
1353 | |||
1354 | // Reserve space for the return value if needed. | ||
1355 | if (fun.return_arity > 0) { | ||
1356 | // Put the return data into a register | ||
1357 | sz ret_size = add_constant(chunk, 8); | ||
1358 | emit_op(OP_RESERVE, ret_size, 0, 0, node, chunk); | ||
1359 | } | ||
1360 | |||
1361 | // Send parameters to the stack. | ||
1362 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
1363 | Node *expr = node->elements[i]; | ||
1364 | CompResult result = compile_expr(compiler, chunk, expr); | ||
1365 | Str type_name = expr->type; | ||
1366 | if (str_has_prefix(type_name, cstr("@")) || | ||
1367 | str_has_prefix(type_name, cstr("["))) { | ||
1368 | type_name = cstr("Ptr"); | ||
1369 | } | ||
1370 | StrTypeMap *t = strtype_lookup(&compiler->type_map, type_name); | ||
1371 | sz size = t->val.size; | ||
1372 | switch (result.type) { | ||
1373 | case COMP_CONST: { | ||
1374 | emit_op(OP_PUSHI, result.idx, 0, 0, expr, chunk); | ||
1375 | } break; | ||
1376 | case COMP_REG: { | ||
1377 | if (str_eq(expr->type, cstr("Str"))) { | ||
1378 | sz str_addr = result.idx; | ||
1379 | // Store the fat string pointer into the stack. | ||
1380 | sz reg_dst = chunk->reg_idx++; | ||
1381 | sz zero = add_constant(chunk, 0); | ||
1382 | sz one = add_constant(chunk, 1); | ||
1383 | emit_sized_op(size, OP_LD64I, OP_LD32I, OP_LD16I, OP_LD8I, | ||
1384 | reg_dst, str_addr, zero, node, chunk); | ||
1385 | emit_op(OP_PUSH, reg_dst, 0, 0, expr, chunk); | ||
1386 | emit_sized_op(size, OP_LD64I, OP_LD32I, OP_LD16I, OP_LD8I, | ||
1387 | reg_dst, str_addr, one, node, chunk); | ||
1388 | emit_op(OP_PUSH, reg_dst, 0, 0, expr, chunk); | ||
1389 | } else { | ||
1390 | emit_op(OP_PUSH, result.idx, 0, 0, expr, chunk); | ||
1391 | } | ||
1392 | } break; | ||
1393 | case COMP_STRING: { | ||
1394 | // Get the address for the string value variable. | ||
1395 | sz str_addr = chunk->reg_idx++; | ||
1396 | emit_op(OP_LDSTR, str_addr, result.idx, 0, node->var.val, | ||
1397 | chunk); | ||
1398 | |||
1399 | // Store the fat string pointer into the stack. | ||
1400 | sz reg_dst = chunk->reg_idx++; | ||
1401 | sz zero = add_constant(chunk, 0); | ||
1402 | sz one = add_constant(chunk, 1); | ||
1403 | emit_op(OP_LD64I, reg_dst, str_addr, zero, node, chunk); | ||
1404 | emit_op(OP_PUSH, reg_dst, 0, 0, expr, chunk); | ||
1405 | emit_op(OP_LD64I, reg_dst, str_addr, one, node, chunk); | ||
1406 | emit_op(OP_PUSH, reg_dst, 0, 0, expr, chunk); | ||
1407 | } break; | ||
1408 | default: { | ||
1409 | emit_compile_err(compiler, chunk, node); | ||
1410 | return (CompResult){.type = COMP_ERR}; | ||
1411 | } break; | ||
1412 | } | ||
1413 | } | ||
1414 | |||
1415 | emit_op(OP_CALL, fun.index, 0, 0, node, chunk); | ||
1416 | |||
1417 | // Only one return parameter for now. | ||
1418 | if (fun.return_arity > 0) { | ||
1419 | // Put the return data into a register | ||
1420 | sz reg_dst = chunk->reg_idx++; | ||
1421 | emit_op(OP_POP, reg_dst, 0, 0, node, chunk); | ||
1422 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
1423 | } | ||
1424 | |||
1425 | return (CompResult){.type = COMP_NIL}; | ||
1426 | } | ||
1427 | |||
1428 | CompResult | ||
1429 | compile_return(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1430 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
1431 | Node *expr = node->elements[i]; | ||
1432 | CompResult res = compile_expr(compiler, chunk, expr); | ||
1433 | |||
1434 | // TODO: Only one return field for now, but this is the idea. | ||
1435 | // Put return values into memory. | ||
1436 | switch (res.type) { | ||
1437 | case COMP_CONST: { | ||
1438 | emit_op(OP_PUTRETI, res.idx, 0, 0, node, chunk); | ||
1439 | } break; | ||
1440 | case COMP_REG: { | ||
1441 | emit_op(OP_PUTRET, res.idx, 0, 0, node, chunk); | ||
1442 | } break; | ||
1443 | case COMP_NIL: break; | ||
1444 | default: { | ||
1445 | emit_compile_err(compiler, chunk, node); | ||
1446 | return (CompResult){.type = COMP_ERR}; | ||
1447 | } break; | ||
1448 | } | ||
1449 | break; | ||
1450 | } | ||
1451 | |||
1452 | emit_op(OP_RET, 0, 0, 0, node, chunk); | ||
1453 | return (CompResult){.type = COMP_RET}; | ||
1454 | } | ||
1455 | |||
1456 | Chunk * | ||
1457 | chunk_alloc(Chunk *parent) { | ||
1458 | static sz chunk_idx = 1; | ||
1459 | Chunk *chunk = arena_calloc((sz)sizeof(Chunk), parent->storage); | ||
1460 | chunk->parent = parent; | ||
1461 | chunk->id = chunk_idx++; | ||
1462 | chunk->storage = parent->storage; | ||
1463 | chunk->file_name = parent->file_name; | ||
1464 | return chunk; | ||
1465 | } | ||
1466 | |||
1467 | void | ||
1468 | verify_chunk(Chunk *chunk) { | ||
1469 | if (chunk->const_idx >= 256) { | ||
1470 | eprintln("too many constants on chunk %s", chunk->id); | ||
1471 | exit(EXIT_FAILURE); | ||
1472 | } | ||
1473 | if (chunk->str_idx >= 256) { | ||
1474 | eprintln("too many strings on chunk %s", chunk->id); | ||
1475 | exit(EXIT_FAILURE); | ||
1476 | } | ||
1477 | if (chunk->reg_idx >= 256) { | ||
1478 | eprintln("too many registers used on chunk %s", chunk->id); | ||
1479 | exit(EXIT_FAILURE); | ||
1480 | } | ||
1481 | if (chunk->labels_idx >= 256) { | ||
1482 | eprintln("too many labels used on chunk %s", chunk->id); | ||
1483 | exit(EXIT_FAILURE); | ||
1484 | } | ||
1485 | if (chunk->fun_idx >= 256) { | ||
1486 | eprintln("too many functions on chunk %s", chunk->id); | ||
1487 | exit(EXIT_FAILURE); | ||
1488 | } | ||
1489 | } | ||
1490 | |||
1491 | void | ||
1492 | declare_function(Chunk *chunk, Node *node) { | ||
1493 | Str name = node->unique_name; | ||
1494 | FunctionMap *map = funcmap_lookup(&chunk->funmap, node->unique_name); | ||
1495 | if (map) { | ||
1496 | return; | ||
1497 | } | ||
1498 | Function fun = (Function){ | ||
1499 | .name = name, | ||
1500 | .index = chunk->fun_idx++, | ||
1501 | .param_arity = array_size(node->func.params), | ||
1502 | .return_arity = array_size(node->func.ret), | ||
1503 | }; | ||
1504 | funcmap_insert(&chunk->funmap, node->unique_name, fun, chunk->storage); | ||
1505 | } | ||
1506 | |||
1507 | CompResult | ||
1508 | compile_function(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1509 | // The current activation record procedure for the VM is as follows: | ||
1510 | // | ||
1511 | // [caller][callee ] | ||
1512 | // [ .... ][ RET VAL ][ PARAMS ][ LOCALS ][ REGISTERS ][ RET META ] | ||
1513 | // ^ | ||
1514 | // frame pointer | ||
1515 | // | ||
1516 | // The caller is responsible for allocating the return memory and the | ||
1517 | // parameter memory and filling the param data before OP_CALL. | ||
1518 | // | ||
1519 | chunk = &compiler->main_chunk; | ||
1520 | Chunk *func = chunk_alloc(chunk); | ||
1521 | func->name = node->unique_name; | ||
1522 | declare_function(chunk, node); | ||
1523 | array_push(chunk->functions, func, chunk->storage); | ||
1524 | |||
1525 | // Push arguments as locals. | ||
1526 | for (sz i = 0; i < array_size(node->func.params); i++) { | ||
1527 | Node *param = node->func.params[i]; | ||
1528 | Str name = param->unique_name; | ||
1529 | Str type = param->type; | ||
1530 | sz arr_size = 0; | ||
1531 | if (str_has_prefix(type, cstr("@"))) { | ||
1532 | // if (param->var.type->sym.arr_size->value.i > 0) { | ||
1533 | // arr_size = param->var.type->sym.arr_size->value.i; | ||
1534 | // } | ||
1535 | } | ||
1536 | add_variable(compiler, func, name, type, arr_size); | ||
1537 | } | ||
1538 | func->param_off = func->var_off; | ||
1539 | |||
1540 | // Compiling the body. | ||
1541 | CompResult res = compile_expr(compiler, func, node->func.body); | ||
1542 | |||
1543 | // Put return values into memory. | ||
1544 | switch (res.type) { | ||
1545 | case COMP_CONST: { | ||
1546 | emit_op(OP_PUTRETI, res.idx, 0, 0, node, func); | ||
1547 | } break; | ||
1548 | case COMP_REG: { | ||
1549 | emit_op(OP_PUTRET, res.idx, 0, 0, node, func); | ||
1550 | } break; | ||
1551 | default: break; | ||
1552 | } | ||
1553 | |||
1554 | emit_op(OP_RET, 0, 0, 0, node, func); | ||
1555 | verify_chunk(func); | ||
1556 | return (CompResult){.type = COMP_NIL}; | ||
1557 | } | ||
1558 | |||
1559 | CompResult | ||
1560 | compile_let(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1561 | sz op_ldaddr = OP_LDLADDR; | ||
1562 | if (chunk == &compiler->main_chunk) { | ||
1563 | op_ldaddr = OP_LDGADDR; | ||
1564 | } | ||
1565 | Str name = node->unique_name; | ||
1566 | Str type_name = node->var.name->type; | ||
1567 | sz arr_size = 0; | ||
1568 | if (node->var.type) { | ||
1569 | Node *next = node->var.type->t.next; | ||
1570 | if (next && next->kind == NODE_ARR) { | ||
1571 | // TODO: handle dynamic arrays and slices | ||
1572 | arr_size = next->array.size->value.i; | ||
1573 | } | ||
1574 | } | ||
1575 | |||
1576 | sz idx = add_variable(compiler, chunk, name, type_name, arr_size); | ||
1577 | StrVarMap *map = varmap_lookup(&chunk->varmap, name); | ||
1578 | Variable var = map->val; | ||
1579 | Type type = map->val.type; | ||
1580 | |||
1581 | // Value. | ||
1582 | if (node->var.val) { | ||
1583 | CompResult res = compile_expr(compiler, chunk, node->var.val); | ||
1584 | switch (res.type) { | ||
1585 | case COMP_CONST: { | ||
1586 | sz reg_addr = chunk->reg_idx++; | ||
1587 | sz reg_dst = chunk->reg_idx++; | ||
1588 | emit_op(op_ldaddr, reg_addr, idx, 0, node, chunk); | ||
1589 | emit_op(OP_LDCONST, reg_dst, res.idx, 0, node, chunk); | ||
1590 | emit_sized_op(type.size, OP_ST64K, OP_ST32K, OP_ST16K, OP_ST8K, | ||
1591 | reg_dst, reg_addr, 0, node, chunk); | ||
1592 | } break; | ||
1593 | case COMP_REG: { | ||
1594 | sz reg_addr = chunk->reg_idx++; | ||
1595 | sz reg_val = res.idx; | ||
1596 | emit_op(op_ldaddr, reg_addr, idx, 0, node, chunk); | ||
1597 | if (var.size > 8 || str_has_prefix(var.type_name, cstr("["))) { | ||
1598 | sz reg_dst = chunk->reg_idx++; | ||
1599 | for (sz i = 0; i < var.size / 8; i++) { | ||
1600 | sz offset = add_constant(chunk, i); | ||
1601 | emit_op(OP_LD64I, reg_dst, reg_val, offset, node, | ||
1602 | chunk); | ||
1603 | emit_op(OP_ST64I, reg_dst, reg_addr, offset, node, | ||
1604 | chunk); | ||
1605 | } | ||
1606 | } else { | ||
1607 | emit_sized_op(var.size, OP_ST64K, OP_ST32K, OP_ST16K, | ||
1608 | OP_ST8K, reg_val, reg_addr, 0, node, chunk); | ||
1609 | } | ||
1610 | } break; | ||
1611 | case COMP_STRING: { | ||
1612 | // Get the address for the string value variable. | ||
1613 | sz str_addr = chunk->reg_idx++; | ||
1614 | emit_op(OP_LDSTR, str_addr, res.idx, 0, node->var.val, chunk); | ||
1615 | |||
1616 | // Get the address for the local/global storage | ||
1617 | // variable. | ||
1618 | sz var_addr = chunk->reg_idx++; | ||
1619 | emit_op(op_ldaddr, var_addr, idx, 0, node, chunk); | ||
1620 | |||
1621 | // Copy the fat pointer. | ||
1622 | emit_fat_copy(chunk, node, var_addr, str_addr); | ||
1623 | } break; | ||
1624 | default: { | ||
1625 | emit_compile_err(compiler, chunk, node); | ||
1626 | return (CompResult){.type = COMP_ERR}; | ||
1627 | } break; | ||
1628 | } | ||
1629 | } | ||
1630 | |||
1631 | return (CompResult){.type = COMP_NIL}; | ||
1632 | } | ||
1633 | |||
1634 | CompResult | ||
1635 | compile_set(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1636 | Str name = node->unique_name; | ||
1637 | StrVarMap *map = NULL; | ||
1638 | Chunk *next = chunk; | ||
1639 | while (next) { | ||
1640 | map = varmap_lookup(&next->varmap, name); | ||
1641 | if (map) { | ||
1642 | break; | ||
1643 | } | ||
1644 | next = chunk->parent; | ||
1645 | } | ||
1646 | if (!map) { | ||
1647 | emit_compile_err(compiler, chunk, node); | ||
1648 | return (CompResult){.type = COMP_ERR}; | ||
1649 | } | ||
1650 | sz op_ldaddr = OP_LDLADDR; | ||
1651 | if (next == &compiler->main_chunk) { | ||
1652 | op_ldaddr = OP_LDGADDR; | ||
1653 | } | ||
1654 | Variable var = map->val; | ||
1655 | Type type = map->val.type; | ||
1656 | sz idx = map->val.idx; | ||
1657 | |||
1658 | CompResult res = compile_expr(compiler, chunk, node->var.val); | ||
1659 | if (false) { | ||
1660 | // // Value. | ||
1661 | // sz reg_val; | ||
1662 | // switch (res.type) { | ||
1663 | // case COMP_CONST: { | ||
1664 | // reg_val = chunk->reg_idx++; | ||
1665 | // emit_op(OP_LDCONST, reg_val, res.idx, 0, node, chunk); | ||
1666 | // } break; | ||
1667 | // case COMP_REG: { | ||
1668 | // reg_val = res.idx; | ||
1669 | // } break; | ||
1670 | // default: { | ||
1671 | // emit_compile_err(compiler, chunk, node); | ||
1672 | // return (CompResult){.type = COMP_ERR}; | ||
1673 | // } break; | ||
1674 | // } | ||
1675 | |||
1676 | // // Address. | ||
1677 | // sz reg_addr = chunk->reg_idx++; | ||
1678 | // // Is this a pointer access or an array access? | ||
1679 | // if (str_has_prefix(map->val.type_name, cstr("[]"))) { | ||
1680 | // emit_op(op_ldaddr, reg_addr, map->val.idx, 0, node->var.val, | ||
1681 | // chunk); | ||
1682 | // } else { | ||
1683 | // emit_op(op_ldvar, reg_addr, map->val.idx, 0, node->var.val, | ||
1684 | // chunk); | ||
1685 | // } | ||
1686 | |||
1687 | // // Index. | ||
1688 | // CompResult res_idx = | ||
1689 | // compile_expr(compiler, chunk, node->var.name->sym.arr_size); | ||
1690 | // switch (res_idx.type) { | ||
1691 | // case COMP_CONST: { | ||
1692 | // emit_sized_op(type.size, OP_ST64I, OP_ST32I, OP_ST16I, | ||
1693 | // OP_ST8I, | ||
1694 | // reg_val, reg_addr, res_idx.idx, node, chunk); | ||
1695 | // } break; | ||
1696 | // case COMP_REG: { | ||
1697 | // emit_sized_op(type.size, OP_ST64, OP_ST32, OP_ST16, OP_ST8, | ||
1698 | // reg_val, reg_addr, res_idx.idx, node, chunk); | ||
1699 | // } break; | ||
1700 | // case COMP_STRING: { | ||
1701 | // // Get the address for the string value variable. | ||
1702 | // sz str_addr = chunk->reg_idx++; | ||
1703 | // emit_op(OP_LDSTR, str_addr, res.idx, 0, node->var.val, | ||
1704 | // chunk); | ||
1705 | |||
1706 | // // Get the address for the local/global storage | ||
1707 | // // variable. | ||
1708 | // sz var_addr = chunk->reg_idx++; | ||
1709 | // emit_op(op_ldaddr, var_addr, idx, 0, node, chunk); | ||
1710 | |||
1711 | // // Copy the fat pointer. | ||
1712 | // emit_fat_copy(chunk, node, var_addr, str_addr); | ||
1713 | // } break; | ||
1714 | // default: { | ||
1715 | // emit_compile_err(compiler, chunk, node); | ||
1716 | // return (CompResult){.type = COMP_ERR}; | ||
1717 | // } break; | ||
1718 | // } | ||
1719 | // return (CompResult){.type = COMP_NIL}; | ||
1720 | } else if (node->var.name->kind == NODE_DEREF) { | ||
1721 | // Value. | ||
1722 | sz reg_val; | ||
1723 | switch (res.type) { | ||
1724 | case COMP_CONST: { | ||
1725 | reg_val = chunk->reg_idx++; | ||
1726 | emit_op(OP_LDCONST, reg_val, res.idx, 0, node, chunk); | ||
1727 | } break; | ||
1728 | case COMP_REG: { | ||
1729 | reg_val = res.idx; | ||
1730 | } break; | ||
1731 | default: { | ||
1732 | emit_compile_err(compiler, chunk, node); | ||
1733 | return (CompResult){.type = COMP_ERR}; | ||
1734 | } break; | ||
1735 | } | ||
1736 | |||
1737 | Node *next = node->var.name->deref.next; | ||
1738 | sz n_deref = 0; | ||
1739 | while (next) { | ||
1740 | n_deref++; | ||
1741 | if (next->kind == NODE_SYMBOL) { | ||
1742 | break; | ||
1743 | } | ||
1744 | next = next->deref.next; | ||
1745 | } | ||
1746 | CompResult res = compile_expr(compiler, chunk, next); | ||
1747 | sz reg_src = res.idx; | ||
1748 | sz reg_dst = reg_src; | ||
1749 | for (sz i = 0; i < n_deref - 1; i++) { | ||
1750 | reg_dst = chunk->reg_idx++; | ||
1751 | emit_op(OP_LD64K, reg_dst, reg_src, 0, node, chunk); | ||
1752 | reg_src = reg_dst; | ||
1753 | } | ||
1754 | emit_sized_op(type.size, OP_ST64K, OP_ST32K, OP_ST16K, OP_ST8K, reg_val, | ||
1755 | reg_dst, 0, node, chunk); | ||
1756 | |||
1757 | return (CompResult){.type = COMP_NIL}; | ||
1758 | } | ||
1759 | |||
1760 | switch (res.type) { | ||
1761 | case COMP_CONST: { | ||
1762 | sz reg_addr = chunk->reg_idx++; | ||
1763 | sz reg_dst = chunk->reg_idx++; | ||
1764 | emit_op(op_ldaddr, reg_addr, idx, 0, node, chunk); | ||
1765 | emit_op(OP_LDCONST, reg_dst, res.idx, 0, node, chunk); | ||
1766 | emit_sized_op(type.size, OP_ST64K, OP_ST32K, OP_ST16K, OP_ST8K, | ||
1767 | reg_dst, reg_addr, 0, node, chunk); | ||
1768 | } break; | ||
1769 | case COMP_REG: { | ||
1770 | sz reg_addr = chunk->reg_idx++; | ||
1771 | sz reg_val = res.idx; | ||
1772 | emit_op(op_ldaddr, reg_addr, idx, 0, node, chunk); | ||
1773 | if (var.size > 8 || str_has_prefix(var.type_name, cstr("["))) { | ||
1774 | sz reg_dst = chunk->reg_idx++; | ||
1775 | for (sz i = 0; i < var.size / 8; i++) { | ||
1776 | sz offset = add_constant(chunk, i); | ||
1777 | emit_op(OP_LD64I, reg_dst, reg_val, offset, node, chunk); | ||
1778 | emit_op(OP_ST64I, reg_dst, reg_addr, offset, node, chunk); | ||
1779 | } | ||
1780 | } else { | ||
1781 | emit_sized_op(var.size, OP_ST64K, OP_ST32K, OP_ST16K, OP_ST8K, | ||
1782 | reg_val, reg_addr, 0, node, chunk); | ||
1783 | } | ||
1784 | } break; | ||
1785 | case COMP_STRING: { | ||
1786 | // Get the address for the string value variable. | ||
1787 | sz str_addr = chunk->reg_idx++; | ||
1788 | emit_op(OP_LDSTR, str_addr, res.idx, 0, node->var.val, chunk); | ||
1789 | |||
1790 | // Get the address for the local/global storage | ||
1791 | // variable. | ||
1792 | sz var_addr = chunk->reg_idx++; | ||
1793 | emit_op(op_ldaddr, var_addr, idx, 0, node, chunk); | ||
1794 | |||
1795 | // Copy the fat pointer. | ||
1796 | emit_fat_copy(chunk, node, var_addr, str_addr); | ||
1797 | } break; | ||
1798 | default: { | ||
1799 | emit_compile_err(compiler, chunk, node); | ||
1800 | return (CompResult){.type = COMP_ERR}; | ||
1801 | } break; | ||
1802 | } | ||
558 | return (CompResult){.type = COMP_NIL}; | 1803 | return (CompResult){.type = COMP_NIL}; |
559 | } | 1804 | } |
560 | 1805 | ||
561 | CompResult | 1806 | CompResult |
562 | compile_expr(Chunk *chunk, Node *node) { | 1807 | compile_symbol(Compiler *compiler, Chunk *chunk, Node *node) { |
1808 | Str name = node->unique_name; | ||
1809 | StrVarMap *map = NULL; | ||
1810 | Chunk *next = chunk; | ||
1811 | while (next) { | ||
1812 | map = varmap_lookup(&next->varmap, name); | ||
1813 | if (map) { | ||
1814 | break; | ||
1815 | } | ||
1816 | next = next->parent; | ||
1817 | } | ||
1818 | if (!map) { | ||
1819 | emit_compile_err(compiler, chunk, node); | ||
1820 | return (CompResult){.type = COMP_ERR}; | ||
1821 | } | ||
1822 | sz op_ldaddr = OP_LDLADDR; | ||
1823 | if (next == &compiler->main_chunk) { | ||
1824 | op_ldaddr = OP_LDGADDR; | ||
1825 | } | ||
1826 | Variable var = map->val; | ||
1827 | sz reg_dst = chunk->reg_idx++; | ||
1828 | if (str_has_prefix(var.type_name, cstr("[")) || | ||
1829 | str_eq(var.type_name, cstr("Str"))) { | ||
1830 | emit_op(op_ldaddr, reg_dst, var.idx, 0, node, chunk); | ||
1831 | } else { | ||
1832 | sz reg_addr = chunk->reg_idx++; | ||
1833 | emit_op(op_ldaddr, reg_addr, var.idx, 0, node, chunk); | ||
1834 | emit_sized_op(var.size, OP_LD64K, OP_LD32K, OP_LD16K, OP_LD8K, reg_dst, | ||
1835 | reg_addr, 0, node, chunk); | ||
1836 | } | ||
1837 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
1838 | } | ||
1839 | |||
1840 | // CompResult | ||
1841 | // compile_symbol_idx(Compiler *compiler, Chunk *chunk, Node *node) { | ||
1842 | // Str name = node->unique_name; | ||
1843 | // StrVarMap *map = NULL; | ||
1844 | // Chunk *next = chunk; | ||
1845 | // while (next) { | ||
1846 | // map = varmap_lookup(&next->varmap, name); | ||
1847 | // if (map) { | ||
1848 | // break; | ||
1849 | // } | ||
1850 | // next = next->parent; | ||
1851 | // } | ||
1852 | // if (!map) { | ||
1853 | // eprintln("couldn't resolve symbol name: %s", name); | ||
1854 | // emit_compile_err(compiler, chunk, node); | ||
1855 | // return (CompResult){.type = COMP_ERR}; | ||
1856 | // } | ||
1857 | // sz op_ldaddr = OP_LDLADDR; | ||
1858 | // sz op_ldvar = OP_LDLVAR; | ||
1859 | // if (next == &compiler->main_chunk) { | ||
1860 | // op_ldaddr = OP_LDGADDR; | ||
1861 | // op_ldvar = OP_LDGVAR; | ||
1862 | // } | ||
1863 | |||
1864 | // // Destination. | ||
1865 | // sz reg_dst = chunk->reg_idx++; | ||
1866 | |||
1867 | // // Address. | ||
1868 | // sz reg_addr = chunk->reg_idx++; | ||
1869 | // if (str_has_prefix(map->val.type_name, cstr("[]"))) { | ||
1870 | // emit_op(op_ldaddr, reg_addr, map->val.idx, 0, node->var.val, chunk); | ||
1871 | // } else { | ||
1872 | // emit_op(op_ldvar, reg_addr, map->val.idx, 0, node->var.val, chunk); | ||
1873 | // } | ||
1874 | |||
1875 | // Type type = map->val.type; | ||
1876 | |||
1877 | // // Index. | ||
1878 | // CompResult idx = compile_expr(compiler, chunk, node->sym.arr_size); | ||
1879 | // switch (idx.type) { | ||
1880 | // case COMP_CONST: { | ||
1881 | // emit_sized_op(type.size, OP_LD64I, OP_LD32I, OP_LD16I, OP_LD8I, | ||
1882 | // reg_dst, reg_addr, idx.idx, node, chunk); | ||
1883 | // } break; | ||
1884 | // case COMP_REG: { | ||
1885 | // emit_sized_op(type.size, OP_LD64, OP_LD32, OP_LD16, OP_LD8, reg_dst, | ||
1886 | // reg_addr, idx.idx, node, chunk); | ||
1887 | // } break; | ||
1888 | // default: { | ||
1889 | // emit_compile_err(compiler, chunk, node); | ||
1890 | // return (CompResult){.type = COMP_ERR}; | ||
1891 | // } break; | ||
1892 | // } | ||
1893 | // return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
1894 | // } | ||
1895 | |||
1896 | CompResult | ||
1897 | compile_expr(Compiler *compiler, Chunk *chunk, Node *node) { | ||
563 | switch (node->kind) { | 1898 | switch (node->kind) { |
564 | case NODE_IF: return compile_if(chunk, node); | 1899 | case NODE_BREAK: return compile_break(compiler, chunk, node); |
1900 | case NODE_CONTINUE: return compile_continue(compiler, chunk, node); | ||
1901 | case NODE_RETURN: return compile_return(compiler, chunk, node); | ||
1902 | case NODE_FUN: return compile_function(compiler, chunk, node); | ||
1903 | case NODE_FUNCALL: return compile_funcall(compiler, chunk, node); | ||
1904 | case NODE_WHILE: return compile_while(compiler, chunk, node); | ||
1905 | case NODE_IF: return compile_if(compiler, chunk, node); | ||
1906 | case NODE_COND: return compile_cond(compiler, chunk, node); | ||
565 | // Logic. | 1907 | // Logic. |
566 | // case NODE_XOR: | ||
567 | case NODE_BITNOT: | 1908 | case NODE_BITNOT: |
568 | case NODE_NOT: return compile_unary(chunk, node); break; | 1909 | case NODE_NOT: return compile_unary(compiler, chunk, node); break; |
569 | case NODE_AND: | 1910 | case NODE_AND: |
570 | case NODE_OR: | 1911 | case NODE_OR: return compile_binary_logic(compiler, chunk, node); break; |
571 | case NODE_EQ: | 1912 | case NODE_EQ: |
572 | case NODE_NEQ: | 1913 | case NODE_NEQ: |
573 | case NODE_LT: | 1914 | case NODE_LT: |
@@ -576,6 +1917,7 @@ compile_expr(Chunk *chunk, Node *node) { | |||
576 | // Bitwise ops. | 1917 | // Bitwise ops. |
577 | case NODE_BITAND: | 1918 | case NODE_BITAND: |
578 | case NODE_BITOR: | 1919 | case NODE_BITOR: |
1920 | case NODE_BITXOR: | ||
579 | case NODE_BITLSHIFT: | 1921 | case NODE_BITLSHIFT: |
580 | case NODE_BITRSHIFT: | 1922 | case NODE_BITRSHIFT: |
581 | // Arithmetic. | 1923 | // Arithmetic. |
@@ -584,7 +1926,7 @@ compile_expr(Chunk *chunk, Node *node) { | |||
584 | case NODE_SUB: | 1926 | case NODE_SUB: |
585 | case NODE_MUL: | 1927 | case NODE_MUL: |
586 | case NODE_DIV: | 1928 | case NODE_DIV: |
587 | case NODE_MOD: return compile_binary(chunk, node); break; | 1929 | case NODE_MOD: return compile_binary(compiler, chunk, node); break; |
588 | case NODE_TRUE: | 1930 | case NODE_TRUE: |
589 | case NODE_FALSE: | 1931 | case NODE_FALSE: |
590 | case NODE_NUM_FLOAT: | 1932 | case NODE_NUM_FLOAT: |
@@ -599,105 +1941,80 @@ compile_expr(Chunk *chunk, Node *node) { | |||
599 | } break; | 1941 | } break; |
600 | case NODE_STRING: { | 1942 | case NODE_STRING: { |
601 | Str string = node->value.str; | 1943 | Str string = node->value.str; |
602 | // Make sure we don't have duplicated strings. | 1944 | sz str_idx = add_string(chunk, string); |
603 | StrIntMap *map = strintmap_lookup(&chunk->strmap, string); | ||
604 | if (!map) { | ||
605 | map = strintmap_insert(&chunk->strmap, string, chunk->str_idx++, | ||
606 | chunk->storage); | ||
607 | array_push(chunk->strings, string, chunk->storage); | ||
608 | } | ||
609 | return (CompResult){ | 1945 | return (CompResult){ |
610 | .type = COMP_STRING, | 1946 | .type = COMP_STRING, |
611 | .idx = map->val, | 1947 | .idx = str_idx, |
612 | }; | 1948 | }; |
613 | } break; | 1949 | } break; |
614 | case NODE_LET: { | 1950 | case NODE_LET: return compile_let(compiler, chunk, node); |
615 | sz idx = array_size(chunk->vars); | 1951 | case NODE_SET: return compile_set(compiler, chunk, node); |
616 | Str name = node->unique_name; | 1952 | case NODE_SYMBOL: return compile_symbol(compiler, chunk, node); |
617 | Str type = node->var_name->type; | 1953 | case NODE_PTR: { |
618 | sz size = 8; | 1954 | // Load and return address of associated symbol. |
619 | // TODO: get type storage from a table to consider all the basic | 1955 | Str name = node->t.next->unique_name; |
620 | // types as well as user defined ones. | 1956 | sz op_ldaddr = OP_LDLADDR; |
621 | if (str_eq(type, cstr("str"))) { | 1957 | if (chunk == &compiler->main_chunk) { |
622 | size = 16; | 1958 | op_ldaddr = OP_LDGADDR; |
623 | } | ||
624 | Variable var = (Variable){ | ||
625 | .name = name, | ||
626 | .type = type, | ||
627 | .size = size, | ||
628 | .offset = chunk->var_off, | ||
629 | .idx = idx, | ||
630 | }; | ||
631 | varmap_insert(&chunk->varmap, name, var, chunk->storage); | ||
632 | array_push(chunk->vars, var, chunk->storage); | ||
633 | chunk->var_off += size; | ||
634 | |||
635 | // Value. | ||
636 | if (node->var_val) { | ||
637 | CompResult res = compile_expr(chunk, node->var_val); | ||
638 | switch (res.type) { | ||
639 | case COMP_CONST: { | ||
640 | EMIT_OP(OP_STGVARI, idx, res.idx, 0, node->var_val, | ||
641 | chunk); | ||
642 | } break; | ||
643 | case COMP_REG: { | ||
644 | EMIT_OP(OP_STGVAR, idx, res.idx, 0, node->var_val, | ||
645 | chunk); | ||
646 | } break; | ||
647 | default: { | ||
648 | return (CompResult){.type = COMP_ERR}; | ||
649 | } break; | ||
650 | } | ||
651 | } | 1959 | } |
652 | 1960 | ||
653 | return (CompResult){.type = COMP_NIL}; | ||
654 | } break; | ||
655 | case NODE_SET: { | ||
656 | Str name = node->unique_name; | ||
657 | StrVarMap *map = varmap_lookup(&chunk->varmap, name); | 1961 | StrVarMap *map = varmap_lookup(&chunk->varmap, name); |
658 | if (!map) { | 1962 | if (!map) { |
659 | println("you wot mate?: %s", name); | 1963 | emit_compile_err(compiler, chunk, node); |
660 | } | ||
661 | CompResult res = compile_expr(chunk, node->var_val); | ||
662 | switch (res.type) { | ||
663 | case COMP_CONST: { | ||
664 | EMIT_OP(OP_STGVARI, map->val.idx, res.idx, 0, node->var_val, | ||
665 | chunk); | ||
666 | } break; | ||
667 | case COMP_REG: { | ||
668 | EMIT_OP(OP_STGVAR, map->val.idx, res.idx, 0, node->var_val, | ||
669 | chunk); | ||
670 | } break; | ||
671 | default: { | ||
672 | return (CompResult){.type = COMP_ERR}; | ||
673 | } break; | ||
674 | } | 1964 | } |
675 | return (CompResult){.type = COMP_NIL}; | 1965 | Variable var = map->val; |
1966 | |||
1967 | sz reg_addr = chunk->reg_idx++; | ||
1968 | emit_op(op_ldaddr, reg_addr, var.idx, 0, node, chunk); | ||
1969 | return (CompResult){.type = COMP_REG, .idx = reg_addr}; | ||
676 | } break; | 1970 | } break; |
677 | case NODE_SYMBOL: { | 1971 | case NODE_DEREF: { |
678 | Str name = node->unique_name; | 1972 | Str type_name = node->type; |
679 | StrVarMap *map = varmap_lookup(&chunk->varmap, name); | 1973 | if (str_has_prefix(type_name, cstr("@")) || |
680 | if (!map) { | 1974 | str_has_prefix(type_name, cstr("["))) { |
681 | println("error: unreachable... name: %s", name); | 1975 | type_name = cstr("Ptr"); |
682 | exit(EXIT_FAILURE); | 1976 | } |
1977 | StrTypeMap *t = strtype_lookup(&compiler->type_map, type_name); | ||
1978 | sz size = t->val.size; | ||
1979 | |||
1980 | Node *next = node->deref.next; | ||
1981 | sz n_deref = 0; | ||
1982 | while (next) { | ||
1983 | n_deref++; | ||
1984 | if (next->kind == NODE_SYMBOL) { | ||
1985 | break; | ||
1986 | } | ||
1987 | next = next->deref.next; | ||
1988 | } | ||
1989 | CompResult res = compile_symbol(compiler, chunk, next); | ||
1990 | sz reg_dst = res.idx; | ||
1991 | sz reg_src = res.idx; | ||
1992 | for (sz i = 0; i < n_deref; i++) { | ||
1993 | reg_dst = chunk->reg_idx++; | ||
1994 | if (i == n_deref - 1) { | ||
1995 | emit_sized_op(size, OP_LD64K, OP_LD32K, OP_LD16K, OP_LD8K, | ||
1996 | reg_dst, reg_src, 0, node, chunk); | ||
1997 | } else { | ||
1998 | emit_op(OP_LD64K, reg_dst, reg_src, 0, node, chunk); | ||
1999 | } | ||
2000 | reg_src = reg_dst; | ||
683 | } | 2001 | } |
684 | Variable var = map->val; | ||
685 | u8 reg_dst = chunk->reg_idx++; | ||
686 | EMIT_OP(OP_LDGVAR, reg_dst, var.idx, 0, node, chunk); | ||
687 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 2002 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
688 | } break; | 2003 | } break; |
689 | case NODE_BLOCK: { | 2004 | case NODE_BLOCK: { |
690 | CompResult res; | 2005 | CompResult res; |
691 | for (sz i = 0; i < array_size(node->elements); i++) { | 2006 | for (sz i = 0; i < array_size(node->elements); i++) { |
692 | Node *root = node->elements[i]; | 2007 | Node *root = node->elements[i]; |
693 | res = compile_expr(chunk, root); | 2008 | res = compile_expr(compiler, chunk, root); |
694 | } | 2009 | } |
695 | return res; | 2010 | return res; |
696 | } break; | 2011 | } break; |
2012 | case NODE_NIL: return (CompResult){.type = COMP_NIL}; | ||
697 | default: { | 2013 | default: { |
698 | eprintln("error: compilation not implemented for node %s", | 2014 | eprintln("error: compilation not implemented for node %s", |
699 | node_str[node->kind]); | 2015 | node_str[node->kind]); |
700 | exit(EXIT_FAILURE); | 2016 | emit_compile_err(compiler, chunk, node); |
2017 | return (CompResult){.type = COMP_ERR}; | ||
701 | } break; | 2018 | } break; |
702 | } | 2019 | } |
703 | return (CompResult){.type = COMP_ERR}; | 2020 | return (CompResult){.type = COMP_ERR}; |
@@ -706,19 +2023,31 @@ compile_expr(Chunk *chunk, Node *node) { | |||
706 | void | 2023 | void |
707 | disassemble_instruction(Instruction instruction) { | 2024 | disassemble_instruction(Instruction instruction) { |
708 | switch (instruction.op) { | 2025 | switch (instruction.op) { |
2026 | case OP_CALL: | ||
2027 | println("%s f%d", op_str[instruction.op], instruction.dst, | ||
2028 | instruction.a, instruction.b); | ||
2029 | break; | ||
709 | case OP_MOV8: | 2030 | case OP_MOV8: |
710 | case OP_MOV16: | 2031 | case OP_MOV16: |
711 | case OP_MOV32: | 2032 | case OP_MOV32: |
712 | case OP_MOV64: | 2033 | case OP_MOV64: |
713 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, | ||
714 | instruction.a, instruction.b); | ||
715 | break; | ||
716 | case OP_LD8K: | 2034 | case OP_LD8K: |
717 | case OP_LD16K: | 2035 | case OP_LD16K: |
718 | case OP_LD32K: | 2036 | case OP_LD32K: |
719 | case OP_LD64K: | 2037 | case OP_LD64K: |
2038 | case OP_ST8K: | ||
2039 | case OP_ST16K: | ||
2040 | case OP_ST32K: | ||
2041 | case OP_ST64K: | ||
2042 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, | ||
2043 | instruction.a, instruction.b); | ||
2044 | break; | ||
720 | case OP_JMPF: | 2045 | case OP_JMPF: |
721 | case OP_JMPT: | 2046 | case OP_JMPT: |
2047 | println("%s l%d, r%d", op_str[instruction.op], instruction.dst, | ||
2048 | instruction.a, instruction.b); | ||
2049 | break; | ||
2050 | case OP_LDCONST: | ||
722 | println("%s r%d, c%d", op_str[instruction.op], instruction.dst, | 2051 | println("%s r%d, c%d", op_str[instruction.op], instruction.dst, |
723 | instruction.a, instruction.b); | 2052 | instruction.a, instruction.b); |
724 | break; | 2053 | break; |
@@ -752,6 +2081,7 @@ disassemble_instruction(Instruction instruction) { | |||
752 | case OP_BITRSHIFTI: | 2081 | case OP_BITRSHIFTI: |
753 | case OP_BITANDI: | 2082 | case OP_BITANDI: |
754 | case OP_BITORI: | 2083 | case OP_BITORI: |
2084 | case OP_BITXORI: | ||
755 | println("%s r%d, r%d, c%d", op_str[instruction.op], instruction.dst, | 2085 | println("%s r%d, r%d, c%d", op_str[instruction.op], instruction.dst, |
756 | instruction.a, instruction.b); | 2086 | instruction.a, instruction.b); |
757 | break; | 2087 | break; |
@@ -785,18 +2115,28 @@ disassemble_instruction(Instruction instruction) { | |||
785 | case OP_BITRSHIFT: | 2115 | case OP_BITRSHIFT: |
786 | case OP_BITAND: | 2116 | case OP_BITAND: |
787 | case OP_BITOR: | 2117 | case OP_BITOR: |
2118 | case OP_BITXOR: | ||
788 | println("%s r%d, r%d, r%d", op_str[instruction.op], instruction.dst, | 2119 | println("%s r%d, r%d, r%d", op_str[instruction.op], instruction.dst, |
789 | instruction.a, instruction.b); | 2120 | instruction.a, instruction.b); |
790 | break; | 2121 | break; |
791 | case OP_LDGVAR: | 2122 | case OP_LDGVAR: |
2123 | case OP_LDGADDR: | ||
2124 | case OP_LDLVAR: | ||
2125 | case OP_LDLADDR: | ||
792 | println("%s r%d, v%d", op_str[instruction.op], instruction.dst, | 2126 | println("%s r%d, v%d", op_str[instruction.op], instruction.dst, |
793 | instruction.a, instruction.b); | 2127 | instruction.a, instruction.b); |
794 | break; | 2128 | break; |
2129 | case OP_LDSTR: | ||
2130 | println("%s r%d, s%d", op_str[instruction.op], instruction.dst, | ||
2131 | instruction.a, instruction.b); | ||
2132 | break; | ||
795 | case OP_STGVAR: | 2133 | case OP_STGVAR: |
2134 | case OP_STLVAR: | ||
796 | println("%s v%d, r%d", op_str[instruction.op], instruction.dst, | 2135 | println("%s v%d, r%d", op_str[instruction.op], instruction.dst, |
797 | instruction.a, instruction.b); | 2136 | instruction.a, instruction.b); |
798 | break; | 2137 | break; |
799 | case OP_STGVARI: | 2138 | case OP_STGVARI: |
2139 | case OP_STLVARI: | ||
800 | println("%s v%d, c%d", op_str[instruction.op], instruction.dst, | 2140 | println("%s v%d, c%d", op_str[instruction.op], instruction.dst, |
801 | instruction.a, instruction.b); | 2141 | instruction.a, instruction.b); |
802 | break; | 2142 | break; |
@@ -810,19 +2150,53 @@ disassemble_instruction(Instruction instruction) { | |||
810 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, | 2150 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, |
811 | instruction.a, instruction.b); | 2151 | instruction.a, instruction.b); |
812 | break; | 2152 | break; |
813 | case OP_JMPI: | ||
814 | println("%s c%d", op_str[instruction.op], instruction.dst, | ||
815 | instruction.a, instruction.b); | ||
816 | break; | ||
817 | case OP_JMP: | 2153 | case OP_JMP: |
818 | println("%s r%d", op_str[instruction.op], instruction.dst, | 2154 | println("%s l%d", op_str[instruction.op], instruction.dst, |
819 | instruction.a, instruction.b); | 2155 | instruction.a, instruction.b); |
820 | break; | 2156 | break; |
821 | case OP_JMPFI: | 2157 | case OP_JMPFI: |
822 | case OP_JMPTI: | 2158 | case OP_JMPTI: |
823 | println("%s c%d, c%d", op_str[instruction.op], instruction.dst, | 2159 | println("%s l%d, c%d", op_str[instruction.op], instruction.dst, |
824 | instruction.a, instruction.b); | 2160 | instruction.a, instruction.b); |
825 | break; | 2161 | break; |
2162 | case OP_PRINTS8: | ||
2163 | case OP_PRINTS16: | ||
2164 | case OP_PRINTS32: | ||
2165 | case OP_PRINTS64: | ||
2166 | case OP_PRINTU8: | ||
2167 | case OP_PRINTU16: | ||
2168 | case OP_PRINTU32: | ||
2169 | case OP_PRINTU64: | ||
2170 | case OP_PRINTF32: | ||
2171 | case OP_PRINTF64: | ||
2172 | case OP_PRINTSTR: | ||
2173 | case OP_PRINTBOOL: | ||
2174 | case OP_PUSH: | ||
2175 | case OP_POP: | ||
2176 | case OP_PUTRET: | ||
2177 | println("%s r%d", op_str[instruction.op], instruction.dst, | ||
2178 | instruction.a, instruction.b); | ||
2179 | break; | ||
2180 | case OP_PRINTSTRI: | ||
2181 | case OP_PRINTS8I: | ||
2182 | case OP_PRINTS16I: | ||
2183 | case OP_PRINTS32I: | ||
2184 | case OP_PRINTS64I: | ||
2185 | case OP_PRINTU8I: | ||
2186 | case OP_PRINTU16I: | ||
2187 | case OP_PRINTU32I: | ||
2188 | case OP_PRINTU64I: | ||
2189 | case OP_PRINTF32I: | ||
2190 | case OP_PRINTF64I: | ||
2191 | case OP_PRINTBOOLI: | ||
2192 | case OP_RESERVE: | ||
2193 | case OP_PUSHI: | ||
2194 | case OP_PUTRETI: | ||
2195 | println("%s c%d", op_str[instruction.op], instruction.dst, | ||
2196 | instruction.a, instruction.b); | ||
2197 | break; | ||
2198 | case OP_RET: | ||
2199 | case OP_RECUR: | ||
826 | case OP_HALT: println("%s", op_str[instruction.op]); break; | 2200 | case OP_HALT: println("%s", op_str[instruction.op]); break; |
827 | default: println("Unknown opcode %d", instruction.op); break; | 2201 | default: println("Unknown opcode %d", instruction.op); break; |
828 | } | 2202 | } |
@@ -830,36 +2204,141 @@ disassemble_instruction(Instruction instruction) { | |||
830 | 2204 | ||
831 | void | 2205 | void |
832 | disassemble_chunk(Chunk chunk) { | 2206 | disassemble_chunk(Chunk chunk) { |
833 | println("%s: ============== code ==============", chunk.file_name); | 2207 | println("CHUNK %d: %s%s", chunk.id, chunk.file_name, chunk.name); |
2208 | println("n_regs: %d, n_vars: %d, n_strings: %d, n_consts: %d", | ||
2209 | chunk.reg_idx, array_size(chunk.vars), chunk.str_idx, | ||
2210 | chunk.const_idx); | ||
2211 | println("================== code =================="); | ||
2212 | println(" LINE:COL INUM LABELS OP OPERANDS "); | ||
2213 | println("------------------------------------------"); | ||
834 | for (sz i = 0; i < array_size(chunk.code); i++) { | 2214 | for (sz i = 0; i < array_size(chunk.code); i++) { |
835 | print("%s: %x{4}: ", chunk.file_name, i); | 2215 | printf(" %.4ld:%.4ld %.4lx ", chunk.linecol[i].line, |
2216 | chunk.linecol[i].col, i); | ||
2217 | IntIntMapIter lab_it = intintmap_iterator(chunk.labels, chunk.storage); | ||
2218 | IntIntMap *m = intintmap_next(&lab_it, chunk.storage); | ||
2219 | Str labs = cstr(""); | ||
2220 | while (m) { | ||
2221 | if (m->val == i) { | ||
2222 | labs = str_concat(labs, cstr(".L"), chunk.storage); | ||
2223 | labs = str_concat(labs, str_from_int(m->key, chunk.storage), | ||
2224 | chunk.storage); | ||
2225 | break; | ||
2226 | } | ||
2227 | m = intintmap_next(&lab_it, chunk.storage); | ||
2228 | } | ||
2229 | if (labs.size) { | ||
2230 | print("%s", labs); | ||
2231 | for (sz i = 0; i < 7 - labs.size; i++) { | ||
2232 | print(" "); | ||
2233 | } | ||
2234 | } else { | ||
2235 | printf(" "); | ||
2236 | } | ||
836 | disassemble_instruction(chunk.code[i]); | 2237 | disassemble_instruction(chunk.code[i]); |
837 | } | 2238 | } |
838 | if (array_size(chunk.constants) > 0) { | 2239 | if (array_size(chunk.constants) > 0) { |
839 | println("%s: ============ constants ===========", chunk.file_name); | 2240 | println("================ constants ===============", chunk.file_name); |
840 | for (sz i = 0; i < array_size(chunk.constants); i++) { | 2241 | for (sz i = 0; i < array_size(chunk.constants); i++) { |
841 | println("%s: %x{2}: %x{8}", chunk.file_name, i, | 2242 | println(" %x{2}: %x{8}", i, chunk.constants[i]); |
842 | chunk.constants[i]); | ||
843 | } | 2243 | } |
844 | } | 2244 | } |
845 | if (array_size(chunk.strings) > 0) { | 2245 | if (array_size(chunk.strings) > 0) { |
846 | println("%s: ============= strings ============", chunk.file_name); | 2246 | println("================= strings ================", chunk.file_name); |
847 | for (sz i = 0; i < array_size(chunk.strings); i++) { | 2247 | for (sz i = 0; i < array_size(chunk.strings); i++) { |
848 | println("%s: %x{2}: %s", chunk.file_name, i, chunk.strings[i]); | 2248 | println(" %x{2}: %s", i, chunk.strings[i]); |
849 | } | 2249 | } |
850 | } | 2250 | } |
851 | if (array_size(chunk.vars) > 0) { | 2251 | if (array_size(chunk.vars) > 0) { |
852 | println("%s: ============ variables ===========", chunk.file_name); | 2252 | println("================ variables ===============", chunk.file_name); |
853 | for (sz i = 0; i < array_size(chunk.vars); i++) { | 2253 | for (sz i = 0; i < array_size(chunk.vars); i++) { |
854 | println("%s: %x{2}: [%x{4}:%x{4}] %s: %s", chunk.file_name, i, | 2254 | println(" %x{2}: [%x{4}:%x{4}] %s: %s", i, chunk.vars[i].offset, |
855 | chunk.vars[i].offset, | ||
856 | chunk.vars[i].offset + chunk.vars[i].size, | 2255 | chunk.vars[i].offset + chunk.vars[i].size, |
857 | chunk.vars[i].name, chunk.vars[i].type); | 2256 | chunk.vars[i].name, chunk.vars[i].type_name); |
858 | } | 2257 | } |
859 | } | 2258 | } |
860 | println("n_regs: %d, n_vars: %d, n_strings: %d, n_consts: %d", | 2259 | if (array_size(chunk.functions) > 0) { |
861 | chunk.reg_idx, array_size(chunk.vars), chunk.str_idx, | 2260 | println("================ functions ===============", chunk.file_name); |
862 | chunk.const_idx); | 2261 | for (sz i = 0; i < array_size(chunk.functions); i++) { |
2262 | Chunk *func = chunk.functions[i]; | ||
2263 | println(" %x{2}: func%d: %s", i, func->id, func->name); | ||
2264 | } | ||
2265 | } | ||
2266 | println("=========================================="); | ||
2267 | for (sz i = 0; i < array_size(chunk.functions); i++) { | ||
2268 | Chunk *func = chunk.functions[i]; | ||
2269 | disassemble_chunk(*func); | ||
2270 | } | ||
2271 | } | ||
2272 | |||
2273 | void | ||
2274 | bytecode_compiler(Compiler *compiler, Parser parser) { | ||
2275 | compiler->main_chunk = (Chunk){ | ||
2276 | .file_name = compiler->file_name, | ||
2277 | .storage = compiler->storage, | ||
2278 | .name = cstr(".main"), | ||
2279 | }; | ||
2280 | array_zero(compiler->main_chunk.constants, 256, compiler->storage); | ||
2281 | array_zero(compiler->main_chunk.code, 0xffff, compiler->storage); | ||
2282 | sz n_roots = array_size(parser.nodes); | ||
2283 | |||
2284 | // Fill up builtin types and tables. | ||
2285 | for (sz i = 0; i < LEN(builtin_types); i++) { | ||
2286 | Type type = builtin_types[i]; | ||
2287 | strtype_insert(&compiler->type_map, type.unique_name, type, | ||
2288 | compiler->storage); | ||
2289 | } | ||
2290 | Str unsigned_ints[] = { | ||
2291 | cstr("U8"), cstr("U16"), cstr("U32"), | ||
2292 | cstr("U64"), cstr("Ptr"), cstr("UInt"), | ||
2293 | }; | ||
2294 | for (sz i = 0; i < LEN(unsigned_ints); i++) { | ||
2295 | Str type = unsigned_ints[i]; | ||
2296 | strset_insert(&compiler->unsigned_ints, type, compiler->storage); | ||
2297 | } | ||
2298 | Str signed_ints[] = { | ||
2299 | cstr("S8"), cstr("S16"), cstr("S32"), cstr("S64"), cstr("Int"), | ||
2300 | }; | ||
2301 | for (sz i = 0; i < LEN(signed_ints); i++) { | ||
2302 | Str type = signed_ints[i]; | ||
2303 | strset_insert(&compiler->signed_ints, type, compiler->storage); | ||
2304 | } | ||
2305 | |||
2306 | // Do a first pass to setup the function declarations on the main scope. | ||
2307 | Chunk *chunk = &compiler->main_chunk; | ||
2308 | for (sz i = 0; i < n_roots; i++) { | ||
2309 | Node *root = parser.nodes[i]; | ||
2310 | if (root->kind == NODE_FUN) { | ||
2311 | declare_function(chunk, root); | ||
2312 | } | ||
2313 | } | ||
2314 | |||
2315 | // Compile all root expressions. | ||
2316 | CompResult res = {0}; | ||
2317 | for (sz i = 0; i < n_roots; i++) { | ||
2318 | Node *root = parser.nodes[i]; | ||
2319 | res = compile_expr(compiler, chunk, root); | ||
2320 | } | ||
2321 | |||
2322 | // Make sure the last result is on r0. | ||
2323 | sz res_reg = 0; | ||
2324 | bool is_nil = false; | ||
2325 | switch (res.type) { | ||
2326 | case COMP_CONST: { | ||
2327 | res_reg = chunk->reg_idx++; | ||
2328 | Instruction inst = | ||
2329 | (Instruction){.op = OP_LDCONST, .dst = res_reg, .a = res.idx}; | ||
2330 | array_push(chunk->code, inst, chunk->storage); | ||
2331 | } break; | ||
2332 | case COMP_REG: { | ||
2333 | res_reg = res.idx; | ||
2334 | } break; | ||
2335 | case COMP_NIL: { | ||
2336 | is_nil = true; | ||
2337 | } break; | ||
2338 | default: break; | ||
2339 | } | ||
2340 | emit_op(OP_HALT, res_reg, !is_nil, 0, NULL, chunk); | ||
2341 | verify_chunk(chunk); | ||
863 | } | 2342 | } |
864 | 2343 | ||
865 | #endif // COMPILER_C | 2344 | #endif // COMPILER_C |