diff --git a/CHANGELOG.md b/CHANGELOG.md index 583887edda..681951936b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Each release's page on GitHub has its notes and measurements. See [the 1.0.0 rel ## Unreleased +- Tests: four host steps that no CI job runs build and pass again: `test-gemma4-affine-host` pins `affine_mm.metal`'s current bytes, the compile-only Metal contract runners import `masked_sources`, `compile-metal-residency-host` imports `kimi_k3`, and the GLM Metal fact tests give `facts()` a host device object instead of an undefined one, which crashed them at random (#2409, part of #2031). + - Flash Next on CUDA with `TF_FLASHNEXT_KV_PAGES`: a shared round's grouped draft head reads and writes each stream's own pages, so paged streams' replies equal their runs alone again, and paged decode runs as fast as unpaged; default settings are unchanged (#2244). Thanks to @Arminova. - Tests: `zig build test-glm-snapshots-host` builds again: its copies module imports `core`, which `cuda_snaps.zig` reads for `TF_GLM_PROMPT_TAIL` since #2226 (#2403). Thanks to @Bizuayeu. diff --git a/zig/build/fsr.zig b/zig/build/fsr.zig index 02855d5a06..06dfc5c8e1 100644 --- a/zig/build/fsr.zig +++ b/zig/build/fsr.zig @@ -25,6 +25,7 @@ pub fn deviceSteps(b: *std.Build, metal: *std.Build.Module, engine: *std.Build.M b.step("test-fsr-metal", "GPU: uncached four-slot synthetic MoE equals resident at every node and logit").dependOn(&run.step); b.step("compile-fsr-metal-gate-host", "Compile the real FSR device gate without shader compilation or execution").dependOn(&device_test.step); const contract = b.createModule(.{ .root_source_file = b.path("zig/tests/contract/metal_main.zig"), .target = b.graph.host, .link_libc = true, .imports = &.{ .{ .name = "metal", .module = metal }, .{ .name = "tensorfold", .module = engine }, .{ .name = "kernel_sources", .module = sources }, .{ .name = "contract", .module = @import("kernel_contract.zig").module(b, b.graph.host, .debug) }, .{ .name = "h3_kernels", .module = b.createModule(.{ .root_source_file = b.path("zig/kernels/metal/h3/kernels.zig") }) }, .{ .name = "ling_metal", .module = b.createModule(.{ .root_source_file = b.path("zig/kernels/metal/ling/sources.zig") }) }, .{ .name = "ling_route", .module = b.createModule(.{ .root_source_file = b.path("zig/src/families/ling/route.zig") }) } } }); + contract.addImport("masked_sources", b.createModule(.{ .root_source_file = b.path("zig/masked_value_sources.zig"), .imports = &.{.{ .name = "kernel_sources", .module = sources }} })); const compile_contract = b.addObject(.{ .name = "fsr-metal-contract", .root_module = b.createModule(.{ .root_source_file = b.path("zig/fsr_contract_compile.zig"), .target = b.graph.host, .link_libc = true, .imports = &.{.{ .name = "contract_main", .module = contract }} }) }); _ = compile_contract.getEmittedBin(); b.step("compile-fsr-metal-contract-host", "Compile the actual Metal contract entry with inert embedded bytes and no execution").dependOn(&compile_contract.step); diff --git a/zig/build/gemma4_host.zig b/zig/build/gemma4_host.zig index 36958cb157..8fde58faae 100644 --- a/zig/build/gemma4_host.zig +++ b/zig/build/gemma4_host.zig @@ -40,6 +40,7 @@ pub fn compileHost(b: *std.Build, metal: *std.Build.Module, engine: *std.Build.M runner.addImport("h3_kernels", b.createModule(.{ .root_source_file = b.path("zig/kernels/metal/h3/kernels.zig") })); runner.addAnonymousImport("ling_metal", .{ .root_source_file = b.path("zig/kernels/metal/ling/sources.zig") }); runner.addAnonymousImport("ling_route", .{ .root_source_file = b.path("zig/src/families/ling/route.zig") }); + runner.addImport("masked_sources", b.createModule(.{ .root_source_file = b.path("zig/masked_value_sources.zig"), .imports = &.{.{ .name = "kernel_sources", .module = sources }} })); const contract_root = b.createModule(.{ .root_source_file = b.path("zig/gemma4_contract_compile.zig"), .target = b.graph.host, .link_libc = true, .imports = &.{.{ .name = "runner", .module = runner }} }); const contract_object = b.addObject(.{ .name = "gemma4-contract-compile", .root_module = contract_root }); _ = contract_object.getEmittedBin(); diff --git a/zig/build/residency_host.zig b/zig/build/residency_host.zig index 55378f3021..e184514552 100644 --- a/zig/build/residency_host.zig +++ b/zig/build/residency_host.zig @@ -23,18 +23,20 @@ pub fn steps(b: *std.Build) void { pub fn compileHost(b: *std.Build, metal: *std.Build.Module, engine: *std.Build.Module, lanes: *std.Build.Module) void { const api = b.createModule(.{ .root_source_file = b.path("zig/src/core/engine_api.zig"), .target = b.graph.host, .link_libc = true, .imports = &.{.{ .name = "lanes", .module = lanes }} }); const host = b.createModule(.{ .root_source_file = b.path("zig/src/native/metal.zig"), .target = b.graph.host, .link_libc = true, .imports = &.{ .{ .name = "metal", .module = metal }, .{ .name = "tensorfold", .module = engine }, .{ .name = "engine_api", .module = api } } }); - const bytes = b.addWriteFiles().add("residency-drama-empty.zig", "pub const bytes: []const u8 = &.{};\n"); + const bytes = b.createModule(.{ .root_source_file = b.addWriteFiles().add("residency-drama-empty.zig", "pub const bytes: []const u8 = &.{};\n") }); const tokenizer = b.createModule(.{ .root_source_file = b.path("zig/src/core/tokenizer/tokenizer.zig"), .target = b.graph.host, .link_libc = true }); const rounds = b.createModule(.{ .root_source_file = b.path("zig/src/core/frame_rounds.zig"), .target = b.graph.host }); const shim = b.addWriteFiles().add("residency-drama-core.zig", "pub const safetensors = @import(\"tensorfold\").safetensors;\npub const tokenizer = @import(\"tokenizer\");\npub const frame_rounds = @import(\"frame_rounds\");\n"); const core = b.createModule(.{ .root_source_file = shim, .target = b.graph.host, .imports = &.{ .{ .name = "tensorfold", .module = engine }, .{ .name = "tokenizer", .module = tokenizer }, .{ .name = "frame_rounds", .module = rounds } } }); - const drama = b.createModule(.{ .root_source_file = b.path("zig/src/families/drama3/drama3_metal.zig"), .target = b.graph.host, .link_libc = true, .imports = &.{ .{ .name = "metal", .module = metal }, .{ .name = "core", .module = core }, .{ .name = "drama3_metallib", .module = b.createModule(.{ .root_source_file = bytes }) } } }); + const drama = b.createModule(.{ .root_source_file = b.path("zig/src/families/drama3/drama3_metal.zig"), .target = b.graph.host, .link_libc = true, .imports = &.{ .{ .name = "metal", .module = metal }, .{ .name = "core", .module = core }, .{ .name = "drama3_metallib", .module = bytes } } }); host.addImport("core", core); host.addImport("tokenizer", tokenizer); host.addImport("drama3", drama); // native/metal_slide.zig imports living: the engine module's own (a file belongs to one module) host.addImport("living", engine.import_table.get("living") orelse @panic("the engine has no living import")); @import("qwen4.zig").metalNative(b, b.graph.host, host); + host.addImport("kimi_k3", @import("metal_targets.zig").kimiModule(b, b.graph.host, .Debug, metal, engine, lanes)); + host.addImport("kimi_metallib", bytes); b.step("test-metal-formats-host", "Actual Metal format offers with inert images and no device").dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = host, .filters = &.{"Metal capabilities:"} })).step); const root = b.createModule(.{ .root_source_file = b.path("zig/residency_native_compile.zig"), .target = b.graph.host, .link_libc = true, .imports = &.{.{ .name = "host", .module = host }} }); const object = b.addObject(.{ .name = "metal-residency-native", .root_module = root }); diff --git a/zig/gemma4_shared_affine_test.zig b/zig/gemma4_shared_affine_test.zig index 5c2b25871b..2f52f224c3 100644 --- a/zig/gemma4_shared_affine_test.zig +++ b/zig/gemma4_shared_affine_test.zig @@ -103,7 +103,7 @@ test "landed Metal source bytes are pinned without shader compilation" { { var hash: [32]u8 = undefined; std.crypto.hash.sha2.Sha256.hash(ks.core_affine_mm, &hash, .{}); - try std.testing.expectEqualStrings("d8f47cbbddf24502ca2c16cfdef0672731cc2dbe59ad027f47a3e662bcf8f627", &std.fmt.bytesToHex(hash, .lower)); + try std.testing.expectEqualStrings("a91a353981c4e24142673ab9c97e9a449b973751bd5df7ad7c980757046cf61e", &std.fmt.bytesToHex(hash, .lower)); } { var hash: [32]u8 = undefined; diff --git a/zig/src/families/glm/backend.zig b/zig/src/families/glm/backend.zig index 70609d6f99..0365621332 100644 --- a/zig/src/families/glm/backend.zig +++ b/zig/src/families/glm/backend.zig @@ -441,6 +441,10 @@ test "zero grant: GLM Metal MTP resumes after a long priced skip" { var e: Engine = undefined; e.w = &w; e.ep = null; + const objc = @import("metal").objc; + const no_arch = objc.msg(objc.Id, objc.class("NSObject"), "new", .{}); // facts() asks it for tensor units, no GPU + defer objc.release(no_arch); + e.device = .{ .id = no_arch }; var slots: [16]slots_mod.Slot = undefined; var sl: slots_mod.Slots = undefined; sl.e = &e; @@ -457,6 +461,10 @@ test "shared head costs: GLM Metal MTP retains costed copy grants" { var e: Engine = undefined; e.w = &w; e.ep = null; + const objc = @import("metal").objc; + const no_arch = objc.msg(objc.Id, objc.class("NSObject"), "new", .{}); // facts() asks it for tensor units, no GPU + defer objc.release(no_arch); + e.device = .{ .id = no_arch }; var slots: [16]slots_mod.Slot = undefined; var sl: slots_mod.Slots = undefined; sl.e = &e;