From 15594acc6b8286942815a6a97d17c8b5d3affc42 Mon Sep 17 00:00:00 2001 From: Vasilev Dmitrii Date: Fri, 14 Aug 2026 08:19:04 +0000 Subject: [PATCH 1/2] =?UTF-8?q?fix(build):=20=D0=B2=D0=BE=D1=81=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=B8=D1=82=D1=8C=20=D1=81=D0=B1?= =?UTF-8?q?=D0=BE=D1=80=D0=BA=D1=83=20tri=20=E2=80=94=20=D0=BC=D0=BE=D0=B4?= =?UTF-8?q?=D1=83=D0=BB=D0=B8,=20=D0=B8=D0=BC=D0=BF=D0=BE=D1=80=D1=82?= =?UTF-8?q?=D1=8B,=20Zig=200.15=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src/depin/mod.zig: один модуль depin вместо трёх пересекающихся - модули basal_ganglia/locus_coeruleus/reticular_formation из src/brain/ - storm_golden_chain: тип GoldenChain и runGoldenChainCommand из src/storm/ - модули kaggle/arrow_of_time/origin_of_life/quantum_gravity_full/ measurement/monopoles/superconductivity/gravity - внутренние импорты src/tri/** переведены на относительные пути - Zig 0.15: спецификаторы {:.Nf} -> {d:.N} (16 мест), ArrayList .items, managed append, лишний catch unreachable, totalVelocity без allocator - ASCII вместо юникода в [N]u8-буфере графика SPARC --- build.zig | 175 +++++++++++---- src/depin/mod.zig | 11 + src/depin/network.zig | 8 +- src/tri/claude_runtime.zig | 3 +- src/tri/dynamic_memory.zig | 2 +- src/tri/economy_engine.zig | 2 +- src/tri/economy_global_engine.zig | 2 +- src/tri/evolution.zig | 52 ++--- src/tri/learning_loops.zig | 2 +- src/tri/local_farm.zig | 332 ++++++++++++++++++++++++++++ src/tri/main.zig | 4 +- src/tri/marketplace_engine.zig | 2 +- src/tri/math/bench.zig | 3 +- src/tri/math/commands.zig | 8 +- src/tri/math/gematria.zig | 3 +- src/tri/math/mod.zig | 7 + src/tri/math/sacred_constants.zig | 9 + src/tri/math/ternary_complexity.zig | 2 +- src/tri/nft_marketplace.zig | 2 +- src/tri/nft_marketplace_engine.zig | 2 +- src/tri/sacred_commands.zig | 4 +- src/tri/sparc/cli.zig | 43 ++-- src/tri/tri_depin.zig | 10 +- src/tri/tri_storm.zig | 4 +- src/tri/zenodo_v16.zig | 2 +- src/tvc/tvc_corpus.zig | 31 +-- src/vibeec/trinity_cli.zig | 7 +- 27 files changed, 587 insertions(+), 145 deletions(-) create mode 100644 src/depin/mod.zig create mode 100644 src/tri/local_farm.zig diff --git a/build.zig b/build.zig index 88ec6852fa..4491ec6e53 100644 --- a/build.zig +++ b/build.zig @@ -1394,6 +1394,15 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); + // server.zig делает @import("trinity_workspace"), но модуль никогда здесь не + // объявлялся — файл `src/trinity_workspace.zig` лежит в репозитории, + // просто не был проводом подведён к цели. + const trinity_workspace_mod = b.createModule(.{ + .root_source_file = b.path("src/trinity_workspace.zig"), + .target = target, + .optimize = optimize, + }); + const trinity_mcp = b.addExecutable(.{ .name = "trinity-mcp", .root_module = b.createModule(.{ @@ -1405,6 +1414,7 @@ pub fn build(b: *std.Build) void { .{ .name = "vsa", .module = vsa_tri }, .{ .name = "treesitter_zig", .module = ts_zig_mod }, .{ .name = "tri_train", .module = tri_train_mod }, + .{ .name = "trinity_workspace", .module = trinity_workspace_mod }, }, }), }); @@ -1648,59 +1658,33 @@ pub fn build(b: *std.Build) void { // HSLM — Platform Benchmark Suite (for arXiv paper) // ═══════════════════════════════════════════════════════════════════════════ - const hslm_bench = b.addExecutable(.{ - .name = "hslm-bench", - .root_module = b.createModule(.{ - .root_source_file = b.path("src/hslm/hslm_benchmark.zig"), - .target = target, - .optimize = .ReleaseFast, - }), - }); - b.installArtifact(hslm_bench); - const run_hslm_bench = b.addRunArtifact(hslm_bench); - const hslm_bench_step = b.step("hslm-bench", "Run HSLM inference platform benchmark"); - hslm_bench_step.dependOn(&run_hslm_bench.step); + // `src/hslm/hslm_benchmark.zig` вынесен в gHashTag/trinity-training + // (36f38639c, "extract HSLM training to trinity-training repository"), а + // шаг сборки остался ссылаться на пустое место. Это одна из причин, по + // которой `zig build` не проходил с апреля. Шаг снят здесь, а не + // восстановлен файл: файл живёт в другом репозитории, и возвращать копию + // значило бы завести второй источник истины. + const hslm_bench_step = b.step("hslm-bench", "Перенесён в gHashTag/trinity-training"); + hslm_bench_step.dependOn(&b.addFail("hslm-bench живёт в gHashTag/trinity-training (36f38639c)").step); // ═══════════════════════════════════════════════════════════════════════════ // BPE Tokenizer Trainer // ═══════════════════════════════════════════════════════════════════════════ - const bpe_train = b.addExecutable(.{ - .name = "bpe-train", - .root_module = b.createModule(.{ - .root_source_file = b.path("src/hslm/bpe_train.zig"), - .target = target, - .optimize = .ReleaseFast, - }), - }); - b.installArtifact(bpe_train); - - const run_bpe_train = b.addRunArtifact(bpe_train); - if (b.args) |run_args| { - run_bpe_train.addArgs(run_args); - } - const bpe_step = b.step("bpe-train", "Train BPE tokenizer merge rules from corpus"); - bpe_step.dependOn(&run_bpe_train.step); + // `src/hslm/bpe_train.zig` — там же, gHashTag/trinity-training (36f38639c). + const bpe_step = b.step("bpe-train", "Перенесён в gHashTag/trinity-training"); + bpe_step.dependOn(&b.addFail("bpe-train живёт в gHashTag/trinity-training (36f38639c)").step); // ═══════════════════════════════════════════════════════════════════════════ // HSLM Entrypoint — Pure Zig replacement for entrypoint-train.sh // ═══════════════════════════════════════════════════════════════════════════ - const hslm_entrypoint = b.addExecutable(.{ - .name = "hslm-entrypoint", - .root_module = b.createModule(.{ - .root_source_file = b.path("src/cli/entrypoint_train.zig"), - .target = target, - .optimize = .ReleaseFast, - .link_libc = true, - }), - }); - b.installArtifact(hslm_entrypoint); + // `src/cli/entrypoint_train.zig` — там же, gHashTag/trinity-training + // (36f38639c). Railway-деплой обучения выполняется из того репозитория. - // train-deploy: build ONLY training binaries (for Railway Dockerfile — no raylib) - const train_deploy_step = b.step("train-deploy", "Build hslm-train + hslm-entrypoint for Railway deploy"); + // train-deploy: собирает только тренировочные бинари (Railway Dockerfile, без raylib) + const train_deploy_step = b.step("train-deploy", "Build hslm-train for Railway deploy"); train_deploy_step.dependOn(&hslm_train.step); - train_deploy_step.dependOn(&hslm_entrypoint.step); // ═══════════════════════════════════════════════════════════════════════════ // Railway Redeploy Tool — Bypasses PreToolUse hook for Railway API @@ -2027,6 +2011,91 @@ pub fn build(b: *std.Build) void { .{ .name = "bsd", .module = bsd_mod }, // P1.6: Registry module for commands export and MCP tools .{ .name = "registry", .module = registry_mod }, + // Научные подкоманды `tri`: файлы лежат в тематических каталогах, + // модулей с ожидаемыми именами в build.zig не было. + .{ .name = "kaggle", .module = b.createModule(.{ + .root_source_file = b.path("src/kaggle/kaggle.zig"), + .target = target, + .optimize = optimize, + }) }, + .{ .name = "arrow_of_time", .module = b.createModule(.{ + .root_source_file = b.path("src/time/arrow_of_time.zig"), + .target = target, + .optimize = optimize, + }) }, + .{ .name = "origin_of_life", .module = b.createModule(.{ + .root_source_file = b.path("src/biology/origin_of_life.zig"), + .target = target, + .optimize = optimize, + }) }, + .{ .name = "quantum_gravity_full", .module = b.createModule(.{ + .root_source_file = b.path("src/gravity/quantum_gravity_full.zig"), + .target = target, + .optimize = optimize, + }) }, + .{ .name = "measurement", .module = b.createModule(.{ + .root_source_file = b.path("src/quantum/measurement_problem.zig"), + .target = target, + .optimize = optimize, + }) }, + .{ .name = "monopoles", .module = b.createModule(.{ + .root_source_file = b.path("src/monopoles/sacred_monopoles.zig"), + .target = target, + .optimize = optimize, + }) }, + .{ .name = "superconductivity", .module = b.createModule(.{ + .root_source_file = b.path("src/superconductivity/room_temperature_superconductivity.zig"), + .target = target, + .optimize = optimize, + }) }, + .{ .name = "gravity", .module = b.createModule(.{ + .root_source_file = b.path("src/gravity/black_hole_information.zig"), + .target = target, + .optimize = optimize, + }) }, + .{ .name = "storm_golden_chain", .module = b.createModule(.{ + .root_source_file = b.path("src/storm/golden_chain.zig"), + .target = target, + .optimize = optimize, + }) }, + // Мозговые ядра: полные реализации лежат в src/brain/ (в src/tri/ + // остались урезанные копии — src/tri/basal_ganglia.zig без поля + // Stats.active_claims). Объявляем модулями с корнем в src/brain/. + .{ .name = "basal_ganglia", .module = b.createModule(.{ + .root_source_file = b.path("src/brain/basal_ganglia.zig"), + .target = target, + .optimize = optimize, + }) }, + .{ .name = "locus_coeruleus", .module = b.createModule(.{ + .root_source_file = b.path("src/brain/locus_coeruleus.zig"), + .target = target, + .optimize = optimize, + }) }, + .{ .name = "reticular_formation", .module = b.createModule(.{ + .root_source_file = b.path("src/brain/reticular_formation.zig"), + .target = target, + .optimize = optimize, + }) }, + // DePIN: network/bootstrap/persistence ссылаются друг на друга, + // поэтому это ОДИН модуль с корнем src/depin/mod.zig. + .{ .name = "depin", .module = b.createModule(.{ + .root_source_file = b.path("src/depin/mod.zig"), + .target = target, + .optimize = optimize, + }) }, + .{ .name = "firebird_slashing", .module = b.createModule(.{ + .root_source_file = b.path("src/firebird/slashing.zig"), + .target = target, + .optimize = optimize, + }) }, + // src/tri/main.zig роутит `tri tri27 ` через @import("tri27_cli"), + // но модуль не был объявлен. Файл есть: src/tri27/tri27_cli.zig + // (экспортирует runTri27Command). + .{ .name = "tri27_cli", .module = b.createModule(.{ + .root_source_file = b.path("src/tri27/tri27_cli.zig"), + .target = target, + .optimize = optimize, + }) }, }, }), }); @@ -2249,10 +2318,15 @@ pub fn build(b: *std.Build) void { }), }); trinity_canvas.linkSystemLibrary("raylib"); - // v8.4: Add raygui include path and C implementation - trinity_canvas.addIncludePath(b.path("external/raygui/src")); - trinity_canvas.addCSourceFile(.{ .file = b.path("src/vsa/raygui_impl.c") }); - // TEMP: Disable install until raygui.h is available + // raygui недоступен: `external/raygui/src` в репозитории нет (сторонний + // header-only пакет никогда не вендорился), а `src/vsa/raygui_impl.c` + // удалён в 42490a222 вместе с остальными миграциями. Установка артефакта + // уже была отключена по этой же причине — теперь отключены и две ссылки на + // отсутствующие пути, которые продолжали ломать `zig build` целиком. + // Чтобы вернуть: вендорить raygui в external/raygui и восстановить + // raygui_impl.c из 42490a222^. + // trinity_canvas.addIncludePath(b.path("external/raygui/src")); + // trinity_canvas.addCSourceFile(.{ .file = b.path("src/vsa/raygui_impl.c") }); // b.installArtifact(trinity_canvas); const run_trinity_canvas = b.addRunArtifact(trinity_canvas); @@ -2574,12 +2648,23 @@ pub fn build(b: *std.Build) void { // TRI-API — Direct Anthropic API Agent (Issue #60) // ═══════════════════════════════════════════════════════════════════════════════ + // То же самое для `token_rotator`: `src/tri/token_rotator.zig` есть в репозитории, + // а main.zig его импортирует как модуль, который не объявлялся. + const token_rotator_mod = b.createModule(.{ + .root_source_file = b.path("src/tri/token_rotator.zig"), + .target = target, + .optimize = optimize, + }); + const tri_api = b.addExecutable(.{ .name = "tri-api", .root_module = b.createModule(.{ .root_source_file = b.path("src/tri-api/main.zig"), .target = target, .optimize = optimize, + .imports = &.{ + .{ .name = "token_rotator", .module = token_rotator_mod }, + }, }), }); b.installArtifact(tri_api); diff --git a/src/depin/mod.zig b/src/depin/mod.zig new file mode 100644 index 0000000000..265f3e5a61 --- /dev/null +++ b/src/depin/mod.zig @@ -0,0 +1,11 @@ +//! Корень модуля DePIN. +//! +//! network.zig, bootstrap.zig и persistence.zig ссылаются друг на друга, +//! поэтому объявлять каждый отдельным модулем нельзя — Zig сообщает +//! «file exists in modules ... and ...». Один модуль-корень с реэкспортами +//! снимает проблему: все файлы попадают ровно в один модуль. + +pub const network = @import("network.zig"); +pub const bootstrap = @import("bootstrap.zig"); +pub const persistence = @import("persistence.zig"); +pub const metrics = @import("metrics.zig"); diff --git a/src/depin/network.zig b/src/depin/network.zig index 7c83dc0670..6e4e3f7ee1 100644 --- a/src/depin/network.zig +++ b/src/depin/network.zig @@ -5,9 +5,11 @@ // ═══════════════════════════════════════════════════════════════════════════════ const std = @import("std"); -const bootstrap = @import("bootstrap"); -const persistence = @import("persistence"); -const metrics_mod = @import("metrics"); +// Соседние файлы в том же каталоге; как модули они нигде не объявлены, +// поэтому импорт по имени не разрешался. Берём по относительному пути. +const bootstrap = @import("bootstrap.zig"); +const persistence = @import("persistence.zig"); +const metrics_mod = @import("metrics.zig"); // Forward decls for firebird types (will be imported by build.zig) pub const NodeStatus = enum { diff --git a/src/tri/claude_runtime.zig b/src/tri/claude_runtime.zig index 8a86790239..a9c9f13231 100644 --- a/src/tri/claude_runtime.zig +++ b/src/tri/claude_runtime.zig @@ -218,7 +218,8 @@ fn runVersionCheck(allocator: Allocator) !void { const min_version_str = "2.1.80"; const meets_min = try compareVersions(allocator, version, min_version_str); if (!meets_min) { - std.debug.print("{s}⚠ Channels requires Claude {s} or later{m}\n", .{ YELLOW, min_version_str, RESET }); + // Было `{m}` — недопустимый спецификатор для строки, компиляция падала. + std.debug.print("{s}⚠ Channels requires Claude {s} or later{s}\n", .{ YELLOW, min_version_str, RESET }); } } diff --git a/src/tri/dynamic_memory.zig b/src/tri/dynamic_memory.zig index 978284a087..dda25c5526 100644 --- a/src/tri/dynamic_memory.zig +++ b/src/tri/dynamic_memory.zig @@ -20,7 +20,7 @@ const Allocator = std.mem.Allocator; // ═══════════════════════════════════════════════════════════════════════════════ // Constants imported from canonical source -const sacred_constants = @import("sacred_constants"); +const sacred_constants = @import("sacred_constants.zig"); pub const PHI = sacred_constants.SacredConstants.PHI; pub const PHI_INV = sacred_constants.SacredConstants.PHI_INVERSE; pub const PHI_SQ = sacred_constants.SacredConstants.PHI_SQ; diff --git a/src/tri/economy_engine.zig b/src/tri/economy_engine.zig index 631011460c..b4fe91bc8a 100644 --- a/src/tri/economy_engine.zig +++ b/src/tri/economy_engine.zig @@ -15,7 +15,7 @@ const std = @import("std"); const math = std.math; // Import canonical constants (NOT inline - anti-pattern!) -const sacred_constants = @import("sacred_constants"); +const sacred_constants = @import("sacred_constants.zig"); // ═══════════════════════════════════════════════════════════════════════════════ // CONSTANTS (export from canonical source) diff --git a/src/tri/economy_global_engine.zig b/src/tri/economy_global_engine.zig index 400bc4bd36..d2799ffe01 100644 --- a/src/tri/economy_global_engine.zig +++ b/src/tri/economy_global_engine.zig @@ -16,7 +16,7 @@ const std = @import("std"); const math = std.math; // Import canonical constants (NOT inline - anti-pattern!) -const sacred_constants = @import("sacred_constants"); +const sacred_constants = @import("sacred_constants.zig"); // ═════════════════════════════════════════════════════════════════════════════ // CONSTANTS (export from canonical source + derived) diff --git a/src/tri/evolution.zig b/src/tri/evolution.zig index bdb3457994..66f790722a 100644 --- a/src/tri/evolution.zig +++ b/src/tri/evolution.zig @@ -25,7 +25,6 @@ const RailwayApi = railway_api.RailwayApi; const tri_commands = @import("tri_commands.zig"); const farm_ws = @import("tri_farm_ws.zig"); const hippocampus = @import("hippocampus.zig"); -const bench = @import("bench"); const print = std.debug.print; // ANSI colors @@ -299,43 +298,22 @@ const ServiceEntry = struct { } }; -/// Run IGLA benchmark for a service and update its metrics -/// Returns accuracy score [0, 1], or 0.0 on failure +/// Заглушка IGLA-бенчмарка. +/// +/// Реализация (src/bench/igla_bench.zig, igla_runner.zig, igla_metrics.zig, +/// igla_tasks.zig) вынесена из этого репозитория в gHashTag/trinity-training +/// коммитом 42490a222 «remove 4425 lines of migrated duplicates (#517)», но +/// вызовы `bench.igla.parseFormatString` / `bench.igla.runSingleConfig` +/// остались здесь и ломали сборку цели `tri`. +/// +/// Возвращаем 0.0 и явно печатаем причину. Никаких синтетических чисел: +/// метрики IGLA не измеряются, пока модуль не подключён обратно. +/// [ТРЕБУЕТ ДЕЙСТВИЯ] подключить trinity-training как зависимость или вернуть +/// бенчмарк в этот репозиторий. fn runIGLABenchmark(allocator: Allocator, service: *ServiceEntry) !f32 { - // Map service format to IGLA WeightFormat - const format_str = service.formatStr(); - const format = bench.igla.parseFormatString(format_str); - - // Use service context length (clamp to reasonable range for benchmark) - const ctx_len = @max(27, @min(243, @as(usize, @intCast(service.context)))); - - // Run single config benchmark with 1 needle at 50% depth - const result = bench.igla.runSingleConfig(allocator, format, ctx_len, 1, 0.5) catch |err| { - print("{s}⚠️ IGLA bench failed for {s}: {s}{s}\n", .{ YELLOW, service.svcName(), @errorName(err), RESET }); - return 0.0; - }; - - // Update service fields with IGLA metrics - service.igla_score = result.accuracy; - service.igla_latency_ms = result.latency_ms; - service.igla_tok_per_sec = result.tok_per_sec; - - // Update format-specific accuracy (map format enum to array index) - const format_idx: u3 = switch (result.format) { - .STD => 0, - .BF16 => 1, - .GF16 => 2, - .TF3 => 3, - }; - service.igla_format_accuracy[format_idx] = result.accuracy; - - // For now, use overall accuracy for all question types (single config test) - service.igla_retrieve_acc = result.accuracy; - service.igla_multi_acc = result.accuracy; - service.igla_ternary_acc = result.accuracy; - service.igla_chain_acc = result.accuracy; - - return result.accuracy; + _ = allocator; + print("{s}IGLA-бенчмарк не подключён (перенесён в gHashTag/trinity-training): {s} — метрика не измерена{s}\n", .{ YELLOW, service.svcName(), RESET }); + return 0.0; } const EventType = enum(u8) { diff --git a/src/tri/learning_loops.zig b/src/tri/learning_loops.zig index 06348b16b7..c72dfc0661 100644 --- a/src/tri/learning_loops.zig +++ b/src/tri/learning_loops.zig @@ -20,7 +20,7 @@ const Allocator = std.mem.Allocator; // ═══════════════════════════════════════════════════════════════════════════════ // Constants imported from canonical source -const sacred_constants = @import("sacred_constants"); +const sacred_constants = @import("sacred_constants.zig"); pub const PHI = sacred_constants.SacredConstants.PHI; pub const PHI_INV = sacred_constants.SacredConstants.PHI_INVERSE; pub const PHI_SQ = sacred_constants.SacredConstants.PHI_SQ; diff --git a/src/tri/local_farm.zig b/src/tri/local_farm.zig new file mode 100644 index 0000000000..a202d568d4 --- /dev/null +++ b/src/tri/local_farm.zig @@ -0,0 +1,332 @@ +// LOCAL FARM MANAGEMENT — Wave 9 S3 MultiObj +// Tracks local Docker workers, checkpoints, PPL, crash history +// +// φ² + 1/φ² = 3 = TRINITY + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const print = std.debug.print; + +const RESET = "\x1b[0m"; +const BOLD = "\x1b[1m"; +const GREEN = "\x1b[32m"; +const RED = "\x1b[31m"; +const YELLOW = "\x1b[33m"; +const CYAN = "\x1b[36m"; +const DIM = "\x1b[2m"; + +const STATE_FILE = ".trinity/local_farm.json"; +const MAX_WORKERS = 48; + +pub const WorkerStatus = enum { + stopped, + starting, + training, + crashed, + finished, + unknown, +}; + +pub const Worker = struct { + id: usize, + container_name: []const u8, + status: WorkerStatus, + step: u64, + ppl: f32, + loss: f32, + seed: u32, + created_at: i64, + updated_at: i64, + crash_count: u8, + last_error: ?[]const u8, +}; + +pub const LocalFarm = struct { + workers: std.ArrayListUnmanaged(Worker), + created_at: i64, + updated_at: i64, + total_steps: u64, + avg_ppl: f32, + best_ppl: f32, + best_worker_id: ?usize, + + const Self = @This(); + + pub fn init(_: Allocator) !Self { + return Self{ + .workers = std.ArrayListUnmanaged(Worker){}, + .created_at = 0, + .updated_at = 0, + .total_steps = 0, + .avg_ppl = 0.0, + .best_ppl = std.math.inf(f32), + .best_worker_id = null, + }; + } + + pub fn deinit(self: *Self, allocator: Allocator) void { + for (self.workers.items) |*w| { + if (w.last_error) |err| { + allocator.free(err); + } + } + self.workers.deinit(allocator); + } + + pub fn load(allocator: Allocator) !Self { + const file = std.fs.cwd().openFile(STATE_FILE, .{}) catch |err| switch (err) { + error.FileNotFound => return init(allocator), + else => return err, + }; + defer file.close(); + + const content = try file.readToEndAlloc(allocator, 1024 * 1024); // 1MB max + defer allocator.free(content); + + const parsed = try std.json.parseFromSlice(Self, allocator, content, .{}); + defer parsed.deinit(); + + // Convert managed to unmanaged + var result = try init(allocator); + for (parsed.value.workers.items) |w| { + try result.workers.append(allocator, w); + } + result.created_at = parsed.value.created_at; + result.updated_at = parsed.value.updated_at; + result.total_steps = parsed.value.total_steps; + result.avg_ppl = parsed.value.avg_ppl; + result.best_ppl = parsed.value.best_ppl; + result.best_worker_id = parsed.value.best_worker_id; + + return result; + } + + pub fn save(self: *const Self, allocator: Allocator) !void { + // Use inline slice for JSON serialization + const json_farm = struct { + workers: []const Worker, + created_at: i64, + updated_at: i64, + total_steps: u64, + avg_ppl: f32, + best_ppl: f32, + best_worker_id: ?usize, + }{ + .workers = self.workers.items, + .created_at = self.created_at, + .updated_at = self.updated_at, + .total_steps = self.total_steps, + .avg_ppl = self.avg_ppl, + .best_ppl = self.best_ppl, + .best_worker_id = self.best_worker_id, + }; + + const state_dir = std.fs.path.dirname(STATE_FILE) orelse "."; + std.fs.cwd().makeDir(state_dir) catch |err| switch (err) { + error.PathAlreadyExists => {}, + else => return err, + }; + + const file = try std.fs.cwd().createFile(STATE_FILE, .{}); + defer file.close(); + + const json_str = try std.json.Stringify.valueAlloc(allocator, json_farm, .{ .whitespace = .indent_2 }); + defer allocator.free(json_str); + try file.writeAll(json_str); + } + + pub fn addWorker(self: *Self, allocator: Allocator, id: usize, seed: u32) !void { + const container_name = try std.fmt.allocPrint(allocator, "wave9-w{d}", .{id}); + const now = std.time.timestamp(); + + try self.workers.append(allocator, .{ + .id = id, + .container_name = container_name, + .status = .stopped, + .step = 0, + .ppl = std.math.inf(f32), + .loss = std.math.inf(f32), + .seed = seed, + .created_at = now, + .updated_at = now, + .crash_count = 0, + .last_error = null, + }); + + self.updated_at = now; + } + + pub fn getWorker(self: *Self, id: usize) ?*Worker { + for (self.workers.items) |*w| { + if (w.id == id) return w; + } + return null; + } + + pub fn updateWorkerStatus(self: *Self, id: usize, status: WorkerStatus) !void { + if (self.getWorker(id)) |w| { + w.status = status; + w.updated_at = std.time.timestamp(); + } + } + + pub fn updateWorkerMetrics(self: *Self, id: usize, step: u64, ppl: f32, loss: f32) !void { + if (self.getWorker(id)) |w| { + w.step = step; + w.ppl = ppl; + w.loss = loss; + w.updated_at = std.time.timestamp(); + + if (ppl < self.best_ppl) { + self.best_ppl = ppl; + self.best_worker_id = id; + } + + self.recalculateAggregates(); + } + } + + fn recalculateAggregates(self: *Self) void { + var active_count: usize = 0; + var sum_ppl: f32 = 0.0; + self.total_steps = 0; + + for (self.workers.items) |w| { + if (w.status == .training or w.status == .finished) { + if (w.ppl < std.math.inf(f32)) { + sum_ppl += w.ppl; + active_count += 1; + } + self.total_steps += w.step; + } + } + + self.avg_ppl = if (active_count > 0) sum_ppl / @as(f32, @floatFromInt(active_count)) else 0.0; + } + + pub fn countByStatus(self: *const Self, status: WorkerStatus) usize { + var count: usize = 0; + for (self.workers.items) |w| { + if (w.status == status) count += 1; + } + return count; + } + + pub fn displayStatus(self: *const Self) void { + print("{s}═══════════════════════════════════════════════════════════════{s}\n", .{ BOLD, RESET }); + print("{s}WAVE 9 LOCAL FARM — S3 MultiObj{s}\n", .{ BOLD, RESET }); + print("{s}═══════════════════════════════════════════════════════════════{s}\n\n", .{ BOLD, RESET }); + + print("{s}Workers: {d}/{d} {s}│{s} Avg PPL: {d:5.2} {s}│{s} Best PPL: {d:5.2} (w{d}) {s}│{s} Total Steps: {d}\n\n", .{ CYAN, self.workers.items.len, MAX_WORKERS, RESET, GREEN, self.avg_ppl, RESET, GREEN, self.best_ppl, self.best_worker_id orelse 0, RESET, CYAN, self.total_steps }); + + print("{s}Status Breakdown:{s}\n", .{ DIM, RESET }); + print(" {s}🟢 Training:{s} {d:2} {s}🔄 Starting:{s} {d:2} {s}✅ Finished:{s} {d:2} {s}💀 Crashed:{s} {d:2} {s}⏸️ Stopped:{s} {d:2}\n\n", .{ GREEN, RESET, self.countByStatus(.training), YELLOW, RESET, self.countByStatus(.starting), GREEN, RESET, self.countByStatus(.finished), RED, RESET, self.countByStatus(.crashed), DIM, RESET, self.countByStatus(.stopped) }); + + print("{s}Workers:{s}\n", .{ BOLD, RESET }); + for (self.workers.items) |w| { + const status_emoji = switch (w.status) { + .stopped => "⏸️", + .starting => "🔄", + .training => "🟢", + .crashed => "💀", + .finished => "✅", + .unknown => "❓", + }; + const status_color = switch (w.status) { + .stopped => DIM, + .starting => YELLOW, + .training => GREEN, + .crashed => RED, + .finished => GREEN, + .unknown => DIM, + }; + print(" {s}{s} w{d:2} {s}step={d:6} PPL={d:5.2} loss={d:6.3} seed={d:4}{s}\n", .{ status_color, status_emoji, w.id, RESET, w.step, w.ppl, w.loss, w.seed, RESET }); + if (w.crash_count > 0) { + print(" {s}[crashes: {d}]{s}\n", .{ RED, w.crash_count, RESET }); + } + } + print("\n", .{}); + } +}; + +// ───────────────────────────────────────────────────────────────────────────── +// DOCKER COMMAND WRAPPER +// ───────────────────────────────────────────────────────────────────────────── + +pub const DockerResult = struct { + stdout: []const u8, + stderr: []const u8, + exit_code: u32, +}; + +pub fn runDockerCompose(allocator: Allocator, args: []const []const u8) !DockerResult { + var argv = try std.ArrayListUnmanaged([]const u8).initCapacity(allocator, args.len + 1); + defer argv.deinit(allocator); + try argv.append(allocator, "docker-compose"); + try argv.appendSlice(allocator, args); + + const result = try std.process.Child.run(.{ + .allocator = allocator, + .argv = argv.items, + }); + + return DockerResult{ + .stdout = result.stdout, + .stderr = result.stderr, + .exit_code = if (result.term == .Exited) @as(u32, @intCast(result.term.Exited)) else 1, + }; +} + +pub fn composeUp(allocator: Allocator, compose_file: []const u8, workers: ?[]const u8) !DockerResult { + var args = try std.ArrayListUnmanaged([]const u8).initCapacity(allocator, 5); + defer args.deinit(allocator); + + try args.append(allocator, "-f"); + try args.append(allocator, compose_file); + try args.append(allocator, "up"); + try args.append(allocator, "-d"); + + if (workers) |w| { + try args.append(allocator, w); + } + + return runDockerCompose(allocator, try args.toOwnedSlice(allocator)); +} + +pub fn composeStop(allocator: Allocator, compose_file: []const u8, workers: ?[]const u8) !DockerResult { + var args = try std.ArrayListUnmanaged([]const u8).initCapacity(allocator, 4); + defer args.deinit(allocator); + + try args.append(allocator, "-f"); + try args.append(allocator, compose_file); + try args.append(allocator, "stop"); + + if (workers) |w| { + try args.append(allocator, w); + } + + return runDockerCompose(allocator, try args.toOwnedSlice(allocator)); +} + +pub fn composeLogs(allocator: Allocator, compose_file: []const u8, worker: []const u8, follow: bool) !DockerResult { + var args = try std.ArrayListUnmanaged([]const u8).initCapacity(allocator, 5); + defer args.deinit(allocator); + + try args.append(allocator, "-f"); + try args.append(allocator, compose_file); + try args.append(allocator, "logs"); + if (follow) try args.append(allocator, "-f"); + try args.append(allocator, worker); + + return runDockerCompose(allocator, try args.toOwnedSlice(allocator)); +} + +pub fn composePs(allocator: Allocator, compose_file: []const u8) !DockerResult { + const args = &[_][]const u8{ "-f", compose_file, "ps" }; + return runDockerCompose(allocator, args[0..args.len]); +} + +pub fn containerExec(allocator: Allocator, container: []const u8, cmd: []const u8) !DockerResult { + const args = &[_][]const u8{ "exec", container, "sh", "-c", cmd }; + return runDockerCompose(allocator, args[0..args.len]); +} diff --git a/src/tri/main.zig b/src/tri/main.zig index 777fb1c6dd..dc5b528432 100644 --- a/src/tri/main.zig +++ b/src/tri/main.zig @@ -42,7 +42,9 @@ const faculty_board = @import("cortex.zig"); const observability = @import("observability.zig"); const structured_log = @import("structured_log.zig"); const env_loader = @import("env_loader.zig"); -const golden_chain = @import("golden_chain"); +// runGoldenChainCommand объявлена в src/storm/golden_chain.zig, а не в +// одноимённом файле рядом — берём модуль storm_golden_chain. +const golden_chain = @import("storm_golden_chain"); const tri_clara = @import("tri_clara.zig"); const tri_sparc = @import("sparc/mod.zig"); diff --git a/src/tri/marketplace_engine.zig b/src/tri/marketplace_engine.zig index c80afa44a7..62895070ed 100644 --- a/src/tri/marketplace_engine.zig +++ b/src/tri/marketplace_engine.zig @@ -17,7 +17,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; // Import canonical constants (NOT inline - anti-pattern!) -const sacred_constants = @import("sacred_constants"); +const sacred_constants = @import("sacred_constants.zig"); const PHI = sacred_constants.SacredConstants.PHI; const PHI_SQ = sacred_constants.SacredConstants.PHI_SQ; const PI = sacred_constants.SacredConstants.PI; diff --git a/src/tri/math/bench.zig b/src/tri/math/bench.zig index 61f3c6ad70..8c7c423a98 100644 --- a/src/tri/math/bench.zig +++ b/src/tri/math/bench.zig @@ -14,7 +14,8 @@ const std = @import("std"); const parent_mod = @import("mod.zig"); const format = @import("format.zig"); const sacred_formula = @import("formula.zig"); -const gematria_engine = @import("gematria.zig"); +// gematria.zig рядом — это CLI-обёртка; движок с numberToGlyphs в src/tri/gematria.zig. +const gematria_engine = @import("../gematria.zig"); // ═══════════════════════════════════════════════════════════════════════════════ // BENCHMARK RESULT diff --git a/src/tri/math/commands.zig b/src/tri/math/commands.zig index 6f1ae609a2..f09687d224 100644 --- a/src/tri/math/commands.zig +++ b/src/tri/math/commands.zig @@ -11,8 +11,8 @@ // ═══════════════════════════════════════════════════════════════════════════════ const std = @import("std"); -const unified = @import("unified_output.zig"); -const tri_exit_codes = @import("tri_exit_codes.zig"); +const unified = @import("../unified_output.zig"); +const tri_exit_codes = @import("../tri_exit_codes.zig"); const parent_mod = @import("mod.zig"); const format = @import("format.zig"); @@ -24,7 +24,7 @@ const identities_mod = @import("identities.zig"); const gematria_math = @import("gematria.zig"); const sacred_formula = @import("formula.zig"); const blind_spots_mod = @import("blind_spots.zig"); -const sacred_v2 = @import("tri_sacred_v2.zig"); +const sacred_v2 = @import("../tri_sacred_v2.zig"); const prediction_mod = @import("prediction.zig"); const constants_table = @import("constants_table.zig"); @@ -252,7 +252,7 @@ pub fn runMathCommand(allocator: std.mem.Allocator, args: []const []const u8) !v } pub fn runConstantsCommand(allocator: std.mem.Allocator, args: []const []const u8) !void { - const tri_config = @import("tri_config.zig"); + const tri_config = @import("../tri_config.zig"); // Check global JSON flag first const global_json = tri_config.isJsonOutput(); diff --git a/src/tri/math/gematria.zig b/src/tri/math/gematria.zig index 66c9f8054b..fea6e8576e 100644 --- a/src/tri/math/gematria.zig +++ b/src/tri/math/gematria.zig @@ -18,7 +18,8 @@ const std = @import("std"); const Allocator = std.mem.Allocator; -const gematria_engine = @import("gematria.zig"); +// Файл импортировал сам себя; движок с numberToGlyphs лежит в src/tri/gematria.zig. +const gematria_engine = @import("../gematria.zig"); const sacred_formula = @import("formula.zig"); // ANSI colors diff --git a/src/tri/math/mod.zig b/src/tri/math/mod.zig index 1bebd8a6bd..91e5049b3d 100644 --- a/src/tri/math/mod.zig +++ b/src/tri/math/mod.zig @@ -26,6 +26,13 @@ pub const PI = sacred.PI; pub const E = sacred.E; pub const TAU = sacred.TAU; pub const TRANSCENDENTAL = sacred.PI * sacred.PHI; +// Эволюционные константы: commands.zig печатает их в JSON (`mu`,`chi`,`sigma`, +// `epsilon`), но реэкспорта здесь не было — цель `tri` не собиралась. +pub const MU = sacred.MU; +pub const CHI = sacred.CHI; +pub const SIGMA = sacred.SIGMA; +pub const EPSILON = sacred.EPSILON; + pub const format = @import("format.zig"); pub const constants = @import("constants.zig"); diff --git a/src/tri/math/sacred_constants.zig b/src/tri/math/sacred_constants.zig index 1221e13eb0..6ef4a0286a 100644 --- a/src/tri/math/sacred_constants.zig +++ b/src/tri/math/sacred_constants.zig @@ -61,6 +61,15 @@ pub const LOG3_2: f64 = 0.6309297535714574; /// μ = 1/φ²/10 = 0.0382 (intelligence gain per fix) pub const MU: f64 = PHI_INV_SQ / 10.0; +/// χ = 1/φ/10 ≈ 0.0618 (crossover rate) — эволюционная константа из φ +pub const CHI: f64 = PHI_INV / 10.0; + +/// σ = φ ≈ 1.618 (selection pressure) — эволюционная константа из φ +pub const SIGMA: f64 = PHI; + +/// ε = 1/3 ≈ 0.333 (elitism ratio) — доля от золотого тождества φ²+φ⁻²=3 +pub const EPSILON: f64 = 1.0 / 3.0; + /// Lucas number L(10) = 123 (Trinity checksum) pub const LAMBDA_10: f64 = 123.0; diff --git a/src/tri/math/ternary_complexity.zig b/src/tri/math/ternary_complexity.zig index 830fe98038..3e6e723b9d 100644 --- a/src/tri/math/ternary_complexity.zig +++ b/src/tri/math/ternary_complexity.zig @@ -22,7 +22,7 @@ //! //! ``` //! const std = @import("std"); -//! const ternary = @import("ternary_complexity"); +//! const ternary = @import("ternary_complexity.zig"); //! //! // Analyze fine structure constant //! const alpha_inv = 137.036; diff --git a/src/tri/nft_marketplace.zig b/src/tri/nft_marketplace.zig index c9f76174ef..11c2418cdc 100644 --- a/src/tri/nft_marketplace.zig +++ b/src/tri/nft_marketplace.zig @@ -15,7 +15,7 @@ const std = @import("std"); const math = std.math; // Import canonical constants (NOT inline - anti-pattern!) -const sacred_constants = @import("sacred_constants"); +const sacred_constants = @import("sacred_constants.zig"); // ═══════════════════════════════════════════════════════════════════════════════ // CONSTANTS (export from canonical source) diff --git a/src/tri/nft_marketplace_engine.zig b/src/tri/nft_marketplace_engine.zig index 827b9b32fb..bdb74f1e4c 100644 --- a/src/tri/nft_marketplace_engine.zig +++ b/src/tri/nft_marketplace_engine.zig @@ -16,7 +16,7 @@ const std = @import("std"); const math = std.math; // Import canonical constants (NOT inline - anti-pattern!) -const sacred_constants = @import("sacred_constants"); +const sacred_constants = @import("sacred_constants.zig"); // ═══════════════════════════════════════════════════════════════════════════════ // CONSTANTS (from canonical source) diff --git a/src/tri/sacred_commands.zig b/src/tri/sacred_commands.zig index 79a849f8dd..635d433dbb 100644 --- a/src/tri/sacred_commands.zig +++ b/src/tri/sacred_commands.zig @@ -20,13 +20,13 @@ const std = @import("std"); /// Run Sacred ALU benchmark (Phase 6.3) /// Usage: tri sacred bench [--n=N] [--mode=ALL|gf16_add|...] [--output=csv|human] pub fn runSacredBenchCommand(allocator: std.mem.Allocator, args: []const []const u8) !void { - @import("sacred_bench").runSacredBenchCommand(allocator, args); + @import("sacred_bench.zig").runSacredBenchCommand(allocator, args); } /// Parse Yosys synthesis JSON and display resource statistics (Phase 6.4) /// Usage: tri sacred synth-report [--input=PATH] [--output=human|csv|json] pub fn runSacredSynthReportCommand(allocator: std.mem.Allocator, args: []const []const u8) !void { - @import("sacred_synth_report").runSacredSynthReportCommand(allocator, args); + @import("sacred_synth_report.zig").runSacredSynthReportCommand(allocator, args); } // ============================================================================= diff --git a/src/tri/sparc/cli.zig b/src/tri/sparc/cli.zig index 1fd41915b0..48b0d03b6a 100644 --- a/src/tri/sparc/cli.zig +++ b/src/tri/sparc/cli.zig @@ -303,7 +303,7 @@ fn runBatchFit(allocator: Allocator, data_content: []const u8, options: SparcOpt if (options.verbose) { const status = if (is_good) colorize("GOOD", .green) else colorize("POOR", .red); - std.debug.print("{s} (χ²={:.3f})\n", .{ status, result.reduced_chi_squared }); + std.debug.print("{s} (χ²={d:.3})\n", .{ status, result.reduced_chi_squared }); } else { // Simple progress indicator if ((i + 1) % 10 == 0 or i + 1 == galaxies.len) { @@ -336,9 +336,9 @@ fn runBatchFit(allocator: Allocator, data_content: []const u8, options: SparcOpt std.debug.print("{s}Batch Fit Summary{s}\n", .{ colorize("════════════════════════════════════════════════════════════", .cyan), colorize("", .reset) }); std.debug.print("\n", .{}); std.debug.print(" Total galaxies: {d}\n", .{results.items.len}); - std.debug.print(" Good fits (χ² < 2.0): {d} ({:.1f}%)\n", .{ total_good, if (results.items.len > 0) @as(f64, @floatFromInt(total_good)) / @as(f64, @floatFromInt(results.items.len)) * 100 else 0 }); - std.debug.print(" Mean reduced χ²: {:.3f}\n", .{mean_chi}); - std.debug.print(" Median reduced χ²: {:.3f}\n", .{median_chi}); + std.debug.print(" Good fits (χ² < 2.0): {d} ({d:.1}%)\n", .{ total_good, if (results.items.len > 0) @as(f64, @floatFromInt(total_good)) / @as(f64, @floatFromInt(results.items.len)) * 100 else 0 }); + std.debug.print(" Mean reduced χ²: {d:.3}\n", .{mean_chi}); + std.debug.print(" Median reduced χ²: {d:.3}\n", .{median_chi}); std.debug.print("\n", .{}); // Quality indicator @@ -355,7 +355,7 @@ fn runBatchFit(allocator: Allocator, data_content: []const u8, options: SparcOpt else colorize("POOR", .red); - std.debug.print(" Overall quality: {s} ({:.1f}% good fits){s}\n\n", .{ status_str, success_rate * 100, colorize("", .reset) }); + std.debug.print(" Overall quality: {s} ({d:.1}% good fits){s}\n\n", .{ status_str, success_rate * 100, colorize("", .reset) }); // Output detailed results based on format switch (options.format) { @@ -407,7 +407,7 @@ fn runBatchFit(allocator: Allocator, data_content: []const u8, options: SparcOpt }, .csv => { std.debug.print("name,chi_squared,reduced_chi_squared,is_good_fit,num_points\n", .{}); - for (results) |r| { + for (results.items) |r| { const good_str = if (r.is_good) "true" else "false"; std.debug.print("{s},{e},{e},{s},{}\n", .{ r.name, r.chi_sq, r.reduced_chi_sq, good_str, r.num_points, @@ -435,9 +435,9 @@ fn outputText(allocator: Allocator, result: mod.FitResult, options: SparcOptions std.debug.print("\n", .{}); std.debug.print("{s}Fit Quality:{s}\n", .{ " ", "" }); - std.debug.print("{s} χ²: {:.6f}\n", .{ " ", result.chi_squared }); + std.debug.print("{s} χ²: {d:.6}\n", .{ " ", result.chi_squared }); std.debug.print("{s} DOF: {}\n", .{ " ", result.dof }); - std.debug.print("{s} Reduced χ²: {:.6f}\n", .{ " ", result.reduced_chi_squared }); + std.debug.print("{s} Reduced χ²: {d:.6}\n", .{ " ", result.reduced_chi_squared }); // Quality indicator const is_good = Fitting.isGoodFit(result); @@ -513,7 +513,7 @@ fn outputCsv(allocator: Allocator, result: mod.FitResult, points: []const mod.Ga result.chi_squared, result.dof, result.reduced_chi_squared, - }) catch unreachable; + }); // Data points header std.debug.print("\nradius_kpc,velocity_kms,velocity_err_kms\n", .{}); @@ -522,7 +522,7 @@ fn outputCsv(allocator: Allocator, result: mod.FitResult, points: []const mod.Ga point.radius, point.velocity, point.velocity_err, - }) catch unreachable; + }); } } @@ -563,7 +563,7 @@ fn runList(allocator: Allocator, options: SparcOptions) !void { else galaxy.name[0..12]; - std.debug.print("{s: <12} {d: >6} {:.1f} {:.0f} {:.0f}\n", .{ + std.debug.print("{s: <12} {d: >6} {d:.1} {d:.0} {d:.0}\n", .{ colorize(name_fmt, .blue), galaxy.points.len, galaxy.distance, @@ -587,7 +587,7 @@ fn runList(allocator: Allocator, options: SparcOptions) !void { try galaxy_obj.put("inclination_deg", json.Value{ .float = galaxy.inclination }); try galaxy_obj.put("position_angle_deg", json.Value{ .float = galaxy.position_angle }); - try root.append(allocator, json.Value{ .object = galaxy_obj }); + try root.append(json.Value{ .object = galaxy_obj }); } { @@ -666,7 +666,7 @@ fn runPlot(allocator: Allocator, options: SparcOptions) !void { // Y-axis label if (y % 5 == 0) { - std.debug.print("{:.0f} |", .{v_at_y}); + std.debug.print("{d:.0} |", .{v_at_y}); } else { std.debug.print(" |", .{}); } @@ -683,7 +683,7 @@ fn runPlot(allocator: Allocator, options: SparcOptions) !void { const v_pos = PLOT_HEIGHT - 1 - @as(usize, @intFromFloat((p.velocity / max_v) * @as(f64, @floatFromInt(PLOT_HEIGHT - 1)))); if (x == x_pos and y == v_pos) { - row_buffer[x] = '●'; // Data point + row_buffer[x] = '*'; // Data point break; } } @@ -691,7 +691,6 @@ fn runPlot(allocator: Allocator, options: SparcOptions) !void { // If no data point, check for model line if (row_buffer[x] == ' ') { const model_v = Savchenko.totalVelocity( - allocator, r_at_x, result.params.rho0, result.params.r_mem, @@ -708,17 +707,17 @@ fn runPlot(allocator: Allocator, options: SparcOptions) !void { const model_y = PLOT_HEIGHT - 1 - @as(usize, @intFromFloat((model_v / max_v) * @as(f64, @floatFromInt(PLOT_HEIGHT - 1)))); if (y == model_y or (y + 1 == model_y and model_y < PLOT_HEIGHT)) { - row_buffer[x] = '─'; // Model line + row_buffer[x] = '-'; // Model line } } } // Print row for (row_buffer) |c| { - if (c == '●') { - std.debug.print("{s}●{s}", .{ colorize("", .blue), colorize("", .reset) }); - } else if (c == '─') { - std.debug.print("{s}─{s}", .{ colorize("", .green), colorize("", .reset) }); + if (c == '*') { + std.debug.print("{s}*{s}", .{ colorize("", .blue), colorize("", .reset) }); + } else if (c == '-') { + std.debug.print("{s}-{s}", .{ colorize("", .green), colorize("", .reset) }); } else { std.debug.print(" ", .{}); } @@ -735,7 +734,7 @@ fn runPlot(allocator: Allocator, options: SparcOptions) !void { // X-axis labels std.debug.print(" 0.0", .{}); - std.debug.print(" {:.1f} kpc (R)\n", .{max_r}); + std.debug.print(" {d:.1} kpc (R)\n", .{max_r}); // Print fit parameters below std.debug.print("\n{s}Fit Parameters:{s}\n", .{ colorize("", .magenta), colorize("", .reset) }); @@ -743,7 +742,7 @@ fn runPlot(allocator: Allocator, options: SparcOptions) !void { std.debug.print(" r_mem: {e:.4} kpc\n", .{result.params.r_mem}); std.debug.print(" r_core: {e:.4} kpc\n", .{result.params.r_core}); std.debug.print(" Υ_bul: {e:.4}\n", .{result.params.upsilon_bul}); - std.debug.print(" χ²: {:.4f} (reduced: {:.4f})\n", .{result.chi_squared, result.reduced_chi_squared}); + std.debug.print(" χ²: {d:.4} (reduced: {d:.4})\n", .{result.chi_squared, result.reduced_chi_squared}); } /// Argument parsing error set diff --git a/src/tri/tri_depin.zig b/src/tri/tri_depin.zig index d07b44b782..176aef0617 100644 --- a/src/tri/tri_depin.zig +++ b/src/tri/tri_depin.zig @@ -28,9 +28,13 @@ const farm_accounts_mod = @import("farm_accounts.zig"); const Account = farm_accounts_mod.Account; // DePIN modules for directed discovery (imported via build.zig) -const depin_network = @import("depin_network"); -const depin_bootstrap = @import("depin_bootstrap"); -const depin_persistence = @import("depin_persistence"); +// Файлы никуда не пропали, их переименовали: src/depin/network.zig, +// bootstrap.zig, persistence.zig и src/firebird/slashing.zig. Корень модуля — +// src/tri/main.zig, поэтому «../» запрещён: модули объявлены в build.zig. +const depin = @import("depin"); +const depin_network = depin.network; +const depin_bootstrap = depin.bootstrap; +const depin_persistence = depin.persistence; const firebird_slashing = @import("firebird_slashing"); const print = std.debug.print; diff --git a/src/tri/tri_storm.zig b/src/tri/tri_storm.zig index fd34ead3df..0e628c4bab 100644 --- a/src/tri/tri_storm.zig +++ b/src/tri/tri_storm.zig @@ -5,7 +5,9 @@ const Allocator = std.mem.Allocator; const print = std.debug.print; // STORM Golden Chain module -const golden_chain = @import("golden_chain"); +// src/tri/golden_chain.zig — это CLI-обёртка (runGoldenChainCommand). +// Тип GoldenChain живёт в src/storm/golden_chain.zig — он и нужен здесь. +const golden_chain = @import("storm_golden_chain"); // ANSI colors const RESET = "\x1b[0m"; diff --git a/src/tri/zenodo_v16.zig b/src/tri/zenodo_v16.zig index 61a46df8e6..813d6122e3 100644 --- a/src/tri/zenodo_v16.zig +++ b/src/tri/zenodo_v16.zig @@ -203,7 +203,7 @@ pub const ExperimentResultEnhanced = struct { const std_str = try std.fmt.allocPrint(allocator, "{d:.4}", .{self.std_dev}); defer allocator.free(std_str); - return std.fmt.allocPrint(allocator, "{s} & {s:.3f} & {s:.3f}", .{ self.experiment_id, self.mean, self.std_dev }); + return std.fmt.allocPrint(allocator, "{s} & {d:.3} & {d:.3}", .{ self.experiment_id, self.mean, self.std_dev }); } }; diff --git a/src/tvc/tvc_corpus.zig b/src/tvc/tvc_corpus.zig index 6a5bd1e3b6..b9a3a34459 100644 --- a/src/tvc/tvc_corpus.zig +++ b/src/tvc/tvc_corpus.zig @@ -190,6 +190,9 @@ pub const TVCCorpus = struct { /// Store query/response pair in TVC /// Returns entry ID on success pub fn store(self: *Self, allocator: std.mem.Allocator, query: []const u8, response: []const u8) !u64 { + // bundle2 больше не аллоцирует, но сигнатуру публичного API не меняем + // (как и в `search` ниже) — параметр помечаем неиспользованным явно. + _ = allocator; if (self.count >= TVC_MAX_ENTRIES) { return error.CorpusFull; } @@ -234,7 +237,9 @@ pub const TVCCorpus = struct { self.count += 1; // 4. Bundle into memory vector (NO FORGETTING) - self.memory_vector = vsa.bundle2(&self.memory_vector, &bound_vec, allocator); + // bundle2 в zig-hdc берёт два аргумента (`a: *HybridBigInt, b: *HybridBigInt`) + // и не аллоцирует. Третий аргумент был лишним и ломал цель `tri`. + self.memory_vector = vsa.bundle2(&self.memory_vector, &bound_vec); // Update stats self.total_stores += 1; @@ -351,7 +356,7 @@ pub const TVCCorpus = struct { // Memory vector (packed) self.memory_vector.ensureUnpacked(); for (0..self.memory_vector.trit_len) |i| { - const byte: [1]u8 = .{@bitCast(self.memory_vector.getTritChecked(i))}; + const byte: [1]u8 = .{@bitCast(self.memory_vector.getTrit(i))}; try file.writeAll(&byte); } @@ -377,7 +382,7 @@ pub const TVCCorpus = struct { std.mem.writeInt(u32, &buf4, @intCast(entry.query_vec.trit_len), .little); try file.writeAll(&buf4); for (0..entry.query_vec.trit_len) |j| { - const byte: [1]u8 = .{@bitCast(entry.query_vec.getTritChecked(j))}; + const byte: [1]u8 = .{@bitCast(entry.query_vec.getTrit(j))}; try file.writeAll(&byte); } @@ -386,7 +391,7 @@ pub const TVCCorpus = struct { std.mem.writeInt(u32, &buf4, @intCast(entry.response_vec.trit_len), .little); try file.writeAll(&buf4); for (0..entry.response_vec.trit_len) |j| { - const byte: [1]u8 = .{@bitCast(entry.response_vec.getTritChecked(j))}; + const byte: [1]u8 = .{@bitCast(entry.response_vec.getTrit(j))}; try file.writeAll(&byte); } @@ -395,7 +400,7 @@ pub const TVCCorpus = struct { std.mem.writeInt(u32, &buf4, @intCast(entry.bound_vec.trit_len), .little); try file.writeAll(&buf4); for (0..entry.bound_vec.trit_len) |j| { - const byte: [1]u8 = .{@bitCast(entry.bound_vec.getTritChecked(j))}; + const byte: [1]u8 = .{@bitCast(entry.bound_vec.getTrit(j))}; try file.writeAll(&byte); } @@ -467,7 +472,7 @@ pub const TVCCorpus = struct { for (0..mem_vec_len) |i| { var byte: [1]u8 = undefined; _ = try file.readAll(&byte); - corpus.memory_vector.unpacked_cache.?[i] = @bitCast(byte[0]); + corpus.memory_vector.unpacked_cache[i] = @bitCast(byte[0]); } // Entries @@ -499,7 +504,7 @@ pub const TVCCorpus = struct { for (0..q_len) |j| { var byte: [1]u8 = undefined; _ = try file.readAll(&byte); - entry.query_vec.unpacked_cache.?[j] = @bitCast(byte[0]); + entry.query_vec.unpacked_cache[j] = @bitCast(byte[0]); } // Response vector @@ -511,7 +516,7 @@ pub const TVCCorpus = struct { for (0..r_len) |j| { var byte: [1]u8 = undefined; _ = try file.readAll(&byte); - entry.response_vec.unpacked_cache.?[j] = @bitCast(byte[0]); + entry.response_vec.unpacked_cache[j] = @bitCast(byte[0]); } // Bound vector @@ -523,7 +528,7 @@ pub const TVCCorpus = struct { for (0..b_len) |j| { var byte: [1]u8 = undefined; _ = try file.readAll(&byte); - entry.bound_vec.unpacked_cache.?[j] = @bitCast(byte[0]); + entry.bound_vec.unpacked_cache[j] = @bitCast(byte[0]); } // Query text @@ -596,7 +601,7 @@ pub const TVCCorpus = struct { for (0..mem_vec_len) |i| { var byte: [1]u8 = undefined; _ = try file.readAll(&byte); - self.memory_vector.unpacked_cache.?[i] = @bitCast(byte[0]); + self.memory_vector.unpacked_cache[i] = @bitCast(byte[0]); } // Entries @@ -627,7 +632,7 @@ pub const TVCCorpus = struct { for (0..q_len) |j| { var byte: [1]u8 = undefined; _ = try file.readAll(&byte); - entry.query_vec.unpacked_cache.?[j] = @bitCast(byte[0]); + entry.query_vec.unpacked_cache[j] = @bitCast(byte[0]); } // Response vector @@ -639,7 +644,7 @@ pub const TVCCorpus = struct { for (0..r_len) |j| { var byte: [1]u8 = undefined; _ = try file.readAll(&byte); - entry.response_vec.unpacked_cache.?[j] = @bitCast(byte[0]); + entry.response_vec.unpacked_cache[j] = @bitCast(byte[0]); } // Bound vector @@ -651,7 +656,7 @@ pub const TVCCorpus = struct { for (0..b_len) |j| { var byte: [1]u8 = undefined; _ = try file.readAll(&byte); - entry.bound_vec.unpacked_cache.?[j] = @bitCast(byte[0]); + entry.bound_vec.unpacked_cache[j] = @bitCast(byte[0]); } // Query text diff --git a/src/vibeec/trinity_cli.zig b/src/vibeec/trinity_cli.zig index 8aed37e9c1..4c04e67bcb 100644 --- a/src/vibeec/trinity_cli.zig +++ b/src/vibeec/trinity_cli.zig @@ -153,7 +153,9 @@ fn printStats(state: *CLIState) void { std.debug.print(" Requests: {d}\n", .{stats.total_requests}); std.debug.print(" Total Time: {d}us ({d:.2}ms)\n", .{ stats.total_time_us, @as(f64, @floatFromInt(stats.total_time_us)) / 1000.0 }); // Speed and vocabulary not available in stub module - std.debug.print(" Stats available in full implementation{s}\n", .{ GRAY, RESET }); + // Было два аргумента при одном {s}: GRAY оставался неиспользованным, + // и std.Io.Writer отказывал компиляцию (unused argument). + std.debug.print(" {s}Stats available in full implementation{s}\n", .{ GRAY, RESET }); std.debug.print(" Mode: 100%% LOCAL\n", .{}); std.debug.print("\n", .{}); } @@ -180,7 +182,8 @@ fn processQuery(state: *CLIState, query: []const u8) void { // Print output (stub module only has output field) std.debug.print("\n", .{}); - std.debug.print("{s}{s}\n", .{ GREEN, result.output, RESET }); + // Три аргумента при двух {s}: RESET не выводился и ломал сборку. + std.debug.print("{s}{s}{s}\n", .{ GREEN, result.output, RESET }); std.debug.print("\n", .{}); } From 4f483610975970cd9baa33d007ca40f3fc744a0a Mon Sep 17 00:00:00 2001 From: Vasilev Dmitrii Date: Fri, 14 Aug 2026 08:29:01 +0000 Subject: [PATCH 2/2] =?UTF-8?q?fix(build):=20=D0=B7=D0=B0=D0=BA=D1=80?= =?UTF-8?q?=D1=8B=D1=82=D1=8C=20=D0=BE=D1=81=D1=82=D0=B0=D1=82=D0=BE=D0=BA?= =?UTF-8?q?=20=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D0=B8=20=E2=80=94=20=D1=81?= =?UTF-8?q?=D0=BB=D0=BE=D0=B9=20=D1=81=D0=BE=D0=B2=D0=BC=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D0=B8=20zig-hdc=20=D0=B8=20API?= =?UTF-8?q?=200.15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src/vsa.zig и src/hybrid.zig: тонкие слои совместимости, только реэкспорт модуля зависимости zig-hdc-vsa (usingnamespace в Zig 0.15 отсутствует, поэтому реэкспорт явный, 42 символа) - src/vsa_jit.zig, src/jit_unified.zig, src/jit_arm64.zig, src/jit_x86_64.zig восстановлены из 42490a222^: копия в zig-hdc не собирается (её jit_unified.zig импортирует ../../jit_arm64.zig за пределами пакета) - build.zig: импорт zig-hdc-vsa добавлен всем модулям - vm.zig, query_cli.zig, sota_report_demo.zig, examples/memory.zig: вызовы приведены к API zig-hdc (bundle*/add/mul/negate/dotProduct/toI64 без allocator, unpacked_cache не optional, bundleN не error union) zig build -Dci=true: exit 0, 0 ошибок. Три raylib-цели (photon-demo, photon-immersive, trinity-node-gui) требуют системного raylib и уже исключены флагом -Dci=true. --- build.zig | 206 +++- examples/memory.zig | 2 +- src/hybrid.zig | 10 + src/jit_arm64.zig | 2175 ++++++++++++++++++++++++++++++++++++++ src/jit_unified.zig | 434 ++++++++ src/jit_x86_64.zig | 471 +++++++++ src/query_cli.zig | 7 +- src/sota_report_demo.zig | 6 +- src/vm.zig | 24 +- src/vsa.zig | 50 + src/vsa_jit.zig | 688 ++++++++++++ 11 files changed, 4018 insertions(+), 55 deletions(-) create mode 100644 src/hybrid.zig create mode 100644 src/jit_arm64.zig create mode 100644 src/jit_unified.zig create mode 100644 src/jit_x86_64.zig create mode 100644 src/vsa.zig create mode 100644 src/vsa_jit.zig diff --git a/build.zig b/build.zig index 4491ec6e53..78847d4a02 100644 --- a/build.zig +++ b/build.zig @@ -32,6 +32,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, .imports = &.{ + .{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }, .{ .name = "hdc_vsa", .module = hdc_vsa_mod }, .{ .name = "golden_float", .module = gf_mod }, }, @@ -42,7 +43,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("deploy/trinity-nexus/lang/src/root.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); // Generated serve module — from .tri spec: specs/integration/full-serve-v1.tri // Links libc because full-serve-v1.zig uses std.c.getpid() for daemonize @@ -51,7 +53,8 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, .link_libc = true, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); // Library artifact const lib = b.addLibrary(.{ @@ -77,7 +80,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/c_api.zig"), .target = target, .optimize = .ReleaseFast, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); // Shared library (.so / .dylib / .dll) const libvsa_shared = b.addLibrary(.{ @@ -96,6 +100,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/c_api.zig"), .target = target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); libvsa_static.linkLibC(); @@ -121,7 +126,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/queen_api.zig"), .target = target, .optimize = .ReleaseFast, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); const libqueen_shared = b.addLibrary(.{ .name = "trinity-queen", @@ -138,6 +144,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/queen_api.zig"), .target = target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); libqueen_static.linkLibC(); @@ -173,7 +180,8 @@ pub fn build(b: *std.Build) void { .target = release_target, .optimize = .ReleaseFast, .link_libc = true, - }), + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }), }); const target_output = b.addInstallArtifact(release_lib, .{ @@ -237,6 +245,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/queen_api.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_queen_api_tests = b.addRunArtifact(queen_api_tests); @@ -248,7 +257,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("tests/benchmarks/benchmarks/benchmark_test.zig"), .target = target, .optimize = .ReleaseFast, - .imports = &.{.{ .name = "vsa", .module = trinity_mod }}, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }, .{ .name = "vsa", .module = trinity_mod }}, }), }); const run_bench_tests = b.addRunArtifact(bench_tests); @@ -261,6 +270,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vm.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_vm_tests = b.addRunArtifact(vm_tests); @@ -272,6 +282,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/e2e_test.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_e2e_tests = b.addRunArtifact(e2e_tests); @@ -285,6 +296,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/c_api.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_c_api_tests = b.addRunArtifact(c_api_tests); @@ -310,6 +322,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/igla/trace.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_trace_tests = b.addRunArtifact(trace_tests); @@ -321,6 +334,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/agent_mu/swarm_collaboration.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_swarm_collab_tests = b.addRunArtifact(swarm_collab_tests); @@ -331,6 +345,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/agent_mu/production_hardening_test.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_production_hardening_tests = b.addRunArtifact(production_hardening_tests); @@ -341,6 +356,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/agent_mu/production_test.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_production_tests = b.addRunArtifact(production_tests); @@ -353,6 +369,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_search.zig"), .target = target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(trinity_search); @@ -371,6 +388,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/query_cli.zig"), .target = target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(trinity_query); @@ -389,7 +407,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("tests/benchmarks/benchmarks/bench_core.zig"), .target = target, .optimize = .ReleaseFast, - .imports = &.{.{ .name = "vsa", .module = trinity_mod }}, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }, .{ .name = "vsa", .module = trinity_mod }}, }), }); b.installArtifact(bench_core); @@ -405,6 +423,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("tests/benchmarks/benchmarks/bench_compression.zig"), .target = target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(bench_compress); @@ -420,7 +439,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("examples/memory.zig"), .target = target, .optimize = optimize, - .imports = &.{.{ .name = "trinity", .module = trinity_mod }}, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }, .{ .name = "trinity", .module = trinity_mod }}, }), }); b.installArtifact(example_memory); @@ -431,7 +450,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("examples/sequence.zig"), .target = target, .optimize = optimize, - .imports = &.{.{ .name = "trinity", .module = trinity_mod }}, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }, .{ .name = "trinity", .module = trinity_mod }}, }), }); b.installArtifact(example_sequence); @@ -442,7 +461,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("examples/vm.zig"), .target = target, .optimize = optimize, - .imports = &.{.{ .name = "trinity", .module = trinity_mod }}, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }, .{ .name = "trinity", .module = trinity_mod }}, }), }); b.installArtifact(example_vm); @@ -464,7 +483,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/sota_report_demo.zig"), .target = target, .optimize = optimize, - .imports = &.{.{ .name = "trinity", .module = trinity_mod }}, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }, .{ .name = "trinity", .module = trinity_mod }}, }), }); b.installArtifact(sota_report); @@ -480,6 +499,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/agent_mu/pas_demo.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(pas_demo); @@ -495,6 +515,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/firebird/cli.zig"), .target = target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(firebird); @@ -512,6 +533,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/firebird/b2t_integration.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_firebird_tests = b.addRunArtifact(firebird_tests); @@ -523,6 +545,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/firebird/wasm_parser.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_wasm_tests = b.addRunArtifact(wasm_tests); @@ -546,7 +569,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/firebird/cli.zig"), .target = release_target, .optimize = .ReleaseFast, - }), + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }), }); const target_output = b.addInstallArtifact(release_exe, .{ @@ -572,6 +596,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/igla_local_chat.zig"), .target = release_target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }); const fluent_release_exe = b.addExecutable(.{ .name = "fluent", @@ -605,6 +630,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/firebird/extension_wasm.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_extension_tests = b.addRunArtifact(extension_tests); @@ -616,6 +642,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/firebird/depin.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_depin_tests = b.addRunArtifact(depin_tests); @@ -627,6 +654,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/depin/network.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_depin_network_tests = b.addRunArtifact(depin_network_tests); @@ -638,6 +666,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/api/unified_server.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_api_tests = b.addRunArtifact(api_tests); @@ -649,6 +678,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/file_encoder.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_file_encoder_tests = b.addRunArtifact(file_encoder_tests); @@ -660,6 +690,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/protocol.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_protocol_tests = b.addRunArtifact(protocol_tests); @@ -671,6 +702,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/storage.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_storage_tests = b.addRunArtifact(storage_tests); @@ -682,6 +714,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/shard_manager.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_shard_manager_tests = b.addRunArtifact(shard_manager_tests); @@ -693,6 +726,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/storage_discovery.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_storage_discovery_tests = b.addRunArtifact(storage_discovery_tests); @@ -704,6 +738,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/remote_storage.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_remote_storage_tests = b.addRunArtifact(remote_storage_tests); @@ -715,6 +750,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/crypto.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_crypto_tests = b.addRunArtifact(crypto_tests); @@ -726,6 +762,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/galois.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_galois_tests = b.addRunArtifact(galois_tests); @@ -737,6 +774,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/reed_solomon.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_reed_solomon_tests = b.addRunArtifact(reed_solomon_tests); @@ -748,6 +786,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/connection_pool.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_connection_pool_tests = b.addRunArtifact(connection_pool_tests); @@ -759,6 +798,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/manifest_dht.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_manifest_dht_tests = b.addRunArtifact(manifest_dht_tests); @@ -770,6 +810,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/integration_test.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_integration_tests = b.addRunArtifact(integration_tests); @@ -781,6 +822,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/proof_of_storage.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_pos_tests = b.addRunArtifact(pos_tests); @@ -792,6 +834,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/shard_rebalancer.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_rebalancer_tests = b.addRunArtifact(rebalancer_tests); @@ -803,6 +846,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/bandwidth_aggregator.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_bw_agg_tests = b.addRunArtifact(bw_agg_tests); @@ -814,6 +858,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/shard_scrubber.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_scrubber_tests = b.addRunArtifact(scrubber_tests); @@ -825,6 +870,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/node_reputation.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_reputation_tests = b.addRunArtifact(reputation_tests); @@ -836,6 +882,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/graceful_shutdown.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_shutdown_tests = b.addRunArtifact(shutdown_tests); @@ -847,6 +894,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/network_stats.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_netstats_tests = b.addRunArtifact(netstats_tests); @@ -858,6 +906,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/auto_repair.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_auto_repair_tests = b.addRunArtifact(auto_repair_tests); @@ -869,6 +918,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/incentive_slashing.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_slashing_tests = b.addRunArtifact(slashing_tests); @@ -880,6 +930,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/prometheus_metrics.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_prometheus_tests = b.addRunArtifact(prometheus_tests); @@ -891,6 +942,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/repair_rate_limiter.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_rate_limiter_tests = b.addRunArtifact(rate_limiter_tests); @@ -902,6 +954,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/token_staking.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_token_staking_tests = b.addRunArtifact(token_staking_tests); @@ -913,6 +966,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/peer_latency.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_peer_latency_tests = b.addRunArtifact(peer_latency_tests); @@ -924,6 +978,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/rs_repair.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_rs_repair_tests = b.addRunArtifact(rs_repair_tests); @@ -935,6 +990,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/metrics_http.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_metrics_http_tests = b.addRunArtifact(metrics_http_tests); @@ -946,6 +1002,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/erasure_repair.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_erasure_repair_tests = b.addRunArtifact(erasure_repair_tests); @@ -957,6 +1014,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/reputation_consensus.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_reputation_consensus_tests = b.addRunArtifact(reputation_consensus_tests); @@ -968,6 +1026,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/stake_delegation.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_stake_delegation_tests = b.addRunArtifact(stake_delegation_tests); @@ -979,6 +1038,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/region_topology.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_region_topology_tests = b.addRunArtifact(region_topology_tests); @@ -990,6 +1050,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/slashing_escrow.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_slashing_escrow_tests = b.addRunArtifact(slashing_escrow_tests); @@ -1001,6 +1062,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/prometheus_http.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_prometheus_http_tests = b.addRunArtifact(prometheus_http_tests); @@ -1012,6 +1074,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/vsa_shard_encoder.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_vsa_shard_encoder_tests = b.addRunArtifact(vsa_shard_encoder_tests); @@ -1023,6 +1086,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/semantic_index.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_semantic_index_tests = b.addRunArtifact(semantic_index_tests); @@ -1034,6 +1098,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/cross_shard_tx.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_cross_shard_tx_tests = b.addRunArtifact(cross_shard_tx_tests); @@ -1045,6 +1110,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/vsa_shard_locks.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_vsa_shard_locks_tests = b.addRunArtifact(vsa_shard_locks_tests); @@ -1056,6 +1122,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/region_router.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_region_router_tests = b.addRunArtifact(region_router_tests); @@ -1067,6 +1134,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/dynamic_erasure.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_dynamic_erasure_tests = b.addRunArtifact(dynamic_erasure_tests); @@ -1078,6 +1146,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/saga_coordinator.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_saga_coordinator_tests = b.addRunArtifact(saga_coordinator_tests); @@ -1089,6 +1158,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/transaction_wal.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_transaction_wal_tests = b.addRunArtifact(transaction_wal_tests); @@ -1100,6 +1170,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/parallel_saga.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_parallel_saga_tests = b.addRunArtifact(parallel_saga_tests); @@ -1111,6 +1182,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/wal_disk.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_wal_disk_tests = b.addRunArtifact(wal_disk_tests); @@ -1123,6 +1195,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/b2t/b2t_cli.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(b2t); @@ -1144,6 +1217,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/claude_ui.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(claude_ui); @@ -1159,6 +1233,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/trinity_cli.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(trinity_cli); @@ -1175,13 +1250,15 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/igla_local_chat.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); const vibeec_fluent_chat = b.createModule(.{ .root_source_file = b.path("src/vibeec/igla_fluent_chat_engine.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); // VSA module for TRI (needed by tvc_corpus_mod and fluent CLI), now the // migrated module rather than a path to a file that is not here. @@ -1201,14 +1278,16 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/igla_knowledge_graph.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); // LLM Triples Extractor module (SYM-002: pattern-based extraction) - defined early for fluent CLI const triples_parser_mod = b.createModule(.{ .root_source_file = b.path("src/vibeec/triples_parser.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); // Fluent CLI - Local Chat with History Truncation (NO HANG!) const fluent_cli = b.addExecutable(.{ @@ -1245,7 +1324,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/agent_mu/agent_mu.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); const vibee = b.addExecutable(.{ .name = "vibee", @@ -1253,6 +1333,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/gen_cmd.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); @@ -1264,6 +1345,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/tvc/treesitter/zig.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }); ts_zig_mod.linkSystemLibrary("tree-sitter", .{}); ts_zig_mod.link_libc = true; @@ -1292,6 +1374,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/self_improver.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(self_improve); @@ -1312,7 +1395,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/tvc/treesitter/zig.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); ts_zig_mod.link_libc = true; // Add stub include path for tree_sitter/api.h when library is not installed ts_zig_mod.addIncludePath(b.path("src/tvc/treesitter")); @@ -1392,7 +1476,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/tri/metabolism.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); // server.zig делает @import("trinity_workspace"), но модуль никогда здесь не // объявлялся — файл `src/trinity_workspace.zig` лежит в репозитории, @@ -1401,7 +1486,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_workspace.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); const trinity_mcp = b.addExecutable(.{ .name = "trinity-mcp", @@ -1433,6 +1519,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("tools/mcp/trinity_mcp/agent/main.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(ralph_agent); @@ -1448,7 +1535,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("tools/mcp/trinity_mcp/agent/telegram.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); const mu_agent = b.addExecutable(.{ .name = "mu-agent", .root_module = b.createModule(.{ @@ -1496,6 +1584,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("tools/mcp/trinity_mcp/agent/entrypoint.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(agent_entrypoint); @@ -1512,6 +1601,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("tools/mcp/trinity_mcp/agent/ralph_hook.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(ralph_hook); @@ -1523,6 +1613,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("tools/hooks/pipeline_guard.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(pipeline_guard); @@ -1534,6 +1625,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("tools/mcp/trinity_mcp/bot/main.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(tri_bot); @@ -1551,6 +1643,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/phi_loop_cli.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(phi_loop); @@ -1569,6 +1662,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/forge/main.zig"), .target = target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(forge); @@ -1590,6 +1684,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/quantum/main.zig"), .target = target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(quantum); @@ -1611,6 +1706,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/beal/main.zig"), .target = target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(beal); @@ -1628,6 +1724,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/beal.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_beal_tests = b.addRunArtifact(beal_tests); @@ -1643,6 +1740,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/hslm/cli.zig"), .target = target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(hslm_train); @@ -1697,6 +1795,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, .link_libc = true, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(railway_redeploy); @@ -1714,6 +1813,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, .link_libc = true, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(railway_update_service); @@ -1731,6 +1831,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, .link_libc = true, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(railway_set_builder); @@ -1748,6 +1849,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, .link_libc = true, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(railway_create_service); @@ -1765,6 +1867,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, .link_libc = true, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(railway_set_startcmd); @@ -1782,6 +1885,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, .link_libc = true, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(railway_trigger_redeploy); @@ -1799,6 +1903,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, .link_libc = true, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(railway_set_dockerfile); @@ -1818,6 +1923,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/cli/entrypoint_swe.zig"), .target = target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(swe_entrypoint); @@ -1832,6 +1938,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("external/zig-hslm/src/root.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_hslm_tests = b.addRunArtifact(hslm_tests); @@ -1854,12 +1961,14 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/trinity_swe_agent.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); const vibeec_coder = b.createModule(.{ .root_source_file = b.path("src/vibeec/igla_local_coder.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); // TVC Distributed module for TRI (file-based sharing) const tvc_distributed_mod = b.createModule(.{ .root_source_file = b.path("src/tvc/tvc_distributed.zig"), @@ -1905,19 +2014,22 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/agent_mu/pas_orchestrator.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); // Unified API Layer (Golden Chain #102) const api_mod = b.createModule(.{ .root_source_file = b.path("src/api/unified_server.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); // TRI Utils module (Cycle 100: for testing) const tri_colors_mod = b.createModule(.{ .root_source_file = b.path("src/tri/tri_colors.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); const tri_utils_mod = b.createModule(.{ .root_source_file = b.path("src/tri/tri_utils.zig"), .target = target, @@ -1956,7 +2068,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/sacred/const.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); const sacred_mod = b.createModule(.{ .root_source_file = b.path("src/sacred/sacred.zig"), .target = target, @@ -1981,7 +2094,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/bsd/scanner.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); const tri = b.addExecutable(.{ .name = "tri", @@ -2145,6 +2259,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/tri/tri_config.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_tri_config_tests = b.addRunArtifact(tri_config_tests); @@ -2157,6 +2272,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/tri/tri_history.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_tri_history_tests = b.addRunArtifact(tri_history_tests); @@ -2169,6 +2285,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/tri/tri_error.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_tri_error_tests = b.addRunArtifact(tri_error_tests); @@ -2182,6 +2299,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/trinity_hybrid_local.zig"), .target = target, .optimize = .ReleaseFast, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(hybrid_local); @@ -2199,7 +2317,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/gguf_model.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); // Trinity Node - Decentralized Inference Network const trinity_node = b.addExecutable(.{ @@ -2232,7 +2351,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/trinity_node/main_gui.zig"), .target = target, .optimize = optimize, - }), + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }), }); trinity_node_gui.linkSystemLibrary("raylib"); trinity_node_gui.linkLibC(); @@ -2253,7 +2373,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("deploy/trinity-nexus/canvas/src/photon_demo.zig"), .target = target, .optimize = optimize, - }), + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }), }); photon_demo.linkSystemLibrary("raylib"); photon_demo.linkLibC(); @@ -2278,7 +2399,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("deploy/trinity-nexus/canvas/src/photon_immersive.zig"), .target = target, .optimize = optimize, - }), + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }), }); photon_immersive.linkSystemLibrary("raylib"); photon_immersive.linkLibC(); @@ -2347,26 +2469,31 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/wasm_stubs/igla_chat_stub.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }); const wasm_fluent_chat = b.createModule(.{ .root_source_file = b.path("src/wasm_stubs/igla_fluent_chat_stub.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }); const wasm_tvc_corpus = b.createModule(.{ .root_source_file = b.path("src/wasm_stubs/tvc_corpus_stub.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }); const wasm_auto_shard = b.createModule(.{ .root_source_file = b.path("src/wasm_stubs/auto_shard_stub.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }); const wasm_igla_kg = b.createModule(.{ .root_source_file = b.path("src/wasm_stubs/igla_knowledge_graph_stub.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }); const wasm_hybrid_chat = b.createModule(.{ .root_source_file = b.path("src/wasm_stubs/igla_hybrid_chat_stub.zig"), @@ -2472,6 +2599,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("deploy/trinity-nexus/canvas/src/photon_terminal.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(photon_terminal); @@ -2493,6 +2621,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/triples_parser.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_triples_parser = b.addRunArtifact(triples_parser_tests); @@ -2506,6 +2635,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/kg_pipeline.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_kg_pipeline = b.addRunArtifact(kg_pipeline_tests); @@ -2519,6 +2649,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/kg_sync.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_kg_sync = b.addRunArtifact(kg_sync_tests); @@ -2531,7 +2662,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/kg_sync.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); const sym_005_tests = b.addTest(.{ .root_module = b.createModule(.{ .root_source_file = b.path("src/vibeec/sym_005_demo.zig"), @@ -2553,7 +2685,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vibeec/kv_cache.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); const prefix_cache_tests = b.addTest(.{ .root_module = b.createModule(.{ .root_source_file = b.path("src/vibeec/prefix_cache_completion.zig"), @@ -2654,7 +2787,8 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/tri/token_rotator.zig"), .target = target, .optimize = optimize, - }); + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, + }); const tri_api = b.addExecutable(.{ .name = "tri-api", @@ -2683,6 +2817,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/arena/main.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); b.installArtifact(arena_exe); @@ -2697,6 +2832,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/arena/main.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ .name = "zig-hdc-vsa", .module = hdc_vsa_mod }}, }), }); const run_arena_tests = b.addRunArtifact(arena_tests); diff --git a/examples/memory.zig b/examples/memory.zig index cb8d20fa45..40febf3395 100644 --- a/examples/memory.zig +++ b/examples/memory.zig @@ -16,7 +16,7 @@ pub fn main() !void { var yellow_banana = trinity.bind(&banana, &yellow); // Bundle into memory - var memory = trinity.bundle2(&red_apple, &yellow_banana, std.heap.page_allocator); + var memory = trinity.bundle2(&red_apple, &yellow_banana); // Query: "What is red?" var query = trinity.bind(&memory, &red); diff --git a/src/hybrid.zig b/src/hybrid.zig new file mode 100644 index 0000000000..b7b8e070b6 --- /dev/null +++ b/src/hybrid.zig @@ -0,0 +1,10 @@ +//! Совместимостный слой для перенесённого src/hybrid.zig. +//! Типы HybridBigInt/Trit и константы доступны через фасад `zig-hdc-vsa`. + +const hdc = @import("zig-hdc-vsa"); + +pub const HybridBigInt = hdc.HybridBigInt; +pub const Trit = hdc.Trit; +pub const MAX_TRITS = hdc.MAX_TRITS; +pub const Vec32i8 = hdc.Vec32i8; +pub const SIMD_WIDTH = hdc.SIMD_WIDTH; diff --git a/src/jit_arm64.zig b/src/jit_arm64.zig new file mode 100644 index 0000000000..84949d340f --- /dev/null +++ b/src/jit_arm64.zig @@ -0,0 +1,2175 @@ +// @origin(spec:jit_arm64.tri) @regen(manual-impl) +// @origin(manual) @regen(pending) +// Trinity JIT Compiler - ARM64 (AArch64) Backend +// Compiles VSA operations to native ARM64 machine code +// +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q +// φ² + 1/φ² = 3 + +const std = @import("std"); +const builtin = @import("builtin"); + +// ═══════════════════════════════════════════════════════════════════════════════ +// ARM64 JIT COMPILER +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Check if we're on ARM64 +pub const is_arm64 = builtin.cpu.arch == .aarch64; + +/// ARM64 JIT Compiler +pub const Arm64JitCompiler = struct { + code: std.ArrayListUnmanaged(u8), + allocator: std.mem.Allocator, + exec_mem: ?[]align(std.heap.page_size_min) u8 = null, + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator) Self { + return Self{ + .code = .{}, + .allocator = allocator, + }; + } + + pub fn deinit(self: *Self) void { + self.code.deinit(self.allocator); + if (self.exec_mem) |mem| { + std.posix.munmap(mem); + } + } + + pub fn reset(self: *Self) void { + self.code.clearRetainingCapacity(); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // ARM64 INSTRUCTION ENCODING HELPERS + // ═══════════════════════════════════════════════════════════════════════════ + + /// Emit a 32-bit ARM64 instruction (little-endian) + fn emit32(self: *Self, instr: u32) !void { + try self.code.appendSlice(self.allocator, &std.mem.toBytes(instr)); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // ARM64 REGISTER ENCODING + // ═══════════════════════════════════════════════════════════════════════════ + + // X registers (64-bit): x0-x30, sp=31, xzr=31 + // W registers (32-bit): w0-w30, wzr=31 + const x0: u5 = 0; + const x1: u5 = 1; + const x2: u5 = 2; + const x3: u5 = 3; + const x8: u5 = 8; // indirect result + const x9: u5 = 9; // temp + const x10: u5 = 10; // temp + const x11: u5 = 11; // temp + const x12: u5 = 12; // temp + const x13: u5 = 13; // temp + const x14: u5 = 14; // temp + const x15: u5 = 15; // temp + const x19: u5 = 19; // callee-saved + const x20: u5 = 20; // callee-saved + const x21: u5 = 21; // callee-saved + const x22: u5 = 22; // callee-saved + const x29: u5 = 29; // frame pointer (fp) + const x30: u5 = 30; // link register (lr) + const sp: u5 = 31; // stack pointer + const xzr: u5 = 31; // zero register + + // ═══════════════════════════════════════════════════════════════════════════ + // ARM64 INSTRUCTION BUILDERS + // ═══════════════════════════════════════════════════════════════════════════ + + /// STP (Store Pair) - stp Xt1, Xt2, [Xn, #imm]! (pre-index) + fn stpPreIndex(self: *Self, rt1: u5, rt2: u5, rn: u5, imm7: i7) !void { + // STP (pre-index, 64-bit): 1 01 0 100 1 1 imm7 Rt2 Rn Rt1 + const uimm: u7 = @bitCast(imm7); + const instr: u32 = 0xA9800000 | + (@as(u32, uimm) << 15) | + (@as(u32, rt2) << 10) | + (@as(u32, rn) << 5) | + @as(u32, rt1); + try self.emit32(instr); + } + + /// LDP (Load Pair) - ldp Xt1, Xt2, [Xn], #imm (post-index) + fn ldpPostIndex(self: *Self, rt1: u5, rt2: u5, rn: u5, imm7: i7) !void { + // LDP (post-index, 64-bit): 1 01 0 100 0 1 1 imm7 Rt2 Rn Rt1 + const uimm: u7 = @bitCast(imm7); + const instr: u32 = 0xA8C00000 | + (@as(u32, uimm) << 15) | + (@as(u32, rt2) << 10) | + (@as(u32, rn) << 5) | + @as(u32, rt1); + try self.emit32(instr); + } + + /// MOV (register) - mov Xd, Xn (actually ORR Xd, XZR, Xn) + fn movReg(self: *Self, rd: u5, rn: u5) !void { + // ORR (shifted register): 1 01 01010 00 0 Rm 000000 Rn Rd + const instr: u32 = 0xAA000000 | + (@as(u32, rn) << 16) | + (@as(u32, xzr) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// MOV (immediate) - mov Xd, #imm16 + fn movImm16(self: *Self, rd: u5, imm16: u16, shift: u2) !void { + // MOVZ: 1 10 100101 hw imm16 Rd + const instr: u32 = 0xD2800000 | + (@as(u32, shift) << 21) | + (@as(u32, imm16) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// MOVK (keep) - movk Xd, #imm16, lsl #shift + fn movkImm16(self: *Self, rd: u5, imm16: u16, shift: u2) !void { + // MOVK: 1 11 100101 hw imm16 Rd + const instr: u32 = 0xF2800000 | + (@as(u32, shift) << 21) | + (@as(u32, imm16) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// Load 64-bit immediate into register + fn loadImm64(self: *Self, rd: u5, imm: u64) !void { + const imm0: u16 = @truncate(imm); + const imm1: u16 = @truncate(imm >> 16); + const imm2: u16 = @truncate(imm >> 32); + const imm3: u16 = @truncate(imm >> 48); + + try self.movImm16(rd, imm0, 0); + if (imm1 != 0) try self.movkImm16(rd, imm1, 1); + if (imm2 != 0) try self.movkImm16(rd, imm2, 2); + if (imm3 != 0) try self.movkImm16(rd, imm3, 3); + } + + /// ADD (immediate) - add Xd, Xn, #imm12 + fn addImm(self: *Self, rd: u5, rn: u5, imm12: u12) !void { + // ADD (imm): 1 00 100010 0 imm12 Rn Rd + const instr: u32 = 0x91000000 | + (@as(u32, imm12) << 10) | + (@as(u32, rn) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// SUB (immediate) - sub Xd, Xn, #imm12 + fn subImm(self: *Self, rd: u5, rn: u5, imm12: u12) !void { + // SUB (imm): 1 10 100010 0 imm12 Rn Rd + const instr: u32 = 0xD1000000 | + (@as(u32, imm12) << 10) | + (@as(u32, rn) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// ADD (register) - add Xd, Xn, Xm + fn addReg(self: *Self, rd: u5, rn: u5, rm: u5) !void { + // ADD (reg): 1 00 01011 00 0 Rm 000000 Rn Rd + const instr: u32 = 0x8B000000 | + (@as(u32, rm) << 16) | + (@as(u32, rn) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// MUL - mul Xd, Xn, Xm (actually MADD Xd, Xn, Xm, XZR) + fn mul(self: *Self, rd: u5, rn: u5, rm: u5) !void { + // MADD: 1 00 11011 000 Rm 0 Ra Rn Rd + const instr: u32 = 0x9B000000 | + (@as(u32, rm) << 16) | + (@as(u32, xzr) << 10) | + (@as(u32, rn) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// SMULL - smull Xd, Wn, Wm (signed multiply long) + fn smull(self: *Self, rd: u5, rn: u5, rm: u5) !void { + // SMULL: 1 00 11011 0 01 Rm 0 11111 Rn Rd + const instr: u32 = 0x9B207C00 | + (@as(u32, rm) << 16) | + (@as(u32, rn) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// LDRSB (register) - ldrsb Wt, [Xn, Xm] + fn ldrsbReg(self: *Self, rt: u5, rn: u5, rm: u5) !void { + // LDRSB (reg, 32-bit): 00 111 0 00 11 1 Rm 011 0 10 Rn Rt + const instr: u32 = 0x38E06800 | + (@as(u32, rm) << 16) | + (@as(u32, rn) << 5) | + @as(u32, rt); + try self.emit32(instr); + } + + /// LDRB (register) - ldrb Wt, [Xn, Xm] + fn ldrbReg(self: *Self, rt: u5, rn: u5, rm: u5) !void { + // LDRB (reg): 00 111 0 00 01 1 Rm 011 0 10 Rn Rt + const instr: u32 = 0x38606800 | + (@as(u32, rm) << 16) | + (@as(u32, rn) << 5) | + @as(u32, rt); + try self.emit32(instr); + } + + /// STRB (register) - strb Wt, [Xn, Xm] + fn strbReg(self: *Self, rt: u5, rn: u5, rm: u5) !void { + // STRB (reg): 00 111 0 00 00 1 Rm 011 0 10 Rn Rt + const instr: u32 = 0x38206800 | + (@as(u32, rm) << 16) | + (@as(u32, rn) << 5) | + @as(u32, rt); + try self.emit32(instr); + } + + /// CMP (immediate) - cmp Xn, #imm12 + fn cmpImm(self: *Self, rn: u5, imm12: u12) !void { + // SUBS XZR, Xn, #imm12 + const instr: u32 = 0xF1000000 | + (@as(u32, imm12) << 10) | + (@as(u32, rn) << 5) | + @as(u32, xzr); + try self.emit32(instr); + } + + /// CMP (register) - cmp Xn, Xm + fn cmpReg(self: *Self, rn: u5, rm: u5) !void { + // SUBS XZR, Xn, Xm + const instr: u32 = 0xEB000000 | + (@as(u32, rm) << 16) | + (@as(u32, rn) << 5) | + @as(u32, xzr); + try self.emit32(instr); + } + + /// B.cond - conditional branch + fn bcond(self: *Self, cond: u4, offset: i19) !void { + // B.cond: 0101010 0 imm19 0 cond + const uoffset: u19 = @bitCast(offset); + const instr: u32 = 0x54000000 | + (@as(u32, uoffset) << 5) | + @as(u32, cond); + try self.emit32(instr); + } + + /// B - unconditional branch + fn b(self: *Self, offset: i26) !void { + // B: 0 00101 imm26 + const uoffset: u26 = @bitCast(offset); + const instr: u32 = 0x14000000 | @as(u32, uoffset); + try self.emit32(instr); + } + + /// RET - return + fn retInstr(self: *Self) !void { + // RET {Xn}: 1101011 0 0 10 11111 0000 0 0 Rn 00000 + const instr: u32 = 0xD65F0000 | (@as(u32, x30) << 5); + try self.emit32(instr); + } + + /// CSET - cset Xd, cond + fn cset(self: *Self, rd: u5, cond: u4) !void { + // CSINC Xd, XZR, XZR, invert(cond) + const inv_cond = cond ^ 1; + const instr: u32 = 0x9A9F0400 | + (@as(u32, inv_cond) << 12) | + @as(u32, rd); + try self.emit32(instr); + } + + /// CSNEG - conditional select negate + fn csneg(self: *Self, rd: u5, rn: u5, rm: u5, cond: u4) !void { + // CSNEG: 1 1 0 11010100 Rm cond 0 1 Rn Rd + const instr: u32 = 0xDA800400 | + (@as(u32, rm) << 16) | + (@as(u32, cond) << 12) | + (@as(u32, rn) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + // Condition codes + const COND_EQ: u4 = 0; // Equal + const COND_NE: u4 = 1; // Not equal + const COND_GE: u4 = 10; // Signed >= + const COND_LT: u4 = 11; // Signed < + const COND_GT: u4 = 12; // Signed > + const COND_LE: u4 = 13; // Signed <= + + // ═══════════════════════════════════════════════════════════════════════════ + // NEON SIMD REGISTERS AND INSTRUCTIONS + // ═══════════════════════════════════════════════════════════════════════════ + + // NEON vector registers V0-V31 (128-bit) + // Use same encoding as X registers (0-31) + const v0: u5 = 0; + const v1: u5 = 1; + const v2: u5 = 2; + const v3: u5 = 3; + const v4: u5 = 4; + const v5: u5 = 5; + const v6: u5 = 6; + const v7: u5 = 7; + const v16: u5 = 16; // callee-saved v8-v15, so use v16+ for temps + const v17: u5 = 17; + const v18: u5 = 18; + const v19: u5 = 19; + + /// LD1 {Vt.16B}, [Xn] - Load 16 bytes into vector register + fn ld1_16b(self: *Self, vt: u5, xn: u5) !void { + // LD1 (single structure, no offset): 0 1 001100 0 10 0000 0111 00 Rn Rt + // Q=1 (128-bit), size=00 (8-bit), opcode=0111 + const instr: u32 = 0x4C407000 | + (@as(u32, xn) << 5) | + @as(u32, vt); + try self.emit32(instr); + } + + /// LD1 {Vt.16B}, [Xn], #16 - Load 16 bytes with post-increment + fn ld1_16b_post(self: *Self, vt: u5, xn: u5) !void { + // LD1 (single structure, post-index, imm): 0 1 001100 1 10 11111 0111 00 Rn Rt + const instr: u32 = 0x4CDF7000 | + (@as(u32, xn) << 5) | + @as(u32, vt); + try self.emit32(instr); + } + + /// SDOT Vd.4S, Vn.16B, Vm.16B - Signed dot product (ARMv8.4-A) + /// Computes 4 dot products of 4 signed i8 values each, accumulates into 4 x i32 + fn sdot_4s(self: *Self, vd: u5, vn: u5, vm: u5) !void { + // SDOT: 0 1 0 01110 10 0 Rm 1 0010 1 Rn Rd + // Q=1 (128-bit), size=10, Rm, opcode=10010, U=0 (signed) + const instr: u32 = 0x4E809400 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// ADDV Sd, Vn.4S - Add across vector lanes to scalar + fn addv_4s(self: *Self, vd: u5, vn: u5) !void { + // ADDV: 0 1 0 01110 10 11000 1 1011 10 Rn Rd + const instr: u32 = 0x4EB1B800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// SMOV Xd, Vn.S[index] - Signed move from vector element to GPR + fn smov_s(self: *Self, xd: u5, vn: u5, index: u2) !void { + // SMOV: 0 1 0 0111 0 00 0 imm5 0 0101 1 Rn Rd + // For S (32-bit) element, imm5 = (index << 3) | 0b00100 + const imm5: u5 = (@as(u5, index) << 3) | 0b00100; + const instr: u32 = 0x4E002C00 | + (@as(u32, imm5) << 16) | + (@as(u32, vn) << 5) | + @as(u32, xd); + try self.emit32(instr); + } + + /// MOVI Vd.4S, #0 - Move immediate to vector (zero vector) + fn movi_4s_zero(self: *Self, vd: u5) !void { + // MOVI: 0 1 0 01111 00000 cmode=0000 op=0 1 a:b:c:d:e:f:g:h Rd + // For all zeros: cmode=0000, imm8=0 + const instr: u32 = 0x4F000400 | + @as(u32, vd); + try self.emit32(instr); + } + + /// MOVI Vd.16B, #imm8 - Move immediate to vector (all bytes) + fn movi_16b(self: *Self, vd: u5, imm8: u8) !void { + // MOVI (16B): for zero just use simplified encoding + if (imm8 == 0) { + // Zero vector - use simple encoding + const instr: u32 = 0x4F000400 | @as(u32, vd); + try self.emit32(instr); + } else { + // Non-zero - full encoding + const bit7 = (imm8 >> 7) & 1; + const bit6 = (imm8 >> 6) & 1; + const bit5 = (imm8 >> 5) & 1; + const bit4 = (imm8 >> 4) & 1; + const bit3 = (imm8 >> 3) & 1; + const bit2 = (imm8 >> 2) & 1; + const bit1 = (imm8 >> 1) & 1; + const bit0 = imm8 & 1; + const instr: u32 = 0x4F00E400 | + (@as(u32, bit7) << 18) | + (@as(u32, bit6) << 17) | + (@as(u32, bit5) << 16) | + (@as(u32, bit4) << 11) | + (@as(u32, bit3) << 10) | + (@as(u32, bit2) << 9) | + (@as(u32, bit1) << 8) | + (@as(u32, bit0) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + } + + /// SUB Xd, Xn, Xm - 64-bit register subtract + fn subReg(self: *Self, xd: u5, xn: u5, xm: u5) !void { + const instr: u32 = 0xCB000000 | + (@as(u32, xm) << 16) | + (@as(u32, xn) << 5) | + @as(u32, xd); + try self.emit32(instr); + } + + /// CSET Xd, GT - Set if greater than + fn csetGT(self: *Self, xd: u5) !void { + // CSET GT = CSINC Xd, XZR, XZR, LE (cond=1101) + const instr: u32 = 0x9A9FD7E0 | @as(u32, xd); + try self.emit32(instr); + } + + /// CSET Xd, LT - Set if less than + fn csetLT(self: *Self, xd: u5) !void { + // CSET LT = CSINC Xd, XZR, XZR, GE (cond=1010) + const instr: u32 = 0x9A9FA7E0 | @as(u32, xd); + try self.emit32(instr); + } + + /// DUP Vd.4S, Xn - Duplicate GPR to all vector lanes + fn dup_4s_gpr(self: *Self, vd: u5, xn: u5) !void { + // DUP (general): 0 1 0 01110 00 0 imm5 0 0001 1 Rn Rd + // For 4S, imm5 = 00100 + const instr: u32 = 0x4E040C00 | + (@as(u32, xn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// ST1 {Vt.16B}, [Xn] - Store 16 bytes from vector register + fn st1_16b(self: *Self, vt: u5, xn: u5) !void { + // ST1 (single structure, no offset): 0 1 001100 0 00 0000 0111 00 Rn Rt + const instr: u32 = 0x4C007000 | + (@as(u32, xn) << 5) | + @as(u32, vt); + try self.emit32(instr); + } + + /// ST1 {Vt.16B}, [Xn], #16 - Store 16 bytes with post-increment + fn st1_16b_post(self: *Self, vt: u5, xn: u5) !void { + // ST1 (single structure, post-index, imm): 0 1 001100 1 00 11111 0111 00 Rn Rt + const instr: u32 = 0x4C9F7000 | + (@as(u32, xn) << 5) | + @as(u32, vt); + try self.emit32(instr); + } + + /// MUL Vd.16B, Vn.16B, Vm.16B - Vector multiply (16 x i8) + fn mul_16b(self: *Self, vd: u5, vn: u5, vm: u5) !void { + // MUL (vector): 0 1 0 01110 00 1 Rm 1 00111 Rn Rd + const instr: u32 = 0x4E209C00 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// CMEQ Vd.16B, Vn.16B, Vm.16B - Compare equal (sets 0xFF where equal, 0 where not) + fn cmeq_16b(self: *Self, vd: u5, vn: u5, vm: u5) !void { + // CMEQ (register): 0 1 1 01110 00 1 Rm 1 00011 Rn Rd + const instr: u32 = 0x6E208C00 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// NOT Vd.16B, Vn.16B - Bitwise NOT + fn not_16b(self: *Self, vd: u5, vn: u5) !void { + // NOT: 0 1 1 01110 00 10000 00101 10 Rn Rd + const instr: u32 = 0x6E205800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// CNT Vd.16B, Vn.16B - Population count per byte + fn cnt_16b(self: *Self, vd: u5, vn: u5) !void { + // CNT: 0 1 0 01110 00 10000 00101 10 Rn Rd + const instr: u32 = 0x4E205800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// UADDLV Hd, Vn.16B - Unsigned add long across vector (sum all bytes to u16) + fn uaddlv_h(self: *Self, vd: u5, vn: u5) !void { + // UADDLV: 0 1 1 01110 00 11000 0 0011 10 Rn Rd + const instr: u32 = 0x6E303800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// UMOV Wd, Vn.H[0] - Unsigned move from vector element to GPR (16-bit) + fn umov_h(self: *Self, wd: u5, vn: u5, index: u3) !void { + // UMOV: 0 0 0 01110 00 0 imm5 0 0111 1 Rn Rd + // For H (16-bit) element, imm5 = (index << 1) | 0b00010 + const imm5: u5 = (@as(u5, index) << 1) | 0b00010; + const instr: u32 = 0x0E003C00 | + (@as(u32, imm5) << 16) | + (@as(u32, vn) << 5) | + @as(u32, wd); + try self.emit32(instr); + } + + /// USHR Vd.16B, Vn.16B, #shift - Unsigned shift right + fn ushr_16b(self: *Self, vd: u5, vn: u5, shift: u4) !void { + // USHR: 0 1 1 01111 0 shift 00000 1 Rn Rd + // For 16B: Q=1, immh:immb encodes shift, for 8-bit elements immh=0001, immb=8-shift + // Actually: 0 1 1 01111 immh immb 0 0000 1 Rn Rd + // immh=0001 for 8-bit, immb = (8 - shift) for shift amount + const immh: u4 = 0b0001; + const immb: u3 = @intCast(8 - shift); + const instr: u32 = 0x6F080400 | + (@as(u32, immh) << 19) | + (@as(u32, immb) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// ADDV Bd, Vn.16B - Add across vector (8-bit result for byte vectors) + fn addv_16b(self: *Self, vd: u5, vn: u5) !void { + // ADDV: 0 1 0 01110 00 11000 1 1011 10 Rn Rd + // Q=1, size=00 (8-bit) + const instr: u32 = 0x4E31B800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// UMOV Wd, Vn.B[0] - Unsigned move from vector byte element to GPR + fn umov_b(self: *Self, wd: u5, vn: u5, index: u4) !void { + // UMOV: 0 0 0 01110 00 0 imm5 0 0111 1 Rn Rd + // For B (8-bit) element, imm5 = (index << 1) | 0b00001 + const imm5: u5 = (@as(u5, index) << 1) | 0b00001; + const instr: u32 = 0x0E003C00 | + (@as(u32, imm5) << 16) | + (@as(u32, vn) << 5) | + @as(u32, wd); + try self.emit32(instr); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // BUNDLE SIMD INSTRUCTIONS (for ternary thresholding) + // ═══════════════════════════════════════════════════════════════════════════ + + /// ADD Vd.16B, Vn.16B, Vm.16B - Vector add (16 bytes) + fn add_16b(self: *Self, vd: u5, vn: u5, vm: u5) !void { + const instr: u32 = 0x4E208400 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// SSHR Vd.16B, Vn.16B, #7 - Signed shift right (arithmetic) by 7 + fn sshr_16b_7(self: *Self, vd: u5, vn: u5) !void { + // For shift by 7 on 8-bit: immh:immb = 16-7 = 9 + const instr: u32 = 0x4F090400 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// CMGT Vd.16B, Vn.16B, #0 - Compare greater than zero + fn cmgt_16b_zero(self: *Self, vd: u5, vn: u5) !void { + const instr: u32 = 0x4E20A800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// NEG Vd.16B, Vn.16B - Vector negate + fn neg_16b(self: *Self, vd: u5, vn: u5) !void { + const instr: u32 = 0x6E20B800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// ORR Vd.16B, Vn.16B, Vm.16B - Bitwise OR + fn orr_16b(self: *Self, vd: u5, vn: u5, vm: u5) !void { + const instr: u32 = 0x4EA01C00 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // FLOATING POINT INSTRUCTIONS (for cosine computation) + // ═══════════════════════════════════════════════════════════════════════════ + + // FP double registers D0-D3 (same encoding as V registers) + const d0: u5 = 0; + const d1: u5 = 1; + const d2: u5 = 2; + const d3: u5 = 3; + + /// SCVTF Dd, Xn - Signed integer to double precision float + fn scvtf_d_x(self: *Self, vd: u5, xn: u5) !void { + const instr: u32 = 0x9E620000 | + (@as(u32, xn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// FMUL Dd, Dn, Dm - Floating point multiply (double) + fn fmul_d(self: *Self, vd: u5, vn: u5, vm: u5) !void { + const instr: u32 = 0x1E600800 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// FSQRT Dd, Dn - Floating point square root (double) + fn fsqrt_d(self: *Self, vd: u5, vn: u5) !void { + const instr: u32 = 0x1E61C000 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// FDIV Dd, Dn, Dm - Floating point divide (double) + fn fdiv_d(self: *Self, vd: u5, vn: u5, vm: u5) !void { + const instr: u32 = 0x1E601800 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// FMOV Xd, Dn - Move f64 from FP register to GPR + fn fmov_x_d(self: *Self, xd: u5, vn: u5) !void { + const instr: u32 = 0x9E660000 | + (@as(u32, vn) << 5) | + @as(u32, xd); + try self.emit32(instr); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // VSA OPERATION COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile dot product for ARM64 + /// Returns i64 in x0 + pub fn compileDotProduct(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue: save fp, lr + try self.stpPreIndex(x29, x30, sp, -2); // stp x29, x30, [sp, #-16]! + try self.movReg(x29, sp); // mov x29, sp + + // Save callee-saved registers + try self.stpPreIndex(x19, x20, sp, -2); // stp x19, x20, [sp, #-16]! + try self.stpPreIndex(x21, x22, sp, -2); // stp x21, x22, [sp, #-16]! + + // x19 = a pointer (first arg is in x0) + // x20 = b pointer (second arg is in x1) + // x21 = accumulator + // x22 = loop counter + try self.movReg(x19, x0); + try self.movReg(x20, x1); + try self.movImm16(x21, 0, 0); // accumulator = 0 + try self.movImm16(x22, 0, 0); // counter = 0 + + // Load dimension into x9 + if (dimension <= 0xFFFF) { + try self.movImm16(x9, @intCast(dimension), 0); + } else { + try self.loadImm64(x9, dimension); + } + + // Loop start + const loop_start = self.code.items.len; + + // Compare counter with dimension + try self.cmpReg(x22, x9); + + // B.GE to loop end (will patch) + const bge_offset = self.code.items.len; + try self.bcond(COND_GE, 0); // placeholder + + // Load a[i] sign-extended into w10 + try self.ldrsbReg(x10, x19, x22); + + // Load b[i] sign-extended into w11 + try self.ldrsbReg(x11, x20, x22); + + // Multiply: x10 = x10 * x11 + try self.smull(x10, x10, x11); + + // Add to accumulator: x21 = x21 + x10 + try self.addReg(x21, x21, x10); + + // Increment counter + try self.addImm(x22, x22, 1); + + // Branch back to loop start + const loop_end_check = self.code.items.len; + const back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(loop_start)) - @as(i32, @intCast(loop_end_check)), 4)); + try self.b(back_offset); + + // Loop end - patch the conditional branch + const loop_end = self.code.items.len; + const forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(loop_end)) - @as(i32, @intCast(bge_offset)), 4)); + const patched_instr: u32 = 0x54000000 | + (@as(u32, @as(u19, @bitCast(forward_offset))) << 5) | + @as(u32, COND_GE); + @memcpy(self.code.items[bge_offset..][0..4], &std.mem.toBytes(patched_instr)); + + // Move result to x0 + try self.movReg(x0, x21); + + // Restore callee-saved registers + try self.ldpPostIndex(x21, x22, sp, 2); // ldp x21, x22, [sp], #16 + try self.ldpPostIndex(x19, x20, sp, 2); // ldp x19, x20, [sp], #16 + + // Function epilogue + try self.ldpPostIndex(x29, x30, sp, 2); // ldp x29, x30, [sp], #16 + try self.retInstr(); + } + + /// Compile SIMD dot product using NEON SDOT instruction (ARMv8.4-A) + /// Processes 16 elements per iteration (4x speedup potential) + /// Requires: dimension >= 16 and dimension % 16 == 0 + pub fn compileDotProductSIMD(self: *Self, dimension: usize) !void { + if (dimension < 16 or dimension % 16 != 0) { + return error.InvalidDimension; + } + + self.reset(); + + // Function prologue + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + + // x19 = a pointer, x20 = b pointer + try self.movReg(x19, x0); + try self.movReg(x20, x1); + + // v0 = accumulator (initialized to zero) + try self.movi_4s_zero(v0); + + // x9 = dimension / 16 (number of SIMD iterations) + const num_iters = dimension / 16; + if (num_iters <= 0xFFFF) { + try self.movImm16(x9, @intCast(num_iters), 0); + } else { + try self.loadImm64(x9, num_iters); + } + + // x10 = loop counter + try self.movImm16(x10, 0, 0); + + // SIMD loop: process 16 elements per iteration + const loop_start = self.code.items.len; + + // Compare counter with num_iters + try self.cmpReg(x10, x9); + const bge_offset = self.code.items.len; + try self.bcond(COND_GE, 0); // placeholder, patch later + + // Load 16 bytes from a into v1 + try self.ld1_16b_post(v1, x19); + + // Load 16 bytes from b into v2 + try self.ld1_16b_post(v2, x20); + + // SDOT: v0.4s += dot(v1.16b, v2.16b) + // This computes 4 dot products of 4 i8 values each + try self.sdot_4s(v0, v1, v2); + + // Increment counter + try self.addImm(x10, x10, 1); + + // Branch back to loop start + const loop_end_check = self.code.items.len; + const back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(loop_start)) - @as(i32, @intCast(loop_end_check)), 4)); + try self.b(back_offset); + + // Loop end - patch conditional branch + const loop_end = self.code.items.len; + const forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(loop_end)) - @as(i32, @intCast(bge_offset)), 4)); + const patched_instr: u32 = 0x54000000 | + (@as(u32, @as(u19, @bitCast(forward_offset))) << 5) | + @as(u32, COND_GE); + @memcpy(self.code.items[bge_offset..][0..4], &std.mem.toBytes(patched_instr)); + + // Horizontal add: sum all 4 lanes of v0.4s into scalar + try self.addv_4s(v0, v0); // v0.s[0] = sum of all lanes + + // Move result from vector to x0 (sign-extended) + try self.smov_s(x0, v0, 0); + + // Restore callee-saved registers + try self.ldpPostIndex(x19, x20, sp, 2); + + // Function epilogue + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + /// Compile hybrid SIMD + scalar dot product for ANY dimension + /// Uses SIMD for (dim/16)*16 elements, scalar for remainder + pub fn compileDotProductHybrid(self: *Self, dimension: usize) !void { + self.reset(); + + const simd_iters = dimension / 16; + const remainder = dimension % 16; + + // Function prologue + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + try self.stpPreIndex(x21, x22, sp, -2); + + // x19 = a pointer, x20 = b pointer + try self.movReg(x19, x0); + try self.movReg(x20, x1); + + // v0 = SIMD accumulator (zero) + try self.movi_4s_zero(v0); + + // x21 = scalar accumulator (zero) + try self.movImm16(x21, 0, 0); + + // ═══════════════════════════════════════════════════════════════ + // SIMD LOOP: Process 16 elements per iteration + // ═══════════════════════════════════════════════════════════════ + if (simd_iters > 0) { + // x9 = number of SIMD iterations + if (simd_iters <= 0xFFFF) { + try self.movImm16(x9, @intCast(simd_iters), 0); + } else { + try self.loadImm64(x9, simd_iters); + } + + // x10 = SIMD loop counter + try self.movImm16(x10, 0, 0); + + const simd_loop_start = self.code.items.len; + + // Compare counter with num_iters + try self.cmpReg(x10, x9); + const simd_bge_offset = self.code.items.len; + try self.bcond(COND_GE, 0); // placeholder + + // Load 16 bytes from a into v1, post-increment x19 + try self.ld1_16b_post(v1, x19); + + // Load 16 bytes from b into v2, post-increment x20 + try self.ld1_16b_post(v2, x20); + + // SDOT: v0.4s += dot(v1.16b, v2.16b) + try self.sdot_4s(v0, v1, v2); + + // Increment counter + try self.addImm(x10, x10, 1); + + // Branch back to loop start + const simd_loop_end_check = self.code.items.len; + const simd_back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop_start)) - @as(i32, @intCast(simd_loop_end_check)), 4)); + try self.b(simd_back_offset); + + // Patch SIMD loop exit + const simd_loop_end = self.code.items.len; + const simd_forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(simd_loop_end)) - @as(i32, @intCast(simd_bge_offset)), 4)); + const simd_patched_instr: u32 = 0x54000000 | + (@as(u32, @as(u19, @bitCast(simd_forward_offset))) << 5) | + @as(u32, COND_GE); + @memcpy(self.code.items[simd_bge_offset..][0..4], &std.mem.toBytes(simd_patched_instr)); + + // Horizontal add SIMD result to scalar + try self.addv_4s(v0, v0); + try self.smov_s(x21, v0, 0); + } + + // ═══════════════════════════════════════════════════════════════ + // SCALAR LOOP: Process remaining elements one by one + // ═══════════════════════════════════════════════════════════════ + if (remainder > 0) { + // x9 = remainder count + try self.movImm16(x9, @intCast(remainder), 0); + + // x10 = scalar loop counter + try self.movImm16(x10, 0, 0); + + const scalar_loop_start = self.code.items.len; + + // Compare counter with remainder + try self.cmpReg(x10, x9); + const scalar_bge_offset = self.code.items.len; + try self.bcond(COND_GE, 0); // placeholder + + // Load a[i] sign-extended + try self.ldrsbReg(x11, x19, x10); + + // Load b[i] sign-extended + try self.ldrsbReg(x22, x20, x10); + + // Multiply + try self.smull(x11, x11, x22); + + // Add to accumulator + try self.addReg(x21, x21, x11); + + // Increment counter + try self.addImm(x10, x10, 1); + + // Branch back + const scalar_loop_end_check = self.code.items.len; + const scalar_back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop_start)) - @as(i32, @intCast(scalar_loop_end_check)), 4)); + try self.b(scalar_back_offset); + + // Patch scalar loop exit + const scalar_loop_end = self.code.items.len; + const scalar_forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_loop_end)) - @as(i32, @intCast(scalar_bge_offset)), 4)); + const scalar_patched_instr: u32 = 0x54000000 | + (@as(u32, @as(u19, @bitCast(scalar_forward_offset))) << 5) | + @as(u32, COND_GE); + @memcpy(self.code.items[scalar_bge_offset..][0..4], &std.mem.toBytes(scalar_patched_instr)); + } + + // Move result to x0 + try self.movReg(x0, x21); + + // Restore callee-saved registers + try self.ldpPostIndex(x21, x22, sp, 2); + try self.ldpPostIndex(x19, x20, sp, 2); + + // Function epilogue + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + /// Compile bind operation for ARM64 + pub fn compileBindDirect(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + try self.stpPreIndex(x21, x22, sp, -2); + + // x19 = a pointer, x20 = b pointer, x21 = dimension, x22 = counter + try self.movReg(x19, x0); + try self.movReg(x20, x1); + try self.movImm16(x22, 0, 0); + + if (dimension <= 0xFFFF) { + try self.movImm16(x21, @intCast(dimension), 0); + } else { + try self.loadImm64(x21, dimension); + } + + const loop_start = self.code.items.len; + try self.cmpReg(x22, x21); + + const bge_offset = self.code.items.len; + try self.bcond(COND_GE, 0); + + // Load a[i] and b[i] + try self.ldrsbReg(x10, x19, x22); + try self.ldrsbReg(x11, x20, x22); + + // Multiply (for ternary: -1*-1=1, -1*1=-1, 1*-1=-1, 1*1=1, 0*x=0) + try self.smull(x10, x10, x11); + + // Store result + try self.strbReg(x10, x19, x22); + + try self.addImm(x22, x22, 1); + + const loop_end_check = self.code.items.len; + const back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(loop_start)) - @as(i32, @intCast(loop_end_check)), 4)); + try self.b(back_offset); + + const loop_end = self.code.items.len; + const forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(loop_end)) - @as(i32, @intCast(bge_offset)), 4)); + const patched_instr: u32 = 0x54000000 | + (@as(u32, @as(u19, @bitCast(forward_offset))) << 5) | + @as(u32, COND_GE); + @memcpy(self.code.items[bge_offset..][0..4], &std.mem.toBytes(patched_instr)); + + try self.ldpPostIndex(x21, x22, sp, 2); + try self.ldpPostIndex(x19, x20, sp, 2); + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + /// Compile SIMD bind operation using NEON vector multiply + /// Processes 16 elements per iteration + pub fn compileBindSIMD(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + try self.stpPreIndex(x21, x22, sp, -2); + + // x19 = a pointer (modified in place), x20 = b pointer + try self.movReg(x19, x0); + try self.movReg(x20, x1); + + // SIMD loop for dimension / 16 iterations + const simd_iters = dimension / 16; + if (simd_iters > 0) { + if (simd_iters <= 0xFFFF) { + try self.movImm16(x21, @intCast(simd_iters), 0); + } else { + try self.loadImm64(x21, simd_iters); + } + try self.movImm16(x22, 0, 0); // counter + + const simd_loop = self.code.items.len; + try self.cmpReg(x22, x21); + const bge_simd = self.code.items.len; + try self.bcond(COND_GE, 0); + + // Load 16 bytes from a and b + try self.ld1_16b(v0, x19); + try self.ld1_16b(v1, x20); + + // Multiply: v0 = v0 * v1 (element-wise i8 multiply) + try self.mul_16b(v0, v0, v1); + + // Store result back to a with post-increment + try self.st1_16b_post(v0, x19); + + // Advance b pointer + try self.addImm(x20, x20, 16); + + // Increment counter + try self.addImm(x22, x22, 1); + + const simd_end_check = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop)) - @as(i32, @intCast(simd_end_check)), 4)); + try self.b(back); + + // Patch branch + const simd_end = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(simd_end)) - @as(i32, @intCast(bge_simd)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_simd..][0..4], &std.mem.toBytes(patched)); + } + + // Scalar loop for remainder (dimension % 16) + const remainder = dimension % 16; + if (remainder > 0) { + try self.movImm16(x21, @intCast(remainder), 0); + try self.movImm16(x22, 0, 0); + + const scalar_loop = self.code.items.len; + try self.cmpReg(x22, x21); + const bge_scalar = self.code.items.len; + try self.bcond(COND_GE, 0); + + try self.ldrsbReg(x10, x19, x22); + try self.ldrsbReg(x11, x20, x22); + try self.smull(x10, x10, x11); + try self.strbReg(x10, x19, x22); + try self.addImm(x22, x22, 1); + + const scalar_end_check = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop)) - @as(i32, @intCast(scalar_end_check)), 4)); + try self.b(back); + + const scalar_end = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_end)) - @as(i32, @intCast(bge_scalar)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_scalar..][0..4], &std.mem.toBytes(patched)); + } + + try self.ldpPostIndex(x21, x22, sp, 2); + try self.ldpPostIndex(x19, x20, sp, 2); + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + /// Compile SIMD hamming distance using NEON compare + /// Counts positions where a[i] != b[i] + pub fn compileHammingSIMD(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + try self.stpPreIndex(x21, x22, sp, -2); + + // x19 = a pointer, x20 = b pointer, x21 = accumulator + try self.movReg(x19, x0); + try self.movReg(x20, x1); + try self.movImm16(x21, 0, 0); // hamming distance = 0 + + // SIMD loop for dimension / 16 iterations + const simd_iters = dimension / 16; + if (simd_iters > 0) { + if (simd_iters <= 0xFFFF) { + try self.movImm16(x9, @intCast(simd_iters), 0); + } else { + try self.loadImm64(x9, simd_iters); + } + try self.movImm16(x22, 0, 0); // counter + + const simd_loop = self.code.items.len; + try self.cmpReg(x22, x9); + const bge_simd = self.code.items.len; + try self.bcond(COND_GE, 0); + + // Load 16 bytes from a and b with post-increment + try self.ld1_16b_post(v0, x19); + try self.ld1_16b_post(v1, x20); + + // Compare equal: v2 = (v0 == v1) ? 0xFF : 0x00 + try self.cmeq_16b(v2, v0, v1); + + // NOT: v2 = (v0 != v1) ? 0xFF : 0x00 + try self.not_16b(v2, v2); + + // Shift right by 7: 0xFF >> 7 = 1, 0x00 >> 7 = 0 + // Now each byte is 1 if positions differ, 0 if same + try self.ushr_16b(v2, v2, 7); + + // Sum all 16 bytes into a single value + try self.addv_16b(v3, v2); // v3.b[0] = sum of all bytes + + // Move byte to GPR + try self.umov_b(x10, v3, 0); + + // Add to accumulator + try self.addReg(x21, x21, x10); + + // Increment counter + try self.addImm(x22, x22, 1); + + const simd_end_check = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop)) - @as(i32, @intCast(simd_end_check)), 4)); + try self.b(back); + + const simd_end = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(simd_end)) - @as(i32, @intCast(bge_simd)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_simd..][0..4], &std.mem.toBytes(patched)); + } + + // Scalar loop for remainder + const remainder = dimension % 16; + if (remainder > 0) { + try self.movImm16(x9, @intCast(remainder), 0); + try self.movImm16(x22, 0, 0); + + const scalar_loop = self.code.items.len; + try self.cmpReg(x22, x9); + const bge_scalar = self.code.items.len; + try self.bcond(COND_GE, 0); + + try self.ldrsbReg(x10, x19, x22); + try self.ldrsbReg(x11, x20, x22); + + // Compare and increment if not equal + try self.cmpReg(x10, x11); + // CSINC x10, xzr, xzr, EQ -> x10 = (EQ) ? 0 : 1 + const csinc: u32 = 0x9A9F07E0 | // CSINC Xd, XZR, XZR, cond + (@as(u32, COND_EQ) << 12) | + @as(u32, x10); + try self.emit32(csinc); + try self.addReg(x21, x21, x10); + + try self.addImm(x22, x22, 1); + + const scalar_end_check = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop)) - @as(i32, @intCast(scalar_end_check)), 4)); + try self.b(back); + + const scalar_end = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_end)) - @as(i32, @intCast(bge_scalar)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_scalar..][0..4], &std.mem.toBytes(patched)); + } + + // Return result + try self.movReg(x0, x21); + + try self.ldpPostIndex(x21, x22, sp, 2); + try self.ldpPostIndex(x19, x20, sp, 2); + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + /// Compile fused cosine: dot_ab, dot_aa, dot_bb in single pass + /// Returns f64 bit pattern: cos = dot_ab / sqrt(dot_aa * dot_bb) + pub fn compileFusedCosine(self: *Self, dimension: usize) !void { + self.reset(); + + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + try self.stpPreIndex(x21, x22, sp, -2); + + try self.movReg(x19, x0); + try self.movReg(x20, x1); + + // Three accumulators + try self.movi_16b(v2, 0); + try self.movi_16b(v3, 0); + try self.movi_16b(v4, 0); + + const simd_iters = dimension / 16; + if (simd_iters > 0) { + if (simd_iters <= 0xFFFF) { + try self.movImm16(x9, @intCast(simd_iters), 0); + } else { + try self.loadImm64(x9, simd_iters); + } + try self.movImm16(x21, 0, 0); + + const simd_loop = self.code.items.len; + try self.cmpReg(x21, x9); + const bge_simd = self.code.items.len; + try self.bcond(COND_GE, 0); + + try self.ld1_16b_post(v0, x19); + try self.ld1_16b_post(v1, x20); + try self.sdot_4s(v2, v0, v1); + try self.sdot_4s(v3, v0, v0); + try self.sdot_4s(v4, v1, v1); + try self.addImm(x21, x21, 1); + + const simd_end = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop)) - @as(i32, @intCast(simd_end)), 4)); + try self.b(back); + + const simd_exit = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(simd_exit)) - @as(i32, @intCast(bge_simd)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_simd..][0..4], &std.mem.toBytes(patched)); + } + + try self.addv_4s(v2, v2); + try self.addv_4s(v3, v3); + try self.addv_4s(v4, v4); + try self.smov_s(x10, v2, 0); + try self.smov_s(x11, v3, 0); + try self.smov_s(x12, v4, 0); + + const remainder = dimension % 16; + if (remainder > 0) { + try self.movReg(x19, x0); + try self.movReg(x20, x1); + const offset = simd_iters * 16; + if (offset > 0) { + try self.loadImm64(x9, offset); + try self.addReg(x19, x19, x9); + try self.addReg(x20, x20, x9); + } + + try self.movImm16(x9, @intCast(remainder), 0); + try self.movImm16(x21, 0, 0); + + const scalar_loop = self.code.items.len; + try self.cmpReg(x21, x9); + const bge_scalar = self.code.items.len; + try self.bcond(COND_GE, 0); + + try self.ldrsbReg(x13, x19, x21); + try self.ldrsbReg(x14, x20, x21); + try self.mul(x15, x13, x14); + try self.addReg(x10, x10, x15); + try self.mul(x15, x13, x13); + try self.addReg(x11, x11, x15); + try self.mul(x15, x14, x14); + try self.addReg(x12, x12, x15); + try self.addImm(x21, x21, 1); + + const scalar_end = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop)) - @as(i32, @intCast(scalar_end)), 4)); + try self.b(back); + + const scalar_exit = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_exit)) - @as(i32, @intCast(bge_scalar)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_scalar..][0..4], &std.mem.toBytes(patched)); + } + + try self.scvtf_d_x(d0, x10); + try self.scvtf_d_x(d1, x11); + try self.scvtf_d_x(d2, x12); + try self.fmul_d(d1, d1, d2); + try self.fsqrt_d(d1, d1); + try self.fdiv_d(d0, d0, d1); + try self.fmov_x_d(x0, d0); + + try self.ldpPostIndex(x21, x22, sp, 2); + try self.ldpPostIndex(x19, x20, sp, 2); + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + /// Compile bundle SIMD: result[i] = threshold(a[i] + b[i]) + pub fn compileBundleSIMD(self: *Self, dimension: usize) !void { + self.reset(); + + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + try self.stpPreIndex(x21, x22, sp, -2); + + try self.movReg(x19, x0); + try self.movReg(x20, x1); + try self.movReg(x21, x0); + + const simd_iters = dimension / 16; + if (simd_iters > 0) { + if (simd_iters <= 0xFFFF) { + try self.movImm16(x9, @intCast(simd_iters), 0); + } else { + try self.loadImm64(x9, simd_iters); + } + try self.movImm16(x22, 0, 0); + + const simd_loop = self.code.items.len; + try self.cmpReg(x22, x9); + const bge_simd = self.code.items.len; + try self.bcond(COND_GE, 0); + + try self.ld1_16b_post(v0, x21); + try self.ld1_16b_post(v1, x20); + try self.add_16b(v2, v0, v1); + try self.sshr_16b_7(v3, v2); + try self.cmgt_16b_zero(v4, v2); + try self.neg_16b(v4, v4); + try self.orr_16b(v2, v3, v4); + try self.st1_16b_post(v2, x19); + try self.addImm(x22, x22, 1); + + const simd_end = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop)) - @as(i32, @intCast(simd_end)), 4)); + try self.b(back); + + const simd_exit = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(simd_exit)) - @as(i32, @intCast(bge_simd)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_simd..][0..4], &std.mem.toBytes(patched)); + } + + const remainder = dimension % 16; + if (remainder > 0) { + try self.movImm16(x9, @intCast(remainder), 0); + try self.movImm16(x22, 0, 0); + + const scalar_loop = self.code.items.len; + try self.cmpReg(x22, x9); + const bge_scalar = self.code.items.len; + try self.bcond(COND_GE, 0); + + try self.ldrsbReg(x10, x21, x22); + try self.ldrsbReg(x11, x20, x22); + try self.addReg(x10, x10, x11); + try self.cmpImm(x10, 0); + try self.movImm16(x11, 0, 0); + try self.csetGT(x11); + try self.movImm16(x12, 0, 0); + try self.csetLT(x12); + try self.subReg(x10, x11, x12); + try self.strbReg(x10, x19, x22); + try self.addImm(x22, x22, 1); + + const scalar_end = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop)) - @as(i32, @intCast(scalar_end)), 4)); + try self.b(back); + + const scalar_exit = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_exit)) - @as(i32, @intCast(bge_scalar)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_scalar..][0..4], &std.mem.toBytes(patched)); + } + + try self.ldpPostIndex(x21, x22, sp, 2); + try self.ldpPostIndex(x19, x20, sp, 2); + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // EXECUTION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Make code executable and return function pointer + pub fn finalize(self: *Self) !*const fn (*anyopaque, *anyopaque) callconv(.c) i64 { + const code_size = self.code.items.len; + if (code_size == 0) return error.EmptyCode; + + // ARM64 can have 16KB pages on Apple Silicon + const page_size: usize = 16384; + const alloc_size = std.mem.alignForward(usize, code_size, page_size); + + const mem = try std.posix.mmap( + null, + alloc_size, + 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); + + self.exec_mem = mem; + + return @ptrCast(mem.ptr); + } + + pub fn codeSize(self: *const Self) usize { + return self.code.items.len; + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "ARM64 JIT compiler init and deinit" { + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + try std.testing.expectEqual(@as(usize, 0), compiler.codeSize()); +} + +test "ARM64 JIT dot product compilation" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 8; + try compiler.compileDotProduct(dim); + + // Code should be generated + try std.testing.expect(compiler.codeSize() > 0); + // ARM64 instructions are 4 bytes each + try std.testing.expect(compiler.codeSize() % 4 == 0); +} + +test "ARM64 JIT dot product execution" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 8; + try compiler.compileDotProduct(dim); + + const func = try compiler.finalize(); + + // Create test data + const a = [dim]i8{ 1, -1, 1, 0, 1, -1, 0, 1 }; + const b = [dim]i8{ 1, 1, -1, 1, 1, 1, 1, -1 }; + + // Expected: 1*1 + (-1)*1 + 1*(-1) + 0*1 + 1*1 + (-1)*1 + 0*1 + 1*(-1) + // = 1 - 1 - 1 + 0 + 1 - 1 + 0 - 1 = -2 + const expected: i64 = -2; + + var a_mut = a; + var b_mut = b; + const result = func(@ptrCast(&a_mut), @ptrCast(&b_mut)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 JIT bind compilation" { + if (!is_arm64) { + return; + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 8; + try compiler.compileBindDirect(dim); + + try std.testing.expect(compiler.codeSize() > 0); + try std.testing.expect(compiler.codeSize() % 4 == 0); +} + +test "ARM64 NEON SIMD dot product compilation" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 64; // Must be multiple of 16 + try compiler.compileDotProductSIMD(dim); + + try std.testing.expect(compiler.codeSize() > 0); + try std.testing.expect(compiler.codeSize() % 4 == 0); +} + +test "ARM64 NEON SIMD dot product execution" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 16; // Minimum SIMD dimension + try compiler.compileDotProductSIMD(dim); + + const func = try compiler.finalize(); + + // Create test data: all 1s dot all 1s = 16 + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + } + + const expected: i64 = 16; + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 NEON SIMD dot product with mixed values" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 32; + try compiler.compileDotProductSIMD(dim); + + const func = try compiler.finalize(); + + // Create test data: alternating 1, -1 + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = if (i % 2 == 0) 1 else -1; + b[i] = 1; + } + // Expected: 16 * 1 + 16 * (-1) = 0 + const expected: i64 = 0; + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 NEON SIMD dot product large dimension" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 256; // 16 SIMD iterations + try compiler.compileDotProductSIMD(dim); + + const func = try compiler.finalize(); + + // Create test data + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + for (0..dim) |i| { + // Ternary values: -1, 0, 1 + const val_a: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); + const val_b: i8 = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + a[i] = val_a; + b[i] = val_b; + expected += @as(i64, val_a) * @as(i64, val_b); + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 NEON SIMD benchmark vs scalar" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + const dim = 1024; // Large dimension for meaningful benchmark + const iterations = 10000; + + // Prepare test data + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + } + + // Compile scalar version + var scalar_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer scalar_compiler.deinit(); + try scalar_compiler.compileDotProduct(dim); + const scalar_func = try scalar_compiler.finalize(); + + // Compile SIMD version + var simd_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer simd_compiler.deinit(); + try simd_compiler.compileDotProductSIMD(dim); + const simd_func = try simd_compiler.finalize(); + + // Benchmark scalar + var timer = try std.time.Timer.start(); + var scalar_result: i64 = 0; + for (0..iterations) |_| { + scalar_result = scalar_func(@ptrCast(&a), @ptrCast(&b)); + } + const scalar_ns = timer.read(); + + // Benchmark SIMD + timer.reset(); + var simd_result: i64 = 0; + for (0..iterations) |_| { + simd_result = simd_func(@ptrCast(&a), @ptrCast(&b)); + } + const simd_ns = timer.read(); + + // Verify results match + try std.testing.expectEqual(scalar_result, simd_result); + + // Print benchmark results + const scalar_ms = @as(f64, @floatFromInt(scalar_ns)) / 1_000_000.0; + const simd_ms = @as(f64, @floatFromInt(simd_ns)) / 1_000_000.0; + const speedup = scalar_ms / simd_ms; + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" ARM64 NEON SIMD BENCHMARK RESULTS\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Dimension: {d} elements\n", .{dim}); + std.debug.print(" Iterations: {d}\n", .{iterations}); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" Scalar: {d:.3} ms ({d:.0} ns/iter)\n", .{ scalar_ms, @as(f64, @floatFromInt(scalar_ns)) / @as(f64, iterations) }); + std.debug.print(" SIMD: {d:.3} ms ({d:.0} ns/iter)\n", .{ simd_ms, @as(f64, @floatFromInt(simd_ns)) / @as(f64, iterations) }); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + + // Assert SIMD is faster (relaxed for CI/heavy-load environments) + try std.testing.expect(speedup > 0.8); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// HYBRID SIMD + SCALAR TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "ARM64 hybrid dot product - aligned dimension (32)" { + if (!is_arm64) return; + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 32; // Aligned: 2 SIMD iters, 0 scalar + try compiler.compileDotProductHybrid(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + expected += 1; + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 hybrid dot product - non-aligned dimension (17)" { + if (!is_arm64) return; + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 17; // Non-aligned: 1 SIMD iter + 1 scalar + try compiler.compileDotProductHybrid(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + expected += 1; + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 hybrid dot product - small dimension (7)" { + if (!is_arm64) return; + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 7; // Pure scalar: 0 SIMD iters, 7 scalar + try compiler.compileDotProductHybrid(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + for (0..dim) |i| { + const val_a: i8 = if (i % 2 == 0) 1 else -1; + a[i] = val_a; + b[i] = 1; + expected += val_a; + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 hybrid dot product - dimension 100" { + if (!is_arm64) return; + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 100; // 6 SIMD iters + 4 scalar + try compiler.compileDotProductHybrid(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + for (0..dim) |i| { + const val_a: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); + const val_b: i8 = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + a[i] = val_a; + b[i] = val_b; + expected += @as(i64, val_a) * @as(i64, val_b); + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 hybrid dot product - dimension 1000" { + if (!is_arm64) return; + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 1000; // 62 SIMD iters + 8 scalar + try compiler.compileDotProductHybrid(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + for (0..dim) |i| { + const val_a: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); + const val_b: i8 = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + a[i] = val_a; + b[i] = val_b; + expected += @as(i64, val_a) * @as(i64, val_b); + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 hybrid benchmark vs pure scalar" { + if (!is_arm64) return; + + const dim = 1000; // Non-aligned dimension + const iterations = 10000; + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + } + + // Compile pure scalar + var scalar_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer scalar_compiler.deinit(); + try scalar_compiler.compileDotProduct(dim); + const scalar_func = try scalar_compiler.finalize(); + + // Compile hybrid + var hybrid_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer hybrid_compiler.deinit(); + try hybrid_compiler.compileDotProductHybrid(dim); + const hybrid_func = try hybrid_compiler.finalize(); + + // Benchmark scalar + var timer = try std.time.Timer.start(); + var scalar_result: i64 = 0; + for (0..iterations) |_| { + scalar_result = scalar_func(@ptrCast(&a), @ptrCast(&b)); + } + const scalar_ns = timer.read(); + + // Benchmark hybrid + timer.reset(); + var hybrid_result: i64 = 0; + for (0..iterations) |_| { + hybrid_result = hybrid_func(@ptrCast(&a), @ptrCast(&b)); + } + const hybrid_ns = timer.read(); + + // Verify results match + try std.testing.expectEqual(scalar_result, hybrid_result); + + const scalar_ms = @as(f64, @floatFromInt(scalar_ns)) / 1_000_000.0; + const hybrid_ms = @as(f64, @floatFromInt(hybrid_ns)) / 1_000_000.0; + const speedup = scalar_ms / hybrid_ms; + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" ARM64 HYBRID SIMD+SCALAR BENCHMARK (dim=1000)\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" SIMD iters: {d}, Scalar remainder: {d}\n", .{ dim / 16, dim % 16 }); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" Pure Scalar: {d:.3} ms ({d:.0} ns/iter)\n", .{ scalar_ms, @as(f64, @floatFromInt(scalar_ns)) / @as(f64, iterations) }); + std.debug.print(" Hybrid: {d:.3} ms ({d:.0} ns/iter)\n", .{ hybrid_ms, @as(f64, @floatFromInt(hybrid_ns)) / @as(f64, iterations) }); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + + // Hybrid should be faster (lenient threshold for flaky benchmarks on loaded systems) + // Minimum 1.0x means it's not slower - any speedup is acceptable + try std.testing.expect(speedup > 1.0); +} + +test "ARM64 SIMD bind correctness" { + if (!is_arm64) return; + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 64; + try compiler.compileBindSIMD(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: [dim]i8 = undefined; + + // Initialize: a = [1, -1, 0, 1, ...], b = [1, 1, -1, -1, ...] + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = if (i % 4 < 2) @as(i8, 1) else @as(i8, -1); + expected[i] = a[i] * b[i]; + } + + // Run SIMD bind (modifies a in place) + _ = func(@ptrCast(&a), @ptrCast(&b)); + + // Verify + for (0..dim) |i| { + try std.testing.expectEqual(expected[i], a[i]); + } +} + +test "ARM64 SIMD bind non-aligned dimension" { + if (!is_arm64) return; + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 100; // Not divisible by 16 + try compiler.compileBindSIMD(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: [dim]i8 = undefined; + + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + expected[i] = a[i] * b[i]; + } + + _ = func(@ptrCast(&a), @ptrCast(&b)); + + for (0..dim) |i| { + try std.testing.expectEqual(expected[i], a[i]); + } +} + +test "ARM64 SIMD hamming correctness" { + if (!is_arm64) return; + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 64; + try compiler.compileHammingSIMD(dim); + const func = try compiler.finalize(); + + // Test 1: identical vectors -> hamming = 0 + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + } + const hamming_identical = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(@as(i64, 0), hamming_identical); + + // Test 2: all different -> hamming = dim + for (0..dim) |i| { + a[i] = 1; + b[i] = -1; + } + const hamming_all_diff = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(@as(i64, dim), hamming_all_diff); + + // Test 3: half different + for (0..dim) |i| { + a[i] = 1; + b[i] = if (i < dim / 2) @as(i8, 1) else @as(i8, -1); + } + const hamming_half = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(@as(i64, dim / 2), hamming_half); +} + +test "ARM64 SIMD hamming non-aligned dimension" { + if (!is_arm64) return; + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 100; // Not divisible by 16 + try compiler.compileHammingSIMD(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + + // Count expected differences manually + var expected_hamming: i64 = 0; + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + if (a[i] != b[i]) expected_hamming += 1; + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected_hamming, result); +} + +test "ARM64 SIMD bind benchmark vs scalar" { + if (!is_arm64) return; + const dim = 1024; + const iterations = 10000; + + var a_simd: [dim]i8 = undefined; + var a_scalar: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + + for (0..dim) |i| { + const val = @as(i8, @intCast(@as(i32, @intCast(i % 3)) - 1)); + a_simd[i] = val; + a_scalar[i] = val; + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + } + + // Compile SIMD + var simd_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer simd_compiler.deinit(); + try simd_compiler.compileBindSIMD(dim); + const simd_func = try simd_compiler.finalize(); + + // Compile scalar + var scalar_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer scalar_compiler.deinit(); + try scalar_compiler.compileBindDirect(dim); + const scalar_func = try scalar_compiler.finalize(); + + // Benchmark SIMD + var timer = try std.time.Timer.start(); + 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(); + + // Benchmark scalar + timer.reset(); + 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 simd_ms = @as(f64, @floatFromInt(simd_ns)) / 1_000_000.0; + const scalar_ms = @as(f64, @floatFromInt(scalar_ns)) / 1_000_000.0; + const speedup = scalar_ms / simd_ms; + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" ARM64 SIMD BIND BENCHMARK (dim={d})\n", .{dim}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Scalar: {d:.3} ms\n", .{scalar_ms}); + std.debug.print(" SIMD: {d:.3} ms\n", .{simd_ms}); + std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + + // Note: Bind speedup modest due to array reset overhead +} + +test "ARM64 fused cosine correctness" { + if (!is_arm64) return; + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 64; + try compiler.compileFusedCosine(dim); + const func = try compiler.finalize(); + + // Test identical vectors: cos = 1.0 + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + } + + const result_bits = func(@ptrCast(&a), @ptrCast(&b)); + const result: f64 = @bitCast(result_bits); + try std.testing.expectApproxEqRel(@as(f64, 1.0), result, 0.001); + + // Test opposite vectors: cos = -1.0 + for (0..dim) |i| { + a[i] = 1; + b[i] = -1; + } + const neg_bits = func(@ptrCast(&a), @ptrCast(&b)); + const neg_result: f64 = @bitCast(neg_bits); + try std.testing.expectApproxEqRel(@as(f64, -1.0), neg_result, 0.001); +} + +test "ARM64 fused cosine benchmark vs 3x dot" { + if (!is_arm64) return; + var fused_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer fused_compiler.deinit(); + var dot_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer dot_compiler.deinit(); + + const dim = 1024; + const iterations = 10000; + + try fused_compiler.compileFusedCosine(dim); + const fused_func = try fused_compiler.finalize(); + + try dot_compiler.compileDotProductHybrid(dim); + const dot_func = try dot_compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + } + + // Benchmark fused + var timer = try std.time.Timer.start(); + 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(); + + // Benchmark 3x dot + timer.reset(); + var dot_result: f64 = 0; + for (0..iterations) |_| { + const dot_ab = dot_func(@ptrCast(&a), @ptrCast(&b)); + const dot_aa = dot_func(@ptrCast(&a), @ptrCast(&a)); + const dot_bb = dot_func(@ptrCast(&b), @ptrCast(&b)); + 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 fused_ms = @as(f64, @floatFromInt(fused_ns)) / 1_000_000.0; + const dot_ms = @as(f64, @floatFromInt(dot_ns)) / 1_000_000.0; + const speedup = dot_ms / fused_ms; + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" ARM64 FUSED COSINE BENCHMARK (dim={d})\n", .{dim}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" 3x Dot: {d:.3} ms\n", .{dot_ms}); + std.debug.print(" Fused: {d:.3} ms\n", .{fused_ms}); + std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); + std.debug.print(" Results: fused={d:.6}, 3xdot={d:.6}\n", .{ fused_result, dot_result }); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); +} + +test "ARM64 bundle SIMD compilation" { + if (!is_arm64) return; + // Just verify compilation works, bundle correctness tested via vsa_jit + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 32; + try compiler.compileBundleSIMD(dim); + const func = try compiler.finalize(); + _ = func; +} + +test "ARM64 bundle SIMD non-aligned" { + if (!is_arm64) return; + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 23; // Non-aligned + try compiler.compileBundleSIMD(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + } + + _ = func(@ptrCast(&a), @ptrCast(&b)); + // Bundle SIMD correctness to be verified via integration tests +} diff --git a/src/jit_unified.zig b/src/jit_unified.zig new file mode 100644 index 0000000000..45ac5c6517 --- /dev/null +++ b/src/jit_unified.zig @@ -0,0 +1,434 @@ +// @origin(spec:jit_unified.tri) @regen(manual-impl) +// @origin(manual) @regen(pending) +// Trinity Unified JIT Compiler +// Architecture-agnostic interface with compile-time backend selection +// +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q +// φ² + 1/φ² = 3 + +const std = @import("std"); +const builtin = @import("builtin"); + +// Import architecture-specific backends +const arm64 = @import("jit_arm64.zig"); +const x86_64 = @import("jit_x86_64.zig"); + +// ═══════════════════════════════════════════════════════════════════════════════ +// ARCHITECTURE DETECTION +// ═══════════════════════════════════════════════════════════════════════════════ + +pub const Architecture = enum { + arm64, + x86_64, + unsupported, +}; + +pub const current_arch: Architecture = switch (builtin.cpu.arch) { + .aarch64 => .arm64, + .x86_64 => .x86_64, + else => .unsupported, +}; + +pub const is_arm64 = current_arch == .arm64; +pub const is_x86_64 = current_arch == .x86_64; +pub const is_jit_supported = current_arch != .unsupported; + +// ═══════════════════════════════════════════════════════════════════════════════ +// UNIFIED JIT FUNCTION TYPES +// ═══════════════════════════════════════════════════════════════════════════════ + +/// JIT-compiled dot product function +/// Takes two i8 array pointers and returns i64 dot product +pub const JitDotFn = *const fn (*anyopaque, *anyopaque) callconv(.c) i64; + +/// JIT-compiled bind function +/// Takes two i8 array pointers, stores result in first +pub const JitBindFn = *const fn (*anyopaque, *anyopaque) callconv(.c) void; + +// ═══════════════════════════════════════════════════════════════════════════════ +// UNIFIED JIT COMPILER +// ═══════════════════════════════════════════════════════════════════════════════ + +pub const UnifiedJitCompiler = struct { + allocator: std.mem.Allocator, + + // Architecture-specific backend + backend: Backend, + + const Backend = union(Architecture) { + arm64: arm64.Arm64JitCompiler, + x86_64: x86_64.X86_64JitCompiler, + unsupported: void, + }; + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator) Self { + return Self{ + .allocator = allocator, + .backend = switch (current_arch) { + .arm64 => .{ .arm64 = arm64.Arm64JitCompiler.init(allocator) }, + .x86_64 => .{ .x86_64 = x86_64.X86_64JitCompiler.init(allocator) }, + .unsupported => .{ .unsupported = {} }, + }, + }; + } + + pub fn deinit(self: *Self) void { + switch (self.backend) { + .arm64 => |*b| b.deinit(), + .x86_64 => |*b| b.deinit(), + .unsupported => {}, + } + } + + /// Get current architecture name + pub fn archName() []const u8 { + return switch (current_arch) { + .arm64 => "ARM64 (AArch64)", + .x86_64 => "x86-64", + .unsupported => "Unsupported", + }; + } + + /// Check if SIMD is available + pub fn hasSIMD() bool { + return switch (current_arch) { + .arm64 => true, // NEON is always available on AArch64 + .x86_64 => false, // DEFERRED: Add CPUID-based AVX/SSE detection for x86_64 + .unsupported => false, + }; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // DOT PRODUCT COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile dot product - automatically selects best implementation + /// For ARM64: uses hybrid SIMD+scalar for any dimension + /// For x86_64: uses scalar loop + pub fn compileDotProduct(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| { + // Use hybrid for best performance on any dimension + try b.compileDotProductHybrid(dimension); + }, + .x86_64 => |*b| { + // x86_64 scalar implementation + try b.compileDotProduct(dimension); + }, + .unsupported => return error.UnsupportedArchitecture, + } + } + + /// Compile pure SIMD dot product (requires dimension % 16 == 0 on ARM64) + pub fn compileDotProductSIMD(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileDotProductSIMD(dimension), + .x86_64 => |*b| { + // x86_64 falls back to scalar (DEFERRED: add AVX2 SIMD implementation) + try b.compileDotProduct(dimension); + }, + .unsupported => return error.UnsupportedArchitecture, + } + } + + /// Compile pure scalar dot product + pub fn compileDotProductScalar(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileDotProduct(dimension), + .x86_64 => |*b| try b.compileDotProduct(dimension), + .unsupported => return error.UnsupportedArchitecture, + } + } + + // ═══════════════════════════════════════════════════════════════════════════ + // BIND COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile bind operation - uses SIMD on ARM64 + pub fn compileBind(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileBindSIMD(dimension), + .x86_64 => |*b| try b.compileBindDirect(dimension), + .unsupported => return error.UnsupportedArchitecture, + } + } + + /// Compile bind operation (scalar version) + pub fn compileBindScalar(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileBindDirect(dimension), + .x86_64 => |*b| try b.compileBindDirect(dimension), + .unsupported => return error.UnsupportedArchitecture, + } + } + + // ═══════════════════════════════════════════════════════════════════════════ + // HAMMING DISTANCE COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile hamming distance - uses SIMD on ARM64 + pub fn compileHamming(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileHammingSIMD(dimension), + .x86_64 => return error.UnsupportedOperation, + .unsupported => return error.UnsupportedArchitecture, + } + } + + // ═══════════════════════════════════════════════════════════════════════════ + // FUSED COSINE COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile fused cosine similarity - computes dot(a,b), dot(a,a), dot(b,b) in single pass + /// Returns f64 bit pattern (2.5x faster than 3 separate dot products) + pub fn compileFusedCosine(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileFusedCosine(dimension), + .x86_64 => return error.UnsupportedOperation, + .unsupported => return error.UnsupportedArchitecture, + } + } + + // ═══════════════════════════════════════════════════════════════════════════ + // BUNDLE COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile bundle operation - threshold(a + b) to {-1, 0, 1} + pub fn compileBundleSIMD(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileBundleSIMD(dimension), + .x86_64 => return error.UnsupportedOperation, + .unsupported => return error.UnsupportedArchitecture, + } + } + + // ═══════════════════════════════════════════════════════════════════════════ + // FINALIZATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Make compiled code executable and return function pointer + pub fn finalize(self: *Self) !JitDotFn { + switch (self.backend) { + .arm64 => |*b| return try b.finalize(), + .x86_64 => |*b| return try b.finalize(), + .unsupported => return error.UnsupportedArchitecture, + } + } + + /// Get generated code size + pub fn codeSize(self: *Self) usize { + return switch (self.backend) { + .arm64 => |*b| b.codeSize(), + .x86_64 => |*b| b.codeSize(), + .unsupported => 0, + }; + } + + /// Reset compiler for new compilation + pub fn reset(self: *Self) void { + switch (self.backend) { + .arm64 => |*b| b.reset(), + .x86_64 => |*b| b.reset(), + .unsupported => {}, + } + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// CONVENIENCE FUNCTIONS +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Quick compile and run dot product +pub fn jitDotProduct(allocator: std.mem.Allocator, a: []const i8, b: []const i8) !i64 { + if (a.len != b.len) return error.DimensionMismatch; + + var compiler = UnifiedJitCompiler.init(allocator); + defer compiler.deinit(); + + try compiler.compileDotProduct(a.len); + const func = try compiler.finalize(); + + // Need mutable copies for the function call + const a_copy = try allocator.alloc(i8, a.len); + defer allocator.free(a_copy); + @memcpy(a_copy, a); + + const b_copy = try allocator.alloc(i8, b.len); + defer allocator.free(b_copy); + @memcpy(b_copy, b); + + return func(@ptrCast(a_copy.ptr), @ptrCast(b_copy.ptr)); +} + +/// Print JIT capabilities info +pub fn printCapabilities() void { + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" TRINITY UNIFIED JIT CAPABILITIES\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Architecture: {s}\n", .{UnifiedJitCompiler.archName()}); + std.debug.print(" JIT Supported: {}\n", .{is_jit_supported}); + std.debug.print(" SIMD Available: {}\n", .{UnifiedJitCompiler.hasSIMD()}); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + + if (is_arm64) { + std.debug.print(" ARM64 Features:\n", .{}); + std.debug.print(" • NEON SIMD (128-bit vectors)\n", .{}); + std.debug.print(" • SDOT instruction (16 i8 elements/cycle)\n", .{}); + std.debug.print(" • Hybrid SIMD+Scalar for any dimension\n", .{}); + std.debug.print(" • Expected speedup: 15-70x over scalar\n", .{}); + } else if (is_x86_64) { + std.debug.print(" x86-64 Features:\n", .{}); + std.debug.print(" • Scalar JIT implementation\n", .{}); + std.debug.print(" • System V ABI compatible\n", .{}); + std.debug.print(" • DEFERRED (v12): AVX2/AVX-512 SIMD support\n", .{}); + } + + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "Unified JIT architecture detection" { + const arch = current_arch; + + // Should be one of the known architectures + try std.testing.expect(arch == .arm64 or arch == .x86_64 or arch == .unsupported); + + // Consistency checks + if (is_arm64) { + try std.testing.expectEqual(Architecture.arm64, arch); + } + if (is_x86_64) { + try std.testing.expectEqual(Architecture.x86_64, arch); + } +} + +test "Unified JIT compiler init/deinit" { + var compiler = UnifiedJitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + // Should initialize without error + try std.testing.expect(true); +} + +test "Unified JIT dot product on ARM64" { + if (!is_arm64) return; + + var compiler = UnifiedJitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 100; // Non-aligned dimension + try compiler.compileDotProduct(dim); + + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + expected += 1; + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "Unified JIT dot product various dimensions" { + if (!is_arm64) return; + + const test_dims = [_]usize{ 1, 7, 16, 17, 32, 100, 256, 1000 }; + + for (test_dims) |dim| { + var compiler = UnifiedJitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + try compiler.compileDotProduct(dim); + const func = try compiler.finalize(); + + // Allocate dynamic arrays + var a = try std.testing.allocator.alloc(i8, dim); + defer std.testing.allocator.free(a); + var b = try std.testing.allocator.alloc(i8, dim); + defer std.testing.allocator.free(b); + + var expected: i64 = 0; + for (0..dim) |i| { + const val: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); + a[i] = val; + b[i] = 1; + expected += val; + } + + const result = func(@ptrCast(a.ptr), @ptrCast(b.ptr)); + try std.testing.expectEqual(expected, result); + } +} + +test "Unified JIT convenience function" { + if (!is_arm64) return; + + const a = [_]i8{ 1, 1, 1, -1, -1, 0, 0, 1 }; + const b = [_]i8{ 1, 1, 1, 1, 1, 1, 1, 1 }; + + // Expected: 1 + 1 + 1 - 1 - 1 + 0 + 0 + 1 = 2 + const expected: i64 = 2; + + const result = try jitDotProduct(std.testing.allocator, &a, &b); + try std.testing.expectEqual(expected, result); +} + +test "Unified JIT print capabilities" { + // Just verify it doesn't crash + printCapabilities(); +} + +test "Unified JIT benchmark" { + if (!is_arm64) return; + + const dim = 1024; + const iterations = 10000; + + var compiler = UnifiedJitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + try compiler.compileDotProduct(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + } + + var timer = try std.time.Timer.start(); + var result: i64 = 0; + for (0..iterations) |_| { + result = func(@ptrCast(&a), @ptrCast(&b)); + } + const ns = timer.read(); + + const ms = @as(f64, @floatFromInt(ns)) / 1_000_000.0; + const ns_per_iter = @as(f64, @floatFromInt(ns)) / @as(f64, iterations); + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" UNIFIED JIT BENCHMARK ({s})\n", .{UnifiedJitCompiler.archName()}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Dimension: {d}, Iterations: {d}\n", .{ dim, iterations }); + std.debug.print(" Total time: {d:.3} ms\n", .{ms}); + std.debug.print(" Per iteration: {d:.0} ns\n", .{ns_per_iter}); + std.debug.print(" Throughput: {d:.2} M dot products/sec\n", .{@as(f64, iterations) / ms * 1000.0 / 1_000_000.0}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + + // Sanity check - result should be deterministic + try std.testing.expect(result != 0 or dim == 0); +} diff --git a/src/jit_x86_64.zig b/src/jit_x86_64.zig new file mode 100644 index 0000000000..104242a4c9 --- /dev/null +++ b/src/jit_x86_64.zig @@ -0,0 +1,471 @@ +// @origin(spec:jit_x86_64.tri) @regen(manual-impl) +// @origin(manual) @regen(pending) +// Trinity JIT Compiler - x86-64 Backend +// Compiles VSA operations to native x86-64 machine code +// +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q +// φ² + 1/φ² = 3 + +const std = @import("std"); +const builtin = @import("builtin"); + +// ═══════════════════════════════════════════════════════════════════════════════ +// X86-64 JIT COMPILER +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Check if we're on x86-64 +pub const is_x86_64 = builtin.cpu.arch == .x86_64; + +/// X86-64 JIT Compiler +pub const X86_64JitCompiler = struct { + code: std.ArrayListUnmanaged(u8), + allocator: std.mem.Allocator, + exec_mem: ?[]align(std.heap.page_size_min) u8 = null, + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator) Self { + return Self{ + .code = .{}, + .allocator = allocator, + }; + } + + pub fn deinit(self: *Self) void { + self.code.deinit(self.allocator); + if (self.exec_mem) |mem| { + std.posix.munmap(mem); + } + } + + pub fn reset(self: *Self) void { + self.code.clearRetainingCapacity(); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // X86-64 INSTRUCTION ENCODING HELPERS + // ═══════════════════════════════════════════════════════════════════════════ + + /// Emit raw bytes + fn emit(self: *Self, bytes: []const u8) !void { + try self.code.appendSlice(self.allocator, bytes); + } + + /// Emit single byte + fn emit1(self: *Self, b: u8) !void { + try self.code.append(self.allocator, b); + } + + /// Emit 32-bit immediate (little-endian) + fn emitImm32(self: *Self, imm: i32) !void { + try self.code.appendSlice(self.allocator, std.mem.asBytes(&imm)); + } + + /// Emit 64-bit immediate (little-endian) + fn emitImm64(self: *Self, imm: i64) !void { + try self.code.appendSlice(self.allocator, std.mem.asBytes(&imm)); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // X86-64 INSTRUCTION ENCODING + // ═══════════════════════════════════════════════════════════════════════════ + + /// push rbp + fn pushRbp(self: *Self) !void { + try self.emit1(0x55); + } + + /// pop rbp + fn popRbp(self: *Self) !void { + try self.emit1(0x5D); + } + + /// mov rbp, rsp + fn movRbpRsp(self: *Self) !void { + try self.emit(&[_]u8{ 0x48, 0x89, 0xE5 }); + } + + /// mov rsp, rbp + fn movRspRbp(self: *Self) !void { + try self.emit(&[_]u8{ 0x48, 0x89, 0xEC }); + } + + /// ret + fn ret(self: *Self) !void { + try self.emit1(0xC3); + } + + /// xor eax, eax (zero rax) + fn xorEaxEax(self: *Self) !void { + try self.emit(&[_]u8{ 0x31, 0xC0 }); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // VSA OPERATION COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile dot product (returns i64 in rax) + /// x86-64 System V ABI: rdi = first arg, rsi = second arg, rax = return + pub fn compileDotProduct(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue + try self.pushRbp(); + try self.movRbpRsp(); + + // Save callee-saved registers + try self.emit(&[_]u8{0x53}); // push rbx + try self.emit(&[_]u8{ 0x41, 0x54 }); // push r12 + try self.emit(&[_]u8{ 0x41, 0x55 }); // push r13 + try self.emit(&[_]u8{ 0x41, 0x56 }); // push r14 + + // r12 = a pointer (from rdi) + try self.emit(&[_]u8{ 0x49, 0x89, 0xFC }); // mov r12, rdi + + // r13 = b pointer (from rsi) + try self.emit(&[_]u8{ 0x49, 0x89, 0xF5 }); // mov r13, rsi + + // r14 = accumulator (0) + try self.emit(&[_]u8{ 0x4D, 0x31, 0xF6 }); // xor r14, r14 + + // rbx = loop counter (0) + try self.xorEaxEax(); + try self.emit(&[_]u8{ 0x48, 0x89, 0xC3 }); // mov rbx, rax + + const loop_start = self.code.items.len; + + // Compare rbx with dimension + try self.emit(&[_]u8{ 0x48, 0x81, 0xFB }); // cmp rbx, imm32 + try self.emitImm32(@intCast(dimension)); + + // jge loop_end + try self.emit(&[_]u8{ 0x0F, 0x8D }); // jge rel32 + const jge_offset = self.code.items.len; + try self.emitImm32(0); // placeholder + + // Load a[rbx] into eax (sign-extended) + try self.emit(&[_]u8{ 0x41, 0x0F, 0xBE, 0x04, 0x1C }); // movsx eax, byte [r12 + rbx] + + // Load b[rbx] into ecx (sign-extended) + try self.emit(&[_]u8{ 0x41, 0x0F, 0xBE, 0x4C, 0x1D, 0x00 }); // movsx ecx, byte [r13 + rbx] + + // imul eax, ecx + try self.emit(&[_]u8{ 0x0F, 0xAF, 0xC1 }); // imul eax, ecx + + // Sign-extend eax to rax + try self.emit(&[_]u8{ 0x48, 0x98 }); // cdqe + + // Add to accumulator: r14 += rax + try self.emit(&[_]u8{ 0x49, 0x01, 0xC6 }); // add r14, rax + + // Increment counter + try self.emit(&[_]u8{ 0x48, 0xFF, 0xC3 }); // inc rbx + + // Jump back to loop start + try self.emit(&[_]u8{0xE9}); // jmp rel32 + const loop_back_offset: i32 = @intCast(@as(i64, @intCast(loop_start)) - @as(i64, @intCast(self.code.items.len + 4))); + try self.emitImm32(loop_back_offset); + + // Patch jge offset + const loop_end = self.code.items.len; + const jge_rel: i32 = @intCast(@as(i64, @intCast(loop_end)) - @as(i64, @intCast(jge_offset + 4))); + @memcpy(self.code.items[jge_offset..][0..4], std.mem.asBytes(&jge_rel)); + + // Move result to rax + try self.emit(&[_]u8{ 0x4C, 0x89, 0xF0 }); // mov rax, r14 + + // Restore callee-saved registers + try self.emit(&[_]u8{ 0x41, 0x5E }); // pop r14 + try self.emit(&[_]u8{ 0x41, 0x5D }); // pop r13 + try self.emit(&[_]u8{ 0x41, 0x5C }); // pop r12 + try self.emit(&[_]u8{0x5B}); // pop rbx + + // Function epilogue + try self.movRspRbp(); + try self.popRbp(); + try self.ret(); + } + + /// Compile bind operation (element-wise multiply for ternary) + pub fn compileBindDirect(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue + try self.pushRbp(); + try self.movRbpRsp(); + + // Save callee-saved registers + try self.emit(&[_]u8{0x53}); // push rbx + try self.emit(&[_]u8{ 0x41, 0x54 }); // push r12 + try self.emit(&[_]u8{ 0x41, 0x55 }); // push r13 + + // r12 = a pointer + try self.emit(&[_]u8{ 0x49, 0x89, 0xFC }); // mov r12, rdi + + // r13 = b pointer + try self.emit(&[_]u8{ 0x49, 0x89, 0xF5 }); // mov r13, rsi + + // rbx = loop counter (0) + try self.xorEaxEax(); + try self.emit(&[_]u8{ 0x48, 0x89, 0xC3 }); // mov rbx, rax + + const loop_start = self.code.items.len; + + // Compare rbx with dimension + try self.emit(&[_]u8{ 0x48, 0x81, 0xFB }); // cmp rbx, imm32 + try self.emitImm32(@intCast(dimension)); + + // jge loop_end + try self.emit(&[_]u8{ 0x0F, 0x8D }); // jge rel32 + const jge_offset = self.code.items.len; + try self.emitImm32(0); // placeholder + + // Load a[rbx] into al + try self.emit(&[_]u8{ 0x41, 0x8A, 0x04, 0x1C }); // mov al, [r12 + rbx] + + // Load b[rbx] into cl + try self.emit(&[_]u8{ 0x41, 0x8A, 0x4C, 0x1D, 0x00 }); // mov cl, [r13 + rbx] + + // imul al, cl (signed multiply) + try self.emit(&[_]u8{ 0xF6, 0xE9 }); // imul cl + + // Store result back to a[rbx] + try self.emit(&[_]u8{ 0x41, 0x88, 0x04, 0x1C }); // mov [r12 + rbx], al + + // Increment counter + try self.emit(&[_]u8{ 0x48, 0xFF, 0xC3 }); // inc rbx + + // Jump back to loop start + try self.emit(&[_]u8{0xE9}); // jmp rel32 + const loop_back_offset: i32 = @intCast(@as(i64, @intCast(loop_start)) - @as(i64, @intCast(self.code.items.len + 4))); + try self.emitImm32(loop_back_offset); + + // Patch jge offset + const loop_end = self.code.items.len; + const jge_rel: i32 = @intCast(@as(i64, @intCast(loop_end)) - @as(i64, @intCast(jge_offset + 4))); + @memcpy(self.code.items[jge_offset..][0..4], std.mem.asBytes(&jge_rel)); + + // Restore callee-saved registers + try self.emit(&[_]u8{ 0x41, 0x5D }); // pop r13 + try self.emit(&[_]u8{ 0x41, 0x5C }); // pop r12 + try self.emit(&[_]u8{0x5B}); // pop rbx + + // Function epilogue + try self.movRspRbp(); + try self.popRbp(); + try self.ret(); + } + + /// Compile bundle operation (element-wise sum with threshold) + pub fn compileBundleDirect(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue + try self.pushRbp(); + try self.movRbpRsp(); + + // Save callee-saved registers + try self.emit(&[_]u8{0x53}); // push rbx + try self.emit(&[_]u8{ 0x41, 0x54 }); // push r12 + try self.emit(&[_]u8{ 0x41, 0x55 }); // push r13 + + // r12 = a pointer, r13 = b pointer + try self.emit(&[_]u8{ 0x49, 0x89, 0xFC }); // mov r12, rdi + try self.emit(&[_]u8{ 0x49, 0x89, 0xF5 }); // mov r13, rsi + + // rbx = loop counter (0) + try self.xorEaxEax(); + try self.emit(&[_]u8{ 0x48, 0x89, 0xC3 }); // mov rbx, rax + + const loop_start = self.code.items.len; + + // Compare rbx with dimension + try self.emit(&[_]u8{ 0x48, 0x81, 0xFB }); // cmp rbx, imm32 + try self.emitImm32(@intCast(dimension)); + + // jge loop_end + try self.emit(&[_]u8{ 0x0F, 0x8D }); // jge rel32 + const jge_offset = self.code.items.len; + try self.emitImm32(0); // placeholder + + // Load a[rbx] into eax (sign-extended) + try self.emit(&[_]u8{ 0x41, 0x0F, 0xBE, 0x04, 0x1C }); // movsx eax, byte [r12 + rbx] + + // Load b[rbx] into ecx (sign-extended) + try self.emit(&[_]u8{ 0x41, 0x0F, 0xBE, 0x4C, 0x1D, 0x00 }); // movsx ecx, byte [r13 + rbx] + + // Add eax, ecx + try self.emit(&[_]u8{ 0x01, 0xC8 }); // add eax, ecx + + // Threshold: if sum > 0 -> 1, if sum < 0 -> -1, else 0 + // cmp eax, 0 + try self.emit(&[_]u8{ 0x83, 0xF8, 0x00 }); // cmp eax, 0 + + // setg dl (set dl = 1 if eax > 0) + try self.emit(&[_]u8{ 0x0F, 0x9F, 0xC2 }); // setg dl + + // setl al (set al = 1 if eax < 0) + try self.emit(&[_]u8{ 0x0F, 0x9C, 0xC0 }); // setl al + + // Result = dl - al (1 if positive, -1 if negative, 0 if zero) + try self.emit(&[_]u8{ 0x28, 0xC2 }); // sub dl, al + + // Store result back to a[rbx] + try self.emit(&[_]u8{ 0x41, 0x88, 0x14, 0x1C }); // mov [r12 + rbx], dl + + // Increment counter + try self.emit(&[_]u8{ 0x48, 0xFF, 0xC3 }); // inc rbx + + // Jump back to loop start + try self.emit(&[_]u8{0xE9}); // jmp rel32 + const loop_back_offset: i32 = @intCast(@as(i64, @intCast(loop_start)) - @as(i64, @intCast(self.code.items.len + 4))); + try self.emitImm32(loop_back_offset); + + // Patch jge offset + const loop_end = self.code.items.len; + const jge_rel: i32 = @intCast(@as(i64, @intCast(loop_end)) - @as(i64, @intCast(jge_offset + 4))); + @memcpy(self.code.items[jge_offset..][0..4], std.mem.asBytes(&jge_rel)); + + // Restore callee-saved registers + try self.emit(&[_]u8{ 0x41, 0x5D }); // pop r13 + try self.emit(&[_]u8{ 0x41, 0x5C }); // pop r12 + try self.emit(&[_]u8{0x5B}); // pop rbx + + // Function epilogue + try self.movRspRbp(); + try self.popRbp(); + try self.ret(); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // EXECUTION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Make code executable and return function pointer + pub fn finalize(self: *Self) !*const fn (*anyopaque, *anyopaque) callconv(.c) i64 { + const code_size = self.code.items.len; + if (code_size == 0) return error.EmptyCode; + + // Use system page size for compatibility + const page_size: usize = std.heap.page_size_min; + const alloc_size = std.mem.alignForward(usize, code_size, page_size); + + // mmap with PROT_READ | PROT_WRITE first + const mem = try std.posix.mmap( + null, + alloc_size, + std.posix.PROT.READ | std.posix.PROT.WRITE, + .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, + -1, + 0, + ); + + // Copy code + @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); + + self.exec_mem = mem; + + return @ptrCast(mem.ptr); + } + + /// Get code size + pub fn codeSize(self: *const Self) usize { + return self.code.items.len; + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "x86-64 JIT compiler init and deinit" { + var compiler = X86_64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + try std.testing.expect(compiler.codeSize() == 0); +} + +test "x86-64 JIT dot product compilation" { + if (!is_x86_64) { + return; // Skip on non-x86-64 + } + + var compiler = X86_64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 8; + try compiler.compileDotProduct(dim); + + try std.testing.expect(compiler.codeSize() > 0); +} + +test "x86-64 JIT dot product execution" { + if (!is_x86_64) { + return; // Skip on non-x86-64 + } + + var compiler = X86_64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 8; + try compiler.compileDotProduct(dim); + + const func = try compiler.finalize(); + + // Create test data + var a = [dim]i8{ 1, -1, 1, 0, 1, -1, 0, 1 }; + var b = [dim]i8{ 1, 1, -1, 1, 1, 1, 1, -1 }; + + // Expected: 1*1 + (-1)*1 + 1*(-1) + 0*1 + 1*1 + (-1)*1 + 0*1 + 1*(-1) + // = 1 - 1 - 1 + 0 + 1 - 1 + 0 - 1 = -2 + const expected: i64 = -2; + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "x86-64 JIT bind compilation" { + if (!is_x86_64) { + return; + } + + var compiler = X86_64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 8; + try compiler.compileBindDirect(dim); + + try std.testing.expect(compiler.codeSize() > 0); +} + +test "x86-64 JIT large dimension" { + if (!is_x86_64) { + return; + } + + var compiler = X86_64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 1000; + try compiler.compileDotProduct(dim); + + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + + for (0..dim) |i| { + const val_a: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); + const val_b: i8 = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + a[i] = val_a; + b[i] = val_b; + expected += @as(i64, val_a) * @as(i64, val_b); + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} diff --git a/src/query_cli.zig b/src/query_cli.zig index 1c7208b9ee..e20ae7b65d 100644 --- a/src/query_cli.zig +++ b/src/query_cli.zig @@ -76,9 +76,8 @@ fn bipolarRandom(dim: usize, seed: u64) HybridBigInt { const random = rng.random(); for (0..result.trit_len) |i| { - if (result.unpacked_cache) |cache| { - cache[i] = if (random.boolean()) @as(i8, 1) else @as(i8, -1); - } + // unpacked_cache в zig-hdc — не optional + result.unpacked_cache[i] = if (random.boolean()) @as(i8, 1) else @as(i8, -1); } return result; } @@ -96,7 +95,7 @@ fn hvSimilarity(a: *HybridBigInt, b: *HybridBigInt) f64 { } fn hvBundle2(a: *HybridBigInt, b: *HybridBigInt) HybridBigInt { - return vsa.bundle2(a, b, std.heap.page_allocator); + return vsa.bundle2(a, b); } fn treeBundleN(items: []HybridBigInt) HybridBigInt { diff --git a/src/sota_report_demo.zig b/src/sota_report_demo.zig index 6d8d1ccd4b..e4bc1e2f1f 100644 --- a/src/sota_report_demo.zig +++ b/src/sota_report_demo.zig @@ -71,7 +71,7 @@ fn verifyBundle3Similarity() SotaMetric { var a = trinity.randomVector(DIM, 100); var b = trinity.randomVector(DIM, 200); var c = trinity.randomVector(DIM, 300); - var bundled = trinity.bundle3(&a, &b, &c, std.heap.page_allocator); + var bundled = trinity.bundle3(&a, &b, &c); const sim_a = trinity.cosineSimilarity(&bundled, &a); const sim_b = trinity.cosineSimilarity(&bundled, &b); const sim_c = trinity.cosineSimilarity(&bundled, &c); @@ -94,7 +94,7 @@ fn verifyBundleNScaling() SotaMetric { vectors[i] = trinity.randomVector(DIM, @as(u32, @intCast(5000 + i))); ptrs[i] = &vectors[i]; } - var bundled = trinity.bundleN(&ptrs, std.heap.page_allocator) catch unreachable; + var bundled = trinity.bundleN(&ptrs); const sim = trinity.cosineSimilarity(&bundled, ptrs[0]); return .{ .name = "BundleN (5 vec)", @@ -179,7 +179,7 @@ fn verifyAssociativeMemory() SotaMetric { var red_apple = trinity.bind(&apple, &red); var yellow_banana = trinity.bind(&banana, &yellow); - var memory = trinity.bundle2(&red_apple, &yellow_banana, std.heap.page_allocator); + var memory = trinity.bundle2(&red_apple, &yellow_banana); var query = trinity.unbind(&memory, &red); const sim_apple = trinity.cosineSimilarity(&query, &apple); diff --git a/src/vm.zig b/src/vm.zig index e356381bd2..00a5f68dcc 100644 --- a/src/vm.zig +++ b/src/vm.zig @@ -249,7 +249,7 @@ pub const VSAVM = struct { fn execVStore(self: *VSAVM, inst: VSAInstruction) void { // Store vector to scalar const src = self.getVReg(inst.src1); - self.registers.s0 = src.toI64(std.heap.page_allocator); + self.registers.s0 = src.toI64(); } fn execVConst(self: *VSAVM, inst: VSAInstruction) void { @@ -311,7 +311,7 @@ pub const VSAVM = struct { const dst = self.getVReg(inst.dst); var src1 = self.getVReg(inst.src1).*; var src2 = self.getVReg(inst.src2).*; - dst.* = tvc_vsa.bundle2(&src1, &src2, std.heap.page_allocator); + dst.* = tvc_vsa.bundle2(&src1, &src2); } fn execVBundle3(self: *VSAVM, inst: VSAInstruction) void { @@ -319,7 +319,7 @@ pub const VSAVM = struct { var src1 = self.getVReg(inst.src1).*; var src2 = self.getVReg(inst.src2).*; var src3 = self.getVReg(inst.dst).*; // Use dst as third source - dst.* = tvc_vsa.bundle3(&src1, &src2, &src3, std.heap.page_allocator); + dst.* = tvc_vsa.bundle3(&src1, &src2, &src3); } fn execVDot(self: *VSAVM, inst: VSAInstruction) void { @@ -339,7 +339,7 @@ pub const VSAVM = struct { } // Scalar fallback - self.registers.s0 = src1.dotProduct(&src2, std.heap.page_allocator); + self.registers.s0 = src1.dotProduct(&src2); } fn execVCosine(self: *VSAVM, inst: VSAInstruction) void { @@ -386,20 +386,20 @@ pub const VSAVM = struct { const dst = self.getVReg(inst.dst); var src1 = self.getVReg(inst.src1).*; var src2 = self.getVReg(inst.src2).*; - dst.* = src1.add(&src2, std.heap.page_allocator); + dst.* = src1.add(&src2); } fn execVNeg(self: *VSAVM, inst: VSAInstruction) void { const dst = self.getVReg(inst.dst); const src = self.getVReg(inst.src1); - dst.* = src.negate(std.heap.page_allocator); + dst.* = src.negate(); } fn execVMul(self: *VSAVM, inst: VSAInstruction) void { const dst = self.getVReg(inst.dst); var src1 = self.getVReg(inst.src1).*; var src2 = self.getVReg(inst.src2).*; - dst.* = src1.mul(&src2, std.heap.page_allocator); + dst.* = src1.mul(&src2); } fn execVMov(self: *VSAVM, inst: VSAInstruction) void { @@ -451,7 +451,7 @@ pub const VSAVM = struct { var src2 = self.getVReg(inst.src2).*; var permuted = tvc_vsa.permute(&src2, 1); - dst.* = src1.add(&permuted, std.heap.page_allocator); + dst.* = src1.add(&permuted); } // ═══════════════════════════════════════════════════════════════════════════ @@ -490,7 +490,7 @@ pub const VSAVM = struct { dst.ensureUnpacked(); dst.trit_len = 16; inline for (0..16) |i| { - dst.unpacked_cache.?[i] = ternary_vec[i]; + dst.unpacked_cache[i] = ternary_vec[i]; } } @@ -503,7 +503,7 @@ pub const VSAVM = struct { // Convert first 16 trits to f16 var f16_vec: @Vector(16, f16) = undefined; inline for (0..16) |i| { - const trit: i8 = if (i < src.trit_len) src.unpacked_cache.?[i] else 0; + const trit: i8 = if (i < src.trit_len) src.unpacked_cache[i] else 0; f16_vec[i] = @floatCast(@as(f32, @floatFromInt(trit))); } @@ -528,8 +528,8 @@ pub const VSAVM = struct { var a_f16: @Vector(16, f16) = undefined; var b_f16: @Vector(16, f16) = undefined; inline for (0..16) |i| { - const a_trit: i8 = if (i < a.trit_len) a.unpacked_cache.?[i] else 0; - const b_trit: i8 = if (i < b.trit_len) b.unpacked_cache.?[i] else 0; + const a_trit: i8 = if (i < a.trit_len) a.unpacked_cache[i] else 0; + const b_trit: i8 = if (i < b.trit_len) b.unpacked_cache[i] else 0; a_f16[i] = @floatCast(@as(f32, @floatFromInt(a_trit))); b_f16[i] = @floatCast(@as(f32, @floatFromInt(b_trit))); } diff --git a/src/vsa.zig b/src/vsa.zig new file mode 100644 index 0000000000..345d0a4ca1 --- /dev/null +++ b/src/vsa.zig @@ -0,0 +1,50 @@ +//! Совместимостный слой: src/vsa.zig был перенесён в gHashTag/zig-hdc +//! коммитом 42490a222, но десятки файлов этого репозитория продолжали +//! импортировать его по относительному пути. Файл не дублирует код — +//! он только реэкспортирует модуль зависимости `zig-hdc-vsa`. +//! Zig 0.15 не поддерживает `usingnamespace`, поэтому реэкспорт явный. + +const hdc = @import("zig-hdc-vsa"); + +pub const common = hdc.common; +pub const core = hdc.core; +pub const encoding = hdc.encoding; +pub const storage = hdc.storage; +pub const concurrency = hdc.concurrency; +pub const HRR = hdc.HRR; +pub const HybridBigInt = hdc.HybridBigInt; +pub const Trit = hdc.Trit; +pub const Vec32i8 = hdc.Vec32i8; +pub const SIMD_WIDTH = hdc.SIMD_WIDTH; +pub const MAX_TRITS = hdc.MAX_TRITS; +pub const SearchResult = hdc.SearchResult; +pub const randomVector = hdc.randomVector; +pub const bind = hdc.bind; +pub const unbind = hdc.unbind; +pub const bundle2 = hdc.bundle2; +pub const bundle3 = hdc.bundle3; +pub const permute = hdc.permute; +pub const inversePermute = hdc.inversePermute; +pub const cosineSimilarity = hdc.cosineSimilarity; +pub const hammingDistance = hdc.hammingDistance; +pub const hammingSimilarity = hdc.hammingSimilarity; +pub const dotSimilarity = hdc.dotSimilarity; +pub const vectorNorm = hdc.vectorNorm; +pub const bundleN = hdc.bundleN; +pub const countNonZero = hdc.countNonZero; +pub const encodeSequence = hdc.encodeSequence; +pub const probeSequence = hdc.probeSequence; +pub const encodeText = hdc.encodeText; +pub const decodeText = hdc.decodeText; +pub const TEXT_VECTOR_DIM = hdc.TEXT_VECTOR_DIM; +pub const charToVector = hdc.charToVector; +pub const encodeTextWords = hdc.encodeTextWords; +pub const textSimilarity = hdc.textSimilarity; +pub const textsAreSimilar = hdc.textsAreSimilar; +pub const TextCorpus = hdc.TextCorpus; +pub const ChaseLevDeque = hdc.ChaseLevDeque; +pub const DependencyGraph = hdc.DependencyGraph; +pub const TaskNode = hdc.TaskNode; +pub const TaskState = hdc.TaskState; +pub const getGlobalPool = hdc.getGlobalPool; +pub const hammingDistanceSlice = hdc.hammingDistanceSlice; diff --git a/src/vsa_jit.zig b/src/vsa_jit.zig new file mode 100644 index 0000000000..d2fbcfb9a1 --- /dev/null +++ b/src/vsa_jit.zig @@ -0,0 +1,688 @@ +// @origin(spec:vsa_jit.tri) @regen(manual-impl) +// @origin(manual) @regen(pending) +// Trinity JIT-Accelerated VSA Operations +// Provides 15-260x speedup for hot paths via native code generation +// +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q +// φ² + 1/φ² = 3 + +const std = @import("std"); +const builtin = @import("builtin"); +const jit_unified = @import("jit_unified.zig"); +const hybrid = @import("hybrid.zig"); + +pub const HybridBigInt = hybrid.HybridBigInt; +pub const Trit = hybrid.Trit; +pub const MAX_TRITS = hybrid.MAX_TRITS; + +// ═══════════════════════════════════════════════════════════════════════════════ +// JIT-ACCELERATED VSA ENGINE +// ═══════════════════════════════════════════════════════════════════════════════ + +/// JIT-accelerated VSA engine with compiled function caching +pub const JitVSAEngine = struct { + allocator: std.mem.Allocator, + + // Cached JIT-compiled functions for common dimensions + dot_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + bind_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + hamming_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + cosine_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + bundle_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + + // Keep compilers alive to prevent exec_mem from being freed + compilers: std.ArrayListUnmanaged(jit_unified.UnifiedJitCompiler), + + // Statistics + jit_hits: u64 = 0, + jit_misses: u64 = 0, + total_ops: u64 = 0, + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator) Self { + return Self{ + .allocator = allocator, + .dot_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .bind_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .hamming_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .cosine_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .bundle_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .compilers = .{}, + }; + } + + pub fn deinit(self: *Self) void { + // Clean up all compilers (which frees exec_mem) + for (self.compilers.items) |*compiler| { + compiler.deinit(); + } + self.compilers.deinit(self.allocator); + self.dot_cache.deinit(); + self.bind_cache.deinit(); + self.hamming_cache.deinit(); + self.cosine_cache.deinit(); + self.bundle_cache.deinit(); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT DOT PRODUCT + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for dot product + fn getDotFunction(self: *Self, dimension: usize) !jit_unified.JitDotFn { + if (self.dot_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Compile new function + self.jit_misses += 1; + + // Create compiler and add to list (keeps exec_mem alive) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + try compiler.compileDotProduct(dimension); + const func = try compiler.finalize(); + + try self.dot_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated dot product for HybridBigInt vectors + pub fn dotProduct(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !i64 { + self.total_ops += 1; + + // Ensure vectors are unpacked for direct memory access + a.ensureUnpacked(); + b.ensureUnpacked(); + + // Use the larger dimension + const dim = @max(a.trit_len, b.trit_len); + + // Get or compile JIT function + const func = try self.getDotFunction(dim); + + // Call JIT-compiled function directly on unpacked cache + // Cast [MAX_TRITS]Trit to *anyopaque + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + + return func(a_ptr, b_ptr); + } + + /// Fallback to non-JIT dot product (for comparison) + pub fn dotProductFallback(a: *HybridBigInt, b: *HybridBigInt) i64 { + return @intCast(a.dotProduct(b, std.heap.page_allocator)); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT BIND + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for bind + fn getBindFunction(self: *Self, dimension: usize) !jit_unified.JitDotFn { + if (self.bind_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Compile new function + self.jit_misses += 1; + + // Create compiler and add to list (keeps exec_mem alive) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + try compiler.compileBind(dimension); + const func = try compiler.finalize(); + + try self.bind_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated bind for HybridBigInt vectors (modifies a in place) + pub fn bind(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !void { + self.total_ops += 1; + + // Ensure vectors are unpacked for direct memory access + a.ensureUnpacked(); + b.ensureUnpacked(); + + // Use the larger dimension + const dim = @max(a.trit_len, b.trit_len); + + // Get or compile JIT function + const func = try self.getBindFunction(dim); + + // Call JIT-compiled function (modifies a in place) + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + + _ = func(a_ptr, b_ptr); + + // Mark as modified (dirty) since JIT wrote to unpacked cache + a.dirty = true; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT FUSED COSINE SIMILARITY (single-pass computation) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for fused cosine similarity + fn getCosineFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { + if (self.cosine_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Try to compile fused cosine (only available on ARM64) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + compiler.compileFusedCosine(dimension) catch |err| { + // Remove the failed compiler + _ = self.compilers.pop(); + if (err == error.UnsupportedOperation) { + return null; // Fall back to 3x dot product + } + return err; + }; + + self.jit_misses += 1; + const func = try compiler.finalize(); + try self.cosine_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated cosine similarity using fused kernel (2.5x faster on ARM64) + /// cos(a,b) = dot(a,b) / sqrt(dot(a,a) * dot(b,b)) + pub fn cosineSimilarity(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !f64 { + self.total_ops += 1; + + // Ensure vectors are unpacked + a.ensureUnpacked(); + b.ensureUnpacked(); + + const dim = @max(a.trit_len, b.trit_len); + + // Try fused cosine kernel (ARM64 only, 2.5x faster) + if (try self.getCosineFunction(dim)) |func| { + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + + // Function returns f64 bit pattern as i64 + const result_bits = func(a_ptr, b_ptr); + return @bitCast(result_bits); + } + + // Fallback: use 3 separate JIT dot products + const dot_ab = try self.dotProduct(a, b); + const dot_aa = try self.dotProduct(a, a); + const dot_bb = try self.dotProduct(b, b); + + // Handle zero vectors + if (dot_aa == 0 or dot_bb == 0) { + return 0.0; + } + + const norm = @sqrt(@as(f64, @floatFromInt(dot_aa)) * @as(f64, @floatFromInt(dot_bb))); + return @as(f64, @floatFromInt(dot_ab)) / norm; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT HAMMING DISTANCE (count of differing positions) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for hamming distance + fn getHammingFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { + if (self.hamming_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Try to compile new function (only available on ARM64) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + compiler.compileHamming(dimension) catch |err| { + // Remove the failed compiler + _ = self.compilers.pop(); + if (err == error.UnsupportedOperation) { + return null; // Fall back to scalar + } + return err; + }; + + self.jit_misses += 1; + const func = try compiler.finalize(); + try self.hamming_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated hamming distance + /// For ternary: counts positions where a[i] != b[i] + pub fn hammingDistance(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !i64 { + self.total_ops += 1; + + // Ensure vectors are unpacked + a.ensureUnpacked(); + b.ensureUnpacked(); + + const dim = @max(a.trit_len, b.trit_len); + + // Try JIT SIMD version (available on ARM64) + if (try self.getHammingFunction(dim)) |func| { + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + return func(a_ptr, b_ptr); + } + + // Scalar fallback + var count: i64 = 0; + for (0..dim) |i| { + if (a.unpacked_cache[i] != b.unpacked_cache[i]) { + count += 1; + } + } + return count; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT BUNDLE OPERATION (n-ary addition with threshold) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for bundle operation + fn getBundleFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { + if (self.bundle_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Try to compile bundle SIMD (only available on ARM64) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + compiler.compileBundleSIMD(dimension) catch |err| { + // Remove the failed compiler + _ = self.compilers.pop(); + if (err == error.UnsupportedOperation) { + return null; // Fall back to scalar + } + return err; + }; + + self.jit_misses += 1; + const func = try compiler.finalize(); + try self.bundle_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated bundle operation + /// result[i] = threshold(a[i] + b[i]) where >0→1, <0→-1, =0→0 + /// Modifies 'a' in place + pub fn bundle(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !void { + self.total_ops += 1; + + // Ensure vectors are unpacked + a.ensureUnpacked(); + b.ensureUnpacked(); + + const dim = @max(a.trit_len, b.trit_len); + + // Try JIT SIMD version (ARM64 only) + if (try self.getBundleFunction(dim)) |func| { + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + _ = func(a_ptr, b_ptr); + a.dirty = true; + return; + } + + // Scalar fallback + for (0..dim) |i| { + const sum: i16 = @as(i16, a.unpacked_cache[i]) + @as(i16, b.unpacked_cache[i]); + if (sum > 0) { + a.unpacked_cache[i] = 1; + } else if (sum < 0) { + a.unpacked_cache[i] = -1; + } else { + a.unpacked_cache[i] = 0; + } + } + a.dirty = true; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // STATISTICS + // ═══════════════════════════════════════════════════════════════════════════ + + pub fn getStats(self: *const Self) Stats { + const total_cache = self.jit_hits + self.jit_misses; + const hit_rate = if (total_cache > 0) + @as(f64, @floatFromInt(self.jit_hits)) / @as(f64, @floatFromInt(total_cache)) * 100.0 + else + 0.0; + + return Stats{ + .total_ops = self.total_ops, + .jit_hits = self.jit_hits, + .jit_misses = self.jit_misses, + .cache_size = self.dot_cache.count() + self.bind_cache.count() + self.hamming_cache.count() + self.cosine_cache.count() + self.bundle_cache.count(), + .hit_rate = hit_rate, + }; + } + + pub fn printStats(self: *const Self) void { + const stats = self.getStats(); + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" JIT VSA ENGINE STATISTICS\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Total operations: {d}\n", .{stats.total_ops}); + std.debug.print(" JIT cache hits: {d}\n", .{stats.jit_hits}); + std.debug.print(" JIT cache misses: {d}\n", .{stats.jit_misses}); + std.debug.print(" Cache size: {d} functions\n", .{stats.cache_size}); + std.debug.print(" Hit rate: {d:.1}%\n", .{stats.hit_rate}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + } + + pub const Stats = struct { + total_ops: u64, + jit_hits: u64, + jit_misses: u64, + cache_size: usize, + hit_rate: f64, + }; +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// CONVENIENCE FUNCTIONS +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Global JIT engine (thread-local for safety) +threadlocal var global_engine: ?JitVSAEngine = null; + +/// Initialize global JIT engine +pub fn initGlobal(allocator: std.mem.Allocator) void { + if (global_engine == null) { + global_engine = JitVSAEngine.init(allocator); + } +} + +/// Deinitialize global JIT engine +pub fn deinitGlobal() void { + if (global_engine) |*engine| { + engine.deinit(); + global_engine = null; + } +} + +/// JIT-accelerated dot product using global engine +pub fn jitDotProduct(allocator: std.mem.Allocator, a: *HybridBigInt, b: *HybridBigInt) !i64 { + initGlobal(allocator); + return global_engine.?.dotProduct(a, b); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "JitVSAEngine init and deinit" { + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + try std.testing.expect(engine.total_ops == 0); +} + +test "JitVSAEngine dot product correctness" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + // Create test vectors using setTrit (proper API) + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + // Simple test: all 1s dot all 1s = dimension + const test_len = 64; + + for (0..test_len) |i| { + a.setTrit(i, 1); + b.setTrit(i, 1); + } + + const expected: i64 = test_len; + + // JIT dot product + const jit_result = try engine.dotProduct(&a, &b); + + // Fallback dot product + const fallback_result = JitVSAEngine.dotProductFallback(&a, &b); + + try std.testing.expectEqual(expected, jit_result); + try std.testing.expectEqual(expected, fallback_result); +} + +test "JitVSAEngine cache hits" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + a.trit_len = 64; + b.trit_len = 64; + + // First call - cache miss + _ = try engine.dotProduct(&a, &b); + try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); + try std.testing.expectEqual(@as(u64, 0), engine.jit_hits); + + // Second call - cache hit + _ = try engine.dotProduct(&a, &b); + try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); + try std.testing.expectEqual(@as(u64, 1), engine.jit_hits); + + // Third call - cache hit + _ = try engine.dotProduct(&a, &b); + try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); + try std.testing.expectEqual(@as(u64, 2), engine.jit_hits); +} + +test "JitVSAEngine benchmark vs fallback" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + const dim = 1024; + const iterations = 10000; + + // Create test vectors using setTrit + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + for (0..dim) |i| { + const val_a: Trit = @intCast(@as(i32, @intCast(i % 3)) - 1); + const val_b: Trit = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + a.setTrit(i, val_a); + b.setTrit(i, val_b); + } + + // Warm up JIT cache + _ = try engine.dotProduct(&a, &b); + + // Benchmark JIT + var timer = try std.time.Timer.start(); + var jit_result: i64 = 0; + for (0..iterations) |_| { + jit_result = try engine.dotProduct(&a, &b); + } + const jit_ns = timer.read(); + + // Benchmark fallback + timer.reset(); + var fallback_result: i64 = 0; + for (0..iterations) |_| { + fallback_result = JitVSAEngine.dotProductFallback(&a, &b); + } + const fallback_ns = timer.read(); + + // Results should match + try std.testing.expectEqual(jit_result, fallback_result); + + const jit_ms = @as(f64, @floatFromInt(jit_ns)) / 1_000_000.0; + const fallback_ms = @as(f64, @floatFromInt(fallback_ns)) / 1_000_000.0; + const speedup = fallback_ms / jit_ms; + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" JIT VSA ENGINE BENCHMARK (HybridBigInt)\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Dimension: {d} trits\n", .{dim}); + std.debug.print(" Iterations: {d}\n", .{iterations}); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" Fallback (HybridBigInt.dotProduct): {d:.3} ms\n", .{fallback_ms}); + std.debug.print(" JIT (NEON SIMD): {d:.3} ms\n", .{jit_ms}); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + + engine.printStats(); + + // JIT should generally be faster, but can be slower due to thermal/load + // Just verify JIT compiles and runs without crashing + if (speedup > 1.0) { + std.debug.print(" JIT is faster! ({d:.2}x speedup)\n", .{speedup}); + } else { + std.debug.print(" JIT is slower ({d:.2}x) - acceptable for flaky benchmark\n", .{speedup}); + } +} + +test "JitVSAEngine various dimensions" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + const test_dims = [_]usize{ 8, 16, 32, 64, 100, 128, 256, 512, 1000 }; + + for (test_dims) |dim| { + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + var expected: i64 = 0; + for (0..dim) |i| { + a.setTrit(i, 1); + b.setTrit(i, 1); + expected += 1; + } + + const result = try engine.dotProduct(&a, &b); + try std.testing.expectEqual(expected, result); + } + + // Should have compiled functions for each unique dimension + try std.testing.expectEqual(@as(usize, test_dims.len), engine.dot_cache.count()); +} + +test "JitVSAEngine bind correctness" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + // Test bind: result[i] = a[i] * b[i] (ternary multiplication) + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + const dim = 16; + for (0..dim) |i| { + // Pattern: a = [1, -1, 0, 1, -1, 0, ...], b = [1, 1, 1, -1, -1, -1, ...] + const a_val: Trit = @intCast(@as(i32, @intCast(i % 3)) - 1); + const b_val: Trit = if (i < dim / 2) @as(Trit, 1) else @as(Trit, -1); + a.setTrit(i, a_val); + b.setTrit(i, b_val); + } + + // Compute expected result + var expected = HybridBigInt.zero(); + for (0..dim) |i| { + const a_val = a.getTrit(i); + const b_val = b.getTrit(i); + expected.setTrit(i, a_val * b_val); + } + + // JIT bind + try engine.bind(&a, &b); + + // Verify result + for (0..dim) |i| { + try std.testing.expectEqual(expected.getTrit(i), a.getTrit(i)); + } +} + +test "JitVSAEngine cosine similarity correctness" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + // Test identical vectors: cos(a, a) = 1.0 + var a = HybridBigInt.zero(); + const dim = 64; + for (0..dim) |i| { + a.setTrit(i, 1); + } + + const cos_identical = try engine.cosineSimilarity(&a, &a); + try std.testing.expectApproxEqRel(@as(f64, 1.0), cos_identical, 0.001); + + // Test orthogonal vectors: cos(a, -a) = -1.0 + var neg_a = HybridBigInt.zero(); + for (0..dim) |i| { + neg_a.setTrit(i, -1); + } + + const cos_opposite = try engine.cosineSimilarity(&a, &neg_a); + try std.testing.expectApproxEqRel(@as(f64, -1.0), cos_opposite, 0.001); +} + +test "JitVSAEngine hamming distance correctness" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + // Test identical vectors: hamming(a, a) = 0 + var a = HybridBigInt.zero(); + const dim = 64; + for (0..dim) |i| { + a.setTrit(i, 1); + } + + const hamming_identical = try engine.hammingDistance(&a, &a); + try std.testing.expectEqual(@as(i64, 0), hamming_identical); + + // Test completely different vectors: hamming(a, -a) = dim + var neg_a = HybridBigInt.zero(); + for (0..dim) |i| { + neg_a.setTrit(i, -1); + } + + const hamming_opposite = try engine.hammingDistance(&a, &neg_a); + try std.testing.expectEqual(@as(i64, dim), hamming_opposite); + + // Test half different: change half the trits + var half = HybridBigInt.zero(); + for (0..dim) |i| { + half.setTrit(i, if (i < dim / 2) @as(Trit, 1) else @as(Trit, -1)); + } + + const hamming_half = try engine.hammingDistance(&a, &half); + try std.testing.expectEqual(@as(i64, dim / 2), hamming_half); +}