@fraczak/k 7.0.2 → 7.2.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 (71) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/DOCS/CODECS.md +37 -2
  3. package/DOCS/CODEC_ARCHITECTURE.md +12 -5
  4. package/DOCS/FILE_FORMATS.md +238 -15
  5. package/DOCS/POLYMORPHIC_IR.md +104 -0
  6. package/DOCS/REPL.md +11 -3
  7. package/DOCS/WEB_REPL_REQUIREMENTS.md +1 -1
  8. package/JOURNAL.md +288 -0
  9. package/README.md +12 -3
  10. package/Value.mjs +28 -3
  11. package/access-table.mjs +343 -0
  12. package/backend-api.mjs +23 -1
  13. package/backends/llvm/README.md +20 -21
  14. package/backends/llvm/bin/k-llvm-build.mjs +105 -32
  15. package/backends/llvm/bin/k-llvm-compile.mjs +63 -24
  16. package/backends/llvm/bin/k-llvm.mjs +292 -0
  17. package/backends/llvm/runtime/krt.c +8 -0
  18. package/backends/llvm/runtime/krt.h +1 -0
  19. package/backends/llvm/src/cli.mjs +95 -17
  20. package/backends/llvm/src/executable.mjs +128 -11
  21. package/backends/llvm/src/kvm2llvm.mjs +10 -5
  22. package/backends/llvm/src/llvm.mjs +121 -9
  23. package/backends/llvm/tests/integration.sh +11 -11
  24. package/backends/llvm/{bin → tests}/k-llvm-run.mjs +48 -22
  25. package/backends/llvm/tests/perf-support.mjs +24 -3
  26. package/backends/llvm/tests/test-llvm.mjs +22 -1
  27. package/backends/wasm/bin/k-wasm-compile.mjs +46 -20
  28. package/backends/wasm/bin/k-wasm-run.mjs +61 -20
  29. package/backends/wasm/bin/k-wasm.mjs +47 -13
  30. package/backends/wasm/src/cli.mjs +141 -19
  31. package/backends/wasm/src/wasm.mjs +130 -23
  32. package/browser/repl-browser.mjs +16 -5
  33. package/browser/tree-view.mjs +47 -8
  34. package/browser/vfs-data.mjs +6 -5
  35. package/codecs/README.md +1 -0
  36. package/codecs/djson.mjs +270 -0
  37. package/codecs/int.mjs +2 -1
  38. package/codecs/json.mjs +7 -2
  39. package/codecs/k-parse.mjs +36 -9
  40. package/codecs/k-print.mjs +26 -6
  41. package/codecs/runtime/codec-sdk.mjs +24 -6
  42. package/codecs/runtime/pattern-json.mjs +80 -1
  43. package/codecs/runtime/prefix-codec.mjs +629 -67
  44. package/codecs/runtime/show-value.mjs +93 -51
  45. package/codecs/runtime/unicode-string.mjs +29 -6
  46. package/codecs/show.mjs +24 -6
  47. package/codecs/utf16.mjs +2 -1
  48. package/k.mjs +56 -16
  49. package/kir.mjs +58 -19
  50. package/kvm.mjs +49 -12
  51. package/objects/compile.mjs +37 -23
  52. package/objects/decompile.mjs +40 -10
  53. package/objects/extract-aliases.mjs +44 -14
  54. package/objects/inspect.mjs +19 -14
  55. package/objects/validate.mjs +18 -11
  56. package/package.json +11 -3
  57. package/pkir.mjs +772 -0
  58. package/repl.mjs +92 -23
  59. package/scripts/build-repl-html.mjs +22 -3
  60. package/scripts/perf-ieee.mjs +264 -13
  61. package/scripts/perf-int.mjs +315 -22
  62. package/scripts/perf-multiplication.mjs +58 -7
  63. package/scripts/perf-poly.mjs +306 -24
  64. package/scripts/run-tests.mjs +3 -0
  65. package/tests/test-access-table.mjs +213 -0
  66. package/tests/test-codecs.mjs +168 -0
  67. package/tests/test-pkir.mjs +530 -0
  68. package/tests/test-polymorphic-tdd.mjs +196 -0
  69. package/tests/test-repl-html.mjs +16 -2
  70. package/tests/test-repl.mjs +63 -1
  71. package/backends/llvm/bin/k-llvm-jit.mjs +0 -160
@@ -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
@@ -72,19 +72,19 @@ node ./bin/k-llvm-compile.mjs --help | grep -q 'Compile a k .ko/.klib object'
72
72
 
73
73
  node ./bin/k-llvm-build.mjs --help | grep -q 'binary k pattern+value envelope'
74
74
 
75
- node ./bin/k-llvm-jit.mjs --help | grep -q 'stdin binary envelope'
75
+ node ./bin/k-llvm.mjs --help | grep -q 'binary pattern+value wire stream'
76
76
 
77
- node ./bin/k-llvm-run.mjs --help | grep -q 'Compile and execute a k .ko/.klib object'
77
+ node ./tests/k-llvm-run.mjs --help | grep -q 'Compile and execute a k .ko/.klib object'
78
78
 
79
79
  node ../../objects/compile.mjs '.x' "$TMP_DIR/run.ko"
80
80
  printf '{"x":"left","y":"right"}' > "$TMP_DIR/run-input.kv"
81
81
  printf '"left"' > "$TMP_DIR/run-expected.kv"
82
- node ./bin/k-llvm-run.mjs --expect "$TMP_DIR/run-expected.kv" "$TMP_DIR/run.ko" "$TMP_DIR/run-input.kv" | grep -qx 'OK'
83
- node ./bin/k-llvm-run.mjs "$TMP_DIR/run.ko" "$TMP_DIR/run-input.kv" | grep -qx '"left"'
82
+ node ./tests/k-llvm-run.mjs --expect "$TMP_DIR/run-expected.kv" "$TMP_DIR/run.ko" "$TMP_DIR/run-input.kv" | grep -qx 'OK'
83
+ node ./tests/k-llvm-run.mjs "$TMP_DIR/run.ko" "$TMP_DIR/run-input.kv" | grep -qx '"left"'
84
84
 
85
85
  node ../../objects/compile.mjs '{ .x fieldA, .y fieldB }' "$TMP_DIR/run-product.ko"
86
86
  printf '{"x":"left","y":"right"}' > "$TMP_DIR/run-product-input.kv"
87
- node ./bin/k-llvm-run.mjs "$TMP_DIR/run-product.ko" "$TMP_DIR/run-product-input.kv" | grep -qx '{"fieldA":"left","fieldB":"right"}'
87
+ node ./tests/k-llvm-run.mjs "$TMP_DIR/run-product.ko" "$TMP_DIR/run-product-input.kv" | grep -qx '{"fieldA":"left","fieldB":"right"}'
88
88
 
89
89
  node ./bin/k-llvm-build.mjs '?{<{}=X0 left, ...> x, <X0 right, ...> y} .x' -o "$TMP_DIR/run-exe"
90
90
  printf '{"x":"left","y":"right"}' \
@@ -130,31 +130,31 @@ printf '987654321' \
130
130
 
131
131
  printf '{"x":"left","y":"right"}' \
132
132
  | node ../../codecs/k-parse.mjs \
133
- | node ./bin/k-llvm-jit.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/run.ko" \
133
+ | node ./bin/k-llvm.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/run.ko" \
134
134
  | node ../../codecs/k-print.mjs \
135
135
  | grep -qx '"left"'
136
136
 
137
137
  node --input-type=module -e "import { stdout } from 'node:process'; import { encodeToWire } from '../../codecs/runtime/prefix-codec.mjs'; import { Value } from '../../Value.mjs'; const pattern = [[\"open-union\",[[\"left\",1],[\"right\",1]]],[\"closed-product\",[]]]; stdout.write(encodeToWire(Value.variant(\"left\", Value.product({}), pattern), pattern));" \
138
- | node ./bin/k-llvm-jit.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/id.ko" \
138
+ | node ./bin/k-llvm.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/id.ko" \
139
139
  | node --input-type=module -e "import { stdin } from 'node:process'; import { decodeWire } from '../../codecs/runtime/prefix-codec.mjs'; const chunks = []; stdin.on('data', (chunk) => chunks.push(chunk)); stdin.on('end', () => console.log(JSON.stringify(decodeWire(Buffer.concat(chunks)).pattern)));" \
140
140
  | grep -Fqx '[["open-union",[["left",1],["right",1]]],["closed-product",[]]]'
141
141
 
142
142
  node ../../objects/compile.mjs '/"ह" |"😀"' "$TMP_DIR/unicode-variant.ko"
143
143
  node --input-type=module -e "import { stdout } from 'node:process'; import { encodeToWire } from '../../codecs/runtime/prefix-codec.mjs'; import { Value } from '../../Value.mjs'; const pattern = [[\"closed-union\", [[\"ह\", 1]]], [\"closed-product\", []]]; stdout.write(encodeToWire(Value.variant('ह', Value.product({}), pattern), pattern));" \
144
- | node ./bin/k-llvm-jit.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/unicode-variant.ko" \
144
+ | node ./bin/k-llvm.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/unicode-variant.ko" \
145
145
  | node ../../codecs/k-print.mjs \
146
146
  | grep -qx '"😀"'
147
147
 
148
148
  node ../../objects/compile.mjs '/"a\u0000b" |"nul-ok"' "$TMP_DIR/nul-variant.ko"
149
149
  node --input-type=module -e "import { stdout } from 'node:process'; import { encodeToWire } from '../../codecs/runtime/prefix-codec.mjs'; import { Value } from '../../Value.mjs'; const tag = 'a\u0000b'; const pattern = [[\"closed-union\", [[tag, 1]]], [\"closed-product\", []]]; stdout.write(encodeToWire(Value.variant(tag, Value.product({}), pattern), pattern));" \
150
- | node ./bin/k-llvm-jit.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/nul-variant.ko" \
150
+ | node ./bin/k-llvm.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/nul-variant.ko" \
151
151
  | node ../../codecs/k-print.mjs \
152
152
  | grep -qx '"nul-ok"'
153
153
 
154
154
  node --input-type=module -e "import { stdout } from 'node:process'; import { encodeToWire } from '../../codecs/runtime/prefix-codec.mjs'; import { Value } from '../../Value.mjs'; const labels = ['a', 'é', 'ह', '\uE000', '😀', '𠀀', 'a\u0000b']; const pattern = [[\"closed-product\", labels.map((label) => [label, 1])], [\"closed-product\", []]]; const value = Value.product(Object.fromEntries(labels.map((label) => [label, Value.product({})])), pattern); stdout.write(encodeToWire(value, pattern));" \
155
- | node ./bin/k-llvm-jit.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/id.ko" \
155
+ | node ./bin/k-llvm.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/id.ko" \
156
156
  | node --input-type=module -e "import assert from 'node:assert/strict'; import { stdin } from 'node:process'; import { decodeWire } from '../../codecs/runtime/prefix-codec.mjs'; const chunks = []; for await (const chunk of stdin) chunks.push(chunk); const labels = ['a', 'é', 'ह', '\uE000', '😀', '𠀀', 'a\u0000b']; const { pattern } = decodeWire(Buffer.concat(chunks)); assert.equal(pattern[0][0], 'closed-product'); assert.deepEqual(new Set(pattern[0][1].map(([label]) => label)), new Set(labels));"
157
157
 
158
158
  node --input-type=module -e "import { stdout } from 'node:process'; import { encodeToWire } from '../../codecs/runtime/prefix-codec.mjs'; import { Value } from '../../Value.mjs'; const labels = ['a', 'é', 'ह', '\uE000', '😀', '𠀀', 'a\u0000b']; const pattern = [[\"open-union\", labels.map((label) => [label, 1])], [\"closed-product\", []]]; stdout.write(encodeToWire(Value.variant('a\u0000b', Value.product({}), pattern), pattern));" \
159
- | node ./bin/k-llvm-jit.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/id.ko" \
159
+ | node ./bin/k-llvm.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/id.ko" \
160
160
  | node --input-type=module -e "import assert from 'node:assert/strict'; import { stdin } from 'node:process'; import { decodeWire } from '../../codecs/runtime/prefix-codec.mjs'; const chunks = []; for await (const chunk of stdin) chunks.push(chunk); const labels = ['a', 'é', 'ह', '\uE000', '😀', '𠀀', 'a\u0000b']; const { pattern, value } = decodeWire(Buffer.concat(chunks)); assert.equal(pattern[0][0], 'open-union'); assert.deepEqual(new Set(pattern[0][1].map(([label]) => label)), new Set(labels)); assert.equal(value.tag, 'a\u0000b');"
@@ -6,22 +6,35 @@ import { decodeObject } from "@fraczak/k/object.mjs";
6
6
  import { parseValue } from "@fraczak/k/valueIO.mjs";
7
7
  import { compileObjectAndRun } from "../src/executable.mjs";
8
8
 
9
- function usage(stream = console.error) {
9
+ function helpText() {
10
10
  const prog = argv[1] || "k-llvm-run.mjs";
11
- stream(`Usage: node ${prog} [options] object-file [input.kv]`);
12
- stream("Compile and execute a k .ko/.klib object through LLVM.");
13
- stream("");
14
- stream("Arguments:");
15
- stream(" object-file Input .ko or .klib file.");
16
- stream(" input.kv Input value. Reads text from stdin when omitted.");
17
- stream("");
18
- stream("Options:");
19
- stream(" --retype rel Relation to specialize. Defaults to object main.");
20
- stream(" --input-pattern value Input pattern property-list JSON, or a file containing it.");
21
- stream(" --expect value Expected output value JSON/k-value text, or a file containing it.");
22
- stream(" -h, --help Show this help.");
23
- stream("");
24
- stream("Without --expect, the runner prints the result value as compact JSON.");
11
+ return [
12
+ "Compile and execute a k .ko/.klib object through LLVM.",
13
+ "",
14
+ `Usage: ${prog} [options] object-file [input.kv]`,
15
+ "",
16
+ "Arguments:",
17
+ " object-file Input .ko or .klib file.",
18
+ " input.kv Input value. Reads text from stdin when omitted.",
19
+ "",
20
+ "Options:",
21
+ " --retype rel Relation to specialize (default: main).",
22
+ " --input-pattern value Input pattern property-list JSON or file.",
23
+ " --expect value Expected output value JSON/text or file.",
24
+ " --pkir Lower via Polymorphic K IR (PKIR).",
25
+ " -h, --help Show this help.",
26
+ "",
27
+ "Without --expect, the runner prints the result as compact JSON.",
28
+ "",
29
+ "Examples:",
30
+ ` ${prog} program.ko input.kv`,
31
+ ` ${prog} --expect expected.kv program.ko input.kv`,
32
+ ` echo '{"x":"left"}' | ${prog} program.ko`
33
+ ].join("\n");
34
+ }
35
+
36
+ function usage(stream = console.error) {
37
+ stream(helpText());
25
38
  }
26
39
 
27
40
  async function readStdinText() {
@@ -33,7 +46,9 @@ async function readStdinText() {
33
46
  }
34
47
 
35
48
  function readMaybeFile(textOrPath) {
36
- return fs.existsSync(textOrPath) ? fs.readFileSync(textOrPath, "utf8") : textOrPath;
49
+ return fs.existsSync(textOrPath)
50
+ ? fs.readFileSync(textOrPath, "utf8")
51
+ : textOrPath;
37
52
  }
38
53
 
39
54
  function readPattern(inputPattern) {
@@ -51,6 +66,7 @@ try {
51
66
  let relation = null;
52
67
  let inputPattern = null;
53
68
  let expectedText = null;
69
+ let usePKIR = false;
54
70
  while (args.length > 0 && args[0].startsWith("--")) {
55
71
  const option = args.shift();
56
72
  if (option === "--retype") {
@@ -58,10 +74,16 @@ try {
58
74
  if (!relation) throw new Error("--retype requires a relation name");
59
75
  } else if (option === "--input-pattern") {
60
76
  inputPattern = args.shift();
61
- if (!inputPattern) throw new Error("--input-pattern requires JSON or a file path");
77
+ if (!inputPattern) {
78
+ throw new Error("--input-pattern requires JSON or a file path");
79
+ }
62
80
  } else if (option === "--expect") {
63
81
  expectedText = args.shift();
64
- if (!expectedText) throw new Error("--expect requires a value or file path");
82
+ if (!expectedText) {
83
+ throw new Error("--expect requires a value or file path");
84
+ }
85
+ } else if (option === "--pkir") {
86
+ usePKIR = true;
65
87
  } else {
66
88
  throw new Error(`Unknown option: ${option}`);
67
89
  }
@@ -73,18 +95,22 @@ try {
73
95
  if (args.length > 0) throw new Error(`Unexpected argument: ${args[0]}`);
74
96
 
75
97
  const object = decodeObject(fs.readFileSync(objectPath));
76
- const inputText = inputPath == null ? await readStdinText() : fs.readFileSync(inputPath, "utf8");
98
+ const inputText = inputPath == null
99
+ ? await readStdinText()
100
+ : fs.readFileSync(inputPath, "utf8");
77
101
  const input = parseValue(inputText);
78
- const expected = expectedText == null ? null : parseValue(readMaybeFile(expectedText));
102
+ const expected = expectedText == null
103
+ ? null
104
+ : parseValue(readMaybeFile(expectedText));
79
105
  const result = compileObjectAndRun(object, {
80
106
  relation: relation || object.main,
81
107
  input,
82
108
  expected,
83
- inputPattern: readPattern(inputPattern)
109
+ inputPattern: readPattern(inputPattern),
110
+ usePKIR
84
111
  });
85
112
  stdout.write(expected == null ? result.stdout : "OK\n");
86
113
  } catch (error) {
87
114
  console.error(error.stack || error.message || String(error));
88
- usage();
89
115
  exit(1);
90
116
  }
@@ -148,7 +148,17 @@ export function wireInput(value, pattern = value.pattern) {
148
148
  };
149
149
  }
150
150
 
151
- export function compileCaseExecutable({ object, relationName, relHash, inputPattern, cacheDir, sourceLabel, runtimeMode = "fast", clangOpt = "-O3" }) {
151
+ export function compileCaseExecutable({
152
+ object,
153
+ relationName,
154
+ relHash,
155
+ inputPattern,
156
+ cacheDir,
157
+ sourceLabel,
158
+ runtimeMode = "fast",
159
+ clangOpt = "-O3",
160
+ usePKIR = false
161
+ }) {
152
162
  const key = sha256([
153
163
  "k-llvm-perf-v1",
154
164
  backendFingerprint(),
@@ -157,12 +167,23 @@ export function compileCaseExecutable({ object, relationName, relHash, inputPatt
157
167
  sourceLabel,
158
168
  relationName,
159
169
  relHash,
160
- JSON.stringify(inputPattern)
170
+ JSON.stringify(inputPattern),
171
+ usePKIR ? "pkir" : "kir"
161
172
  ].join("\0"));
162
173
  const exePath = path.join(cacheDir, `${key}.exe`);
163
174
  if (!fs.existsSync(exePath)) {
164
175
  const tmpPath = path.join(cacheDir, `${key}.${process.pid}.tmp`);
165
- compileObjectToExecutable(cloneObjectPayload(object), tmpPath, { relation: relationName, inputPattern, runtimeMode, clangOpt });
176
+ compileObjectToExecutable(
177
+ cloneObjectPayload(object),
178
+ tmpPath,
179
+ {
180
+ relation: relationName,
181
+ inputPattern,
182
+ runtimeMode,
183
+ clangOpt,
184
+ usePKIR
185
+ }
186
+ );
166
187
  fs.renameSync(tmpPath, exePath);
167
188
  }
168
189
  return exePath;