summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTaylan Kammer <taylan.kammer@gmail.com>2026-07-02 06:14:48 +0200
committerTaylan Kammer <taylan.kammer@gmail.com>2026-07-02 06:14:48 +0200
commit86e77fc35b3a9146a31b872643a85f8f6a486a07 (patch)
tree056a35081a7f5d87d351c710ae6f34ee7c0c5e9c /src
parentb0043e83d3bf2f4d49bf1f153d65c48b4fabb4f6 (diff)
Exciting things are happening. - Vague Posting King
Diffstat (limited to 'src')
-rw-r--r--src/zisp/gc.zig39
-rw-r--r--src/zisp/gc/ListPool.zig (renamed from src/zisp/gc/CodePool.zig)6
-rw-r--r--src/zisp/value.zig32
-rw-r--r--src/zisp/value/array.zig137
-rw-r--r--src/zisp/value/pair.zig23
5 files changed, 149 insertions, 88 deletions
diff --git a/src/zisp/gc.zig b/src/zisp/gc.zig
index 0e26aa9..b36628f 100644
--- a/src/zisp/gc.zig
+++ b/src/zisp/gc.zig
@@ -5,25 +5,26 @@ const Alloc = std.mem.Allocator;
const value = @import("value.zig");
-pub const CodePool = @import("gc/CodePool.zig");
+pub const ListPool = @import("gc/ListPool.zig");
pub const IstrPool = @import("gc/IstrPool.zig");
const HeapPtr = value.HeapPtr;
+const HeapType = value.HeapType;
var main_alloc: Alloc = undefined;
-var code_heap_start: usize = undefined;
+var list_heap_start: usize = undefined;
var main_heap_start: usize = undefined;
var istr_heap_start: usize = undefined;
var main_list_pool: ListPool = undefined;
var main_istr_pool: IstrPool = undefined;
-pub fn codePtrFromIdx(idx: u32) [*]Value {
- return @ptrFromInt(code_heap_start + idx * 8);
+pub fn listPtrFromIdx(idx: u32) [*]Value {
+ return @ptrFromInt(list_heap_start + idx * 8);
}
-pub fn heapPtrFromIdx(idx: u32) HeapPtr {
+pub fn heapPtrFromIdx(comptime typ: HeapType, idx: u32) typ.PtrType() {
return @ptrFromInt(main_heap_start + idx * 8);
}
@@ -31,8 +32,8 @@ pub fn istrPtrFromIdx(idx: u32) [*]const u8 {
return @ptrFromInt(istr_heap_start + idx);
}
-pub fn codeIdxFromPtr(ptr: [*]Value) u32 {
- return @intCast(@intFromPtr(ptr - code_heap_start) / 8);
+pub fn listIdxFromPtr(ptr: [*]Value) u32 {
+ return @intCast(@intFromPtr(ptr - list_heap_start) / 8);
}
pub fn heapIdxFromPtr(ptr: HeapPtr) u32 {
@@ -43,6 +44,26 @@ pub fn istrIdxFromPtr(ptr: [*]const u8) u32 {
return @intCast(@intFromPtr(ptr - istr_heap_start));
}
+pub fn allocHeap(comptime typ: HeapType, len: usize) !typ.PtrType() {
+ const aln = std.mem.Alignment.of(HeapPtr);
+ return @ptrCast(main_alloc.alignedAlloc(u8, aln, len));
+}
+
+pub fn createHeap(comptime typ: HeapType) !typ.PtrType() {
+ return @ptrCast(main_alloc.create(typ.ObjType()));
+}
+
+pub fn packHeap(ptr: anytype) Value {
+ const idx = heapIdxFromPtr(ptr);
+ const typ = value.HeapType.of(ptr);
+ return .{ .hptr = .{ .idx = idx, .typ = typ } };
+}
+
+pub fn unpackHeap(comptime typ: HeapType, v: Value) typ.PtrType() {
+ if (!v.isHptrTyp(typ)) @panic("Wrong type.");
+ return heapPtrFromIdx(typ, v.hptr.idx);
+}
+
// init
var init_done = false;
@@ -53,10 +74,10 @@ pub fn init() !void {
main_alloc = std.heap.smp_allocator;
- //code_heap_start = ...;
+ //list_heap_start = ...;
main_list_pool = try ListPool.init(main_alloc);
- main_istr_set = try IstrPool.init(main_alloc);
+ main_istr_pool = try IstrPool.init(main_alloc);
}
pub fn mainAlloc() Alloc {
diff --git a/src/zisp/gc/CodePool.zig b/src/zisp/gc/ListPool.zig
index 56bf384..42bb50e 100644
--- a/src/zisp/gc/CodePool.zig
+++ b/src/zisp/gc/ListPool.zig
@@ -91,7 +91,7 @@
//! This shouldn't affect programs that simply load up all their modules, run
//! forever without dynamic modification, and thus never deallocate code.
//!
-//! Otherwise, a simple solution is to use separate CodePool instances for
+//! Otherwise, a simple solution is to use separate ListPool instances for
//! modules, or just forget about all of this and rely on a compacting GC.
//!
@@ -106,8 +106,8 @@ const seg_stack = @import("../util/seg_stack.zig");
const Value = value.Value;
const SegStack = seg_stack.SegStack;
-const i2p = gc.codePtrFromIdx;
-const p2i = gc.codeIdxFromPtr;
+const i2p = gc.listPtrFromIdx;
+const p2i = gc.listIdxFromPtr;
const Self = @This();
diff --git a/src/zisp/value.zig b/src/zisp/value.zig
index 9262a17..1a63349 100644
--- a/src/zisp/value.zig
+++ b/src/zisp/value.zig
@@ -19,6 +19,7 @@ pub const sval = @import("value/sval.zig");
pub const char = @import("value/char.zig");
pub const misc = @import("value/misc.zig");
+pub const pair = @import("value/pair.zig");
pub const array = @import("value/array.zig");
pub const boole = @import("value/boole.zig");
@@ -50,7 +51,7 @@ pub fn runeXsstr(v: Value) Value {
}
pub const MiscValue = enum(u8) {
- // Make f/t only differ in one bit, to make `bool?` more efficient.
+ // Make sure f/t only differ in one bit, to make `bool?` more efficient.
/// False
f = 0,
/// True
@@ -71,20 +72,39 @@ pub const eof = Value{ .misc = .{ .value = .eof } };
pub const none = Value{ .misc = .{ .value = .none } };
// zig fmt: on
-/// A plain (unpacked, untagged) pointer into the Zisp heap.
-pub const HeapPtr = *anyopaque;
+/// A plain (unpacked, untagged, uncompressed) pointer into the main heap.
+pub const HeapPtr = *align(8) anyopaque;
-/// Values for the lowest 4 bits of a heap pointer, indicating the heap type.
+/// Values for the 8 type bits on main heap pointers.
pub const HeapType = enum(u8) {
+ /// Pair (car, cdr)
+ pair,
/// Array of various types: see `ArrayPtr`.
array,
+ pub fn ObjType(self: HeapType) type {
+ return switch (self) {
+ .pair => pair.Pair,
+ .array => array.ArrayHeader,
+ else => @panic("Invalid HeapType."),
+ };
+ }
+
pub fn PtrType(self: HeapType) type {
return switch (self) {
+ .pair => pair.PairPtr,
.array => array.ArrayPtr,
else => @panic("Invalid HeapType."),
};
}
+
+ pub fn of(ptr: anytype) HeapType {
+ return switch (@TypeOf(ptr)) {
+ PairPtr => .pair,
+ ArrayHeader => .array,
+ else => @panic("Unknown heap pointer."),
+ };
+ }
};
fn hi16(comptime tag: u4) u16 {
@@ -275,8 +295,8 @@ pub const Value = packed union {
}
/// Check for heap pointer (given type).
- pub fn isHptrTyp(v: Value, comptime htype: HeapType) bool {
- return v.hptr._hi == hi16_hptr and v.htptr.typ == htype;
+ pub fn isHptrTyp(v: Value, comptime typ: HeapType) bool {
+ return v.hptr._hi == hi16_hptr and v.hptr.typ == typ;
}
/// Check for istr pointer.
diff --git a/src/zisp/value/array.zig b/src/zisp/value/array.zig
index cdc5f75..88baf41 100644
--- a/src/zisp/value/array.zig
+++ b/src/zisp/value/array.zig
@@ -1,8 +1,6 @@
const builtin = @import("builtin");
const std = @import("std");
-const Alloc = std.mem.Allocator;
-
const gc = @import("../gc.zig");
const value = @import("../value.zig");
@@ -12,25 +10,24 @@ const Value = value.Value;
///
/// The low 48 bits are either the length (element count, not buffer size) of
/// the array contents that follow immediately, or a pointer to the header of
-/// another array whose memory is shared with this one, or a null pointer that
-/// has special meaning; see below.
+/// another array whose memory is shared with this one, or null, which has a
+/// special meaning; see below.
///
/// NOTE: For strings, the length is in fact the size in bytes of the buffer.
/// To get the "length" of a string according to other definitions of length,
-/// such as count of Unicode Scalar Values, or count of Grapheme Clusters,
+/// such as count of Unicode Scalar Values, count of UTF-16 code units, etc.,
/// different encoding-specific string APIs must be used.
///
/// If this is a pointer (`is_ptr` is set) it means it's a re-interpretation of
/// the contents of the array that is being pointed to. (But see below.)
///
/// If this is a slice (`is_slice` is set) then two more u64 values follow this
-/// one, marking the start and end of the slice of the pointed-to array that
+/// one, marking the start and end byte offsets of the slice of the array that
/// this one represents. (But see next paragraph.)
///
/// If this is a pointer or slice, but the pointer value is null, then another
-/// u64 follows this one immediately, and points directly to a memory buffer
-/// (not array head) whose contents are used. In this case, if it's a slice,
-/// the start and end u64 values come after that pointer instead.
+/// u64 is found at the end, which is a direct pointer to a memory buffer (not
+/// array head) whose contents are used.
///
/// If this header encodes a non-slice, non-array, direct buffer pointer, then
/// there is no length information, so the count of elements is unknown and
@@ -48,7 +45,19 @@ const Value = value.Value;
/// another array with the same buffer pointer and different type info.
///
/// Other remaining bits provide information about element type and size.
-pub const ArrayPtr = *align(@alignOf(value.Zptr)) ArrayHeader;
+pub const ArrayPtr = *align(@alignOf(value.HeapPtr)) ArrayHeader;
+
+const ArrayType = enum(u2) { int, flt, val, str };
+
+const Endian = enum(u1) {
+ little,
+ big,
+
+ const native: Endian = switch (builtin.target.cpu.arch.endian()) {
+ .little => .little,
+ .big => .big,
+ };
+};
// Important: We may or may not use a hack one day in which an algorithm, like
// for GC purposes, scans through certain memory regions looking for NaN-packed
@@ -71,7 +80,7 @@ pub const ArrayHeader = packed struct(u64) {
},
flt: packed struct(u12) {
endian: Endian = .native,
- _DONTUSE: bool = false
+ _DONTUSE: bool = false,
size: u10,
},
val: packed struct(u12) {
@@ -98,26 +107,39 @@ pub const ArrayHeader = packed struct(u64) {
return @ptrCast(self);
}
- fn bufContent(self: *Self) [*]u8 {
+ fn bufOfDirect(self: *Self) [*]u8 {
std.debug.assert(!self.is_ptr);
- return @ptrCast(self.bufU64() + 1);
+ return @ptrCast(&self.bufU64()[1]);
}
- fn bufPointer(self: *Self) [*]u8 {
+ fn bufOfPointer(self: *Self) [*]u8 {
std.debug.assert(self.is_ptr);
std.debug.assert(self.len_or_ptr == 0);
return @ptrFromInt(self.bufU64()[1]);
}
+ fn sliceInfo(self: *Self) [2]u64 {
+ std.debug.assert(self.is_slice);
+ const buf = self.bufU64();
+ return .{ buf[1], buf[2] };
+ }
+
+ fn bufOfSlice(self: *Self) [*]u8 {
+ std.debug.assert(self.is_slice);
+ std.debug.assert(self.len_or_ptr == 0);
+ return @ptrFromInt(self.bufU64()[3]);
+ }
+
fn eltSize(self: *Self) u16 {
std.debug.assert(!self.is_ptr);
return switch (self.type) {
.str => 1,
+ .val => 8,
else => @panic("not implemented"),
};
}
- fn size(self: *Self) usize {
+ fn sizeInBytes(self: *Self) usize {
std.debug.assert(!self.is_ptr);
return self.len_or_ptr * self.eltSize();
}
@@ -128,77 +150,76 @@ pub const ArrayHeader = packed struct(u64) {
return if (p != 0) @ptrFromInt(p) else null;
}
- fn sliceInfo(self: *Self) [2]u64 {
- std.debug.assert(self.is_slice);
- const ptr = self.len_or_ptr;
- const buf = self.bufU64();
- if (ptr != 0) {
- return .{ buf[1], buf[2] };
- } else {
- return .{ buf[2], buf[3] };
- }
- }
-
- pub fn bufU8(self: *Self) [*]u8 {
+ /// Get a pointer to the array's contents as a u8 multi-pointer.
+ pub fn bufU8RW(self: *Self) [*]u8 {
if (self.is_ptr) {
- if (self.arrPointer()) |a| {
- return a.bufContent();
+ if (self.arrPointer()) |dest| {
+ std.debug.assert(!dest.is_ptr);
+ return dest.bufOfDirect();
+ } else if (self.is_slice) {
+ return self.bufOfSlice();
} else {
- return self.bufPointer();
+ return self.bufOfPointer();
}
- } else {
- return self.bufContent();
}
+ return self.bufOfDirect();
+ }
+
+ /// Get a pointer to the array's contents as a const u8 multi-pointer.
+ pub fn bufU8RO(self: *Self) [*]const u8 {
+ return self.bufU8RW();
}
- pub fn bytes(self: *Self) []u8 {
+ /// Get a u8 slice of the array's contents.
+ pub fn sliceU8RW(self: *Self) []u8 {
if (self.is_slice) {
- const buf = self.bufU8();
+ std.debug.assert(self.is_ptr);
const start, const end = self.sliceInfo();
+ var buf = undefined;
+ if (self.arrPointer()) |dest| {
+ std.debug.assert(!dest.is_ptr);
+ std.debug.assert(end <= dest.sizeInBytes());
+ buf = dest.bufOfDirect();
+ } else {
+ buf = self.bufOfSlice();
+ }
return buf[start..end];
}
var arr = self;
if (self.is_ptr) {
arr = self.arrPointer() orelse {
- @panic("Called bytes() on array with direct buffer pointer.");
+ @panic("Array lacks length information; can't take slice.");
};
}
- return arr.bufContent()[0..arr.size()];
+ return arr.bufContent()[0..arr.sizeInBytes()];
}
- pub fn bytesRO(self: *Self) []const u8 {
- return self.bytes();
+ /// Get a const u8 slice of the array's contents.
+ pub fn sliceU8RO(self: *Self) []const u8 {
+ return self.sliceU8RW();
}
};
-const ArrayType = enum(u2) { int, flt, val, str };
-
-const Endian = enum(u1) {
- little,
- big,
-
- const native: Endian = switch (builtin.target.cpu.arch.endian()) {
- .little => .little,
- .big => .big,
- };
-};
-
-pub fn newString(alloc: Alloc, s: []const u8) !Value {
+pub fn newString(s: []const u8) !Value {
std.debug.assert(s.len <= std.math.maxInt(u48));
- const algn = std.mem.Alignment.of(ArrayPtr);
- const size = @sizeOf(ArrayHeader) + s.len;
- const arr: ArrayPtr = @ptrCast(try alloc.alignedAlloc(u8, algn, size));
+ const len = @sizeOf(ArrayHeader) + s.len;
+ const arr = try gc.allocHeap(.array, len);
arr.* = .{
.len_or_ptr = @intCast(s.len),
.type = .str,
.info = .{ .str = .{} },
};
- const buf = arr.bufContent();
+ const buf = arr.bufOfDirect();
@memcpy(buf[0..s.len], s);
- return value.ptr.pack(.array, arr);
+ return gc.packHeap(arr);
+}
+
+pub fn checkAny(v: Value) ?ArrayPtr {
+ if (!v.isHptrTyp(.array)) return null;
+ return @ptrCast(gc.heapPtrFromIdx(v.hptr.idx));
}
pub fn check(comptime t: ArrayType, v: Value) ?ArrayPtr {
- if (v.getPtr(.array)) |p| if (p.type == t) return p;
- return null;
+ const ptr = checkAny(v);
+ return if (ptr.type == t) ptr else null;
}
diff --git a/src/zisp/value/pair.zig b/src/zisp/value/pair.zig
index 09e50f2..c4b6f2c 100644
--- a/src/zisp/value/pair.zig
+++ b/src/zisp/value/pair.zig
@@ -1,12 +1,13 @@
+const std = @import("std");
+
const gc = @import("../gc.zig");
const value = @import("../value.zig");
-const ptr = @import("ptr.zig");
+const Alloc = std.mem.Allocator;
-const PairPool = gc.PairPool;
const Value = value.Value;
-pub const PairPtr = *align(@alignOf(value.Zptr)) Pair;
+pub const PairPtr = *align(@alignOf(value.HeapPtr)) Pair;
pub const Pair = struct {
car: Value,
@@ -16,7 +17,8 @@ pub const Pair = struct {
// Zig API
pub fn check(v: Value) ?PairPtr {
- return v.getPtr(.pair);
+ if (!v.isHptrTyp(.pair)) return null;
+ return @ptrCast(gc.heapPtrFromIdx(v.hptr.idx));
}
pub fn assert(v: Value) PairPtr {
@@ -30,20 +32,17 @@ pub fn unpack(v: Value) PairPtr {
return assert(v);
}
-pub fn consInPool(pool: *PairPool, car: Value, cdr: Value) !Value {
- const pair = try pool.cons(car, cdr);
- return ptr.pack(.pair, pair);
-}
-
// Zisp API
pub fn pred(v: Value) Value {
return value.boole.pack(check(v) != null);
}
-pub fn cons(car: Value, cdr: Value) Value {
- const pool = gc.mainPairPool();
- return consInPool(pool, car, cdr) catch @panic("OOM"); // TODO
+pub fn cons(car: Value, cdr: Value) !Value {
+ const pair = try gc.createHeap(.pair);
+ pair.car = car;
+ pair.cdr = cdr;
+ return gc.packHeap(pair);
}
pub fn getCar(v: Value) Value {