diff options
Diffstat (limited to 'src/vm.c')
-rw-r--r-- | src/vm.c | 262 |
1 files changed, 231 insertions, 31 deletions
@@ -5,12 +5,15 @@ | |||
5 | #include "compiler.c" | 5 | #include "compiler.c" |
6 | 6 | ||
7 | #define N_CONST 256 | 7 | #define N_CONST 256 |
8 | #define STACK_SIZE KB(64) | 8 | #define STACK_SIZE MB(4) |
9 | typedef struct VM { | 9 | typedef struct VM { |
10 | Chunk *main; | ||
10 | Chunk *chunk; | 11 | Chunk *chunk; |
11 | Constant regs[N_CONST]; | ||
12 | u8 stack[STACK_SIZE]; | 12 | u8 stack[STACK_SIZE]; |
13 | Instruction *ip; | 13 | Instruction *ip; |
14 | u8 *sp; | ||
15 | u64 *fp; | ||
16 | Constant *regs; | ||
14 | } VM; | 17 | } VM; |
15 | 18 | ||
16 | void | 19 | void |
@@ -18,8 +21,13 @@ vm_init(VM *vm, Chunk *chunk) { | |||
18 | assert(vm); | 21 | assert(vm); |
19 | assert(chunk); | 22 | assert(chunk); |
20 | assert(chunk->code); | 23 | assert(chunk->code); |
24 | vm->main = chunk; | ||
21 | vm->chunk = chunk; | 25 | vm->chunk = chunk; |
22 | vm->ip = vm->chunk->code; | 26 | vm->ip = vm->chunk->code; |
27 | vm->fp = (u64 *)vm->stack; | ||
28 | vm->sp = vm->stack + chunk->var_off; | ||
29 | vm->regs = (Constant *)vm->sp; | ||
30 | vm->sp += sizeof(Constant) * chunk->reg_idx; | ||
23 | } | 31 | } |
24 | 32 | ||
25 | #define OP_UNARY(OP, TYPE) \ | 33 | #define OP_UNARY(OP, TYPE) \ |
@@ -77,6 +85,7 @@ vm_run(VM *vm) { | |||
77 | case OP_NOT: OP_UNARY(!, i) break; | 85 | case OP_NOT: OP_UNARY(!, i) break; |
78 | case OP_BITNOT: OP_UNARY(~, i) break; | 86 | case OP_BITNOT: OP_UNARY(~, i) break; |
79 | case OP_BITOR: OP_BINARY(|, i) break; | 87 | case OP_BITOR: OP_BINARY(|, i) break; |
88 | case OP_BITXOR: OP_BINARY(^, i) break; | ||
80 | case OP_BITAND: OP_BINARY(&, i) break; | 89 | case OP_BITAND: OP_BINARY(&, i) break; |
81 | case OP_BITLSHIFT: OP_BINARY(<<, i) break; | 90 | case OP_BITLSHIFT: OP_BINARY(<<, i) break; |
82 | case OP_BITRSHIFT: OP_BINARY(>>, i) break; | 91 | case OP_BITRSHIFT: OP_BINARY(>>, i) break; |
@@ -107,6 +116,7 @@ vm_run(VM *vm) { | |||
107 | case OP_NOTI: OP_UNARY_CONST(!, i) break; | 116 | case OP_NOTI: OP_UNARY_CONST(!, i) break; |
108 | case OP_BITNOTI: OP_UNARY_CONST(~, i) break; | 117 | case OP_BITNOTI: OP_UNARY_CONST(~, i) break; |
109 | case OP_BITORI: OP_BINARY_CONST(|, i) break; | 118 | case OP_BITORI: OP_BINARY_CONST(|, i) break; |
119 | case OP_BITXORI: OP_BINARY_CONST(^, i) break; | ||
110 | case OP_BITANDI: OP_BINARY_CONST(&, i) break; | 120 | case OP_BITANDI: OP_BINARY_CONST(&, i) break; |
111 | case OP_BITLSHIFTI: OP_BINARY_CONST(<<, i) break; | 121 | case OP_BITLSHIFTI: OP_BINARY_CONST(<<, i) break; |
112 | case OP_BITRSHIFTI: OP_BINARY_CONST(>>, i) break; | 122 | case OP_BITRSHIFTI: OP_BINARY_CONST(>>, i) break; |
@@ -134,61 +144,121 @@ vm_run(VM *vm) { | |||
134 | vm->regs[dst].f = | 144 | vm->regs[dst].f = |
135 | fmod(vm->regs[src_a].f, vm->chunk->constants[src_b].f); | 145 | fmod(vm->regs[src_a].f, vm->chunk->constants[src_b].f); |
136 | } break; | 146 | } break; |
147 | case OP_STGVAR: { | ||
148 | u8 dst = instruction.dst; | ||
149 | u8 src = instruction.a; | ||
150 | Variable var = vm->main->vars[dst]; | ||
151 | s64 *stack = (s64 *)&vm->stack[var.offset]; | ||
152 | *stack = vm->regs[src].i; | ||
153 | } break; | ||
154 | case OP_STGVARI: { | ||
155 | u8 dst = instruction.dst; | ||
156 | u8 src = instruction.a; | ||
157 | Variable var = vm->main->vars[dst]; | ||
158 | s64 *stack = (s64 *)&vm->stack[var.offset]; | ||
159 | *stack = vm->chunk->constants[src].i; | ||
160 | } break; | ||
137 | case OP_LDGVAR: { | 161 | case OP_LDGVAR: { |
138 | u8 dst = instruction.dst; | 162 | u8 dst = instruction.dst; |
139 | u8 src = instruction.a; | 163 | u8 src = instruction.a; |
140 | Variable var = vm->chunk->vars[src]; | 164 | Variable var = vm->main->vars[src]; |
141 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 165 | s64 *stack = (s64 *)&vm->stack[var.offset]; |
142 | vm->regs[dst].i = *stack; | 166 | vm->regs[dst].i = *stack; |
143 | } break; | 167 | } break; |
144 | case OP_STGVAR: { | 168 | case OP_LDGADDR: { |
145 | u8 dst = instruction.dst; | 169 | u8 dst = instruction.dst; |
146 | u8 src = instruction.a; | 170 | u8 src = instruction.a; |
147 | Variable var = vm->chunk->vars[dst]; | 171 | Variable var = vm->main->vars[src]; |
148 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 172 | s64 *stack = (s64 *)&vm->stack[var.offset]; |
149 | *stack = vm->regs[src].i; | 173 | vm->regs[dst].ptr = (ptrsize)stack; |
150 | } break; | 174 | } break; |
151 | case OP_STGVARI: { | 175 | case OP_STLVAR: { |
152 | u8 dst = instruction.dst; | 176 | u8 dst = instruction.dst; |
153 | u8 src = instruction.a; | 177 | u8 src = instruction.a; |
154 | Variable var = vm->chunk->vars[dst]; | 178 | Variable var = vm->chunk->vars[dst]; |
155 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 179 | vm->fp[var.offset / 8] = vm->regs[src].i; |
156 | *stack = vm->chunk->constants[src].i; | ||
157 | } break; | 180 | } break; |
158 | case OP_JMPI: { | 181 | case OP_STLVARI: { |
159 | sz offset = vm->chunk->constants[instruction.dst].i; | 182 | u8 dst = instruction.dst; |
160 | vm->ip += offset - 1; | 183 | u8 src = instruction.a; |
184 | Variable var = vm->chunk->vars[dst]; | ||
185 | vm->fp[var.offset / 8] = vm->chunk->constants[src].i; | ||
186 | } break; | ||
187 | case OP_LDLVAR: { | ||
188 | u8 dst = instruction.dst; | ||
189 | u8 src = instruction.a; | ||
190 | Variable var = vm->chunk->vars[src]; | ||
191 | vm->regs[dst].i = vm->fp[var.offset / 8]; | ||
192 | } break; | ||
193 | case OP_LDLADDR: { | ||
194 | u8 dst = instruction.dst; | ||
195 | u8 src = instruction.a; | ||
196 | Variable var = vm->chunk->vars[src]; | ||
197 | vm->regs[dst].i = (ptrsize)&vm->fp[var.offset / 8]; | ||
198 | } break; | ||
199 | case OP_LDSTR: { | ||
200 | u8 dst = instruction.dst; | ||
201 | u8 src = instruction.a; | ||
202 | Str *str = &vm->chunk->strings[src]; | ||
203 | vm->regs[dst].ptr = (ptrsize)str; | ||
204 | } break; | ||
205 | case OP_ST64I: { | ||
206 | sz value = vm->regs[instruction.dst].ptr; | ||
207 | s64 *addr = (s64 *)vm->regs[instruction.a].ptr; | ||
208 | sz offset = vm->chunk->constants[instruction.b].i; | ||
209 | addr[offset] = value; | ||
210 | } break; | ||
211 | case OP_ST64: { | ||
212 | sz value = vm->regs[instruction.dst].i; | ||
213 | s64 *addr = (s64 *)vm->regs[instruction.a].ptr; | ||
214 | sz offset = vm->regs[instruction.b].i; | ||
215 | addr[offset] = value; | ||
216 | } break; | ||
217 | case OP_LD64I: { | ||
218 | s64 *addr = (s64 *)vm->regs[instruction.a].ptr; | ||
219 | sz offset = vm->chunk->constants[instruction.b].i; | ||
220 | vm->regs[instruction.dst].i = addr[offset]; | ||
221 | } break; | ||
222 | case OP_LD64: { | ||
223 | s64 *addr = (s64 *)vm->regs[instruction.a].ptr; | ||
224 | sz offset = vm->regs[instruction.b].i; | ||
225 | vm->regs[instruction.dst].i = addr[offset]; | ||
226 | } break; | ||
227 | case OP_JMP: { | ||
228 | u8 dst = instruction.dst; | ||
229 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; | ||
230 | vm->ip = vm->chunk->code + pos; | ||
161 | } break; | 231 | } break; |
162 | case OP_JMPFI: { | 232 | case OP_JMPFI: { |
163 | bool cond = vm->chunk->constants[instruction.dst].i; | 233 | u8 dst = instruction.dst; |
164 | sz offset = vm->chunk->constants[instruction.a].i; | 234 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; |
235 | bool cond = vm->chunk->constants[instruction.a].i; | ||
165 | if (!cond) { | 236 | if (!cond) { |
166 | vm->ip += offset - 1; | 237 | vm->ip = vm->chunk->code + pos; |
167 | } | 238 | } |
168 | } break; | 239 | } break; |
169 | case OP_JMPTI: { | 240 | case OP_JMPTI: { |
170 | bool cond = vm->chunk->constants[instruction.dst].i; | 241 | u8 dst = instruction.dst; |
171 | sz offset = vm->chunk->constants[instruction.a].i; | 242 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; |
243 | bool cond = vm->chunk->constants[instruction.a].i; | ||
172 | if (cond) { | 244 | if (cond) { |
173 | vm->ip += offset - 1; | 245 | vm->ip = vm->chunk->code + pos; |
174 | } | 246 | } |
175 | } break; | 247 | } break; |
176 | case OP_JMP: { | ||
177 | sz offset = vm->chunk->constants[instruction.dst].i; | ||
178 | vm->ip += offset - 1; | ||
179 | } break; | ||
180 | case OP_JMPF: { | 248 | case OP_JMPF: { |
181 | bool cond = vm->regs[instruction.dst].i; | 249 | u8 dst = instruction.dst; |
182 | sz offset = vm->chunk->constants[instruction.a].i; | 250 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; |
251 | bool cond = vm->regs[instruction.a].i; | ||
183 | if (!cond) { | 252 | if (!cond) { |
184 | vm->ip += offset - 1; | 253 | vm->ip = vm->chunk->code + pos; |
185 | } | 254 | } |
186 | } break; | 255 | } break; |
187 | case OP_JMPT: { | 256 | case OP_JMPT: { |
188 | bool cond = vm->regs[instruction.dst].i; | 257 | u8 dst = instruction.dst; |
189 | sz offset = vm->chunk->constants[instruction.a].i; | 258 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; |
259 | bool cond = vm->regs[instruction.a].i; | ||
190 | if (cond) { | 260 | if (cond) { |
191 | vm->ip += offset - 1; | 261 | vm->ip = vm->chunk->code + pos; |
192 | } | 262 | } |
193 | } break; | 263 | } break; |
194 | case OP_MOV64: { | 264 | case OP_MOV64: { |
@@ -211,10 +281,140 @@ vm_run(VM *vm) { | |||
211 | u8 src = instruction.a; | 281 | u8 src = instruction.a; |
212 | vm->regs[dst].i = vm->regs[src].i & 0xFF; | 282 | vm->regs[dst].i = vm->regs[src].i & 0xFF; |
213 | } break; | 283 | } break; |
284 | case OP_PRINTS64: { | ||
285 | u8 idx = instruction.dst; | ||
286 | print("%d", vm->regs[idx].i); | ||
287 | } break; | ||
288 | case OP_PRINTS64I: { | ||
289 | u8 idx = instruction.dst; | ||
290 | print("%d", vm->chunk->constants[idx].i); | ||
291 | } break; | ||
292 | case OP_PRINTBOOL: { | ||
293 | u8 idx = instruction.dst; | ||
294 | bool val = vm->regs[idx].i; | ||
295 | if (val) { | ||
296 | print("true"); | ||
297 | } else { | ||
298 | print("false"); | ||
299 | } | ||
300 | } break; | ||
301 | case OP_PRINTBOOLI: { | ||
302 | u8 idx = instruction.dst; | ||
303 | bool val = vm->chunk->constants[idx].i; | ||
304 | if (val) { | ||
305 | print("true"); | ||
306 | } else { | ||
307 | print("false"); | ||
308 | } | ||
309 | } break; | ||
310 | case OP_PRINTF64: { | ||
311 | u8 idx = instruction.dst; | ||
312 | printf("%f", vm->regs[idx].f); | ||
313 | } break; | ||
314 | case OP_PRINTF64I: { | ||
315 | u8 idx = instruction.dst; | ||
316 | printf("%f", vm->chunk->constants[idx].f); | ||
317 | } break; | ||
318 | case OP_PRINTSTR: { | ||
319 | u8 idx = instruction.dst; | ||
320 | Str *string = (Str *)vm->regs[idx].ptr; | ||
321 | print("%s", *string); | ||
322 | } break; | ||
323 | case OP_PRINTSTRI: { | ||
324 | u8 idx = instruction.dst; | ||
325 | Str string = vm->chunk->strings[idx]; | ||
326 | print("%s", string); | ||
327 | } break; | ||
328 | case OP_RESERVE: { | ||
329 | sz offset = vm->chunk->constants[instruction.dst].i; | ||
330 | vm->sp += offset; | ||
331 | } break; | ||
332 | case OP_PUSH: { | ||
333 | sz val = vm->regs[instruction.dst].i; | ||
334 | u64 *p = (u64 *)vm->sp; | ||
335 | *p = val; | ||
336 | vm->sp += sizeof(ptrsize); | ||
337 | } break; | ||
338 | case OP_PUSHI: { | ||
339 | sz val = vm->chunk->constants[instruction.dst].i; | ||
340 | u64 *p = (u64 *)vm->sp; | ||
341 | *p = val; | ||
342 | vm->sp += sizeof(ptrsize); | ||
343 | } break; | ||
344 | case OP_POP: { | ||
345 | vm->sp -= sizeof(ptrsize); | ||
346 | u64 *p = (u64 *)vm->sp; | ||
347 | vm->regs[instruction.dst].i = *p; | ||
348 | } break; | ||
349 | case OP_PUTRET: { | ||
350 | sz val = vm->regs[instruction.dst].i; | ||
351 | vm->fp[-1] = val; | ||
352 | } break; | ||
353 | case OP_PUTRETI: { | ||
354 | sz val = vm->chunk->constants[instruction.dst].i; | ||
355 | vm->fp[-1] = val; | ||
356 | } break; | ||
357 | case OP_CALL: { | ||
358 | u8 dst = instruction.dst; | ||
359 | Chunk *func = vm->main->functions[dst]; | ||
360 | |||
361 | ptrsize chunk_addr = (ptrsize)vm->chunk; | ||
362 | ptrsize ip_addr = (ptrsize)vm->ip; | ||
363 | ptrsize reg_addr = (ptrsize)vm->regs; | ||
364 | ptrsize old_fp = (ptrsize)vm->fp; | ||
365 | |||
366 | // Allocate space for the locals. | ||
367 | memset(vm->sp, 0, func->var_off - func->param_off); | ||
368 | vm->fp = (u64 *)(vm->sp - func->param_off); | ||
369 | vm->sp += func->var_off - func->param_off; | ||
370 | vm->chunk = func; | ||
371 | vm->ip = func->code; | ||
372 | vm->regs = (Constant *)vm->sp; | ||
373 | |||
374 | // Allocate registers. | ||
375 | vm->sp += sizeof(Constant) * func->reg_idx; | ||
376 | |||
377 | // Store memory addresses we need to return to this function. | ||
378 | u64 *p = (u64 *)vm->sp; | ||
379 | p[0] = chunk_addr; | ||
380 | p[1] = ip_addr; | ||
381 | p[2] = reg_addr; | ||
382 | p[3] = old_fp; | ||
383 | vm->sp += sizeof(ptrsize) * 4; | ||
384 | } break; | ||
385 | case OP_RECUR: { | ||
386 | vm->ip = vm->chunk->code; | ||
387 | } break; | ||
388 | case OP_RET: { | ||
389 | u64 *p = (u64 *)vm->sp; | ||
390 | ptrsize chunk_addr = p[-4]; | ||
391 | ptrsize ip_addr = p[-3]; | ||
392 | ptrsize reg_addr = p[-2]; | ||
393 | ptrsize old_fp = p[-1]; | ||
394 | vm->sp -= sizeof(ptrsize) * 4; | ||
395 | |||
396 | // Deallocate registers. | ||
397 | vm->sp -= sizeof(Constant) * vm->chunk->reg_idx; | ||
398 | |||
399 | // Deallocate locals. | ||
400 | vm->sp -= vm->chunk->var_off; | ||
401 | |||
402 | // Restore previous activation record. | ||
403 | vm->regs = (Constant *)reg_addr; | ||
404 | vm->ip = (Instruction *)ip_addr; | ||
405 | vm->chunk = (Chunk *)chunk_addr; | ||
406 | vm->fp = (u64 *)old_fp; | ||
407 | } break; | ||
214 | case OP_HALT: { | 408 | case OP_HALT: { |
215 | println("VM HALT (int) -> %d", vm->regs[instruction.dst]); | 409 | println("VM halt..."); |
216 | println("VM HALT (float) -> %f", vm->regs[instruction.dst]); | 410 | if (instruction.a != 0) { |
217 | println("VM HALT (hex) -> %x", vm->regs[instruction.dst]); | 411 | println("Result:"); |
412 | Constant result = vm->regs[instruction.dst]; | ||
413 | printf("\tint -> %lld\n", result.i); | ||
414 | printf("\tfloat -> %.10e\n", result.f); | ||
415 | printf("\thex -> %llx\n", (u64)result.i); | ||
416 | println("\tbinary -> %b", result.i); | ||
417 | } | ||
218 | return; | 418 | return; |
219 | } | 419 | } |
220 | default: { | 420 | default: { |