Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ zig fetch --save https://github.com/gHashTag/zig-golden-float/archive/refs/tags/
```

```zig
const gf = @import("golden_float");
const gf = @import("golden-float");

const x = gf.GF16.fromF32(3.14);
const y = gf.GF16.fromF32(2.71);
Expand Down
15 changes: 15 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,22 @@ pub fn build(b: *std.Build) void {
});
const run_jepa_t_tests = b.addRunArtifact(jepa_t_tests);

// The public module root. Nothing else compiles it, which is how thirteen
// broken import paths shipped invisibly: every consumer of
// @import("golden-float") failed while the suite stayed green.
const root_api_tests_root = b.createModule(.{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
const root_api_tests = b.addTest(.{
.name = "root-api-tests",
.root_module = root_api_tests_root,
});
const run_root_api_tests = b.addRunArtifact(root_api_tests);

const test_step = b.step("test", "Run all tests");
test_step.dependOn(&run_root_api_tests.step);
test_step.dependOn(&run_tests.step);
test_step.dependOn(&run_gft_tests.step);
test_step.dependOn(&run_gf_binary_tests.step);
Expand Down
147 changes: 147 additions & 0 deletions docs/AUDIT_2026-08-20.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion src/root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ test "every public declaration of this module is analysed" {
// That is not hypothetical. The same omission in gHashTag/zig-hdc hid five
// distinct API-drift errors against the version of this package it pins,
// while its CI stayed green throughout (zig-hdc#2).
@import("std").testing.refAllDeclsRecursive(@This());
// refAllDeclsRecursive was removed by zig 0.16; refAllDecls references
// every top-level export, which is what forces each module file to load.
@import("std").testing.refAllDecls(@This());
}

// vsa_jit was never exported, so nothing ever compiled it, so nobody found
Expand Down
7 changes: 5 additions & 2 deletions src/ternary/bigint.zig
Original file line number Diff line number Diff line change
Expand Up @@ -544,11 +544,14 @@ pub const TVCBigInt = struct {
const cmp = simdCompareTrits(a_vec, b_vec);

// Check from most significant position in chunk
// A @Vector cannot be indexed at runtime (zig 0.16); coerce to
// an array first.
const cmp_arr: [32]i8 = cmp;
var pos: usize = 32;
while (pos > 0) {
pos -= 1;
if (cmp[pos] != 0) {
return cmp[pos];
if (cmp_arr[pos] != 0) {
return cmp_arr[pos];
}
}
}
Expand Down
69 changes: 50 additions & 19 deletions src/vm/jit_arm64.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,32 @@
// φ² + 1/φ² = 3

const std = @import("std");

/// PROT flags are decl-constants on 0.15.x and a packed struct on 0.16;
/// mprotect left std.posix in 0.16. Both paths compile because the condition
/// is comptime-known and Zig skips the untaken branch.
const zig_016_mem = @import("builtin").zig_version.major == 0 and
@import("builtin").zig_version.minor >= 16;

/// zig 0.15.x and 0.16 disagree on several std APIs this file uses. The
/// repository declares minimum_zig_version 0.15.0 and CI runs 0.15.2, while
/// development happens on 0.16 — so both must compile. Zig does not analyse
/// the untaken branch of a comptime-known `if`, which is what makes this work.
const zig_016 = @import("builtin").zig_version.major == 0 and
@import("builtin").zig_version.minor >= 16;

/// Monotonic nanoseconds. std.time.Timer/nanoTimestamp left std by 0.16.
fn monotonicNs() u64 {
if (comptime zig_016) {
var ts: std.c.timespec = undefined;
_ = std.c.clock_gettime(.MONOTONIC, &ts);
return @as(u64, @intCast(ts.sec)) * 1_000_000_000 + @as(u64, @intCast(ts.nsec));
} else {
return @intCast(std.time.nanoTimestamp());
}
}


const builtin = @import("builtin");

// ═══════════════════════════════════════════════════════════════════════════════
Expand All @@ -26,7 +52,7 @@ pub const Arm64JitCompiler = struct {

pub fn init(allocator: std.mem.Allocator) Self {
return Self{
.code = .{},
.code = .empty,
.allocator = allocator,
};
}
Expand Down Expand Up @@ -1426,15 +1452,20 @@ pub const Arm64JitCompiler = struct {
const mem = try std.posix.mmap(
null,
alloc_size,
std.posix.PROT.READ | std.posix.PROT.WRITE,
if (comptime zig_016_mem) .{ .READ = true, .WRITE = true } else std.posix.PROT.READ | std.posix.PROT.WRITE,
.{ .TYPE = .PRIVATE, .ANONYMOUS = true },
-1,
0,
);

@memcpy(mem[0..code_size], self.code.items);

try std.posix.mprotect(mem, std.posix.PROT.READ | std.posix.PROT.EXEC);
if (comptime zig_016_mem) {
if (std.c.mprotect(@ptrCast(mem.ptr), mem.len, .{ .READ = true, .EXEC = true }) != 0)
return error.MprotectFailed;
} else {
try std.posix.mprotect(mem, std.posix.PROT.READ | std.posix.PROT.EXEC);
}

self.exec_mem = mem;

Expand Down Expand Up @@ -1642,20 +1673,20 @@ test "ARM64 NEON SIMD benchmark vs scalar" {
const simd_func = try simd_compiler.finalize();

// Benchmark scalar
var timer = try std.time.Timer.start();
var t_mark = monotonicNs();
var scalar_result: i64 = 0;
for (0..iterations) |_| {
scalar_result = scalar_func(@ptrCast(&a), @ptrCast(&b));
}
const scalar_ns = timer.read();
const scalar_ns = monotonicNs() - t_mark;

// Benchmark SIMD
timer.reset();
t_mark = monotonicNs();
var simd_result: i64 = 0;
for (0..iterations) |_| {
simd_result = simd_func(@ptrCast(&a), @ptrCast(&b));
}
const simd_ns = timer.read();
const simd_ns = monotonicNs() - t_mark;

// Verify results match
try std.testing.expectEqual(scalar_result, simd_result);
Expand Down Expand Up @@ -1832,20 +1863,20 @@ test "ARM64 hybrid benchmark vs pure scalar" {
const hybrid_func = try hybrid_compiler.finalize();

// Benchmark scalar
var timer = try std.time.Timer.start();
var t_mark = monotonicNs();
var scalar_result: i64 = 0;
for (0..iterations) |_| {
scalar_result = scalar_func(@ptrCast(&a), @ptrCast(&b));
}
const scalar_ns = timer.read();
const scalar_ns = monotonicNs() - t_mark;

// Benchmark hybrid
timer.reset();
t_mark = monotonicNs();
var hybrid_result: i64 = 0;
for (0..iterations) |_| {
hybrid_result = hybrid_func(@ptrCast(&a), @ptrCast(&b));
}
const hybrid_ns = timer.read();
const hybrid_ns = monotonicNs() - t_mark;

// Verify results match
try std.testing.expectEqual(scalar_result, hybrid_result);
Expand Down Expand Up @@ -2015,25 +2046,25 @@ test "ARM64 SIMD bind benchmark vs scalar" {
const scalar_func = try scalar_compiler.finalize();

// Benchmark SIMD
var timer = try std.time.Timer.start();
var t_mark = monotonicNs();
for (0..iterations) |_| {
// Reset a for fair comparison
for (0..dim) |i| {
a_simd[i] = @intCast(@as(i32, @intCast(i % 3)) - 1);
}
_ = simd_func(@ptrCast(&a_simd), @ptrCast(&b));
}
const simd_ns = timer.read();
const simd_ns = monotonicNs() - t_mark;

// Benchmark scalar
timer.reset();
t_mark = monotonicNs();
for (0..iterations) |_| {
for (0..dim) |i| {
a_scalar[i] = @intCast(@as(i32, @intCast(i % 3)) - 1);
}
_ = scalar_func(@ptrCast(&a_scalar), @ptrCast(&b));
}
const scalar_ns = timer.read();
const scalar_ns = monotonicNs() - t_mark;

const simd_ms = @as(f64, @floatFromInt(simd_ns)) / 1_000_000.0;
const scalar_ms = @as(f64, @floatFromInt(scalar_ns)) / 1_000_000.0;
Expand Down Expand Up @@ -2106,16 +2137,16 @@ test "ARM64 fused cosine benchmark vs 3x dot" {
}

// Benchmark fused
var timer = try std.time.Timer.start();
var t_mark = monotonicNs();
var fused_result: f64 = 0;
for (0..iterations) |_| {
const bits = fused_func(@ptrCast(&a), @ptrCast(&b));
fused_result = @bitCast(bits);
}
const fused_ns = timer.read();
const fused_ns = monotonicNs() - t_mark;

// Benchmark 3x dot
timer.reset();
t_mark = monotonicNs();
var dot_result: f64 = 0;
for (0..iterations) |_| {
const dot_ab = dot_func(@ptrCast(&a), @ptrCast(&b));
Expand All @@ -2124,7 +2155,7 @@ test "ARM64 fused cosine benchmark vs 3x dot" {
const norm = @sqrt(@as(f64, @floatFromInt(dot_aa)) * @as(f64, @floatFromInt(dot_bb)));
dot_result = @as(f64, @floatFromInt(dot_ab)) / norm;
}
const dot_ns = timer.read();
const dot_ns = monotonicNs() - t_mark;

const fused_ms = @as(f64, @floatFromInt(fused_ns)) / 1_000_000.0;
const dot_ms = @as(f64, @floatFromInt(dot_ns)) / 1_000_000.0;
Expand Down
24 changes: 22 additions & 2 deletions src/vm/jit_unified.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@
// φ² + 1/φ² = 3

const std = @import("std");

/// zig 0.15.x and 0.16 disagree on several std APIs this file uses. The
/// repository declares minimum_zig_version 0.15.0 and CI runs 0.15.2, while
/// development happens on 0.16 — so both must compile. Zig does not analyse
/// the untaken branch of a comptime-known `if`, which is what makes this work.
const zig_016 = @import("builtin").zig_version.major == 0 and
@import("builtin").zig_version.minor >= 16;

/// Monotonic nanoseconds. std.time.Timer/nanoTimestamp left std by 0.16.
fn monotonicNs() u64 {
if (comptime zig_016) {
var ts: std.c.timespec = undefined;
_ = std.c.clock_gettime(.MONOTONIC, &ts);
return @as(u64, @intCast(ts.sec)) * 1_000_000_000 + @as(u64, @intCast(ts.nsec));
} else {
return @intCast(std.time.nanoTimestamp());
}
}


const builtin = @import("builtin");

// Import architecture-specific backends
Expand Down Expand Up @@ -409,12 +429,12 @@ test "Unified JIT benchmark" {
b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1);
}

var timer = try std.time.Timer.start();
const t_mark = monotonicNs();
var result: i64 = 0;
for (0..iterations) |_| {
result = func(@ptrCast(&a), @ptrCast(&b));
}
const ns = timer.read();
const ns = monotonicNs() - t_mark;

const ms = @as(f64, @floatFromInt(ns)) / 1_000_000.0;
const ns_per_iter = @as(f64, @floatFromInt(ns)) / @as(f64, iterations);
Expand Down
17 changes: 14 additions & 3 deletions src/vm/jit_x86_64.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
// φ² + 1/φ² = 3

const std = @import("std");

/// PROT flags are decl-constants on 0.15.x and a packed struct on 0.16;
/// mprotect left std.posix in 0.16. Both paths compile because the condition
/// is comptime-known and Zig skips the untaken branch.
const zig_016_mem = @import("builtin").zig_version.major == 0 and
@import("builtin").zig_version.minor >= 16;
const builtin = @import("builtin");

// ═══════════════════════════════════════════════════════════════════════════════
Expand All @@ -26,7 +32,7 @@ pub const X86_64JitCompiler = struct {

pub fn init(allocator: std.mem.Allocator) Self {
return Self{
.code = .{},
.code = .empty,
.allocator = allocator,
};
}
Expand Down Expand Up @@ -354,7 +360,7 @@ pub const X86_64JitCompiler = struct {
const mem = try std.posix.mmap(
null,
alloc_size,
std.posix.PROT.READ | std.posix.PROT.WRITE,
if (comptime zig_016_mem) .{ .READ = true, .WRITE = true } else std.posix.PROT.READ | std.posix.PROT.WRITE,
.{ .TYPE = .PRIVATE, .ANONYMOUS = true },
-1,
0,
Expand All @@ -364,7 +370,12 @@ pub const X86_64JitCompiler = struct {
@memcpy(mem[0..code_size], self.code.items);

// Change to PROT_READ | PROT_EXEC
try std.posix.mprotect(mem, std.posix.PROT.READ | std.posix.PROT.EXEC);
if (comptime zig_016_mem) {
if (std.c.mprotect(@ptrCast(mem.ptr), mem.len, .{ .READ = true, .EXEC = true }) != 0)
return error.MprotectFailed;
} else {
try std.posix.mprotect(mem, std.posix.PROT.READ | std.posix.PROT.EXEC);
}

self.exec_mem = mem;

Expand Down
2 changes: 1 addition & 1 deletion src/vm/vm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q

const std = @import("std");
const tvc_hybrid = @import("hybrid.zig");
const tvc_hybrid = @import("../ternary/hybrid.zig");
const tvc_vsa = @import("vsa.zig");
const gf = @import("golden-float");

Expand Down
Loading
Loading