@fraczak/k 6.3.3 → 6.4.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/DOCS/CODECS.md +258 -0
- package/DOCS/DICTIONARY.md +1172 -0
- package/DOCS/KIR_SPECIALIZATION_DESIGN.md +51 -46
- package/DOCS/KIR_V1.md +221 -0
- package/DOCS/KVM_EXECUTION_MODEL.md +56 -19
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +2 -1
- package/DOCS/PROFILING_AND_TRACING.md +196 -0
- package/DOCS/REPL.md +61 -4
- package/DOCS/WEB_REPL_REQUIREMENTS.md +135 -0
- package/DOCS/dependency-graph.md +144 -0
- package/Examples/arithmetics.k +51 -48
- package/Examples/bnat.k +1 -1
- package/Examples/buda.k +44 -0
- package/Examples/ieee.k +300 -8671
- package/Examples/poly.k +67 -0
- package/README.md +68 -19
- package/augmentation.mjs +9 -7
- package/backend-api.mjs +11 -2
- package/backends/arm64/README.md +104 -0
- package/backends/arm64/bin/k-arm64-compile.mjs +101 -0
- package/backends/arm64/bin/k-arm64-run.mjs +58 -0
- package/backends/arm64/bin/k-arm64.mjs +88 -0
- package/backends/arm64/scripts/run-tests.mjs +41 -0
- package/backends/arm64/src/arm64.mjs +229 -0
- package/backends/arm64/src/cli.mjs +155 -0
- package/backends/arm64/src/kvm2arm64.mjs +906 -0
- package/backends/arm64/src/runtime.c +728 -0
- package/backends/arm64/tests/integration.sh +112 -0
- package/backends/arm64/tests/test-arm64.mjs +240 -0
- package/backends/llvm/README.md +135 -0
- package/backends/llvm/bin/k-llvm-build.mjs +71 -0
- package/backends/llvm/bin/k-llvm-compile.mjs +68 -0
- package/backends/llvm/bin/k-llvm-jit.mjs +160 -0
- package/backends/llvm/bin/k-llvm-run.mjs +90 -0
- package/backends/llvm/runtime/krt.c +1508 -0
- package/backends/llvm/runtime/krt.h +109 -0
- package/backends/llvm/scripts/conformance.mjs +76 -0
- package/backends/llvm/scripts/run-tests.mjs +28 -0
- package/backends/llvm/src/executable.mjs +562 -0
- package/backends/llvm/src/kvm2llvm.mjs +998 -0
- package/backends/llvm/src/llvm.mjs +1092 -0
- package/backends/llvm/tests/composition-driver.c +22 -0
- package/backends/llvm/tests/identity-driver.c +27 -0
- package/backends/llvm/tests/integration.sh +153 -0
- package/backends/llvm/tests/perf-support.mjs +735 -0
- package/backends/llvm/tests/product-driver.c +23 -0
- package/backends/llvm/tests/projection-driver.c +19 -0
- package/backends/llvm/tests/relation-driver.c +29 -0
- package/backends/llvm/tests/test-llvm.mjs +240 -0
- package/backends/llvm/tests/union-driver.c +31 -0
- package/backends/llvm/tests/variant-driver.c +19 -0
- package/backends/llvm/tests/variant-projection-driver.c +21 -0
- package/backends/wasm/DOCS/USE-WEB-ASSEMBLY.md +233 -0
- package/backends/wasm/README.md +97 -0
- package/backends/wasm/TECHNICAL-DEBT.md +60 -0
- package/backends/wasm/bin/k-wasm-compile.mjs +50 -0
- package/backends/wasm/bin/k-wasm-run.mjs +140 -0
- package/backends/wasm/bin/k-wasm.mjs +49 -0
- package/backends/wasm/demo_ieee.mjs +319 -0
- package/backends/wasm/runtime.wat +102 -0
- package/backends/wasm/scripts/run-tests.mjs +41 -0
- package/backends/wasm/src/cli.mjs +133 -0
- package/backends/wasm/src/kvm2wasm.mjs +661 -0
- package/backends/wasm/src/wasm.mjs +1010 -0
- package/backends/wasm/tests/integration.sh +182 -0
- package/backends/wasm/tests/perf-support.mjs +336 -0
- package/backends/wasm/tests/test-wasm.mjs +617 -0
- package/browser/repl-browser.mjs +1584 -0
- package/browser/shims/assert.mjs +19 -0
- package/browser/shims/buffer.mjs +224 -0
- package/browser/shims/crypto.mjs +103 -0
- package/browser/shims/fs.mjs +159 -0
- package/browser/shims/os.mjs +17 -0
- package/browser/shims/path.mjs +54 -0
- package/browser/shims/process.mjs +44 -0
- package/browser/shims/readline.mjs +11 -0
- package/browser/shims/url.mjs +12 -0
- package/browser/vfs-data.mjs +19 -0
- package/codecs/README.md +3 -0
- package/codecs/runtime/show-value.mjs +2 -2
- package/conformance/helper-reuse/case.json +8 -0
- package/conformance/helper-reuse/expected.kv +1 -0
- package/conformance/helper-reuse/input.kv +1 -0
- package/conformance/helper-reuse/program.k +2 -0
- package/conformance/identity/case.json +8 -0
- package/conformance/identity/expected.kv +1 -0
- package/conformance/identity/input.kv +1 -0
- package/conformance/identity/program.k +1 -0
- package/conformance/product-projection/case.json +8 -0
- package/conformance/product-projection/expected.kv +1 -0
- package/conformance/product-projection/input.kv +1 -0
- package/conformance/product-projection/program.k +1 -0
- package/conformance/run.mjs +149 -0
- package/conformance/variant-construction/case.json +8 -0
- package/conformance/variant-construction/expected.kv +1 -0
- package/conformance/variant-construction/input.kv +1 -0
- package/conformance/variant-construction/program.k +1 -0
- package/harness/adapters/arm64.mjs +105 -0
- package/harness/adapters/kvm.mjs +144 -0
- package/harness/adapters/llvm.mjs +118 -0
- package/harness/adapters/native.mjs +77 -0
- package/harness/adapters/wasm.mjs +169 -0
- package/harness/backend.mjs +95 -0
- package/harness/cli.mjs +130 -0
- package/harness/format.mjs +95 -0
- package/harness/index.mjs +25 -0
- package/harness/persistent.mjs +121 -0
- package/harness/registry.mjs +44 -0
- package/harness/suite.mjs +257 -0
- package/harness/suites/poly.mjs +156 -0
- package/harness/trace.mjs +50 -0
- package/harness/util.mjs +92 -0
- package/intrinsics.mjs +41 -0
- package/kir.mjs +399 -0
- package/kvm.mjs +585 -81
- package/objects/README.md +37 -3
- package/objects/compile.mjs +15 -22
- package/objects/inspect.mjs +96 -0
- package/objects/validate.mjs +415 -0
- package/package.json +51 -19
- package/repl-codecs.mjs +105 -6
- package/repl.mjs +335 -49
- package/run.mjs +2 -7
- package/scripts/benchmark.mjs +406 -22
- package/scripts/build-repl-html.mjs +1784 -0
- package/scripts/perf-ieee.mjs +586 -0
- package/scripts/perf-int.mjs +531 -0
- package/scripts/perf-poly.mjs +945 -0
- package/scripts/render-benchmark.mjs +12 -8
- package/scripts/run-tests.mjs +6 -0
- package/tests/integration.sh +19 -2
- package/tests/test-arithmetics.mjs +54 -0
- package/tests/test-ieee-arithmetic.mjs +4 -0
- package/tests/test-kir.mjs +85 -0
- package/tests/test-kvm-polymorphic.mjs +278 -0
- package/tests/test-kvm.mjs +25 -6
- package/tests/test-poly.mjs +129 -0
- package/tests/test-repl-html.mjs +303 -0
- package/tests/test-repl.mjs +144 -1
- package/tests/test-validate-object.mjs +40 -0
- package/a.klib +0 -1
- package/a.kvm +0 -142676
- package/a.wasm +0 -0
- package/benchmark-results.html +0 -532
- package/benchmark-results.json +0 -480
- package/k-vscode/.vscode/launch.json +0 -14
- package/k-vscode/README.md +0 -75
- package/k-vscode/extension.js +0 -273
- package/k-vscode/language-configuration.json +0 -42
- package/k-vscode/package.json +0 -79
- package/k-vscode/snippets/k.json +0 -92
- package/k-vscode/syntaxes/k.tmLanguage.json +0 -599
- package/k-vscode/test/background-ranges.test.js +0 -50
- package/k-vscode/themes/k-contrast-color-theme.json +0 -104
- package/scripts/git-hooks/pre-commit +0 -10
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
K_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
6
|
+
TMP_DIR=$(mktemp -d)
|
|
7
|
+
trap 'rm -rf "$TMP_DIR"' EXIT
|
|
8
|
+
|
|
9
|
+
echo "Running './backends/arm64/tests/integration.sh' ..."
|
|
10
|
+
|
|
11
|
+
compare_json_envelope() {
|
|
12
|
+
local name=$1
|
|
13
|
+
local input=$2
|
|
14
|
+
local program=$3
|
|
15
|
+
printf '%s\n' "$input" |
|
|
16
|
+
node "$K_ROOT/codecs/json.mjs" --parse |
|
|
17
|
+
node "$K_ROOT/k.mjs" "$program" > "$TMP_DIR/native.kv"
|
|
18
|
+
printf '%s\n' "$input" |
|
|
19
|
+
node "$K_ROOT/codecs/json.mjs" --parse |
|
|
20
|
+
node "$K_ROOT/kvm.mjs" "$program" > "$TMP_DIR/kvm.kv"
|
|
21
|
+
printf '%s\n' "$input" |
|
|
22
|
+
node "$K_ROOT/codecs/json.mjs" --parse |
|
|
23
|
+
node "$K_ROOT/backends/arm64/bin/k-arm64.mjs" "$program" > "$TMP_DIR/arm64.kv"
|
|
24
|
+
|
|
25
|
+
if ! cmp -s "$TMP_DIR/native.kv" "$TMP_DIR/kvm.kv" ||
|
|
26
|
+
! cmp -s "$TMP_DIR/native.kv" "$TMP_DIR/arm64.kv"; then
|
|
27
|
+
echo "output envelope mismatch for $name" >&2
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
echo "Testing basic tag injection..."
|
|
33
|
+
node "$K_ROOT/codecs/unit.mjs" --parse |
|
|
34
|
+
node "$K_ROOT/backends/arm64/bin/k-arm64.mjs" '|ok' |
|
|
35
|
+
node "$K_ROOT/codecs/k-print.mjs" |
|
|
36
|
+
grep -qx '"ok"'
|
|
37
|
+
|
|
38
|
+
echo "Testing record wrapping..."
|
|
39
|
+
printf '{"a": {}, "b": {}}\n' |
|
|
40
|
+
node "$K_ROOT/codecs/k-parse.mjs" |
|
|
41
|
+
node "$K_ROOT/backends/arm64/bin/k-arm64.mjs" '|ok' |
|
|
42
|
+
node "$K_ROOT/codecs/k-print.mjs" |
|
|
43
|
+
grep -qx '{"ok":{"a":{},"b":{}}}'
|
|
44
|
+
|
|
45
|
+
echo "Testing field projection with JSON codec..."
|
|
46
|
+
printf '{"a":123,"b":true,"c":[1,2,"alsk"]}\n' |
|
|
47
|
+
node "$K_ROOT/codecs/json.mjs" --parse |
|
|
48
|
+
node "$K_ROOT/backends/arm64/bin/k-arm64.mjs" '.c' |
|
|
49
|
+
node "$K_ROOT/codecs/json.mjs" --print |
|
|
50
|
+
grep -qx '\[1,2,"alsk"\]'
|
|
51
|
+
|
|
52
|
+
echo "Testing nested array projection..."
|
|
53
|
+
compare_json_envelope "nested array string projection" \
|
|
54
|
+
'{"a":[1,2,"kupa"], "b": 123}' \
|
|
55
|
+
'.a.2'
|
|
56
|
+
|
|
57
|
+
echo "Testing JSON number projection..."
|
|
58
|
+
compare_json_envelope "projected JSON number" \
|
|
59
|
+
'{"a":[1,2,"kupa"], "b": 123}' \
|
|
60
|
+
'.b'
|
|
61
|
+
|
|
62
|
+
echo "Testing closed product filter..."
|
|
63
|
+
compare_json_envelope "closed product filter" \
|
|
64
|
+
'{"a":[1,2,"kupa"], "b": 123}' \
|
|
65
|
+
'?{X a, Y b}'
|
|
66
|
+
|
|
67
|
+
echo "Testing record construction..."
|
|
68
|
+
printf '{"a":[1,2,"kupa"], "b": 123}\n' |
|
|
69
|
+
node "$K_ROOT/codecs/json.mjs" --parse |
|
|
70
|
+
node "$K_ROOT/backends/arm64/bin/k-arm64.mjs" '{ .a.2 word, .b number }' |
|
|
71
|
+
node "$K_ROOT/codecs/json.mjs" --print |
|
|
72
|
+
grep -qx '{"number":123,"word":"kupa"}'
|
|
73
|
+
|
|
74
|
+
echo "Testing standalone executable compilation and execution..."
|
|
75
|
+
node "$K_ROOT/backends/arm64/bin/k-arm64-compile.mjs" -o "$TMP_DIR/dot-c" '.c'
|
|
76
|
+
printf '{"a":123,"b":true,"c":[1,2,"alsk"]}\n' |
|
|
77
|
+
node "$K_ROOT/codecs/json.mjs" --parse |
|
|
78
|
+
node "$K_ROOT/backends/arm64/bin/k-arm64-run.mjs" "$TMP_DIR/dot-c" |
|
|
79
|
+
node "$K_ROOT/codecs/json.mjs" --print |
|
|
80
|
+
grep -qx '\[1,2,"alsk"\]'
|
|
81
|
+
|
|
82
|
+
echo "Testing direct execution of compiled executable with --json..."
|
|
83
|
+
printf '{"a":123,"b":true,"c":[1,2,"alsk"]}\n' |
|
|
84
|
+
node "$K_ROOT/codecs/json.mjs" --parse |
|
|
85
|
+
"$TMP_DIR/dot-c" --json |
|
|
86
|
+
grep -q 'exponent'
|
|
87
|
+
|
|
88
|
+
echo "Testing compilation from .ko object..."
|
|
89
|
+
node "$K_ROOT/objects/compile.mjs" '.c' "$TMP_DIR/dot-c.ko"
|
|
90
|
+
printf '{"a":123,"b":true,"c":[1,2,"alsk"]}\n' |
|
|
91
|
+
node "$K_ROOT/codecs/json.mjs" --parse |
|
|
92
|
+
node "$K_ROOT/backends/arm64/bin/k-arm64.mjs" "$TMP_DIR/dot-c.ko" |
|
|
93
|
+
node "$K_ROOT/codecs/json.mjs" --print |
|
|
94
|
+
grep -qx '\[1,2,"alsk"\]'
|
|
95
|
+
|
|
96
|
+
echo "Testing compilation to assembly (-S)..."
|
|
97
|
+
node "$K_ROOT/backends/arm64/bin/k-arm64-compile.mjs" -S '.c' "$TMP_DIR/dot-c.s"
|
|
98
|
+
test -s "$TMP_DIR/dot-c.s"
|
|
99
|
+
grep -q "rel___main__" "$TMP_DIR/dot-c.s"
|
|
100
|
+
|
|
101
|
+
echo "Testing library dependencies (--lib and --export)..."
|
|
102
|
+
node "$K_ROOT/backends/arm64/bin/k-arm64-compile.mjs" \
|
|
103
|
+
--lib "$K_ROOT/Examples/arithmetics.k" \
|
|
104
|
+
--export plus:+ --export succ --export int \
|
|
105
|
+
'{succ int x,int y}+' \
|
|
106
|
+
-o "$TMP_DIR/lib-add"
|
|
107
|
+
|
|
108
|
+
node "$K_ROOT/codecs/k-parse.mjs" <<< '{"x":{"succ":"zero"},"y":{"succ":{"succ":"zero"}}}' |
|
|
109
|
+
"$TMP_DIR/lib-add" --json |
|
|
110
|
+
grep -Fxq '{"+":{"1":"_"}}'
|
|
111
|
+
|
|
112
|
+
echo "==> ARM64 Integration Tests Completed Successfully!"
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
compileARM64Artifact,
|
|
10
|
+
compileARM64ArtifactFromKVM,
|
|
11
|
+
compileARM64ArtifactFromObject,
|
|
12
|
+
runARM64Artifact
|
|
13
|
+
} from "../src/arm64.mjs";
|
|
14
|
+
import {
|
|
15
|
+
compileKVMModuleToARM64,
|
|
16
|
+
emitMetadataC,
|
|
17
|
+
getTagEntries,
|
|
18
|
+
resetTagIds
|
|
19
|
+
} from "../src/kvm2arm64.mjs";
|
|
20
|
+
import { compileObjectBuffer, decodeObject } from "../../../object.mjs";
|
|
21
|
+
import { objectToKVMArtifact } from "../../../kvm.mjs";
|
|
22
|
+
|
|
23
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
const ROOT = path.resolve(__dirname, "../../..");
|
|
25
|
+
|
|
26
|
+
console.log("==> Running ARM64 Backend Test Suite");
|
|
27
|
+
|
|
28
|
+
// Helper to convert JSON value to wire buffer via codecs/k-parse.mjs
|
|
29
|
+
function jsonToWire(jsonObj) {
|
|
30
|
+
const jsonStr = typeof jsonObj === "string" ? jsonObj : JSON.stringify(jsonObj);
|
|
31
|
+
return execFileSync("node", [path.join(ROOT, "codecs/k-parse.mjs")], {
|
|
32
|
+
input: jsonStr
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Helper to convert wire buffer to JSON value via codecs/k-print.mjs
|
|
37
|
+
function wireToJSON(wireBuf) {
|
|
38
|
+
const outputStr = execFileSync("node", [path.join(ROOT, "codecs/k-print.mjs")], {
|
|
39
|
+
input: wireBuf,
|
|
40
|
+
encoding: "utf8"
|
|
41
|
+
});
|
|
42
|
+
return JSON.parse(outputStr.trim());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 1. Identity / Unit Relation
|
|
46
|
+
console.log("==> Test 1: Identity / Unit Relation");
|
|
47
|
+
{
|
|
48
|
+
const source = `()`;
|
|
49
|
+
const artifact = compileARM64Artifact(source);
|
|
50
|
+
const inputWire = jsonToWire({});
|
|
51
|
+
const outWire = runARM64Artifact(artifact, inputWire);
|
|
52
|
+
const outJSON = wireToJSON(outWire);
|
|
53
|
+
assert.deepEqual(outJSON, {}, "Unit relation must return empty object {}");
|
|
54
|
+
|
|
55
|
+
// Test --json mode
|
|
56
|
+
const outJsonStr = runARM64Artifact(artifact, inputWire, { json: true });
|
|
57
|
+
assert.equal(outJsonStr.trim(), "{}", "--json mode must return {}");
|
|
58
|
+
console.log(" Passed!");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 2. Boolean Negation (Variant Projection & Creation)
|
|
62
|
+
console.log("==> Test 2: Boolean Negation (Variants)");
|
|
63
|
+
{
|
|
64
|
+
const source = `
|
|
65
|
+
true = {} | true;
|
|
66
|
+
false = {} | false;
|
|
67
|
+
neg = < /true false, /false true >;
|
|
68
|
+
neg
|
|
69
|
+
`;
|
|
70
|
+
const artifact = compileARM64Artifact(source);
|
|
71
|
+
|
|
72
|
+
// Test neg(true) -> false
|
|
73
|
+
const trueWire = jsonToWire({ true: {} });
|
|
74
|
+
const falseWireOut = runARM64Artifact(artifact, trueWire);
|
|
75
|
+
assert.deepEqual(wireToJSON(falseWireOut), "false");
|
|
76
|
+
|
|
77
|
+
// Test neg(false) -> true
|
|
78
|
+
const falseWire = jsonToWire({ false: {} });
|
|
79
|
+
const trueWireOut = runARM64Artifact(artifact, falseWire);
|
|
80
|
+
assert.deepEqual(wireToJSON(trueWireOut), "true");
|
|
81
|
+
|
|
82
|
+
// Test --json mode
|
|
83
|
+
const jsonOut = runARM64Artifact(artifact, trueWire, { json: true });
|
|
84
|
+
assert.equal(jsonOut.trim(), '"false"');
|
|
85
|
+
console.log(" Passed!");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 3. Product Projection and Field Swapping
|
|
89
|
+
console.log("==> Test 3: Product Projection and Record Construction");
|
|
90
|
+
{
|
|
91
|
+
const source = `
|
|
92
|
+
swap = { .y x, .x y };
|
|
93
|
+
swap
|
|
94
|
+
`;
|
|
95
|
+
const artifact = compileARM64Artifact(source);
|
|
96
|
+
const inWire = jsonToWire({ x: "a", y: "b" });
|
|
97
|
+
const outWire = runARM64Artifact(artifact, inWire);
|
|
98
|
+
assert.deepEqual(wireToJSON(outWire), { x: "b", y: "a" });
|
|
99
|
+
console.log(" Passed!");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 4. Multi-branch Union with Backtracking
|
|
103
|
+
console.log("==> Test 4: Multi-branch Union with Backtracking");
|
|
104
|
+
{
|
|
105
|
+
const source = `
|
|
106
|
+
$ t = < {} a, {} b, {} c >;
|
|
107
|
+
classify = <
|
|
108
|
+
/a ({} | first),
|
|
109
|
+
/b ({} | second),
|
|
110
|
+
/c ({} | third)
|
|
111
|
+
>;
|
|
112
|
+
classify
|
|
113
|
+
`;
|
|
114
|
+
const artifact = compileARM64Artifact(source);
|
|
115
|
+
|
|
116
|
+
assert.deepEqual(wireToJSON(runARM64Artifact(artifact, jsonToWire({ a: {} }))), "first");
|
|
117
|
+
assert.deepEqual(wireToJSON(runARM64Artifact(artifact, jsonToWire({ b: {} }))), "second");
|
|
118
|
+
assert.deepEqual(wireToJSON(runARM64Artifact(artifact, jsonToWire({ c: {} }))), "third");
|
|
119
|
+
console.log(" Passed!");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 5. Peano Addition with Tail-Call Optimization
|
|
123
|
+
console.log("==> Test 5: Peano Addition with Tail Recursion Optimization");
|
|
124
|
+
{
|
|
125
|
+
const source = `
|
|
126
|
+
$ nat = < {} 0, nat +1 >;
|
|
127
|
+
0 = {} | 0 $ nat;
|
|
128
|
+
inc = | +1 $ nat;
|
|
129
|
+
dec = $ nat / +1;
|
|
130
|
+
add = $ { nat x, nat y } <
|
|
131
|
+
{ . x dec x, . y inc y } add,
|
|
132
|
+
. y
|
|
133
|
+
>;
|
|
134
|
+
add
|
|
135
|
+
`;
|
|
136
|
+
const artifact = compileARM64Artifact(source);
|
|
137
|
+
|
|
138
|
+
// 3 + 2 = 5
|
|
139
|
+
const n0 = "0";
|
|
140
|
+
const n1 = { "+1": n0 };
|
|
141
|
+
const n2 = { "+1": n1 };
|
|
142
|
+
const n3 = { "+1": n2 };
|
|
143
|
+
const n5 = { "+1": { "+1": n3 } };
|
|
144
|
+
|
|
145
|
+
const inWire = jsonToWire({ x: n3, y: n2 });
|
|
146
|
+
const outWire = runARM64Artifact(artifact, inWire);
|
|
147
|
+
assert.deepEqual(wireToJSON(outWire), n5);
|
|
148
|
+
|
|
149
|
+
// Test 0 + 2 = 2
|
|
150
|
+
const inWireZero = jsonToWire({ x: n0, y: n2 });
|
|
151
|
+
const outWireZero = runARM64Artifact(artifact, inWireZero);
|
|
152
|
+
assert.deepEqual(wireToJSON(outWireZero), n2);
|
|
153
|
+
console.log(" Passed!");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 6. Deep Tail Recursion (testing stack safety with loop)
|
|
157
|
+
console.log("==> Test 6: Deep Tail Recursion Loop");
|
|
158
|
+
{
|
|
159
|
+
const source = `
|
|
160
|
+
$ nat = < {} 0, nat +1 >;
|
|
161
|
+
0 = {} | 0 $ nat;
|
|
162
|
+
inc = | +1 $ nat;
|
|
163
|
+
dec = $ nat / +1;
|
|
164
|
+
add = $ { nat x, nat y } <
|
|
165
|
+
{ . x dec x, . y inc y } add,
|
|
166
|
+
. y
|
|
167
|
+
>;
|
|
168
|
+
add
|
|
169
|
+
`;
|
|
170
|
+
const artifact = compileARM64Artifact(source);
|
|
171
|
+
|
|
172
|
+
// Compute 50 + 1 = 51
|
|
173
|
+
let cur = "0";
|
|
174
|
+
for (let i = 0; i < 50; i++) cur = { "+1": cur };
|
|
175
|
+
const one = { "+1": "0" };
|
|
176
|
+
|
|
177
|
+
const inWire = jsonToWire({ x: cur, y: one });
|
|
178
|
+
const outWire = runARM64Artifact(artifact, inWire);
|
|
179
|
+
const outJSON = wireToJSON(outWire);
|
|
180
|
+
|
|
181
|
+
// Count the +1 depth
|
|
182
|
+
let count = 0;
|
|
183
|
+
let p = outJSON;
|
|
184
|
+
while (p && typeof p === "object" && "+1" in p) {
|
|
185
|
+
count++;
|
|
186
|
+
p = p["+1"];
|
|
187
|
+
}
|
|
188
|
+
assert.equal(count, 51, "50 + 1 must yield depth 51");
|
|
189
|
+
console.log(" Passed!");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 7. Compilation to Assembly Text (-S)
|
|
193
|
+
console.log("==> Test 7: Assembly Text Generation");
|
|
194
|
+
{
|
|
195
|
+
const source = `true = {} | true; true`;
|
|
196
|
+
const { assembly, entryName } = compileARM64Artifact(source, { emit: "assembly" });
|
|
197
|
+
assert.ok(typeof assembly === "string", "Assembly output must be a string");
|
|
198
|
+
assert.ok(assembly.includes(".arch armv8-a"), "Assembly must target armv8-a");
|
|
199
|
+
assert.ok(assembly.includes(`.global ${entryName}`), "Assembly must export entry symbol");
|
|
200
|
+
assert.ok(assembly.includes("ret"), "Assembly must contain return instructions");
|
|
201
|
+
console.log(" Passed!");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 8. Compilation from .kvm Artifact
|
|
205
|
+
console.log("==> Test 8: Compilation from .kvm Artifact");
|
|
206
|
+
{
|
|
207
|
+
const script = `
|
|
208
|
+
$ bool = < {} true, {} false >;
|
|
209
|
+
not = $ bool < /true ({} | false), /false ({} | true) >;
|
|
210
|
+
not
|
|
211
|
+
`;
|
|
212
|
+
const obj = decodeObject(compileObjectBuffer(script));
|
|
213
|
+
const kvm = objectToKVMArtifact(obj);
|
|
214
|
+
|
|
215
|
+
const artifact = compileARM64ArtifactFromKVM(kvm);
|
|
216
|
+
const inWire = jsonToWire({ true: {} });
|
|
217
|
+
const outWire = runARM64Artifact(artifact, inWire);
|
|
218
|
+
assert.deepEqual(wireToJSON(outWire), "false");
|
|
219
|
+
console.log(" Passed!");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 9. Failure Case (Partial Relation Non-Match)
|
|
223
|
+
console.log("==> Test 9: Failure Case (Relation Rejection)");
|
|
224
|
+
{
|
|
225
|
+
const source = `
|
|
226
|
+
onlyA = /a ({} | success);
|
|
227
|
+
onlyA
|
|
228
|
+
`;
|
|
229
|
+
const artifact = compileARM64Artifact(source);
|
|
230
|
+
const inWireFail = jsonToWire({ b: {} });
|
|
231
|
+
|
|
232
|
+
assert.throws(
|
|
233
|
+
() => runARM64Artifact(artifact, inWireFail),
|
|
234
|
+
/failed with exit status 1/,
|
|
235
|
+
"Executable must exit with status 1 on partial relation failure"
|
|
236
|
+
);
|
|
237
|
+
console.log(" Passed!");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
console.log("==> All ARM64 Backend Unit Tests Passed Successfully!");
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# k-llvm
|
|
2
|
+
|
|
3
|
+
Experimental LLVM backend prototype for `k`.
|
|
4
|
+
|
|
5
|
+
This package lives under the `k` monorepo as `backends/llvm`. It consumes the
|
|
6
|
+
backend bridge from the root `@fraczak/k` package and starts from compiled
|
|
7
|
+
`.ko` objects:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
.k source -> .ko -> envelope-specialized KIR-P -> LLVM IR prototype
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The first milestone is not performance. It is a stable backend pipeline that
|
|
14
|
+
can load an object, specialize it through KIR-P, and produce an inspectable
|
|
15
|
+
LLVM module.
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
From the monorepo root, install the workspace dependencies, then run the
|
|
20
|
+
backend from `backends/llvm`:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npm install
|
|
24
|
+
cd backends/llvm
|
|
25
|
+
node ../../objects/compile.mjs '()' /tmp/id.ko
|
|
26
|
+
node ./bin/k-llvm-compile.mjs --input-pattern '[["open-product",[]]]' /tmp/id.ko /tmp/id.ll
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The generated `.ll` embeds the envelope-specialized KIR-P contract as module
|
|
30
|
+
data and exposes the first runtime ABI slice:
|
|
31
|
+
|
|
32
|
+
```llvm
|
|
33
|
+
%k_result = type { i32, ptr }
|
|
34
|
+
|
|
35
|
+
define %k_result @k_main(ptr %rt, ptr %input)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`%rt` is an opaque runtime handle and `%input` is an opaque boxed `k_value*`.
|
|
39
|
+
The first executable lowerings support identity, typed filters and singleton
|
|
40
|
+
code filters, product field projection, product construction, variant
|
|
41
|
+
construction, and variant projection. Unsupported operations return a nonzero
|
|
42
|
+
status in `k_result`. These operations can compose through KIR `comp`.
|
|
43
|
+
|
|
44
|
+
## CLI
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
k-llvm-build [options] object.ko output-exe
|
|
48
|
+
k-llvm-compile [options] object.ko [output.ll]
|
|
49
|
+
k-llvm-jit [options] object.ko
|
|
50
|
+
k-llvm-run [options] object.ko [input.kv]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Options:
|
|
54
|
+
|
|
55
|
+
- `--retype rel`: current option spelling for the relation to
|
|
56
|
+
envelope-specialize; defaults to the object's `main`.
|
|
57
|
+
- `--input-pattern json-or-file`: KIR property-list input pattern; required
|
|
58
|
+
for `k-llvm-build`.
|
|
59
|
+
- `--expect value-or-file`: for `k-llvm-run`, compare the output value against
|
|
60
|
+
expected value text.
|
|
61
|
+
- `-h`, `--help`: show usage.
|
|
62
|
+
|
|
63
|
+
Only `.ko` / `.klib` object input is supported in this first prototype.
|
|
64
|
+
Source compilation remains owned by core `k`.
|
|
65
|
+
Without `--expect`, `k-llvm-run` prints the result value as compact JSON.
|
|
66
|
+
`k-llvm-build` emits a native executable that reads and writes the binary
|
|
67
|
+
k pattern+value envelope used by `codecs/k-parse.mjs` and `codecs/k-print.mjs`.
|
|
68
|
+
The executable checks the stdin envelope pattern against the compiled input
|
|
69
|
+
pattern and encodes stdout with the compiled output pattern.
|
|
70
|
+
`k-llvm-jit` reads the stdin binary envelope first, specializes the object with
|
|
71
|
+
that input pattern, caches the native executable, and writes the compiled
|
|
72
|
+
output envelope to stdout.
|
|
73
|
+
The native envelope codec supports the k Unicode string encoding used for
|
|
74
|
+
labels and tags.
|
|
75
|
+
|
|
76
|
+
## Tests
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
npm test
|
|
80
|
+
node ./scripts/conformance.mjs
|
|
81
|
+
npm run perf:int
|
|
82
|
+
npm run perf:ieee
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The conformance runner reuses supported fixtures from `../../conformance`,
|
|
86
|
+
emits LLVM IR for each fixture, generates a small C driver for the fixture
|
|
87
|
+
input and expected value, links it with `runtime/krt.c`, and runs the binary.
|
|
88
|
+
|
|
89
|
+
The performance runners mirror the `k-wasm` integer and IEEE harnesses. They
|
|
90
|
+
load the real example programs from `@fraczak/k`, generate the same operation
|
|
91
|
+
matrices, compare backend output to native `k` results, and time the native JS,
|
|
92
|
+
kVM, and LLVM executable lanes. Set `LLVM_ONLY=1` to skip native timing,
|
|
93
|
+
`ITERATIONS=N` to change samples, and `LLVM_STRICT=1` to make unsupported LLVM
|
|
94
|
+
cases fail the process. The LLVM lane uses persistent executables by default;
|
|
95
|
+
set `LLVM_PIPELINE=1` to measure parallel request pipelining or
|
|
96
|
+
`LLVM_SPAWN_PER_CALL=1` to reproduce the older spawn-per-operation timing.
|
|
97
|
+
|
|
98
|
+
## Scope
|
|
99
|
+
|
|
100
|
+
Current output:
|
|
101
|
+
|
|
102
|
+
- KIR-P envelope specialization through `@fraczak/k/backend-api.mjs`;
|
|
103
|
+
- textual LLVM IR with embedded envelope-specialized KIR-P metadata;
|
|
104
|
+
- boxed runtime ABI declarations;
|
|
105
|
+
- identity, filter/code erasure, product projection/construction, and variant
|
|
106
|
+
construction/projection lowerings as `@k_main(k_rt*, k_value*) -> k_result`;
|
|
107
|
+
- KIR `comp` lowering for sequencing supported operations;
|
|
108
|
+
- KIR `empty` failure and ordered `union` branch selection;
|
|
109
|
+
- KIR `ref` lowering to internal `%k_result` relation functions;
|
|
110
|
+
- a tiny C runtime under `runtime/`.
|
|
111
|
+
|
|
112
|
+
Next backend steps:
|
|
113
|
+
|
|
114
|
+
- add an executable conformance mode once the runtime ABI exists.
|
|
115
|
+
|
|
116
|
+
## Runtime ABI
|
|
117
|
+
|
|
118
|
+
The C side owns all runtime allocation:
|
|
119
|
+
|
|
120
|
+
```c
|
|
121
|
+
typedef struct k_rt k_rt;
|
|
122
|
+
typedef struct k_value k_value;
|
|
123
|
+
|
|
124
|
+
typedef struct {
|
|
125
|
+
int32_t status;
|
|
126
|
+
k_value *value;
|
|
127
|
+
} k_result;
|
|
128
|
+
|
|
129
|
+
k_result k_main(k_rt *rt, k_value *input);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`runtime/krt.h` provides the initial boxed helpers for units, products,
|
|
133
|
+
variants, and pointer equality. The representation is intentionally opaque so
|
|
134
|
+
future KIR specialization can add unboxed fast paths without changing the
|
|
135
|
+
external ABI.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { argv, exit } from "node:process";
|
|
5
|
+
import { decodeObject } from "@fraczak/k/object.mjs";
|
|
6
|
+
import { compileObjectToExecutable } from "../src/executable.mjs";
|
|
7
|
+
|
|
8
|
+
function usage(stream = console.error) {
|
|
9
|
+
const prog = argv[1] || "k-llvm-build.mjs";
|
|
10
|
+
stream(`Usage: node ${prog} [options] object-file output-exe`);
|
|
11
|
+
stream("Compile a k .ko/.klib object into a native executable.");
|
|
12
|
+
stream("");
|
|
13
|
+
stream("The executable reads a binary k pattern+value envelope from stdin and");
|
|
14
|
+
stream("writes a binary k pattern+value envelope to stdout.");
|
|
15
|
+
stream("The stdin envelope pattern must match the compiled input pattern;");
|
|
16
|
+
stream("stdout is encoded with the compiled output pattern.");
|
|
17
|
+
stream("");
|
|
18
|
+
stream("Options:");
|
|
19
|
+
stream(" --retype rel Relation to specialize. Defaults to object main.");
|
|
20
|
+
stream(" --input-pattern value Required input pattern property-list JSON, or a file containing it.");
|
|
21
|
+
stream(" -h, --help Show this help.");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function readMaybeFile(textOrPath) {
|
|
25
|
+
return fs.existsSync(textOrPath) ? fs.readFileSync(textOrPath, "utf8") : textOrPath;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function readPattern(inputPattern) {
|
|
29
|
+
if (inputPattern == null) return null;
|
|
30
|
+
return JSON.parse(readMaybeFile(inputPattern));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const args = argv.slice(2);
|
|
35
|
+
if (args.includes("-h") || args.includes("--help")) {
|
|
36
|
+
usage(console.log);
|
|
37
|
+
exit(0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let relation = null;
|
|
41
|
+
let inputPattern = null;
|
|
42
|
+
while (args.length > 0 && args[0].startsWith("--")) {
|
|
43
|
+
const option = args.shift();
|
|
44
|
+
if (option === "--retype") {
|
|
45
|
+
relation = args.shift();
|
|
46
|
+
if (!relation) throw new Error("--retype requires a relation name");
|
|
47
|
+
} else if (option === "--input-pattern") {
|
|
48
|
+
inputPattern = args.shift();
|
|
49
|
+
if (!inputPattern) throw new Error("--input-pattern requires JSON or a file path");
|
|
50
|
+
} else {
|
|
51
|
+
throw new Error(`Unknown option: ${option}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const objectPath = args.shift() || null;
|
|
56
|
+
const outputPath = args.shift() || null;
|
|
57
|
+
if (objectPath == null) throw new Error("object-file is required");
|
|
58
|
+
if (outputPath == null) throw new Error("output-exe is required");
|
|
59
|
+
if (args.length > 0) throw new Error(`Unexpected argument: ${args[0]}`);
|
|
60
|
+
if (inputPattern == null) throw new Error("--input-pattern is required for k-llvm-build");
|
|
61
|
+
|
|
62
|
+
const object = decodeObject(fs.readFileSync(objectPath));
|
|
63
|
+
compileObjectToExecutable(object, outputPath, {
|
|
64
|
+
relation: relation || object.main,
|
|
65
|
+
inputPattern: readPattern(inputPattern)
|
|
66
|
+
});
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error(error.stack || error.message || String(error));
|
|
69
|
+
usage();
|
|
70
|
+
exit(1);
|
|
71
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { argv, exit, stdin, stdout } from "node:process";
|
|
5
|
+
import { compileBufferToLLVM } from "../src/llvm.mjs";
|
|
6
|
+
|
|
7
|
+
function usage(stream = console.error) {
|
|
8
|
+
const prog = argv[1] || "k-llvm-compile.mjs";
|
|
9
|
+
stream(`Usage: node ${prog} [options] object-file [output.ll]`);
|
|
10
|
+
stream("Compile a k .ko/.klib object into prototype LLVM IR.");
|
|
11
|
+
stream("");
|
|
12
|
+
stream("Arguments:");
|
|
13
|
+
stream(" object-file Input .ko or .klib file. Reads bytes from stdin when omitted.");
|
|
14
|
+
stream(" output.ll Output LLVM IR path. Writes to stdout when omitted.");
|
|
15
|
+
stream("");
|
|
16
|
+
stream("Options:");
|
|
17
|
+
stream(" --retype rel Relation to specialize. Defaults to object main.");
|
|
18
|
+
stream(" --input-pattern value Input pattern property-list JSON, or a file containing it.");
|
|
19
|
+
stream(" -h, --help Show this help.");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function readStdinBytes() {
|
|
23
|
+
const chunks = [];
|
|
24
|
+
for await (const chunk of stdin) {
|
|
25
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
26
|
+
}
|
|
27
|
+
return Buffer.concat(chunks);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const args = argv.slice(2);
|
|
32
|
+
if (args.includes("-h") || args.includes("--help")) {
|
|
33
|
+
usage(console.log);
|
|
34
|
+
exit(0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let relation = null;
|
|
38
|
+
let inputPattern = null;
|
|
39
|
+
while (args.length > 0 && args[0].startsWith("--")) {
|
|
40
|
+
const option = args.shift();
|
|
41
|
+
if (option === "--retype") {
|
|
42
|
+
relation = args.shift();
|
|
43
|
+
if (!relation) throw new Error("--retype requires a relation name");
|
|
44
|
+
} else if (option === "--input-pattern") {
|
|
45
|
+
inputPattern = args.shift();
|
|
46
|
+
if (!inputPattern) throw new Error("--input-pattern requires JSON or a file path");
|
|
47
|
+
} else {
|
|
48
|
+
throw new Error(`Unknown option: ${option}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (inputPattern == null) throw new Error("--input-pattern is required");
|
|
52
|
+
|
|
53
|
+
const inputPath = args.shift() || null;
|
|
54
|
+
const outputPath = args.shift() || null;
|
|
55
|
+
if (args.length > 0) throw new Error(`Unexpected argument: ${args[0]}`);
|
|
56
|
+
|
|
57
|
+
const input = inputPath == null ? await readStdinBytes() : fs.readFileSync(inputPath);
|
|
58
|
+
const { llvm } = compileBufferToLLVM(input, { relation, inputPattern });
|
|
59
|
+
if (outputPath == null) {
|
|
60
|
+
stdout.write(llvm);
|
|
61
|
+
} else {
|
|
62
|
+
fs.writeFileSync(outputPath, llvm);
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error(error.stack || error.message || String(error));
|
|
66
|
+
usage();
|
|
67
|
+
exit(1);
|
|
68
|
+
}
|