@fraczak/k 6.10.3 → 6.10.4
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 +43 -0
- package/DOCS/FILE_FORMATS.md +51 -16
- package/Examples/karatsuba-mult.k +215 -0
- package/backends/arm64/scripts/run-tests.mjs +16 -0
- package/backends/llvm/bin/k-llvm-build.mjs +40 -14
- package/backends/llvm/bin/k-llvm-compile.mjs +29 -12
- package/backends/llvm/runtime/krt.c +10 -10
- package/backends/llvm/runtime/krt.h +6 -1
- package/backends/llvm/scripts/run-tests.mjs +16 -0
- package/backends/llvm/src/cli.mjs +1 -1
- package/backends/llvm/src/executable.mjs +36 -0
- package/backends/llvm/src/kvm2llvm.mjs +10 -4
- package/backends/llvm/tests/integration.sh +11 -0
- package/backends/llvm/tests/test-llvm.mjs +15 -1
- package/backends/wasm/scripts/run-tests.mjs +16 -0
- package/browser/vfs-data.mjs +1 -1
- package/core.k +5 -5
- package/package.json +2 -1
- package/scripts/benchmark-idempotency.mjs +27 -0
- package/scripts/benchmark.mjs +51 -0
- package/scripts/build-repl-html.mjs +17 -0
- package/scripts/generate-dependency-graph.mjs +25 -1
- package/scripts/perf-ieee.mjs +75 -0
- package/scripts/perf-int.mjs +67 -0
- package/scripts/perf-multiplication.mjs +644 -0
- package/scripts/perf-poly.mjs +67 -0
- package/scripts/render-benchmark.mjs +25 -0
- package/scripts/run-tests.mjs +19 -0
- package/tests/test-karatsuba.mjs +133 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,49 @@ All notable changes to this project will be documented here.
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
6
|
This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [6.10.4] — 2026-10-06 — Karatsuba Multiplication, Multi-Backend Harness, and CLI Help
|
|
9
|
+
|
|
10
|
+
### Arithmetic & Algorithms
|
|
11
|
+
|
|
12
|
+
- **Karatsuba Multiplication (`Examples/karatsuba-mult.k`)**:
|
|
13
|
+
- Implemented standalone, minimal Karatsuba integer multiplication with
|
|
14
|
+
asymptotic time complexity $O(n^{\log_2 3}) \approx O(n^{1.585})$.
|
|
15
|
+
- Utilizes unary counter (`$unat`) splitting with a 512-bit threshold
|
|
16
|
+
switching to grade-school multiplication (`times_bits`) for small
|
|
17
|
+
inputs.
|
|
18
|
+
- Implements direct bit-level arithmetic (`plus_bits`, `minus_bits`,
|
|
19
|
+
`times_bits`) and direct $2m$ bit shifting (`shift2_bits_u`).
|
|
20
|
+
- Added dedicated test suite in `tests/test-karatsuba.mjs` verifying
|
|
21
|
+
squaring cases and mixed-sign pair multiplications against BigInt
|
|
22
|
+
oracle.
|
|
23
|
+
|
|
24
|
+
### Performance & Benchmarking
|
|
25
|
+
|
|
26
|
+
- **Multiplication Performance Harness**
|
|
27
|
+
(`scripts/perf-multiplication.mjs`):
|
|
28
|
+
- Added multi-backend performance runner comparing `times` and
|
|
29
|
+
`karatsuba` across LLVM, Wasm, kVM, and JS engines.
|
|
30
|
+
- Supports `--mode squaring` (doubling bit width per step) and
|
|
31
|
+
`--mode sizes` (random operands of fixed bit lengths).
|
|
32
|
+
- Includes per-step timeout protection and process RSS memory guards.
|
|
33
|
+
- Added `npm run perf:mult` script in `package.json`.
|
|
34
|
+
|
|
35
|
+
### CLI Usability & Backend Enhancements
|
|
36
|
+
|
|
37
|
+
- **Unified `-h` and `--help` CLI Documentation**:
|
|
38
|
+
- Added comprehensive option and environment variable documentation
|
|
39
|
+
across all benchmark, performance, and test scripts in `scripts/`
|
|
40
|
+
and `backends/*/scripts/`.
|
|
41
|
+
- **LLVM Direct `.kvm` Bytecode Compilation**:
|
|
42
|
+
- Added `compileKVMToExecutable` in `backends/llvm/src/executable.mjs`,
|
|
43
|
+
enabling direct binary compilation from lowered `.kvm` IR.
|
|
44
|
+
- **Runtime Mark Compaction Fixes**:
|
|
45
|
+
- Updated LLVM runtime compaction tracking (`%k_rt_tail_mark`) to
|
|
46
|
+
accurately preserve base and last compaction arena marks in
|
|
47
|
+
tail loops.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
8
51
|
## [6.10.3] — 2026-10-04 — Unified File Formats Specification and Documentation Modernization
|
|
9
52
|
|
|
10
53
|
### Documentation & Core Architecture
|
package/DOCS/FILE_FORMATS.md
CHANGED
|
@@ -14,7 +14,7 @@ type-checked artifacts and JIT/native execution.
|
|
|
14
14
|
| **`.k`** | Human-readable source code | UTF-8 plain text | Optional (trailing expression) | Editor, `k-decompile` | `k.mjs`, `k-repl`, `k-compile`, backends |
|
|
15
15
|
| **`.ko`** | Executable compiled object | Binary (`KOBJ\n` + JSON payload) | **Required** (`"__main__"`) | `k-compile`, REPL (`:ko`) | `k.mjs`, `k-wasm-compile`, `k-llvm-build`, inspect tools |
|
|
16
16
|
| **`.klib`**| Reusable compiled library | UTF-8 JSON payload | **None** (`null`) | `k-compile --lib`, REPL (`:klib`) | REPL (`:load`), CLI linkers (`--lib`) |
|
|
17
|
-
| **`.kvm`** | Polymorphic kVM bytecode template | JSON / CBOR (`format: "k-vm", layer: "KVM-P"`) | Per-relation functions | `k-compile --format kvm`, kvm.mjs |
|
|
17
|
+
| **`.kvm`** | Polymorphic kVM bytecode template | JSON / CBOR (`format: "k-vm", layer: "KVM-P"`) | Per-relation functions | `k-compile --format kvm`, kvm.mjs | `kvm.mjs`, `k-wasm-compile`, `k-llvm-build` |
|
|
18
18
|
|
|
19
19
|
### Format Transformation Workflow
|
|
20
20
|
|
|
@@ -63,6 +63,7 @@ flowchart TD
|
|
|
63
63
|
|
|
64
64
|
%% Lowering from .kvm
|
|
65
65
|
KVM -->|"k-wasm-compile"| WASM
|
|
66
|
+
KVM -->|"k-llvm-build"| NATIVE
|
|
66
67
|
KVM -->|"kvm.mjs / specializeKVM"| INTERP
|
|
67
68
|
```
|
|
68
69
|
|
|
@@ -276,21 +277,38 @@ Export all currently loaded relations and types into a reusable library:
|
|
|
276
277
|
`.kvm` is a polymorphic register-IR template artifact
|
|
277
278
|
(`format: "k-vm"`, `layer: "KVM-P"`, `isPolymorphic: true`).
|
|
278
279
|
|
|
279
|
-
####
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
280
|
+
#### Why `.kvm` Exists (What You Gain):
|
|
281
|
+
|
|
282
|
+
1. **Relational AST vs. Linear Register Machine:**
|
|
283
|
+
`.ko` and `.klib` store high-level relational combinators (compositions
|
|
284
|
+
`(f g)`, products `{a x, b y}`, unions `<f, g>`) and structural type
|
|
285
|
+
graphs. They contain no virtual registers, instruction sequences, or
|
|
286
|
+
control-flow branches. `.kvm` transforms this functional tree into a
|
|
287
|
+
linear 3-address virtual register machine IR (`alloc`, `project_field`,
|
|
288
|
+
`make_variant`, `project_variant`, `jump_if_fail`, `call`, and loops).
|
|
289
|
+
|
|
290
|
+
2. **$1 \times N$ Backend Decoupling:**
|
|
291
|
+
Rather than having every code generator (LLVM, WebAssembly, ARM64,
|
|
292
|
+
and the JIT interpreter) independently lower the relational tree and
|
|
293
|
+
re-implement type-guided control flow, relational-to-imperative
|
|
294
|
+
lowering occurs once in `kvm.mjs`. All backends consume the exact same
|
|
295
|
+
~10 linear kVM instructions (`kvm2llvm.mjs`, `kvm2wasm.mjs`,
|
|
296
|
+
`kvm2arm64.mjs`, `kvm.mjs`).
|
|
297
|
+
|
|
298
|
+
3. **Microsecond Polymorphic Specialization (`layer: "KVM-P"`):**
|
|
299
|
+
In polymorphic programs, specializing `.ko` requires re-running the
|
|
300
|
+
compiler's full structural type derivation and constraint convergence
|
|
301
|
+
engine. A `.kvm` template stores pre-compiled register bytecode with
|
|
302
|
+
principal pattern graphs. The runtime specializer (`specializeKVM`)
|
|
303
|
+
prunes dead branches and folds redundant guards against concrete
|
|
304
|
+
runtime input envelopes $(v, P_v)$ in microseconds with zero AST
|
|
305
|
+
recompilation overhead.
|
|
306
|
+
|
|
307
|
+
4. **Inspectable & Cacheable Register IR:**
|
|
308
|
+
`.kvm` produces a human- and machine-readable JSON or CBOR format.
|
|
309
|
+
You can inspect virtual register assignments, verify dead-branch
|
|
310
|
+
elimination, diff compiler output across runs, or cache the lowered IR
|
|
311
|
+
to bypass frontend compilation when building native or WASM targets.
|
|
294
312
|
|
|
295
313
|
### How to Generate
|
|
296
314
|
|
|
@@ -309,7 +327,24 @@ k-compile add5.ko add5.kvm
|
|
|
309
327
|
|
|
310
328
|
### How to Consume
|
|
311
329
|
|
|
330
|
+
- **Direct lowering from `.kvm` to Native Binaries (`k-llvm-build`):**
|
|
331
|
+
Compile a `.kvm` template directly into a native executable:
|
|
332
|
+
```bash
|
|
333
|
+
k-llvm-build add5.kvm add5-bin
|
|
334
|
+
echo "10" | ./codecs/int.mjs --parse | \
|
|
335
|
+
./add5-bin | \
|
|
336
|
+
./codecs/int.mjs --print
|
|
337
|
+
# Output: 15
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
- **Direct lowering from `.kvm` to LLVM IR (`k-llvm-compile`):**
|
|
341
|
+
Emit LLVM assembly directly from the register IR:
|
|
342
|
+
```bash
|
|
343
|
+
k-llvm-compile add5.kvm add5.ll
|
|
344
|
+
```
|
|
345
|
+
|
|
312
346
|
- **Direct lowering from `.kvm` to WebAssembly (`k-wasm-compile`):**
|
|
347
|
+
Compile a `.kvm` template directly into a standalone `.wasm` module:
|
|
313
348
|
```bash
|
|
314
349
|
k-wasm-compile add5.kvm add5.wasm
|
|
315
350
|
echo "10" | ./codecs/int.mjs --parse | \
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
-- Karatsuba multiplication O(n^1.585)
|
|
2
|
+
-- Self-contained and minimal implementation
|
|
3
|
+
-- for arbitrary-precision integers
|
|
4
|
+
|
|
5
|
+
$ yes_no = < {} yes, {} no >;
|
|
6
|
+
yes = {} | yes $yes_no;
|
|
7
|
+
no = {} | no $yes_no;
|
|
8
|
+
|
|
9
|
+
$ bits = < {} _, bits 0, bits 1 >;
|
|
10
|
+
0b = {} | _;
|
|
11
|
+
0 = 0b |0;
|
|
12
|
+
1 = 0b |1;
|
|
13
|
+
|
|
14
|
+
$ unat = < {} _, unat 1 >;
|
|
15
|
+
0u = {} | _ $unat;
|
|
16
|
+
|
|
17
|
+
empty = $bits < /_ yes, no >;
|
|
18
|
+
empty? = {empty /yes if, () then} .then;
|
|
19
|
+
|
|
20
|
+
inv = {()x, 0b r} _inv;
|
|
21
|
+
_inv = ${bits x, bits r}
|
|
22
|
+
<
|
|
23
|
+
{.x empty? if, .r then} .then,
|
|
24
|
+
<
|
|
25
|
+
{.x /0 x, .r |0 r},
|
|
26
|
+
{.x /1 x, .r |1 r}
|
|
27
|
+
> _inv
|
|
28
|
+
>;
|
|
29
|
+
|
|
30
|
+
concat = ${bits x, bits y} {.x inv x, .y y} concat_;
|
|
31
|
+
concat_ = ${bits x, bits y}
|
|
32
|
+
<
|
|
33
|
+
{.x empty? if, .y then} .then,
|
|
34
|
+
<
|
|
35
|
+
{.x /0 x, .y |0 y},
|
|
36
|
+
{.x /1 x, .y |1 y}
|
|
37
|
+
> concat_
|
|
38
|
+
>;
|
|
39
|
+
|
|
40
|
+
remove_leading_zeros = < /0 remove_leading_zeros, () >;
|
|
41
|
+
nat = remove_leading_zeros <empty? 0, ()>;
|
|
42
|
+
|
|
43
|
+
$ int = < bits '+', bits '-' >;
|
|
44
|
+
int = nat | '+' $int;
|
|
45
|
+
abs = $int </'+', /'-'>;
|
|
46
|
+
|
|
47
|
+
$ sign = < {} '+', {} '-' >;
|
|
48
|
+
sign_plus = {} | '+' $sign;
|
|
49
|
+
sign_minus = {} | '-' $sign;
|
|
50
|
+
swap_sign = < /'+' | '-', /'-' | '+' >;
|
|
51
|
+
|
|
52
|
+
$ carry = < {} 0, {} 1 >;
|
|
53
|
+
c0 = {} | 0 $carry;
|
|
54
|
+
c1 = {} | 1 $carry;
|
|
55
|
+
|
|
56
|
+
_plus = {.x x, .y y, .c c, 0b r} _plus_;
|
|
57
|
+
_plus_ = ${bits x, bits y, carry c, bits r}
|
|
58
|
+
<
|
|
59
|
+
{.x empty? if, .y empty? and, .c/0 c, .r inv then} .then,
|
|
60
|
+
{.x empty? if, .y empty? and, {.r x, 1 r} _inv then} .then,
|
|
61
|
+
{.x empty? if, .c/0 and, {.r x, .y r} _inv then} .then,
|
|
62
|
+
<
|
|
63
|
+
{.x y, .y empty? x, .c c, .r r},
|
|
64
|
+
{<.c/0 0, 1> x, .y y, .x empty? c0 c, .r r},
|
|
65
|
+
{.x /0 x, .y /0 y, .c/0 c0 c, .r |0 r},
|
|
66
|
+
{.x /0 x, .y /0 y, .c/1 c0 c, .r |1 r},
|
|
67
|
+
{.x /0 x, .y /1 y, .c/0 c0 c, .r |1 r},
|
|
68
|
+
{.x /0 x, .y /1 y, .c/1 c1 c, .r |0 r},
|
|
69
|
+
{.x /1 x, .y /0 y, .c/0 c0 c, .r |1 r},
|
|
70
|
+
{.x /1 x, .y /0 y, .c/1 c1 c, .r |0 r},
|
|
71
|
+
{.x /1 x, .y /1 y, .c/0 c1 c, .r |0 r},
|
|
72
|
+
{.x /1 x, .y /1 y, .c/1 c1 c, .r |1 r}
|
|
73
|
+
> _plus_
|
|
74
|
+
>;
|
|
75
|
+
|
|
76
|
+
_minus = {.x x, .y y, .c c, 0b r} _minus_;
|
|
77
|
+
_minus_ = ${bits x, bits y, carry c, bits r}
|
|
78
|
+
<
|
|
79
|
+
{.y empty? if, .c/0 and, {.r x, .x r} _inv then} .then,
|
|
80
|
+
<
|
|
81
|
+
{.x/0 x, .y empty? y, .c/1 c1 c, .r |1 r},
|
|
82
|
+
{.x/1 x, .y empty? y, .c/1 c0 c, .r |0 r},
|
|
83
|
+
{.x/0 x, .y/0 y, .c/0 c0 c, .r |0 r},
|
|
84
|
+
{.x/0 x, .y/0 y, .c/1 c1 c, .r |1 r},
|
|
85
|
+
{.x/0 x, .y/1 y, .c/0 c1 c, .r |1 r},
|
|
86
|
+
{.x/0 x, .y/1 y, .c/1 c1 c, .r |0 r},
|
|
87
|
+
{.x/1 x, .y/0 y, .c/0 c0 c, .r |1 r},
|
|
88
|
+
{.x/1 x, .y/0 y, .c/1 c0 c, .r |0 r},
|
|
89
|
+
{.x/1 x, .y/1 y, .c/0 c0 c, .r |0 r},
|
|
90
|
+
{.x/1 x, .y/1 y, .c/1 c1 c, .r |1 r}
|
|
91
|
+
> _minus_
|
|
92
|
+
>;
|
|
93
|
+
|
|
94
|
+
plus_bits = ${bits x, bits y} {.x inv x, .y inv y, c0 c} _plus inv nat;
|
|
95
|
+
minus_bits = ${bits x, bits y} {.x inv x, .y inv y, c0 c} _minus inv nat;
|
|
96
|
+
|
|
97
|
+
_times = ${bits result, bits _x, bits y}
|
|
98
|
+
<
|
|
99
|
+
{.y empty? if, .result then} .then,
|
|
100
|
+
<
|
|
101
|
+
{.result |0 result, ._x _x, .y /0 y},
|
|
102
|
+
{{._x x, .result |0 y, c0 c} _plus result, ._x _x, .y /1 y}
|
|
103
|
+
> _times
|
|
104
|
+
>;
|
|
105
|
+
times_bits = ${bits x, bits y} {0 result, .x inv _x, .y y} _times inv nat;
|
|
106
|
+
|
|
107
|
+
unat_to_zeros = $unat < /_ 0b, /1 unat_to_zeros |0 > $bits;
|
|
108
|
+
unat_to_double_zeros = $unat
|
|
109
|
+
< /_ 0b, /1 unat_to_double_zeros |0 |0 > $bits;
|
|
110
|
+
|
|
111
|
+
_peel2 = < </0,/1> </0,/1>, </0,/1>, () >;
|
|
112
|
+
_half_unat = ${bits x, bits y, unat m}
|
|
113
|
+
<
|
|
114
|
+
{.x empty? if, .y empty? and, .m then} .then,
|
|
115
|
+
{.x _peel2 x, .y _peel2 y, .m |1 m} _half_unat
|
|
116
|
+
>;
|
|
117
|
+
half_unat = {.x x, .y y, 0u m} _half_unat;
|
|
118
|
+
|
|
119
|
+
_split_rev_u = ${bits x_rev, unat m, bits low}
|
|
120
|
+
<
|
|
121
|
+
{.m /_ if, {.low nat low, .x_rev inv nat high} then} .then,
|
|
122
|
+
{.x_rev empty? if, {.low nat low, 0 high} then} .then,
|
|
123
|
+
<
|
|
124
|
+
{.x_rev /0 x_rev, .m /1 m, .low |0 low},
|
|
125
|
+
{.x_rev /1 x_rev, .m /1 m, .low |1 low}
|
|
126
|
+
> _split_rev_u
|
|
127
|
+
>;
|
|
128
|
+
split_at = {.x inv x_rev, .m m, 0b low} _split_rev_u;
|
|
129
|
+
|
|
130
|
+
at_least_256_u? = <
|
|
131
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
132
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
133
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
134
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
135
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
136
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
137
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
138
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
139
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
140
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
141
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
142
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
143
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
144
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
145
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
146
|
+
/1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1 /1
|
|
147
|
+
yes, no
|
|
148
|
+
>;
|
|
149
|
+
|
|
150
|
+
shift_bits_u = ${bits val, unat m}
|
|
151
|
+
<
|
|
152
|
+
{.val empty? if, 0b then} .then,
|
|
153
|
+
{.val x, .m unat_to_zeros y} concat
|
|
154
|
+
>;
|
|
155
|
+
|
|
156
|
+
shift2_bits_u = ${bits val, unat m}
|
|
157
|
+
<
|
|
158
|
+
{.val empty? if, 0b then} .then,
|
|
159
|
+
{.val x, .m unat_to_double_zeros y} concat
|
|
160
|
+
>;
|
|
161
|
+
|
|
162
|
+
_karatsuba = ${bits x, bits y}
|
|
163
|
+
<
|
|
164
|
+
-- Base case: if m < 256 (< 512 bits), fall back to times_bits
|
|
165
|
+
{{.x x, .y y} half_unat m, .x x, .y y}
|
|
166
|
+
<
|
|
167
|
+
{
|
|
168
|
+
.m at_least_256_u? /no if,
|
|
169
|
+
{.x x, .y y} times_bits then
|
|
170
|
+
} .then,
|
|
171
|
+
-- Recursive Karatsuba:
|
|
172
|
+
{
|
|
173
|
+
{.x x, .m m} split_at sx,
|
|
174
|
+
{.y x, .m m} split_at sy,
|
|
175
|
+
.m m
|
|
176
|
+
}
|
|
177
|
+
{
|
|
178
|
+
{.sx.high x, .sy.high y} _karatsuba z2,
|
|
179
|
+
{.sx.low x, .sy.low y} _karatsuba z0,
|
|
180
|
+
{
|
|
181
|
+
{.sx.high x, .sx.low y} plus_bits x,
|
|
182
|
+
{.sy.high x, .sy.low y} plus_bits y
|
|
183
|
+
} _karatsuba z_sum,
|
|
184
|
+
.m m
|
|
185
|
+
}
|
|
186
|
+
{
|
|
187
|
+
.z2 z2,
|
|
188
|
+
.z0 z0,
|
|
189
|
+
{{.z_sum x, .z2 y} minus_bits x, .z0 y} minus_bits z1,
|
|
190
|
+
.m m
|
|
191
|
+
}
|
|
192
|
+
{
|
|
193
|
+
{.z2 val, .m m} shift2_bits_u z2_s,
|
|
194
|
+
{.z1 val, .m m} shift_bits_u z1_s,
|
|
195
|
+
.z0 z0
|
|
196
|
+
}
|
|
197
|
+
{{.z2_s x, .z1_s y} plus_bits x, .z0 y} plus_bits
|
|
198
|
+
>
|
|
199
|
+
>;
|
|
200
|
+
|
|
201
|
+
karatsuba = ${int x, int y}
|
|
202
|
+
{
|
|
203
|
+
<
|
|
204
|
+
{.x/'+' if, .y /'+' and} sign_plus,
|
|
205
|
+
{.x/'-' if, .y /'-' and} sign_plus,
|
|
206
|
+
sign_minus
|
|
207
|
+
> sign,
|
|
208
|
+
{.x abs x, .y abs y} _karatsuba result
|
|
209
|
+
}
|
|
210
|
+
<
|
|
211
|
+
{.sign/'+' if, .result int then} .then,
|
|
212
|
+
{.sign/'-' if, .result int swap_sign then} .then
|
|
213
|
+
>;
|
|
214
|
+
|
|
215
|
+
()
|
|
@@ -6,6 +6,22 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
|
|
7
7
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8
8
|
const node = process.execPath;
|
|
9
|
+
|
|
10
|
+
function printHelp() {
|
|
11
|
+
console.log(`Usage: node backends/arm64/scripts/run-tests.mjs [options]
|
|
12
|
+
|
|
13
|
+
Runs ARM64 backend test suite and integration tests.
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
-h, --help
|
|
17
|
+
Show this help message and exit.`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
21
|
+
printHelp();
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
const tests = [
|
|
10
26
|
["tests/test-arm64.mjs", node, ["tests/test-arm64.mjs"]],
|
|
11
27
|
["tests/integration.sh", path.join(root, "tests/integration.sh"), []]
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import fs from "node:fs";
|
|
3
4
|
import { argv, exit, stdin } from "node:process";
|
|
4
5
|
import {
|
|
5
6
|
compileProgramInputToObject,
|
|
6
7
|
parseCompileOptions,
|
|
7
8
|
resolveProgramInput
|
|
8
9
|
} from "../src/cli.mjs";
|
|
9
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
compileKVMToExecutable,
|
|
12
|
+
compileObjectToExecutable
|
|
13
|
+
} from "../src/executable.mjs";
|
|
10
14
|
|
|
11
15
|
function usage(stream = console.error) {
|
|
12
16
|
const prog = argv[1] || "k-llvm-build.mjs";
|
|
13
17
|
stream(`Usage: node ${prog} [options] [source-snippet | input-file [output-exe]]`);
|
|
14
|
-
stream("Compile k source, .ko, or .
|
|
18
|
+
stream("Compile k source, .ko, .klib, or .kvm input into a native executable.");
|
|
15
19
|
stream("");
|
|
16
20
|
stream("Arguments:");
|
|
17
21
|
stream(" source-snippet Inline k source, in the same style as k.mjs.");
|
|
18
|
-
stream(" input-file Source .k, .ko, or .
|
|
22
|
+
stream(" input-file Source .k, .ko, .klib, or .kvm file. Reads UTF-8 source from stdin when omitted.");
|
|
19
23
|
stream(" output-exe Output executable path.");
|
|
20
24
|
stream("");
|
|
21
25
|
stream("The executable reads a binary k pattern+value envelope from stdin and");
|
|
@@ -24,11 +28,21 @@ function usage(stream = console.error) {
|
|
|
24
28
|
stream("stdout is encoded with the compiled output pattern.");
|
|
25
29
|
stream("");
|
|
26
30
|
stream("Options:");
|
|
27
|
-
stream(" -o, --output path
|
|
28
|
-
stream(" --
|
|
29
|
-
stream(" --
|
|
30
|
-
stream("
|
|
31
|
-
stream("
|
|
31
|
+
stream(" -o, --output path Specify output file path explicitly.");
|
|
32
|
+
stream(" --input-pattern pattern Input pattern JSON or file path for specialization.");
|
|
33
|
+
stream(" --lib file Load one .klib dependency before compiling.");
|
|
34
|
+
stream(" --export spec Export a library alias into source scope. May be repeated.");
|
|
35
|
+
stream(" spec is 'name' or 'libname:localname'.");
|
|
36
|
+
stream(" -h, --help Show this help.");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readMaybeFile(textOrPath) {
|
|
40
|
+
return fs.existsSync(textOrPath) ? fs.readFileSync(textOrPath, "utf8") : textOrPath;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readPattern(inputPattern) {
|
|
44
|
+
if (inputPattern == null) return null;
|
|
45
|
+
return JSON.parse(readMaybeFile(inputPattern));
|
|
32
46
|
}
|
|
33
47
|
|
|
34
48
|
try {
|
|
@@ -39,6 +53,7 @@ try {
|
|
|
39
53
|
}
|
|
40
54
|
|
|
41
55
|
let explicitOutput = null;
|
|
56
|
+
let rawInputPattern = null;
|
|
42
57
|
const remainingArgs = [];
|
|
43
58
|
while (args.length > 0) {
|
|
44
59
|
const arg = args[0];
|
|
@@ -46,11 +61,17 @@ try {
|
|
|
46
61
|
args.shift();
|
|
47
62
|
explicitOutput = args.shift();
|
|
48
63
|
if (!explicitOutput) throw new Error("-o/--output requires a file argument");
|
|
64
|
+
} else if (arg === "--input-pattern") {
|
|
65
|
+
args.shift();
|
|
66
|
+
rawInputPattern = args.shift();
|
|
67
|
+
if (!rawInputPattern) throw new Error("--input-pattern requires a JSON or file argument");
|
|
49
68
|
} else {
|
|
50
69
|
remainingArgs.push(args.shift());
|
|
51
70
|
}
|
|
52
71
|
}
|
|
53
72
|
|
|
73
|
+
const inputPattern = readPattern(rawInputPattern);
|
|
74
|
+
|
|
54
75
|
const { libraries, exportSpecs } = parseCompileOptions(remainingArgs);
|
|
55
76
|
const input = resolveProgramInput(remainingArgs, { allowStdinSource: true });
|
|
56
77
|
const positionalOutput = remainingArgs.shift();
|
|
@@ -59,13 +80,18 @@ try {
|
|
|
59
80
|
const outputPath = explicitOutput || positionalOutput || null;
|
|
60
81
|
if (outputPath == null) throw new Error("Output executable path is required (pass output-exe or -o/--output)");
|
|
61
82
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
83
|
+
if (input.kind === "kvm") {
|
|
84
|
+
const kvm = JSON.parse(fs.readFileSync(input.path, "utf8"));
|
|
85
|
+
compileKVMToExecutable(kvm, outputPath, { inputPattern });
|
|
86
|
+
} else {
|
|
87
|
+
const object = await compileProgramInputToObject(input, {
|
|
88
|
+
libraries,
|
|
89
|
+
exportSpecs,
|
|
90
|
+
stdin
|
|
91
|
+
});
|
|
67
92
|
|
|
68
|
-
|
|
93
|
+
compileObjectToExecutable(object, outputPath, { inputPattern });
|
|
94
|
+
}
|
|
69
95
|
} catch (error) {
|
|
70
96
|
console.error(error.stack || error.message || String(error));
|
|
71
97
|
usage();
|
|
@@ -7,17 +7,20 @@ import {
|
|
|
7
7
|
parseCompileOptions,
|
|
8
8
|
resolveProgramInput
|
|
9
9
|
} from "../src/cli.mjs";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
compileObjectToLLVM,
|
|
12
|
+
compileLLVMArtifactFromKVM
|
|
13
|
+
} from "../src/llvm.mjs";
|
|
11
14
|
import { inputPatternForObjectRelation } from "../src/executable.mjs";
|
|
12
15
|
|
|
13
16
|
function usage(stream = console.error) {
|
|
14
17
|
const prog = argv[1] || "k-llvm-compile.mjs";
|
|
15
18
|
stream(`Usage: node ${prog} [options] [source-snippet | input-file [output.ll]]`);
|
|
16
|
-
stream("Compile a k .ko/.klib object into prototype LLVM IR.");
|
|
19
|
+
stream("Compile a k .ko/.klib object, source, or .kvm input into prototype LLVM IR.");
|
|
17
20
|
stream("");
|
|
18
21
|
stream("Arguments:");
|
|
19
22
|
stream(" source-snippet Inline k source, in the same style as k.mjs.");
|
|
20
|
-
stream(" input-file Source .k, .ko, or .
|
|
23
|
+
stream(" input-file Source .k, .ko, .klib, or .kvm file. Reads UTF-8 source from stdin when omitted.");
|
|
21
24
|
stream(" output.ll Output LLVM IR path. Writes to stdout when omitted.");
|
|
22
25
|
stream("");
|
|
23
26
|
stream("Options:");
|
|
@@ -55,16 +58,30 @@ try {
|
|
|
55
58
|
|
|
56
59
|
const outputPath = explicitOutput || positionalOutput || null;
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
let llvm;
|
|
62
|
+
if (input.kind === "kvm") {
|
|
63
|
+
const kvm = JSON.parse(fs.readFileSync(input.path, "utf8"));
|
|
64
|
+
const entry = kvm.entry || kvm.relation || "__main__";
|
|
65
|
+
const mainFunc = kvm.functions?.[entry];
|
|
66
|
+
const artifact = compileLLVMArtifactFromKVM(kvm, {
|
|
67
|
+
relation: entry,
|
|
68
|
+
inputPattern: mainFunc?.inputPattern || kvm.inputPattern,
|
|
69
|
+
outputPattern: mainFunc?.outputPattern || kvm.outputPattern
|
|
70
|
+
});
|
|
71
|
+
llvm = typeof artifact === "string" ? artifact : artifact.llvm;
|
|
72
|
+
} else {
|
|
73
|
+
const object = await compileProgramInputToObject(input, {
|
|
74
|
+
libraries,
|
|
75
|
+
exportSpecs,
|
|
76
|
+
stdin
|
|
77
|
+
});
|
|
63
78
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
const res = compileObjectToLLVM(object, {
|
|
80
|
+
relation: object.main,
|
|
81
|
+
inputPattern: inputPatternForObjectRelation(object, object.main)
|
|
82
|
+
});
|
|
83
|
+
llvm = res.llvm;
|
|
84
|
+
}
|
|
68
85
|
|
|
69
86
|
if (outputPath == null) {
|
|
70
87
|
stdout.write(llvm);
|
|
@@ -553,17 +553,17 @@ static void rt_rewind_and_free(k_rt *rt, k_rt_checkpoint mark) {
|
|
|
553
553
|
rt->has_reusable_blocks = 0;
|
|
554
554
|
}
|
|
555
555
|
|
|
556
|
-
k_value *k_rt_compact(k_rt *rt, k_value *root,
|
|
557
|
-
if (rt == NULL || root == NULL ||
|
|
556
|
+
k_value *k_rt_compact(k_rt *rt, k_value *root, k_rt_tail_mark *tm) {
|
|
557
|
+
if (rt == NULL || root == NULL || tm == NULL) return root;
|
|
558
558
|
|
|
559
|
-
size_t bytes = bytes_since_mark(rt,
|
|
559
|
+
size_t bytes = bytes_since_mark(rt, tm->last_mark);
|
|
560
560
|
if (bytes < 4194304) {
|
|
561
561
|
return root;
|
|
562
562
|
}
|
|
563
563
|
|
|
564
|
-
if (!is_after_mark(rt, root,
|
|
565
|
-
rt_rewind_and_free(rt,
|
|
566
|
-
|
|
564
|
+
if (!is_after_mark(rt, root, tm->base_mark)) {
|
|
565
|
+
rt_rewind_and_free(rt, tm->base_mark);
|
|
566
|
+
tm->last_mark = tm->base_mark;
|
|
567
567
|
return root;
|
|
568
568
|
}
|
|
569
569
|
|
|
@@ -574,14 +574,14 @@ k_value *k_rt_compact(k_rt *rt, k_value *root, k_rt_checkpoint *mark_ptr) {
|
|
|
574
574
|
|
|
575
575
|
k_fixup_list fixups = {NULL, 0, 0};
|
|
576
576
|
|
|
577
|
-
k_value *staged_root = copy_value(rt, root, &staging, &staging_used, &staging_cap, &fixups,
|
|
577
|
+
k_value *staged_root = copy_value(rt, root, &staging, &staging_used, &staging_cap, &fixups, tm->base_mark);
|
|
578
578
|
|
|
579
|
-
rt_rewind_and_free(rt,
|
|
579
|
+
rt_rewind_and_free(rt, tm->base_mark);
|
|
580
580
|
|
|
581
581
|
if (staging_used == 0) {
|
|
582
582
|
free(staging);
|
|
583
583
|
if (fixups.offsets != NULL) free(fixups.offsets);
|
|
584
|
-
|
|
584
|
+
tm->last_mark = tm->base_mark;
|
|
585
585
|
return staged_root;
|
|
586
586
|
}
|
|
587
587
|
|
|
@@ -605,7 +605,7 @@ k_value *k_rt_compact(k_rt *rt, k_value *root, k_rt_checkpoint *mark_ptr) {
|
|
|
605
605
|
free(staging);
|
|
606
606
|
if (fixups.offsets != NULL) free(fixups.offsets);
|
|
607
607
|
|
|
608
|
-
|
|
608
|
+
tm->last_mark = k_rt_mark(rt);
|
|
609
609
|
|
|
610
610
|
return new_root;
|
|
611
611
|
}
|
|
@@ -13,6 +13,11 @@ typedef struct {
|
|
|
13
13
|
size_t used;
|
|
14
14
|
} k_rt_checkpoint;
|
|
15
15
|
|
|
16
|
+
typedef struct {
|
|
17
|
+
k_rt_checkpoint base_mark;
|
|
18
|
+
k_rt_checkpoint last_mark;
|
|
19
|
+
} k_rt_tail_mark;
|
|
20
|
+
|
|
16
21
|
typedef struct {
|
|
17
22
|
const char *label;
|
|
18
23
|
size_t label_length;
|
|
@@ -66,7 +71,7 @@ k_rt *k_rt_new(void);
|
|
|
66
71
|
void k_rt_reset(k_rt *rt);
|
|
67
72
|
k_rt_checkpoint k_rt_mark(k_rt *rt);
|
|
68
73
|
void k_rt_rewind(k_rt *rt, k_rt_checkpoint mark);
|
|
69
|
-
k_value *k_rt_compact(k_rt *rt, k_value *root,
|
|
74
|
+
k_value *k_rt_compact(k_rt *rt, k_value *root, k_rt_tail_mark *mark_ptr);
|
|
70
75
|
void *k_rt_alloc(k_rt *rt, size_t size);
|
|
71
76
|
void k_rt_free(k_rt *rt);
|
|
72
77
|
|
|
@@ -6,6 +6,22 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
|
|
7
7
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8
8
|
const node = process.execPath;
|
|
9
|
+
|
|
10
|
+
function printHelp() {
|
|
11
|
+
console.log(`Usage: node backends/llvm/scripts/run-tests.mjs [options]
|
|
12
|
+
|
|
13
|
+
Runs LLVM backend test suite, integration tests, and conformance suite.
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
-h, --help
|
|
17
|
+
Show this help message and exit.`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
21
|
+
printHelp();
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
const tests = [
|
|
10
26
|
["tests/test-llvm.mjs", node, ["tests/test-llvm.mjs"]],
|
|
11
27
|
["tests/integration.sh", path.join(root, "tests/integration.sh"), []],
|
|
@@ -135,7 +135,7 @@ export async function compileProgramInputToObject(
|
|
|
135
135
|
} else if (input.kind === "ko" || input.kind === "klib") {
|
|
136
136
|
object = decodeObject(fs.readFileSync(input.path));
|
|
137
137
|
} else if (input.kind === "kvm") {
|
|
138
|
-
throw new Error("
|
|
138
|
+
throw new Error("Input is already lowered to .kvm bytecode; use compileLLVMArtifactFromKVM or compileKVMToExecutable.");
|
|
139
139
|
} else {
|
|
140
140
|
throw new Error(`Unsupported input kind: ${input.kind}`);
|
|
141
141
|
}
|