diff options
author | Bad Diode <bd@badd10de.dev> | 2024-07-02 23:23:57 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-07-02 23:23:57 +0200 |
commit | 203e955a38fbb1321571444463eeac8ab2d31437 (patch) | |
tree | 4931f7586ea674a89ec8cd3353d7a20fcaef855c | |
parent | 3b37ecc2357251b99d8b99b3532cff343ece6971 (diff) | |
download | bdl-203e955a38fbb1321571444463eeac8ab2d31437.tar.gz bdl-203e955a38fbb1321571444463eeac8ab2d31437.zip |
Change AR layout
-rw-r--r-- | src/compiler.c | 90 | ||||
-rw-r--r-- | src/vm.c | 70 | ||||
-rw-r--r-- | tests/compilation.bad | 18 |
3 files changed, 131 insertions, 47 deletions
diff --git a/src/compiler.c b/src/compiler.c index d1c56b3..9b6a458 100644 --- a/src/compiler.c +++ b/src/compiler.c | |||
@@ -96,8 +96,11 @@ typedef enum OpCode { | |||
96 | OP_CALL, // call fx ; Bumps the stack pointer by cx | 96 | OP_CALL, // call fx ; Bumps the stack pointer by cx |
97 | OP_RET, // ret ; Returns from current function | 97 | OP_RET, // ret ; Returns from current function |
98 | OP_RESERVE, // reserve cx ; Increments the stack pointer by cx bytes | 98 | OP_RESERVE, // reserve cx ; Increments the stack pointer by cx bytes |
99 | OP_POP, // pop cx ; Decrements the stack pointer by cx bytes | 99 | OP_POP, // pop rx ; Pops the last value of the stack into rx. |
100 | OP_SPVAL, // val rx, cx ; u64 *stack; rx = stack[cx] | 100 | OP_PUSH, // push rx ; Push the rx value to the stack. |
101 | OP_PUSHI, // pushi cx ; Push the cx value to the stack. | ||
102 | OP_PUTRET, // putret rx ; Put rx into the return value memory. | ||
103 | OP_PUTRETI, // putreti cx ; Put cx into the return value memory. | ||
101 | // Printing values with builtin print/println functions. | 104 | // Printing values with builtin print/println functions. |
102 | OP_PRINTSTR, // p rx | 105 | OP_PRINTSTR, // p rx |
103 | OP_PRINTS64, // p rx | 106 | OP_PRINTS64, // p rx |
@@ -202,12 +205,15 @@ Str op_str[] = { | |||
202 | [OP_PRINTF64] = cstr("PRNTF64 "), | 205 | [OP_PRINTF64] = cstr("PRNTF64 "), |
203 | [OP_PRINTS64I] = cstr("PRNTS64I"), | 206 | [OP_PRINTS64I] = cstr("PRNTS64I"), |
204 | [OP_PRINTF64I] = cstr("PRNTF64I"), | 207 | [OP_PRINTF64I] = cstr("PRNTF64I"), |
208 | [OP_PUTRET] = cstr("PUTRET "), | ||
209 | [OP_PUTRETI] = cstr("PUTRETI "), | ||
205 | // Functions. | 210 | // Functions. |
206 | [OP_CALL] = cstr("CALL "), | 211 | [OP_CALL] = cstr("CALL "), |
207 | [OP_RET] = cstr("RET "), | 212 | [OP_RET] = cstr("RET "), |
208 | [OP_RESERVE] = cstr("RESERVE "), | 213 | [OP_RESERVE] = cstr("RESERVE "), |
209 | [OP_POP] = cstr("POP "), | 214 | [OP_POP] = cstr("POP "), |
210 | [OP_SPVAL] = cstr("SPVAL "), | 215 | [OP_PUSH] = cstr("PUSH "), |
216 | [OP_PUSHI] = cstr("PUSHI "), | ||
211 | // Load ops. | 217 | // Load ops. |
212 | [OP_LD8K] = cstr("LD8K "), | 218 | [OP_LD8K] = cstr("LD8K "), |
213 | [OP_LD16K] = cstr("LD16K "), | 219 | [OP_LD16K] = cstr("LD16K "), |
@@ -831,33 +837,57 @@ compile_funcall(Chunk *chunk, Node *node) { | |||
831 | } | 837 | } |
832 | Function fun = map->val; | 838 | Function fun = map->val; |
833 | 839 | ||
834 | // TODO: prologue (how much space do we actually need)? we need the symbol | 840 | // Reserve space for the return value if needed. |
835 | // table, arity and return parameters boy. | 841 | if (fun.return_arity > 0) { |
836 | // FIXME: assuming all values are 8 bytes for now... | 842 | // Put the return data into a register |
837 | sz alloc_size = (fun.param_arity + fun.return_arity) * 8; | 843 | sz ret_size = add_constant(chunk, 8); |
838 | sz alloc_const = add_constant(chunk, alloc_size); | 844 | EMIT_OP(OP_RESERVE, ret_size, 0, 0, node, chunk); |
839 | if (alloc_size > 0) { | 845 | } |
840 | EMIT_OP(OP_RESERVE, alloc_const, 0, 0, node, chunk); | 846 | |
847 | // Send parameters to the stack. | ||
848 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
849 | Node *expr = node->elements[i]; | ||
850 | CompResult result = compile_expr(chunk, expr); | ||
851 | // TODO: Assuming all values are 8 bytes... again. | ||
852 | switch (result.type) { | ||
853 | case COMP_CONST: { | ||
854 | EMIT_OP(OP_PUSHI, result.idx, 0, 0, expr, chunk); | ||
855 | } break; | ||
856 | case COMP_REG: { | ||
857 | EMIT_OP(OP_PUSH, result.idx, 0, 0, expr, chunk); | ||
858 | } break; | ||
859 | default: { | ||
860 | return (CompResult){.type = COMP_ERR}; | ||
861 | } break; | ||
862 | } | ||
841 | } | 863 | } |
842 | 864 | ||
843 | // println("FUNCALL: unique name: %s", node->unique_name); | 865 | // // TODO: prologue (how much space do we actually need)? we need the |
844 | // println("FUN: param_arity: %d", fun.param_arity); | 866 | // symbol |
845 | // println("FUN: return_arity: %d", fun.return_arity); | 867 | // // table, arity and return parameters boy. |
868 | // // FIXME: assuming all values are 8 bytes for now... | ||
869 | // sz alloc_size = (fun.param_arity + fun.return_arity) * 8; | ||
870 | // sz alloc_const = add_constant(chunk, alloc_size); | ||
871 | // if (alloc_size > 0) { | ||
872 | // // EMIT_OP(OP_RESERVE, alloc_const, 0, 0, node, chunk); | ||
873 | // } | ||
874 | |||
875 | // // println("FUNCALL: unique name: %s", node->unique_name); | ||
876 | // // println("FUN: param_arity: %d", fun.param_arity); | ||
877 | // // println("FUN: return_arity: %d", fun.return_arity); | ||
846 | EMIT_OP(OP_CALL, fun.index, 0, 0, node, chunk); | 878 | EMIT_OP(OP_CALL, fun.index, 0, 0, node, chunk); |
847 | 879 | ||
848 | // Only one return parameter for now. | 880 | // Only one return parameter for now. |
849 | if (fun.return_arity > 0) { | 881 | if (fun.return_arity > 0) { |
850 | // Put the return data into a register | 882 | // Put the return data into a register |
851 | sz reg_dst = chunk->reg_idx++; | 883 | sz reg_dst = chunk->reg_idx++; |
852 | sz ret_offset = add_constant(chunk, -fun.return_arity); | 884 | EMIT_OP(OP_POP, reg_dst, 0, 0, node, chunk); |
853 | EMIT_OP(OP_SPVAL, reg_dst, ret_offset, 0, node, chunk); | ||
854 | EMIT_OP(OP_POP, alloc_const, 0, 0, node, chunk); | ||
855 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 885 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
856 | } | 886 | } |
857 | 887 | ||
858 | if (alloc_size > 0) { | 888 | // if (alloc_size > 0) { |
859 | EMIT_OP(OP_POP, alloc_const, 0, 0, node, chunk); | 889 | // // EMIT_OP(OP_POP, alloc_const, 0, 0, node, chunk); |
860 | } | 890 | // } |
861 | // TODO: epilogue (how much space do we actually need)? we need to remove | 891 | // TODO: epilogue (how much space do we actually need)? we need to remove |
862 | // the AR data here after using it. If we only have one return parameter we | 892 | // the AR data here after using it. If we only have one return parameter we |
863 | // can put it in a register but what if that's not actually the case? | 893 | // can put it in a register but what if that's not actually the case? |
@@ -887,8 +917,19 @@ compile_function(Chunk *chunk, Node *node) { | |||
887 | }; | 917 | }; |
888 | funcmap_insert(&chunk->funmap, func->name, fun, chunk->storage); | 918 | funcmap_insert(&chunk->funmap, func->name, fun, chunk->storage); |
889 | array_push(chunk->functions, func, chunk->storage); | 919 | array_push(chunk->functions, func, chunk->storage); |
890 | // TODO: put return values into memory. | 920 | |
891 | compile_expr(func, node->func_body); | 921 | // Put return values into memory. |
922 | CompResult res = compile_expr(func, node->func_body); | ||
923 | switch (res.type) { | ||
924 | case COMP_CONST: { | ||
925 | EMIT_OP(OP_PUTRETI, res.idx, 0, 0, node, func); | ||
926 | } break; | ||
927 | case COMP_REG: { | ||
928 | EMIT_OP(OP_PUTRET, res.idx, 0, 0, node, func); | ||
929 | } break; | ||
930 | default: break; | ||
931 | } | ||
932 | |||
892 | // TODO: handle captured locals/globals? | 933 | // TODO: handle captured locals/globals? |
893 | EMIT_OP(OP_RET, 0, 0, 0, node, func); | 934 | EMIT_OP(OP_RET, 0, 0, 0, node, func); |
894 | return (CompResult){.type = COMP_NIL}; | 935 | return (CompResult){.type = COMP_NIL}; |
@@ -1159,7 +1200,6 @@ disassemble_instruction(Instruction instruction) { | |||
1159 | case OP_LD16K: | 1200 | case OP_LD16K: |
1160 | case OP_LD32K: | 1201 | case OP_LD32K: |
1161 | case OP_LD64K: | 1202 | case OP_LD64K: |
1162 | case OP_SPVAL: | ||
1163 | println("%s r%d, c%d", op_str[instruction.op], instruction.dst, | 1203 | println("%s r%d, c%d", op_str[instruction.op], instruction.dst, |
1164 | instruction.a, instruction.b); | 1204 | instruction.a, instruction.b); |
1165 | break; | 1205 | break; |
@@ -1264,13 +1304,17 @@ disassemble_instruction(Instruction instruction) { | |||
1264 | case OP_PRINTS64: | 1304 | case OP_PRINTS64: |
1265 | case OP_PRINTF64: | 1305 | case OP_PRINTF64: |
1266 | case OP_PRINTSTR: | 1306 | case OP_PRINTSTR: |
1307 | case OP_PUSH: | ||
1308 | case OP_POP: | ||
1309 | case OP_PUTRET: | ||
1267 | println("%s r%d", op_str[instruction.op], instruction.dst, | 1310 | println("%s r%d", op_str[instruction.op], instruction.dst, |
1268 | instruction.a, instruction.b); | 1311 | instruction.a, instruction.b); |
1269 | break; | 1312 | break; |
1270 | case OP_PRINTS64I: | 1313 | case OP_PRINTS64I: |
1271 | case OP_PRINTF64I: | 1314 | case OP_PRINTF64I: |
1272 | case OP_RESERVE: | 1315 | case OP_RESERVE: |
1273 | case OP_POP: | 1316 | case OP_PUSHI: |
1317 | case OP_PUTRETI: | ||
1274 | println("%s c%d", op_str[instruction.op], instruction.dst, | 1318 | println("%s c%d", op_str[instruction.op], instruction.dst, |
1275 | instruction.a, instruction.b); | 1319 | instruction.a, instruction.b); |
1276 | break; | 1320 | break; |
@@ -11,6 +11,7 @@ typedef struct VM { | |||
11 | u8 stack[STACK_SIZE]; | 11 | u8 stack[STACK_SIZE]; |
12 | Instruction *ip; | 12 | Instruction *ip; |
13 | u8 *sp; | 13 | u8 *sp; |
14 | u64 *fp; | ||
14 | Constant *regs; | 15 | Constant *regs; |
15 | } VM; | 16 | } VM; |
16 | 17 | ||
@@ -21,6 +22,7 @@ vm_init(VM *vm, Chunk *chunk) { | |||
21 | assert(chunk->code); | 22 | assert(chunk->code); |
22 | vm->chunk = chunk; | 23 | vm->chunk = chunk; |
23 | vm->ip = vm->chunk->code; | 24 | vm->ip = vm->chunk->code; |
25 | vm->fp = (u64 *)vm->stack; | ||
24 | vm->sp = vm->stack + chunk->var_off; | 26 | vm->sp = vm->stack + chunk->var_off; |
25 | vm->regs = (Constant *)vm->sp; | 27 | vm->regs = (Constant *)vm->sp; |
26 | vm->sp += sizeof(Constant) * chunk->reg_idx; | 28 | vm->sp += sizeof(Constant) * chunk->reg_idx; |
@@ -266,49 +268,81 @@ vm_run(VM *vm) { | |||
266 | Str string = vm->chunk->strings[idx]; | 268 | Str string = vm->chunk->strings[idx]; |
267 | print("%s", string); | 269 | print("%s", string); |
268 | } break; | 270 | } break; |
271 | case OP_RESERVE: { | ||
272 | sz offset = vm->chunk->constants[instruction.dst].i; | ||
273 | vm->sp += offset; | ||
274 | } break; | ||
275 | case OP_PUSH: { | ||
276 | sz val = vm->regs[instruction.dst].i; | ||
277 | u64 *p = (u64 *)vm->sp; | ||
278 | *p = val; | ||
279 | vm->sp += sizeof(ptrsize); | ||
280 | } break; | ||
281 | case OP_PUSHI: { | ||
282 | sz val = vm->chunk->constants[instruction.dst].i; | ||
283 | u64 *p = (u64 *)vm->sp; | ||
284 | *p = val; | ||
285 | vm->sp += sizeof(ptrsize); | ||
286 | } break; | ||
287 | case OP_POP: { | ||
288 | vm->sp -= sizeof(ptrsize); | ||
289 | u64 *p = (u64 *)vm->sp; | ||
290 | vm->regs[instruction.dst].i = *p; | ||
291 | } break; | ||
292 | case OP_PUTRET: { | ||
293 | sz val = vm->regs[instruction.dst].i; | ||
294 | vm->fp[-1] = val; | ||
295 | } break; | ||
296 | case OP_PUTRETI: { | ||
297 | sz val = vm->chunk->constants[instruction.dst].i; | ||
298 | vm->fp[-1] = val; | ||
299 | } break; | ||
269 | case OP_CALL: { | 300 | case OP_CALL: { |
270 | u8 dst = instruction.dst; | 301 | u8 dst = instruction.dst; |
271 | Chunk *func = vm->chunk->functions[dst]; | 302 | Chunk *func = vm->chunk->functions[dst]; |
272 | 303 | ||
273 | // Store memory addresses we need to return to this function. | ||
274 | ptrsize chunk_addr = (ptrsize)vm->chunk; | 304 | ptrsize chunk_addr = (ptrsize)vm->chunk; |
275 | ptrsize ip_addr = (ptrsize)vm->ip; | 305 | ptrsize ip_addr = (ptrsize)vm->ip; |
276 | ptrsize reg_addr = (ptrsize)vm->regs; | 306 | ptrsize reg_addr = (ptrsize)vm->regs; |
277 | u64 *p = (u64 *)vm->sp; | 307 | ptrsize old_fp = (ptrsize)vm->fp; |
278 | p[0] = chunk_addr; | ||
279 | p[1] = ip_addr; | ||
280 | p[2] = reg_addr; | ||
281 | vm->sp += sizeof(ptrsize); | ||
282 | vm->sp += sizeof(ptrsize); | ||
283 | vm->sp += sizeof(ptrsize); | ||
284 | 308 | ||
285 | // Allocate space for the locals. | 309 | // Allocate space for the locals. |
286 | vm->sp += func->var_off; | 310 | vm->fp = (u64 *)vm->sp; |
311 | vm->sp += func->var_off; // FIXME: - func->n_params! | ||
287 | vm->chunk = func; | 312 | vm->chunk = func; |
288 | vm->ip = func->code; | 313 | vm->ip = func->code; |
289 | vm->regs = (Constant *)vm->sp; | 314 | vm->regs = (Constant *)vm->sp; |
290 | 315 | ||
291 | // Allocate registers. | 316 | // Allocate registers. |
292 | vm->sp += sizeof(Constant) * func->reg_idx; | 317 | vm->sp += sizeof(Constant) * func->reg_idx; |
318 | |||
319 | // Store memory addresses we need to return to this function. | ||
320 | u64 *p = (u64 *)vm->sp; | ||
321 | p[0] = chunk_addr; | ||
322 | p[1] = ip_addr; | ||
323 | p[2] = reg_addr; | ||
324 | p[3] = old_fp; | ||
325 | vm->sp += sizeof(ptrsize) * 4; | ||
293 | } break; | 326 | } break; |
294 | case OP_RET: { | 327 | case OP_RET: { |
295 | // Deallocate locals. | 328 | u64 *p = (u64 *)vm->sp; |
296 | vm->sp -= vm->chunk->var_off; | 329 | ptrsize chunk_addr = p[-4]; |
330 | ptrsize ip_addr = p[-3]; | ||
331 | ptrsize reg_addr = p[-2]; | ||
332 | ptrsize old_fp = p[-1]; | ||
333 | vm->sp -= sizeof(ptrsize) * 4; | ||
297 | 334 | ||
298 | // Deallocate registers. | 335 | // Deallocate registers. |
299 | vm->sp -= sizeof(Constant) * vm->chunk->reg_idx; | 336 | vm->sp -= sizeof(Constant) * vm->chunk->reg_idx; |
300 | 337 | ||
338 | // Deallocate locals. | ||
339 | vm->sp -= vm->chunk->var_off; | ||
340 | |||
301 | // Restore previous activation record. | 341 | // Restore previous activation record. |
302 | u64 *p = (u64 *)vm->sp; | ||
303 | ptrsize chunk_addr = p[-3]; | ||
304 | ptrsize ip_addr = p[-2]; | ||
305 | ptrsize reg_addr = p[-1]; | ||
306 | vm->sp -= sizeof(ptrsize); | ||
307 | vm->sp -= sizeof(ptrsize); | ||
308 | vm->sp -= sizeof(ptrsize); | ||
309 | vm->regs = (Constant *)reg_addr; | 342 | vm->regs = (Constant *)reg_addr; |
310 | vm->ip = (Instruction *)ip_addr; | 343 | vm->ip = (Instruction *)ip_addr; |
311 | vm->chunk = (Chunk *)chunk_addr; | 344 | vm->chunk = (Chunk *)chunk_addr; |
345 | vm->fp = (u64 *)old_fp; | ||
312 | } break; | 346 | } break; |
313 | case OP_HALT: { | 347 | case OP_HALT: { |
314 | println("VM halt..."); | 348 | println("VM halt..."); |
diff --git a/tests/compilation.bad b/tests/compilation.bad index c0da031..ea467d2 100644 --- a/tests/compilation.bad +++ b/tests/compilation.bad | |||
@@ -1,15 +1,21 @@ | |||
1 | fun adder(a: int): int { | ||
2 | ; a + 1 | ||
3 | 4 | ||
4 | } | ||
5 | adder(2) | ||
1 | ; let a = 1 | 6 | ; let a = 1 |
2 | ; let b = 8 | 7 | ; let b = 8 |
3 | 8 | ||
4 | ; a + b | 9 | ; a + b |
5 | ; 1 + 2 | 10 | ; 1 + 2 |
6 | fun greet(): nil { | 11 | ; fun greet(name: str): nil { |
7 | println("hello world") | 12 | ; fun greet(name: str): nil { |
8 | ; 7 | 13 | ; println("hello ") |
9 | } | 14 | ; } |
10 | 15 | ||
11 | greet() | 16 | ; greet("alex") |
12 | greet() | 17 | ; greet("ding") |
18 | ; greet() | ||
13 | ; if true { | 19 | ; if true { |
14 | ; 1 | 20 | ; 1 |
15 | ; nil | 21 | ; nil |