@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.
Files changed (54) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/DOCS/FILE_FORMATS.md +238 -15
  3. package/DOCS/POLYMORPHIC_IR.md +104 -0
  4. package/JOURNAL.md +288 -0
  5. package/README.md +10 -2
  6. package/access-table.mjs +343 -0
  7. package/backend-api.mjs +18 -0
  8. package/backends/llvm/README.md +20 -21
  9. package/backends/llvm/bin/k-llvm-build.mjs +105 -32
  10. package/backends/llvm/bin/k-llvm-compile.mjs +63 -24
  11. package/backends/llvm/bin/k-llvm.mjs +292 -0
  12. package/backends/llvm/runtime/krt.c +8 -0
  13. package/backends/llvm/runtime/krt.h +1 -0
  14. package/backends/llvm/src/cli.mjs +95 -17
  15. package/backends/llvm/src/executable.mjs +128 -11
  16. package/backends/llvm/src/kvm2llvm.mjs +10 -5
  17. package/backends/llvm/src/llvm.mjs +121 -9
  18. package/backends/llvm/tests/integration.sh +11 -11
  19. package/backends/llvm/{bin → tests}/k-llvm-run.mjs +48 -22
  20. package/backends/llvm/tests/perf-support.mjs +24 -3
  21. package/backends/llvm/tests/test-llvm.mjs +22 -1
  22. package/backends/wasm/bin/k-wasm-compile.mjs +46 -20
  23. package/backends/wasm/bin/k-wasm-run.mjs +61 -20
  24. package/backends/wasm/bin/k-wasm.mjs +47 -13
  25. package/backends/wasm/src/cli.mjs +141 -19
  26. package/backends/wasm/src/wasm.mjs +130 -23
  27. package/browser/repl-browser.mjs +16 -5
  28. package/codecs/k-parse.mjs +29 -8
  29. package/codecs/k-print.mjs +22 -6
  30. package/codecs/runtime/codec-sdk.mjs +8 -4
  31. package/codecs/show.mjs +20 -6
  32. package/k.mjs +56 -16
  33. package/kir.mjs +58 -19
  34. package/kvm.mjs +49 -12
  35. package/objects/compile.mjs +37 -23
  36. package/objects/decompile.mjs +40 -10
  37. package/objects/extract-aliases.mjs +44 -14
  38. package/objects/inspect.mjs +19 -14
  39. package/objects/validate.mjs +18 -11
  40. package/package.json +10 -3
  41. package/pkir.mjs +772 -0
  42. package/repl.mjs +36 -18
  43. package/scripts/build-repl-html.mjs +14 -2
  44. package/scripts/perf-ieee.mjs +264 -13
  45. package/scripts/perf-int.mjs +315 -22
  46. package/scripts/perf-multiplication.mjs +58 -7
  47. package/scripts/perf-poly.mjs +306 -24
  48. package/scripts/run-tests.mjs +3 -0
  49. package/tests/test-access-table.mjs +213 -0
  50. package/tests/test-pkir.mjs +530 -0
  51. package/tests/test-polymorphic-tdd.mjs +196 -0
  52. package/tests/test-repl-html.mjs +15 -2
  53. package/tests/test-repl.mjs +30 -1
  54. package/backends/llvm/bin/k-llvm-jit.mjs +0 -160
package/repl.mjs CHANGED
@@ -50,7 +50,7 @@ const NAME_RE = /^[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/;
50
50
  const COMMAND_NAMES = [
51
51
  "help", "rel",
52
52
  "codecs", "rels", "codec", "input", "reset", "klib", "ko", "load",
53
- "engine", "wasm", "js",
53
+ "engine", "wasm", "pkir", "js",
54
54
  "quit", "exit"
55
55
  ];
56
56
  const CODEC_COMMAND_NAMES = ["load", "unload", "list"];
@@ -76,8 +76,10 @@ function emptyValue() {
76
76
  function createState(options = {}) {
77
77
  codes.load(cloneJSON(initialCodes));
78
78
  const engine = (options.engine || "wasm").toLowerCase();
79
- if (engine !== "wasm" && engine !== "js") {
80
- throw new Error(`Invalid engine '${options.engine}'. Valid engines: wasm, js`);
79
+ if (engine !== "wasm" && engine !== "pkir" && engine !== "js") {
80
+ throw new Error(
81
+ `Invalid engine '${options.engine}'. Valid engines: wasm, pkir, js`
82
+ );
81
83
  }
82
84
  return {
83
85
  engine,
@@ -768,7 +770,7 @@ function completeCommandArgument(line, state) {
768
770
  }
769
771
 
770
772
  if (command === "engine") {
771
- return completeCommandWord(line, argStart, arg, ["wasm", "js"]);
773
+ return completeCommandWord(line, argStart, arg, ["wasm", "pkir", "js"]);
772
774
  }
773
775
 
774
776
  if (command === "input") {
@@ -1003,7 +1005,8 @@ async function executeExpressionWithWasm(annotated, state, lineOffset = 0) {
1003
1005
 
1004
1006
  const wasmCompileStart = performance.now();
1005
1007
  const wasmBytes = await compileWasmArtifactFromObject(obj, {
1006
- inputEnvelopePattern: inputValue?.pattern
1008
+ inputEnvelopePattern: inputValue?.pattern,
1009
+ usePKIR: state.engine === "pkir" || state.usePKIR
1007
1010
  });
1008
1011
  const runner = await instantiateWasmArtifact(wasmBytes);
1009
1012
  const wasmCompileMs = performance.now() - wasmCompileStart;
@@ -1242,8 +1245,10 @@ async function evaluateCommand(line, state) {
1242
1245
  if (!mode) {
1243
1246
  return [`Current engine: ${state.engine}`];
1244
1247
  }
1245
- if (mode !== "wasm" && mode !== "js") {
1246
- throw new Error(`Invalid engine '${arg.trim()}'. Valid engines: wasm, js`);
1248
+ if (mode !== "wasm" && mode !== "pkir" && mode !== "js") {
1249
+ throw new Error(
1250
+ `Invalid engine '${arg.trim()}'. Valid engines: wasm, pkir, js`
1251
+ );
1247
1252
  }
1248
1253
  state.engine = mode;
1249
1254
  return [`engine set to ${mode}`];
@@ -1252,6 +1257,10 @@ async function evaluateCommand(line, state) {
1252
1257
  state.engine = "wasm";
1253
1258
  return ["engine set to wasm"];
1254
1259
  }
1260
+ case "pkir": {
1261
+ state.engine = "pkir";
1262
+ return ["engine set to pkir"];
1263
+ }
1255
1264
  case "js": {
1256
1265
  state.engine = "js";
1257
1266
  return ["engine set to js"];
@@ -1290,7 +1299,7 @@ async function evaluateCommand(line, state) {
1290
1299
 
1291
1300
  function helpText() {
1292
1301
  return [
1293
- ":engine [wasm|js] display or switch evaluation engine (wasm or js)",
1302
+ ":engine [wasm|pkir|js] display or switch evaluation engine",
1294
1303
  ":rel name show relation definition",
1295
1304
  ":rels list relation aliases",
1296
1305
  ":codec load file load a codec module from file (e.g. :codec load codecs/int.mjs)",
@@ -1316,15 +1325,18 @@ function promptForState(state) {
1316
1325
  }
1317
1326
 
1318
1327
  function cliUsage() {
1319
- console.log("Usage: k-repl [options]");
1328
+ console.log("Start the interactive k interpreter.");
1329
+ console.log("Type :help inside the REPL for commands.");
1320
1330
  console.log("");
1321
- console.log("Start the interactive k interpreter. Type :help inside the REPL for commands.");
1331
+ console.log("Usage: k-repl [options]");
1322
1332
  console.log("");
1323
1333
  console.log("Options:");
1324
- console.log(" --engine=<wasm|js> Select evaluation engine (default: wasm)");
1325
- console.log(" --js Shortcut for --engine=js");
1326
- console.log(" --wasm Shortcut for --engine=wasm");
1327
- console.log(" -h, --help Show this help.");
1334
+ console.log(" --engine=<wasm|pkir|js> Select engine: wasm, pkir, js");
1335
+ console.log(" (default: wasm).");
1336
+ console.log(" --pkir Shortcut for --engine=pkir.");
1337
+ console.log(" --wasm Shortcut for --engine=wasm.");
1338
+ console.log(" --js Shortcut for --engine=js.");
1339
+ console.log(" -h, --help Show this help.");
1328
1340
  }
1329
1341
 
1330
1342
  function startRepl(options = {}) {
@@ -1428,18 +1440,24 @@ if (isMainEntrypoint()) {
1428
1440
  engine = "js";
1429
1441
  } else if (arg === "--wasm") {
1430
1442
  engine = "wasm";
1443
+ } else if (arg === "--pkir") {
1444
+ engine = "pkir";
1431
1445
  } else if (arg.startsWith("--engine=")) {
1432
1446
  const val = arg.slice("--engine=".length).toLowerCase();
1433
- if (val !== "wasm" && val !== "js") {
1434
- console.error(`Invalid engine '${val}'. Valid engines: wasm, js`);
1447
+ if (val !== "wasm" && val !== "pkir" && val !== "js") {
1448
+ console.error(
1449
+ `Invalid engine '${val}'. Valid engines: wasm, pkir, js`
1450
+ );
1435
1451
  exit(1);
1436
1452
  }
1437
1453
  engine = val;
1438
1454
  } else if (arg === "--engine") {
1439
1455
  i++;
1440
1456
  const val = (args[i] || "").toLowerCase();
1441
- if (val !== "wasm" && val !== "js") {
1442
- console.error(`Invalid engine '${val}'. Valid engines: wasm, js`);
1457
+ if (val !== "wasm" && val !== "pkir" && val !== "js") {
1458
+ console.error(
1459
+ `Invalid engine '${val}'. Valid engines: wasm, pkir, js`
1460
+ );
1443
1461
  exit(1);
1444
1462
  }
1445
1463
  engine = val;
@@ -284,6 +284,17 @@ const htmlContent = `<!DOCTYPE html>
284
284
  border-color: rgba(240, 180, 41, 0.7);
285
285
  }
286
286
 
287
+ .engine-badge.pkir-mode {
288
+ background: rgba(187, 128, 255, 0.15);
289
+ color: #d2a8ff;
290
+ border-color: rgba(187, 128, 255, 0.4);
291
+ }
292
+
293
+ .engine-badge.pkir-mode:hover {
294
+ background: rgba(187, 128, 255, 0.3);
295
+ border-color: rgba(187, 128, 255, 0.7);
296
+ }
297
+
287
298
  .status-indicator {
288
299
  display: flex;
289
300
  align-items: center;
@@ -1644,7 +1655,7 @@ const htmlContent = `<!DOCTYPE html>
1644
1655
  <div class="nav-left">
1645
1656
  <div class="brand">
1646
1657
  <span class="brand-name">k repl</span>
1647
- <span id="engine-badge" class="engine-badge" title="Click to toggle engine (wasm/js)">wasm-in-process</span>
1658
+ <span id="engine-badge" class="engine-badge" title="Click to toggle engine (wasm/pkir/js)">wasm-in-process</span>
1648
1659
  </div>
1649
1660
  <div class="status-indicator">
1650
1661
  <span id="status-dot" class="status-dot ready"></span>
@@ -1673,6 +1684,7 @@ const htmlContent = `<!DOCTYPE html>
1673
1684
  <option value="expr::rels">:rels (list relations)</option>
1674
1685
  <option value="expr::engine">:engine (show engine)</option>
1675
1686
  <option value="expr::engine wasm">:engine wasm</option>
1687
+ <option value="expr::engine pkir">:engine pkir</option>
1676
1688
  <option value="expr::engine js">:engine js</option>
1677
1689
  </optgroup>
1678
1690
  <optgroup label="Codecs &amp; Format Adapters">
@@ -1867,7 +1879,7 @@ const htmlContent = `<!DOCTYPE html>
1867
1879
  <div class="help-section">
1868
1880
  <h4>REPL Commands</h4>
1869
1881
  <table class="help-table">
1870
- <tr><td><code>:engine [wasm|js]</code></td><td>Display or switch evaluation engine (<code>wasm</code> or <code>js</code>)</td></tr>
1882
+ <tr><td><code>:engine [wasm|pkir|js]</code></td><td>Display or switch evaluation engine (<code>wasm</code>, <code>pkir</code>, or <code>js</code>)</td></tr>
1871
1883
  <tr><td><code>:load &lt;file&gt;</code></td><td>Load a <code>.k</code> source or <code>.klib</code> library into current state</td></tr>
1872
1884
  <tr><td><code>:rel &lt;name&gt;</code></td><td>Display definition of relation <code>&lt;name&gt;</code></td></tr>
1873
1885
  <tr><td><code>:rels</code></td><td>List all relation aliases</td></tr>
@@ -54,7 +54,7 @@ function printHelp() {
54
54
 
55
55
  IEEE 754 Floating-Point Arithmetic Benchmark Harness.
56
56
  Benchmarks add, sub, mul, and div on Examples/ieee.k across
57
- Native JS, kVM interpreter, LLVM, and Wasm backends.
57
+ Native JS, kVM interpreter, LLVM, Wasm, and PKIR backends.
58
58
 
59
59
  Options:
60
60
  --opt=<levels>, --opts=<levels>
@@ -65,16 +65,20 @@ Options:
65
65
  Run full LLVM optimization sweep (-O0, -O1, -O2).
66
66
  --wasm-in-process
67
67
  Run WebAssembly in-process instead of persistent subprocess runner.
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 'wasm'.
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
  VALUES
@@ -116,11 +120,52 @@ 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 llvmOnly = process.env.LLVM_ONLY === "1" || backendsEnv === "llvm";
120
- const wasmOnly = process.env.WASM_ONLY === "1" || backendsEnv === "wasm";
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");
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
+ );
141
+ const wasmOnly = !pkirOnly && !llvmPkirOnly && !llvmOnly && (
142
+ process.env.WASM_ONLY === "1"
143
+ || (backendsList && backendsList.length === 1 &&
144
+ backendsList[0] === "wasm")
145
+ );
146
+ const runLLVM = backendsList
147
+ ? backendsList.includes("llvm")
148
+ : !wasmOnly && !pkirOnly && !llvmPkirOnly;
149
+ const runLLVMPKIR = backendsList
150
+ ? backendsList.includes("llvm-pkir")
151
+ : !wasmOnly && !pkirOnly && !llvmOnly;
152
+ const runWasm = backendsList
153
+ ? backendsList.includes("wasm")
154
+ : !llvmOnly && !pkirOnly && !llvmPkirOnly;
155
+ const runPKIR = backendsList
156
+ ? backendsList.includes("pkir")
157
+ : !llvmOnly && !wasmOnly && !llvmPkirOnly;
158
+ const runBaselines = !process.env.BACKENDS_ONLY
159
+ && (backendsList == null || backendsList.includes("kvm") ||
160
+ backendsList.includes("js"))
161
+ && !llvmOnly && !wasmOnly && !pkirOnly && !llvmPkirOnly;
162
+
163
+ function llvmLaneNameStr() {
164
+ return llvmLaneName();
165
+ }
166
+ function llvmPkirLaneNameStr() {
167
+ return llvmLaneName().replace("LLVM", "LLVM-PKIR");
168
+ }
124
169
 
125
170
  const ops = ["add", "sub", "mul", "div"];
126
171
  const values = csvEnv("VALUES", "0.5,-4,0,Infinity,-Infinity,NaN");
@@ -172,6 +217,9 @@ function wasmLaneName() {
172
217
  const mode = wasmPipe ? "persistent" : "in-process";
173
218
  return `WebAssembly (${mode})`;
174
219
  }
220
+ function pkirLaneName() {
221
+ return "PKIR (Wasm persistent)";
222
+ }
175
223
 
176
224
  console.log("==> Initializing state and loading @fraczak/k/Examples/ieee.k");
177
225
  const state = createState();
@@ -206,7 +254,8 @@ const perfKSource = generatePerfK(values);
206
254
  await evaluateInput(perfKSource, state);
207
255
 
208
256
  const ieeeRaw = fs.readFileSync(ieeePath, "utf8");
209
- // Exclude trailing unassigned expression and comment block to ensure clean parse
257
+ // Exclude trailing unassigned expression and comment block to ensure
258
+ // clean parse.
210
259
  const endIdx = ieeeRaw.indexOf("\n()\n");
211
260
  const ieeeBase = endIdx !== -1 ? ieeeRaw.slice(0, endIdx) : ieeeRaw;
212
261
  const fullSource = `${ieeeBase}\n${perfKSource}\nperf_ieee`;
@@ -249,7 +298,8 @@ if (runLLVM) {
249
298
  cacheDir,
250
299
  sourceLabel: "@fraczak/k/Examples/ieee.k#perf_ieee",
251
300
  runtimeMode: llvmRuntimeMode,
252
- clangOpt: opt
301
+ clangOpt: opt,
302
+ usePKIR: false
253
303
  });
254
304
  const singleCase = [{
255
305
  op: "perf_ieee",
@@ -266,6 +316,37 @@ if (runLLVM) {
266
316
  codes.load(state.codes);
267
317
  }
268
318
 
319
+ // Setup LLVM-PKIR
320
+ const llvmPkirCasesByOpt = new Map();
321
+ if (runLLVMPKIR) {
322
+ for (const opt of llvmOptLevels) {
323
+ console.log(`==> Compiling LLVM-PKIR executable (${opt})...`);
324
+ const llvm = tryCompileCase({
325
+ object: relation.object,
326
+ relationName: relation.relationName,
327
+ relHash: relation.relHash,
328
+ inputPattern,
329
+ cacheDir,
330
+ sourceLabel: "@fraczak/k/Examples/ieee.k#perf_ieee",
331
+ runtimeMode: llvmRuntimeMode,
332
+ clangOpt: opt,
333
+ usePKIR: true
334
+ });
335
+ const singleCase = [{
336
+ op: "perf_ieee",
337
+ label: "params",
338
+ inputVal,
339
+ inputWire,
340
+ inputPattern,
341
+ expected,
342
+ llvm
343
+ }];
344
+ llvmPkirCasesByOpt.set(opt, singleCase);
345
+ printCompileFailures(singleCase);
346
+ }
347
+ codes.load(state.codes);
348
+ }
349
+
269
350
  // Setup Wasm
270
351
  let wasmExports = null;
271
352
  let wasmPtrIn = null;
@@ -310,6 +391,28 @@ if (runWasm) {
310
391
  }
311
392
  }
312
393
 
394
+ // Setup PKIR
395
+ let pkirExePath = null;
396
+ let pkirCompileError = null;
397
+ if (runPKIR) {
398
+ console.log("==> Compiling PKIR executable (persistent runner)...");
399
+ try {
400
+ const pkirPath = path.join(cacheDir, "perf_ieee_pkir.wasm");
401
+ pkirExePath = path.join(cacheDir, "perf_ieee_pkir.exe");
402
+ const wasmBuf = await compileWasmArtifactFromObject(relation.object, {
403
+ entry: "__main__",
404
+ usePKIR: true
405
+ });
406
+ fs.writeFileSync(pkirPath, wasmBuf);
407
+ const runBin = fileURLToPath(import.meta.resolve("../backends/wasm/bin/k-wasm-run.mjs"));
408
+ fs.writeFileSync(pkirExePath, `#!/bin/sh\nexec node "${runBin}" "${pkirPath}" "$@"\n`, { mode: 0o755 });
409
+ } catch (error) {
410
+ pkirCompileError = error.stack || error.message || String(error);
411
+ console.log("PKIR compilation failed:");
412
+ console.log(pkirCompileError.split("\n").slice(0, 8).join("\n"));
413
+ }
414
+ }
415
+
313
416
  function printBenchmarkDescription() {
314
417
  const lanes = [];
315
418
  if (runBaselines) {
@@ -319,11 +422,28 @@ function printBenchmarkDescription() {
319
422
  }
320
423
  if (runLLVM) {
321
424
  for (const opt of llvmOptLevels) {
322
- const mode = llvmLaneName().replace(/^LLVM Executable\\s*\\((.*)\\)$/, "$1");
323
- lanes.push(llvmOptLevels.length > 1 ? `LLVM ${opt} (${mode})` : llvmLaneName());
425
+ const mode = llvmLaneName()
426
+ .replace(/^LLVM Executable\s*\((.*)\)$/, "$1");
427
+ lanes.push(
428
+ llvmOptLevels.length > 1
429
+ ? `LLVM ${opt} (${mode})`
430
+ : llvmLaneName()
431
+ );
432
+ }
433
+ }
434
+ if (runLLVMPKIR) {
435
+ for (const opt of llvmOptLevels) {
436
+ const mode = llvmLaneName()
437
+ .replace(/^LLVM Executable\s*\((.*)\)$/, "$1");
438
+ lanes.push(
439
+ llvmOptLevels.length > 1
440
+ ? `LLVM-PKIR ${opt} (${mode})`
441
+ : llvmLaneName().replace("LLVM", "LLVM-PKIR")
442
+ );
324
443
  }
325
444
  }
326
445
  if (runWasm) lanes.push(wasmLaneName());
446
+ if (runPKIR) lanes.push(pkirLaneName());
327
447
 
328
448
  console.log("==> Benchmark description");
329
449
  console.log(" source: @fraczak/k/Examples/ieee.k#perf_ieee (single program)");
@@ -336,6 +456,11 @@ function printBenchmarkDescription() {
336
456
  console.log(` llvm clang opts: ${llvmOptLevels.join(", ")}`);
337
457
  console.log(` llvm cache dir: ${cacheDir}`);
338
458
  }
459
+ if (runLLVMPKIR) {
460
+ console.log(` llvm-pkir warmup iterations: ${llvmWarmupIterations}`);
461
+ console.log(` llvm-pkir runtime mode: ${llvmRuntimeMode}`);
462
+ console.log(` llvm-pkir clang opts: ${llvmOptLevels.join(", ")}`);
463
+ }
339
464
  if (runWasm) {
340
465
  console.log(` wasm runner mode: ${wasmPipe ? "persistent pipe" : "in-process"}`);
341
466
  console.log(` wasm warmup iterations: ${wasmWarmupIterations}`);
@@ -343,6 +468,9 @@ function printBenchmarkDescription() {
343
468
  console.log(` wasm arena reset: ${wasmReset ? "yes" : "no"}`);
344
469
  }
345
470
  }
471
+ if (runPKIR) {
472
+ console.log(` pkir warmup iterations: ${wasmWarmupIterations}`);
473
+ }
346
474
  console.log(` iterations: ${iterations}`);
347
475
  console.log(` benchmark lanes: ${lanes.join("; ")}`);
348
476
  console.log(" conformance: all outputs are compared to native expected values");
@@ -403,6 +531,28 @@ if (runLLVM) {
403
531
  }
404
532
  }
405
533
 
534
+ // 2b. LLVM-PKIR lanes
535
+ const llvmPkirResults = new Map();
536
+ if (runLLVMPKIR) {
537
+ for (const opt of llvmOptLevels) {
538
+ const casesForOpt = llvmPkirCasesByOpt.get(opt);
539
+ const llvmRunner = createLLVMRunner(casesForOpt);
540
+ try {
541
+ if (llvmWarmupIterations > 0) {
542
+ console.log(`==> Warming LLVM-PKIR (${opt}) executable (${llvmWarmupIterations} iterations)...`);
543
+ await llvmRunner.run(llvmWarmupIterations);
544
+ }
545
+ console.log(`==> Running LLVM-PKIR (${opt}) (${iterations} iterations)...`);
546
+ const result = await llvmRunner.run(iterations);
547
+ llvmPkirResults.set(opt, result);
548
+ } catch (err) {
549
+ console.log(`==> LLVM-PKIR (${opt}) execution failed: ${err.message?.split("\n")[0]}`);
550
+ } finally {
551
+ llvmRunner.close();
552
+ }
553
+ }
554
+ }
555
+
406
556
  // 3. Wasm lane
407
557
  let wasmResult = null;
408
558
  if (runWasm) {
@@ -445,6 +595,28 @@ if (runWasm) {
445
595
  }
446
596
  }
447
597
 
598
+ // 4. PKIR lane
599
+ let pkirResult = null;
600
+ if (runPKIR) {
601
+ if (pkirExePath) {
602
+ const server = new PersistentExecutable(pkirExePath);
603
+ try {
604
+ if (wasmWarmupIterations > 0) {
605
+ console.log(`==> Warming PKIR (${wasmWarmupIterations} iterations)...`);
606
+ for (let i = 0; i < wasmWarmupIterations; i++) {
607
+ await server.request(inputWire);
608
+ }
609
+ }
610
+ console.log(`==> Running PKIR (${iterations} iterations)...`);
611
+ pkirResult = await runTimedIterationsAsync(iterations, async () => {
612
+ await server.request(inputWire);
613
+ });
614
+ } finally {
615
+ server.close();
616
+ }
617
+ }
618
+ }
619
+
448
620
  console.log("\n=================== IEEE BENCHMARK RESULTS ===================");
449
621
  console.log(`Program calls per iteration: 1 (${totalOps} IEEE operations evaluated inside)`);
450
622
  console.log(`Total requested program calls: ${iterations} (${iterations * totalOps} equivalent IEEE operations)`);
@@ -457,16 +629,38 @@ if (runBaselines) {
457
629
  }
458
630
  if (runLLVM) {
459
631
  for (const opt of llvmOptLevels) {
632
+ const mode = llvmLaneName()
633
+ .replace(/^LLVM Executable\s*\((.*)\)$/, "$1");
460
634
  const title = llvmOptLevels.length > 1
461
- ? `LLVM ${opt} (${llvmLaneName().replace(/^LLVM Executable\\s*\\((.*)\\)$/, "$1")})`
635
+ ? `LLVM ${opt} (${mode})`
462
636
  : llvmLaneName();
463
- console.log(`${laneIndex++}. ${title.padEnd(29)} ${formatTiming(llvmResults.get(opt))}`);
637
+ console.log(
638
+ `${laneIndex++}. ${title.padEnd(29)} ` +
639
+ `${formatTiming(llvmResults.get(opt))}`
640
+ );
641
+ }
642
+ }
643
+ if (runLLVMPKIR) {
644
+ for (const opt of llvmOptLevels) {
645
+ const mode = llvmLaneName()
646
+ .replace(/^LLVM Executable\s*\((.*)\)$/, "$1");
647
+ const title = llvmOptLevels.length > 1
648
+ ? `LLVM-PKIR ${opt} (${mode})`
649
+ : llvmLaneName().replace("LLVM", "LLVM-PKIR");
650
+ console.log(
651
+ `${laneIndex++}. ${title.padEnd(29)} ` +
652
+ `${formatTiming(llvmPkirResults.get(opt))}`
653
+ );
464
654
  }
465
655
  }
466
656
  if (runWasm) {
467
657
  const timingStr = wasmResult ? formatTiming(wasmResult) : "compile failed";
468
658
  console.log(`${laneIndex++}. ${wasmLaneName().padEnd(29)} ${timingStr}`);
469
659
  }
660
+ if (runPKIR) {
661
+ const timingStr = pkirResult ? formatTiming(pkirResult) : "compile failed";
662
+ console.log(`${laneIndex++}. ${pkirLaneName().padEnd(29)} ${timingStr}`);
663
+ }
470
664
  console.log("==============================================================\n");
471
665
 
472
666
  // Conformance Validation
@@ -504,6 +698,28 @@ if (runLLVM) {
504
698
  }
505
699
  }
506
700
 
701
+ if (runLLVMPKIR) {
702
+ for (const opt of llvmOptLevels) {
703
+ const casesForOpt = llvmPkirCasesByOpt.get(opt);
704
+ const tc = casesForOpt[0];
705
+ if (tc.llvm.status !== "ok") {
706
+ tc.llvmConformance = "compile-failed";
707
+ } else {
708
+ try {
709
+ const outputWire = await runExecutable(tc.llvm.exePath, tc.inputWire);
710
+ const actual = decodeWire(outputWire).value;
711
+ assert.deepEqual(toPlainObject(actual), toPlainObject(expected));
712
+ tc.llvmConformance = "ok";
713
+ } catch (error) {
714
+ tc.llvmConformance = "failed";
715
+ tc.llvm.error = error.stack || error.message || String(error);
716
+ console.log(`LLVM-PKIR (${opt}) conformance failure:`);
717
+ console.log(tc.llvm.error.split("\n").slice(0, 8).join("\n"));
718
+ }
719
+ }
720
+ }
721
+ }
722
+
507
723
  let wasmOk = false;
508
724
  if (runWasm) {
509
725
  if (wasmPipe) {
@@ -540,6 +756,22 @@ if (runWasm) {
540
756
  }
541
757
  }
542
758
 
759
+ let pkirOk = false;
760
+ if (runPKIR) {
761
+ if (!pkirExePath) {
762
+ console.log("PKIR conformance failure: executable was not compiled");
763
+ } else {
764
+ try {
765
+ const outputWire = await runExecutable(pkirExePath, inputWire);
766
+ const actual = decodeWire(outputWire).value;
767
+ assert.deepEqual(toPlainObject(actual), toPlainObject(expected));
768
+ pkirOk = true;
769
+ } catch (error) {
770
+ console.log("PKIR conformance failure:", error.message || error);
771
+ }
772
+ }
773
+ }
774
+
543
775
  const validationSummary = [];
544
776
  if (runBaselines && kvmOk) validationSummary.push(`kVM (all ${totalOps} ops)`);
545
777
  if (runLLVM) {
@@ -549,9 +781,21 @@ if (runLLVM) {
549
781
  validationSummary.push(`LLVM ${opt} (${tc.llvmConformance === "ok" ? `all ${totalOps} ops` : tc.llvmConformance})`);
550
782
  }
551
783
  }
784
+ if (runLLVMPKIR) {
785
+ for (const opt of llvmOptLevels) {
786
+ const casesForOpt = llvmPkirCasesByOpt.get(opt);
787
+ const tc = casesForOpt[0];
788
+ validationSummary.push(
789
+ `LLVM-PKIR ${opt} (${tc.llvmConformance === "ok" ? `all ${totalOps} ops` : tc.llvmConformance})`
790
+ );
791
+ }
792
+ }
552
793
  if (runWasm) {
553
794
  validationSummary.push(`Wasm (${wasmOk ? `all ${totalOps} ops` : "failed"})`);
554
795
  }
796
+ if (runPKIR) {
797
+ validationSummary.push(`PKIR (${pkirOk ? `all ${totalOps} ops` : "failed"})`);
798
+ }
555
799
 
556
800
  console.log(`Conformance validation: ${validationSummary.join(", ")} match expected values.`);
557
801
  if (runLLVM) {
@@ -560,4 +804,11 @@ if (runLLVM) {
560
804
  if (shouldStrictFail(casesForOpt)) process.exitCode = 1;
561
805
  }
562
806
  }
807
+ if (runLLVMPKIR) {
808
+ for (const opt of llvmOptLevels) {
809
+ const casesForOpt = llvmPkirCasesByOpt.get(opt);
810
+ if (shouldStrictFail(casesForOpt)) process.exitCode = 1;
811
+ }
812
+ }
563
813
  if (runWasm && !wasmOk) process.exitCode = 1;
814
+ if (runPKIR && !pkirOk) process.exitCode = 1;