diff options
author | Bad Diode <bd@badd10de.dev> | 2024-06-19 12:51:51 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-06-19 12:51:51 +0200 |
commit | aeca71546bb8c94d1aeae6fe3e15835a2541317d (patch) | |
tree | d3bc6a6d0b4943ded12603225f39ea333fff5c24 | |
parent | 06220393b4d3e9bdd227f333650883971e37b9f9 (diff) | |
download | bdl-aeca71546bb8c94d1aeae6fe3e15835a2541317d.tar.gz bdl-aeca71546bb8c94d1aeae6fe3e15835a2541317d.zip |
Implement the worlds worst register machine
-rw-r--r-- | src/badlib.h | 14 | ||||
-rw-r--r-- | src/main.c | 127 |
2 files changed, 109 insertions, 32 deletions
diff --git a/src/badlib.h b/src/badlib.h index 25bb92b..522e4bb 100644 --- a/src/badlib.h +++ b/src/badlib.h | |||
@@ -920,9 +920,12 @@ typedef struct ArrayHeader { | |||
920 | #define array_cap(ARR) ((ARR) ? array_head(ARR)->cap : 0) | 920 | #define array_cap(ARR) ((ARR) ? array_head(ARR)->cap : 0) |
921 | 921 | ||
922 | // Initialize a dynamic array ARR with N elements. The initialization doesn't | 922 | // Initialize a dynamic array ARR with N elements. The initialization doesn't |
923 | // zero out the data, so thread carefully.. | 923 | // zero out the data, so thread carefully. Use array_zero instead if that's what |
924 | // you need. | ||
924 | #define array_init(ARR, N, ARENA) \ | 925 | #define array_init(ARR, N, ARENA) \ |
925 | ((ARR) = _array_reserve(N, sizeof(*(ARR)), (ARENA))) | 926 | ((ARR) = _array_reserve(N, sizeof(*(ARR)), (ARENA))) |
927 | #define array_zero(ARR, N, ARENA) \ | ||
928 | ((ARR) = _array_reserve_zero(N, sizeof(*(ARR)), (ARENA))) | ||
926 | 929 | ||
927 | // Push a given element T to the dynamic array ARR. | 930 | // Push a given element T to the dynamic array ARR. |
928 | #define array_push(ARR, T, ARENA) \ | 931 | #define array_push(ARR, T, ARENA) \ |
@@ -953,6 +956,15 @@ _array_reserve(sz num_elem, sz type_size, Arena *a) { | |||
953 | } | 956 | } |
954 | 957 | ||
955 | static inline void * | 958 | static inline void * |
959 | _array_reserve_zero(sz num_elem, sz type_size, Arena *a) { | ||
960 | u8 *p = arena_calloc(num_elem * type_size + sizeof(ArrayHeader), a); | ||
961 | p += sizeof(ArrayHeader); | ||
962 | array_head(p)->size = 0; | ||
963 | array_head(p)->cap = num_elem; | ||
964 | return p; | ||
965 | } | ||
966 | |||
967 | static inline void * | ||
956 | _array_maybe_grow(void *arr, sz type_size, Arena *a) { | 968 | _array_maybe_grow(void *arr, sz type_size, Arena *a) { |
957 | if (!arr) { | 969 | if (!arr) { |
958 | arr = _array_reserve(0, 0, a); | 970 | arr = _array_reserve(0, 0, a); |
@@ -22,9 +22,9 @@ init(void) { | |||
22 | } | 22 | } |
23 | 23 | ||
24 | typedef struct Instruction { | 24 | typedef struct Instruction { |
25 | u8 dst; | ||
25 | u8 a; | 26 | u8 a; |
26 | u8 b; | 27 | u8 b; |
27 | u8 c; | ||
28 | u8 op; | 28 | u8 op; |
29 | } Instruction; | 29 | } Instruction; |
30 | 30 | ||
@@ -44,20 +44,29 @@ typedef struct Chunk { | |||
44 | 44 | ||
45 | typedef enum OpCode { | 45 | typedef enum OpCode { |
46 | OP_HALT, | 46 | OP_HALT, |
47 | OP_ADD_INT, | 47 | OP_INT_ADD, |
48 | OP_INT_SUB, | ||
49 | OP_INT_MUL, | ||
50 | OP_INT_DIV, | ||
51 | OP_INT_MOD, | ||
48 | } OpCode; | 52 | } OpCode; |
49 | 53 | ||
50 | Str op_str[] = { | 54 | Str op_str[] = { |
51 | [OP_HALT] = cstr("HALT "), | 55 | [OP_HALT] = cstr("HALT "), [OP_INT_ADD] = cstr("ADD "), |
52 | [OP_ADD_INT] = cstr("ADD "), | 56 | [OP_INT_SUB] = cstr("SUB "), [OP_INT_MUL] = cstr("MUL "), |
57 | [OP_INT_DIV] = cstr("DIV "), [OP_INT_MOD] = cstr("MOD "), | ||
53 | }; | 58 | }; |
54 | 59 | ||
55 | void | 60 | void |
56 | disassemble_instruction(Instruction instruction) { | 61 | disassemble_instruction(Instruction instruction) { |
57 | switch (instruction.op) { | 62 | switch (instruction.op) { |
58 | case OP_ADD_INT: | 63 | case OP_INT_SUB: |
59 | println("%s %d %d %d", op_str[instruction.op], instruction.a, | 64 | case OP_INT_MUL: |
60 | instruction.b, instruction.c); | 65 | case OP_INT_DIV: |
66 | case OP_INT_MOD: | ||
67 | case OP_INT_ADD: | ||
68 | println("%s %d %d %d", op_str[instruction.op], instruction.dst, | ||
69 | instruction.a, instruction.b); | ||
61 | break; | 70 | break; |
62 | case OP_HALT: println("%s", op_str[instruction.op]); break; | 71 | case OP_HALT: println("%s", op_str[instruction.op]); break; |
63 | default: println("Unknown opcode %d", instruction.op); break; | 72 | default: println("Unknown opcode %d", instruction.op); break; |
@@ -79,9 +88,10 @@ disassemble_chunk(Chunk chunk) { | |||
79 | } | 88 | } |
80 | } | 89 | } |
81 | 90 | ||
91 | #define N_CONST 256 | ||
82 | typedef struct VM { | 92 | typedef struct VM { |
83 | Chunk *chunk; | 93 | Chunk *chunk; |
84 | Constant regs[256]; | 94 | Constant regs[N_CONST]; |
85 | Instruction *ip; | 95 | Instruction *ip; |
86 | } VM; | 96 | } VM; |
87 | 97 | ||
@@ -89,8 +99,9 @@ void | |||
89 | vm_init(VM *vm, Chunk *chunk) { | 99 | vm_init(VM *vm, Chunk *chunk) { |
90 | assert(vm); | 100 | assert(vm); |
91 | assert(chunk); | 101 | assert(chunk); |
102 | assert(chunk->code); | ||
92 | vm->chunk = chunk; | 103 | vm->chunk = chunk; |
93 | for (sz i = 0; i < array_size(chunk->constants); i++) { | 104 | for (sz i = 0; i < N_CONST; i++) { |
94 | vm->regs[i] = chunk->constants[i]; | 105 | vm->regs[i] = chunk->constants[i]; |
95 | } | 106 | } |
96 | vm->ip = vm->chunk->code; | 107 | vm->ip = vm->chunk->code; |
@@ -98,6 +109,9 @@ vm_init(VM *vm, Chunk *chunk) { | |||
98 | 109 | ||
99 | void | 110 | void |
100 | vm_run(VM *vm) { | 111 | vm_run(VM *vm) { |
112 | assert(vm); | ||
113 | assert(vm->chunk); | ||
114 | assert(vm->ip); | ||
101 | println("VM running..."); | 115 | println("VM running..."); |
102 | while (true) { | 116 | while (true) { |
103 | Instruction instruction = *vm->ip++; | 117 | Instruction instruction = *vm->ip++; |
@@ -106,19 +120,79 @@ vm_run(VM *vm) { | |||
106 | disassemble_instruction(instruction); | 120 | disassemble_instruction(instruction); |
107 | #endif | 121 | #endif |
108 | switch (instruction.op) { | 122 | switch (instruction.op) { |
109 | case OP_ADD_INT: { | 123 | // TODO: Other numeric types |
110 | u8 dst = instruction.a; | 124 | case OP_INT_ADD: { |
111 | u8 src_a = instruction.b; | 125 | u8 dst = instruction.dst; |
112 | u8 src_b = instruction.c; | 126 | u8 src_a = instruction.a; |
113 | vm->regs[dst].u = vm->regs[src_a].u + vm->regs[src_b].u; | 127 | u8 src_b = instruction.b; |
128 | vm->regs[dst].i = vm->regs[src_a].i + vm->regs[src_b].i; | ||
129 | } break; | ||
130 | case OP_INT_SUB: { | ||
131 | u8 dst = instruction.dst; | ||
132 | u8 src_a = instruction.a; | ||
133 | u8 src_b = instruction.b; | ||
134 | vm->regs[dst].i = vm->regs[src_a].i - vm->regs[src_b].i; | ||
135 | } break; | ||
136 | case OP_INT_MUL: { | ||
137 | u8 dst = instruction.dst; | ||
138 | u8 src_a = instruction.a; | ||
139 | u8 src_b = instruction.b; | ||
140 | vm->regs[dst].i = vm->regs[src_a].i * vm->regs[src_b].i; | ||
141 | } break; | ||
142 | case OP_INT_DIV: { | ||
143 | u8 dst = instruction.dst; | ||
144 | u8 src_a = instruction.a; | ||
145 | u8 src_b = instruction.b; | ||
146 | vm->regs[dst].i = vm->regs[src_a].i / vm->regs[src_b].i; | ||
147 | } break; | ||
148 | case OP_INT_MOD: { | ||
149 | u8 dst = instruction.dst; | ||
150 | u8 src_a = instruction.a; | ||
151 | u8 src_b = instruction.b; | ||
152 | vm->regs[dst].i = vm->regs[src_a].i % vm->regs[src_b].i; | ||
114 | } break; | 153 | } break; |
115 | case OP_HALT: { | 154 | case OP_HALT: { |
116 | return; | 155 | return; |
117 | } | 156 | } |
157 | default: { | ||
158 | eprintln("unimplemented OP code: %d", instruction.op); | ||
159 | return; | ||
160 | } | ||
118 | } | 161 | } |
119 | } | 162 | } |
120 | } | 163 | } |
121 | 164 | ||
165 | sz reg_idx = 0; | ||
166 | sz compile_expr(Chunk *chunk, Node *node, Arena *a); | ||
167 | |||
168 | sz | ||
169 | compile_binary(OpCode op, Chunk *chunk, Node *node, Arena *a) { | ||
170 | sz reg_a = compile_expr(chunk, node->left, a); | ||
171 | sz reg_b = compile_expr(chunk, node->right, a); | ||
172 | sz reg_dst = reg_idx++; | ||
173 | Instruction inst = | ||
174 | (Instruction){.op = op, .dst = reg_dst, .a = reg_a, .b = reg_b}; | ||
175 | array_push(chunk->code, inst, a); | ||
176 | return reg_dst; | ||
177 | } | ||
178 | |||
179 | sz | ||
180 | compile_expr(Chunk *chunk, Node *node, Arena *a) { | ||
181 | switch (node->kind) { | ||
182 | case NODE_ADD: return compile_binary(OP_INT_ADD, chunk, node, a); break; | ||
183 | case NODE_SUB: return compile_binary(OP_INT_SUB, chunk, node, a); break; | ||
184 | case NODE_MUL: return compile_binary(OP_INT_MUL, chunk, node, a); break; | ||
185 | case NODE_DIV: return compile_binary(OP_INT_DIV, chunk, node, a); break; | ||
186 | case NODE_MOD: return compile_binary(OP_INT_MOD, chunk, node, a); break; | ||
187 | case NODE_NUM_INT: { | ||
188 | chunk->constants[reg_idx] = (Constant){.i = node->value.i}; | ||
189 | return reg_idx++; | ||
190 | } break; | ||
191 | default: break; | ||
192 | } | ||
193 | return -1; | ||
194 | } | ||
195 | |||
122 | void | 196 | void |
123 | process_file(Str path) { | 197 | process_file(Str path) { |
124 | Arena lexer_arena = arena_create(LEXER_MEM, os_allocator); | 198 | Arena lexer_arena = arena_create(LEXER_MEM, os_allocator); |
@@ -181,30 +255,21 @@ process_file(Str path) { | |||
181 | } | 255 | } |
182 | // TODO: Semantic analysis. | 256 | // TODO: Semantic analysis. |
183 | // TODO: Type checking. | 257 | // TODO: Type checking. |
184 | // TODO: Compile roots. | 258 | |
259 | // Compile roots. | ||
260 | Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator); | ||
261 | Chunk chunk = {.file_name = path}; | ||
262 | array_zero(chunk.constants, 256, &bytecode_arena); | ||
263 | array_zero(chunk.code, 0xffff, &bytecode_arena); | ||
185 | sz n_roots = array_size(parser.nodes); | 264 | sz n_roots = array_size(parser.nodes); |
186 | for (sz i = 0; i < n_roots; i++) { | 265 | for (sz i = 0; i < n_roots; i++) { |
187 | // The parser stores the root nodes as a stack. | 266 | // The parser stores the root nodes as a stack. |
188 | Node *root = parser.nodes[i]; | 267 | Node *root = parser.nodes[i]; |
189 | // compile_expr(Node *root) | 268 | compile_expr(&chunk, root, &bytecode_arena); |
190 | (void)root; | ||
191 | } | 269 | } |
192 | |||
193 | // Run bytecode on VM. | ||
194 | Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator); | ||
195 | Chunk chunk = {.file_name = path}; | ||
196 | array_init(chunk.constants, 256, &bytecode_arena); | ||
197 | array_init(chunk.code, 0xffff, &bytecode_arena); | ||
198 | |||
199 | // FIXME: Some debugging instructions for now. | ||
200 | array_push(chunk.constants, (Constant){.u = 1}, &bytecode_arena); | ||
201 | array_push(chunk.constants, (Constant){.u = 2}, &bytecode_arena); | ||
202 | Instruction add = (Instruction){.op = OP_ADD_INT, .a = 2, .b = 0, .c = 1}; | ||
203 | array_push(chunk.code, add, &bytecode_arena); | ||
204 | array_push(chunk.code, (Instruction){.op = OP_HALT}, &bytecode_arena); | 270 | array_push(chunk.code, (Instruction){.op = OP_HALT}, &bytecode_arena); |
205 | disassemble_chunk(chunk); | ||
206 | 271 | ||
207 | // Testing VM execution here. | 272 | // Run bytecode on VM. |
208 | VM vm = {0}; | 273 | VM vm = {0}; |
209 | vm_init(&vm, &chunk); | 274 | vm_init(&vm, &chunk); |
210 | println("VM REGISTERS BEFORE:\n%{Mem}", | 275 | println("VM REGISTERS BEFORE:\n%{Mem}", |