@fraczak/k 6.3.3 → 6.5.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 +17 -0
- 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 +108 -82
- 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/Value.mjs +8 -2
- 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 +58 -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 +475 -0
- package/backends/wasm/scripts/run-tests.mjs +41 -0
- package/backends/wasm/src/cli.mjs +133 -0
- package/backends/wasm/src/kvm2wasm.mjs +697 -0
- package/backends/wasm/src/wasm.mjs +1054 -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 +625 -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 +12 -3
- 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
|
@@ -11,7 +11,10 @@ const outputPath = path.resolve(root, process.argv[3] || "benchmark-results.html
|
|
|
11
11
|
const rowOrder = [
|
|
12
12
|
"Native JS (Envelope-Aware)",
|
|
13
13
|
"Native JS (Envelope-Free)",
|
|
14
|
-
"kVM Interpreter (Env-Free)"
|
|
14
|
+
"kVM Interpreter (Env-Free)",
|
|
15
|
+
"KIR-P Export",
|
|
16
|
+
"k-wasm",
|
|
17
|
+
"k-llvm-jit"
|
|
15
18
|
];
|
|
16
19
|
|
|
17
20
|
const columnGroups = [
|
|
@@ -90,7 +93,7 @@ function cellFor(result, best) {
|
|
|
90
93
|
const ratio = best ? result.avgMs / best.avgMs : NaN;
|
|
91
94
|
return `
|
|
92
95
|
<div class="time">${fmtMs(result.avgMs)} <span>${fmtRatio(ratio)}</span></div>
|
|
93
|
-
<div class="sub">rss ${fmtMiB(result.maxRssMiB)}</div>`;
|
|
96
|
+
<div class="sub">warmup ${fmtMs(result.warmupMs)} · rss ${fmtMiB(result.maxRssMiB)}</div>`;
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
function factorialCell(byCase, allResults) {
|
|
@@ -151,6 +154,7 @@ function buildDetails(results) {
|
|
|
151
154
|
<td class="num">${fmtMs(result.avgMs)}</td>
|
|
152
155
|
<td class="num">${fmtMs(result.minMs)}</td>
|
|
153
156
|
<td class="num">${fmtMs(result.maxMs)}</td>
|
|
157
|
+
<td class="num">${fmtMs(result.warmupMs)}</td>
|
|
154
158
|
<td class="num">${fmtMiB(result.maxRssMiB)}</td>
|
|
155
159
|
<td class="hash">${escapeHtml(result.resultDigest || "")}</td>
|
|
156
160
|
</tr>`)
|
|
@@ -167,7 +171,7 @@ function render(doc) {
|
|
|
167
171
|
<head>
|
|
168
172
|
<meta charset="utf-8">
|
|
169
173
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
170
|
-
<title>k Benchmark Results</title>
|
|
174
|
+
<title>k Backend Benchmark Results</title>
|
|
171
175
|
<style>
|
|
172
176
|
:root {
|
|
173
177
|
color-scheme: light;
|
|
@@ -201,7 +205,7 @@ function render(doc) {
|
|
|
201
205
|
font-size: 14px;
|
|
202
206
|
}
|
|
203
207
|
main {
|
|
204
|
-
max-width:
|
|
208
|
+
max-width: 1480px;
|
|
205
209
|
margin: 0 auto;
|
|
206
210
|
padding: 24px 32px 40px;
|
|
207
211
|
}
|
|
@@ -237,7 +241,7 @@ function render(doc) {
|
|
|
237
241
|
font-weight: 700;
|
|
238
242
|
}
|
|
239
243
|
.matrix {
|
|
240
|
-
min-width:
|
|
244
|
+
min-width: 1120px;
|
|
241
245
|
}
|
|
242
246
|
.matrix .group {
|
|
243
247
|
text-align: center;
|
|
@@ -253,7 +257,7 @@ function render(doc) {
|
|
|
253
257
|
margin-top: 2px;
|
|
254
258
|
}
|
|
255
259
|
.row-head {
|
|
256
|
-
width:
|
|
260
|
+
width: 250px;
|
|
257
261
|
color: var(--ink);
|
|
258
262
|
font-weight: 800;
|
|
259
263
|
background: #fbfcfe;
|
|
@@ -302,7 +306,7 @@ function render(doc) {
|
|
|
302
306
|
</head>
|
|
303
307
|
<body>
|
|
304
308
|
<header>
|
|
305
|
-
<h1>k Benchmark Results</h1>
|
|
309
|
+
<h1>k Backend Benchmark Results</h1>
|
|
306
310
|
<div class="meta">
|
|
307
311
|
<span>Generated: ${escapeHtml(generatedAt)}</span>
|
|
308
312
|
<span>Node: ${escapeHtml(doc.node || "unknown")}</span>
|
|
@@ -326,7 +330,7 @@ function render(doc) {
|
|
|
326
330
|
<div class="panel">
|
|
327
331
|
<table>
|
|
328
332
|
<thead>
|
|
329
|
-
<tr><th>Case</th><th>Mode</th><th>Status</th><th>Avg</th><th>Min</th><th>Max</th><th>Max RSS</th><th>Digest</th></tr>
|
|
333
|
+
<tr><th>Case</th><th>Mode</th><th>Status</th><th>Avg</th><th>Min</th><th>Max</th><th>Warmup</th><th>Max RSS</th><th>Digest</th></tr>
|
|
330
334
|
</thead>
|
|
331
335
|
<tbody>${buildDetails(results)}</tbody>
|
|
332
336
|
</table>
|
package/scripts/run-tests.mjs
CHANGED
|
@@ -21,9 +21,15 @@ const tests = [
|
|
|
21
21
|
["tests/test-structural-values.mjs", node, ["tests/test-structural-values.mjs"]],
|
|
22
22
|
["tests/test-deep-wire-value.mjs", node, ["tests/test-deep-wire-value.mjs"]],
|
|
23
23
|
["tests/test-k-object.mjs", node, ["tests/test-k-object.mjs"]],
|
|
24
|
+
["tests/test-kir.mjs", node, ["tests/test-kir.mjs"]],
|
|
25
|
+
["tests/test-validate-object.mjs", node, ["tests/test-validate-object.mjs"]],
|
|
26
|
+
["conformance/run.mjs", node, ["conformance/run.mjs"]],
|
|
24
27
|
["tests/test-repl.mjs", node, ["tests/test-repl.mjs"]],
|
|
28
|
+
["tests/test-repl-html.mjs", node, ["tests/test-repl-html.mjs"]],
|
|
25
29
|
["tests/test-kvm.mjs", node, ["tests/test-kvm.mjs"]],
|
|
30
|
+
["tests/test-kvm-polymorphic.mjs", node, ["tests/test-kvm-polymorphic.mjs"]],
|
|
26
31
|
["tests/test-arithmetics.mjs", node, ["tests/test-arithmetics.mjs"]],
|
|
32
|
+
["tests/test-poly.mjs", node, ["tests/test-poly.mjs"]],
|
|
27
33
|
["tests/test-ieee-arithmetic.mjs", node, ["tests/test-ieee-arithmetic.mjs"]],
|
|
28
34
|
["tests/integration.sh", path.join(root, "tests/integration.sh"), []]
|
|
29
35
|
];
|
package/tests/integration.sh
CHANGED
|
@@ -10,11 +10,28 @@ trap 'rm -rf "$TMP_DIR"' EXIT
|
|
|
10
10
|
|
|
11
11
|
./objects/compile.mjs 'x = |x; x x' "$TMP_DIR/inline.ko"
|
|
12
12
|
./objects/decompile.mjs "$TMP_DIR/inline.ko" | grep -q '^----- main -----$'
|
|
13
|
+
./objects/inspect.mjs "$TMP_DIR/inline.ko" | grep -q '^format: k-object$'
|
|
14
|
+
./objects/inspect.mjs --kir "$TMP_DIR/inline.ko" | grep -q '"format": "k-ir"'
|
|
15
|
+
./kir.mjs --help | grep -q 'Export the KIR-P JSON view'
|
|
16
|
+
./kir.mjs "$TMP_DIR/inline.ko" | grep -q '"format": "k-ir"'
|
|
17
|
+
./objects/validate.mjs "$TMP_DIR/inline.ko" | grep -q '^OK object$'
|
|
18
|
+
./kir.mjs "$TMP_DIR/inline.ko" > "$TMP_DIR/inline.kir.json"
|
|
19
|
+
./objects/validate.mjs --kir "$TMP_DIR/inline.kir.json" | grep -q '^OK KIR-P$'
|
|
20
|
+
printf '[["open-product",[]]]' > "$TMP_DIR/input.pattern.json"
|
|
21
|
+
./kir.mjs --retype __main__ --input-pattern "$TMP_DIR/input.pattern.json" "$TMP_DIR/inline.ko" > "$TMP_DIR/inline.retyped.kir.json"
|
|
22
|
+
./objects/validate.mjs --kir "$TMP_DIR/inline.retyped.kir.json" | grep -q '^OK KIR-P$'
|
|
23
|
+
printf '[["closed-product",[]]]' > "$TMP_DIR/unit.pattern.json"
|
|
13
24
|
printf '()' | ./objects/compile.mjs > "$TMP_DIR/stdin.ko"
|
|
14
25
|
./objects/decompile.mjs "$TMP_DIR/stdin.ko" | grep -q '^----- main -----$'
|
|
15
|
-
./objects/compile.mjs --format kvm '()' | grep -q '"
|
|
16
|
-
./objects/compile.mjs
|
|
26
|
+
./objects/compile.mjs --format kvm '()' | grep -q '"format": "k-vm"'
|
|
27
|
+
./objects/compile.mjs --format kvm '()' > "$TMP_DIR/stdin.kvm"
|
|
28
|
+
grep -q '"format": "k-vm"' "$TMP_DIR/stdin.kvm"
|
|
29
|
+
grep -q '"layer": "KVM-P"' "$TMP_DIR/stdin.kvm"
|
|
30
|
+
grep -q '"isPolymorphic": true' "$TMP_DIR/stdin.kvm"
|
|
17
31
|
grep -q '"__main__"' "$TMP_DIR/stdin.kvm"
|
|
32
|
+
./objects/compile.mjs "$TMP_DIR/stdin.ko" "$TMP_DIR/poly-object.kvm"
|
|
33
|
+
grep -q '"layer": "KVM-P"' "$TMP_DIR/poly-object.kvm"
|
|
34
|
+
grep -q '"isPolymorphic": true' "$TMP_DIR/poly-object.kvm"
|
|
18
35
|
./objects/compile.mjs 'id = ();' "$TMP_DIR/export.klib"
|
|
19
36
|
./objects/compile.mjs --lib "$TMP_DIR/export.klib" --export id:alias 'alias' "$TMP_DIR/export.ko"
|
|
20
37
|
./objects/decompile.mjs "$TMP_DIR/export.ko" | grep -q '^----- main -----$'
|
|
@@ -89,6 +89,60 @@ try {
|
|
|
89
89
|
);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// Regression tests for inc/dec normalization and div
|
|
93
|
+
const resDec = run(node, [
|
|
94
|
+
"k.mjs",
|
|
95
|
+
"--lib", libPath,
|
|
96
|
+
"--export", "dec:dec",
|
|
97
|
+
"--export", "zero_int?:zero_int?",
|
|
98
|
+
"dec dec zero_int?"
|
|
99
|
+
], { input: parseIntWire("2") });
|
|
100
|
+
assertOk(resDec, "2 int dec dec zero_int? should succeed");
|
|
101
|
+
|
|
102
|
+
const divTestCases = [
|
|
103
|
+
{ x: "4", yAlias: "1", yVal: 1n },
|
|
104
|
+
{ x: "10", yAlias: "2", yVal: 2n },
|
|
105
|
+
{ x: "12", yAlias: "3", yVal: 3n },
|
|
106
|
+
{ x: "11", yAlias: "3", yVal: 3n },
|
|
107
|
+
{ x: "100", yAlias: "7", yVal: 7n },
|
|
108
|
+
{ x: "-100", yAlias: "7", yVal: 7n },
|
|
109
|
+
{ x: "1000", yAlias: "10", yVal: 10n }
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
for (const { x, yAlias, yVal } of divTestCases) {
|
|
113
|
+
const xBig = BigInt(x);
|
|
114
|
+
const expectedDiv = (xBig / yVal).toString();
|
|
115
|
+
const xAbs = xBig < 0n ? -xBig : xBig;
|
|
116
|
+
const yAbs = yVal < 0n ? -yVal : yVal;
|
|
117
|
+
const expectedRem = (xAbs % yAbs).toString();
|
|
118
|
+
|
|
119
|
+
const wire = parseIntWire(x);
|
|
120
|
+
|
|
121
|
+
const resDiv = run(node, [
|
|
122
|
+
"k.mjs",
|
|
123
|
+
"--lib", libPath,
|
|
124
|
+
"--export", "div:div",
|
|
125
|
+
"--export", "int:int",
|
|
126
|
+
"--export", `${yAlias}:divisor`,
|
|
127
|
+
"{() x, divisor int y} div .div"
|
|
128
|
+
], { input: wire });
|
|
129
|
+
assertOk(resDiv, `native div(${x}, ${yAlias}) quotient`);
|
|
130
|
+
const divOut = printIntWire(resDiv.stdout, `div quotient`);
|
|
131
|
+
assert.equal(divOut, expectedDiv, `div(${x}, ${yAlias}) quotient should match BigInt oracle`);
|
|
132
|
+
|
|
133
|
+
const resRem = run(node, [
|
|
134
|
+
"k.mjs",
|
|
135
|
+
"--lib", libPath,
|
|
136
|
+
"--export", "div:div",
|
|
137
|
+
"--export", "int:int",
|
|
138
|
+
"--export", `${yAlias}:divisor`,
|
|
139
|
+
"{() x, divisor int y} div .rem int"
|
|
140
|
+
], { input: wire });
|
|
141
|
+
assertOk(resRem, `native div(${x}, ${yAlias}) remainder`);
|
|
142
|
+
const remOut = printIntWire(resRem.stdout, `div remainder`);
|
|
143
|
+
assert.equal(remOut, expectedRem, `div(${x}, ${yAlias}) remainder should match BigInt oracle`);
|
|
144
|
+
}
|
|
145
|
+
|
|
92
146
|
console.log("OK");
|
|
93
147
|
} finally {
|
|
94
148
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
@@ -69,6 +69,10 @@ const cases = [
|
|
|
69
69
|
{ op: "mul", x: "-2", y: "4", result: "-8", flags: "none" },
|
|
70
70
|
{ op: "mul", x: "Infinity", y: "0", result: "NaN", flags: "invalid" },
|
|
71
71
|
{ op: "div", x: "1", y: "2", result: "0.5" },
|
|
72
|
+
{ op: "div", x: "9", y: "3", result: "3", flags: "none", projection: true },
|
|
73
|
+
{ op: "div", x: "10", y: "2", result: "5", flags: "none" },
|
|
74
|
+
{ op: "div", x: "1", y: "3", result: "0.3333333333333333", flags: "inexact" },
|
|
75
|
+
{ op: "div", x: "-6", y: "2", result: "-3", flags: "none" },
|
|
72
76
|
{ op: "div", x: "1", y: "0", result: "Infinity", flags: "div_by_zero" },
|
|
73
77
|
{ op: "div", x: "0", y: "0", result: "NaN", flags: "invalid" }
|
|
74
78
|
];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { compileLibraryBuffer, compileObjectBuffer, decodeObject } from "../object.mjs";
|
|
3
|
+
import { objectToKIRP, retypeObjectRelation } from "../kir.mjs";
|
|
4
|
+
import {
|
|
5
|
+
objectToKIRP as backendObjectToKIRP,
|
|
6
|
+
objectToKVMArtifact,
|
|
7
|
+
retypeObjectRelation as backendRetypeObjectRelation
|
|
8
|
+
} from "../backend-api.mjs";
|
|
9
|
+
import { validateKIRP } from "../objects/validate.mjs";
|
|
10
|
+
|
|
11
|
+
const object = decodeObject(compileObjectBuffer(`
|
|
12
|
+
$ bit = < {} 0, {} 1 >;
|
|
13
|
+
id = ();
|
|
14
|
+
.x id
|
|
15
|
+
`, { source: "kir-test.k" }));
|
|
16
|
+
const kir = objectToKIRP(object);
|
|
17
|
+
assert.equal(backendObjectToKIRP(object).format, "k-ir");
|
|
18
|
+
|
|
19
|
+
assert.equal(kir.format, "k-ir");
|
|
20
|
+
assert.equal(kir.version, 1);
|
|
21
|
+
assert.equal(kir.layer, "KIR-P");
|
|
22
|
+
assert.equal(kir.sourceFormat, "k-object");
|
|
23
|
+
assert.equal(kir.kind, "executable");
|
|
24
|
+
assert.equal(kir.main, "__main__");
|
|
25
|
+
assert.equal(kir.rels.__main__.typeDerivation.status, "converged");
|
|
26
|
+
assert.equal(typeof kir.rels.__main__.inputPattern, "number");
|
|
27
|
+
assert.equal(typeof kir.rels.__main__.outputPattern, "number");
|
|
28
|
+
assert(Array.isArray(kir.rels.__main__.patternGraph.nodes));
|
|
29
|
+
assert(kir.rels.__main__.patternGraph.nodes.every((node, index) => node.id === index));
|
|
30
|
+
assert.deepEqual(new Set(kir.rels.__main__.patternGraph.nodes.map((node) => node.kind)).has("open-product"), true);
|
|
31
|
+
assert.equal(kir.rels.__main__.body.op, "dot");
|
|
32
|
+
assert.equal(kir.rels.__main__.body.label, "x");
|
|
33
|
+
const retyped = validateKIRP(retypeObjectRelation(object, "__main__", [
|
|
34
|
+
["closed-product", [["x", 1]]],
|
|
35
|
+
["closed-product", []]
|
|
36
|
+
]));
|
|
37
|
+
assert.equal(retyped.layer, "KIR-P");
|
|
38
|
+
assert.equal(retyped.kind, "executable");
|
|
39
|
+
assert.equal(retyped.main, "__main__");
|
|
40
|
+
assert.equal(retyped.rels.__main__.body.op, "ref");
|
|
41
|
+
assert.equal(retyped.rels.__main__.body.ref, "__kir_target__");
|
|
42
|
+
assert(!("instanceKey" in retyped));
|
|
43
|
+
assert(!("callSites" in retyped));
|
|
44
|
+
assert.equal(backendRetypeObjectRelation(object, "__main__", [["open-product", []]]).layer, "KIR-P");
|
|
45
|
+
assert.throws(
|
|
46
|
+
() => objectToKVMArtifact(object, "__main__", [["open-product", []]]),
|
|
47
|
+
/\.kvm emission requires a singleton input pattern/
|
|
48
|
+
);
|
|
49
|
+
const kvmArtifact = objectToKVMArtifact(object, "__main__", [
|
|
50
|
+
["closed-product", [["x", 1]]],
|
|
51
|
+
["closed-product", []]
|
|
52
|
+
]);
|
|
53
|
+
assert.equal(kvmArtifact.format, "k-vm");
|
|
54
|
+
assert.equal(kvmArtifact.layer, "KVM");
|
|
55
|
+
assert.equal(kvmArtifact.entry, "__main__");
|
|
56
|
+
assert.deepEqual(kvmArtifact.inputPattern, [
|
|
57
|
+
["closed-product", [["x", 1]]],
|
|
58
|
+
["closed-product", []]
|
|
59
|
+
]);
|
|
60
|
+
assert.deepEqual(kvmArtifact.outputPattern, [["closed-product", []]]);
|
|
61
|
+
assert.ok(kvmArtifact.functions.__main__);
|
|
62
|
+
assert.equal(kvmArtifact.kir.layer, "KIR-P");
|
|
63
|
+
assert(!("instanceKey" in kvmArtifact));
|
|
64
|
+
|
|
65
|
+
const helperObject = decodeObject(compileObjectBuffer("pick = .x; {.a pick left, .b pick right}", { source: "kir-helper.k" }));
|
|
66
|
+
const helperRetyped = validateKIRP(retypeObjectRelation(helperObject, "__main__", [
|
|
67
|
+
["open-product", [["a", 1], ["b", 2]]],
|
|
68
|
+
["open-product", [["x", 3]]],
|
|
69
|
+
["open-product", [["x", 4]]],
|
|
70
|
+
["closed-product", []],
|
|
71
|
+
["closed-product", []]
|
|
72
|
+
]));
|
|
73
|
+
assert.equal(helperRetyped.layer, "KIR-P");
|
|
74
|
+
assert.equal(helperRetyped.kind, "executable");
|
|
75
|
+
assert.ok(helperRetyped.rels.pick);
|
|
76
|
+
assert(!("instanceKey" in helperRetyped));
|
|
77
|
+
assert(!("callSites" in helperRetyped));
|
|
78
|
+
|
|
79
|
+
const library = decodeObject(compileLibraryBuffer("succ = |succ;\n", { source: "kir-lib.k" }));
|
|
80
|
+
const libraryKir = objectToKIRP(library);
|
|
81
|
+
assert.equal(libraryKir.kind, "library");
|
|
82
|
+
assert.equal(libraryKir.main, null);
|
|
83
|
+
assert(Object.values(libraryKir.rels).some((rel) => rel.body.op === "vid"));
|
|
84
|
+
|
|
85
|
+
console.log("OK");
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import k from "../index.mjs";
|
|
3
|
+
import {
|
|
4
|
+
objectToKVMArtifact,
|
|
5
|
+
specializeKVM,
|
|
6
|
+
executeKVM
|
|
7
|
+
} from "../kvm.mjs";
|
|
8
|
+
import { compileObjectBuffer, decodeObject } from "../object.mjs";
|
|
9
|
+
import { objectToKIRP } from "../kir.mjs";
|
|
10
|
+
import { fromObject, Value } from "../Value.mjs";
|
|
11
|
+
import { lowerToWasm } from "../backends/wasm/src/kvm2wasm.mjs";
|
|
12
|
+
import wabtFactory from "wabt";
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { spawnSync } from "node:child_process";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const runtimeWat = fs.readFileSync(path.join(__dirname, "../backends/wasm/runtime.wat"), "utf8");
|
|
20
|
+
|
|
21
|
+
function toJSON(value) {
|
|
22
|
+
if (value === undefined) return undefined;
|
|
23
|
+
return JSON.parse(JSON.stringify(value));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log("==> Starting Polymorphic kVM & Specialization Tests");
|
|
27
|
+
|
|
28
|
+
// Test 1: Compile Polymorphic kVM Artifact (AOT) without input pattern
|
|
29
|
+
{
|
|
30
|
+
const script = `
|
|
31
|
+
map = { .a first, .b second };
|
|
32
|
+
map
|
|
33
|
+
`;
|
|
34
|
+
const obj = decodeObject(compileObjectBuffer(script));
|
|
35
|
+
const kvmArtifact = objectToKVMArtifact(obj);
|
|
36
|
+
|
|
37
|
+
assert.equal(kvmArtifact.format, "k-vm");
|
|
38
|
+
assert.equal(kvmArtifact.layer, "KVM-P");
|
|
39
|
+
assert.equal(kvmArtifact.isPolymorphic, true);
|
|
40
|
+
assert.ok(kvmArtifact.functions["__main__"]);
|
|
41
|
+
assert.ok(Array.isArray(kvmArtifact.inputPattern));
|
|
42
|
+
console.log("Test 1 (Polymorphic kVM AOT Artifact): Passed");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Test 2: Specialize Polymorphic kVM with Conforming Envelope & Subtyping
|
|
46
|
+
{
|
|
47
|
+
const script = `
|
|
48
|
+
map = { .a first, .b second };
|
|
49
|
+
map
|
|
50
|
+
`;
|
|
51
|
+
const obj = decodeObject(compileObjectBuffer(script));
|
|
52
|
+
const kvmArtifact = objectToKVMArtifact(obj);
|
|
53
|
+
|
|
54
|
+
// Input envelope carries extra field 'extra'
|
|
55
|
+
const inputEnvelope = [
|
|
56
|
+
["closed-product", [["a", 1], ["b", 2], ["extra", 3]]],
|
|
57
|
+
["closed-product", []],
|
|
58
|
+
["closed-union", [["valB", 4]]],
|
|
59
|
+
["closed-product", []],
|
|
60
|
+
["closed-product", []]
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const specialized = specializeKVM(kvmArtifact, inputEnvelope);
|
|
64
|
+
assert.equal(specialized.isSpecialized, true);
|
|
65
|
+
assert.equal(specialized.layer, "KVM-M");
|
|
66
|
+
|
|
67
|
+
const specFunc = specialized.functions[specialized.entry];
|
|
68
|
+
assert.ok(specFunc);
|
|
69
|
+
assert.equal(specFunc.isSpecialized, true);
|
|
70
|
+
|
|
71
|
+
// Execute in envelopeFree mode
|
|
72
|
+
const val = fromObject({
|
|
73
|
+
a: { unit: {} },
|
|
74
|
+
b: { valB: {} },
|
|
75
|
+
extra: { dummy: {} }
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const res = executeKVM(specFunc, val, {
|
|
79
|
+
rels: kvmArtifact.kir.rels,
|
|
80
|
+
codes: kvmArtifact.codes,
|
|
81
|
+
options: { envelopeFree: true }
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
assert.deepEqual(toJSON(res), {
|
|
85
|
+
first: "unit",
|
|
86
|
+
second: "valB"
|
|
87
|
+
});
|
|
88
|
+
console.log("Test 2 (Specialize with Subtyping & Envelope-Free Execution): Passed");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Test 3: Type Error on Envelope Mismatch
|
|
92
|
+
{
|
|
93
|
+
const script = `
|
|
94
|
+
$ bool = < {} true, {} false >;
|
|
95
|
+
check = $bool;
|
|
96
|
+
check
|
|
97
|
+
`;
|
|
98
|
+
const obj = decodeObject(compileObjectBuffer(script));
|
|
99
|
+
const kvmArtifact = objectToKVMArtifact(obj);
|
|
100
|
+
|
|
101
|
+
// An envelope with variant 'other' does not conform to bool
|
|
102
|
+
const incompatibleEnvelope = [
|
|
103
|
+
["closed-union", [["other", 1]]],
|
|
104
|
+
["closed-product", []]
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
assert.throws(
|
|
108
|
+
() => specializeKVM(kvmArtifact, incompatibleEnvelope),
|
|
109
|
+
(err) => {
|
|
110
|
+
assert.ok(err instanceof TypeError);
|
|
111
|
+
assert.ok(err.message.includes("Type Error"));
|
|
112
|
+
return true;
|
|
113
|
+
},
|
|
114
|
+
"Expected TypeError on incompatible envelope"
|
|
115
|
+
);
|
|
116
|
+
console.log("Test 3 (Type Error on Envelope Mismatch): Passed");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Test 4: Dead Branch Elimination in Union Specialization
|
|
120
|
+
{
|
|
121
|
+
const script = `
|
|
122
|
+
choose = < /x, /y >;
|
|
123
|
+
choose
|
|
124
|
+
`;
|
|
125
|
+
const obj = decodeObject(compileObjectBuffer(script));
|
|
126
|
+
const kvmArtifact = objectToKVMArtifact(obj);
|
|
127
|
+
const unspecFunc = kvmArtifact.functions[kvmArtifact.entry];
|
|
128
|
+
const unspecUnion = unspecFunc.body.find(inst => inst.op === "union");
|
|
129
|
+
assert.equal(unspecUnion.branches.length, 2, "Unspecialized union has 2 branches");
|
|
130
|
+
|
|
131
|
+
// Specialize with envelope containing ONLY variant 'x'
|
|
132
|
+
const xEnvelope = [
|
|
133
|
+
["closed-union", [["x", 1]]],
|
|
134
|
+
["closed-product", []]
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
const specialized = specializeKVM(kvmArtifact, xEnvelope);
|
|
138
|
+
const specFunc = specialized.functions[specialized.entry];
|
|
139
|
+
const specUnion = specFunc.body.find(inst => inst.op === "union");
|
|
140
|
+
|
|
141
|
+
// The 'y' branch should be pruned!
|
|
142
|
+
assert.equal(specUnion.branches.length, 1, "Specialized union should prune unreachable branch");
|
|
143
|
+
assert.equal(specUnion.branches[0].body[0].tag, "x");
|
|
144
|
+
|
|
145
|
+
// Execution on { x: payload }
|
|
146
|
+
const valX = fromObject({ x: { payload: {} } });
|
|
147
|
+
const resX = executeKVM(specFunc, valX, {
|
|
148
|
+
rels: kvmArtifact.kir.rels,
|
|
149
|
+
codes: kvmArtifact.codes,
|
|
150
|
+
options: { envelopeFree: true }
|
|
151
|
+
});
|
|
152
|
+
assert.deepEqual(toJSON(resX), "payload");
|
|
153
|
+
|
|
154
|
+
console.log("Test 4 (Dead Branch Elimination in Union): Passed");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Test 5: Redundant Guard Elimination
|
|
158
|
+
{
|
|
159
|
+
const script = `
|
|
160
|
+
filterTest = ?{ ... } (.a);
|
|
161
|
+
filterTest
|
|
162
|
+
`;
|
|
163
|
+
const obj = decodeObject(compileObjectBuffer(script));
|
|
164
|
+
const kvmArtifact = objectToKVMArtifact(obj);
|
|
165
|
+
|
|
166
|
+
const inputEnvelope = [
|
|
167
|
+
["closed-product", [["a", 1]]],
|
|
168
|
+
["closed-product", []]
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
const specialized = specializeKVM(kvmArtifact, inputEnvelope);
|
|
172
|
+
const specFunc = specialized.functions[specialized.entry];
|
|
173
|
+
|
|
174
|
+
// The guard_pattern was checking open product, which inputEnvelope already satisfies.
|
|
175
|
+
// It should be simplified to 'id' or eliminated!
|
|
176
|
+
const hasUnsimplifiedGuard = specFunc.body.some(inst => inst.op === "guard_pattern");
|
|
177
|
+
assert.equal(hasUnsimplifiedGuard, false, "Redundant guard should be eliminated or simplified to id");
|
|
178
|
+
|
|
179
|
+
console.log("Test 5 (Redundant Guard Elimination): Passed");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Test 6: Fast Compilation to WebAssembly from Specialized kVM
|
|
183
|
+
{
|
|
184
|
+
const script = `
|
|
185
|
+
map = { .a first, .b second };
|
|
186
|
+
map
|
|
187
|
+
`;
|
|
188
|
+
const obj = decodeObject(compileObjectBuffer(script));
|
|
189
|
+
const kvmArtifact = objectToKVMArtifact(obj);
|
|
190
|
+
|
|
191
|
+
const inputEnvelope = [
|
|
192
|
+
["closed-product", [["a", 1], ["b", 2], ["extra", 3]]],
|
|
193
|
+
["closed-product", []],
|
|
194
|
+
["closed-product", []],
|
|
195
|
+
["closed-product", []]
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
const specialized = specializeKVM(kvmArtifact, inputEnvelope);
|
|
199
|
+
const specFunc = specialized.functions[specialized.entry];
|
|
200
|
+
|
|
201
|
+
// Lower directly to Wasm WAT
|
|
202
|
+
const watBody = lowerToWasm(specFunc, "map_spec");
|
|
203
|
+
assert.ok(watBody.includes("(func $map_spec"));
|
|
204
|
+
|
|
205
|
+
// Assemble with wabt to ensure it is 100% valid Wasm
|
|
206
|
+
const wabt = await wabtFactory();
|
|
207
|
+
const fullWat = runtimeWat.trim().slice(0, -1) + "\n" + watBody + "\n)";
|
|
208
|
+
const wasmModule = wabt.parseWat("test.wat", fullWat, {
|
|
209
|
+
mutable_globals: true,
|
|
210
|
+
sat_float_to_int: true,
|
|
211
|
+
sign_extension: true,
|
|
212
|
+
multi_value: true,
|
|
213
|
+
bulk_memory: true,
|
|
214
|
+
reference_types: true
|
|
215
|
+
});
|
|
216
|
+
wasmModule.resolveNames();
|
|
217
|
+
wasmModule.validate();
|
|
218
|
+
const binary = wasmModule.toBinary({ log: false, write_debug_names: false });
|
|
219
|
+
assert.ok(binary.buffer.byteLength > 0);
|
|
220
|
+
|
|
221
|
+
console.log("Test 6 (Direct Lowering to WebAssembly): Passed");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Test 7: CLI Compilation to Polymorphic .kvm without --input-pattern
|
|
225
|
+
{
|
|
226
|
+
const compileScript = path.join(__dirname, "../objects/compile.mjs");
|
|
227
|
+
const res = spawnSync(process.execPath, [compileScript, "--format", "kvm", "Examples/arithmetics.k"], {
|
|
228
|
+
cwd: path.join(__dirname, ".."),
|
|
229
|
+
encoding: "utf8"
|
|
230
|
+
});
|
|
231
|
+
assert.equal(res.status, 0, `CLI compile failed: ${res.stderr}`);
|
|
232
|
+
const kvm = JSON.parse(res.stdout);
|
|
233
|
+
assert.equal(kvm.format, "k-vm");
|
|
234
|
+
assert.equal(kvm.layer, "KVM-P");
|
|
235
|
+
assert.equal(kvm.isPolymorphic, true);
|
|
236
|
+
assert.ok(kvm.functions["__main__"]);
|
|
237
|
+
console.log("Test 7 (CLI Compilation to Polymorphic .kvm): Passed");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Test 8: CLI Compilation with --lib and explicit --export (no auto-export)
|
|
241
|
+
{
|
|
242
|
+
const compileScript = path.join(__dirname, "../objects/compile.mjs");
|
|
243
|
+
// Fails without explicit --export
|
|
244
|
+
const failRes = spawnSync(process.execPath, [
|
|
245
|
+
compileScript,
|
|
246
|
+
"--lib", "Examples/arithmetics.k",
|
|
247
|
+
"{succ int x,int y}+"
|
|
248
|
+
], {
|
|
249
|
+
cwd: path.join(__dirname, ".."),
|
|
250
|
+
encoding: "utf8"
|
|
251
|
+
});
|
|
252
|
+
assert.notEqual(failRes.status, 0, "Should fail without explicit --export");
|
|
253
|
+
|
|
254
|
+
// Succeeds with explicit --export
|
|
255
|
+
const successRes = spawnSync(process.execPath, [
|
|
256
|
+
compileScript,
|
|
257
|
+
"--lib", "Examples/arithmetics.k",
|
|
258
|
+
"--export", "plus:+",
|
|
259
|
+
"--export", "succ",
|
|
260
|
+
"--export", "int",
|
|
261
|
+
"--format", "kvm",
|
|
262
|
+
"{succ int x,int y}+"
|
|
263
|
+
], {
|
|
264
|
+
cwd: path.join(__dirname, ".."),
|
|
265
|
+
encoding: "utf8",
|
|
266
|
+
maxBuffer: 10 * 1024 * 1024
|
|
267
|
+
});
|
|
268
|
+
assert.equal(successRes.status, 0, `CLI compile with --export failed: ${successRes.stderr}`);
|
|
269
|
+
const kvm = JSON.parse(successRes.stdout);
|
|
270
|
+
assert.equal(kvm.format, "k-vm");
|
|
271
|
+
assert.equal(kvm.layer, "KVM-P");
|
|
272
|
+
assert.equal(kvm.isPolymorphic, true);
|
|
273
|
+
assert.ok(kvm.functions["__main__"]);
|
|
274
|
+
console.log("Test 8 (CLI Compilation with --lib and explicit --export): Passed");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
console.log("==> All Polymorphic kVM Tests Passed Successfully!");
|
|
278
|
+
|
package/tests/test-kvm.mjs
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import k from "../index.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { lowerKIRRelationToKVM, executeKVM } from "../kvm.mjs";
|
|
4
4
|
import codes from "../codes.mjs";
|
|
5
5
|
import { fromObject } from "../Value.mjs";
|
|
6
6
|
import run, { run_converged } from "../run.mjs";
|
|
7
|
+
import { compileObjectBuffer, decodeObject } from "../object.mjs";
|
|
8
|
+
import { objectToKIRP } from "../kir.mjs";
|
|
7
9
|
|
|
8
10
|
function runKVM(script, data, options = {}) {
|
|
9
|
-
const
|
|
11
|
+
const object = decodeObject(compileObjectBuffer(script, {
|
|
10
12
|
convergence: { strategy: "auto" }
|
|
11
|
-
});
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
}));
|
|
14
|
+
const kir = objectToKIRP(object);
|
|
15
|
+
const mainRel = kir.rels[kir.main];
|
|
16
|
+
const kvmFunc = lowerKIRRelationToKVM(mainRel, kir.main, { codes: kir.codes });
|
|
17
|
+
const context = {
|
|
18
|
+
rels: kir.rels,
|
|
19
|
+
codes: kir.codes,
|
|
20
|
+
findCode: (hash) => kir.codes[hash] || codes.find(hash),
|
|
21
|
+
options
|
|
22
|
+
};
|
|
15
23
|
return executeKVM(kvmFunc, fromObject(data), context);
|
|
16
24
|
}
|
|
17
25
|
|
|
@@ -160,4 +168,15 @@ console.log("==> Starting kVM Tests");
|
|
|
160
168
|
console.log("Test 9 (KVM vs JS run equivalence & Peano addition): Passed");
|
|
161
169
|
}
|
|
162
170
|
|
|
171
|
+
// Test 10: Intrinsic handling is explicit, not based on identifier spelling
|
|
172
|
+
{
|
|
173
|
+
const result = runKVM("_user = .a; _user", { a: {}, b: {} });
|
|
174
|
+
assert.deepEqual(toJSON(result), {});
|
|
175
|
+
assert.throws(
|
|
176
|
+
() => runKVM("_log!", { unit: {} }),
|
|
177
|
+
/kVM interpreter: unsupported intrinsic '_log!'/
|
|
178
|
+
);
|
|
179
|
+
console.log("Test 10 (Explicit intrinsics): Passed");
|
|
180
|
+
}
|
|
181
|
+
|
|
163
182
|
console.log("==> All kVM Tests Passed!");
|