diff options
Diffstat (limited to 'src/vm.c')
-rw-r--r-- | src/vm.c | 286 |
1 files changed, 271 insertions, 15 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,36 +144,282 @@ 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; |
137 | case OP_LDVAR: { | 147 | case OP_STGVAR: { |
138 | u8 dst = instruction.dst; | 148 | u8 dst = instruction.dst; |
139 | u8 src = instruction.a; | 149 | u8 src = instruction.a; |
140 | println("dst: %d src: %d", dst, src); | 150 | Variable var = vm->main->vars[dst]; |
141 | Variable var = vm->chunk->vars[src]; | 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; | ||
161 | case OP_LDGVAR: { | ||
162 | u8 dst = instruction.dst; | ||
163 | u8 src = instruction.a; | ||
164 | Variable var = vm->main->vars[src]; | ||
142 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 165 | s64 *stack = (s64 *)&vm->stack[var.offset]; |
143 | vm->regs[dst].i = *stack; | 166 | vm->regs[dst].i = *stack; |
144 | } break; | 167 | } break; |
145 | case OP_STVAR: { | 168 | case OP_LDGADDR: { |
146 | u8 dst = instruction.dst; | 169 | u8 dst = instruction.dst; |
147 | u8 src = instruction.a; | 170 | u8 src = instruction.a; |
148 | Variable var = vm->chunk->vars[dst]; | 171 | Variable var = vm->main->vars[src]; |
149 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 172 | s64 *stack = (s64 *)&vm->stack[var.offset]; |
150 | *stack = vm->regs[src].i; | 173 | vm->regs[dst].ptr = (ptrsize)stack; |
151 | } break; | 174 | } break; |
152 | case OP_STVARI: { | 175 | case OP_STLVAR: { |
153 | u8 dst = instruction.dst; | 176 | u8 dst = instruction.dst; |
154 | u8 src = instruction.a; | 177 | u8 src = instruction.a; |
155 | Variable var = vm->chunk->vars[dst]; | 178 | Variable var = vm->chunk->vars[dst]; |
156 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 179 | vm->fp[var.offset / 8] = vm->regs[src].i; |
157 | *stack = vm->chunk->constants[src].i; | 180 | } break; |
181 | case OP_STLVARI: { | ||
182 | u8 dst = instruction.dst; | ||
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; | ||
231 | } break; | ||
232 | case OP_JMPFI: { | ||
233 | u8 dst = instruction.dst; | ||
234 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; | ||
235 | bool cond = vm->chunk->constants[instruction.a].i; | ||
236 | if (!cond) { | ||
237 | vm->ip = vm->chunk->code + pos; | ||
238 | } | ||
239 | } break; | ||
240 | case OP_JMPTI: { | ||
241 | u8 dst = instruction.dst; | ||
242 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; | ||
243 | bool cond = vm->chunk->constants[instruction.a].i; | ||
244 | if (cond) { | ||
245 | vm->ip = vm->chunk->code + pos; | ||
246 | } | ||
247 | } break; | ||
248 | case OP_JMPF: { | ||
249 | u8 dst = instruction.dst; | ||
250 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; | ||
251 | bool cond = vm->regs[instruction.a].i; | ||
252 | if (!cond) { | ||
253 | vm->ip = vm->chunk->code + pos; | ||
254 | } | ||
255 | } break; | ||
256 | case OP_JMPT: { | ||
257 | u8 dst = instruction.dst; | ||
258 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; | ||
259 | bool cond = vm->regs[instruction.a].i; | ||
260 | if (cond) { | ||
261 | vm->ip = vm->chunk->code + pos; | ||
262 | } | ||
263 | } break; | ||
264 | case OP_MOV64: { | ||
265 | u8 dst = instruction.dst; | ||
266 | u8 src = instruction.a; | ||
267 | vm->regs[dst] = vm->regs[src]; | ||
268 | } break; | ||
269 | case OP_MOV32: { | ||
270 | u8 dst = instruction.dst; | ||
271 | u8 src = instruction.a; | ||
272 | vm->regs[dst].i = vm->regs[src].i & 0xFFFFFFFF; | ||
273 | } break; | ||
274 | case OP_MOV16: { | ||
275 | u8 dst = instruction.dst; | ||
276 | u8 src = instruction.a; | ||
277 | vm->regs[dst].i = vm->regs[src].i & 0xFFFF; | ||
278 | } break; | ||
279 | case OP_MOV8: { | ||
280 | u8 dst = instruction.dst; | ||
281 | u8 src = instruction.a; | ||
282 | vm->regs[dst].i = vm->regs[src].i & 0xFF; | ||
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; | ||
158 | } break; | 407 | } break; |
159 | case OP_HALT: { | 408 | case OP_HALT: { |
160 | println("VM HALT (int) -> %d", vm->regs[instruction.dst]); | 409 | println("VM halt..."); |
161 | println("VM HALT (float) -> %f", vm->regs[instruction.dst]); | 410 | if (instruction.a != 0) { |
162 | 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 | } | ||
163 | return; | 418 | return; |
164 | } | 419 | } |
165 | default: { | 420 | default: { |
166 | eprintln("unimplemented OP code: %d", instruction.op); | 421 | // eprintln("unimplemented OP code: %d", instruction.op); |
422 | eprintln("unimplemented OP code: %s", op_str[instruction.op]); | ||
167 | return; | 423 | return; |
168 | } | 424 | } |
169 | } | 425 | } |