@fraczak/k 7.0.2 → 7.1.0
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 +42 -0
- package/DOCS/FILE_FORMATS.md +238 -15
- package/DOCS/POLYMORPHIC_IR.md +104 -0
- package/JOURNAL.md +288 -0
- package/README.md +10 -2
- package/access-table.mjs +343 -0
- package/backend-api.mjs +18 -0
- package/backends/llvm/README.md +20 -21
- package/backends/llvm/bin/k-llvm-build.mjs +105 -32
- package/backends/llvm/bin/k-llvm-compile.mjs +63 -24
- package/backends/llvm/bin/k-llvm.mjs +292 -0
- package/backends/llvm/runtime/krt.c +8 -0
- package/backends/llvm/runtime/krt.h +1 -0
- package/backends/llvm/src/cli.mjs +95 -17
- package/backends/llvm/src/executable.mjs +128 -11
- package/backends/llvm/src/kvm2llvm.mjs +10 -5
- package/backends/llvm/src/llvm.mjs +121 -9
- package/backends/llvm/tests/integration.sh +11 -11
- package/backends/llvm/{bin → tests}/k-llvm-run.mjs +48 -22
- package/backends/llvm/tests/perf-support.mjs +24 -3
- package/backends/llvm/tests/test-llvm.mjs +22 -1
- package/backends/wasm/bin/k-wasm-compile.mjs +46 -20
- package/backends/wasm/bin/k-wasm-run.mjs +61 -20
- package/backends/wasm/bin/k-wasm.mjs +47 -13
- package/backends/wasm/src/cli.mjs +141 -19
- package/backends/wasm/src/wasm.mjs +130 -23
- package/browser/repl-browser.mjs +16 -5
- package/codecs/k-parse.mjs +29 -8
- package/codecs/k-print.mjs +22 -6
- package/codecs/runtime/codec-sdk.mjs +8 -4
- package/codecs/show.mjs +20 -6
- package/k.mjs +56 -16
- package/kir.mjs +58 -19
- package/kvm.mjs +49 -12
- package/objects/compile.mjs +37 -23
- package/objects/decompile.mjs +40 -10
- package/objects/extract-aliases.mjs +44 -14
- package/objects/inspect.mjs +19 -14
- package/objects/validate.mjs +18 -11
- package/package.json +10 -3
- package/pkir.mjs +772 -0
- package/repl.mjs +36 -18
- package/scripts/build-repl-html.mjs +14 -2
- package/scripts/perf-ieee.mjs +264 -13
- package/scripts/perf-int.mjs +315 -22
- package/scripts/perf-multiplication.mjs +58 -7
- package/scripts/perf-poly.mjs +306 -24
- package/scripts/run-tests.mjs +3 -0
- package/tests/test-access-table.mjs +213 -0
- package/tests/test-pkir.mjs +530 -0
- package/tests/test-polymorphic-tdd.mjs +196 -0
- package/tests/test-repl-html.mjs +15 -2
- package/tests/test-repl.mjs +30 -1
- package/backends/llvm/bin/k-llvm-jit.mjs +0 -160
package/scripts/perf-int.mjs
CHANGED
|
@@ -58,23 +58,27 @@ function printHelp() {
|
|
|
58
58
|
|
|
59
59
|
Big Integer Arithmetic Performance Benchmark Harness.
|
|
60
60
|
Benchmarks plus, minus, times, and squaring scale on arithmetics.k
|
|
61
|
-
across Native JS, kVM interpreter, LLVM, and
|
|
61
|
+
across Native JS, kVM interpreter, LLVM, Wasm, and PKIR backends.
|
|
62
62
|
|
|
63
63
|
Options:
|
|
64
64
|
--wasm-in-process
|
|
65
65
|
Run WebAssembly in-process instead of persistent subprocess runner.
|
|
66
66
|
--squaring-llvm-only
|
|
67
67
|
Run squaring benchmark on LLVM only.
|
|
68
|
+
--pkir-only
|
|
69
|
+
Run only PKIR backend.
|
|
68
70
|
-h, --help
|
|
69
71
|
Show this help message and exit.
|
|
70
72
|
|
|
71
73
|
Environment Variables:
|
|
72
74
|
BACKENDS
|
|
73
|
-
Target backend to run: 'llvm' or '
|
|
75
|
+
Target backend to run: 'llvm', 'wasm', or 'pkir'.
|
|
74
76
|
LLVM_ONLY
|
|
75
77
|
Run only LLVM backend ('1' to enable).
|
|
76
78
|
WASM_ONLY
|
|
77
79
|
Run only WebAssembly backend ('1' to enable).
|
|
80
|
+
PKIR_ONLY
|
|
81
|
+
Run only PKIR backend ('1' to enable).
|
|
78
82
|
BACKENDS_ONLY
|
|
79
83
|
Skip JS and kVM interpreter baselines ('1' to enable).
|
|
80
84
|
SQUARING_LLVM_ONLY
|
|
@@ -116,12 +120,57 @@ if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
|
116
120
|
|
|
117
121
|
// Backend selection
|
|
118
122
|
const backendsEnv = process.env.BACKENDS?.toLowerCase();
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
const backendsList = backendsEnv
|
|
124
|
+
? backendsEnv.split(",").map(s => s.trim())
|
|
125
|
+
: null;
|
|
126
|
+
const llvmPkirOnly = process.env.LLVM_PKIR_ONLY === "1"
|
|
127
|
+
|| (backendsList && backendsList.length === 1 &&
|
|
128
|
+
backendsList[0] === "llvm-pkir")
|
|
129
|
+
|| process.argv.includes("--llvm-pkir-only");
|
|
130
|
+
const pkirOnly = !llvmPkirOnly && (
|
|
131
|
+
process.env.PKIR_ONLY === "1"
|
|
132
|
+
|| (backendsList && backendsList.length === 1 &&
|
|
133
|
+
backendsList[0] === "pkir")
|
|
134
|
+
|| process.argv.includes("--pkir-only")
|
|
135
|
+
);
|
|
136
|
+
const llvmOnly = !pkirOnly && !llvmPkirOnly && (
|
|
137
|
+
process.env.LLVM_ONLY === "1"
|
|
138
|
+
|| (backendsList && backendsList.length === 1 &&
|
|
139
|
+
backendsList[0] === "llvm")
|
|
140
|
+
|| process.argv.includes("--llvm-only")
|
|
141
|
+
);
|
|
142
|
+
const wasmOnly = !pkirOnly && !llvmPkirOnly && !llvmOnly && (
|
|
143
|
+
process.env.WASM_ONLY === "1"
|
|
144
|
+
|| (backendsList && backendsList.length === 1 &&
|
|
145
|
+
backendsList[0] === "wasm")
|
|
146
|
+
|| process.argv.includes("--wasm-only")
|
|
147
|
+
);
|
|
148
|
+
const runLLVM = backendsList
|
|
149
|
+
? backendsList.includes("llvm")
|
|
150
|
+
: !wasmOnly && !pkirOnly && !llvmPkirOnly;
|
|
151
|
+
const runLLVMPKIR = backendsList
|
|
152
|
+
? backendsList.includes("llvm-pkir")
|
|
153
|
+
: !wasmOnly && !pkirOnly && !llvmOnly;
|
|
154
|
+
const runWasm = backendsList
|
|
155
|
+
? backendsList.includes("wasm")
|
|
156
|
+
: !llvmOnly && !pkirOnly && !llvmPkirOnly;
|
|
157
|
+
const runPKIR = backendsList
|
|
158
|
+
? backendsList.includes("pkir")
|
|
159
|
+
: !llvmOnly && !wasmOnly && !llvmPkirOnly;
|
|
160
|
+
const runBaselines = !process.env.BACKENDS_ONLY
|
|
161
|
+
&& (backendsList == null || backendsList.includes("kvm") ||
|
|
162
|
+
backendsList.includes("js"))
|
|
163
|
+
&& !llvmOnly && !wasmOnly && !pkirOnly && !llvmPkirOnly;
|
|
164
|
+
const squaringLlvmOnly = llvmOnly || llvmPkirOnly
|
|
165
|
+
|| process.env.SQUARING_LLVM_ONLY === "1"
|
|
166
|
+
|| process.argv.includes("--squaring-llvm-only");
|
|
167
|
+
|
|
168
|
+
function llvmLaneNameStr() {
|
|
169
|
+
return llvmLaneName();
|
|
170
|
+
}
|
|
171
|
+
function llvmPkirLaneNameStr() {
|
|
172
|
+
return llvmLaneName().replace("LLVM", "LLVM-PKIR");
|
|
173
|
+
}
|
|
125
174
|
|
|
126
175
|
const ops = ["plus", "minus", "times"];
|
|
127
176
|
const benchNames = Object.fromEntries(ops.map(op => [op, `bench_${op}`]));
|
|
@@ -149,6 +198,9 @@ function wasmLaneName() {
|
|
|
149
198
|
const mode = wasmPipe ? "persistent" : "in-process";
|
|
150
199
|
return `WebAssembly (${mode})`;
|
|
151
200
|
}
|
|
201
|
+
function pkirLaneName() {
|
|
202
|
+
return "PKIR (Wasm persistent)";
|
|
203
|
+
}
|
|
152
204
|
|
|
153
205
|
console.log("==> Initializing state and loading @fraczak/k/Examples/arithmetics.k");
|
|
154
206
|
const state = createState();
|
|
@@ -212,13 +264,37 @@ if (runLLVM) {
|
|
|
212
264
|
relHash: relation.relHash,
|
|
213
265
|
inputPattern: tc.inputPattern,
|
|
214
266
|
cacheDir,
|
|
215
|
-
sourceLabel:
|
|
267
|
+
sourceLabel:
|
|
268
|
+
`@fraczak/k/Examples/arithmetics.k#${relation.relationName}`,
|
|
269
|
+
usePKIR: false
|
|
216
270
|
});
|
|
217
271
|
}
|
|
218
272
|
codes.load(state.codes);
|
|
219
273
|
printCompileFailures(testSuite);
|
|
220
274
|
}
|
|
221
275
|
|
|
276
|
+
// Setup LLVM-PKIR
|
|
277
|
+
if (runLLVMPKIR) {
|
|
278
|
+
console.log("==> Compiling LLVM-PKIR executables");
|
|
279
|
+
for (const tc of testSuite) {
|
|
280
|
+
const relation = relations[tc.op];
|
|
281
|
+
tc.llvmPKIR = tryCompileCase({
|
|
282
|
+
object: relation.object,
|
|
283
|
+
relationName: relation.relationName,
|
|
284
|
+
relHash: relation.relHash,
|
|
285
|
+
inputPattern: tc.inputPattern,
|
|
286
|
+
cacheDir,
|
|
287
|
+
sourceLabel:
|
|
288
|
+
`@fraczak/k/Examples/arithmetics.k#${relation.relationName}`,
|
|
289
|
+
usePKIR: true
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
codes.load(state.codes);
|
|
293
|
+
printCompileFailures(
|
|
294
|
+
testSuite.map(tc => ({ ...tc, llvm: tc.llvmPKIR }))
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
222
298
|
// Setup Wasm
|
|
223
299
|
let wasmExports = null;
|
|
224
300
|
const wasmExes = new Map();
|
|
@@ -272,6 +348,35 @@ if (runWasm) {
|
|
|
272
348
|
}
|
|
273
349
|
}
|
|
274
350
|
|
|
351
|
+
// Setup PKIR (persistent runner)
|
|
352
|
+
const pkirExes = new Map();
|
|
353
|
+
if (runPKIR) {
|
|
354
|
+
console.log("==> Compiling PKIR executables (persistent runner)...");
|
|
355
|
+
for (const op of ops) {
|
|
356
|
+
const relation = relations[op];
|
|
357
|
+
try {
|
|
358
|
+
const wasmPath = path.join(cacheDir, `perf_int_pkir_${op}.wasm`);
|
|
359
|
+
const exePath = path.join(cacheDir, `perf_int_pkir_${op}.exe`);
|
|
360
|
+
const wasmBuf = await compileWasmArtifactFromObject(relation.object, {
|
|
361
|
+
entry: "__main__",
|
|
362
|
+
usePKIR: true
|
|
363
|
+
});
|
|
364
|
+
fs.writeFileSync(wasmPath, wasmBuf);
|
|
365
|
+
const runBin = fileURLToPath(import.meta.resolve("../backends/wasm/bin/k-wasm-run.mjs"));
|
|
366
|
+
fs.writeFileSync(exePath, `#!/bin/sh\nexec node "${runBin}" "${wasmPath}" "$@"\n`, { mode: 0o755 });
|
|
367
|
+
pkirExes.set(op, { status: "ok", exePath, wasmPath });
|
|
368
|
+
} catch (error) {
|
|
369
|
+
pkirExes.set(op, {
|
|
370
|
+
status: "failed",
|
|
371
|
+
error: error.stack || error.message || String(error)
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
for (const tc of testSuite) {
|
|
376
|
+
tc.pkir = pkirExes.get(tc.op);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
275
380
|
function createWasmRunner(testSuite) {
|
|
276
381
|
const wasmCases = testSuite.filter(tc => tc.wasm?.status === "ok");
|
|
277
382
|
const servers = new Map();
|
|
@@ -300,6 +405,34 @@ function createWasmRunner(testSuite) {
|
|
|
300
405
|
};
|
|
301
406
|
}
|
|
302
407
|
|
|
408
|
+
function createPKIRRunner(testSuite) {
|
|
409
|
+
const pkirCases = testSuite.filter(tc => tc.pkir?.status === "ok");
|
|
410
|
+
const servers = new Map();
|
|
411
|
+
function serverFor(exePath) {
|
|
412
|
+
let server = servers.get(exePath);
|
|
413
|
+
if (server == null) {
|
|
414
|
+
server = new PersistentExecutable(exePath);
|
|
415
|
+
servers.set(exePath, server);
|
|
416
|
+
}
|
|
417
|
+
return server;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return {
|
|
421
|
+
async run(iterations) {
|
|
422
|
+
if (pkirCases.length === 0) return null;
|
|
423
|
+
return runTimedIterationsAsync(iterations, async () => {
|
|
424
|
+
for (const tc of pkirCases) {
|
|
425
|
+
await serverFor(tc.pkir.exePath).request(tc.inputWire);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
},
|
|
429
|
+
serverFor,
|
|
430
|
+
close() {
|
|
431
|
+
for (const server of servers.values()) server.close();
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
303
436
|
function printBenchmarkDescription() {
|
|
304
437
|
const lanes = [];
|
|
305
438
|
if (runBaselines) {
|
|
@@ -307,8 +440,10 @@ function printBenchmarkDescription() {
|
|
|
307
440
|
lanes.push("Native JS (Envelope-Free)");
|
|
308
441
|
lanes.push("kVM Interpreter (Env-Free)");
|
|
309
442
|
}
|
|
310
|
-
if (runLLVM) lanes.push(
|
|
443
|
+
if (runLLVM) lanes.push(llvmLaneNameStr());
|
|
444
|
+
if (runLLVMPKIR) lanes.push(llvmPkirLaneNameStr());
|
|
311
445
|
if (runWasm) lanes.push(wasmLaneName());
|
|
446
|
+
if (runPKIR) lanes.push(pkirLaneName());
|
|
312
447
|
|
|
313
448
|
console.log("==> Benchmark description");
|
|
314
449
|
console.log(" source: @fraczak/k/Examples/arithmetics.k");
|
|
@@ -320,11 +455,18 @@ function printBenchmarkDescription() {
|
|
|
320
455
|
console.log(` llvm-ready cases: ${testSuite.filter(tc => tc.llvm?.status === "ok").length}`);
|
|
321
456
|
console.log(` llvm warmup iterations: ${llvmWarmupIterations}`);
|
|
322
457
|
}
|
|
458
|
+
if (runLLVMPKIR) {
|
|
459
|
+
console.log(` llvm-pkir ready cases: ${testSuite.filter(tc => tc.llvmPKIR?.status === "ok").length}`);
|
|
460
|
+
console.log(` llvm-pkir warmup iterations: ${llvmWarmupIterations}`);
|
|
461
|
+
}
|
|
323
462
|
if (runWasm) {
|
|
324
463
|
console.log(` wasm runner mode: ${wasmPipe ? "persistent pipe" : "in-process"}`);
|
|
325
464
|
console.log(` wasm warmup iterations: ${wasmWarmupIterations}`);
|
|
326
465
|
if (!wasmPipe) console.log(` wasm arena reset: ${wasmReset ? "yes" : "no"}`);
|
|
327
466
|
}
|
|
467
|
+
if (runPKIR) {
|
|
468
|
+
console.log(` pkir warmup iterations: ${wasmWarmupIterations}`);
|
|
469
|
+
}
|
|
328
470
|
console.log(` iterations: ${iterations}`);
|
|
329
471
|
console.log(` benchmark lanes: ${lanes.join("; ")}`);
|
|
330
472
|
console.log(" conformance: all outputs are compared to native expected values");
|
|
@@ -359,6 +501,22 @@ if (runLLVM) {
|
|
|
359
501
|
}
|
|
360
502
|
}
|
|
361
503
|
|
|
504
|
+
// 2b. LLVM-PKIR lane
|
|
505
|
+
let llvmPkirResult = null;
|
|
506
|
+
if (runLLVMPKIR) {
|
|
507
|
+
const llvmPkirCases = testSuite.map(tc => ({ ...tc, llvm: tc.llvmPKIR }));
|
|
508
|
+
const llvmPkirRunner = createLLVMRunner(llvmPkirCases);
|
|
509
|
+
try {
|
|
510
|
+
if (llvmWarmupIterations > 0) {
|
|
511
|
+
console.log(`==> Warming LLVM-PKIR executables (${llvmWarmupIterations} iterations)...`);
|
|
512
|
+
await llvmPkirRunner.run(llvmWarmupIterations);
|
|
513
|
+
}
|
|
514
|
+
llvmPkirResult = await llvmPkirRunner.run(iterations);
|
|
515
|
+
} finally {
|
|
516
|
+
llvmPkirRunner.close();
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
362
520
|
// 3. Wasm lane
|
|
363
521
|
let wasmResult = null;
|
|
364
522
|
if (runWasm) {
|
|
@@ -403,6 +561,25 @@ if (runWasm) {
|
|
|
403
561
|
}
|
|
404
562
|
}
|
|
405
563
|
|
|
564
|
+
// 4. PKIR lane
|
|
565
|
+
let pkirResult = null;
|
|
566
|
+
if (runPKIR) {
|
|
567
|
+
const pkirCases = testSuite.filter(tc => tc.pkir?.status === "ok");
|
|
568
|
+
if (pkirCases.length === testSuite.length) {
|
|
569
|
+
const pkirRunner = createPKIRRunner(testSuite);
|
|
570
|
+
try {
|
|
571
|
+
if (wasmWarmupIterations > 0) {
|
|
572
|
+
console.log(`==> Warming PKIR (${wasmWarmupIterations} iterations)...`);
|
|
573
|
+
await pkirRunner.run(wasmWarmupIterations);
|
|
574
|
+
}
|
|
575
|
+
console.log(`==> Running PKIR (${iterations} iterations)...`);
|
|
576
|
+
pkirResult = await pkirRunner.run(iterations);
|
|
577
|
+
} finally {
|
|
578
|
+
pkirRunner.close();
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
406
583
|
console.log("\n=================== INTEGER BENCHMARK RESULTS ===================");
|
|
407
584
|
console.log(`Operation calls per iteration: ${testSuite.length}`);
|
|
408
585
|
console.log(`Total requested operation calls: ${iterations * testSuite.length}`);
|
|
@@ -414,11 +591,19 @@ if (runBaselines) {
|
|
|
414
591
|
console.log(`${laneIndex++}. kVM Interpreter (Env-Free): ${formatTiming(kvmFreeResult)}`);
|
|
415
592
|
}
|
|
416
593
|
if (runLLVM) {
|
|
417
|
-
|
|
594
|
+
const name = llvmLaneNameStr().padEnd(29);
|
|
595
|
+
console.log(`${laneIndex++}. ${name} ${formatTiming(llvmResult)}`);
|
|
596
|
+
}
|
|
597
|
+
if (runLLVMPKIR) {
|
|
598
|
+
const name = llvmPkirLaneNameStr().padEnd(29);
|
|
599
|
+
console.log(`${laneIndex++}. ${name} ${formatTiming(llvmPkirResult)}`);
|
|
418
600
|
}
|
|
419
601
|
if (runWasm) {
|
|
420
602
|
console.log(`${laneIndex++}. ${wasmLaneName().padEnd(29)} ${formatTiming(wasmResult)}`);
|
|
421
603
|
}
|
|
604
|
+
if (runPKIR) {
|
|
605
|
+
console.log(`${laneIndex++}. ${pkirLaneName().padEnd(29)} ${formatTiming(pkirResult)}`);
|
|
606
|
+
}
|
|
422
607
|
console.log("=================================================================\n");
|
|
423
608
|
|
|
424
609
|
// Conformance Validation
|
|
@@ -446,6 +631,24 @@ for (const tc of testSuite) {
|
|
|
446
631
|
}
|
|
447
632
|
}
|
|
448
633
|
|
|
634
|
+
if (runLLVMPKIR) {
|
|
635
|
+
if (tc.llvmPKIR.status !== "ok") {
|
|
636
|
+
tc.llvmPkirConformance = "compile-failed";
|
|
637
|
+
} else {
|
|
638
|
+
try {
|
|
639
|
+
const outputWire = await runExecutable(tc.llvmPKIR.exePath, tc.inputWire);
|
|
640
|
+
const actual = decodeWire(outputWire).value;
|
|
641
|
+
assert.deepEqual(toPlainObject(actual), toPlainObject(tc.expected));
|
|
642
|
+
tc.llvmPkirConformance = "ok";
|
|
643
|
+
} catch (error) {
|
|
644
|
+
tc.llvmPkirConformance = "failed";
|
|
645
|
+
tc.llvmPKIR.error = error.stack || error.message || String(error);
|
|
646
|
+
console.log(`LLVM-PKIR conformance failure for ${tc.op}(${tc.label}):`);
|
|
647
|
+
console.log(tc.llvmPKIR.error.split("\n").slice(0, 8).join("\n"));
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
449
652
|
if (runWasm) {
|
|
450
653
|
if (wasmPipe) {
|
|
451
654
|
if (tc.wasm?.status !== "ok") {
|
|
@@ -484,6 +687,24 @@ for (const tc of testSuite) {
|
|
|
484
687
|
}
|
|
485
688
|
}
|
|
486
689
|
|
|
690
|
+
if (runPKIR) {
|
|
691
|
+
if (tc.pkir?.status !== "ok") {
|
|
692
|
+
tc.pkirConformance = "compile-failed";
|
|
693
|
+
} else {
|
|
694
|
+
try {
|
|
695
|
+
const outputWire = await runExecutable(tc.pkir.exePath, tc.inputWire);
|
|
696
|
+
const actual = decodeWire(outputWire).value;
|
|
697
|
+
assert.deepEqual(toPlainObject(actual), toPlainObject(tc.expected));
|
|
698
|
+
tc.pkirConformance = "ok";
|
|
699
|
+
} catch (error) {
|
|
700
|
+
tc.pkirConformance = "failed";
|
|
701
|
+
tc.pkir.error = error.stack || error.message || String(error);
|
|
702
|
+
console.log(`PKIR conformance failure for ${tc.op}(${tc.label}):`);
|
|
703
|
+
console.log(tc.pkir.error.split("\n").slice(0, 8).join("\n"));
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
487
708
|
}
|
|
488
709
|
|
|
489
710
|
const validationSummary = [];
|
|
@@ -492,14 +713,24 @@ if (runLLVM) {
|
|
|
492
713
|
const llvmOk = testSuite.filter(tc => tc.llvmConformance === "ok").length;
|
|
493
714
|
validationSummary.push(`LLVM ${llvmOk}/${testSuite.length}`);
|
|
494
715
|
}
|
|
716
|
+
if (runLLVMPKIR) {
|
|
717
|
+
const pkirOk = testSuite.filter(tc => tc.llvmPkirConformance === "ok").length;
|
|
718
|
+
validationSummary.push(`LLVM-PKIR ${pkirOk}/${testSuite.length}`);
|
|
719
|
+
}
|
|
495
720
|
if (runWasm) {
|
|
496
721
|
const wasmOk = testSuite.filter(tc => tc.wasmConformance === "ok").length;
|
|
497
722
|
validationSummary.push(`Wasm ${wasmOk}/${testSuite.length}`);
|
|
498
723
|
}
|
|
724
|
+
if (runPKIR) {
|
|
725
|
+
const pkirOk = testSuite.filter(tc => tc.pkirConformance === "ok").length;
|
|
726
|
+
validationSummary.push(`PKIR ${pkirOk}/${testSuite.length}`);
|
|
727
|
+
}
|
|
499
728
|
|
|
500
729
|
console.log(`Conformance validation: ${validationSummary.join(", ")} cases match expected values.`);
|
|
501
730
|
if (runLLVM && shouldStrictFail(testSuite)) process.exitCode = 1;
|
|
731
|
+
if (runLLVMPKIR && shouldStrictFail(testSuite.map(tc => ({ ...tc, llvm: tc.llvmPKIR })))) process.exitCode = 1;
|
|
502
732
|
if (runWasm && testSuite.some(tc => tc.wasmConformance !== "ok")) process.exitCode = 1;
|
|
733
|
+
if (runPKIR && testSuite.some(tc => tc.pkirConformance !== "ok")) process.exitCode = 1;
|
|
503
734
|
|
|
504
735
|
// =======================================================================
|
|
505
736
|
// Squaring Scale & Memory Profiling Benchmark
|
|
@@ -596,13 +827,31 @@ for (const algo of squaringAlgos) {
|
|
|
596
827
|
relHash: algo.rel.relHash,
|
|
597
828
|
inputPattern: sInputPattern,
|
|
598
829
|
cacheDir,
|
|
599
|
-
sourceLabel: algo.sourceLabel
|
|
830
|
+
sourceLabel: algo.sourceLabel,
|
|
831
|
+
usePKIR: false
|
|
600
832
|
});
|
|
601
833
|
if (sLlvmCompiled.status === "ok") {
|
|
602
834
|
algo.llvmServer = new PersistentExecutable(sLlvmCompiled.exePath);
|
|
603
835
|
}
|
|
604
836
|
}
|
|
605
837
|
|
|
838
|
+
algo.llvmPkirServer = null;
|
|
839
|
+
if (runLLVMPKIR) {
|
|
840
|
+
const sLlvmPkirCompiled = tryCompileCase({
|
|
841
|
+
object: algo.rel.object,
|
|
842
|
+
relationName: algo.rel.relationName,
|
|
843
|
+
relHash: algo.rel.relHash,
|
|
844
|
+
inputPattern: sInputPattern,
|
|
845
|
+
cacheDir,
|
|
846
|
+
sourceLabel: algo.sourceLabel,
|
|
847
|
+
usePKIR: true
|
|
848
|
+
});
|
|
849
|
+
if (sLlvmPkirCompiled.status === "ok") {
|
|
850
|
+
algo.llvmPkirServer =
|
|
851
|
+
new PersistentExecutable(sLlvmPkirCompiled.exePath);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
606
855
|
algo.wasmServer = null;
|
|
607
856
|
if (runWasm && !squaringLlvmOnly) {
|
|
608
857
|
try {
|
|
@@ -618,6 +867,24 @@ for (const algo of squaringAlgos) {
|
|
|
618
867
|
}
|
|
619
868
|
}
|
|
620
869
|
|
|
870
|
+
algo.pkirServer = null;
|
|
871
|
+
if (runPKIR && !squaringLlvmOnly) {
|
|
872
|
+
try {
|
|
873
|
+
const sPkirPath = path.join(cacheDir, `perf_int_pkir_${algo.id}.wasm`);
|
|
874
|
+
const sPkirExe = path.join(cacheDir, `perf_int_pkir_${algo.id}.exe`);
|
|
875
|
+
const wasmBuf = await compileWasmArtifactFromObject(algo.rel.object, {
|
|
876
|
+
entry: "__main__",
|
|
877
|
+
usePKIR: true
|
|
878
|
+
});
|
|
879
|
+
fs.writeFileSync(sPkirPath, wasmBuf);
|
|
880
|
+
const runBin = fileURLToPath(import.meta.resolve("../backends/wasm/bin/k-wasm-run.mjs"));
|
|
881
|
+
fs.writeFileSync(sPkirExe, `#!/bin/sh\nexec node "${runBin}" "${sPkirPath}" "$@"\n`, { mode: 0o755 });
|
|
882
|
+
algo.pkirServer = new PersistentExecutable(sPkirExe);
|
|
883
|
+
} catch (e) {
|
|
884
|
+
console.log(`Failed to build PKIR executable for '${algo.id}':`, e.message);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
621
888
|
algo.results = {};
|
|
622
889
|
}
|
|
623
890
|
|
|
@@ -628,11 +895,27 @@ if (runBaselines && !squaringLlvmOnly) {
|
|
|
628
895
|
squaringLanes.push({ id: "kvm", name: "kVM (Env-Free)", type: "kvm", memLabel: "heap" });
|
|
629
896
|
}
|
|
630
897
|
if (runLLVM) {
|
|
631
|
-
squaringLanes.push({
|
|
898
|
+
squaringLanes.push({
|
|
899
|
+
id: "llvm",
|
|
900
|
+
name: llvmLaneNameStr(),
|
|
901
|
+
type: "persistent",
|
|
902
|
+
memLabel: "peak RSS"
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
if (runLLVMPKIR) {
|
|
906
|
+
squaringLanes.push({
|
|
907
|
+
id: "llvm-pkir",
|
|
908
|
+
name: llvmPkirLaneNameStr(),
|
|
909
|
+
type: "persistent",
|
|
910
|
+
memLabel: "peak RSS"
|
|
911
|
+
});
|
|
632
912
|
}
|
|
633
913
|
if (runWasm && !squaringLlvmOnly) {
|
|
634
914
|
squaringLanes.push({ id: "wasm", name: wasmLaneName(), type: "persistent", memLabel: "peak RSS" });
|
|
635
915
|
}
|
|
916
|
+
if (runPKIR && !squaringLlvmOnly) {
|
|
917
|
+
squaringLanes.push({ id: "pkir", name: pkirLaneName(), type: "persistent", memLabel: "peak RSS" });
|
|
918
|
+
}
|
|
636
919
|
|
|
637
920
|
for (const algo of squaringAlgos) {
|
|
638
921
|
for (const lane of squaringLanes) {
|
|
@@ -642,10 +925,16 @@ for (const algo of squaringAlgos) {
|
|
|
642
925
|
const stepMetadata = [];
|
|
643
926
|
|
|
644
927
|
try {
|
|
645
|
-
// 1. Persistent child processes (Wasm, LLVM)
|
|
928
|
+
// 1. Persistent child processes (Wasm, PKIR, LLVM)
|
|
646
929
|
for (const lane of squaringLanes.filter(l => l.type === "persistent")) {
|
|
647
930
|
for (const algo of squaringAlgos) {
|
|
648
|
-
const server = lane.id === "llvm"
|
|
931
|
+
const server = lane.id === "llvm"
|
|
932
|
+
? algo.llvmServer
|
|
933
|
+
: lane.id === "llvm-pkir"
|
|
934
|
+
? algo.llvmPkirServer
|
|
935
|
+
: lane.id === "pkir"
|
|
936
|
+
? algo.pkirServer
|
|
937
|
+
: algo.wasmServer;
|
|
649
938
|
if (!server) continue;
|
|
650
939
|
console.log(`==> Running ${lane.name} squaring [${algo.name}]...`);
|
|
651
940
|
let curWire = sInitWire;
|
|
@@ -746,7 +1035,9 @@ try {
|
|
|
746
1035
|
} finally {
|
|
747
1036
|
for (const algo of squaringAlgos) {
|
|
748
1037
|
if (algo.wasmServer) algo.wasmServer.close();
|
|
1038
|
+
if (algo.pkirServer) algo.pkirServer.close();
|
|
749
1039
|
if (algo.llvmServer) algo.llvmServer.close();
|
|
1040
|
+
if (algo.llvmPkirServer) algo.llvmPkirServer.close();
|
|
750
1041
|
}
|
|
751
1042
|
}
|
|
752
1043
|
|
|
@@ -765,11 +1056,13 @@ for (let step = 1; step <= squaringSteps; step++) {
|
|
|
765
1056
|
const sep130 = "=".repeat(130);
|
|
766
1057
|
const line130 = "-".repeat(130);
|
|
767
1058
|
|
|
768
|
-
if (squaringLanes.length === 1 && squaringLanes[0].id === "llvm") {
|
|
1059
|
+
if (squaringLanes.length === 1 && (squaringLanes[0].id === "llvm" || squaringLanes[0].id === "llvm-pkir")) {
|
|
1060
|
+
const laneId = squaringLanes[0].id;
|
|
1061
|
+
const laneName = squaringLanes[0].name;
|
|
769
1062
|
// LLVM Only side-by-side comparison table
|
|
770
1063
|
console.log(`\n${sep130}`);
|
|
771
|
-
console.log(
|
|
772
|
-
console.log(`Algorithms: times vs karatsuba on ${
|
|
1064
|
+
console.log(`=================== SQUARING SCALE & ALGORITHM BENCHMARK (${laneName}) ===================`);
|
|
1065
|
+
console.log(`Algorithms: times vs karatsuba on ${laneName}`);
|
|
773
1066
|
console.log(`Initial: ${squaringInputText} (${countBitsValue(sInitialVal)} bits)`);
|
|
774
1067
|
console.log(line130);
|
|
775
1068
|
|
|
@@ -780,8 +1073,8 @@ if (squaringLanes.length === 1 && squaringLanes[0].id === "llvm") {
|
|
|
780
1073
|
"Step",
|
|
781
1074
|
"Bits",
|
|
782
1075
|
"Wire(B)",
|
|
783
|
-
|
|
784
|
-
|
|
1076
|
+
`${laneName} times (peak RSS)`,
|
|
1077
|
+
`${laneName} karatsuba (peak RSS)`,
|
|
785
1078
|
"Speedup (T/K)"
|
|
786
1079
|
];
|
|
787
1080
|
console.log(hCols.map((h, i) => (i < 3 ? h.padStart(colW[i]) : h.padEnd(colW[i]))).join(" | "));
|
|
@@ -789,8 +1082,8 @@ if (squaringLanes.length === 1 && squaringLanes[0].id === "llvm") {
|
|
|
789
1082
|
|
|
790
1083
|
for (let step = 1; step <= squaringSteps; step++) {
|
|
791
1084
|
const meta = stepMetadata[step - 1] || { bits: "?", wireLen: "?" };
|
|
792
|
-
const tRes = tAlgo.results
|
|
793
|
-
const kRes = kAlgo.results
|
|
1085
|
+
const tRes = tAlgo.results[laneId][step - 1];
|
|
1086
|
+
const kRes = kAlgo.results[laneId][step - 1];
|
|
794
1087
|
|
|
795
1088
|
const formatCell = (res, w) => {
|
|
796
1089
|
if (!res) return "-".padEnd(w);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* across available execution backends:
|
|
8
8
|
* - LLVM (native binary via Clang/LLVM)
|
|
9
9
|
* - WebAssembly (persistent runner via Wasm)
|
|
10
|
+
* - PKIR (persistent runner via Wasm/PKIR)
|
|
10
11
|
* - kVM Interpreter (envelope-free bytecode engine)
|
|
11
12
|
* - Native JS (envelope-free convergence engine)
|
|
12
13
|
*
|
|
@@ -66,7 +67,7 @@ function printHelp() {
|
|
|
66
67
|
|
|
67
68
|
Multiplication Algorithm & Multi-Backend Performance Harness.
|
|
68
69
|
Compares multiplication algorithms ('times' vs 'karatsuba') across
|
|
69
|
-
available execution backends (LLVM, Wasm, kVM, JS).
|
|
70
|
+
available execution backends (LLVM, Wasm, PKIR, kVM, JS).
|
|
70
71
|
|
|
71
72
|
Options:
|
|
72
73
|
--mode <squaring|sizes>
|
|
@@ -75,7 +76,8 @@ Options:
|
|
|
75
76
|
(default: squaring)
|
|
76
77
|
--backends <csv>
|
|
77
78
|
Comma-separated list of execution backends to test:
|
|
78
|
-
llvm, wasm, kvm, js.
|
|
79
|
+
llvm, llvm-pkir, wasm, pkir, kvm, js.
|
|
80
|
+
(default: llvm,llvm-pkir,wasm,pkir,kvm,js)
|
|
79
81
|
--algos <csv>
|
|
80
82
|
Comma-separated list of multiplication algorithms to test:
|
|
81
83
|
times, karatsuba. (default: times,karatsuba)
|
|
@@ -114,7 +116,11 @@ if (hasFlag("-h") || hasFlag("--help")) {
|
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
const mode = getArg("--mode", "squaring"); // 'squaring' or 'sizes'
|
|
117
|
-
const
|
|
119
|
+
const defaultBackends = "llvm,llvm-pkir,wasm,pkir,kvm,js";
|
|
120
|
+
const backendsArg = getArg(
|
|
121
|
+
"--backends",
|
|
122
|
+
process.env.BACKENDS || defaultBackends
|
|
123
|
+
);
|
|
118
124
|
const selectedBackends = backendsArg
|
|
119
125
|
.split(",").map(b => b.trim().toLowerCase()).filter(Boolean);
|
|
120
126
|
const algosArg = getArg("--algos", "times,karatsuba");
|
|
@@ -239,7 +245,7 @@ const cacheDir = makeCacheDir("k-mult-perf-");
|
|
|
239
245
|
// 2. Prepare Squaring Relations & Binaries
|
|
240
246
|
// For squaring: relation name `s_${algo} = {()x, ()y} ${algo}; s_${algo}`
|
|
241
247
|
const algoRelations = {};
|
|
242
|
-
const algoServers = { llvm: {}, wasm: {} };
|
|
248
|
+
const algoServers = { llvm: {}, "llvm-pkir": {}, wasm: {}, pkir: {} };
|
|
243
249
|
|
|
244
250
|
let commonState = null;
|
|
245
251
|
|
|
@@ -285,6 +291,26 @@ for (const algo of selectedAlgos) {
|
|
|
285
291
|
}
|
|
286
292
|
}
|
|
287
293
|
|
|
294
|
+
// LLVM (via PKIR)
|
|
295
|
+
if (selectedBackends.includes("llvm-pkir")) {
|
|
296
|
+
const exePath = path.join(cacheDir, `perf_mult_llvm_pkir_${algo}`);
|
|
297
|
+
try {
|
|
298
|
+
compileObjectToExecutable(
|
|
299
|
+
algoRelations[algo].object, exePath, {
|
|
300
|
+
clangOpt: "-O2",
|
|
301
|
+
inputPattern: parseIntValue("0").pattern,
|
|
302
|
+
usePKIR: true
|
|
303
|
+
}
|
|
304
|
+
);
|
|
305
|
+
algoServers["llvm-pkir"][algo] =
|
|
306
|
+
new PersistentExecutable(exePath);
|
|
307
|
+
} catch (e) {
|
|
308
|
+
console.warn(
|
|
309
|
+
`[WARN] Failed to compile LLVM-PKIR for ${algo}: ${e.message}`
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
288
314
|
// Wasm
|
|
289
315
|
if (selectedBackends.includes("wasm")) {
|
|
290
316
|
const wasmPath = path.join(cacheDir, `perf_mult_wasm_${algo}.wasm`);
|
|
@@ -309,6 +335,31 @@ for (const algo of selectedAlgos) {
|
|
|
309
335
|
);
|
|
310
336
|
}
|
|
311
337
|
}
|
|
338
|
+
|
|
339
|
+
// PKIR
|
|
340
|
+
if (selectedBackends.includes("pkir")) {
|
|
341
|
+
const pkirPath = path.join(cacheDir, `perf_mult_pkir_${algo}.wasm`);
|
|
342
|
+
const exePath = path.join(cacheDir, `perf_mult_pkir_${algo}.exe`);
|
|
343
|
+
try {
|
|
344
|
+
const wasmBuf = await compileWasmArtifactFromObject(
|
|
345
|
+
algoRelations[algo].object, { entry: "__main__", usePKIR: true }
|
|
346
|
+
);
|
|
347
|
+
fs.writeFileSync(pkirPath, wasmBuf);
|
|
348
|
+
const runBin = fileURLToPath(
|
|
349
|
+
import.meta.resolve("../backends/wasm/bin/k-wasm-run.mjs")
|
|
350
|
+
);
|
|
351
|
+
fs.writeFileSync(
|
|
352
|
+
exePath,
|
|
353
|
+
`#!/bin/sh\nexec node "${runBin}" "${pkirPath}" "$@"\n`,
|
|
354
|
+
{ mode: 0o755 }
|
|
355
|
+
);
|
|
356
|
+
algoServers.pkir[algo] = new PersistentExecutable(exePath);
|
|
357
|
+
} catch (e) {
|
|
358
|
+
console.warn(
|
|
359
|
+
`[WARN] Failed to compile PKIR for ${algo}: ${e.message}`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
312
363
|
}
|
|
313
364
|
|
|
314
365
|
// 3. Squaring Mode Benchmark
|
|
@@ -327,8 +378,8 @@ if (mode === "squaring") {
|
|
|
327
378
|
const stepBits = [];
|
|
328
379
|
|
|
329
380
|
try {
|
|
330
|
-
// A. Run persistent backends (LLVM, Wasm)
|
|
331
|
-
const activeBackends = ["llvm", "wasm"]
|
|
381
|
+
// A. Run persistent backends (LLVM, LLVM-PKIR, Wasm, PKIR)
|
|
382
|
+
const activeBackends = ["llvm", "llvm-pkir", "wasm", "pkir"]
|
|
332
383
|
.filter(b => selectedBackends.includes(b));
|
|
333
384
|
for (const backend of activeBackends) {
|
|
334
385
|
for (const algo of selectedAlgos) {
|
|
@@ -541,7 +592,7 @@ if (mode === "squaring") {
|
|
|
541
592
|
|
|
542
593
|
} finally {
|
|
543
594
|
// Teardown persistent servers
|
|
544
|
-
for (const backend of ["llvm", "wasm"]) {
|
|
595
|
+
for (const backend of ["llvm", "llvm-pkir", "wasm", "pkir"]) {
|
|
545
596
|
for (const algo of selectedAlgos) {
|
|
546
597
|
algoServers[backend]?.[algo]?.child?.kill();
|
|
547
598
|
}
|