vm.zig (12226B)
1 const std = @import("std"); 2 const c = @import("chunk.zig"); 3 const v = @import("value.zig"); 4 5 const vmlog = std.log.scoped(.vm); 6 7 // TODO: efficient stack implementation at some point ? 8 const Stack = std.ArrayList(*v.Value); 9 10 const VMError = error { 11 UndefinedSymbol, 12 InvalidType, 13 OutOfMemory, 14 NegativeIntoUnsigned, 15 TargetTooSmall, 16 NativeError, 17 } || std.fs.File.WriteError; 18 19 const CallFrame = struct { 20 ip: []const c.Op, 21 offset: usize, 22 func: *const v.Function, 23 }; 24 25 const CallStack = std.ArrayList(CallFrame); 26 27 pub const VM = struct { 28 const Self = @This(); 29 30 stack: Stack, 31 all: std.mem.Allocator, 32 call_stack: CallStack, 33 34 // Global variables 35 globals: std.StringHashMap(*v.Value), 36 37 pub fn init(all: std.mem.Allocator) !Self { 38 return Self{ 39 .stack = try Stack.initCapacity(all, 256), 40 .all = all, 41 .globals = std.StringHashMap(*v.Value).init(all), 42 .call_stack = CallStack.init(all), 43 }; 44 } 45 46 pub fn setupCore(self: *Self, comptime funcs: []const v.NativeFunction) !void { 47 inline for (funcs) |func| { 48 try self.globals.put(func.name, try v.Value.newNative(self.all, func)); 49 } 50 } 51 52 fn push(self: *Self, val: *v.Value) !void { 53 try self.stack.append(val); 54 } 55 56 fn pop(self: *Self) *v.Value { 57 return self.stack.pop(); 58 } 59 60 fn peek(self: *Self) *v.Value { 61 return self.stack.getLast(); 62 } 63 64 fn currentFrame(self: *Self) *CallFrame { 65 return &self.call_stack.items[self.call_stack.items.len - 1]; 66 } 67 68 pub fn interpret(self: *Self, func: *const v.Function) VMError!*v.Value { 69 std.debug.assert(self.call_stack.items.len == 0); 70 try self.call_stack.append(.{ 71 .offset = 0, 72 .func = func, 73 .ip = func.code.instrs.items, 74 }); 75 76 const start_time = std.time.microTimestamp(); 77 const res = self.run(); 78 const end_time = std.time.microTimestamp(); 79 vmlog.info("Eval in {} us", .{end_time - start_time}); 80 81 return res; 82 } 83 84 fn advanceIp(self: *Self) void { 85 self.currentFrame().ip = self.currentFrame().ip[1..]; 86 } 87 88 fn doPopScope(self: *Self, offset: u16, amount: u16) !void { 89 var valbuf: [255]*v.Value = undefined; 90 91 for (0..offset) |idx| { 92 valbuf[idx] = self.pop(); 93 } 94 95 for (0..amount) |_| { 96 self.pop().unref(); 97 } 98 99 100 for (0..offset) |idx| { 101 try self.push(valbuf[offset - idx - 1]); 102 } 103 } 104 105 fn run(self: *Self) !*v.Value { 106 while (true) { 107 const cframe = self.currentFrame(); 108 const instr = cframe.ip[0]; 109 const cchunk = cframe.func.code; 110 111 if (comptime std.log.logEnabled(.debug, .vm)) { 112 // TODO: proper logging here 113 for (self.stack.items) |val| { 114 std.debug.print("[{}] ", .{val.formatter()}); 115 } 116 std.debug.print("\n", .{}); 117 cchunk.disassembleInstr(instr); 118 } 119 120 switch (instr) { 121 .constant => |vindex| { 122 const val = cchunk.values.items[vindex]; 123 try self.push(val.ref()); 124 self.advanceIp(); 125 }, 126 127 .local => |vindex| { 128 const val = self.stack.items[vindex + cframe.offset]; 129 try self.push(val.ref()); 130 self.advanceIp(); 131 }, 132 133 .get_global => |vindex| { 134 const sym = cchunk.values.items[vindex]; 135 if (!sym.isA(.symbol)) { 136 return VMError.InvalidType; 137 } 138 139 vmlog.debug("Getting {s}", .{sym.val.symbol}); 140 141 if (self.globals.get(sym.val.symbol)) |found| { 142 try self.push(found.ref()); 143 } else { 144 return VMError.UndefinedSymbol; 145 } 146 self.advanceIp(); 147 }, 148 149 .print => { 150 const val = self.stack.getLast(); 151 152 var stdout = std.io.getStdOut().writer(); 153 154 try stdout.print("{}\n", .{val.formatter()}); 155 156 self.advanceIp(); 157 }, 158 159 .define_global => |index| { 160 const sym = cchunk.values.items[index]; 161 const val = self.pop(); 162 163 std.debug.assert(sym.isA(.symbol)); 164 165 vmlog.debug("Defining {s}", .{sym.val.symbol}); 166 if (try self.globals.fetchPut(sym.val.symbol, val)) |old| { 167 old.value.unref(); 168 } 169 self.advanceIp(); 170 }, 171 172 .jumpfalse => |offset| { 173 const val = self.pop(); 174 defer val.unref(); 175 176 if (val.isFalsy()) { 177 cframe.ip = cchunk.instrs.items[offset..]; 178 } else { 179 self.advanceIp(); 180 } 181 }, 182 .jump => |offset| { 183 cframe.ip = cchunk.instrs.items[offset..]; 184 }, 185 186 .call => |arity| { 187 const funcval = self.pop(); 188 defer funcval.unref(); 189 190 switch (funcval.val) { 191 .function => |func| { 192 std.debug.assert(arity == func.arity); 193 194 const offset = self.stack.items.len - arity; 195 196 const frame: CallFrame = .{.ip = func.code.instrs.items, .offset = offset, .func = &funcval.val.function}; 197 198 self.advanceIp(); 199 try self.call_stack.append(frame); 200 }, 201 .native => |n| { 202 std.debug.assert(arity == n.arity); 203 204 const offset = self.stack.items.len - arity; 205 var new = n.code(self.stack.items[offset..], self.all) catch return VMError.NativeError; 206 errdefer new.unref(); 207 208 self.advanceIp(); 209 210 try self.doPopScope(0, n.arity); 211 212 try self.push(new); 213 }, 214 else => return VMError.InvalidType, 215 } 216 }, 217 218 .@"return" => { 219 const frame = self.call_stack.pop(); 220 221 if (self.call_stack.items.len == 0) { 222 // XXX: no unref because we return 223 if (self.stack.items.len > 0) { 224 return self.pop(); 225 } else { 226 return try v.Value.newNil(self.all); 227 } 228 } else { 229 try self.doPopScope(1, frame.func.arity); 230 } 231 }, 232 233 .pop => pop: { 234 self.advanceIp(); 235 break :pop self.pop().unref(); 236 }, 237 238 .popscope => |amount| { 239 try self.doPopScope(amount.offset, amount.scope); 240 self.advanceIp(); 241 }, 242 243 .negate => { 244 var val = self.pop(); 245 defer val.unref(); 246 247 std.debug.assert(val.isA(.number)); 248 249 var n = try v.Int.cloneWithDifferentAllocator(val.val.number, self.all); 250 n.negate(); 251 252 var new = try v.Value.newInt(self.all, n); 253 errdefer new.unref(); 254 255 try self.push(new); 256 self.advanceIp(); 257 }, 258 259 .cons => { 260 var head = self.pop(); 261 defer head.unref(); 262 263 var lst = self.pop(); 264 defer lst.unref(); 265 266 if (!lst.isA(.list)) { 267 return VMError.InvalidType; 268 } 269 270 var newarr = try self.all.alloc(*v.Value, lst.val.list.len + 1); 271 272 newarr[0] = head.ref(); 273 for (lst.val.list, 1..) |oldval, idx| { 274 newarr[idx] = oldval.ref(); 275 } 276 277 errdefer { 278 for (newarr) |nval| { 279 nval.unref(); 280 } 281 self.all.free(newarr); 282 } 283 284 try self.push(try v.Value.newList(self.all, newarr)); 285 self.advanceIp(); 286 }, 287 288 .uncons => { 289 var lst = self.pop(); 290 defer lst.unref(); 291 292 if (!lst.isA(.list)) { 293 return VMError.InvalidType; 294 } 295 296 const oldarr = lst.val.list; 297 298 var newarr = try self.all.alloc(*v.Value, oldarr.len - 1); 299 300 for (oldarr[1..], 0..) |oldval, idx| { 301 newarr[idx] = oldval.ref(); 302 } 303 errdefer { 304 for (newarr) |nval| { 305 nval.unref(); 306 } 307 self.all.free(newarr); 308 } 309 310 try self.push(try v.Value.newList(self.all, newarr)); 311 try self.push(oldarr[0].ref()); 312 self.advanceIp(); 313 }, 314 315 .div => { 316 var left = self.pop(); 317 defer left.unref(); 318 319 var right = self.pop(); 320 defer right.unref(); 321 322 var div = try v.Int.init(self.all); 323 var rem = try v.Int.init(self.all); 324 325 try div.divTrunc(&rem, &left.val.number, &right.val.number); 326 327 try self.push(try v.Value.newInt(self.all, div)); 328 try self.push(try v.Value.newInt(self.all, rem)); 329 self.advanceIp(); 330 }, 331 332 inline .add, .multiply, .substract, .pow => |_, tag| { 333 var left = self.pop(); 334 defer left.unref(); 335 336 var right = self.pop(); 337 defer right.unref(); 338 339 const lnum = &left.val.number; 340 const rnum = &right.val.number; 341 342 var new = try v.Int.init(self.all); 343 errdefer new.deinit(); 344 345 switch (tag) { 346 .add => try new.add(lnum, rnum), 347 .substract => try new.sub(lnum, rnum), 348 .multiply => try new.mul(lnum, rnum), 349 .pow => try new.pow(lnum, try rnum.to(u32)), 350 else => { 351 // Unreachable 352 }, 353 } 354 355 try self.push(try v.Value.newInt(self.all, new)); 356 self.advanceIp(); 357 }, 358 359 .compare => |op| { 360 var left = self.pop(); 361 defer left.unref(); 362 363 var right = self.pop(); 364 defer right.unref(); 365 366 const lnum = left.val.number; 367 const rnum = right.val.number; 368 369 try self.push(try v.Value.newBool(self.all, lnum.order(rnum).compare(op))); 370 self.advanceIp(); 371 } 372 } 373 } 374 } 375 376 pub fn deinit(self: *Self) void { 377 for (self.stack.items) |val| { 378 val.unref(); 379 } 380 self.stack.deinit(); 381 382 var iter = self.globals.iterator(); 383 384 while (iter.next()) |entry| { 385 entry.value_ptr.*.unref(); 386 } 387 self.globals.deinit(); 388 389 self.call_stack.deinit(); 390 } 391 };