1#include "vm.h"
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6
7void bytecode_init(Bytecode *bc) {
8 bc->words = NULL;
9 bc->count = 0;
10 bc->capacity = 0;
11}
12
13void bytecode_free(Bytecode *bc) {
14 free(bc->words);
15 bc->words = NULL;
16 bc->count = 0;
17 bc->capacity = 0;
18}
19
20VmError bytecode_emit(Bytecode *bc, int64_t word) {
21 if (bc->count == bc->capacity) {
22 size_t new_capacity = bc->capacity == 0 ? 64 : bc->capacity * 2;
23 int64_t *new_words = (int64_t *)realloc(bc->words, new_capacity * sizeof(*new_words));
24 if (!new_words) {
25 return VM_ERR_OUT_OF_MEMORY;
26 }
27 bc->words = new_words;
28 bc->capacity = new_capacity;
29 }
30 bc->words[bc->count++] = word;
31 return VM_OK;
32}
33
34static int default_output(int64_t value, void *userdata) {
35 (void)userdata;
36 return printf("%lld\n", (long long)value) < 0 ? -1 : 0;
37}
38
39void vm_init(VM *vm, const Bytecode *code) {
40 memset(vm, 0, sizeof(*vm));
41 vm->code = code;
42 vm->output = default_output;
43 vm->frame_count = 1;
44}
45
46void vm_set_output(VM *vm, VmOutputFn output, void *userdata) {
47 vm->output = output ? output : default_output;
48 vm->output_userdata = userdata;
49}
50
51static VmError push(VM *vm, int64_t value) {
52 if (vm->sp >= VM_STACK_MAX) {
53 return VM_ERR_STACK_OVERFLOW;
54 }
55 vm->stack[vm->sp++] = value;
56 return VM_OK;
57}
58
59static VmError pop(VM *vm, int64_t *out) {
60 if (vm->sp == 0) {
61 return VM_ERR_STACK_UNDERFLOW;
62 }
63 *out = vm->stack[--vm->sp];
64 return VM_OK;
65}
66
67static VmError read_operand(VM *vm, int64_t *out) {
68 if (vm->ip >= vm->code->count) {
69 return VM_ERR_TRUNCATED_INSTRUCTION;
70 }
71 *out = vm->code->words[vm->ip++];
72 return VM_OK;
73}
74
75static int is_valid_opcode(int64_t opcode) {
76 return opcode >= OP_PUSH && opcode <= OP_HALT;
77}
78
79static VmError validate_target(const VM *vm, int64_t target) {
80 if (target < 0 || (uint64_t)target >= vm->code->count) {
81 return VM_ERR_BAD_JUMP;
82 }
83 if (!is_valid_opcode(vm->code->words[target])) {
84 return VM_ERR_BAD_JUMP;
85 }
86 return VM_OK;
87}
88
89VmError vm_run(VM *vm) {
90 if (!vm->code) {
91 vm->last_error = VM_ERR_BAD_OPERAND;
92 return vm->last_error;
93 }
94
95 VmError err = VM_OK;
96 for (;;) {
97 err = VM_OK;
98 if (vm->ip >= vm->code->count) {
99 vm->last_error = VM_ERR_NO_HALT;
100 return vm->last_error;
101 }
102
103 int64_t raw_opcode = vm->code->words[vm->ip++];
104 if (!is_valid_opcode(raw_opcode)) {
105 vm->last_error = VM_ERR_BAD_OPCODE;
106 return vm->last_error;
107 }
108
109 VmOpcode opcode = (VmOpcode)raw_opcode;
110 int64_t a = 0;
111 int64_t b = 0;
112 int64_t operand = 0;
113
114 switch (opcode) {
115 case OP_PUSH:
116 if ((err = read_operand(vm, &operand)) != VM_OK) goto fail;
117 if ((err = push(vm, operand)) != VM_OK) goto fail;
118 break;
119
120 case OP_POP:
121 if ((err = pop(vm, &a)) != VM_OK) goto fail;
122 break;
123
124 case OP_DUP:
125 if (vm->sp == 0) {
126 err = VM_ERR_STACK_UNDERFLOW;
127 goto fail;
128 }
129 if ((err = push(vm, vm->stack[vm->sp - 1])) != VM_OK) goto fail;
130 break;
131
132 case OP_SWAP:
133 if (vm->sp < 2) {
134 err = VM_ERR_STACK_UNDERFLOW;
135 goto fail;
136 }
137 a = vm->stack[vm->sp - 1];
138 vm->stack[vm->sp - 1] = vm->stack[vm->sp - 2];
139 vm->stack[vm->sp - 2] = a;
140 break;
141
142 case OP_ADD:
143 if ((err = pop(vm, &b)) != VM_OK) goto fail;
144 if ((err = pop(vm, &a)) != VM_OK) goto fail;
145 if ((err = push(vm, a + b)) != VM_OK) goto fail;
146 break;
147
148 case OP_SUB:
149 if ((err = pop(vm, &b)) != VM_OK) goto fail;
150 if ((err = pop(vm, &a)) != VM_OK) goto fail;
151 if ((err = push(vm, a - b)) != VM_OK) goto fail;
152 break;
153
154 case OP_MUL:
155 if ((err = pop(vm, &b)) != VM_OK) goto fail;
156 if ((err = pop(vm, &a)) != VM_OK) goto fail;
157 if ((err = push(vm, a * b)) != VM_OK) goto fail;
158 break;
159
160 case OP_DIV:
161 if ((err = pop(vm, &b)) != VM_OK) goto fail;
162 if ((err = pop(vm, &a)) != VM_OK) goto fail;
163 if (b == 0) {
164 err = VM_ERR_DIVISION_BY_ZERO;
165 goto fail;
166 }
167 if ((err = push(vm, a / b)) != VM_OK) goto fail;
168 break;
169
170 case OP_MOD:
171 if ((err = pop(vm, &b)) != VM_OK) goto fail;
172 if ((err = pop(vm, &a)) != VM_OK) goto fail;
173 if (b == 0) {
174 err = VM_ERR_DIVISION_BY_ZERO;
175 goto fail;
176 }
177 if ((err = push(vm, a % b)) != VM_OK) goto fail;
178 break;
179
180 case OP_NEG:
181 if ((err = pop(vm, &a)) != VM_OK) goto fail;
182 if ((err = push(vm, -a)) != VM_OK) goto fail;
183 break;
184
185 case OP_EQ:
186 if ((err = pop(vm, &b)) != VM_OK) goto fail;
187 if ((err = pop(vm, &a)) != VM_OK) goto fail;
188 if ((err = push(vm, a == b ? 1 : 0)) != VM_OK) goto fail;
189 break;
190
191 case OP_LT:
192 if ((err = pop(vm, &b)) != VM_OK) goto fail;
193 if ((err = pop(vm, &a)) != VM_OK) goto fail;
194 if ((err = push(vm, a < b ? 1 : 0)) != VM_OK) goto fail;
195 break;
196
197 case OP_GT:
198 if ((err = pop(vm, &b)) != VM_OK) goto fail;
199 if ((err = pop(vm, &a)) != VM_OK) goto fail;
200 if ((err = push(vm, a > b ? 1 : 0)) != VM_OK) goto fail;
201 break;
202
203 case OP_JMP:
204 if ((err = read_operand(vm, &operand)) != VM_OK) goto fail;
205 if ((err = validate_target(vm, operand)) != VM_OK) goto fail;
206 vm->ip = (size_t)operand;
207 break;
208
209 case OP_JZ:
210 if ((err = read_operand(vm, &operand)) != VM_OK) goto fail;
211 if ((err = pop(vm, &a)) != VM_OK) goto fail;
212 if (a == 0) {
213 if ((err = validate_target(vm, operand)) != VM_OK) goto fail;
214 vm->ip = (size_t)operand;
215 }
216 break;
217
218 case OP_JNZ:
219 if ((err = read_operand(vm, &operand)) != VM_OK) goto fail;
220 if ((err = pop(vm, &a)) != VM_OK) goto fail;
221 if (a != 0) {
222 if ((err = validate_target(vm, operand)) != VM_OK) goto fail;
223 vm->ip = (size_t)operand;
224 }
225 break;
226
227 case OP_CALL:
228 if ((err = read_operand(vm, &operand)) != VM_OK) goto fail;
229 if ((err = validate_target(vm, operand)) != VM_OK) goto fail;
230 if (vm->frame_count >= VM_CALL_STACK_MAX) {
231 err = VM_ERR_CALL_OVERFLOW;
232 goto fail;
233 }
234 vm->frames[vm->frame_count].return_ip = vm->ip;
235 memset(vm->frames[vm->frame_count].locals, 0, sizeof(vm->frames[vm->frame_count].locals));
236 vm->frame_count++;
237 vm->ip = (size_t)operand;
238 break;
239
240 case OP_RET:
241 if (vm->frame_count <= 1) {
242 err = VM_ERR_CALL_UNDERFLOW;
243 goto fail;
244 }
245 vm->frame_count--;
246 vm->ip = vm->frames[vm->frame_count].return_ip;
247 break;
248
249 case OP_LOAD:
250 if ((err = read_operand(vm, &operand)) != VM_OK) goto fail;
251 if (operand < 0 || operand >= VM_LOCALS_PER_FRAME) {
252 err = VM_ERR_BAD_LOCAL;
253 goto fail;
254 }
255 if ((err = push(vm, vm->frames[vm->frame_count - 1].locals[operand])) != VM_OK) goto fail;
256 break;
257
258 case OP_STORE:
259 if ((err = read_operand(vm, &operand)) != VM_OK) goto fail;
260 if (operand < 0 || operand >= VM_LOCALS_PER_FRAME) {
261 err = VM_ERR_BAD_LOCAL;
262 goto fail;
263 }
264 if ((err = pop(vm, &a)) != VM_OK) goto fail;
265 vm->frames[vm->frame_count - 1].locals[operand] = a;
266 break;
267
268 case OP_PRINT:
269 if ((err = pop(vm, &a)) != VM_OK) goto fail;
270 if (vm->output(a, vm->output_userdata) != 0) {
271 err = VM_ERR_OUTPUT;
272 goto fail;
273 }
274 break;
275
276 case OP_HALT:
277 vm->last_error = VM_OK;
278 return VM_OK;
279 }
280 }
281
282fail:
283 vm->last_error = err;
284 return err;
285}
286
287const char *vm_error_string(VmError error) {
288 switch (error) {
289 case VM_OK: return "ok";
290 case VM_ERR_STACK_UNDERFLOW: return "stack underflow";
291 case VM_ERR_STACK_OVERFLOW: return "stack overflow";
292 case VM_ERR_CALL_UNDERFLOW: return "call stack underflow";
293 case VM_ERR_CALL_OVERFLOW: return "call stack overflow";
294 case VM_ERR_DIVISION_BY_ZERO: return "division by zero";
295 case VM_ERR_BAD_OPCODE: return "bad opcode";
296 case VM_ERR_BAD_OPERAND: return "bad operand";
297 case VM_ERR_BAD_LOCAL: return "bad local";
298 case VM_ERR_BAD_JUMP: return "out-of-range jump";
299 case VM_ERR_TRUNCATED_INSTRUCTION: return "truncated instruction";
300 case VM_ERR_NO_HALT: return "program ended without halt";
301 case VM_ERR_OUT_OF_MEMORY: return "out of memory";
302 case VM_ERR_OUTPUT: return "output error";
303 default: return "unknown error";
304 }
305}
306
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.