commit c28ffd6c01e94fde49e78fc710a307922efbaf0a
parent b7e7127585b30cb5b1f3148c3462e4958616a10c
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date: Wed, 26 Aug 2026 12:55:10 +0200
GC done
Diffstat:
4 files changed, 42 insertions(+), 24 deletions(-)
diff --git a/zlox/src/gc.zig b/zlox/src/gc.zig
@@ -7,7 +7,13 @@ const Value = @import("value.zig").Value;
const VM = @import("vm.zig").VM;
pub const GC = struct {
- pub const Obj = @import("obj.zig").Obj(.{ .gc = true, .mark = false });
+ pub const Color = enum(u8) {
+ White,
+ Black,
+ None,
+ };
+
+ pub const Obj = @import("obj.zig").Obj(.{ .color = Color.White });
const Self = @This();
@@ -28,7 +34,7 @@ pub const GC = struct {
};
const DBG_STRESS = false;
- const DBG_LOG = true;
+ const DBG_LOG = false;
const GC_HEAP_GROW_FACTOR = 2;
allocator: std.mem.Allocator,
@@ -59,7 +65,7 @@ pub const GC = struct {
};
}
- fn collect(self: *Self) void {
+ pub fn collect(self: *Self) void {
if (self.callbacks.len() > 0) {
self.mark_roots();
self.trace_references();
@@ -131,7 +137,7 @@ pub const GC = struct {
table.for_each(table, struct {
pub fn fun(tbl: *Table, key: Table.Key, _: Table.Value) void {
const obj = key.cast();
- if (obj.fields.gc and !obj.fields.mark)
+ if (obj.fields.color == .White)
_ = tbl.delete(key);
}
}.fun);
@@ -140,17 +146,16 @@ pub const GC = struct {
fn sweep(self: *Self) void {
var iter = self.objs.iter();
while (iter.next()) |obj| {
- if (obj.fields.gc) {
- if (!obj.fields.mark) {
- iter.pop();
- dbg_obj("O", "free", obj, false);
- obj.free(self.allocator);
- switch (obj.type) {
- inline else => |tp| self.allocated -= @sizeOf(tp.get()) + if (tp == .String) (obj.cast(tp) catch unreachable).len else 0,
- }
- } else {
- obj.fields.mark = false;
+ if (obj.fields.color == .White) {
+ iter.pop();
+ dbg_obj("O", "free", obj, false);
+ switch (obj.type) {
+ inline else => |tp| self.allocated -= @sizeOf(tp.get()),
}
+ if (obj.cast_if(.String)) |str| self.allocated -= str.len;
+ obj.free(self.allocator);
+ } else if (obj.fields.color == .Black) {
+ obj.fields.color = .White;
}
}
}
@@ -193,7 +198,9 @@ pub const GC = struct {
const chd = try tp.get().init(arg, self.allocator);
- self.allocated += @sizeOf(tp.get()) + if (tp == .String) chd.len else 0;
+ self.allocated += @sizeOf(tp.get());
+ if (tp == .String) self.allocated += chd.len;
+
if (DBG_STRESS or self.allocated > self.next) {
self.collect();
}
@@ -203,7 +210,6 @@ pub const GC = struct {
const obj = chd.cast();
dbg_obj("O", "new", obj, true);
- dbg_print("{d}/{d}\n", .{ self.allocated, self.next });
try self.objs.push(0, obj);
@@ -212,16 +218,16 @@ pub const GC = struct {
pub fn mark(self: *Self, msg: []const u8, arg: anytype) void {
if (Obj.from(arg)) |obj| {
- if (obj.fields.gc and !obj.fields.mark) {
+ if (obj.fields.color == .White) {
dbg_obj(msg, "mark", obj, true);
- obj.fields.mark = true;
+ obj.fields.color = .Black;
self.greys.push(-1, obj) catch @panic("Grey stack overflow");
}
}
}
pub fn exclude(obj: *Obj) void {
- obj.fields.gc = false;
+ obj.fields.color = .None;
}
pub fn emplace_cast(self: *Self, comptime tp: Obj.Type, arg: tp.get().Arg) !*Obj {
diff --git a/zlox/src/obj.zig b/zlox/src/obj.zig
@@ -95,8 +95,11 @@ pub fn Obj(fields: anytype) type {
}
pub fn cast(self: anytype, comptime tp: Type) Error!utils.copy_const(@TypeOf(self), *tp.get()) {
- if (!self.is(tp)) return Error.IllegalCastError;
- return self._cast(tp);
+ return if (self.is(tp)) self._cast(tp) else Error.IllegalCastError;
+ }
+
+ pub fn cast_if(self: anytype, comptime tp: Type) ?utils.copy_const(@TypeOf(self), *tp.get()) {
+ return if (self.is(tp)) self._cast(tp) else null;
}
fn _cast(self: anytype, comptime tp: Type) utils.copy_const(@TypeOf(self), *tp.get()) {
diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig
@@ -101,6 +101,7 @@ pub const VM = struct {
try self.defineNative("typeof", 1, 1, native.typeof);
try self.defineNative("table", 0, Obj.Native.ArityMax, native.table);
try self.defineNative("list", 0, Obj.Native.ArityMax, native.list);
+ try self.defineNative("rungc", 0, 0, native.rungc);
native.Clock.set_start(io);
@@ -297,10 +298,13 @@ pub const VM = struct {
}
fn binary_op(self: *@This(), comptime in_tag: anytype, comptime out_tag: anytype, op: callbacks.Type(in_tag, out_tag)) InterpreterError!void {
- const b = self.pop();
- const a = self.pop();
+ const b = self.peek(0);
+ const a = self.peek(1);
if (a.is(in_tag) and b.is(in_tag)) {
- self.push(Value.init(try op.call(a.get(in_tag), b.get(in_tag))));
+ const val = Value.init(try op.call(a.get(in_tag), b.get(in_tag)));
+ _ = self.pop();
+ _ = self.pop();
+ self.push(val);
} else {
self.runtimeError("Operands have invalid types, expected: {s}", .{@tagName(in_tag)});
return InterpreterError.RuntimeError;
diff --git a/zlox/src/vm::native.zig b/zlox/src/vm::native.zig
@@ -43,3 +43,8 @@ pub fn list(gc: *GC, args: []const Value) Error!Value {
pub fn typeof(gc: *GC, args: []const Value) Error!Value {
return Value.init(gc.emplace_cast(.String, &.{args[0].typeName()}) catch return Error.Native);
}
+
+pub fn rungc(gc: *GC, _: []const Value) Error!Value {
+ gc.collect();
+ return Value.init({});
+}