@fraczak/k 7.0.1 → 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 +78 -0
- package/DOCS/BIG_PICTURE.md +5 -5
- package/DOCS/FILE_FORMATS.md +241 -18
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +9 -9
- package/DOCS/PATTERNS.md +3 -3
- package/DOCS/POLYMORPHIC_IR.md +104 -0
- package/DOCS/PROFILING_AND_TRACING.md +60 -72
- package/DOCS/REPL.md +3 -3
- package/DOCS/book/03-types-as-finite-automata.md +4 -4
- package/DOCS/book/06-values-in-memory.md +9 -9
- package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
- package/DOCS/book/09-compiler-architecture.md +2 -2
- package/DOCS/book/10-from-ast-to-intermediate-representation.md +2 -2
- package/DOCS/book/14-canonical-serialization.md +1 -1
- package/Examples/arithmetics.k +2 -2
- package/Examples/bnat.k +1 -1
- package/Examples/karatsuba-mult.k +2 -2
- package/JOURNAL.md +288 -0
- package/README.md +13 -10
- 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 +41 -0
- package/backends/llvm/runtime/krt.h +2 -0
- package/backends/llvm/src/cli.mjs +95 -17
- package/backends/llvm/src/executable.mjs +132 -15
- package/backends/llvm/src/kvm2llvm.mjs +50 -23
- package/backends/llvm/src/llvm.mjs +133 -20
- 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 +25 -58
- package/backends/llvm/tests/test-llvm.mjs +25 -4
- 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 +132 -24
- package/backends/wasm/tests/test-wasm.mjs +5 -5
- package/browser/repl-browser.mjs +16 -5
- package/browser/vfs-data.mjs +5 -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/core.k +8 -8
- package/harness/format.mjs +2 -2
- package/harness/registry.mjs +0 -2
- package/k.mjs +56 -16
- package/kir.mjs +200 -75
- 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 -7
- package/pkir.mjs +772 -0
- package/repl.mjs +36 -18
- package/scripts/benchmark-idempotency.mjs +12 -8
- package/scripts/benchmark.mjs +5 -9
- package/scripts/build-repl-html.mjs +16 -4
- package/scripts/perf-ieee.mjs +253 -103
- package/scripts/perf-int.mjs +585 -297
- package/scripts/perf-multiplication.mjs +66 -25
- package/scripts/perf-poly.mjs +292 -164
- package/scripts/run-tests.mjs +3 -0
- package/tests/code-derivation/pattern-envelope.mjs +4 -4
- package/tests/test-access-table.mjs +213 -0
- package/tests/test-kvm-polymorphic.mjs +2 -2
- 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/arm64/README.md +0 -103
- package/backends/arm64/bin/k-arm64-compile.mjs +0 -93
- package/backends/arm64/bin/k-arm64-run.mjs +0 -58
- package/backends/arm64/bin/k-arm64.mjs +0 -88
- package/backends/arm64/scripts/run-tests.mjs +0 -57
- package/backends/arm64/src/arm64.mjs +0 -229
- package/backends/arm64/src/cli.mjs +0 -155
- package/backends/arm64/src/kvm2arm64.mjs +0 -906
- package/backends/arm64/src/runtime.c +0 -744
- package/backends/arm64/tests/integration.sh +0 -112
- package/backends/arm64/tests/test-arm64.mjs +0 -240
- package/backends/llvm/bin/k-llvm-jit.mjs +0 -160
- package/harness/adapters/arm64.mjs +0 -105
|
@@ -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 {
|
|
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;",
|
|
@@ -292,7 +296,7 @@ export function stdioDriverSource({ inputPattern = null, outputPattern = null }
|
|
|
292
296
|
" k_wire_input_free(input);",
|
|
293
297
|
" return 2;",
|
|
294
298
|
" }",
|
|
295
|
-
" if (!
|
|
299
|
+
" if (!k_pattern_compatible(input.pattern, &compiled_input_pattern)) {",
|
|
296
300
|
" k_wire_input_free(input);",
|
|
297
301
|
" return 5;",
|
|
298
302
|
" }",
|
|
@@ -350,7 +354,7 @@ export function stdioDriverSource({ inputPattern = null, outputPattern = null }
|
|
|
350
354
|
" k_value *in_val = k_decode_wire_value_with_prefix(payload, length, &input_prefix, &compiled_input_pattern, rt);",
|
|
351
355
|
" if (in_val == 0) {",
|
|
352
356
|
" k_wire_input envelope = k_decode_wire_envelope(payload, length, rt);",
|
|
353
|
-
" if (envelope.value != 0 && envelope.pattern != 0 &&
|
|
357
|
+
" if (envelope.value != 0 && envelope.pattern != 0 && k_pattern_compatible(envelope.pattern, &compiled_input_pattern)) {",
|
|
354
358
|
" in_val = envelope.value;",
|
|
355
359
|
" } else {",
|
|
356
360
|
" k_wire_input_free(envelope);",
|
|
@@ -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;",
|
|
@@ -437,7 +444,7 @@ export function stdioDriverSource({ inputPattern = null, outputPattern = null }
|
|
|
437
444
|
" k_rt_free(input_rt);",
|
|
438
445
|
" return 2;",
|
|
439
446
|
" }",
|
|
440
|
-
" if (!
|
|
447
|
+
" if (!k_pattern_compatible(input.pattern, &compiled_input_pattern)) {",
|
|
441
448
|
" k_wire_input_free(input);",
|
|
442
449
|
" k_rt_free(run_rt);",
|
|
443
450
|
" k_rt_free(input_rt);",
|
|
@@ -478,7 +485,7 @@ export function stdioDriverSource({ inputPattern = null, outputPattern = null }
|
|
|
478
485
|
" k_rt_free(rt);",
|
|
479
486
|
" return 2;",
|
|
480
487
|
" }",
|
|
481
|
-
" if (!
|
|
488
|
+
" if (!k_pattern_compatible(input.pattern, &compiled_input_pattern)) {",
|
|
482
489
|
" k_wire_input_free(input);",
|
|
483
490
|
" k_wire_prefix_free(output_prefix);",
|
|
484
491
|
" k_rt_free(rt);",
|
|
@@ -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(
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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
|
|
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:
|
|
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
|
+
}
|
|
@@ -35,15 +35,17 @@ function alignArenaSize(size) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function cStringBytes(text) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
.
|
|
38
|
+
const buf = Buffer.from(text, "utf8");
|
|
39
|
+
let out = "";
|
|
40
|
+
for (let i = 0; i < buf.length; i++) {
|
|
41
|
+
const byte = buf[i];
|
|
42
|
+
if (byte === 10) out += "\\0A";
|
|
43
|
+
else if (byte === 34) out += "\\22";
|
|
44
|
+
else if (byte === 92) out += "\\5C";
|
|
45
|
+
else if (byte >= 32 && byte <= 126) out += String.fromCharCode(byte);
|
|
46
|
+
else out += `\\${byte.toString(16).padStart(2, "0").toUpperCase()}`;
|
|
47
|
+
}
|
|
48
|
+
return out + "\\00";
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
export function runtimeDeclarations() {
|
|
@@ -449,12 +451,15 @@ function hasSelfTailCall(insts, funcName, isTail = true) {
|
|
|
449
451
|
return false;
|
|
450
452
|
}
|
|
451
453
|
|
|
452
|
-
function getFieldEdgeIndex(inst, regPatterns, kvmFunc) {
|
|
454
|
+
function getFieldEdgeIndex(inst, regPatterns, kvmFunc, ctx = {}) {
|
|
453
455
|
const srcReg = cleanReg(inst.src);
|
|
454
456
|
const srcPattern = regPatterns.get(srcReg) || (srcReg === "in" ? kvmFunc.inputPattern : null);
|
|
455
457
|
if (Array.isArray(srcPattern) && srcPattern.length > 0) {
|
|
456
458
|
const root = srcPattern[0];
|
|
457
|
-
|
|
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])) {
|
|
458
463
|
const idx = root[1].findIndex(([l]) => l === inst.label);
|
|
459
464
|
if (idx >= 0) return idx;
|
|
460
465
|
}
|
|
@@ -512,6 +517,7 @@ export function compileKVMModuleToLLVM(entryName, kvmProgram, options = {}) {
|
|
|
512
517
|
labels,
|
|
513
518
|
functionNames,
|
|
514
519
|
runtimeMode,
|
|
520
|
+
usePKIR: Boolean(options.usePKIR),
|
|
515
521
|
armIndex: 0,
|
|
516
522
|
unionExprIndex: 0,
|
|
517
523
|
syntheticFunctions: [],
|
|
@@ -551,8 +557,7 @@ export function compileKVMModuleToLLVM(entryName, kvmProgram, options = {}) {
|
|
|
551
557
|
const payload = JSON.stringify({
|
|
552
558
|
format: ARTIFACT_FORMAT,
|
|
553
559
|
version: ARTIFACT_VERSION,
|
|
554
|
-
relation
|
|
555
|
-
kvm: kvmProgram
|
|
560
|
+
relation
|
|
556
561
|
});
|
|
557
562
|
const payloadBytes = Buffer.byteLength(payload, "utf8") + 1;
|
|
558
563
|
const symbol = llvmIdentifier(options.symbol || relation);
|
|
@@ -583,6 +588,7 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
|
|
|
583
588
|
labels: moduleCtx.labels,
|
|
584
589
|
functionNames: moduleCtx.functionNames,
|
|
585
590
|
runtimeMode: moduleCtx.runtimeMode,
|
|
591
|
+
usePKIR: moduleCtx.usePKIR,
|
|
586
592
|
allocas: [],
|
|
587
593
|
lines: [],
|
|
588
594
|
currentBlock: "entry",
|
|
@@ -674,7 +680,7 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
|
|
|
674
680
|
case "project_field": {
|
|
675
681
|
const input = getReg(inst.src);
|
|
676
682
|
nullCheck(ctx, input, failTarget);
|
|
677
|
-
const edgeIndex = getFieldEdgeIndex(inst, regPatterns, kvmFunc);
|
|
683
|
+
const edgeIndex = getFieldEdgeIndex(inst, regPatterns, kvmFunc, ctx);
|
|
678
684
|
const label = ctx.labelRef(inst.label);
|
|
679
685
|
let val;
|
|
680
686
|
if (edgeIndex >= 0 && ctx.runtimeMode !== "compact") {
|
|
@@ -815,11 +821,32 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
|
|
|
815
821
|
storeI64At(ctx, product, K_VALUE_PRODUCT_CAPACITY_OFFSET, N);
|
|
816
822
|
storePtrAt(ctx, product, K_VALUE_PRODUCT_FIELDS_OFFSET, fields);
|
|
817
823
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
824
|
+
let sortedFields;
|
|
825
|
+
if (Array.isArray(inst.pattern) && inst.pattern.length > 0) {
|
|
826
|
+
const root = inst.pattern[0];
|
|
827
|
+
if (
|
|
828
|
+
(root[0] === "open-product" ||
|
|
829
|
+
root[0] === "closed-product") &&
|
|
830
|
+
Array.isArray(root[1])
|
|
831
|
+
) {
|
|
832
|
+
const order = new Map(root[1].map(([l], i) => [l, i]));
|
|
833
|
+
sortedFields = [...fieldValues].sort((a, b) => {
|
|
834
|
+
const aI = order.has(a.label)
|
|
835
|
+
? order.get(a.label)
|
|
836
|
+
: 999999;
|
|
837
|
+
const bI = order.has(b.label)
|
|
838
|
+
? order.get(b.label)
|
|
839
|
+
: 999999;
|
|
840
|
+
if (aI !== bI) return aI - bI;
|
|
841
|
+
return a.label.localeCompare(b.label);
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
if (!sortedFields) {
|
|
846
|
+
sortedFields = [...fieldValues].sort((a, b) =>
|
|
847
|
+
a.label.localeCompare(b.label)
|
|
848
|
+
);
|
|
849
|
+
}
|
|
823
850
|
|
|
824
851
|
for (let sIdx = 0; sIdx < N; sIdx++) {
|
|
825
852
|
const { label: fLabel, val: fVal } = sortedFields[sIdx];
|
|
@@ -838,12 +865,12 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
|
|
|
838
865
|
const uInput = getReg(inst.src);
|
|
839
866
|
|
|
840
867
|
if (!isTopLevel) {
|
|
841
|
-
// Nested union: synthesize
|
|
868
|
+
// Nested union: synthesize helper @k_union_expr_${index}
|
|
842
869
|
const exprIndex = moduleCtx.unionExprIndex++;
|
|
843
870
|
const exprName = `@k_union_expr_${exprIndex}`;
|
|
844
871
|
const exprFunc = {
|
|
845
872
|
name: `union_expr_${exprIndex}`,
|
|
846
|
-
inputPattern:
|
|
873
|
+
inputPattern: regPatterns.get(cleanReg(inst.src)) || null,
|
|
847
874
|
outputPattern: kvmFunc.outputPattern,
|
|
848
875
|
body: [
|
|
849
876
|
{ op: "id", dest: "%v0", src: "%in" },
|
|
@@ -863,7 +890,7 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
|
|
|
863
890
|
break;
|
|
864
891
|
}
|
|
865
892
|
|
|
866
|
-
// Top-level union: synthesize
|
|
893
|
+
// Top-level union: synthesize arms @k_union_arm_${index}
|
|
867
894
|
const mark = lowerRtMark(ctx);
|
|
868
895
|
const unionDone = ctx.blockName("union_done");
|
|
869
896
|
const N = inst.branches.length;
|
|
@@ -877,7 +904,7 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
|
|
|
877
904
|
|
|
878
905
|
const armFunc = {
|
|
879
906
|
name: `union_arm_${armIndex}`,
|
|
880
|
-
inputPattern:
|
|
907
|
+
inputPattern: regPatterns.get(cleanReg(inst.src)) || null,
|
|
881
908
|
outputPattern: kvmFunc.outputPattern,
|
|
882
909
|
body: branch.body
|
|
883
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
|
|
|
@@ -45,15 +49,17 @@ function alignArenaSize(size) {
|
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
function cStringBytes(text) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.
|
|
52
|
+
const buf = Buffer.from(text, "utf8");
|
|
53
|
+
let out = "";
|
|
54
|
+
for (let i = 0; i < buf.length; i++) {
|
|
55
|
+
const byte = buf[i];
|
|
56
|
+
if (byte === 10) out += "\\0A";
|
|
57
|
+
else if (byte === 34) out += "\\22";
|
|
58
|
+
else if (byte === 92) out += "\\5C";
|
|
59
|
+
else if (byte >= 32 && byte <= 126) out += String.fromCharCode(byte);
|
|
60
|
+
else out += `\\${byte.toString(16).padStart(2, "0").toUpperCase()}`;
|
|
61
|
+
}
|
|
62
|
+
return out + "\\00";
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
function readPattern(inputPattern) {
|
|
@@ -921,7 +927,8 @@ function emitFunctionBody(symbol, body, ctx, linkage = "", options = {}) {
|
|
|
921
927
|
}
|
|
922
928
|
const value = lowerExpr(ctx, body, input, { tail: options.tail !== false });
|
|
923
929
|
if (value === false) {
|
|
924
|
-
// The expression emitted complete control flow, including all
|
|
930
|
+
// The expression emitted complete control flow, including all
|
|
931
|
+
// returns.
|
|
925
932
|
} else if (value == null) {
|
|
926
933
|
unsupported(ctx);
|
|
927
934
|
} else {
|
|
@@ -986,8 +993,7 @@ export function emitLLVMModule(input, options = {}) {
|
|
|
986
993
|
const payload = JSON.stringify({
|
|
987
994
|
format: ARTIFACT_FORMAT,
|
|
988
995
|
version: ARTIFACT_VERSION,
|
|
989
|
-
relation
|
|
990
|
-
kir
|
|
996
|
+
relation
|
|
991
997
|
});
|
|
992
998
|
const payloadBytes = Buffer.byteLength(payload, "utf8") + 1;
|
|
993
999
|
const symbol = llvmIdentifier(options.symbol || relation);
|
|
@@ -1009,25 +1015,74 @@ export function emitLLVMModule(input, options = {}) {
|
|
|
1009
1015
|
}
|
|
1010
1016
|
|
|
1011
1017
|
export function compileObjectToLLVM(object, options = {}) {
|
|
1012
|
-
const inputPattern = options.inputPattern
|
|
1018
|
+
const inputPattern = options.inputPattern
|
|
1019
|
+
? readPattern(options.inputPattern)
|
|
1020
|
+
: null;
|
|
1013
1021
|
const relation = options.relation || object.main;
|
|
1014
1022
|
let targetObject = object;
|
|
1015
1023
|
let entryInputPattern = inputPattern;
|
|
1016
1024
|
let outputPattern = null;
|
|
1017
1025
|
|
|
1018
|
-
if (
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1021
|
-
|
|
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
|
+
);
|
|
1022
1051
|
targetObject = retyped.retypedObject;
|
|
1023
1052
|
entryInputPattern = retyped.inputPattern;
|
|
1024
1053
|
outputPattern = retyped.outputPattern;
|
|
1025
1054
|
}
|
|
1026
1055
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1056
|
+
let functions;
|
|
1057
|
+
let retypedKir = null;
|
|
1029
1058
|
const entry = relation || targetObject.main || "__main__";
|
|
1030
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
|
+
|
|
1031
1086
|
const specializedKvm = {
|
|
1032
1087
|
format: "k-vm",
|
|
1033
1088
|
version: 1,
|
|
@@ -1064,6 +1119,63 @@ export function compileLLVMArtifactFromKVM(kvmInput, options = {}) {
|
|
|
1064
1119
|
return compileKVMModuleToLLVM(entry, functions, options);
|
|
1065
1120
|
}
|
|
1066
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
|
+
|
|
1067
1179
|
export function compileLLVMArtifactFromObject(object, options = {}) {
|
|
1068
1180
|
return compileObjectToLLVM(object, options);
|
|
1069
1181
|
}
|
|
@@ -1079,6 +1191,7 @@ export default {
|
|
|
1079
1191
|
compileBufferToLLVM,
|
|
1080
1192
|
compileObjectToLLVM,
|
|
1081
1193
|
compileLLVMArtifactFromKVM,
|
|
1194
|
+
compileLLVMArtifactFromPKIR,
|
|
1082
1195
|
compileLLVMArtifactFromObject,
|
|
1083
1196
|
emitLLVMModule,
|
|
1084
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
|
|
75
|
+
node ./bin/k-llvm.mjs --help | grep -q 'binary pattern+value wire stream'
|
|
76
76
|
|
|
77
|
-
node ./
|
|
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 ./
|
|
83
|
-
node ./
|
|
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 ./
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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');"
|