@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
@@ -1914,6 +1914,14 @@ unsigned char *k_encode_wire_as(k_pattern *pattern, k_value *value, size_t *out_
1914
1914
  return bytes;
1915
1915
  }
1916
1916
 
1917
+ unsigned char *k_encode_wire(k_value *value, size_t *out_length) {
1918
+ k_pattern *pattern = derive_pattern(value);
1919
+ if (pattern == NULL) return NULL;
1920
+ unsigned char *bytes = k_encode_wire_as(pattern, value, out_length);
1921
+ free_pattern(pattern);
1922
+ return bytes;
1923
+ }
1924
+
1917
1925
  int k_write_wire_as(FILE *out, k_pattern *pattern, k_value *value) {
1918
1926
  size_t length = 0;
1919
1927
  unsigned char *bytes = k_encode_wire_as(pattern, value, &length);
@@ -110,6 +110,7 @@ void k_wire_prefix_free(k_wire_prefix prefix);
110
110
  k_value *k_decode_wire_value_with_prefix(const unsigned char *bytes, size_t length, k_wire_prefix *prefix, k_pattern *pattern, k_rt *rt);
111
111
  unsigned char *k_encode_wire_as_with_prefix(k_wire_prefix *prefix, k_pattern *pattern, k_value *value, size_t *out_length);
112
112
  unsigned char *k_encode_wire_as(k_pattern *pattern, k_value *value, size_t *out_length);
113
+ unsigned char *k_encode_wire(k_value *value, size_t *out_length);
113
114
  int k_write_wire(FILE *out, k_value *value);
114
115
  int k_write_wire_as(FILE *out, k_pattern *pattern, k_value *value);
115
116
 
@@ -1,20 +1,73 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { compileLibrary, compileObjectBuffer, decodeObject, loadLibrary } from "@fraczak/k/object.mjs";
3
+ import {
4
+ compileLibrary,
5
+ compileObjectBuffer,
6
+ decodeObject,
7
+ loadLibrary
8
+ } from "@fraczak/k/object.mjs";
4
9
 
5
10
  export function readAll(stream) {
6
11
  return new Promise((resolve, reject) => {
7
12
  const chunks = [];
8
- stream.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
13
+ stream.on("data", (chunk) => {
14
+ chunks.push(
15
+ Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
16
+ );
17
+ });
9
18
  stream.on("end", () => resolve(Buffer.concat(chunks)));
10
19
  stream.on("error", reject);
11
20
  });
12
21
  }
13
22
 
23
+ export function loadLibraryFromPath(libPath) {
24
+ const ext = path.extname(libPath).toLowerCase();
25
+ if (ext === ".klib" || ext === ".ko") {
26
+ return loadLibrary(decodeObject(fs.readFileSync(libPath)));
27
+ }
28
+ if (ext === ".k") {
29
+ let depLibs = [];
30
+ const baseName = path.basename(libPath);
31
+ if (baseName === "poly.k") {
32
+ const dir = path.dirname(libPath);
33
+ const arithPath = path.join(dir, "arithmetics.k");
34
+ if (fs.existsSync(arithPath)) {
35
+ const arithLib = loadLibrary(
36
+ compileLibrary(fs.readFileSync(arithPath, "utf8"), {
37
+ source: arithPath
38
+ })
39
+ );
40
+ depLibs.push(arithLib);
41
+ }
42
+ }
43
+ const rawSource = fs.readFileSync(libPath, "utf8");
44
+ if (depLibs.length > 0) {
45
+ const neededExports = [
46
+ "0", "int", "inc", "dec", "nat", "zero_int?", "neg?", "GT"
47
+ ];
48
+ const preamble = buildExportPreamble(neededExports, depLibs);
49
+ const compiled = compileLibrary(preamble + rawSource, {
50
+ source: libPath,
51
+ libraries: depLibs
52
+ });
53
+ const mergedLib = loadLibrary(compiled);
54
+ for (const dep of depLibs) {
55
+ for (const [k, v] of Object.entries(dep.relAlias || {})) {
56
+ if (!mergedLib.relAlias[k]) mergedLib.relAlias[k] = v;
57
+ }
58
+ }
59
+ return mergedLib;
60
+ }
61
+ return loadLibrary(compileLibrary(rawSource, { source: libPath }));
62
+ }
63
+ throw new Error(`Unsupported library extension: ${ext}`);
64
+ }
65
+
14
66
  export function parseCompileOptions(args) {
15
67
  const libraries = [];
16
68
  const exportSpecs = [];
17
69
  let loadedLibrary = false;
70
+ let usePKIR = false;
18
71
 
19
72
  while (args.length > 0) {
20
73
  if (args[0] === "--") {
@@ -22,22 +75,26 @@ export function parseCompileOptions(args) {
22
75
  break;
23
76
  }
24
77
  if (args[0] === "--lib") {
25
- if (loadedLibrary) throw new Error("--lib may only be specified once");
78
+ if (loadedLibrary) {
79
+ throw new Error("--lib may only be specified once");
80
+ }
26
81
  args.shift();
27
82
  const libPath = args.shift();
28
83
  if (!libPath) throw new Error("--lib requires a file argument");
29
- const ext = path.extname(libPath).toLowerCase();
30
- if (ext === ".k") {
31
- libraries.push(loadLibrary(compileLibrary(fs.readFileSync(libPath, "utf8"), { source: libPath })));
32
- } else {
33
- libraries.push(loadLibrary(decodeObject(fs.readFileSync(libPath))));
34
- }
84
+ libraries.push(loadLibraryFromPath(libPath));
35
85
  loadedLibrary = true;
36
86
  } else if (args[0] === "--export") {
37
87
  args.shift();
38
88
  const spec = args.shift();
39
- if (!spec) throw new Error("--export requires a spec argument (name or libname:localname)");
89
+ if (!spec) {
90
+ throw new Error(
91
+ "--export requires a spec argument (name or libname:localname)"
92
+ );
93
+ }
40
94
  exportSpecs.push(spec);
95
+ } else if (args[0] === "--pkir") {
96
+ args.shift();
97
+ usePKIR = true;
41
98
  } else if (args[0].startsWith("--")) {
42
99
  break;
43
100
  } else {
@@ -45,7 +102,7 @@ export function parseCompileOptions(args) {
45
102
  }
46
103
  }
47
104
 
48
- return { libraries, exportSpecs };
105
+ return { libraries, exportSpecs, usePKIR };
49
106
  }
50
107
 
51
108
  export function fileInputType(filePath) {
@@ -53,6 +110,7 @@ export function fileInputType(filePath) {
53
110
  if (ext === ".ko") return "ko";
54
111
  if (ext === ".kvm") return "kvm";
55
112
  if (ext === ".klib") return "klib";
113
+ if (ext === ".pkir") return "pkir";
56
114
  return "source-file";
57
115
  }
58
116
 
@@ -69,11 +127,15 @@ export function resolveProgramInput(args, { allowStdinSource = false } = {}) {
69
127
  return { kind, path: inputArg, sourceName: inputArg };
70
128
  }
71
129
 
72
- if ([".k", ".ko", ".kvm", ".klib"].includes(ext)) {
130
+ if ([".k", ".ko", ".kvm", ".klib", ".pkir"].includes(ext)) {
73
131
  throw new Error(`Input file not found: ${inputArg}`);
74
132
  }
75
133
 
76
- return { kind: "inline-source", source: inputArg, sourceName: "<inline>" };
134
+ return {
135
+ kind: "inline-source",
136
+ source: inputArg,
137
+ sourceName: "<inline>"
138
+ };
77
139
  }
78
140
 
79
141
  export function buildExportPreamble(exports = [], libraries = []) {
@@ -94,9 +156,15 @@ export function buildExportPreamble(exports = [], libraries = []) {
94
156
  }
95
157
  const lines = [];
96
158
  for (const spec of exports) {
97
- const [libName, localName] = spec.includes(":") ? spec.split(":", 2) : [spec, spec];
159
+ const [libName, localName] = spec.includes(":")
160
+ ? spec.split(":", 2)
161
+ : [spec, spec];
98
162
  const hash = aliasMap[libName];
99
- if (!hash) throw new Error(`--export: '${libName}' not found in loaded libraries`);
163
+ if (!hash) {
164
+ throw new Error(
165
+ `--export: '${libName}' not found in loaded libraries`
166
+ );
167
+ }
100
168
  const body = hash.startsWith("@") ? hash.slice(1) : hash;
101
169
  lines.push(`${localName} = @${body};`);
102
170
  }
@@ -116,7 +184,9 @@ export async function compileProgramInputToObject(
116
184
  });
117
185
  object = decodeObject(objBuffer);
118
186
  } else if (input.kind === "stdin-source") {
119
- if (!stdin) throw new Error("stdin source requested without a stdin stream");
187
+ if (!stdin) {
188
+ throw new Error("stdin source requested without a stdin stream");
189
+ }
120
190
  const source = (await readAll(stdin)).toString("utf8");
121
191
  const preamble = buildExportPreamble(exportSpecs, libraries);
122
192
  const objBuffer = compileObjectBuffer(preamble + source, {
@@ -135,7 +205,15 @@ export async function compileProgramInputToObject(
135
205
  } else if (input.kind === "ko" || input.kind === "klib") {
136
206
  object = decodeObject(fs.readFileSync(input.path));
137
207
  } else if (input.kind === "kvm") {
138
- throw new Error("Input is already lowered to .kvm bytecode; use compileLLVMArtifactFromKVM or compileKVMToExecutable.");
208
+ throw new Error(
209
+ "Input is already lowered to .kvm bytecode; use " +
210
+ "compileLLVMArtifactFromKVM or compileKVMToExecutable."
211
+ );
212
+ } else if (input.kind === "pkir") {
213
+ throw new Error(
214
+ "Input is already lowered to .pkir IR; use " +
215
+ "compileLLVMArtifactFromPKIR or compilePKIRToExecutable."
216
+ );
139
217
  } else {
140
218
  throw new Error(`Unsupported input kind: ${input.kind}`);
141
219
  }
@@ -7,7 +7,8 @@ import { isProduct, isVariant } from "@fraczak/k/Value.mjs";
7
7
  import { exportPatternGraph } from "@fraczak/k/codecs/runtime/codec.mjs";
8
8
  import { patternToPropertyList } from "@fraczak/k/codecs/runtime/pattern-json.mjs";
9
9
  import { specializeKVM } from "@fraczak/k/kvm.mjs";
10
- import { compileObjectToLLVM } from "./llvm.mjs";
10
+ import { lowerPKIRToKVM } from "@fraczak/k/backend-api.mjs";
11
+ import { compileObjectToLLVM, compileLLVMArtifactFromPKIR } from "./llvm.mjs";
11
12
  import { compileKVMModuleToLLVM } from "./kvm2llvm.mjs";
12
13
 
13
14
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
@@ -263,6 +264,9 @@ export function stdioDriverSource({ inputPattern = null, outputPattern = null }
263
264
  "static int write_frame(k_wire_prefix *prefix, k_pattern *pattern, k_value *value) {",
264
265
  " size_t length = 0;",
265
266
  " unsigned char *payload = k_encode_wire_as_with_prefix(prefix, pattern, value, &length);",
267
+ " if (payload == NULL) {",
268
+ " payload = k_encode_wire(value, &length);",
269
+ " }",
266
270
  " if (payload == NULL || length > UINT32_MAX) {",
267
271
  " free(payload);",
268
272
  " return 0;",
@@ -376,6 +380,9 @@ export function stdioDriverSource({ inputPattern = null, outputPattern = null }
376
380
  " }",
377
381
  " size_t out_len = 0;",
378
382
  " unsigned char *out_payload = k_encode_wire_as_with_prefix(&output_prefix, &compiled_output_pattern, result.value, &out_len);",
383
+ " if (out_payload == NULL) {",
384
+ " out_payload = k_encode_wire(result.value, &out_len);",
385
+ " }",
379
386
  " if (tracing) t4 = monotonic_ns();",
380
387
  " int ok = 0;",
381
388
  " uint64_t write_start = t4;",
@@ -495,6 +502,9 @@ export function stdioDriverSource({ inputPattern = null, outputPattern = null }
495
502
  " }",
496
503
  " size_t output_length = 0;",
497
504
  " unsigned char *output_payload = k_encode_wire_as_with_prefix(&output_prefix, &compiled_output_pattern, result.value, &output_length);",
505
+ " if (output_payload == NULL) {",
506
+ " output_payload = k_encode_wire(result.value, &output_length);",
507
+ " }",
498
508
  " if (tracing) t3 = monotonic_ns();",
499
509
  " int ok = output_payload != NULL && fwrite(output_payload, 1, output_length, stdout) == output_length && fflush(stdout) == 0;",
500
510
  " if (tracing) {",
@@ -540,12 +550,25 @@ export function compileLLVMToExecutable(llvm, outputPath, { driver = stdioDriver
540
550
  }
541
551
  }
542
552
 
543
- export function compileObjectToExecutable(object, outputPath, { relation = object.main, inputPattern = null, runtimeMode = "fast", clangOpt = "-O3" } = {}) {
544
- const { inputPattern: compiledInputPattern, outputPattern, llvm } = compileObjectToLLVM(object, {
545
- relation,
546
- inputPattern: inputPattern || inputPatternForObjectRelation(object, relation),
547
- runtimeMode
548
- });
553
+ export function compileObjectToExecutable(
554
+ object,
555
+ outputPath,
556
+ {
557
+ relation = object.main,
558
+ inputPattern = null,
559
+ runtimeMode = "fast",
560
+ clangOpt = "-O3",
561
+ usePKIR = false
562
+ } = {}
563
+ ) {
564
+ const { inputPattern: compiledInputPattern, outputPattern, llvm } =
565
+ compileObjectToLLVM(object, {
566
+ relation,
567
+ inputPattern:
568
+ inputPattern || inputPatternForObjectRelation(object, relation),
569
+ runtimeMode,
570
+ usePKIR
571
+ });
549
572
  compileLLVMToExecutable(llvm, outputPath, {
550
573
  driver: stdioDriverSource({
551
574
  inputPattern: compiledInputPattern,
@@ -559,7 +582,8 @@ export function compileKVMToExecutable(kvmInput, outputPath, {
559
582
  entry = null,
560
583
  inputPattern = null,
561
584
  runtimeMode = "fast",
562
- clangOpt = "-O3"
585
+ clangOpt = "-O3",
586
+ usePKIR = false
563
587
  } = {}) {
564
588
  let kvm = kvmInput;
565
589
  if (kvm?.format === "k-vm") {
@@ -578,7 +602,8 @@ export function compileKVMToExecutable(kvmInput, outputPath, {
578
602
  relation: entryFunc,
579
603
  inputPattern: compiledInputPattern,
580
604
  outputPattern,
581
- runtimeMode
605
+ runtimeMode,
606
+ usePKIR
582
607
  });
583
608
  compileLLVMToExecutable(llvm, outputPath, {
584
609
  driver: stdioDriverSource({
@@ -589,10 +614,102 @@ export function compileKVMToExecutable(kvmInput, outputPath, {
589
614
  });
590
615
  }
591
616
 
592
- export function compileObjectAndRun(object, { relation = object.main, input, expected = null, inputPattern = null }) {
617
+ export function compilePKIRToExecutable(
618
+ pkirInput,
619
+ outputPath,
620
+ {
621
+ entry = null,
622
+ inputPattern = null,
623
+ runtimeMode = "fast",
624
+ clangOpt = "-O3"
625
+ } = {}
626
+ ) {
627
+ let kvmProgram;
628
+ let entryFunc;
629
+ if (
630
+ (pkirInput.format === "pk-ir" || pkirInput.format === "k-pkir") &&
631
+ pkirInput.functions
632
+ ) {
633
+ kvmProgram = {};
634
+ for (const [name, f] of Object.entries(pkirInput.functions)) {
635
+ kvmProgram[name] = lowerPKIRToKVM(f);
636
+ }
637
+ entryFunc =
638
+ entry ||
639
+ pkirInput.entry ||
640
+ Object.keys(kvmProgram)[0] ||
641
+ "__main__";
642
+ } else if (pkirInput.instructions) {
643
+ const kvm = lowerPKIRToKVM(pkirInput);
644
+ entryFunc = pkirInput.name || entry || "__main__";
645
+ kvmProgram = { [entryFunc]: kvm };
646
+ const scan = (insts) => {
647
+ for (const inst of insts || []) {
648
+ if (inst.op === "call" && inst.func && !kvmProgram[inst.func]) {
649
+ kvmProgram[inst.func] = kvm;
650
+ }
651
+ if (inst.branches) {
652
+ for (const b of inst.branches) scan(b.body);
653
+ }
654
+ }
655
+ };
656
+ scan(kvm.body);
657
+ } else if (pkirInput.functions) {
658
+ kvmProgram = {};
659
+ for (const [name, f] of Object.entries(pkirInput.functions)) {
660
+ kvmProgram[name] = lowerPKIRToKVM(f);
661
+ }
662
+ entryFunc =
663
+ entry ||
664
+ pkirInput.entry ||
665
+ Object.keys(kvmProgram)[0] ||
666
+ "__main__";
667
+ } else {
668
+ const kvm = lowerPKIRToKVM(pkirInput);
669
+ entryFunc = pkirInput.name || entry || "__main__";
670
+ kvmProgram = { [entryFunc]: kvm };
671
+ }
672
+ return compileKVMToExecutable(
673
+ {
674
+ format: "k-vm",
675
+ layer: "KVM",
676
+ entry: entryFunc,
677
+ functions: kvmProgram
678
+ },
679
+ outputPath,
680
+ {
681
+ entry: entryFunc,
682
+ inputPattern,
683
+ runtimeMode,
684
+ clangOpt,
685
+ usePKIR: true
686
+ }
687
+ );
688
+ }
689
+
690
+ export function compileObjectAndRun(
691
+ object,
692
+ {
693
+ relation = object.main,
694
+ input,
695
+ expected = null,
696
+ inputPattern = null,
697
+ usePKIR = false
698
+ }
699
+ ) {
593
700
  const { llvm } = compileObjectToLLVM(object, {
594
701
  relation,
595
- inputPattern: inputPattern || inputPatternForObjectValue(object, input, relation)
702
+ inputPattern:
703
+ inputPattern || inputPatternForObjectValue(object, input, relation),
704
+ usePKIR
596
705
  });
597
706
  return compileAndRunLLVM(llvm, { input, expected });
598
707
  }
708
+
709
+ export function compilePKIRAndRun(
710
+ pkirInput,
711
+ { input, expected = null, options = {} } = {}
712
+ ) {
713
+ const llvm = compileLLVMArtifactFromPKIR(pkirInput, options);
714
+ return compileAndRunLLVM(llvm, { input, expected });
715
+ }
@@ -451,12 +451,15 @@ function hasSelfTailCall(insts, funcName, isTail = true) {
451
451
  return false;
452
452
  }
453
453
 
454
- function getFieldEdgeIndex(inst, regPatterns, kvmFunc) {
454
+ function getFieldEdgeIndex(inst, regPatterns, kvmFunc, ctx = {}) {
455
455
  const srcReg = cleanReg(inst.src);
456
456
  const srcPattern = regPatterns.get(srcReg) || (srcReg === "in" ? kvmFunc.inputPattern : null);
457
457
  if (Array.isArray(srcPattern) && srcPattern.length > 0) {
458
458
  const root = srcPattern[0];
459
- if ((root[0] === "open-product" || root[0] === "closed-product") && Array.isArray(root[1])) {
459
+ const allowed = ctx.usePKIR
460
+ ? root[0] === "closed-product"
461
+ : (root[0] === "open-product" || root[0] === "closed-product");
462
+ if (allowed && Array.isArray(root[1])) {
460
463
  const idx = root[1].findIndex(([l]) => l === inst.label);
461
464
  if (idx >= 0) return idx;
462
465
  }
@@ -514,6 +517,7 @@ export function compileKVMModuleToLLVM(entryName, kvmProgram, options = {}) {
514
517
  labels,
515
518
  functionNames,
516
519
  runtimeMode,
520
+ usePKIR: Boolean(options.usePKIR),
517
521
  armIndex: 0,
518
522
  unionExprIndex: 0,
519
523
  syntheticFunctions: [],
@@ -584,6 +588,7 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
584
588
  labels: moduleCtx.labels,
585
589
  functionNames: moduleCtx.functionNames,
586
590
  runtimeMode: moduleCtx.runtimeMode,
591
+ usePKIR: moduleCtx.usePKIR,
587
592
  allocas: [],
588
593
  lines: [],
589
594
  currentBlock: "entry",
@@ -675,7 +680,7 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
675
680
  case "project_field": {
676
681
  const input = getReg(inst.src);
677
682
  nullCheck(ctx, input, failTarget);
678
- const edgeIndex = getFieldEdgeIndex(inst, regPatterns, kvmFunc);
683
+ const edgeIndex = getFieldEdgeIndex(inst, regPatterns, kvmFunc, ctx);
679
684
  const label = ctx.labelRef(inst.label);
680
685
  let val;
681
686
  if (edgeIndex >= 0 && ctx.runtimeMode !== "compact") {
@@ -865,7 +870,7 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
865
870
  const exprName = `@k_union_expr_${exprIndex}`;
866
871
  const exprFunc = {
867
872
  name: `union_expr_${exprIndex}`,
868
- inputPattern: kvmFunc.inputPattern,
873
+ inputPattern: regPatterns.get(cleanReg(inst.src)) || null,
869
874
  outputPattern: kvmFunc.outputPattern,
870
875
  body: [
871
876
  { op: "id", dest: "%v0", src: "%in" },
@@ -899,7 +904,7 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
899
904
 
900
905
  const armFunc = {
901
906
  name: `union_arm_${armIndex}`,
902
- inputPattern: kvmFunc.inputPattern,
907
+ inputPattern: regPatterns.get(cleanReg(inst.src)) || null,
903
908
  outputPattern: kvmFunc.outputPattern,
904
909
  body: branch.body
905
910
  };
@@ -7,7 +7,11 @@ import {
7
7
  objectToKVMArtifact,
8
8
  specializeKVM,
9
9
  lowerKIRToKVM,
10
- objectToKIRP
10
+ objectToKIRP,
11
+ compileRelationToPKIR,
12
+ lowerPKIRToKVM,
13
+ exportPatternGraph,
14
+ patternToPropertyList
11
15
  } from "@fraczak/k/backend-api.mjs";
12
16
  import { compileKVMModuleToLLVM } from "./kvm2llvm.mjs";
13
17
 
@@ -923,7 +927,8 @@ function emitFunctionBody(symbol, body, ctx, linkage = "", options = {}) {
923
927
  }
924
928
  const value = lowerExpr(ctx, body, input, { tail: options.tail !== false });
925
929
  if (value === false) {
926
- // The expression emitted complete control flow, including all returns.
930
+ // The expression emitted complete control flow, including all
931
+ // returns.
927
932
  } else if (value == null) {
928
933
  unsupported(ctx);
929
934
  } else {
@@ -1010,25 +1015,74 @@ export function emitLLVMModule(input, options = {}) {
1010
1015
  }
1011
1016
 
1012
1017
  export function compileObjectToLLVM(object, options = {}) {
1013
- const inputPattern = options.inputPattern ? readPattern(options.inputPattern) : null;
1018
+ const inputPattern = options.inputPattern
1019
+ ? readPattern(options.inputPattern)
1020
+ : null;
1014
1021
  const relation = options.relation || object.main;
1015
1022
  let targetObject = object;
1016
1023
  let entryInputPattern = inputPattern;
1017
1024
  let outputPattern = null;
1018
1025
 
1019
- if (inputPattern) {
1020
- const retyped = retypeObjectRelationForBackend(object, relation, inputPattern, {
1021
- source: options.source || "<k-llvm>"
1022
- });
1026
+ if (options.usePKIR) {
1027
+ const rel = object.rels?.[relation] ||
1028
+ (object.relAlias?.[relation] &&
1029
+ object.rels?.[object.relAlias[relation]]) ||
1030
+ object.rels?.[object.main];
1031
+ if (rel?.typePatternGraph) {
1032
+ const graph = rel.typePatternGraph;
1033
+ if (!entryInputPattern &&
1034
+ rel.def?.patterns?.[0] !== undefined) {
1035
+ const inId = graph.find(rel.def.patterns[0]);
1036
+ entryInputPattern =
1037
+ patternToPropertyList(exportPatternGraph(graph, inId));
1038
+ }
1039
+ if (rel.def?.patterns?.[1] !== undefined) {
1040
+ const outId = graph.find(rel.def.patterns[1]);
1041
+ outputPattern =
1042
+ patternToPropertyList(exportPatternGraph(graph, outId));
1043
+ }
1044
+ }
1045
+ } else if (inputPattern) {
1046
+ const retyped = retypeObjectRelationForBackend(
1047
+ object, relation, inputPattern, {
1048
+ source: options.source || "<k-llvm>"
1049
+ }
1050
+ );
1023
1051
  targetObject = retyped.retypedObject;
1024
1052
  entryInputPattern = retyped.inputPattern;
1025
1053
  outputPattern = retyped.outputPattern;
1026
1054
  }
1027
1055
 
1028
- const retypedKir = objectToKIRP(targetObject);
1029
- const functions = lowerKIRToKVM(retypedKir);
1056
+ let functions;
1057
+ let retypedKir = null;
1030
1058
  const entry = relation || targetObject.main || "__main__";
1031
1059
 
1060
+ if (options.usePKIR) {
1061
+ functions = {};
1062
+ for (const [name, relDef] of Object.entries(targetObject.rels || {})) {
1063
+ const pkir = compileRelationToPKIR(relDef, name);
1064
+ functions[name] = lowerPKIRToKVM(pkir);
1065
+ }
1066
+ for (const [alias, target] of Object.entries(targetObject.relAlias || {})) {
1067
+ if (functions[target] && !functions[alias]) {
1068
+ functions[alias] = functions[target];
1069
+ }
1070
+ const stripped = target.startsWith("@") ? target.slice(1) : target;
1071
+ if (functions[stripped] && !functions[alias]) {
1072
+ functions[alias] = functions[stripped];
1073
+ }
1074
+ if (functions[alias] && !functions[target]) {
1075
+ functions[target] = functions[alias];
1076
+ }
1077
+ }
1078
+ if (relation && !functions[relation] && functions[targetObject.main]) {
1079
+ functions[relation] = functions[targetObject.main];
1080
+ }
1081
+ } else {
1082
+ retypedKir = objectToKIRP(targetObject);
1083
+ functions = lowerKIRToKVM(retypedKir);
1084
+ }
1085
+
1032
1086
  const specializedKvm = {
1033
1087
  format: "k-vm",
1034
1088
  version: 1,
@@ -1065,6 +1119,63 @@ export function compileLLVMArtifactFromKVM(kvmInput, options = {}) {
1065
1119
  return compileKVMModuleToLLVM(entry, functions, options);
1066
1120
  }
1067
1121
 
1122
+ export function compileLLVMArtifactFromPKIR(pkirInput, options = {}) {
1123
+ let kvmProgram;
1124
+ let entry;
1125
+ if (
1126
+ (pkirInput.format === "pk-ir" || pkirInput.format === "k-pkir") &&
1127
+ pkirInput.functions
1128
+ ) {
1129
+ entry =
1130
+ pkirInput.entry ||
1131
+ options.entry ||
1132
+ Object.keys(pkirInput.functions)[0] ||
1133
+ "__main__";
1134
+ kvmProgram = {};
1135
+ for (const [name, f] of Object.entries(pkirInput.functions)) {
1136
+ kvmProgram[name] = lowerPKIRToKVM(f);
1137
+ }
1138
+ } else if (pkirInput.instructions) {
1139
+ const kvm = lowerPKIRToKVM(pkirInput);
1140
+ entry = pkirInput.name || options.entry || "__main__";
1141
+ kvmProgram = { [entry]: kvm };
1142
+ const scan = (insts) => {
1143
+ for (const inst of insts || []) {
1144
+ if (inst.op === "call" && inst.func && !kvmProgram[inst.func]) {
1145
+ kvmProgram[inst.func] = kvm;
1146
+ }
1147
+ if (inst.branches) {
1148
+ for (const b of inst.branches) scan(b.body);
1149
+ }
1150
+ }
1151
+ };
1152
+ scan(kvm.body);
1153
+ } else if (pkirInput.functions) {
1154
+ entry =
1155
+ pkirInput.entry ||
1156
+ options.entry ||
1157
+ Object.keys(pkirInput.functions)[0] ||
1158
+ "__main__";
1159
+ kvmProgram = {};
1160
+ for (const [name, f] of Object.entries(pkirInput.functions)) {
1161
+ kvmProgram[name] = lowerPKIRToKVM(f);
1162
+ }
1163
+ } else {
1164
+ const kvm = lowerPKIRToKVM(pkirInput);
1165
+ entry = pkirInput.name || options.entry || "__main__";
1166
+ kvmProgram = { [entry]: kvm };
1167
+ }
1168
+ return compileLLVMArtifactFromKVM(
1169
+ {
1170
+ format: "k-vm",
1171
+ layer: "KVM",
1172
+ entry,
1173
+ functions: kvmProgram
1174
+ },
1175
+ options
1176
+ );
1177
+ }
1178
+
1068
1179
  export function compileLLVMArtifactFromObject(object, options = {}) {
1069
1180
  return compileObjectToLLVM(object, options);
1070
1181
  }
@@ -1080,6 +1191,7 @@ export default {
1080
1191
  compileBufferToLLVM,
1081
1192
  compileObjectToLLVM,
1082
1193
  compileLLVMArtifactFromKVM,
1194
+ compileLLVMArtifactFromPKIR,
1083
1195
  compileLLVMArtifactFromObject,
1084
1196
  emitLLVMModule,
1085
1197
  llvmIdentifier