From 666746b9c96669aa5e7d1a599723e413a7187f49 Mon Sep 17 00:00:00 2001 From: Bizuayeu <30091157+Bizuayeu@users.noreply.github.com> Date: Wed, 14 Oct 2026 03:26:26 +0900 Subject: [PATCH] native: each forwarded lane-backend entry calls the family's entry of the same name The test that walks cuda_bound.vtable's fields checks that an entry is present exactly when the family gives it; a forwarder aimed at another inner entry of the same type (unspeculate at prefill, say) would still pass. This one gives the forwarders a backend that records which of its entries ran, calls each forwarded entry once, and checks the name and the count. No change outside the test. --- zig/src/native/cuda_bound.zig | 172 ++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/zig/src/native/cuda_bound.zig b/zig/src/native/cuda_bound.zig index f621397cb3..40977dfa50 100644 --- a/zig/src/native/cuda_bound.zig +++ b/zig/src/native/cuda_bound.zig @@ -137,3 +137,175 @@ test "every entry the family's backend gives is forwarded, and none it leaves ou } try std.testing.expect(vtable(H, fake.stepped().vtable.*).prefill_step != null); } + +test "each forwarded entry calls the family's entry of the same name, once" { + const S = lanes.Stream; + const Rec = struct { + name: []const u8 = "", + calls: usize = 0, + fn hit(p: *anyopaque, name: []const u8) void { + const r: *@This() = @ptrCast(@alignCast(p)); + r.name = name; + r.calls += 1; + } + fn once(r: *@This(), name: []const u8) !void { + try std.testing.expectEqualStrings(name, r.name); + try std.testing.expectEqual(@as(usize, 1), r.calls); + r.* = .{}; + } + }; + const spy: be.Backend.VTable = .{ + .prefill = struct { + fn f(p: *anyopaque, _: *S) anyerror!void { + Rec.hit(p, "prefill"); + } + }.f, + .prefill_step = struct { + fn f(p: *anyopaque, _: *S) anyerror!bool { + Rec.hit(p, "prefill_step"); + return true; + } + }.f, + .first = struct { + fn f(p: *anyopaque, _: *S, _: u64) anyerror!u64 { + Rec.hit(p, "first"); + return 0; + } + }.f, + .queue = struct { + fn f(p: *anyopaque, _: *S, _: be.Feed, _: u64) anyerror!u64 { + Rec.hit(p, "queue"); + return 0; + } + }.f, + .read = struct { + fn f(p: *anyopaque, _: u64) anyerror!u32 { + Rec.hit(p, "read"); + return 0; + } + }.f, + .verify = struct { + fn f(p: *anyopaque, _: []const be.Window, _: []be.Verified) anyerror!void { + Rec.hit(p, "verify"); + } + }.f, + .keep = struct { + fn f(p: *anyopaque, _: []const be.Window, _: []const []const u32) anyerror!void { + Rec.hit(p, "keep"); + } + }.f, + .draft = struct { + fn f(p: *anyopaque, _: []const be.DraftRequest) anyerror!void { + Rec.hit(p, "draft"); + } + }.f, + .unspeculate = struct { + fn f(p: *anyopaque, _: *S) anyerror!void { + Rec.hit(p, "unspeculate"); + } + }.f, + .probabilities = struct { + fn f(p: *anyopaque, _: *S, _: []f64) anyerror!bool { + Rec.hit(p, "probabilities"); + return true; + } + }.f, + .tree = struct { + fn f(p: *anyopaque, _: *S, _: std.mem.Allocator) anyerror!?lanes.stream.Held { + Rec.hit(p, "tree"); + return null; + } + }.f, + .alternatives = struct { + fn f(p: *anyopaque, _: *S, _: []be.Alternative) anyerror!usize { + Rec.hit(p, "alternatives"); + return 0; + } + }.f, + .prepare_features = struct { + fn f(p: *anyopaque, _: []const u32) anyerror!void { + Rec.hit(p, "prepare_features"); + } + }.f, + .features = struct { + fn f(p: *anyopaque, _: *S, _: []const u32, _: u64, _: u32) anyerror!be.Features { + Rec.hit(p, "features"); + return undefined; + } + }.f, + .first_row = struct { + fn f(p: *anyopaque, _: *S) anyerror!lanes.LogprobRow { + Rec.hit(p, "first_row"); + return undefined; + } + }.f, + .first_masked = struct { + fn f(p: *anyopaque, _: *S, _: u64, _: []const u32) anyerror!u64 { + Rec.hit(p, "first_masked"); + return 0; + } + }.f, + .held = struct { + fn f(p: *anyopaque, _: *S, _: []u32) anyerror!void { + Rec.hit(p, "held"); + } + }.f, + .release = struct { + fn f(p: *anyopaque, _: *S) void { + Rec.hit(p, "release"); + } + }.f, + }; + const H = struct { + inner: be.Backend, + pub fn bind(p: *anyopaque) *@This() { + return @ptrCast(@alignCast(p)); + } + }; + var rec: Rec = .{}; + var h: H = .{ .inner = .{ .ptr = &rec, .vtable = &spy } }; + const w = vtable(H, spy); + const p: *anyopaque = &h; + var s: S = undefined; + const feed: be.Feed = undefined; + var verified: [0]be.Verified = .{}; + var probs: [0]f64 = .{}; + var alts: [0]be.Alternative = .{}; + var out: [0]u32 = .{}; + try w.prefill(p, &s); + try rec.once("prefill"); + _ = try w.prefill_step.?(p, &s); + try rec.once("prefill_step"); + _ = try w.first(p, &s, 0); + try rec.once("first"); + _ = try w.queue(p, &s, feed, 0); + try rec.once("queue"); + _ = try w.read(p, 0); + try rec.once("read"); + try w.verify(p, &.{}, &verified); + try rec.once("verify"); + try w.keep(p, &.{}, &.{}); + try rec.once("keep"); + try w.draft(p, &.{}); + try rec.once("draft"); + try w.unspeculate.?(p, &s); + try rec.once("unspeculate"); + _ = try w.probabilities.?(p, &s, &probs); + try rec.once("probabilities"); + _ = try w.tree.?(p, &s, std.testing.allocator); + try rec.once("tree"); + _ = try w.alternatives.?(p, &s, &alts); + try rec.once("alternatives"); + try w.prepare_features.?(p, &.{}); + try rec.once("prepare_features"); + _ = try w.features.?(p, &s, &.{}, 0, 0); + try rec.once("features"); + _ = try w.first_row.?(p, &s); + try rec.once("first_row"); + _ = try w.first_masked.?(p, &s, 0, &.{}); + try rec.once("first_masked"); + try w.held.?(p, &s, &out); + try rec.once("held"); + w.release(p, &s); + try rec.once("release"); +}