@fraczak/k 7.0.1 → 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.
- package/CHANGELOG.md +36 -0
- package/DOCS/BIG_PICTURE.md +5 -5
- package/DOCS/FILE_FORMATS.md +5 -5
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +9 -9
- package/DOCS/PATTERNS.md +3 -3
- package/DOCS/PROFILING_AND_TRACING.md +60 -72
- package/DOCS/REPL.md +3 -3
- package/DOCS/book/03-types-as-finite-automata.md +4 -4
- package/DOCS/book/06-values-in-memory.md +9 -9
- package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
- package/DOCS/book/09-compiler-architecture.md +2 -2
- package/DOCS/book/10-from-ast-to-intermediate-representation.md +2 -2
- package/DOCS/book/14-canonical-serialization.md +1 -1
- package/Examples/arithmetics.k +2 -2
- package/Examples/bnat.k +1 -1
- package/Examples/karatsuba-mult.k +2 -2
- package/README.md +3 -8
- package/backends/llvm/runtime/krt.c +33 -0
- package/backends/llvm/runtime/krt.h +1 -0
- package/backends/llvm/src/executable.mjs +4 -4
- package/backends/llvm/src/kvm2llvm.mjs +40 -18
- package/backends/llvm/src/llvm.mjs +12 -11
- package/backends/llvm/tests/perf-support.mjs +1 -55
- package/backends/llvm/tests/test-llvm.mjs +3 -3
- package/backends/wasm/src/wasm.mjs +2 -1
- package/backends/wasm/tests/test-wasm.mjs +5 -5
- package/browser/vfs-data.mjs +5 -5
- package/core.k +8 -8
- package/harness/format.mjs +2 -2
- package/harness/registry.mjs +0 -2
- package/kir.mjs +142 -56
- package/package.json +1 -5
- package/scripts/benchmark-idempotency.mjs +12 -8
- package/scripts/benchmark.mjs +5 -9
- package/scripts/build-repl-html.mjs +2 -2
- package/scripts/perf-ieee.mjs +17 -118
- package/scripts/perf-int.mjs +313 -318
- package/scripts/perf-multiplication.mjs +10 -20
- package/scripts/perf-poly.mjs +29 -183
- package/tests/code-derivation/pattern-envelope.mjs +4 -4
- package/tests/test-kvm-polymorphic.mjs +2 -2
- package/backends/arm64/README.md +0 -103
- package/backends/arm64/bin/k-arm64-compile.mjs +0 -93
- package/backends/arm64/bin/k-arm64-run.mjs +0 -58
- package/backends/arm64/bin/k-arm64.mjs +0 -88
- package/backends/arm64/scripts/run-tests.mjs +0 -57
- package/backends/arm64/src/arm64.mjs +0 -229
- package/backends/arm64/src/cli.mjs +0 -155
- package/backends/arm64/src/kvm2arm64.mjs +0 -906
- package/backends/arm64/src/runtime.c +0 -744
- package/backends/arm64/tests/integration.sh +0 -112
- package/backends/arm64/tests/test-arm64.mjs +0 -240
- 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
|
|
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
|
|
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, {
|
|
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 =
|
|
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 =
|
|
588
|
-
|
|
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) {
|
package/scripts/perf-poly.mjs
CHANGED
|
@@ -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,
|
|
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'
|
|
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
|
|
135
|
-
|
|
136
|
-
const
|
|
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
|
|
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 |
|
|
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} | ${
|
|
781
|
+
console.log(`${pName} | ${llvmCol} | ${wasmCol} | ${kvmCol}`);
|
|
928
782
|
}
|
|
929
783
|
|
|
930
|
-
const hasProfile = item.
|
|
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 |
|
|
949
|
-
console.log("
|
|
950
|
-
let
|
|
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} | ${
|
|
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} | ${
|
|
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 |
|
|
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} | ${
|
|
838
|
+
console.log(`${pName} | ${llvmCol} | ${wasmCol} | ${kvmCol}`);
|
|
991
839
|
}
|
|
992
840
|
|
|
993
|
-
if (phaseResults.some(r => r.
|
|
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) |
|
|
996
|
-
console.log("
|
|
997
|
-
let
|
|
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(
|
|
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(
|
|
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);
|
package/backends/arm64/README.md
DELETED
|
@@ -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
|
-
```
|