@fraczak/k 7.0.1 → 7.0.2

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/DOCS/BIG_PICTURE.md +5 -5
  3. package/DOCS/FILE_FORMATS.md +5 -5
  4. package/DOCS/OBJECT_FILE_AND_PATTERN.md +9 -9
  5. package/DOCS/PATTERNS.md +3 -3
  6. package/DOCS/PROFILING_AND_TRACING.md +60 -72
  7. package/DOCS/REPL.md +3 -3
  8. package/DOCS/book/03-types-as-finite-automata.md +4 -4
  9. package/DOCS/book/06-values-in-memory.md +9 -9
  10. package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
  11. package/DOCS/book/09-compiler-architecture.md +2 -2
  12. package/DOCS/book/10-from-ast-to-intermediate-representation.md +2 -2
  13. package/DOCS/book/14-canonical-serialization.md +1 -1
  14. package/Examples/arithmetics.k +2 -2
  15. package/Examples/bnat.k +1 -1
  16. package/Examples/karatsuba-mult.k +2 -2
  17. package/README.md +3 -8
  18. package/backends/llvm/runtime/krt.c +33 -0
  19. package/backends/llvm/runtime/krt.h +1 -0
  20. package/backends/llvm/src/executable.mjs +4 -4
  21. package/backends/llvm/src/kvm2llvm.mjs +40 -18
  22. package/backends/llvm/src/llvm.mjs +12 -11
  23. package/backends/llvm/tests/perf-support.mjs +1 -55
  24. package/backends/llvm/tests/test-llvm.mjs +3 -3
  25. package/backends/wasm/src/wasm.mjs +2 -1
  26. package/backends/wasm/tests/test-wasm.mjs +5 -5
  27. package/browser/vfs-data.mjs +5 -5
  28. package/core.k +8 -8
  29. package/harness/format.mjs +2 -2
  30. package/harness/registry.mjs +0 -2
  31. package/kir.mjs +142 -56
  32. package/package.json +1 -5
  33. package/scripts/benchmark-idempotency.mjs +12 -8
  34. package/scripts/benchmark.mjs +5 -9
  35. package/scripts/build-repl-html.mjs +2 -2
  36. package/scripts/perf-ieee.mjs +17 -118
  37. package/scripts/perf-int.mjs +313 -318
  38. package/scripts/perf-multiplication.mjs +10 -20
  39. package/scripts/perf-poly.mjs +29 -183
  40. package/tests/code-derivation/pattern-envelope.mjs +4 -4
  41. package/tests/test-kvm-polymorphic.mjs +2 -2
  42. package/backends/arm64/README.md +0 -103
  43. package/backends/arm64/bin/k-arm64-compile.mjs +0 -93
  44. package/backends/arm64/bin/k-arm64-run.mjs +0 -58
  45. package/backends/arm64/bin/k-arm64.mjs +0 -88
  46. package/backends/arm64/scripts/run-tests.mjs +0 -57
  47. package/backends/arm64/src/arm64.mjs +0 -229
  48. package/backends/arm64/src/cli.mjs +0 -155
  49. package/backends/arm64/src/kvm2arm64.mjs +0 -906
  50. package/backends/arm64/src/runtime.c +0 -744
  51. package/backends/arm64/tests/integration.sh +0 -112
  52. package/backends/arm64/tests/test-arm64.mjs +0 -240
  53. package/harness/adapters/arm64.mjs +0 -105
package/CHANGELOG.md CHANGED
@@ -5,6 +5,36 @@ 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
+ ## [7.0.2] — 2026-10-13 — LLVM Squaring Scaling & Backend Streamlining
9
+
10
+ ### Features & Performance Improvements
11
+
12
+ - **Multiplication Algorithm Scaling Benchmark (LLVM & Multi-Backend)**:
13
+ - Added successive squaring scale benchmark comparing standard long
14
+ multiplication (`times`) and Karatsuba multiplication (`karatsuba`)
15
+ across bit lengths doubling up to 7,650+ bits.
16
+ - Added support for squaring execution and memory (peak RSS / heap)
17
+ tracking across LLVM, WebAssembly, and interpreter lanes.
18
+ - Aligned product field layouts and offset indexing in `kvm2llvm.mjs`.
19
+
20
+ ### Bug Fixes & Compatibility
21
+
22
+ - **LLVM Open/Closed Pattern Compatibility on Wire Input**:
23
+ - Allowed open and closed pattern compatibility when validating and
24
+ unpacking wire inputs in the LLVM runtime (`krt.c` and
25
+ `executable.mjs`).
26
+ - Gracefully accepts framed inputs matching open-product schemas.
27
+
28
+ ### Chores & Maintenance
29
+
30
+ - **Backend Streamlining (Wasm & LLVM Focus)**:
31
+ - Removed legacy experimental ARM64 backend (`backends/arm64`) to focus
32
+ on WebAssembly (portable) and LLVM (native) backends.
33
+ - Updated documentation (`DOCS/FILE_FORMATS.md`,
34
+ `DOCS/PROFILING_AND_TRACING.md`) and benchmark suites.
35
+ - Enhanced pattern graph specialization and KIR monomorphization in
36
+ `kir.mjs`.
37
+
8
38
  ## [7.0.1] — 2026-10-10 — Call-Graph Pattern Graph Specialization
9
39
 
10
40
  ### Bug Fixes & Backend Alignments
@@ -18,6 +48,12 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
18
48
  memory layout (`tagId` at header slots).
19
49
  - Resolved polymorphic list and open product projection alignment across
20
50
  LLVM, WASM, and Web REPL.
51
+ - **Filter Relation Naming Convention (`?` Suffix)**:
52
+ - Standardized naming convention for all filter and schema relations to use
53
+ the `?` suffix (e.g. `int?`, `bits?`, `bit?`, `string?`, `pattern?`,
54
+ `bnat?`, `nat?`, `bool?`, matching `list?`).
55
+ - Separated integer filter schema `int? = ?<...>` from constructor
56
+ `int = nat | '+'` in `Examples/arithmetics.k`, removing symbol collision.
21
57
 
22
58
  ## [7.0.0] — 2026-10-10 — Unified Schemas, Relational Contracts, and Pure Syntax
23
59
 
@@ -49,7 +49,7 @@ set of pairwise distinct labels `l1, l2, ..., ln`:
49
49
  - Variant values are constructed using "|" (pipe) followed by the tag:
50
50
  - Unit variant: `{} | nil`, `{} | zero`
51
51
  - Variant with payload `v` at tag: `v | tag`
52
- - Example (list): with `list = ?< {} nil, { X car, L cdr } cons > = L;`:
52
+ - Example (list): with `list? = ?< {} nil, { X car, L cdr } cons > = L;`:
53
53
  - Empty list `[]`: `{} | nil`
54
54
  - Singleton `[v]`: `{ v car, {} | nil cdr } | cons`
55
55
  - Angle brackets `< ... >` denote ordered choice (union expressions); use
@@ -202,14 +202,14 @@ The contract derivation steps are:
202
202
  #### Example 1
203
203
 
204
204
  ```text
205
- bit = ?< {} o, {} i >;
205
+ bit? = ?< {} o, {} i >;
206
206
  bit0 = | o;
207
207
  bit1 = | i;
208
208
 
209
- byte = ?{ bit b0, bit b1, bit b2, bit b3 };
209
+ byte? = ?{ bit? b0, bit? b1, bit? b2, bit? b3 };
210
210
  zero = bit0 { () b0, () b1, () b2, () b3 };
211
211
 
212
- inc = byte
212
+ inc = byte?
213
213
  <
214
214
  { bit0 overflown, { .b0 /o bit1 b0, .b1 b1, .b2 b2, .b3 b3 } byte },
215
215
  { bit0 overflown, { bit0 b0, .b1 /o bit1 b1, .b2 b2, .b3 b3 } byte },
@@ -347,7 +347,7 @@ conversion via `5 int`.
347
347
  Bit trees are defined algebraically:
348
348
 
349
349
  ```text
350
- bits = ?< {} _, B 0, B 1 > = B;
350
+ bits? = ?< {} _, B 0, B 1 > = B;
351
351
  ```
352
352
 
353
353
  ### Literals for `@bits` are:
@@ -288,12 +288,12 @@ Export all currently loaded relations and types into a reusable library:
288
288
  `make_variant`, `project_variant`, `jump_if_fail`, `call`, and loops).
289
289
 
290
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
291
+ Rather than having every code generator (LLVM, WebAssembly, and the
292
+ interpreter) independently lower the relational tree and
293
293
  re-implement type-guided control flow, relational-to-imperative
294
294
  lowering occurs once in `kvm.mjs`. All backends consume the exact same
295
295
  ~10 linear kVM instructions (`kvm2llvm.mjs`, `kvm2wasm.mjs`,
296
- `kvm2arm64.mjs`, `kvm.mjs`).
296
+ `kvm.mjs`).
297
297
 
298
298
  3. **Microsecond Polymorphic Specialization (`layer: "KVM-P"`):**
299
299
  In polymorphic programs, specializing `.ko` requires re-running the
@@ -398,9 +398,9 @@ In `k`, there are no primitive numerical types or built-in integer
398
398
  literals. Identifiers such as `5` or `10` are not language keywords;
399
399
  they are defined relations (aliases) in `Examples/arithmetics.k`:
400
400
  - `5` and `10` are nullary relations (constants) of type `{} -> bits`,
401
- defined as binary digit trees over `bits = ?< {} _, bits 0, bits 1 > = bits;`.
401
+ defined as binary digit trees over `bits? = ?< {} _, bits? 0, bits? 1 > = bits?;`.
402
402
  - Arithmetic operations (`plus`, `minus`, `times`, `inc`) operate on
403
- signed integers (`int = < <bits 0, bits 1, {} _>=bits "+", bits "-" >`).
403
+ signed integers (`int? = ?< <bits 0, bits 1, {} _>=bits "+", bits "-" >`).
404
404
  - Passing a bit-representation like `5` to an integer relation requires
405
405
  applying the `int` constructor (`5 int`), producing a constant
406
406
  relation of type `{} -> int`.
@@ -15,18 +15,18 @@ The pattern is the framing protocol. There is no separate envelope type.
15
15
  [`core.k`](../core.k) defines the canonical self-hosted pattern type:
16
16
 
17
17
  ```k
18
- edge = ?{ string label, bits target };
19
- edges = ?< {} nil, { edge car, E cdr } cons > = E;
18
+ edge? = ?{ string? label, bits? target };
19
+ edges? = ?< {} nil, { edge? car, E cdr } cons > = E;
20
20
 
21
- pattern-node = ?<
21
+ pattern-node? = ?<
22
22
  {} any,
23
- edges open-product,
24
- edges open-union,
25
- edges closed-product,
26
- edges closed-union
23
+ edges? open-product,
24
+ edges? open-union,
25
+ edges? closed-product,
26
+ edges? closed-union
27
27
  >;
28
28
 
29
- pattern = ?< {} nil, { pattern-node car, P cdr } cons > = P;
29
+ pattern? = ?< {} nil, { pattern-node? car, P cdr } cons > = P;
30
30
  ```
31
31
 
32
32
  This does not mean `core.k` is pre-loaded into ordinary program execution. The
@@ -274,7 +274,7 @@ For a definitions-only source file:
274
274
 
275
275
  ```k
276
276
  -- defs.k
277
- nat = ?< {} zero, N succ > = N;
277
+ nat? = ?< {} zero, N succ > = N;
278
278
  succ = |succ;
279
279
  ```
280
280
 
package/DOCS/PATTERNS.md CHANGED
@@ -56,9 +56,9 @@ A k script can describe a pattern through:
56
56
  Examples:
57
57
 
58
58
  ```k
59
- bits = ?< {} _, bits 0, bits 1 > = bits;
60
- list = ?< {} nil, { < {} _, B 0, B 1 >=B car, L cdr } cons > = L;
61
- list
59
+ bits? = ?< {} _, bits? 0, bits? 1 > = bits?;
60
+ list? = ?< {} nil, { < {} _, B 0, B 1 >=B car, L cdr } cons > = L;
61
+ list?
62
62
  ```
63
63
 
64
64
  ```k
@@ -5,7 +5,6 @@ and function-level call profiling infrastructure for `k` backends.
5
5
 
6
6
  The profiling framework allows fine-grained performance analysis of polymorphic `k`
7
7
  programs across multiple execution targets:
8
- - **Linux ARM64** (native standalone ELF executable via direct kVM assembly emission)
9
8
  - **WebAssembly** (in-process Wasm module via single-pass kVM lowering)
10
9
  - **LLVM** (native specialized executable via KIR-P lowering)
11
10
  - **kVM Interpreter** (in-process TypeScript/JavaScript kVM evaluator)
@@ -15,17 +14,23 @@ programs across multiple execution targets:
15
14
 
16
15
  ## 1. Motivation: Apples-to-Apples Backend Comparison
17
16
 
18
- When measuring end-to-end request latency across backends, wall-clock time can be
19
- dominated by factors outside the core relation algorithm:
20
- - **Process IPC**: Out-of-process native executables (ARM64, LLVM) communicate via stdin/stdout pipes, incurring OS context switches and pipe buffer copies.
21
- - **Wire Codec**: Decoding and validating the binary wire format (`pattern + value`) and encoding the response.
22
- - **In-Memory Representation**: WebAssembly converts incoming values into a flat linear memory arena layout.
23
- - **Pure Evaluation**: The actual relational instruction loop performing the algorithm.
24
-
25
- Without detailed accounting, native ARM64 or LLVM binaries communicating over pipes can
26
- appear slower than in-process WebAssembly or the interpreter simply because of IPC overhead.
27
- The tracing infrastructure breaks request latency down into nanosecond-level phases so that
28
- pure execution time can be measured and compared directly.
17
+ When measuring end-to-end request latency across backends, wall-clock
18
+ time can be dominated by factors outside the core relation algorithm:
19
+ - **Process IPC**: Out-of-process native executables (LLVM)
20
+ communicate via stdin/stdout pipes, incurring OS context switches and
21
+ pipe buffer copies.
22
+ - **Wire Codec**: Decoding and validating the binary wire format
23
+ (`pattern + value`) and encoding the response.
24
+ - **In-Memory Representation**: WebAssembly converts incoming values into
25
+ a flat linear memory arena layout.
26
+ - **Pure Evaluation**: The actual relational instruction loop performing
27
+ the algorithm.
28
+
29
+ Without detailed accounting, native LLVM binaries communicating over
30
+ pipes can appear slower than in-process WebAssembly or the interpreter
31
+ simply because of IPC overhead. The tracing infrastructure breaks
32
+ request latency down into nanosecond-level phases so that pure
33
+ execution time can be measured and compared directly.
29
34
 
30
35
  In addition, **per-function invocation profiling** verifies that every backend performs the
31
36
  exact same number of relational evaluations and loop iterations for a given input.
@@ -56,37 +61,41 @@ Following `k`'s canonical content-addressed design:
56
61
 
57
62
  When tracing is enabled, each request is instrumented to measure 7 discrete phases with nanosecond precision:
58
63
 
59
- | Phase | Description | Linux ARM64 | LLVM | WebAssembly | kVM Interp |
60
- | :--- | :--- | :---: | :---: | :---: | :---: |
61
- | **1. IPC Read (Pipe In)** | Waiting for and reading input payload from stdin | Yes | Yes | N/A (in-process) | N/A (in-process) |
62
- | **2. Wire Decode & Validate** | Decoding wire buffer and validating against pattern | Yes | Yes | N/A (in-process) | N/A (in-process) |
63
- | **3. Flat Input Prep (Arena)** | Flattening decoded structures into linear arena memory | Yes | N/A | Yes | N/A |
64
- | **4. Pure Evaluation** | Core relational execution loop | **Yes** | **Yes** | **Yes** | **Yes** |
65
- | **5. Flat Output Prep (Arena)** | Serializing/reading in-memory representations | Yes | N/A | Yes | N/A |
66
- | **6. Wire Encode** | Encoding result to canonical wire format | Yes | Yes | N/A (in-process) | N/A (in-process) |
67
- | **7. IPC Write (Pipe Out)** | Writing output wire bytes to stdout pipe | Yes | Yes | Yes (driver) | N/A |
64
+ | Phase | Description | LLVM | WebAssembly | kVM Interp |
65
+ | :--- | :--- | :---: | :---: | :---: |
66
+ | **1. IPC Read (Pipe In)** | Waiting for and reading input payload from stdin | Yes | N/A (in-process) | N/A (in-process) |
67
+ | **2. Wire Decode & Validate** | Decoding wire buffer and validating against pattern | Yes | N/A (in-process) | N/A (in-process) |
68
+ | **3. Flat Input Prep (Arena)** | Flattening decoded structures into linear arena memory | N/A | Yes | N/A |
69
+ | **4. Pure Evaluation** | Core relational execution loop | **Yes** | **Yes** | **Yes** |
70
+ | **5. Flat Output Prep (Arena)** | Serializing/reading in-memory representations | N/A | Yes | N/A |
71
+ | **6. Wire Encode** | Encoding result to canonical wire format | Yes | N/A (in-process) | N/A (in-process) |
72
+ | **7. IPC Write (Pipe Out)** | Writing output wire bytes to stdout pipe | Yes | Yes (driver) | N/A |
68
73
 
69
74
  ### Phase Timing Output Example
70
75
 
71
76
  ```text
72
77
  --- Operation: reverse (len 40) ---
73
- Phase | Linux ARM64 | LLVM | WebAssembly | kVM Interp
74
- -------------------------+---------------+---------------+---------------+---------------
75
- 1. IPC Read (Pipe In) | 169.83 µs | 161.42 µs | - | -
76
- 2. Wire Decode & Validate | 2.04 µs | 1.58 µs | - | -
77
- 3. Flat Input Prep (Arena) | 3.08 µs | - | 80.88 µs | -
78
- 4. Pure Evaluation | 0.46 µs | 0.54 µs | 1.71 µs | 26.04 µs
79
- 5. Flat Output Prep (Arena) | 2.67 µs | - | 76.71 µs | -
80
- 6. Wire Encode | 1.96 µs | 4.88 µs | - | -
81
- 7. IPC Write (Pipe Out) | 11.29 µs | 11.08 µs | 0.62 µs | -
82
- -------------------------+---------------+---------------+---------------+---------------
83
- Total Request Time | 191.33 µs | 179.50 µs | 159.92 µs | 26.04 µs
78
+ Phase | LLVM | WebAssembly | kVM Interp
79
+ -------------------------+---------------+---------------+---------------
80
+ 1. IPC Read (Pipe In) | 161.42 µs | - | -
81
+ 2. Wire Decode & Validate | 1.58 µs | - | -
82
+ 3. Flat Input Prep (Arena) | - | 80.88 µs | -
83
+ 4. Pure Evaluation | 0.54 µs | 1.71 µs | 26.04 µs
84
+ 5. Flat Output Prep (Arena) | - | 76.71 µs | -
85
+ 6. Wire Encode | 4.88 µs | - | -
86
+ 7. IPC Write (Pipe Out) | 11.08 µs | 0.62 µs | -
87
+ -------------------------+---------------+---------------+---------------
88
+ Total Request Time | 179.50 µs | 159.92 µs | 26.04 µs
84
89
  ```
85
90
 
86
91
  **Key Takeaways**:
87
- - **Pure evaluation** in Linux ARM64 native code (`0.46 µs`) is ~3.7× faster than WebAssembly (`1.71 µs`) and ~56× faster than the interpreter (`26.04 µs`).
88
- - **End-to-end request time** for native binaries is dominated by pipe IPC read (`~160–170 µs`).
89
- - **WebAssembly overhead** is concentrated in flat arena conversion (`~80 µs` in, `~76 µs` out).
92
+ - **Pure evaluation** in LLVM native code (`0.54 µs`) is ~3.2× faster
93
+ than WebAssembly (`1.71 µs`) and ~48× faster than the interpreter
94
+ (`26.04 µs`).
95
+ - **End-to-end request time** for native binaries is dominated by pipe
96
+ IPC read (`~160 µs`).
97
+ - **WebAssembly overhead** is concentrated in flat arena conversion
98
+ (`~80 µs` in, `~76 µs` out).
90
99
 
91
100
  ---
92
101
 
@@ -100,22 +109,20 @@ every backend executes the exact same call path.
100
109
 
101
110
  ```text
102
111
  Function Call Profile: reverse
103
- Function Name | Linux ARM64 Calls | WebAssembly Calls | kVM Interp Calls
104
- -------------------------+-------------------+-------------------+------------------
105
- reverse | - | 1 | 1
106
- _reverse | - | 41 | 41
107
- @dhiaXnh7AYZnewyvbXdh... | 1 | - | -
108
- @EwQ2P3yZ8v4z2qA7kF... | 41 | - | -
109
- car | 40 | 40 | 40
110
- cdr | 40 | 40 | 40
111
- cons | 40 | 40 | 40
112
- nil | 1 | 1 | 1
113
- -------------------------+-------------------+-------------------+------------------
114
- Total Calls | 163 | 163 | 163
112
+ Function Name | WebAssembly Calls | kVM Interp Calls
113
+ -------------------------+-------------------+------------------
114
+ reverse | 1 | 1
115
+ _reverse | 41 | 41
116
+ car | 40 | 40
117
+ cdr | 40 | 40
118
+ cons | 40 | 40
119
+ nil | 1 | 1
120
+ -------------------------+-------------------+------------------
121
+ Total Calls | 163 | 163
115
122
  ```
116
123
 
117
- *(Note: ARM64 displays canonical content hashes `@hash` for unaliased internal relations.
118
- All three backends arrive at the exact same total call count: 163 calls for reversing a list of length 40).*
124
+ *(Note: Both backends arrive at the exact same total call count:
125
+ 163 calls for reversing a list of length 40).*
119
126
 
120
127
  ---
121
128
 
@@ -143,9 +150,7 @@ node scripts/perf-poly.mjs --trace
143
150
  | `TRACE_SAMPLES` | `5` | Repetitions sampled and averaged for phase timings. |
144
151
  | `TRACE` | `0` | Set to `1` (or pass `--trace`) to enable phase breakdown. |
145
152
  | `PROFILE` | `1` (with trace) | Set to `0` to disable function call counters. |
146
- | `ARM64_OPT` | `-O2` | Optimization level for the native ARM64 runtime (`-O0`, `-O1`, `-O2`, `-O3`). |
147
153
  | `WASM_RESET` | `1` | Reset the WebAssembly linear memory arena between calls (`1` or `0`). |
148
- | `ARM64_ONLY` | `0` | Run only the Linux ARM64 native target. |
149
154
  | `WASM_ONLY` | `0` | Run only the WebAssembly target. |
150
155
  | `LLVM_ONLY` | `0` | Run only the LLVM target. |
151
156
  | `BACKENDS_ONLY` | `0` | Skip the native JS baseline lanes. |
@@ -156,34 +161,17 @@ node scripts/perf-poly.mjs --trace
156
161
  # Fast smoke check with list length 10 and 1 iteration:
157
162
  ITERATIONS=1 LIST_LENGTH=10 npm run perf:poly:trace
158
163
 
159
- # Measure high-optimization ARM64 (-O3) vs WebAssembly on large lists (100 elements):
160
- LIST_LENGTH=100 ARM64_OPT=-O3 npm run perf:poly:trace
164
+ # Trace WebAssembly on large lists (100 elements):
165
+ LIST_LENGTH=100 WASM_ONLY=1 npm run perf:poly:trace
161
166
 
162
- # Isolate ARM64 native execution:
163
- ARM64_ONLY=1 npm run perf:poly:trace
167
+ # Isolate LLVM native execution:
168
+ LLVM_ONLY=1 npm run perf:poly:trace
164
169
  ```
165
170
 
166
171
  ---
167
172
 
168
173
  ## 6. Implementation Architecture
169
174
 
170
- ### Linux ARM64 Backend
171
- - **Code Emission** ([`backends/arm64/src/kvm2arm64.mjs`](../backends/arm64/src/kvm2arm64.mjs)): When `options.profile` is active, each function emits counter increment instructions in its entry sequence:
172
- ```asm
173
- adrp x9, k_prof_<func>
174
- add x9, x9, :lo12:k_prof_<func>
175
- ldr x10, [x9]
176
- add x10, x10, #1
177
- str x10, [x9]
178
- ```
179
- Counters are emitted into a dedicated `.data` section, and an exported metadata table `k_profile_entries[]` is generated.
180
- - **Runtime Reporting** ([`backends/arm64/src/runtime.c`](../backends/arm64/src/runtime.c)):
181
- - Nanosecond phase timestamps are sampled via `clock_gettime(CLOCK_MONOTONIC, ...)` and emitted over stderr formatted as:
182
- ```text
183
- K_TRACE_PHASES backend=arm64 ipc_read_ns=... decode_ns=... flat_in_ns=... eval_ns=... flat_out_ns=... encode_ns=... ipc_write_ns=... total_ns=...
184
- ```
185
- - `dump_profile_counts()` emits nonzero function call counts between `K_PROFILE_BEGIN` and `K_PROFILE_END` tags.
186
-
187
175
  ### WebAssembly Backend
188
176
  - **Code Emission** ([`backends/wasm/src/kvm2wasm.mjs`](../backends/wasm/src/kvm2wasm.mjs)): Emits mutable 64-bit Wasm globals:
189
177
  ```wat
package/DOCS/REPL.md CHANGED
@@ -58,7 +58,7 @@ Examples:
58
58
  ```
59
59
 
60
60
  ```k
61
- > bool = ?<{} true, {} false>;
61
+ > bool? = ?<{} true, {} false>;
62
62
  ```
63
63
 
64
64
  ```k
@@ -147,7 +147,7 @@ aliases, and metadata into the session. Aliases are recovered from
147
147
 
148
148
  In `k`, there are no primitive numbers or built-in numerical literals.
149
149
  Values such as integers are represented as algebraic bit trees (e.g.
150
- `bits = ?< {} _, B 0, B 1 > = B;`).
150
+ `bits? = ?< {} _, B 0, B 1 > = B;`).
151
151
 
152
152
  A codec translates between external text and enveloped *k* values:
153
153
  - `parse(text)`: parses an external string into an enveloped *k* value.
@@ -309,7 +309,7 @@ Evaluated values print in k syntax together with the inferred envelope:
309
309
  ## Example Session
310
310
 
311
311
  ```text
312
- > nat = ?< {} 0, N +1 > = N;
312
+ > nat? = ?< {} 0, N +1 > = N;
313
313
  > inc = | +1;
314
314
  > {} | 0
315
315
  {}|0 ?<{} 0, ...>
@@ -7,8 +7,8 @@ A schema in `k` describes a set of finite labeled trees, which can be recognized
7
7
  For example, the recursive schema of a `list`:
8
8
 
9
9
  ```k-lang
10
- bool = ?< {} true, {} false >;
11
- list = ?< {} nil, { < {} true, {} false > head, L tail } cons > = L;
10
+ bool? = ?< {} true, {} false >;
11
+ list? = ?< {} nil, { < {} true, {} false > head, L tail } cons > = L;
12
12
  ```
13
13
 
14
14
  corresponds to the following 4-rule grammar:
@@ -44,7 +44,7 @@ Each schema structure introduces one or more *states* in the automaton. A union
44
44
  For example, consider the `bool` schema:
45
45
 
46
46
  ```k-lang
47
- bool = ?< {} true, {} false >;
47
+ bool? = ?< {} true, {} false >;
48
48
  ```
49
49
 
50
50
  This schema translates to an automaton with two states:
@@ -88,7 +88,7 @@ This traversal produces a unique and deterministic numbering for any finite dire
88
88
  For a schema representing binary natural numbers:
89
89
 
90
90
  ```k-lang
91
- bnat = ?< Bnat 0, Bnat 1, {} _ > = Bnat;
91
+ bnat? = ?< Bnat 0, Bnat 1, {} _ > = Bnat;
92
92
  ```
93
93
 
94
94
  the canonical automaton form is:
@@ -111,11 +111,11 @@ Each node in the value tree is encoded based on its automaton state:
111
111
 
112
112
  #### **Example: Natural numbers**
113
113
 
114
- For type `bnat`, intuitively binary natural numbers defined by `bnat = ?< Bnat 0, Bnat 1, {} _ > = Bnat;`, we get
114
+ For type `bnat?`, intuitively binary natural numbers defined by `bnat? = ?< Bnat 0, Bnat 1, {} _ > = Bnat;`, we get
115
115
 
116
116
  ```text
117
- > :rel bnat
118
- bnat = ?< Bnat 0, Bnat 1, {} _ > = Bnat; -- @...
117
+ > :rel bnat?
118
+ bnat? = ?< Bnat 0, Bnat 1, {} _ > = Bnat; -- @...
119
119
  ```
120
120
 
121
121
  The corresponding Context-Free grammar is:
@@ -191,7 +191,7 @@ Subtrees that happen to produce the same local payload bits under different type
191
191
  Consider a binary tree with repeated leaf patterns:
192
192
 
193
193
  ```k-lang
194
- tree = ?< { T left, T right } branch, int leaf > = T;
194
+ tree? = ?< { T left, T right } branch, int? leaf > = T;
195
195
  ```
196
196
 
197
197
  Value: A tree corresponding to the structure:
@@ -293,8 +293,8 @@ These are encoder-side construction choices for reaching the same canonical resu
293
293
  Consider a list of similar records:
294
294
 
295
295
  ```k-lang
296
- record = ?{ string name, int age, string department };
297
- company = ?< { record head, C tail } cons, {} nil > = C;
296
+ record? = ?{ string? name, int? age, string? department };
297
+ company? = ?< { record? head, C tail } cons, {} nil > = C;
298
298
  ```
299
299
 
300
300
  Value representing 1000 employees where 80% work in "Engineering":
@@ -712,9 +712,9 @@ const Arena = struct {
712
712
  Consider processing a large JSON-like structure where we only need one field:
713
713
 
714
714
  ```k-lang
715
- metadata = ?{ timestamp created, string author };
716
- list = ?< { nat value, L next } cons, {} nil > = L;
717
- record = ?{ string name, list data, metadata meta };
715
+ metadata? = ?{ timestamp created, string? author };
716
+ list? = ?< { nat? value, L next } cons, {} nil > = L;
717
+ record? = ?{ string? name, list? data, metadata? meta };
718
718
  ```
719
719
 
720
720
  For the input program `\x.x.name` (extract name field):
@@ -153,7 +153,7 @@ for any given input, at most one result tree can be produced.
153
153
  Given:
154
154
 
155
155
  ```k-lang
156
- bool = ?< {} true, {} false >;
156
+ bool? = ?< {} true, {} false >;
157
157
  neg = ?< {} true, {} false > < /true | false, /false | true >;
158
158
  ```
159
159
 
@@ -73,8 +73,8 @@ These tables ensure that generated code can find fields and variants by number r
73
73
  Consider a simple k program:
74
74
 
75
75
  ```k-lang
76
- bool = ?< {} true, {} false >;
77
- neg = bool < /true | false, /false | true > bool;
76
+ bool? = ?< {} true, {} false >;
77
+ neg = bool? < /true | false, /false | true > bool?;
78
78
  ```
79
79
 
80
80
  1. **Parsing:**
@@ -42,7 +42,7 @@ Each function in the program becomes a small list or tree of such IR instruction
42
42
  Source program:
43
43
 
44
44
  ```k-lang
45
- bool = ?< {} true, {} false >;
45
+ bool? = ?< {} true, {} false >;
46
46
  neg = ?< {} true, {} false > < /true | false, /false | true >;
47
47
  ```
48
48
 
@@ -50,7 +50,7 @@ The IR for `neg` can be written informally as:
50
50
 
51
51
  ```text
52
52
  neg:
53
- CHECK_FILTER bool
53
+ CHECK_FILTER bool?
54
54
  UNION [
55
55
  SEQ [ PROJECT true, CONST false ],
56
56
  SEQ [ PROJECT false, CONST true ]
@@ -68,7 +68,7 @@ This rule allows the decoder to know, from the type alone, how many bits to read
68
68
  For the type
69
69
 
70
70
  ```k-lang
71
- bnat = ?< Bnat 0, Bnat 1, {} _ > = Bnat;
71
+ bnat? = ?< Bnat 0, Bnat 1, {} _ > = Bnat;
72
72
  ```
73
73
 
74
74
  the canonical form has three transitions from state C0.
@@ -3,7 +3,7 @@ swap_yes_no = < /yes |no, /no |yes >;
3
3
  yes = {}|yes;
4
4
  no = yes swap_yes_no;
5
5
 
6
- bits = ?< {} _, bits 0, bits 1 > = bits;
6
+ bits? = ?< {} _, bits? 0, bits? 1 > = bits?;
7
7
 
8
8
  0b = {} | _;
9
9
  -- |1 = | 1;
@@ -64,7 +64,7 @@ GT = {inOrder /no if, () then} . then;
64
64
  EQ = {inOrder /yes if, {.x y, .y x} inOrder /yes and, () then} .then;
65
65
 
66
66
  --------------------------------------
67
- int = ?< <bits 0, bits 1, {} _>=bits "+", bits "-" >;
67
+ int? = ?< <bits 0, bits 1, {} _>=bits "+", bits "-" >;
68
68
 
69
69
  remove_leading_zeros = <
70
70
  /0 remove_leading_zeros,
package/Examples/bnat.k CHANGED
@@ -1,6 +1,6 @@
1
1
  -- Standard binary encoding of natural numbers
2
2
 
3
- bnat = ?< {} _, bnat 0, bnat 1 > = bnat;
3
+ bnat? = ?< {} _, bnat? 0, bnat? 1 > = bnat?;
4
4
 
5
5
  _ = {}|_;
6
6
  _1 = |1;
@@ -5,12 +5,12 @@
5
5
  yes = {} | yes;
6
6
  no = {} | no;
7
7
 
8
- bits = ?< {} _, bits 0, bits 1 > = bits;
8
+ bits? = ?< {} _, bits? 0, bits? 1 > = bits?;
9
9
  0b = {} | _;
10
10
  0 = 0b |0;
11
11
  1 = 0b |1;
12
12
 
13
- unat = ?< {} _, unat 1 > = unat;
13
+ unat? = ?< {} _, unat? 1 > = unat?;
14
14
  0u = {} | _;
15
15
 
16
16
  empty = < /_ yes, no >;
package/README.md CHANGED
@@ -122,9 +122,9 @@ All data structures in k are built from products and tagged unions (variants).
122
122
  Relations and pattern filters describe their domains:
123
123
 
124
124
  ```k
125
- bit = ?< {} 0, {} 1 >;
126
- byte = ?{ Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6, Bit 7 };
127
- bits = ?< {} _, bits 0, bits 1 > = bits;
125
+ bit? = ?< {} 0, {} 1 >;
126
+ byte? = ?{ Bit 0, Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6, Bit 7 };
127
+ bits? = ?< {} _, bits? 0, bits? 1 > = bits?;
128
128
  ```
129
129
 
130
130
  There are no built-in primitive values. The empty product `{}` is the
@@ -236,7 +236,6 @@ For a detailed guide to all four formats, commands, and workflows, see
236
236
  | `k-vm` | Execute or inspect lowered kVM bytecode artifacts (`.kvm`) |
237
237
  | `k-wasm` / `k-wasm-compile` / `k-wasm-run` | Compile and run via WebAssembly backend |
238
238
  | `k-llvm-build` / `k-llvm-compile` / `k-llvm-run` | Compile and run via LLVM backend |
239
- | `k-arm64` / `k-arm64-compile` / `k-arm64-run` | Compile and run via Linux ARM64 native backend |
240
239
 
241
240
  Installed binary names are `k-` plus the source basename without
242
241
  `.mjs`, except for `k.mjs` itself. Source names that already include
@@ -251,9 +250,6 @@ Backends live under [`backends/`](backends/) as npm workspaces:
251
250
  and the standalone Web REPL.
252
251
  - [`backends/llvm`](backends/llvm/) lowers polymorphic kVM programs into
253
252
  LLVM IR and compiled native test executables.
254
- - [`backends/arm64`](backends/arm64/) lowers polymorphic kVM programs
255
- directly into standalone Linux ARM64 native executables without external
256
- compiler dependencies.
257
253
 
258
254
  All backends integrate with the compiler and binary codecs through
259
255
  [`backend-api.mjs`](backend-api.mjs).
@@ -353,7 +349,6 @@ npm run prepare # regenerate parsers from .jison grammars
353
349
  npm test # run the fail-fast full suite with per-test timings
354
350
  npm run test:wasm # run the WebAssembly backend tests
355
351
  npm run test:llvm # run the LLVM backend tests
356
- npm run test:arm64 # run the Linux ARM64 backend tests
357
352
  npm run build:repl-html # build the standalone single-file Web REPL (repl.html)
358
353
  npm run compare # run multi-backend benchmark and conformance harness
359
354
  npm run perf:poly # run polymorphic benchmark across backends
@@ -1489,6 +1489,39 @@ k_value *k_read_wire(FILE *in, k_rt *rt) {
1489
1489
  return value;
1490
1490
  }
1491
1491
 
1492
+ static int is_union_kind(int kind) {
1493
+ return kind == KP_OPEN_UNION || kind == KP_CLOSED_UNION;
1494
+ }
1495
+
1496
+ static int is_product_kind(int kind) {
1497
+ return kind == KP_OPEN_PRODUCT || kind == KP_CLOSED_PRODUCT;
1498
+ }
1499
+
1500
+ int k_pattern_compatible(k_pattern *a, k_pattern *b) {
1501
+ if (a == b) return 1;
1502
+ if (a == NULL || b == NULL) return 0;
1503
+ if (a->node_count != b->node_count) return 0;
1504
+ for (size_t i = 0; i < a->node_count; i++) {
1505
+ k_pattern_node *left = &a->nodes[i];
1506
+ k_pattern_node *right = &b->nodes[i];
1507
+ if (left->kind != right->kind) {
1508
+ if (!(is_union_kind(left->kind) && is_union_kind(right->kind)) &&
1509
+ !(is_product_kind(left->kind) && is_product_kind(right->kind))) {
1510
+ return 0;
1511
+ }
1512
+ }
1513
+ if (left->edge_count != right->edge_count) return 0;
1514
+ for (size_t j = 0; j < left->edge_count; j++) {
1515
+ if (left->edges[j].target != right->edges[j].target) return 0;
1516
+ if (!bytes_equal(left->edges[j].label, left->edges[j].label_length,
1517
+ right->edges[j].label, right->edges[j].label_length)) {
1518
+ return 0;
1519
+ }
1520
+ }
1521
+ }
1522
+ return 1;
1523
+ }
1524
+
1492
1525
  int k_pattern_equal(k_pattern *a, k_pattern *b) {
1493
1526
  if (a == b) return 1;
1494
1527
  if (a == NULL || b == NULL) return 0;
@@ -104,6 +104,7 @@ k_wire_input k_read_wire_envelope(FILE *in, k_rt *rt);
104
104
  k_wire_input k_decode_wire_envelope(const unsigned char *bytes, size_t length, k_rt *rt);
105
105
  void k_wire_input_free(k_wire_input input);
106
106
  int k_pattern_equal(k_pattern *a, k_pattern *b);
107
+ int k_pattern_compatible(k_pattern *a, k_pattern *b);
107
108
  k_wire_prefix k_wire_prefix_for_pattern(k_pattern *pattern);
108
109
  void k_wire_prefix_free(k_wire_prefix prefix);
109
110
  k_value *k_decode_wire_value_with_prefix(const unsigned char *bytes, size_t length, k_wire_prefix *prefix, k_pattern *pattern, k_rt *rt);