@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
|
@@ -1,112 +0,0 @@
|
|
|
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":{"0":"_"},"y":{"+":{"0":"_"}}}' |
|
|
109
|
-
"$TMP_DIR/lib-add" --json |
|
|
110
|
-
grep -Fxq '{"+":{"1":"_"}}'
|
|
111
|
-
|
|
112
|
-
echo "==> ARM64 Integration Tests Completed Successfully!"
|
|
@@ -1,240 +0,0 @@
|
|
|
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!");
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import crypto from "node:crypto";
|
|
4
|
-
import fs from "node:fs";
|
|
5
|
-
import os from "node:os";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import { spawn } from "node:child_process";
|
|
8
|
-
import { argv, exit, stdin, stdout } from "node:process";
|
|
9
|
-
import { fileURLToPath } from "node:url";
|
|
10
|
-
import { decodeObject } from "@fraczak/k/object.mjs";
|
|
11
|
-
import { decodeWire } from "@fraczak/k/codecs/runtime/prefix-codec.mjs";
|
|
12
|
-
import { compileObjectToExecutable } from "../src/executable.mjs";
|
|
13
|
-
|
|
14
|
-
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
15
|
-
|
|
16
|
-
function usage(stream = console.error) {
|
|
17
|
-
const prog = argv[1] || "k-llvm-jit.mjs";
|
|
18
|
-
stream(`Usage: node ${prog} [options] object-file`);
|
|
19
|
-
stream("Compile and execute a k .ko/.klib object against the stdin binary envelope.");
|
|
20
|
-
stream("");
|
|
21
|
-
stream("The runner reads a binary k pattern+value envelope from stdin, specializes");
|
|
22
|
-
stream("the object with that envelope pattern, then writes a binary envelope to stdout.");
|
|
23
|
-
stream("");
|
|
24
|
-
stream("Options:");
|
|
25
|
-
stream(" --retype rel Relation to specialize. Defaults to object main.");
|
|
26
|
-
stream(" --cache-dir path Directory for compiled native executables.");
|
|
27
|
-
stream(" --no-cache Compile in a temporary directory and remove it after execution.");
|
|
28
|
-
stream(" -h, --help Show this help.");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function readStdinBuffer() {
|
|
32
|
-
const chunks = [];
|
|
33
|
-
for await (const chunk of stdin) {
|
|
34
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
35
|
-
}
|
|
36
|
-
return Buffer.concat(chunks);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let cachedBackendFingerprint = null;
|
|
40
|
-
|
|
41
|
-
function backendFingerprint() {
|
|
42
|
-
if (cachedBackendFingerprint != null) return cachedBackendFingerprint;
|
|
43
|
-
const hash = crypto.createHash("sha256");
|
|
44
|
-
for (const relPath of ["src/llvm.mjs", "src/kvm2llvm.mjs", "src/executable.mjs", "runtime/krt.c", "runtime/krt.h"]) {
|
|
45
|
-
hash.update(relPath);
|
|
46
|
-
hash.update("\0");
|
|
47
|
-
hash.update(fs.readFileSync(path.join(root, relPath)));
|
|
48
|
-
hash.update("\0");
|
|
49
|
-
}
|
|
50
|
-
cachedBackendFingerprint = hash.digest("hex").slice(0, 16);
|
|
51
|
-
return cachedBackendFingerprint;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function cacheKey({ objectBuffer, relation, inputPattern }) {
|
|
55
|
-
const hash = crypto.createHash("sha256");
|
|
56
|
-
hash.update("k-llvm-jit-v2\0");
|
|
57
|
-
hash.update(backendFingerprint());
|
|
58
|
-
hash.update("\0");
|
|
59
|
-
hash.update(relation);
|
|
60
|
-
hash.update("\0");
|
|
61
|
-
hash.update(JSON.stringify(inputPattern));
|
|
62
|
-
hash.update("\0");
|
|
63
|
-
hash.update(objectBuffer);
|
|
64
|
-
return hash.digest("hex");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function runExecutable(exePath, inputBuffer) {
|
|
68
|
-
return new Promise((resolve, reject) => {
|
|
69
|
-
const child = spawn(exePath, [], { stdio: ["pipe", "pipe", "pipe"] });
|
|
70
|
-
const stdoutChunks = [];
|
|
71
|
-
const stderrChunks = [];
|
|
72
|
-
|
|
73
|
-
child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
74
|
-
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
75
|
-
child.on("error", reject);
|
|
76
|
-
child.on("close", (status) => {
|
|
77
|
-
if (status !== 0) {
|
|
78
|
-
const stderr = Buffer.concat(stderrChunks).toString("utf8");
|
|
79
|
-
reject(new Error(`${exePath} failed with status ${status}${stderr ? `\n${stderr}` : ""}`));
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
resolve(Buffer.concat(stdoutChunks));
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
child.stdin.end(inputBuffer);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function compileCached({ object, objectBuffer, relation, inputPattern, cacheDir }) {
|
|
90
|
-
fs.mkdirSync(cacheDir, { recursive: true });
|
|
91
|
-
const key = cacheKey({ objectBuffer, relation, inputPattern });
|
|
92
|
-
const exePath = path.join(cacheDir, `${key}.exe`);
|
|
93
|
-
if (!fs.existsSync(exePath)) {
|
|
94
|
-
const tmpPath = path.join(cacheDir, `${key}.${process.pid}.tmp`);
|
|
95
|
-
compileObjectToExecutable(object, tmpPath, { relation, inputPattern });
|
|
96
|
-
fs.renameSync(tmpPath, exePath);
|
|
97
|
-
}
|
|
98
|
-
return exePath;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function compileTemporary({ object, relation, inputPattern }) {
|
|
102
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-llvm-jit-"));
|
|
103
|
-
const exePath = path.join(tmpDir, "program");
|
|
104
|
-
compileObjectToExecutable(object, exePath, { relation, inputPattern });
|
|
105
|
-
return { exePath, tmpDir };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
const args = argv.slice(2);
|
|
110
|
-
if (args.includes("-h") || args.includes("--help")) {
|
|
111
|
-
usage(console.log);
|
|
112
|
-
exit(0);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
let relation = null;
|
|
116
|
-
let cacheDir = path.join(os.tmpdir(), "k-llvm-jit-cache");
|
|
117
|
-
let useCache = true;
|
|
118
|
-
while (args.length > 0 && args[0].startsWith("--")) {
|
|
119
|
-
const option = args.shift();
|
|
120
|
-
if (option === "--retype") {
|
|
121
|
-
relation = args.shift();
|
|
122
|
-
if (!relation) throw new Error("--retype requires a relation name");
|
|
123
|
-
} else if (option === "--cache-dir") {
|
|
124
|
-
cacheDir = args.shift();
|
|
125
|
-
if (!cacheDir) throw new Error("--cache-dir requires a path");
|
|
126
|
-
} else if (option === "--no-cache") {
|
|
127
|
-
useCache = false;
|
|
128
|
-
} else {
|
|
129
|
-
throw new Error(`Unknown option: ${option}`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const objectPath = args.shift() || null;
|
|
134
|
-
if (objectPath == null) throw new Error("object-file is required");
|
|
135
|
-
if (args.length > 0) throw new Error(`Unexpected argument: ${args[0]}`);
|
|
136
|
-
|
|
137
|
-
const objectBuffer = fs.readFileSync(objectPath);
|
|
138
|
-
const object = decodeObject(objectBuffer);
|
|
139
|
-
const relationName = relation || object.main;
|
|
140
|
-
const inputBuffer = await readStdinBuffer();
|
|
141
|
-
const { pattern: inputPattern } = decodeWire(inputBuffer);
|
|
142
|
-
|
|
143
|
-
let tmpDir = null;
|
|
144
|
-
try {
|
|
145
|
-
const exePath = useCache
|
|
146
|
-
? compileCached({ object, objectBuffer, relation: relationName, inputPattern, cacheDir })
|
|
147
|
-
: (() => {
|
|
148
|
-
const compiled = compileTemporary({ object, relation: relationName, inputPattern });
|
|
149
|
-
tmpDir = compiled.tmpDir;
|
|
150
|
-
return compiled.exePath;
|
|
151
|
-
})();
|
|
152
|
-
stdout.write(await runExecutable(exePath, inputBuffer));
|
|
153
|
-
} finally {
|
|
154
|
-
if (tmpDir != null) fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
155
|
-
}
|
|
156
|
-
} catch (error) {
|
|
157
|
-
console.error(error.stack || error.message || String(error));
|
|
158
|
-
usage();
|
|
159
|
-
exit(1);
|
|
160
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { BackendAdapter, BackendRunner } from "../backend.mjs";
|
|
4
|
-
import { PersistentExecutable } from "../persistent.mjs";
|
|
5
|
-
import { compileARM64ArtifactFromObject } from "../../backends/arm64/src/arm64.mjs";
|
|
6
|
-
import { decodeWire } from "../../backend-api.mjs";
|
|
7
|
-
|
|
8
|
-
class ARM64Runner extends BackendRunner {
|
|
9
|
-
constructor(backendId, servers = new Map()) {
|
|
10
|
-
super(backendId);
|
|
11
|
-
this.servers = servers;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
getServer(exePath) {
|
|
15
|
-
let server = this.servers.get(exePath);
|
|
16
|
-
if (!server) {
|
|
17
|
-
server = new PersistentExecutable(exePath);
|
|
18
|
-
this.servers.set(exePath, server);
|
|
19
|
-
}
|
|
20
|
-
return server;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async runSingle(testCase, options = {}) {
|
|
24
|
-
const server = this.getServer(testCase.exePath);
|
|
25
|
-
if (options.trace) {
|
|
26
|
-
const res = await server.requestWithTrace(testCase.inputWire);
|
|
27
|
-
const decoded = decodeWire(res.payload);
|
|
28
|
-
return {
|
|
29
|
-
outputWire: res.payload,
|
|
30
|
-
outputVal: decoded.value,
|
|
31
|
-
outputPattern: decoded.pattern,
|
|
32
|
-
trace: res.trace,
|
|
33
|
-
profile: res.trace?.profile
|
|
34
|
-
};
|
|
35
|
-
} else {
|
|
36
|
-
const payload = await server.request(testCase.inputWire);
|
|
37
|
-
const decoded = decodeWire(payload);
|
|
38
|
-
return {
|
|
39
|
-
outputWire: payload,
|
|
40
|
-
outputVal: decoded.value,
|
|
41
|
-
outputPattern: decoded.pattern
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async runBatch(compiledCases) {
|
|
47
|
-
for (const c of compiledCases) {
|
|
48
|
-
const server = this.getServer(c.exePath);
|
|
49
|
-
await server.request(c.inputWire);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async close() {
|
|
54
|
-
for (const server of this.servers.values()) {
|
|
55
|
-
server.close();
|
|
56
|
-
}
|
|
57
|
-
this.servers.clear();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export class ARM64Adapter extends BackendAdapter {
|
|
62
|
-
constructor(options = {}) {
|
|
63
|
-
const optLevel = options.optLevel || "-O2";
|
|
64
|
-
super("arm64", `Linux ARM64 ${optLevel} (persistent)`, options);
|
|
65
|
-
this.optLevel = optLevel;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
isAvailable() {
|
|
69
|
-
return process.arch === "arm64" && process.platform === "linux";
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async compile(testCase, options = {}) {
|
|
73
|
-
const cacheDir = options.cacheDir || path.join(process.cwd(), ".cache");
|
|
74
|
-
fs.mkdirSync(cacheDir, { recursive: true });
|
|
75
|
-
const exePath = path.join(cacheDir, `harness_arm64_${testCase.op || testCase.relName || "case"}`);
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
compileARM64ArtifactFromObject(testCase.object, {
|
|
79
|
-
entry: testCase.relName || testCase.op || testCase.relationName,
|
|
80
|
-
inputPattern: testCase.inputPattern,
|
|
81
|
-
outputPattern: testCase.outputPattern,
|
|
82
|
-
outputPath: exePath,
|
|
83
|
-
optLevel: this.optLevel,
|
|
84
|
-
profile: Boolean(options.profile)
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
status: "ok",
|
|
89
|
-
exePath,
|
|
90
|
-
inputWire: testCase.inputWire,
|
|
91
|
-
inputVal: testCase.inputVal,
|
|
92
|
-
outputPattern: testCase.outputPattern
|
|
93
|
-
};
|
|
94
|
-
} catch (error) {
|
|
95
|
-
return {
|
|
96
|
-
status: "failed",
|
|
97
|
-
error: error.stack || error.message || String(error)
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async createRunner(compiledCases) {
|
|
103
|
-
return new ARM64Runner(this.id);
|
|
104
|
-
}
|
|
105
|
-
}
|