@fraczak/k 7.0.0 → 7.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/DOCS/BIG_PICTURE.md +5 -5
  3. package/DOCS/FILE_FORMATS.md +5 -5
  4. package/DOCS/OBJECT_FILE_AND_PATTERN.md +9 -9
  5. package/DOCS/PATTERNS.md +3 -3
  6. package/DOCS/PROFILING_AND_TRACING.md +60 -72
  7. package/DOCS/REPL.md +3 -3
  8. package/DOCS/book/03-types-as-finite-automata.md +4 -4
  9. package/DOCS/book/06-values-in-memory.md +9 -9
  10. package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
  11. package/DOCS/book/09-compiler-architecture.md +2 -2
  12. package/DOCS/book/10-from-ast-to-intermediate-representation.md +2 -2
  13. package/DOCS/book/14-canonical-serialization.md +1 -1
  14. package/Examples/arithmetics.k +2 -2
  15. package/Examples/bnat.k +1 -1
  16. package/Examples/karatsuba-mult.k +2 -2
  17. package/README.md +3 -8
  18. package/backend-api.mjs +9 -1
  19. package/backends/llvm/runtime/krt.c +33 -0
  20. package/backends/llvm/runtime/krt.h +1 -0
  21. package/backends/llvm/scripts/run-tests.mjs +5 -0
  22. package/backends/llvm/src/executable.mjs +4 -4
  23. package/backends/llvm/src/kvm2llvm.mjs +42 -19
  24. package/backends/llvm/src/llvm.mjs +30 -36
  25. package/backends/llvm/tests/integration.sh +2 -2
  26. package/backends/llvm/tests/perf-support.mjs +1 -55
  27. package/backends/llvm/tests/test-llvm.mjs +3 -3
  28. package/backends/llvm/tests/test-open-product-misalignment.mjs +140 -0
  29. package/backends/wasm/demo_ieee.mjs +2 -3
  30. package/backends/wasm/runtime.wat +157 -5
  31. package/backends/wasm/src/kvm2wasm.mjs +57 -34
  32. package/backends/wasm/src/wasm.mjs +40 -18
  33. package/backends/wasm/tests/perf-support.mjs +4 -4
  34. package/backends/wasm/tests/test-wasm.mjs +13 -14
  35. package/browser/vfs-data.mjs +6 -6
  36. package/core.k +8 -8
  37. package/harness/format.mjs +2 -2
  38. package/harness/registry.mjs +0 -2
  39. package/kir.mjs +299 -17
  40. package/package.json +1 -5
  41. package/scripts/benchmark-idempotency.mjs +12 -8
  42. package/scripts/benchmark.mjs +5 -9
  43. package/scripts/build-repl-html.mjs +2 -2
  44. package/scripts/perf-ieee.mjs +17 -118
  45. package/scripts/perf-int.mjs +313 -318
  46. package/scripts/perf-multiplication.mjs +10 -20
  47. package/scripts/perf-poly.mjs +29 -183
  48. package/tests/code-derivation/pattern-envelope.mjs +4 -4
  49. package/tests/test-kvm-polymorphic.mjs +2 -2
  50. package/tests/test-repl-html.mjs +15 -0
  51. package/tests/test-repl.mjs +10 -0
  52. package/backends/arm64/README.md +0 -103
  53. package/backends/arm64/bin/k-arm64-compile.mjs +0 -93
  54. package/backends/arm64/bin/k-arm64-run.mjs +0 -58
  55. package/backends/arm64/bin/k-arm64.mjs +0 -88
  56. package/backends/arm64/scripts/run-tests.mjs +0 -57
  57. package/backends/arm64/src/arm64.mjs +0 -229
  58. package/backends/arm64/src/cli.mjs +0 -155
  59. package/backends/arm64/src/kvm2arm64.mjs +0 -906
  60. package/backends/arm64/src/runtime.c +0 -744
  61. package/backends/arm64/tests/integration.sh +0 -112
  62. package/backends/arm64/tests/test-arm64.mjs +0 -240
  63. package/harness/adapters/arm64.mjs +0 -105
@@ -9,7 +9,6 @@
9
9
  * - WebAssembly (persistent runner via Wasm)
10
10
  * - kVM Interpreter (envelope-free bytecode engine)
11
11
  * - Native JS (envelope-free convergence engine)
12
- * - ARM64 (native binary, on supported Linux aarch64 hosts)
13
12
  *
14
13
  * Modes:
15
14
  * --mode squaring : Successive squarings starting from --input
@@ -67,7 +66,7 @@ function printHelp() {
67
66
 
68
67
  Multiplication Algorithm & Multi-Backend Performance Harness.
69
68
  Compares multiplication algorithms ('times' vs 'karatsuba') across
70
- available execution backends (LLVM, Wasm, kVM, JS, ARM64).
69
+ available execution backends (LLVM, Wasm, kVM, JS).
71
70
 
72
71
  Options:
73
72
  --mode <squaring|sizes>
@@ -76,7 +75,7 @@ Options:
76
75
  (default: squaring)
77
76
  --backends <csv>
78
77
  Comma-separated list of execution backends to test:
79
- llvm, wasm, kvm, js, arm64. (default: llvm,wasm,kvm,js)
78
+ llvm, wasm, kvm, js. (default: llvm,wasm,kvm,js)
80
79
  --algos <csv>
81
80
  Comma-separated list of multiplication algorithms to test:
82
81
  times, karatsuba. (default: times,karatsuba)
@@ -106,11 +105,7 @@ Environment Variables:
106
105
  LLVM_PIPELINE
107
106
  Set to '1' to use persistent pipelined LLVM runner mode.
108
107
  LLVM_SPAWN_PER_CALL
109
- Set to '1' to spawn a new LLVM process on each invocation.
110
- ARM64_PIPELINE
111
- Set to '1' to use persistent pipelined ARM64 runner mode.
112
- ARM64_SPAWN_PER_CALL
113
- Set to '1' to spawn a new ARM64 process on each invocation.`);
108
+ Set to '1' to spawn a new LLVM process on each invocation.`);
114
109
  }
115
110
 
116
111
  if (hasFlag("-h") || hasFlag("--help")) {
@@ -277,7 +272,10 @@ for (const algo of selectedAlgos) {
277
272
  const exePath = path.join(cacheDir, `perf_mult_llvm_${algo}`);
278
273
  try {
279
274
  compileObjectToExecutable(
280
- algoRelations[algo].object, exePath, { clangOpt: "-O2" }
275
+ algoRelations[algo].object, exePath, {
276
+ clangOpt: "-O2",
277
+ inputPattern: parseIntValue("0").pattern
278
+ }
281
279
  );
282
280
  algoServers.llvm[algo] = new PersistentExecutable(exePath);
283
281
  } catch (e) {
@@ -315,11 +313,7 @@ for (const algo of selectedAlgos) {
315
313
 
316
314
  // 3. Squaring Mode Benchmark
317
315
  if (mode === "squaring") {
318
- const sInitVal = valueForCode(
319
- parseIntValue(squaringInputText),
320
- commonState.typeAliases.int,
321
- codes.find
322
- );
316
+ const sInitVal = parseIntValue(squaringInputText);
323
317
  const { inputWire: sInitWire } = wireInput(sInitVal);
324
318
 
325
319
  const results = {};
@@ -584,12 +578,8 @@ if (mode === "sizes") {
584
578
  const strA = randomBigIntBits(bits);
585
579
  const strB = randomBigIntBits(bits);
586
580
 
587
- const valA = valueForCode(
588
- parseIntValue(strA), commonState.typeAliases.int, codes.find
589
- );
590
- const valB = valueForCode(
591
- parseIntValue(strB), commonState.typeAliases.int, codes.find
592
- );
581
+ const valA = parseIntValue(strA);
582
+ const valB = parseIntValue(strB);
593
583
  const pairInput = { tag: "product", value: { x: valA, y: valB } };
594
584
 
595
585
  for (const backend of selectedBackends) {
@@ -24,8 +24,6 @@ import {
24
24
  csvEnv,
25
25
  formatTiming,
26
26
  createLLVMRunner,
27
- createARM64Runner,
28
- arm64LaneName,
29
27
  llvmLaneName,
30
28
  makeCacheDir,
31
29
  parseNonNegativeIntEnv,
@@ -56,7 +54,6 @@ import {
56
54
  writeValueToArena
57
55
  } from "../backends/wasm/tests/perf-support.mjs";
58
56
 
59
- import { compileARM64ArtifactFromObject } from "../backends/arm64/src/arm64.mjs";
60
57
  import { compileWasmArtifactFromObject } from "../backends/wasm/src/wasm.mjs";
61
58
  import { inputPatternForObjectRelation } from "../backends/llvm/src/executable.mjs";
62
59
 
@@ -65,7 +62,7 @@ function printHelp() {
65
62
 
66
63
  Polymorphic List Operations Benchmark Harness.
67
64
  Benchmarks reverse, concat, split_by, get_nth, and length on
68
- Examples/poly.k across Native JS, kVM interpreter, LLVM, Wasm, and ARM64.
65
+ Examples/poly.k across Native JS, kVM interpreter, LLVM, and Wasm.
69
66
 
70
67
  Options:
71
68
  --trace
@@ -79,13 +76,11 @@ Options:
79
76
 
80
77
  Environment Variables:
81
78
  BACKENDS
82
- Target backend to run: 'llvm', 'wasm', or 'arm64'.
79
+ Target backend to run: 'llvm' or 'wasm'.
83
80
  LLVM_ONLY
84
81
  Run only LLVM backend ('1' to enable).
85
82
  WASM_ONLY
86
83
  Run only WebAssembly backend ('1' to enable).
87
- ARM64_ONLY
88
- Run only ARM64 backend ('1' to enable).
89
84
  BACKENDS_ONLY
90
85
  Skip JS and kVM interpreter baselines ('1' to enable).
91
86
  LIST_LENGTH
@@ -96,14 +91,6 @@ Environment Variables:
96
91
  Enable nanosecond execution tracing ('1' to enable).
97
92
  PROFILE, K_PROFILE
98
93
  Enable per-function call count profiling ('1' to enable).
99
- ARM64_WARMUP_ITERATIONS
100
- Warmup iterations for ARM64 backend (default: 1).
101
- ARM64_OPT
102
- ARM64 backend optimization flags (default: -O2).
103
- ARM64_SPAWN_PER_CALL
104
- Spawn process per ARM64 call instead of persistent ('1' or '0').
105
- ARM64_PIPELINE
106
- Use persistent pipelined runner for ARM64 ('1' or '0').
107
94
  LLVM_WARMUP_ITERATIONS
108
95
  Warmup iterations for LLVM backend (default: 1).
109
96
  LLVM_SPAWN_PER_CALL
@@ -131,13 +118,9 @@ if (process.argv.includes("-h") || process.argv.includes("--help")) {
131
118
  const backendsEnv = process.env.BACKENDS?.toLowerCase();
132
119
  const llvmOnly = process.env.LLVM_ONLY === "1" || backendsEnv === "llvm";
133
120
  const wasmOnly = process.env.WASM_ONLY === "1" || backendsEnv === "wasm";
134
- const arm64Only = process.env.ARM64_ONLY === "1" || backendsEnv === "arm64";
135
-
136
- const isArm64Host = process.arch === "arm64" && process.platform === "linux";
137
- const runARM64 = !wasmOnly && !llvmOnly && (isArm64Host || arm64Only);
138
- const runLLVM = !wasmOnly && !arm64Only;
139
- const runWasm = !llvmOnly && !arm64Only;
140
- const runBaselines = !process.env.BACKENDS_ONLY && (process.env.LLVM_ONLY !== "1") && (process.env.WASM_ONLY !== "1") && (process.env.ARM64_ONLY !== "1");
121
+ const runLLVM = !wasmOnly;
122
+ const runWasm = !llvmOnly;
123
+ const runBaselines = !process.env.BACKENDS_ONLY && (process.env.LLVM_ONLY !== "1") && (process.env.WASM_ONLY !== "1");
141
124
 
142
125
  const ops = ["reverse", "concat", "split_by", "get_nth", "length"];
143
126
  const benchNames = Object.fromEntries(ops.map(op => [op, `bench_${op}`]));
@@ -146,10 +129,6 @@ const iterations = parsePositiveIntEnv("ITERATIONS", 3);
146
129
  const runTrace = process.argv.includes("--trace") || process.env.TRACE === "1" || process.env.K_TRACE === "1";
147
130
  const runProfile = runTrace || process.argv.includes("--profile") || process.env.K_PROFILE === "1" || process.env.PROFILE === "1";
148
131
 
149
- // ARM64 options
150
- const arm64WarmupIterations = parseNonNegativeIntEnv("ARM64_WARMUP_ITERATIONS", 1);
151
- const arm64OptLevel = process.env.ARM64_OPT || "-O2";
152
-
153
132
  // LLVM options
154
133
  const llvmWarmupIterations = parseNonNegativeIntEnv("LLVM_WARMUP_ITERATIONS", 1);
155
134
  const cacheDir = makeCacheDir("k-poly-perf-");
@@ -193,7 +172,7 @@ function buildExportPreamble(exports, libraries) {
193
172
  return lines.join("\n") + "\n";
194
173
  }
195
174
 
196
- const neededExports = ["0", "int", "inc", "dec", "nat", "zero_int?"];
175
+ const neededExports = ["0", "int", "inc", "dec", "nat", "zero_int?", "neg?", "GT"];
197
176
  const exportPreamble = buildExportPreamble(neededExports, [arithmeticsLib]);
198
177
  const polyLib = loadLibrary(compileLibrary(exportPreamble + polySource, {
199
178
  source: polyPath,
@@ -219,7 +198,7 @@ for (const op of ops) {
219
198
  }
220
199
 
221
200
  function intValue(text) {
222
- return valueForCode(parseIntValue(text), state.typeAliases.int, codes.find);
201
+ return parseIntValue(text);
223
202
  }
224
203
 
225
204
  function makeList(elements) {
@@ -392,41 +371,6 @@ function createWasmRunner(testSuite) {
392
371
  };
393
372
  }
394
373
 
395
- // Setup ARM64
396
- const arm64Exes = new Map();
397
- if (runARM64) {
398
- console.log(`==> Compiling Linux ARM64 executables (${arm64OptLevel})...`);
399
- for (const tc of testSuite) {
400
- const relation = relations[tc.op];
401
- try {
402
- const exePath = path.join(cacheDir, `perf_poly_arm64_${tc.op}`);
403
- compileARM64ArtifactFromObject(relation.object, {
404
- entry: relation.relationName,
405
- inputPattern: tc.inputPattern,
406
- outputPattern: tc.outputPattern,
407
- outputPath: exePath,
408
- optLevel: arm64OptLevel,
409
- profile: runProfile
410
- });
411
- tc.arm64 = { status: "ok", exePath };
412
- arm64Exes.set(tc.op, tc.arm64);
413
- } catch (error) {
414
- tc.arm64 = {
415
- status: "failed",
416
- error: error.stack || error.message || String(error)
417
- };
418
- arm64Exes.set(tc.op, tc.arm64);
419
- }
420
- }
421
- const failedOps = [...arm64Exes.entries()].filter(([_, v]) => v.status !== "ok");
422
- if (failedOps.length > 0) {
423
- console.log("Linux ARM64 compilation failures:");
424
- for (const [op, res] of failedOps) {
425
- console.log(` ${op}: ${res.error.split("\n")[0]}`);
426
- }
427
- }
428
- }
429
-
430
374
  function printBenchmarkDescription() {
431
375
  const lanes = [];
432
376
  if (runBaselines) {
@@ -436,7 +380,6 @@ function printBenchmarkDescription() {
436
380
  }
437
381
  if (runLLVM) lanes.push(llvmLaneName());
438
382
  if (runWasm) lanes.push(wasmLaneName());
439
- if (runARM64) lanes.push(arm64LaneName(arm64OptLevel));
440
383
 
441
384
  console.log("==> Benchmark description");
442
385
  console.log(" source: @fraczak/k/Examples/poly.k");
@@ -453,12 +396,6 @@ function printBenchmarkDescription() {
453
396
  console.log(` wasm warmup iterations: ${wasmWarmupIterations}`);
454
397
  if (!wasmPipe) console.log(` wasm arena reset: ${wasmReset ? "yes" : "no"}`);
455
398
  }
456
- if (runARM64) {
457
- console.log(` arm64-ready cases: ${testSuite.filter(tc => tc.arm64?.status === "ok").length}`);
458
- console.log(` arm64 warmup iterations: ${arm64WarmupIterations}`);
459
- console.log(` arm64 opt level: ${arm64OptLevel}`);
460
- console.log(` arm64 runner mode: ${process.env.ARM64_SPAWN_PER_CALL === "1" ? "spawn per call" : "persistent"}`);
461
- }
462
399
  console.log(` iterations: ${iterations}`);
463
400
  console.log(` benchmark lanes: ${lanes.join("; ")}`);
464
401
  console.log(" conformance: all outputs are compared to native expected values");
@@ -539,26 +476,6 @@ if (runWasm) {
539
476
  }
540
477
  }
541
478
 
542
- // 4. Linux ARM64 lane
543
- let arm64Result = null;
544
- if (runARM64) {
545
- const arm64Cases = testSuite.filter(tc => tc.arm64?.status === "ok");
546
- if (arm64Cases.length === testSuite.length) {
547
- const arm64Runner = createARM64Runner(testSuite);
548
- try {
549
- if (arm64WarmupIterations > 0) {
550
- console.log(`==> Warming Linux ARM64 (${arm64WarmupIterations} iterations)...`);
551
- await arm64Runner.run(arm64WarmupIterations);
552
- }
553
-
554
- console.log(`==> Running Linux ARM64 (${iterations} iterations)...`);
555
- arm64Result = await arm64Runner.run(iterations);
556
- } finally {
557
- arm64Runner.close();
558
- }
559
- }
560
- }
561
-
562
479
  console.log("\n=================== POLYMORPHIC BENCHMARK RESULTS ===================");
563
480
  console.log(`Polymorphic operations per iteration: ${testSuite.length}`);
564
481
  console.log(`Total requested operation calls: ${iterations * testSuite.length}`);
@@ -575,10 +492,6 @@ if (runLLVM) {
575
492
  if (runWasm) {
576
493
  console.log(`${laneIndex++}. ${wasmLaneName().padEnd(29)} ${formatTiming(wasmResult)}`);
577
494
  }
578
- if (runARM64) {
579
- const timingStr = arm64Result ? formatTiming(arm64Result) : "compile failed";
580
- console.log(`${laneIndex++}. ${arm64LaneName(arm64OptLevel).padEnd(29)} ${timingStr}`);
581
- }
582
495
  console.log("=====================================================================\n");
583
496
 
584
497
  // Conformance Validation
@@ -644,22 +557,6 @@ for (const tc of testSuite) {
644
557
  }
645
558
  }
646
559
 
647
- if (runARM64) {
648
- if (tc.arm64?.status !== "ok") {
649
- tc.arm64Conformance = "compile-failed";
650
- } else {
651
- try {
652
- const outputWire = await runExecutable(tc.arm64.exePath, tc.inputWire);
653
- const actual = decodeWire(outputWire).value;
654
- assert.ok(safeDeepEqual(actual, tc.expected), `ARM64 output mismatch for ${tc.op}`);
655
- tc.arm64Conformance = "ok";
656
- } catch (error) {
657
- tc.arm64Conformance = "failed";
658
- tc.arm64.error = error.stack || error.message || String(error);
659
- console.log(`Linux ARM64 conformance failure for ${tc.op}:`, tc.arm64.error.split("\n")[0]);
660
- }
661
- }
662
- }
663
560
  }
664
561
 
665
562
  const validationSummary = [];
@@ -672,15 +569,10 @@ if (runWasm) {
672
569
  const wasmOk = testSuite.filter(tc => tc.wasmConformance === "ok").length;
673
570
  validationSummary.push(`Wasm ${wasmOk}/${testSuite.length}`);
674
571
  }
675
- if (runARM64) {
676
- const arm64Ok = testSuite.filter(tc => tc.arm64Conformance === "ok").length;
677
- validationSummary.push(`Linux ARM64 ${arm64Ok}/${testSuite.length}`);
678
- }
679
572
 
680
573
  console.log(`Conformance validation: ${validationSummary.join(", ")} cases match expected values.`);
681
574
  if (runLLVM && shouldStrictFail(testSuite)) process.exitCode = 1;
682
575
  if (runWasm && testSuite.some(tc => tc.wasmConformance !== "ok")) process.exitCode = 1;
683
- if (runARM64 && testSuite.some(tc => tc.arm64Conformance !== "ok")) process.exitCode = 1;
684
576
 
685
577
  if (runTrace && process.exitCode !== 1) {
686
578
  console.log("\n=================== RUNNING MACRO-PHASE TRACING ===================");
@@ -705,15 +597,6 @@ if (runTrace && process.exitCode !== 1) {
705
597
  }
706
598
  }
707
599
 
708
- const arm64Servers = new Map();
709
- if (runARM64) {
710
- for (const tc of testSuite) {
711
- if (tc.arm64?.status === "ok") {
712
- arm64Servers.set(tc.op, new PersistentExecutable(tc.arm64.exePath));
713
- }
714
- }
715
- }
716
-
717
600
  const averageTrace = (samples) => {
718
601
  if (!samples || samples.length === 0) return null;
719
602
  const avg = {};
@@ -746,33 +629,6 @@ if (runTrace && process.exitCode !== 1) {
746
629
  const op = tc.op;
747
630
  const opData = { op, label: tc.label };
748
631
 
749
- // 1. ARM64
750
- if (arm64Servers.has(op)) {
751
- const server = arm64Servers.get(op);
752
- for (let i = 0; i < 3; i++) await server.requestWithTrace(tc.inputWire);
753
- const samples = [];
754
- let arm64Profile = null;
755
- for (let i = 0; i < sampleReps; i++) {
756
- const res = await server.requestWithTrace(tc.inputWire);
757
- if (res.trace) {
758
- samples.push(res.trace);
759
- if (res.trace.profile) arm64Profile = res.trace.profile;
760
- }
761
- }
762
- opData.arm64 = averageTrace(samples);
763
- if (arm64Profile) {
764
- if (arm64Profile.__main__ !== undefined) {
765
- if (arm64Profile[op] !== undefined) {
766
- arm64Profile[benchNames[op]] = arm64Profile.__main__;
767
- } else {
768
- arm64Profile[op] = arm64Profile.__main__;
769
- }
770
- delete arm64Profile.__main__;
771
- }
772
- opData.arm64Profile = arm64Profile;
773
- }
774
- }
775
-
776
632
  // 2. LLVM
777
633
  if (llvmServers.has(op)) {
778
634
  const server = llvmServers.get(op);
@@ -891,7 +747,6 @@ if (runTrace && process.exitCode !== 1) {
891
747
  }
892
748
 
893
749
  for (const s of llvmServers.values()) s.close();
894
- for (const s of arm64Servers.values()) s.close();
895
750
  for (const s of wasmServers.values()) s.close();
896
751
 
897
752
  function formatUs(ns) {
@@ -913,24 +768,22 @@ if (runTrace && process.exitCode !== 1) {
913
768
 
914
769
  for (const item of phaseResults) {
915
770
  console.log(`\n--- Operation: ${item.op} (${item.label}) ---`);
916
- console.log("Phase | Linux ARM64 | LLVM | WebAssembly | kVM Interp");
917
- console.log("-------------------------+---------------+---------------+---------------+---------------");
771
+ console.log("Phase | LLVM | WebAssembly | kVM Interp");
772
+ console.log("-------------------------+---------------+---------------+---------------");
918
773
  for (const phase of phases) {
919
774
  if (phase.isTotal) {
920
- console.log("-------------------------+---------------+---------------+---------------+---------------");
775
+ console.log("-------------------------+---------------+---------------+---------------");
921
776
  }
922
777
  const pName = phase.name.padEnd(24);
923
- const arm64Col = formatUs(item.arm64?.[phase.key]);
924
778
  const llvmCol = formatUs(item.llvm?.[phase.key]);
925
779
  const wasmCol = formatUs(item.wasm?.[phase.key]);
926
780
  const kvmCol = formatUs(item.kvm?.[phase.key]);
927
- console.log(`${pName} | ${arm64Col} | ${llvmCol} | ${wasmCol} | ${kvmCol}`);
781
+ console.log(`${pName} | ${llvmCol} | ${wasmCol} | ${kvmCol}`);
928
782
  }
929
783
 
930
- const hasProfile = item.arm64Profile || item.wasmProfile || item.kvmProfile;
784
+ const hasProfile = item.wasmProfile || item.kvmProfile;
931
785
  if (hasProfile) {
932
786
  const allFuncs = new Set([
933
- ...Object.keys(item.arm64Profile || {}),
934
787
  ...Object.keys(item.wasmProfile || {}),
935
788
  ...Object.keys(item.kvmProfile || {})
936
789
  ]);
@@ -945,37 +798,33 @@ if (runTrace && process.exitCode !== 1) {
945
798
  });
946
799
 
947
800
  console.log(`\n Function Call Profile: ${item.op}`);
948
- console.log(" Function Name | Linux ARM64 Calls | WebAssembly Calls | kVM Interp Calls");
949
- console.log(" -------------------------+-------------------+-------------------+------------------");
950
- let arm64Total = 0, wasmTotal = 0, kvmTotal = 0;
801
+ console.log(" Function Name | WebAssembly Calls | kVM Interp Calls");
802
+ console.log(" -------------------------+-------------------+------------------");
803
+ let wasmTotal = 0, kvmTotal = 0;
951
804
  for (const fn of sortedFuncs) {
952
- const aCount = item.arm64Profile?.[fn] ?? 0;
953
805
  const wCount = item.wasmProfile?.[fn] ?? 0;
954
806
  const kCount = item.kvmProfile?.[fn] ?? 0;
955
- arm64Total += aCount;
956
807
  wasmTotal += wCount;
957
808
  kvmTotal += kCount;
958
809
  const fnCol = fn.padEnd(24);
959
- const aStr = (aCount > 0 ? String(aCount) : "-").padStart(17);
960
810
  const wStr = (wCount > 0 ? String(wCount) : "-").padStart(17);
961
811
  const kStr = (kCount > 0 ? String(kCount) : "-").padStart(16);
962
- console.log(` ${fnCol} | ${aStr} | ${wStr} | ${kStr}`);
812
+ console.log(` ${fnCol} | ${wStr} | ${kStr}`);
963
813
  }
964
- console.log(" -------------------------+-------------------+-------------------+------------------");
814
+ console.log(" -------------------------+-------------------+------------------");
965
815
  const totCol = "Total Calls".padEnd(24);
966
- const aTotStr = String(arm64Total).padStart(17);
967
816
  const wTotStr = String(wasmTotal).padStart(17);
968
817
  const kTotStr = String(kvmTotal).padStart(16);
969
- console.log(` ${totCol} | ${aTotStr} | ${wTotStr} | ${kTotStr}`);
818
+ console.log(` ${totCol} | ${wTotStr} | ${kTotStr}`);
970
819
  }
971
820
  }
972
821
 
973
822
  console.log(`\n=== AVERAGE ACROSS ALL ${phaseResults.length} OPERATIONS ===`);
974
- console.log("Phase | Linux ARM64 | LLVM | WebAssembly | kVM Interp");
975
- console.log("-------------------------+---------------+---------------+---------------+---------------");
823
+ console.log("Phase | LLVM | WebAssembly | kVM Interp");
824
+ console.log("-------------------------+---------------+---------------+---------------");
976
825
  for (const phase of phases) {
977
826
  if (phase.isTotal) {
978
- console.log("-------------------------+---------------+---------------+---------------+---------------");
827
+ console.log("-------------------------+---------------+---------------+---------------");
979
828
  }
980
829
  const pName = phase.name.padEnd(24);
981
830
  const avg = (backend) => {
@@ -983,30 +832,27 @@ if (runTrace && process.exitCode !== 1) {
983
832
  if (vals.length === 0) return null;
984
833
  return vals.reduce((a, b) => a + b, 0) / vals.length;
985
834
  };
986
- const arm64Col = formatUs(avg("arm64"));
987
835
  const llvmCol = formatUs(avg("llvm"));
988
836
  const wasmCol = formatUs(avg("wasm"));
989
837
  const kvmCol = formatUs(avg("kvm"));
990
- console.log(`${pName} | ${arm64Col} | ${llvmCol} | ${wasmCol} | ${kvmCol}`);
838
+ console.log(`${pName} | ${llvmCol} | ${wasmCol} | ${kvmCol}`);
991
839
  }
992
840
 
993
- if (phaseResults.some(r => r.arm64Profile || r.wasmProfile || r.kvmProfile)) {
841
+ if (phaseResults.some(r => r.wasmProfile || r.kvmProfile)) {
994
842
  console.log("\n=== FUNCTION CALL COUNT SUMMARY ACROSS ALL OPERATIONS ===");
995
- console.log("Operation (Case) | Linux ARM64 Calls | WebAssembly Calls | kVM Interp Calls");
996
- console.log("-------------------------+-------------------+-------------------+------------------");
997
- let arm64Grand = 0, wasmGrand = 0, kvmGrand = 0;
843
+ console.log("Operation (Case) | WebAssembly Calls | kVM Interp Calls");
844
+ console.log("-------------------------+-------------------+------------------");
845
+ let wasmGrand = 0, kvmGrand = 0;
998
846
  for (const r of phaseResults) {
999
- const aSum = Object.values(r.arm64Profile || {}).reduce((a, b) => a + b, 0);
1000
847
  const wSum = Object.values(r.wasmProfile || {}).reduce((a, b) => a + b, 0);
1001
848
  const kSum = Object.values(r.kvmProfile || {}).reduce((a, b) => a + b, 0);
1002
- arm64Grand += aSum;
1003
849
  wasmGrand += wSum;
1004
850
  kvmGrand += kSum;
1005
851
  const opCol = `${r.op} (${r.label})`.slice(0, 24).padEnd(24);
1006
- console.log(`${opCol} | ${String(aSum).padStart(17)} | ${String(wSum).padStart(17)} | ${String(kSum).padStart(16)}`);
852
+ console.log(`${opCol} | ${String(wSum).padStart(17)} | ${String(kSum).padStart(16)}`);
1007
853
  }
1008
- console.log("-------------------------+-------------------+-------------------+------------------");
1009
- console.log(`${"Total Calls".padEnd(24)} | ${String(arm64Grand).padStart(17)} | ${String(wasmGrand).padStart(17)} | ${String(kvmGrand).padStart(16)}`);
854
+ console.log("-------------------------+-------------------+------------------");
855
+ console.log(`${"Total Calls".padEnd(24)} | ${String(wasmGrand).padStart(17)} | ${String(kvmGrand).padStart(16)}`);
1010
856
  }
1011
857
  console.log("=====================================================================\n");
1012
858
  }
@@ -12,8 +12,8 @@ const INT_PATTERN = [
12
12
  ];
13
13
 
14
14
  const script = `
15
- int = ?< <bits 0, bits 1, {} _>=bits "+", bits "-" >;
16
- {}|_|0|1|+ int
15
+ int? = ?< <bits 0, bits 1, {} _>=bits "+", bits "-" >;
16
+ {}|_|0|1|+ int?
17
17
  `;
18
18
 
19
19
  const result = k.compile(script)(Value.product({}));
@@ -75,8 +75,8 @@ assert.throws(
75
75
  );
76
76
 
77
77
  const projectionScript = `
78
- int = ?< <bits 0, bits 1, {} _>=bits "+", bits "-" >;
79
- int /+
78
+ int? = ?< <bits 0, bits 1, {} _>=bits "+", bits "-" >;
79
+ int? /+
80
80
  `;
81
81
  const annotated = k.annotate(projectionScript);
82
82
  run.defs = annotated;
@@ -91,8 +91,8 @@ console.log("==> Starting Polymorphic kVM & Specialization Tests");
91
91
  // Test 3: Type Error on Envelope Mismatch
92
92
  {
93
93
  const script = `
94
- check = ?< {} true, {} false >;
95
- check
94
+ check? = ?< {} true, {} false >;
95
+ check?
96
96
  `;
97
97
  const obj = decodeObject(compileObjectBuffer(script));
98
98
  const kvmArtifact = objectToKVMArtifact(obj);
@@ -714,6 +714,21 @@ if (chromiumBin) {
714
714
  "Explicit :codec load of uploaded file must succeed"
715
715
  );
716
716
 
717
+ // Test Wasm engine with polymorphic/open list len
718
+ const wasmLenCheck = await evaluateAsync(`(async () => {
719
+ await window.kRepl.executeCommand(":engine wasm");
720
+ await window.kRepl.executeCommand(":codec load int.mjs");
721
+ await window.kRepl.executeCommand("len = {()list,0 int len} len_; len_ = ?X <{.list/nil if, .len then} .then, {.list/cons.cdr list, .len inc len} ?X len_ >;");
722
+ await window.kRepl.executeCommand(":input int.mjs [1,2,3]");
723
+ await window.kRepl.executeCommand("len");
724
+ const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
725
+ return lines[lines.length - 1];
726
+ })()`);
727
+ assert(
728
+ wasmLenCheck.includes("int.mjs: 3"),
729
+ `Expected 'int.mjs: 3' from wasm execution, got: ${wasmLenCheck}`
730
+ );
731
+
717
732
  ws.close();
718
733
  console.log(" Browser execution verified successfully!");
719
734
  } finally {
@@ -509,6 +509,16 @@ output = await evaluateInput("{ 10 int x, 5 int y } plus", jsState);
509
509
  assert.match(output[0], /^\{\}\|_\|1\|1\|1\|1\|\+/);
510
510
  assert.match(output[1], /\(js\)/);
511
511
 
512
+ // Verify open product projection in wasm engine
513
+ const wasmListState = createState({ engine: "wasm" });
514
+ await evaluateInput(":load Examples/arithmetics.k", wasmListState);
515
+ await evaluateInput(":codec load codecs/int.mjs", wasmListState);
516
+ await evaluateInput("len = {()list,0 int len} len_; len_ = ?X <{.list/nil if, .len then} .then, {.list/cons.cdr list, .len inc len} ?X len_ >;", wasmListState);
517
+ await evaluateInput(":input int.mjs [3,3,3]", wasmListState);
518
+ output = await evaluateInput("len", wasmListState);
519
+ assert.match(output[0], /int\.mjs:\s*3/);
520
+ assert.match(output[1], /\(wasm\)/);
521
+
512
522
  fs.rmSync(tmpDir, { recursive: true, force: true });
513
523
  fs.rmSync(symlinkPath, { force: true });
514
524
  console.log("OK");
@@ -1,103 +0,0 @@
1
- # k Linux ARM64 Backend
2
-
3
- `@fraczak/k-arm64` is the native Linux AArch64 (ARM64) backend for
4
- [`@fraczak/k`](https://github.com/fraczak/k). It lowers typed k programs directly from
5
- the kVM representation into GNU AArch64 assembly text (`.s`), compiles native ELF
6
- executables, and provides CLI tools and runners for binary `pattern + value` streams
7
- and `--json` input/output.
8
-
9
- ## Features
10
-
11
- - **Direct kVM to ARM64 Lowering**: Generates pure GNU AArch64 assembly text without requiring LLVM or external code generators.
12
- - **Calling Convention & Registers (AAPCS64)**:
13
- - `x0`: Input value pointer on entry; Return status (`0` for success, non-zero for failure) on exit.
14
- - `x1`: Output value pointer on exit.
15
- - `x19`: Callee-saved Arena bump pointer preserved and updated across relation calls.
16
- - Standard 16-byte stack frame alignment with FP (`x29`) and LR (`x30`) chaining.
17
- - **Flat Arena Memory Representation**:
18
- - Word 0 (bytes 0–1): Total block size in bytes (16-byte aligned).
19
- - Word 1 (bytes 2–3): Value kind (`1` = Product, `2` = Variant).
20
- - Word 2 (bytes 4–7): Product field count OR Variant 32-bit tag ID.
21
- - Payloads (bytes 8+): 64-bit pointers to child values in the arena.
22
- - **Self Tail-Call Optimization (TCO)**:
23
- - Recursion where the tail call passes new product fields directly updates cached field slots and jumps back to `.Ltail_loop` with zero frame overhead, enabling deep recursion without stack overflow.
24
- - **Union Backtracking & Memory Rollback**:
25
- - Union branches save the arena bump pointer (`x19`) before evaluation. If a branch fails, `x19` is rewound to cleanly roll back all allocations made in the failed speculative branch before attempting subsequent branches.
26
- - **Standalone Linux Binaries**:
27
- - Links with a lightweight C driver (`backends/arm64/src/runtime.c`) and `backends/llvm/runtime/krt.c` to produce independent native ELF executables that execute directly on Linux without Node.js.
28
- - Supports `--json` for direct text output, stdin/stdout piping, and binary envelope streams.
29
-
30
- ## Quick Start
31
-
32
- Compile an expression to a standalone native Linux ARM64 executable:
33
-
34
- ```bash
35
- node ./backends/arm64/bin/k-arm64-compile.mjs -o /tmp/ok '|ok'
36
- ```
37
-
38
- Execute it over unit input and decode output:
39
-
40
- ```bash
41
- node ./codecs/unit.mjs --parse | /tmp/ok --json
42
- ```
43
-
44
- Output:
45
- ```json
46
- "ok"
47
- ```
48
-
49
- Compile with library dependencies (`--lib` and `--export`):
50
-
51
- ```bash
52
- node ./backends/arm64/bin/k-arm64-compile.mjs \
53
- --lib Examples/arithmetics.k \
54
- --export plus:+ --export succ --export int \
55
- '{succ int x,int y}+' \
56
- -o /tmp/add-one
57
-
58
- node ./codecs/k-parse.mjs <<< '{"x":{"succ":"zero"},"y":{"succ":{"succ":"zero"}}}' |
59
- /tmp/add-one --json
60
- ```
61
-
62
- Output:
63
- ```json
64
- {"+":{"1":"_"}}
65
- ```
66
-
67
- ## CLI Commands
68
-
69
- | Command | Purpose |
70
- | --- | --- |
71
- | `k-arm64` | Compile k source, `.ko`, or `.kvm` in memory and run immediately |
72
- | `k-arm64-compile` | Compile k source, `.ko`, or `.kvm` to standalone ELF executable or assembly (`-S`) |
73
- | `k-arm64-run` | Run a compiled ELF executable over binary wire streams |
74
-
75
- ### Options
76
-
77
- All tools accept `--help`.
78
-
79
- - `-S, --assembly`: Emit GNU ARM64 assembly text (`.s`) instead of an ELF binary.
80
- - `-o, --output <file>`: Specify the output file path.
81
- - `--lib <file>`: Load one `.klib` or `.k` library dependency.
82
- - `--export <spec>`: Export library symbols into source scope (`name` or `libname:localname`).
83
- - `--json`: Format output as JSON instead of binary wire format.
84
-
85
- ## Running Tests
86
-
87
- Run unit tests:
88
-
89
- ```bash
90
- node ./backends/arm64/tests/test-arm64.mjs
91
- ```
92
-
93
- Run integration tests:
94
-
95
- ```bash
96
- ./backends/arm64/tests/integration.sh
97
- ```
98
-
99
- Run the complete ARM64 backend test suite:
100
-
101
- ```bash
102
- npm run test:arm64
103
- ```