From ede65518591b9af79cb50b18dc83419625d27782 Mon Sep 17 00:00:00 2001 From: Taylan Kammer Date: Wed, 24 Jun 2026 14:34:37 +0200 Subject: Various code cleanup. --- _tests/eval.scm | 25 +++++++++++ _tests/perf.zig | 105 ++++++++++++++++++++++++++++++++++++++++++++++ _tests/syntax.scm | 36 ++++++++++++++++ _tests/test.zig | 39 +++++++++++++---- src/test/strings.zig | 6 +-- src/zisp/gc/IstrSet.zig | 2 +- src/zisp/io/Parser.zig | 25 ++++++----- src/zisp/value/fixnum.zig | 2 - src/zisp/value/istr.zig | 25 +++++------ 9 files changed, 228 insertions(+), 37 deletions(-) create mode 100644 _tests/eval.scm create mode 100644 _tests/perf.zig create mode 100644 _tests/syntax.scm diff --git a/_tests/eval.scm b/_tests/eval.scm new file mode 100644 index 0000000..accd858 --- /dev/null +++ b/_tests/eval.scm @@ -0,0 +1,25 @@ + + + +(define (eval form module) + (cond + ((const? form) form) + ((id? form) (lookup form module)) + ((list? form) + (let ((len (list-len form)) + (ptr (list-ptr form))) + (eval-list ptr len module))))) + +(define (eval-list ptr len module) + (let ((first ptr[0])) + (cond + ((funcall? first) + (let ((fun-ptr (funcall-ptr first)) + (argv (eval-args ptr len module))) + (funcall fun-ptr argv))) + (())))) + + + + + diff --git a/_tests/perf.zig b/_tests/perf.zig new file mode 100644 index 0000000..02c9226 --- /dev/null +++ b/_tests/perf.zig @@ -0,0 +1,105 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +const endian = builtin.target.cpu.arch.endian(); + +const min = std.math.minInt(i52) + 1; +const max = std.math.maxInt(i52) - 1; + +export fn checkValidRange(int: i64) u8 { + return if (min < int and int < max) 1 else 0; +} + +export fn checkValidRange2(int: i64) u8 { + const x: u64 = @bitCast(int); + return if (if (int < 0) + x >> 51 == std.math.maxInt(u13) + else + x >> 51 == 0) 1 else 0; +} + +export fn isFixnum(v: u64) u8 { + const expt = v >> 52; + const rest = v << 13; + return if (expt == 0xfff and rest != 0) 1 else 0; +} + +export fn isFixnum2(v: u64) u8 { + const hi: u14 = @intCast(v >> 50); + return if (hi == (0xfff << 2 | 0b01)) 1 else 0; +} + +export fn sstrLen(x: u64) u8 { + const bytes: @Vector(8, u8) = @bitCast(x); + const nulls: @Vector(8, u8) = @splat(0); + const comps: u8 = @bitCast(bytes == nulls); + // Two bits will always be 0, since the actual short string starts at the + // third byte; third lowest or third highest depending on endianness. So, + // depending on endianness, either cut off the two leading bits and ensure + // that the second-last is set, or ensure that the second highest set, to + // limit the length to 6. + return switch (endian) { + .big => @clz(comps << 2 | 2), + .little => @ctz(comps | 64), + }; +} + +const positive_mask: u64 = 0xfff7ffffffffffff; + +fn unpackNegative(v: u64) i64 { + return @bitCast(v); +} + +fn unpackPositive(v: u64) i64 { + const uint: u64 = @bitCast(v); + return @bitCast(uint ^ positive_mask); +} + +fn packNegative(int: i64) u64 { + return @bitCast(int); +} + +fn packPositive(int: i64) u64 { + const uint: u64 = @bitCast(int); + return @bitCast(uint ^ positive_mask); +} + +export fn packFx(int: i64) u64 { + if (int < 0) { + return packNegative(int); + } else { + return packPositive(int); + } +} + +export fn packSstr(len: usize, s: [*]const u8) u64 { + var v: u64 = 0xfff3000000000000; + const buf: *[8]u8 = @ptrCast(&v); + @memcpy(buf[0 .. 0 + len], s); + return v; +} + +pub fn packSstrTest(buf: [*]const u64, len: usize) ?Value { + if (len > 6) return null; + + const v = switch (endian) { + .big => @byteSwap(buf[0]), + .little => buf[0], + }; + + const shift: u6 = 8 * @as(u6, @intCast(len)); + const mask = @as(u64, std.math.maxInt(u64)) << shift; + const s = Value{ .sstr = .{ .bytes = @intCast(v & ~mask) } }; + + // This effectively checks if there were any NUL bytes: + if (len != value.sstrLen(&s)) return null; + + return s; +} + +pub fn packSstrStatic(comptime s: []const u8) Value { + var val: u64 = undefined; + const buf: [*]align(8) u8 = @ptrCast(&val); + @memcpy(buf, s); + return pack(@ptrCast(buf), s.len).?; +} diff --git a/_tests/syntax.scm b/_tests/syntax.scm new file mode 100644 index 0000000..467e669 --- /dev/null +++ b/_tests/syntax.scm @@ -0,0 +1,36 @@ +(let ((foo bar)) + (normal scheme stuff)) + +;; local +(let foo bar) + +;; local fn +(let foo + {(x:Int y:Double) + (print x) + (print y) + (+ x y)}) + +;; exported +(define foo bar) + +;; exported fn +(define foo + {(x:Int y:Double) + (print x) + (print y) + (+ y z)}) + + + + + + +( (( ) + )) + + + +(foo (+ x (* y z)) + (blah) + (blah)) diff --git a/_tests/test.zig b/_tests/test.zig index 7e86ed2..21e8c14 100644 --- a/_tests/test.zig +++ b/_tests/test.zig @@ -1,21 +1,42 @@ const std = @import("std"); -pub fn main() u8 { - // const y: [3]u64 = .{ 1, 2, 3 }; - // const x: struct { u8, u64, u8 } = y; - // @import("std").debug.print("{}\n", .{x[0] + x[1] + x[2]}); +const List = std.ArrayListAlignedUnmanaged; - // std.debug.print("{}\n", .{@sizeOf(struct { u64, ?u8 })}); +const MyList = List(u8, .fromByteUnits(1 << 20)); - // return while (true) if (true) break 1; +pub fn main() !u8 { + const alloc = std.heap.smp_allocator; - const x: ?*u64 = null; - const y: ?*u32 = @ptrCast(x); - _ = y; + var my_list = try MyList.initCapacity(alloc, 4); + try my_list.append(alloc, 0); + try my_list.append(alloc, 1); + try my_list.append(alloc, 2); + try my_list.append(alloc, 3); + + std.debug.print("addrs: {}, {}, {}, {}\n", .{ + &my_list.items[0], + &my_list.items[1], + &my_list.items[2], + &my_list.items[3], + }); return 0; } +// pub fn main() u8 { +// const y: [3]u64 = .{ 1, 2, 3 }; +// const x: struct { u8, u64, u8 } = y; +// @import("std").debug.print("{}\n", .{x[0] + x[1] + x[2]}); + +// std.debug.print("{}\n", .{@sizeOf(struct { u64, ?u8 })}); + +// return while (true) if (true) break 1; + +// const x: ?*u64 = null; +// const y: ?*u32 = @ptrCast(x); +// _ = y; +// } + // const x: ?u8 = 5; // if (x == null) { // return 1; diff --git a/src/test/strings.zig b/src/test/strings.zig index f0cdde9..1cffa39 100644 --- a/src/test/strings.zig +++ b/src/test/strings.zig @@ -11,13 +11,13 @@ const fx = value.fixnum; test "istr" { const s1 = "foo bar baz"; - const v1 = try istr.getOrNew(s1); + const v1 = istr.pack(try istr.getOrNew(s1)); const v1_len: usize = @intCast(fx.unpack(istr.getLen(v1))); try testing.expectEqualStrings(s1, istr.getBytes(v1)); try testing.expectEqual(s1.len, v1_len); const s2 = @embedFile("data/string.txt"); - const v2 = try istr.getOrNew(s2); + const v2 = istr.pack(try istr.getOrNew(s2)); const v2_len: usize = @intCast(fx.unpack(istr.getLen(v2))); try testing.expectEqualStrings(s2, istr.getBytes(v2)); try testing.expectEqual(s2.len, v2_len); @@ -25,7 +25,7 @@ test "istr" { // Check that modifying a slice doesn't affect the string. var s3 = "test".*; - const v3 = try istr.getOrNew(&s3); + const v3 = istr.pack(try istr.getOrNew(&s3)); s3[0] = 'x'; try testing.expectEqualStrings("test", istr.getBytes(v3)); } diff --git a/src/zisp/gc/IstrSet.zig b/src/zisp/gc/IstrSet.zig index bf0db47..900caa7 100644 --- a/src/zisp/gc/IstrSet.zig +++ b/src/zisp/gc/IstrSet.zig @@ -65,10 +65,10 @@ pub fn deinit(self: *Set) void { /// Get the istr with the given string contents, or alloc and store a new one. pub fn getOrNew(self: *Set, s: []const u8) !IstrPtr { + std.debug.assert(s.len <= value.istr.max_len); if (test_stdlib_impl) { return self.addStdlib(s); } - std.debug.assert(s.len < 256); return self.getOrPutOrNew(s, null); } diff --git a/src/zisp/io/Parser.zig b/src/zisp/io/Parser.zig index 29b8590..6dcfe42 100644 --- a/src/zisp/io/Parser.zig +++ b/src/zisp/io/Parser.zig @@ -48,6 +48,7 @@ const ListPool = gc.ListPool; const IstrSet = gc.IstrSet; const Decoder = io.Decoder; const Value = value.Value; +const IstrPtr = value.istr.IstrPtr; const Parser = @This(); @@ -115,7 +116,7 @@ pub fn init() !Parser { const istr_set = gc.mainIstrSet(); const alloc = gc.mainAlloc(); const decoder = io.mainDecoder(); - return initCustom(list_pool, istr_set, alloc, 32, 2048, 32, decoder); + return initCustom(list_pool, istr_set, alloc, 16, 512, 32, decoder); } pub fn initCustom( @@ -127,13 +128,17 @@ pub fn initCustom( init_list_elts_cap: usize, decoder: ?*Decoder, ) !Parser { + // Force some sane minimum values. + const ctx_cap = @max(1, init_ctx_stack_cap); + const str_cap = @max(8, init_str_chars_cap); + const lst_cap = @max(2, init_list_elts_cap); return .{ .list_pool = list_pool, .istr_set = istr_set, .alloc = alloc, - .ctx_stack = try .initCapacity(alloc, init_ctx_stack_cap), - .str_chars = try .initCapacity(alloc, init_str_chars_cap), - .list_elts = try .initCapacity(alloc, init_list_elts_cap), + .ctx_stack = try .initCapacity(alloc, ctx_cap), + .str_chars = try .initCapacity(alloc, str_cap), + .list_elts = try .initCapacity(alloc, lst_cap), .decoder = decoder, }; } @@ -218,17 +223,17 @@ fn getCharsAsString(p: *Parser) !Value { if (value.sstr.isValidSstr(s)) { return value.sstr.pack(s); } else if (value.istr.isValidIstr(s)) { - return p.getIstr(s); + return value.istr.pack(try p.getIstr(s)); } else { - return value.array.newString(p.alloc, s); + return try value.array.newString(p.alloc, s); } } -fn getIstr(p: *Parser, s: []const u8) !Value { +fn getIstr(p: *Parser, s: []const u8) !IstrPtr { if (p.istr_set) |set| { - return value.istr.getOrNewInSet(set, s); + return try value.istr.getOrNewInSet(set, s); } else { - return value.istr.pack(try value.istr.new(p.alloc, s)); + return try value.istr.new(p.alloc, s); } } @@ -249,7 +254,7 @@ fn addListElt(p: *Parser, elt: Value) !void { fn getList(p: *Parser) !Value { if (p.context.comment) return value.nil; if (p.list_elts.items.len == p.context.list_start) return value.nil; - defer p.list_elts.items.len = p.context.list_start; + defer p.list_elts.shrinkRetainingCapacity(p.context.list_start); const vals = p.list_elts.items[p.context.list_start..]; return value.list.new(p.alloc, p.list_pool, vals); } diff --git a/src/zisp/value/fixnum.zig b/src/zisp/value/fixnum.zig index 1929ba2..3bd6112 100644 --- a/src/zisp/value/fixnum.zig +++ b/src/zisp/value/fixnum.zig @@ -54,8 +54,6 @@ fn unpackPositive(v: Value) i64 { return @bitCast(uint ^ positive_mask); } -// Although we use if, these should compile to branchless code using cmov. - pub fn pack(int: i64) Value { assertValidRange(int); if (int < 0) { diff --git a/src/zisp/value/istr.zig b/src/zisp/value/istr.zig index dfe4614..a581289 100644 --- a/src/zisp/value/istr.zig +++ b/src/zisp/value/istr.zig @@ -12,6 +12,8 @@ const value = @import("../value.zig"); const IstrSet = gc.IstrSet; const Value = value.Value; +pub const max_len = 255; + // Zig API /// Pointer to an interned string. First byte is length. @@ -49,7 +51,7 @@ pub fn assert(v: Value) void { } pub fn isValidIstr(s: []const u8) bool { - return s.len <= 255; + return s.len <= max_len; } fn assertValidIstr(s: []const u8) void { @@ -67,6 +69,15 @@ pub fn new(alloc: Alloc, s: []const u8) !IstrPtr { return istr; } +pub fn getOrNew(s: []const u8) !IstrPtr { + return getOrNewInSet(gc.mainIstrSet(), s); +} + +pub fn getOrNewInSet(set: *IstrSet, s: []const u8) !IstrPtr { + assertValidIstr(s); + return try set.getOrNew(s); +} + pub fn pack(istr: IstrPtr) Value { const ptr = @intFromPtr(istr); return .{ .istr = .{ .ptr = @intCast(ptr) } }; @@ -76,19 +87,9 @@ pub fn unpack(v: Value) IstrPtr { return @ptrFromInt(v.istr.ptr); } -pub fn getOrNew(s: []const u8) !Value { - return getOrNewInSet(gc.mainIstrSet(), s); -} - -pub fn getOrNewInSet(set: *IstrSet, s: []const u8) !Value { - assertValidIstr(s); - return pack(try set.getOrNew(s)); -} - pub fn getBytes(v: Value) []const u8 { assert(v); - const istr: IstrPtr = @ptrFromInt(v.istr.ptr); - return istr.bytes(); + return unpack(v).bytes(); } // Zisp API -- cgit v1.2.3