@fraczak/k 6.3.2 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/DOCS/CODECS.md +258 -0
  2. package/DOCS/DICTIONARY.md +1172 -0
  3. package/DOCS/KIR_SPECIALIZATION_DESIGN.md +51 -46
  4. package/DOCS/KIR_V1.md +221 -0
  5. package/DOCS/KVM_EXECUTION_MODEL.md +56 -19
  6. package/DOCS/OBJECT_FILE_AND_PATTERN.md +2 -1
  7. package/DOCS/PROFILING_AND_TRACING.md +196 -0
  8. package/DOCS/REPL.md +76 -14
  9. package/DOCS/WEB_REPL_REQUIREMENTS.md +135 -0
  10. package/DOCS/dependency-graph.md +144 -0
  11. package/Examples/arithmetics.k +51 -48
  12. package/Examples/bnat.k +1 -1
  13. package/Examples/buda.k +44 -0
  14. package/Examples/ieee.k +300 -8671
  15. package/Examples/poly.k +67 -0
  16. package/README.md +68 -19
  17. package/augmentation.mjs +9 -7
  18. package/backend-api.mjs +11 -2
  19. package/backends/arm64/README.md +104 -0
  20. package/backends/arm64/bin/k-arm64-compile.mjs +101 -0
  21. package/backends/arm64/bin/k-arm64-run.mjs +58 -0
  22. package/backends/arm64/bin/k-arm64.mjs +88 -0
  23. package/backends/arm64/scripts/run-tests.mjs +41 -0
  24. package/backends/arm64/src/arm64.mjs +229 -0
  25. package/backends/arm64/src/cli.mjs +155 -0
  26. package/backends/arm64/src/kvm2arm64.mjs +906 -0
  27. package/backends/arm64/src/runtime.c +728 -0
  28. package/backends/arm64/tests/integration.sh +112 -0
  29. package/backends/arm64/tests/test-arm64.mjs +240 -0
  30. package/backends/llvm/README.md +135 -0
  31. package/backends/llvm/bin/k-llvm-build.mjs +71 -0
  32. package/backends/llvm/bin/k-llvm-compile.mjs +68 -0
  33. package/backends/llvm/bin/k-llvm-jit.mjs +160 -0
  34. package/backends/llvm/bin/k-llvm-run.mjs +90 -0
  35. package/backends/llvm/runtime/krt.c +1508 -0
  36. package/backends/llvm/runtime/krt.h +109 -0
  37. package/backends/llvm/scripts/conformance.mjs +76 -0
  38. package/backends/llvm/scripts/run-tests.mjs +28 -0
  39. package/backends/llvm/src/executable.mjs +562 -0
  40. package/backends/llvm/src/kvm2llvm.mjs +998 -0
  41. package/backends/llvm/src/llvm.mjs +1092 -0
  42. package/backends/llvm/tests/composition-driver.c +22 -0
  43. package/backends/llvm/tests/identity-driver.c +27 -0
  44. package/backends/llvm/tests/integration.sh +153 -0
  45. package/backends/llvm/tests/perf-support.mjs +735 -0
  46. package/backends/llvm/tests/product-driver.c +23 -0
  47. package/backends/llvm/tests/projection-driver.c +19 -0
  48. package/backends/llvm/tests/relation-driver.c +29 -0
  49. package/backends/llvm/tests/test-llvm.mjs +240 -0
  50. package/backends/llvm/tests/union-driver.c +31 -0
  51. package/backends/llvm/tests/variant-driver.c +19 -0
  52. package/backends/llvm/tests/variant-projection-driver.c +21 -0
  53. package/backends/wasm/DOCS/USE-WEB-ASSEMBLY.md +233 -0
  54. package/backends/wasm/README.md +97 -0
  55. package/backends/wasm/TECHNICAL-DEBT.md +60 -0
  56. package/backends/wasm/bin/k-wasm-compile.mjs +50 -0
  57. package/backends/wasm/bin/k-wasm-run.mjs +140 -0
  58. package/backends/wasm/bin/k-wasm.mjs +49 -0
  59. package/backends/wasm/demo_ieee.mjs +319 -0
  60. package/backends/wasm/runtime.wat +102 -0
  61. package/backends/wasm/scripts/run-tests.mjs +41 -0
  62. package/backends/wasm/src/cli.mjs +133 -0
  63. package/backends/wasm/src/kvm2wasm.mjs +661 -0
  64. package/backends/wasm/src/wasm.mjs +1010 -0
  65. package/backends/wasm/tests/integration.sh +182 -0
  66. package/backends/wasm/tests/perf-support.mjs +336 -0
  67. package/backends/wasm/tests/test-wasm.mjs +617 -0
  68. package/browser/repl-browser.mjs +1584 -0
  69. package/browser/shims/assert.mjs +19 -0
  70. package/browser/shims/buffer.mjs +224 -0
  71. package/browser/shims/crypto.mjs +103 -0
  72. package/browser/shims/fs.mjs +159 -0
  73. package/browser/shims/os.mjs +17 -0
  74. package/browser/shims/path.mjs +54 -0
  75. package/browser/shims/process.mjs +44 -0
  76. package/browser/shims/readline.mjs +11 -0
  77. package/browser/shims/url.mjs +12 -0
  78. package/browser/vfs-data.mjs +19 -0
  79. package/codecs/README.md +3 -0
  80. package/codecs/json.mjs +16 -2
  81. package/codecs/runtime/show-value.mjs +2 -2
  82. package/conformance/helper-reuse/case.json +8 -0
  83. package/conformance/helper-reuse/expected.kv +1 -0
  84. package/conformance/helper-reuse/input.kv +1 -0
  85. package/conformance/helper-reuse/program.k +2 -0
  86. package/conformance/identity/case.json +8 -0
  87. package/conformance/identity/expected.kv +1 -0
  88. package/conformance/identity/input.kv +1 -0
  89. package/conformance/identity/program.k +1 -0
  90. package/conformance/product-projection/case.json +8 -0
  91. package/conformance/product-projection/expected.kv +1 -0
  92. package/conformance/product-projection/input.kv +1 -0
  93. package/conformance/product-projection/program.k +1 -0
  94. package/conformance/run.mjs +149 -0
  95. package/conformance/variant-construction/case.json +8 -0
  96. package/conformance/variant-construction/expected.kv +1 -0
  97. package/conformance/variant-construction/input.kv +1 -0
  98. package/conformance/variant-construction/program.k +1 -0
  99. package/harness/adapters/arm64.mjs +105 -0
  100. package/harness/adapters/kvm.mjs +144 -0
  101. package/harness/adapters/llvm.mjs +118 -0
  102. package/harness/adapters/native.mjs +77 -0
  103. package/harness/adapters/wasm.mjs +169 -0
  104. package/harness/backend.mjs +95 -0
  105. package/harness/cli.mjs +130 -0
  106. package/harness/format.mjs +95 -0
  107. package/harness/index.mjs +25 -0
  108. package/harness/persistent.mjs +121 -0
  109. package/harness/registry.mjs +44 -0
  110. package/harness/suite.mjs +257 -0
  111. package/harness/suites/poly.mjs +156 -0
  112. package/harness/trace.mjs +50 -0
  113. package/harness/util.mjs +92 -0
  114. package/intrinsics.mjs +41 -0
  115. package/kir.mjs +399 -0
  116. package/kvm.mjs +585 -81
  117. package/objects/README.md +37 -3
  118. package/objects/compile.mjs +15 -22
  119. package/objects/inspect.mjs +96 -0
  120. package/objects/validate.mjs +415 -0
  121. package/package.json +51 -19
  122. package/repl-codecs.mjs +133 -20
  123. package/repl.mjs +408 -59
  124. package/run.mjs +2 -7
  125. package/scripts/benchmark.mjs +406 -22
  126. package/scripts/build-repl-html.mjs +1784 -0
  127. package/scripts/perf-ieee.mjs +586 -0
  128. package/scripts/perf-int.mjs +531 -0
  129. package/scripts/perf-poly.mjs +945 -0
  130. package/scripts/render-benchmark.mjs +12 -8
  131. package/scripts/run-tests.mjs +6 -0
  132. package/tests/integration.sh +19 -2
  133. package/tests/test-arithmetics.mjs +54 -0
  134. package/tests/test-ieee-arithmetic.mjs +6 -2
  135. package/tests/test-kir.mjs +85 -0
  136. package/tests/test-kvm-polymorphic.mjs +278 -0
  137. package/tests/test-kvm.mjs +25 -6
  138. package/tests/test-poly.mjs +129 -0
  139. package/tests/test-repl-html.mjs +303 -0
  140. package/tests/test-repl.mjs +175 -1
  141. package/tests/test-validate-object.mjs +40 -0
  142. package/a.klib +0 -1
  143. package/a.kvm +0 -142676
  144. package/a.wasm +0 -0
  145. package/benchmark-results.html +0 -532
  146. package/benchmark-results.json +0 -480
  147. package/k-vscode/.vscode/launch.json +0 -14
  148. package/k-vscode/README.md +0 -75
  149. package/k-vscode/extension.js +0 -273
  150. package/k-vscode/language-configuration.json +0 -42
  151. package/k-vscode/package.json +0 -79
  152. package/k-vscode/snippets/k.json +0 -92
  153. package/k-vscode/syntaxes/k.tmLanguage.json +0 -599
  154. package/k-vscode/test/background-ranges.test.js +0 -50
  155. package/k-vscode/themes/k-contrast-color-theme.json +0 -104
  156. package/scripts/git-hooks/pre-commit +0 -10
@@ -0,0 +1,1092 @@
1
+ import fs from "node:fs";
2
+ import {
3
+ decodeObject,
4
+ isIntrinsic,
5
+ unsupportedIntrinsic,
6
+ retypeObjectRelationForBackend,
7
+ objectToKVMArtifact,
8
+ specializeKVM,
9
+ lowerKIRToKVM,
10
+ objectToKIRP
11
+ } from "@fraczak/k/backend-api.mjs";
12
+ import { compileKVMModuleToLLVM } from "./kvm2llvm.mjs";
13
+
14
+ const ARTIFACT_FORMAT = "k-llvm";
15
+ const ARTIFACT_VERSION = 1;
16
+
17
+ const K_VALUE_SIZE = 40;
18
+ const K_VALUE_ALLOC_SIZE = 48;
19
+ const K_VALUE_KIND_PRODUCT = 1;
20
+ const K_VALUE_KIND_VARIANT = 2;
21
+ const K_VALUE_KIND_OFFSET = 0;
22
+ const K_VALUE_RT_OFFSET = 8;
23
+ const K_VALUE_PRODUCT_COUNT_OFFSET = 16;
24
+ const K_VALUE_PRODUCT_CAPACITY_OFFSET = 24;
25
+ const K_VALUE_PRODUCT_FIELDS_OFFSET = 32;
26
+ const K_VALUE_VARIANT_TAG_OFFSET = 16;
27
+ const K_VALUE_VARIANT_TAG_LENGTH_OFFSET = 24;
28
+ const K_VALUE_VARIANT_PAYLOAD_OFFSET = 32;
29
+ const K_PRODUCT_HEADER_SIZE = 48;
30
+ const K_FIELD_SIZE = 24;
31
+ const K_FIELD_LABEL_OFFSET = 0;
32
+ const K_FIELD_LABEL_LENGTH_OFFSET = 8;
33
+ const K_FIELD_VALUE_OFFSET = 16;
34
+ const K_RT_BLOCKS_OFFSET = 0;
35
+ const K_RT_UNIT_CACHE_OFFSET = 8;
36
+ const K_RT_BIT0_CACHE_OFFSET = 16;
37
+ const K_RT_BIT1_CACHE_OFFSET = 24;
38
+ const K_RT_HAS_REUSABLE_BLOCKS_OFFSET = 32;
39
+ const K_ARENA_BLOCK_USED_OFFSET = 8;
40
+ const K_ARENA_BLOCK_CAPACITY_OFFSET = 16;
41
+ const K_ARENA_BLOCK_DATA_OFFSET = 24;
42
+
43
+ function alignArenaSize(size) {
44
+ return (size + 15) & ~15;
45
+ }
46
+
47
+ function cStringBytes(text) {
48
+ return [...Buffer.from(text, "utf8"), 0]
49
+ .map((byte) => {
50
+ if (byte === 10) return "\\0A";
51
+ if (byte === 34) return "\\22";
52
+ if (byte === 92) return "\\5C";
53
+ if (byte >= 32 && byte <= 126) return String.fromCharCode(byte);
54
+ return `\\${byte.toString(16).padStart(2, "0").toUpperCase()}`;
55
+ })
56
+ .join("");
57
+ }
58
+
59
+ function readPattern(inputPattern) {
60
+ if (Array.isArray(inputPattern)) return inputPattern;
61
+ if (typeof inputPattern !== "string" || inputPattern.length === 0) {
62
+ throw new Error("compileToLLVM requires an input pattern property list");
63
+ }
64
+ const text = fs.existsSync(inputPattern) ? fs.readFileSync(inputPattern, "utf8") : inputPattern;
65
+ return JSON.parse(text);
66
+ }
67
+
68
+ export function llvmIdentifier(name) {
69
+ return String(name || "main").replace(/[^A-Za-z0-9_$.-]/g, "_");
70
+ }
71
+
72
+ function llvmStringGlobalName(index) {
73
+ return `@k_label_${index}`;
74
+ }
75
+
76
+ function llvmRelationFunctionName(name, index) {
77
+ return `@k_rel_${llvmIdentifier(name)}_${index}`;
78
+ }
79
+
80
+ function runtimeDeclarations() {
81
+ return [
82
+ "%k_rt_mark = type { ptr, i64 }",
83
+ "%k_result = type { i32, ptr }",
84
+ "",
85
+ "declare %k_rt_mark @k_rt_mark(ptr)",
86
+ "declare void @k_rt_rewind(ptr, %k_rt_mark)",
87
+ "declare ptr @k_rt_alloc(ptr, i64)",
88
+ "declare ptr @k_unit(ptr)",
89
+ "declare ptr @k_bit0(ptr)",
90
+ "declare ptr @k_bit1(ptr)",
91
+ "declare ptr @k_product(ptr, i64)",
92
+ "declare void @k_product_set(ptr, ptr, ptr)",
93
+ "declare void @k_product_set_n(ptr, ptr, i64, ptr)",
94
+ "declare void @k_product_set_borrowed_n(ptr, ptr, i64, ptr)",
95
+ "declare void @k_product_set_at(ptr, i64, ptr, i64, ptr)",
96
+ "declare ptr @k_product_get(ptr, ptr)",
97
+ "declare ptr @k_product_get_n(ptr, ptr, i64)",
98
+ "declare ptr @k_product_get_at(ptr, i64)",
99
+ "declare ptr @k_variant(ptr, ptr, ptr)",
100
+ "declare ptr @k_variant_n(ptr, ptr, i64, ptr)",
101
+ "declare ptr @k_variant_borrowed_n(ptr, ptr, i64, ptr)",
102
+ "declare ptr @k_variant_borrowed_direct_n(ptr, ptr, i64, ptr)",
103
+ "declare ptr @k_variant_unit_borrowed_n(ptr, ptr, i64)",
104
+ "declare ptr @k_variant_tag(ptr)",
105
+ "declare ptr @k_variant_payload(ptr)",
106
+ "declare i32 @k_equal(ptr, ptr)",
107
+ "declare i32 @k_variant_tag_matches(ptr, ptr, i64)"
108
+ ];
109
+ }
110
+
111
+ function createLoweringContext(functionNames = new Map(), labels = new Map(), syntheticFunctions = [], patternGraph = null, tail = {}) {
112
+ return {
113
+ temp: 0,
114
+ block: 0,
115
+ functionNames,
116
+ labels,
117
+ syntheticFunctions,
118
+ patternGraph,
119
+ runtimeMode: tail.runtimeMode || "fast",
120
+ tailRef: tail.refName || null,
121
+ catchTail: tail.catchTail || false,
122
+ tailInputSlot: tail.inputSlot || null,
123
+ tailLoopBlock: tail.loopBlock || null,
124
+ currentBlock: "entry",
125
+ lines: [],
126
+ addSyntheticFunction(body, tailPosition = false) {
127
+ const name = `@k_union_arm_${this.syntheticFunctions.length}`;
128
+ const index = this.syntheticFunctions.length;
129
+ this.syntheticFunctions.push(null);
130
+ const ctx = createLoweringContext(this.functionNames, this.labels, this.syntheticFunctions, this.patternGraph, {
131
+ refName: this.tailRef,
132
+ runtimeMode: this.runtimeMode
133
+ });
134
+ this.syntheticFunctions[index] = emitFunctionBody(name, body, ctx, "internal", { tail: tailPosition });
135
+ return name;
136
+ },
137
+ addUnionFunction(items, tailPosition = false) {
138
+ const name = `@k_union_expr_${this.syntheticFunctions.length}`;
139
+ const ctx = createLoweringContext(this.functionNames, this.labels, this.syntheticFunctions, this.patternGraph, {
140
+ refName: this.tailRef,
141
+ runtimeMode: this.runtimeMode
142
+ });
143
+ const body = emitUnionFunctionBody(name, items, ctx, "internal", { tail: tailPosition });
144
+ this.syntheticFunctions.push(body);
145
+ return name;
146
+ },
147
+ tempName(prefix) {
148
+ return `%${prefix}${this.temp++}`;
149
+ },
150
+ blockName(prefix) {
151
+ return `bb_${prefix}${this.block++}`;
152
+ },
153
+ labelRef(label) {
154
+ const text = String(label);
155
+ let global = this.labels.get(text);
156
+ if (!global) {
157
+ const index = this.labels.size;
158
+ global = {
159
+ name: llvmStringGlobalName(index),
160
+ length: Buffer.byteLength(text, "utf8") + 1,
161
+ byteLength: Buffer.byteLength(text, "utf8"),
162
+ text
163
+ };
164
+ this.labels.set(text, global);
165
+ }
166
+ const pointer = this.tempName("label");
167
+ this.lines.push(` ${pointer} = getelementptr inbounds [${global.length} x i8], ptr ${global.name}, i64 0, i64 0`);
168
+ return { pointer, length: global.byteLength, text };
169
+ }
170
+ };
171
+ }
172
+
173
+ function patternNode(ctx, patternId) {
174
+ if (patternId == null || ctx.patternGraph?.nodes == null) return null;
175
+ return ctx.patternGraph.nodes.find((node) => node.id === patternId) || null;
176
+ }
177
+
178
+ function patternEdgeIndex(ctx, patternId, label) {
179
+ const node = patternNode(ctx, patternId);
180
+ const edges = node?.edges;
181
+ if (!Array.isArray(edges)) return -1;
182
+ return edges.findIndex((edge) => edge.label === label);
183
+ }
184
+
185
+ function result(ctx, status, value = "null") {
186
+ const statusValue = ctx.tempName("status");
187
+ const resultValue = ctx.tempName("result");
188
+ return [
189
+ ` ${statusValue} = insertvalue %k_result undef, i32 ${status}, 0`,
190
+ ` ${resultValue} = insertvalue %k_result ${statusValue}, ptr ${value}, 1`,
191
+ ` ret %k_result ${resultValue}`
192
+ ];
193
+ }
194
+
195
+ function unsupported(ctx) {
196
+ ctx.lines.push(...result(ctx, 1));
197
+ }
198
+
199
+ function nullCheck(ctx, value) {
200
+ const missing = ctx.tempName("missing");
201
+ const okBlock = ctx.blockName("ok");
202
+ const missingBlock = ctx.blockName("missing");
203
+ ctx.lines.push(` ${missing} = icmp eq ptr ${value}, null`);
204
+ ctx.lines.push(` br i1 ${missing}, label %${missingBlock}, label %${okBlock}`);
205
+ ctx.lines.push(`${missingBlock}:`);
206
+ ctx.lines.push(...result(ctx, 1));
207
+ ctx.lines.push(`${okBlock}:`);
208
+ ctx.currentBlock = okBlock;
209
+ }
210
+
211
+ function bytePtr(ctx, base, offset, prefix = "slot") {
212
+ const slot = ctx.tempName(prefix);
213
+ ctx.lines.push(` ${slot} = getelementptr inbounds i8, ptr ${base}, i64 ${offset}`);
214
+ return slot;
215
+ }
216
+
217
+ function storeI32At(ctx, base, offset, value) {
218
+ ctx.lines.push(` store i32 ${value}, ptr ${bytePtr(ctx, base, offset, "i32_slot")}`);
219
+ }
220
+
221
+ function storeI64At(ctx, base, offset, value) {
222
+ ctx.lines.push(` store i64 ${value}, ptr ${bytePtr(ctx, base, offset, "i64_slot")}`);
223
+ }
224
+
225
+ function storePtrAt(ctx, base, offset, value) {
226
+ ctx.lines.push(` store ptr ${value}, ptr ${bytePtr(ctx, base, offset, "ptr_slot")}`);
227
+ }
228
+
229
+ function loadI32At(ctx, base, offset, prefix = "i32") {
230
+ const value = ctx.tempName(prefix);
231
+ ctx.lines.push(` ${value} = load i32, ptr ${bytePtr(ctx, base, offset, `${prefix}_slot`)}`);
232
+ return value;
233
+ }
234
+
235
+ function loadI64At(ctx, base, offset, prefix = "i64") {
236
+ const value = ctx.tempName(prefix);
237
+ ctx.lines.push(` ${value} = load i64, ptr ${bytePtr(ctx, base, offset, `${prefix}_slot`)}`);
238
+ return value;
239
+ }
240
+
241
+ function loadPtrAt(ctx, base, offset, prefix = "ptr") {
242
+ const value = ctx.tempName(prefix);
243
+ ctx.lines.push(` ${value} = load ptr, ptr ${bytePtr(ctx, base, offset, `${prefix}_slot`)}`);
244
+ return value;
245
+ }
246
+
247
+ function lowerRawAlloc(ctx, requestedSize, allocatedSize = requestedSize) {
248
+ const blockSlot = bytePtr(ctx, "%rt", K_RT_BLOCKS_OFFSET, "rt_blocks_slot");
249
+ const block = ctx.tempName("arena_block");
250
+ const hasBlock = ctx.tempName("has_block");
251
+ const checkBlock = ctx.blockName("alloc_check");
252
+ const fastBlock = ctx.blockName("alloc_fast");
253
+ const slowBlock = ctx.blockName("alloc_slow");
254
+ const doneBlock = ctx.blockName("alloc_done");
255
+
256
+ ctx.lines.push(` ${block} = load ptr, ptr ${blockSlot}`);
257
+ ctx.lines.push(` ${hasBlock} = icmp ne ptr ${block}, null`);
258
+ ctx.lines.push(` br i1 ${hasBlock}, label %${checkBlock}, label %${slowBlock}`);
259
+
260
+ ctx.lines.push(`${checkBlock}:`);
261
+ const usedSlot = bytePtr(ctx, block, K_ARENA_BLOCK_USED_OFFSET, "arena_used_slot");
262
+ const capacitySlot = bytePtr(ctx, block, K_ARENA_BLOCK_CAPACITY_OFFSET, "arena_capacity_slot");
263
+ const used = ctx.tempName("arena_used");
264
+ const capacity = ctx.tempName("arena_capacity");
265
+ const remaining = ctx.tempName("arena_remaining");
266
+ const fits = ctx.tempName("arena_fits");
267
+ ctx.lines.push(` ${used} = load i64, ptr ${usedSlot}`);
268
+ ctx.lines.push(` ${capacity} = load i64, ptr ${capacitySlot}`);
269
+ ctx.lines.push(` ${remaining} = sub i64 ${capacity}, ${used}`);
270
+ ctx.lines.push(` ${fits} = icmp uge i64 ${remaining}, ${allocatedSize}`);
271
+ ctx.lines.push(` br i1 ${fits}, label %${fastBlock}, label %${slowBlock}`);
272
+
273
+ ctx.lines.push(`${fastBlock}:`);
274
+ const data = bytePtr(ctx, block, K_ARENA_BLOCK_DATA_OFFSET, "arena_data");
275
+ const value = ctx.tempName("raw_value");
276
+ const newUsed = ctx.tempName("arena_new_used");
277
+ ctx.lines.push(` ${value} = getelementptr inbounds i8, ptr ${data}, i64 ${used}`);
278
+ ctx.lines.push(` ${newUsed} = add i64 ${used}, ${allocatedSize}`);
279
+ ctx.lines.push(` store i64 ${newUsed}, ptr ${usedSlot}`);
280
+ ctx.lines.push(` br label %${doneBlock}`);
281
+
282
+ ctx.lines.push(`${slowBlock}:`);
283
+ const slowValue = ctx.tempName("raw_slow_value");
284
+ ctx.lines.push(` ${slowValue} = call ptr @k_rt_alloc(ptr %rt, i64 ${requestedSize})`);
285
+ ctx.lines.push(` br label %${doneBlock}`);
286
+
287
+ ctx.lines.push(`${doneBlock}:`);
288
+ const resultValue = ctx.tempName("raw_alloc");
289
+ ctx.lines.push(` ${resultValue} = phi ptr [${value}, %${fastBlock}], [${slowValue}, %${slowBlock}]`);
290
+ ctx.currentBlock = doneBlock;
291
+ return resultValue;
292
+ }
293
+
294
+ function lowerUnit(ctx) {
295
+ if (ctx.runtimeMode === "compact") {
296
+ const unit = ctx.tempName("unit");
297
+ ctx.lines.push(` ${unit} = call ptr @k_unit(ptr %rt)`);
298
+ return unit;
299
+ }
300
+
301
+ const slot = bytePtr(ctx, "%rt", K_RT_UNIT_CACHE_OFFSET, "rt_unit_slot");
302
+ const cached = ctx.tempName("unit_cached");
303
+ const hasCached = ctx.tempName("unit_cached_ok");
304
+ const hitBlock = ctx.blockName("unit_cached");
305
+ const allocBlock = ctx.blockName("unit_alloc");
306
+ const doneBlock = ctx.blockName("unit_done");
307
+
308
+ ctx.lines.push(` ${cached} = load ptr, ptr ${slot}`);
309
+ ctx.lines.push(` ${hasCached} = icmp ne ptr ${cached}, null`);
310
+ ctx.lines.push(` br i1 ${hasCached}, label %${hitBlock}, label %${allocBlock}`);
311
+
312
+ ctx.lines.push(`${hitBlock}:`);
313
+ ctx.lines.push(` br label %${doneBlock}`);
314
+
315
+ ctx.lines.push(`${allocBlock}:`);
316
+ const allocated = lowerRawAlloc(ctx, K_PRODUCT_HEADER_SIZE, K_PRODUCT_HEADER_SIZE);
317
+ storeI32At(ctx, allocated, K_VALUE_KIND_OFFSET, K_VALUE_KIND_PRODUCT);
318
+ storePtrAt(ctx, allocated, K_VALUE_RT_OFFSET, "%rt");
319
+ storeI64At(ctx, allocated, K_VALUE_PRODUCT_COUNT_OFFSET, 0);
320
+ storeI64At(ctx, allocated, K_VALUE_PRODUCT_CAPACITY_OFFSET, 0);
321
+ storePtrAt(ctx, allocated, K_VALUE_PRODUCT_FIELDS_OFFSET, "null");
322
+ ctx.lines.push(` store ptr ${allocated}, ptr ${slot}`);
323
+ const allocatedBlock = ctx.currentBlock;
324
+ ctx.lines.push(` br label %${doneBlock}`);
325
+
326
+ ctx.lines.push(`${doneBlock}:`);
327
+ const unit = ctx.tempName("unit");
328
+ ctx.lines.push(` ${unit} = phi ptr [${cached}, %${hitBlock}], [${allocated}, %${allocatedBlock}]`);
329
+ ctx.currentBlock = doneBlock;
330
+ return unit;
331
+ }
332
+
333
+ function lowerBit(ctx, tag) {
334
+ if (ctx.runtimeMode === "compact") {
335
+ const bit = ctx.tempName("bit");
336
+ ctx.lines.push(` ${bit} = call ptr @k_bit${tag}(ptr %rt)`);
337
+ return bit;
338
+ }
339
+
340
+ const cacheOffset = tag === "0" ? K_RT_BIT0_CACHE_OFFSET : K_RT_BIT1_CACHE_OFFSET;
341
+ const slot = bytePtr(ctx, "%rt", cacheOffset, `rt_bit${tag}_slot`);
342
+ const cached = ctx.tempName("bit_cached");
343
+ const hasCached = ctx.tempName("bit_cached_ok");
344
+ const hitBlock = ctx.blockName("bit_cached");
345
+ const allocBlock = ctx.blockName("bit_alloc");
346
+ const doneBlock = ctx.blockName("bit_done");
347
+
348
+ ctx.lines.push(` ${cached} = load ptr, ptr ${slot}`);
349
+ ctx.lines.push(` ${hasCached} = icmp ne ptr ${cached}, null`);
350
+ ctx.lines.push(` br i1 ${hasCached}, label %${hitBlock}, label %${allocBlock}`);
351
+
352
+ ctx.lines.push(`${hitBlock}:`);
353
+ ctx.lines.push(` br label %${doneBlock}`);
354
+
355
+ ctx.lines.push(`${allocBlock}:`);
356
+ const unit = lowerUnit(ctx);
357
+ nullCheck(ctx, unit);
358
+ const allocated = lowerRawVariant(ctx, tag, unit);
359
+ ctx.lines.push(` store ptr ${allocated}, ptr ${slot}`);
360
+ const allocatedBlock = ctx.currentBlock;
361
+ ctx.lines.push(` br label %${doneBlock}`);
362
+
363
+ ctx.lines.push(`${doneBlock}:`);
364
+ const bit = ctx.tempName("bit");
365
+ ctx.lines.push(` ${bit} = phi ptr [${cached}, %${hitBlock}], [${allocated}, %${allocatedBlock}]`);
366
+ ctx.currentBlock = doneBlock;
367
+ return bit;
368
+ }
369
+
370
+ function lowerRtMark(ctx) {
371
+ const block = loadPtrAt(ctx, "%rt", K_RT_BLOCKS_OFFSET, "mark_block");
372
+ const hasBlock = ctx.tempName("mark_has_block");
373
+ const loadBlock = ctx.blockName("mark_load");
374
+ const emptyBlock = ctx.blockName("mark_empty");
375
+ const doneBlock = ctx.blockName("mark_done");
376
+ ctx.lines.push(` ${hasBlock} = icmp ne ptr ${block}, null`);
377
+ ctx.lines.push(` br i1 ${hasBlock}, label %${loadBlock}, label %${emptyBlock}`);
378
+
379
+ ctx.lines.push(`${loadBlock}:`);
380
+ const usedValue = loadI64At(ctx, block, K_ARENA_BLOCK_USED_OFFSET, "mark_used_value");
381
+ ctx.lines.push(` br label %${doneBlock}`);
382
+
383
+ ctx.lines.push(`${emptyBlock}:`);
384
+ ctx.lines.push(` br label %${doneBlock}`);
385
+
386
+ ctx.lines.push(`${doneBlock}:`);
387
+ const used = ctx.tempName("mark_used");
388
+ const partial = ctx.tempName("mark");
389
+ const aggregate = ctx.tempName("mark");
390
+ ctx.lines.push(` ${used} = phi i64 [${usedValue}, %${loadBlock}], [0, %${emptyBlock}]`);
391
+ ctx.lines.push(` ${partial} = insertvalue %k_rt_mark undef, ptr ${block}, 0`);
392
+ ctx.lines.push(` ${aggregate} = insertvalue %k_rt_mark ${partial}, i64 ${used}, 1`);
393
+ ctx.currentBlock = doneBlock;
394
+ return { aggregate, block, used };
395
+ }
396
+
397
+ function lowerRtRewind(ctx, mark) {
398
+ const currentBlock = loadPtrAt(ctx, "%rt", K_RT_BLOCKS_OFFSET, "rewind_block");
399
+ const hasMark = ctx.tempName("rewind_has_mark");
400
+ const sameBlock = ctx.tempName("rewind_same_block");
401
+ const fast = ctx.tempName("rewind_fast_ok");
402
+ const fastBlock = ctx.blockName("rewind_fast");
403
+ const slowBlock = ctx.blockName("rewind_slow");
404
+ const doneBlock = ctx.blockName("rewind_done");
405
+ ctx.lines.push(` ${hasMark} = icmp ne ptr ${mark.block}, null`);
406
+ ctx.lines.push(` ${sameBlock} = icmp eq ptr ${currentBlock}, ${mark.block}`);
407
+ ctx.lines.push(` ${fast} = and i1 ${hasMark}, ${sameBlock}`);
408
+ ctx.lines.push(` br i1 ${fast}, label %${fastBlock}, label %${slowBlock}`);
409
+
410
+ ctx.lines.push(`${fastBlock}:`);
411
+ storeI64At(ctx, currentBlock, K_ARENA_BLOCK_USED_OFFSET, mark.used);
412
+ storePtrAt(ctx, "%rt", K_RT_UNIT_CACHE_OFFSET, "null");
413
+ storePtrAt(ctx, "%rt", K_RT_BIT0_CACHE_OFFSET, "null");
414
+ storePtrAt(ctx, "%rt", K_RT_BIT1_CACHE_OFFSET, "null");
415
+ storeI32At(ctx, "%rt", K_RT_HAS_REUSABLE_BLOCKS_OFFSET, 0);
416
+ ctx.lines.push(` br label %${doneBlock}`);
417
+
418
+ ctx.lines.push(`${slowBlock}:`);
419
+ ctx.lines.push(` call void @k_rt_rewind(ptr %rt, %k_rt_mark ${mark.aggregate})`);
420
+ ctx.lines.push(` br label %${doneBlock}`);
421
+
422
+ ctx.lines.push(`${doneBlock}:`);
423
+ ctx.currentBlock = doneBlock;
424
+ }
425
+
426
+ function statusCheck(ctx, callResult) {
427
+ const status = ctx.tempName("status");
428
+ const failed = ctx.tempName("failed");
429
+ const okBlock = ctx.blockName("ok");
430
+ const failedBlock = ctx.blockName("failed");
431
+ ctx.lines.push(` ${status} = extractvalue %k_result ${callResult}, 0`);
432
+ ctx.lines.push(` ${failed} = icmp ne i32 ${status}, 0`);
433
+ ctx.lines.push(` br i1 ${failed}, label %${failedBlock}, label %${okBlock}`);
434
+ ctx.lines.push(`${failedBlock}:`);
435
+ if (ctx.catchTail) {
436
+ const isTail = ctx.tempName("tail_status");
437
+ const tailBlock = ctx.blockName("tail");
438
+ const returnBlock = ctx.blockName("return_failed");
439
+ ctx.lines.push(` ${isTail} = icmp eq i32 ${status}, 2`);
440
+ ctx.lines.push(` br i1 ${isTail}, label %${tailBlock}, label %${returnBlock}`);
441
+ ctx.lines.push(`${tailBlock}:`);
442
+ const tailInput = ctx.tempName("tail_input");
443
+ ctx.lines.push(` ${tailInput} = extractvalue %k_result ${callResult}, 1`);
444
+ ctx.lines.push(` store ptr ${tailInput}, ptr ${ctx.tailInputSlot}`);
445
+ ctx.lines.push(` br label %${ctx.tailLoopBlock}`);
446
+ ctx.lines.push(`${returnBlock}:`);
447
+ }
448
+ ctx.lines.push(` ret %k_result ${callResult}`);
449
+ ctx.lines.push(`${okBlock}:`);
450
+ }
451
+
452
+ function unionBranch(ctx, functionName, input, isLast) {
453
+ const mark = ctx.runtimeMode === "compact" ? ctx.tempName("mark") : lowerRtMark(ctx);
454
+ const callResult = ctx.tempName("union");
455
+ const status = ctx.tempName("status");
456
+ const failed = ctx.tempName("failed");
457
+ const successBlock = ctx.blockName("union_success");
458
+ const failedDispatchBlock = ctx.blockName("union_failed");
459
+ const tailBlock = ctx.blockName("union_tail");
460
+ const nextBlock = isLast ? null : ctx.blockName("union_next");
461
+ const failureBlock = isLast ? ctx.blockName("union_failure") : nextBlock;
462
+ if (ctx.runtimeMode === "compact") {
463
+ ctx.lines.push(` ${mark} = call %k_rt_mark @k_rt_mark(ptr %rt)`);
464
+ }
465
+ ctx.lines.push(` ${callResult} = call %k_result ${functionName}(ptr %rt, ptr ${input})`);
466
+ ctx.lines.push(` ${status} = extractvalue %k_result ${callResult}, 0`);
467
+ ctx.lines.push(` ${failed} = icmp ne i32 ${status}, 0`);
468
+ ctx.lines.push(` br i1 ${failed}, label %${failedDispatchBlock}, label %${successBlock}`);
469
+ ctx.lines.push(`${failedDispatchBlock}:`);
470
+ const isTail = ctx.tempName("tail_status");
471
+ ctx.lines.push(` ${isTail} = icmp eq i32 ${status}, 2`);
472
+ ctx.lines.push(` br i1 ${isTail}, label %${tailBlock}, label %${failureBlock}`);
473
+ ctx.lines.push(`${tailBlock}:`);
474
+ ctx.lines.push(` ret %k_result ${callResult}`);
475
+ ctx.lines.push(`${successBlock}:`);
476
+ const value = ctx.tempName("union_value");
477
+ ctx.lines.push(` ${value} = extractvalue %k_result ${callResult}, 1`);
478
+ ctx.lines.push(...result(ctx, 0, value));
479
+ if (isLast) {
480
+ ctx.lines.push(`${failureBlock}:`);
481
+ if (ctx.runtimeMode === "compact") {
482
+ ctx.lines.push(` call void @k_rt_rewind(ptr %rt, %k_rt_mark ${mark})`);
483
+ } else {
484
+ lowerRtRewind(ctx, mark);
485
+ }
486
+ ctx.lines.push(...result(ctx, 1));
487
+ } else {
488
+ ctx.lines.push(`${nextBlock}:`);
489
+ if (ctx.runtimeMode === "compact") {
490
+ ctx.lines.push(` call void @k_rt_rewind(ptr %rt, %k_rt_mark ${mark})`);
491
+ } else {
492
+ lowerRtRewind(ctx, mark);
493
+ }
494
+ }
495
+ }
496
+
497
+ function emitUnionFunctionBody(symbol, items, ctx, linkage = "", options = {}) {
498
+ if (!items?.length) {
499
+ unsupported(ctx);
500
+ } else {
501
+ items.forEach((item, index) => {
502
+ unionBranch(ctx, ctx.addSyntheticFunction(item, options.tail === true), "%input", index === items.length - 1);
503
+ });
504
+ }
505
+ const prefix = linkage ? `define ${linkage} %k_result` : "define %k_result";
506
+ return [
507
+ `${prefix} ${symbol}(ptr %rt, ptr %input) {`,
508
+ "entry:",
509
+ ...ctx.lines,
510
+ "}",
511
+ ""
512
+ ];
513
+ }
514
+
515
+ function isTailNeutral(exp) {
516
+ if (exp == null) return false;
517
+ if (exp.op === "identity" || exp.op === "code" || exp.op === "filter") return true;
518
+ return exp.op === "comp" && exp.items.every(isTailNeutral);
519
+ }
520
+
521
+ function isTailSelfRef(exp, ctx) {
522
+ return exp?.op === "ref" && exp.ref === ctx.tailRef;
523
+ }
524
+
525
+ function hasTailSelfRefSuffix(items, index, ctx) {
526
+ let seenSelfRef = false;
527
+ for (let i = index; i < items.length; i++) {
528
+ const item = items[i];
529
+ if (!seenSelfRef && isTailSelfRef(item, ctx)) {
530
+ seenSelfRef = true;
531
+ continue;
532
+ }
533
+ if (!isTailNeutral(item)) return false;
534
+ }
535
+ return seenSelfRef;
536
+ }
537
+
538
+ function lowerTailSelfRef(ctx, input) {
539
+ if (ctx.catchTail) {
540
+ ctx.lines.push(` store ptr ${input}, ptr ${ctx.tailInputSlot}`);
541
+ ctx.lines.push(` br label %${ctx.tailLoopBlock}`);
542
+ } else {
543
+ ctx.lines.push(...result(ctx, 2, input));
544
+ }
545
+ return false;
546
+ }
547
+
548
+ function lowerProductSetAt(ctx, product, edgeIndex, label, value) {
549
+ const fields = loadPtrAt(ctx, product, K_VALUE_PRODUCT_FIELDS_OFFSET, "product_fields_ptr");
550
+ const slot = bytePtr(ctx, fields, edgeIndex * K_FIELD_SIZE, "field_slot");
551
+ storePtrAt(ctx, slot, K_FIELD_LABEL_OFFSET, label.pointer);
552
+ storeI64At(ctx, slot, K_FIELD_LABEL_LENGTH_OFFSET, label.length);
553
+ storePtrAt(ctx, slot, K_FIELD_VALUE_OFFSET, value);
554
+
555
+ const countSlot = bytePtr(ctx, product, K_VALUE_PRODUCT_COUNT_OFFSET, "product_count_slot");
556
+ const count = ctx.tempName("product_count");
557
+ const needsCountUpdate = ctx.tempName("product_count_update");
558
+ const updateBlock = ctx.blockName("product_count_update");
559
+ const doneBlock = ctx.blockName("product_count_done");
560
+ ctx.lines.push(` ${count} = load i64, ptr ${countSlot}`);
561
+ ctx.lines.push(` ${needsCountUpdate} = icmp ule i64 ${count}, ${edgeIndex}`);
562
+ ctx.lines.push(` br i1 ${needsCountUpdate}, label %${updateBlock}, label %${doneBlock}`);
563
+ ctx.lines.push(`${updateBlock}:`);
564
+ ctx.lines.push(` store i64 ${edgeIndex + 1}, ptr ${countSlot}`);
565
+ ctx.lines.push(` br label %${doneBlock}`);
566
+ ctx.lines.push(`${doneBlock}:`);
567
+ ctx.currentBlock = doneBlock;
568
+ }
569
+
570
+ function lowerTailSelfProduct(ctx, exp, input) {
571
+ const outputNode = patternNode(ctx, exp.patterns?.[1]);
572
+ const edges = outputNode?.edges;
573
+ if (!Array.isArray(edges) || edges.length !== exp.fields.length) return null;
574
+
575
+ const seen = new Set();
576
+ const fields = [];
577
+ for (const field of exp.fields) {
578
+ const edgeIndex = patternEdgeIndex(ctx, exp.patterns?.[1], field.label);
579
+ if (edgeIndex < 0 || seen.has(edgeIndex)) return null;
580
+ seen.add(edgeIndex);
581
+ const child = lowerExpr(ctx, field.expr, input);
582
+ if (child === false || child == null) return child;
583
+ fields.push({ field, edgeIndex, child });
584
+ }
585
+ if (seen.size !== edges.length) return null;
586
+
587
+ for (const { field, edgeIndex, child } of fields) {
588
+ const label = ctx.labelRef(field.label);
589
+ if (ctx.runtimeMode === "compact") {
590
+ ctx.lines.push(` call void @k_product_set_at(ptr ${input}, i64 ${edgeIndex}, ptr ${label.pointer}, i64 ${label.length}, ptr ${child})`);
591
+ } else {
592
+ lowerProductSetAt(ctx, input, edgeIndex, label, child);
593
+ }
594
+ }
595
+ return lowerTailSelfRef(ctx, input);
596
+ }
597
+
598
+ function lowerUnitVariantConstant(ctx, tag) {
599
+ if (tag === "0" || tag === "1") {
600
+ const bit = lowerBit(ctx, tag);
601
+ nullCheck(ctx, bit);
602
+ return bit;
603
+ }
604
+ if (ctx.runtimeMode === "compact") {
605
+ const label = ctx.labelRef(tag);
606
+ const variant = ctx.tempName("variant");
607
+ ctx.lines.push(` ${variant} = call ptr @k_variant_unit_borrowed_n(ptr %rt, ptr ${label.pointer}, i64 ${label.length})`);
608
+ nullCheck(ctx, variant);
609
+ return variant;
610
+ }
611
+ const unit = lowerUnit(ctx);
612
+ nullCheck(ctx, unit);
613
+ return lowerRawVariant(ctx, tag, unit);
614
+ }
615
+
616
+ function lowerRawVariant(ctx, tag, payload) {
617
+ const label = ctx.labelRef(tag);
618
+ if (ctx.runtimeMode === "compact") {
619
+ const variant = ctx.tempName("variant");
620
+ ctx.lines.push(` ${variant} = call ptr @k_variant_borrowed_direct_n(ptr %rt, ptr ${label.pointer}, i64 ${label.length}, ptr ${payload})`);
621
+ nullCheck(ctx, variant);
622
+ return variant;
623
+ }
624
+ const variant = lowerRawAlloc(ctx, K_VALUE_SIZE, K_VALUE_ALLOC_SIZE);
625
+ nullCheck(ctx, variant);
626
+ storeI32At(ctx, variant, K_VALUE_KIND_OFFSET, K_VALUE_KIND_VARIANT);
627
+ storePtrAt(ctx, variant, K_VALUE_RT_OFFSET, "%rt");
628
+ storePtrAt(ctx, variant, K_VALUE_VARIANT_TAG_OFFSET, label.pointer);
629
+ storeI64At(ctx, variant, K_VALUE_VARIANT_TAG_LENGTH_OFFSET, label.length);
630
+ storePtrAt(ctx, variant, K_VALUE_VARIANT_PAYLOAD_OFFSET, payload);
631
+ return variant;
632
+ }
633
+
634
+ function lowerProductGetAt(ctx, input, edgeIndex) {
635
+ const nonNullBlock = ctx.blockName("product_non_null");
636
+ const productBlock = ctx.blockName("product_kind");
637
+ const fieldBlock = ctx.blockName("product_field");
638
+ const missingBlock = ctx.blockName("product_missing");
639
+ const doneBlock = ctx.blockName("product_done");
640
+ const isMissing = ctx.tempName("product_is_missing");
641
+ ctx.lines.push(` ${isMissing} = icmp eq ptr ${input}, null`);
642
+ ctx.lines.push(` br i1 ${isMissing}, label %${missingBlock}, label %${nonNullBlock}`);
643
+
644
+ ctx.lines.push(`${nonNullBlock}:`);
645
+ const kind = loadI32At(ctx, input, K_VALUE_KIND_OFFSET, "product_kind");
646
+ const isProduct = ctx.tempName("product_kind_ok");
647
+ ctx.lines.push(` ${isProduct} = icmp eq i32 ${kind}, ${K_VALUE_KIND_PRODUCT}`);
648
+ ctx.lines.push(` br i1 ${isProduct}, label %${productBlock}, label %${missingBlock}`);
649
+
650
+ ctx.lines.push(`${productBlock}:`);
651
+ const count = loadI64At(ctx, input, K_VALUE_PRODUCT_COUNT_OFFSET, "product_count");
652
+ const inRange = ctx.tempName("product_index_ok");
653
+ ctx.lines.push(` ${inRange} = icmp ugt i64 ${count}, ${edgeIndex}`);
654
+ ctx.lines.push(` br i1 ${inRange}, label %${fieldBlock}, label %${missingBlock}`);
655
+
656
+ ctx.lines.push(`${fieldBlock}:`);
657
+ const fields = loadPtrAt(ctx, input, K_VALUE_PRODUCT_FIELDS_OFFSET, "product_fields_ptr");
658
+ const slot = bytePtr(ctx, fields, edgeIndex * K_FIELD_SIZE, "field_slot");
659
+ const value = loadPtrAt(ctx, slot, K_FIELD_VALUE_OFFSET, "field");
660
+ ctx.lines.push(` br label %${doneBlock}`);
661
+
662
+ ctx.lines.push(`${missingBlock}:`);
663
+ ctx.lines.push(` br label %${doneBlock}`);
664
+
665
+ ctx.lines.push(`${doneBlock}:`);
666
+ const resultValue = ctx.tempName("field_value");
667
+ ctx.lines.push(` ${resultValue} = phi ptr [${value}, %${fieldBlock}], [null, %${missingBlock}]`);
668
+ ctx.currentBlock = doneBlock;
669
+ return resultValue;
670
+ }
671
+
672
+ function lowerVariantMatchBranch(ctx, input, label, matchBlock, mismatchBlock) {
673
+ const nonNullBlock = ctx.blockName("tag_non_null");
674
+ const variantBlock = ctx.blockName("tag_variant");
675
+ const lengthBlock = ctx.blockName("tag_length");
676
+ const isMissing = ctx.tempName("tag_is_missing");
677
+ ctx.lines.push(` ${isMissing} = icmp eq ptr ${input}, null`);
678
+ ctx.lines.push(` br i1 ${isMissing}, label %${mismatchBlock}, label %${nonNullBlock}`);
679
+
680
+ ctx.lines.push(`${nonNullBlock}:`);
681
+ const kind = loadI32At(ctx, input, K_VALUE_KIND_OFFSET, "tag_kind");
682
+ const isVariant = ctx.tempName("tag_kind_ok");
683
+ ctx.lines.push(` ${isVariant} = icmp eq i32 ${kind}, ${K_VALUE_KIND_VARIANT}`);
684
+ ctx.lines.push(` br i1 ${isVariant}, label %${variantBlock}, label %${mismatchBlock}`);
685
+
686
+ ctx.lines.push(`${variantBlock}:`);
687
+ const tagLength = loadI64At(ctx, input, K_VALUE_VARIANT_TAG_LENGTH_OFFSET, "tag_length");
688
+ const lengthMatches = ctx.tempName("tag_length_ok");
689
+ ctx.lines.push(` ${lengthMatches} = icmp eq i64 ${tagLength}, ${label.length}`);
690
+
691
+ const bytes = [...Buffer.from(label.text, "utf8")];
692
+ if (bytes.length === 0) {
693
+ ctx.lines.push(` br i1 ${lengthMatches}, label %${matchBlock}, label %${mismatchBlock}`);
694
+ return;
695
+ }
696
+
697
+ ctx.lines.push(` br i1 ${lengthMatches}, label %${lengthBlock}, label %${mismatchBlock}`);
698
+ ctx.lines.push(`${lengthBlock}:`);
699
+ const tagPointer = loadPtrAt(ctx, input, K_VALUE_VARIANT_TAG_OFFSET, "tag_ptr");
700
+
701
+ for (let index = 0; index < bytes.length; index++) {
702
+ const tagBytePointer = bytePtr(ctx, tagPointer, index, "tag_byte_ptr");
703
+ const tagByte = ctx.tempName("tag_byte");
704
+ const byteMatches = ctx.tempName("tag_byte_ok");
705
+ const nextBlock = index === bytes.length - 1 ? matchBlock : ctx.blockName("tag_byte");
706
+ ctx.lines.push(` ${tagByte} = load i8, ptr ${tagBytePointer}`);
707
+ ctx.lines.push(` ${byteMatches} = icmp eq i8 ${tagByte}, ${bytes[index]}`);
708
+ ctx.lines.push(` br i1 ${byteMatches}, label %${nextBlock}, label %${mismatchBlock}`);
709
+ if (index !== bytes.length - 1) {
710
+ ctx.lines.push(`${nextBlock}:`);
711
+ }
712
+ }
713
+ }
714
+
715
+ function lowerVariantPayload(ctx, input) {
716
+ return loadPtrAt(ctx, input, K_VALUE_VARIANT_PAYLOAD_OFFSET, "payload");
717
+ }
718
+
719
+ function rawProductPlan(ctx, exp) {
720
+ const capacity = exp.fields.length;
721
+ const slots = new Array(capacity);
722
+ const seen = new Set();
723
+ for (let fieldIndex = 0; fieldIndex < exp.fields.length; fieldIndex++) {
724
+ const field = exp.fields[fieldIndex];
725
+ const edgeIndex = patternEdgeIndex(ctx, exp.patterns?.[1], field.label);
726
+ if (edgeIndex < 0 || edgeIndex >= capacity || seen.has(edgeIndex)) return null;
727
+ seen.add(edgeIndex);
728
+ slots[edgeIndex] = fieldIndex;
729
+ }
730
+ if (seen.size !== capacity || slots.some((slot) => slot == null)) return null;
731
+ return { capacity, slots };
732
+ }
733
+
734
+ function lowerRawProduct(ctx, exp, input, plan) {
735
+ const loweredFields = [];
736
+ for (const field of exp.fields) {
737
+ const child = lowerExpr(ctx, field.expr, input);
738
+ if (child === false || child == null) return child;
739
+ loweredFields.push({
740
+ child,
741
+ label: ctx.labelRef(field.label)
742
+ });
743
+ }
744
+
745
+ const requestedSize = K_PRODUCT_HEADER_SIZE + (K_FIELD_SIZE * plan.capacity);
746
+ const product = lowerRawAlloc(ctx, requestedSize, alignArenaSize(requestedSize));
747
+ nullCheck(ctx, product);
748
+ const fields = bytePtr(ctx, product, K_PRODUCT_HEADER_SIZE, "product_fields");
749
+ storeI32At(ctx, product, K_VALUE_KIND_OFFSET, K_VALUE_KIND_PRODUCT);
750
+ storePtrAt(ctx, product, K_VALUE_RT_OFFSET, "%rt");
751
+ storeI64At(ctx, product, K_VALUE_PRODUCT_COUNT_OFFSET, plan.capacity);
752
+ storeI64At(ctx, product, K_VALUE_PRODUCT_CAPACITY_OFFSET, plan.capacity);
753
+ storePtrAt(ctx, product, K_VALUE_PRODUCT_FIELDS_OFFSET, fields);
754
+
755
+ for (let slotIndex = 0; slotIndex < plan.slots.length; slotIndex++) {
756
+ const lowered = loweredFields[plan.slots[slotIndex]];
757
+ const slot = bytePtr(ctx, fields, slotIndex * K_FIELD_SIZE, "field_slot");
758
+ storePtrAt(ctx, slot, K_FIELD_LABEL_OFFSET, lowered.label.pointer);
759
+ storeI64At(ctx, slot, K_FIELD_LABEL_LENGTH_OFFSET, lowered.label.length);
760
+ storePtrAt(ctx, slot, K_FIELD_VALUE_OFFSET, lowered.child);
761
+ }
762
+
763
+ return product;
764
+ }
765
+
766
+ function lowerRuntimeProduct(ctx, exp, input) {
767
+ const product = ctx.tempName("product");
768
+ ctx.lines.push(` ${product} = call ptr @k_product(ptr %rt, i64 ${exp.fields.length})`);
769
+ for (const field of exp.fields) {
770
+ const child = lowerExpr(ctx, field.expr, input);
771
+ if (child == null) return null;
772
+ const label = ctx.labelRef(field.label);
773
+ const edgeIndex = patternEdgeIndex(ctx, exp.patterns?.[1], field.label);
774
+ if (edgeIndex >= 0) {
775
+ ctx.lines.push(` call void @k_product_set_at(ptr ${product}, i64 ${edgeIndex}, ptr ${label.pointer}, i64 ${label.length}, ptr ${child})`);
776
+ } else {
777
+ ctx.lines.push(` call void @k_product_set_borrowed_n(ptr ${product}, ptr ${label.pointer}, i64 ${label.length}, ptr ${child})`);
778
+ }
779
+ }
780
+ return product;
781
+ }
782
+
783
+ function lowerExpr(ctx, exp, input = "%input", options = {}) {
784
+ switch (exp?.op) {
785
+ case "empty":
786
+ return null;
787
+ case "identity":
788
+ case "code":
789
+ case "filter":
790
+ return input;
791
+ case "ref": {
792
+ const functionName = ctx.functionNames.get(exp.ref);
793
+ if (!functionName) {
794
+ if (isIntrinsic(exp.ref)) throw unsupportedIntrinsic("LLVM compiler", exp.ref);
795
+ return null;
796
+ }
797
+ if (options.tail === true && exp.ref === ctx.tailRef) {
798
+ return lowerTailSelfRef(ctx, input);
799
+ }
800
+ const callResult = ctx.tempName("call");
801
+ ctx.lines.push(` ${callResult} = call %k_result ${functionName}(ptr %rt, ptr ${input})`);
802
+ statusCheck(ctx, callResult);
803
+ const value = ctx.tempName("ref");
804
+ ctx.lines.push(` ${value} = extractvalue %k_result ${callResult}, 1`);
805
+ return value;
806
+ }
807
+ case "dot": {
808
+ const edgeIndex = patternEdgeIndex(ctx, exp.patterns?.[0], exp.label);
809
+ let value;
810
+ if (edgeIndex >= 0 && ctx.runtimeMode !== "compact") {
811
+ value = lowerProductGetAt(ctx, input, edgeIndex);
812
+ } else {
813
+ const label = ctx.labelRef(exp.label);
814
+ value = ctx.tempName("field");
815
+ if (edgeIndex >= 0) {
816
+ ctx.lines.push(` ${value} = call ptr @k_product_get_at(ptr ${input}, i64 ${edgeIndex})`);
817
+ } else {
818
+ ctx.lines.push(` ${value} = call ptr @k_product_get_n(ptr ${input}, ptr ${label.pointer}, i64 ${label.length})`);
819
+ }
820
+ }
821
+ nullCheck(ctx, value);
822
+ return value;
823
+ }
824
+ case "div": {
825
+ const label = ctx.labelRef(exp.tag);
826
+ const matchBlock = ctx.blockName("tag_match");
827
+ const mismatchBlock = ctx.blockName("tag_mismatch");
828
+ if (ctx.runtimeMode === "compact") {
829
+ const matches = ctx.tempName("tagmatch");
830
+ const compare = ctx.tempName("tagcmp");
831
+ ctx.lines.push(` ${compare} = call i32 @k_variant_tag_matches(ptr ${input}, ptr ${label.pointer}, i64 ${label.length})`);
832
+ ctx.lines.push(` ${matches} = icmp ne i32 ${compare}, 0`);
833
+ ctx.lines.push(` br i1 ${matches}, label %${matchBlock}, label %${mismatchBlock}`);
834
+ } else {
835
+ lowerVariantMatchBranch(ctx, input, label, matchBlock, mismatchBlock);
836
+ }
837
+ ctx.lines.push(`${mismatchBlock}:`);
838
+ ctx.lines.push(...result(ctx, 1));
839
+ ctx.lines.push(`${matchBlock}:`);
840
+ let payload;
841
+ if (ctx.runtimeMode === "compact") {
842
+ payload = ctx.tempName("payload");
843
+ ctx.lines.push(` ${payload} = call ptr @k_variant_payload(ptr ${input})`);
844
+ } else {
845
+ payload = lowerVariantPayload(ctx, input);
846
+ }
847
+ nullCheck(ctx, payload);
848
+ return payload;
849
+ }
850
+ case "vid": {
851
+ return lowerRawVariant(ctx, exp.tag, input);
852
+ }
853
+ case "comp": {
854
+ let current = input;
855
+ for (let index = 0; index < exp.items.length; index++) {
856
+ const item = exp.items[index];
857
+ const next = exp.items[index + 1];
858
+ if (item?.op === "product" && item.fields.length === 0 && next?.op === "vid") {
859
+ current = lowerUnitVariantConstant(ctx, next.tag);
860
+ index++;
861
+ continue;
862
+ }
863
+ if (options.tail === true && item?.op === "product" && hasTailSelfRefSuffix(exp.items, index + 1, ctx)) {
864
+ const tailProduct = lowerTailSelfProduct(ctx, item, current);
865
+ if (tailProduct !== null) return tailProduct;
866
+ }
867
+ const tail = options.tail === true && exp.items.slice(index + 1).every(isTailNeutral);
868
+ current = lowerExpr(ctx, item, current, { tail });
869
+ if (current === false) return false;
870
+ if (current == null) return null;
871
+ }
872
+ return current;
873
+ }
874
+ case "product": {
875
+ if (exp.fields.length === 0) {
876
+ const unit = lowerUnit(ctx);
877
+ nullCheck(ctx, unit);
878
+ return unit;
879
+ }
880
+ if (ctx.runtimeMode === "compact") return lowerRuntimeProduct(ctx, exp, input);
881
+ const plan = rawProductPlan(ctx, exp);
882
+ if (plan != null) return lowerRawProduct(ctx, exp, input, plan);
883
+ return lowerRuntimeProduct(ctx, exp, input);
884
+ }
885
+ case "union": {
886
+ if (!exp.items?.length) return null;
887
+ const functionName = ctx.addUnionFunction(exp.items, options.tail === true);
888
+ const callResult = ctx.tempName("union");
889
+ ctx.lines.push(` ${callResult} = call %k_result ${functionName}(ptr %rt, ptr ${input})`);
890
+ statusCheck(ctx, callResult);
891
+ const value = ctx.tempName("union_value");
892
+ ctx.lines.push(` ${value} = extractvalue %k_result ${callResult}, 1`);
893
+ return value;
894
+ }
895
+ default:
896
+ return null;
897
+ }
898
+ }
899
+
900
+ function entryRelation(kir) {
901
+ const entry = kir?.rels?.[kir.main];
902
+ if (!entry) throw new Error(`KIR main relation '${kir?.main}' is missing`);
903
+ return entry;
904
+ }
905
+
906
+ function labelGlobals(ctx) {
907
+ return [...ctx.labels.values()].map((label) =>
908
+ `${label.name} = private unnamed_addr constant [${label.length} x i8] c"${cStringBytes(label.text)}", align 1`);
909
+ }
910
+
911
+ function emitFunctionBody(symbol, body, ctx, linkage = "", options = {}) {
912
+ const input = ctx.catchTail ? "%tail_input" : "%input";
913
+ if (ctx.catchTail) {
914
+ ctx.tailInputSlot = "%tail_input_slot";
915
+ ctx.tailLoopBlock = "tail_loop";
916
+ ctx.lines.push(` ${ctx.tailInputSlot} = alloca ptr`);
917
+ ctx.lines.push(` store ptr %input, ptr ${ctx.tailInputSlot}`);
918
+ ctx.lines.push(` br label %${ctx.tailLoopBlock}`);
919
+ ctx.lines.push(`${ctx.tailLoopBlock}:`);
920
+ ctx.lines.push(` ${input} = load ptr, ptr ${ctx.tailInputSlot}`);
921
+ }
922
+ const value = lowerExpr(ctx, body, input, { tail: options.tail !== false });
923
+ if (value === false) {
924
+ // The expression emitted complete control flow, including all returns.
925
+ } else if (value == null) {
926
+ unsupported(ctx);
927
+ } else {
928
+ ctx.lines.push(...result(ctx, 0, value));
929
+ }
930
+ const prefix = linkage ? `define ${linkage} %k_result` : "define %k_result";
931
+ return [
932
+ `${prefix} ${symbol}(ptr %rt, ptr %input) {`,
933
+ "entry:",
934
+ ...ctx.lines,
935
+ "}",
936
+ ""
937
+ ];
938
+ }
939
+
940
+ function sortedRelationEntries(kir) {
941
+ return Object.entries(kir.rels || {})
942
+ .filter(([name]) => name !== kir.main)
943
+ .sort(([a], [b]) => a.localeCompare(b));
944
+ }
945
+
946
+ function relationFunctionNames(kir) {
947
+ return new Map(sortedRelationEntries(kir).map(([name], index) => [
948
+ name,
949
+ llvmRelationFunctionName(name, index)
950
+ ]));
951
+ }
952
+
953
+ export function emitLLVMModule(input, options = {}) {
954
+ if (input?.format === "k-vm" || input?.functions) {
955
+ const entry = input.entry || options.relation || "__main__";
956
+ return compileKVMModuleToLLVM(entry, input.functions || input, options);
957
+ }
958
+ if (input && typeof input === "object" && !input.rels && !input.format) {
959
+ const values = Object.values(input);
960
+ if (values.length > 0 && values[0]?.body && Array.isArray(values[0].body)) {
961
+ const entry = options.relation || "__main__";
962
+ return compileKVMModuleToLLVM(entry, input, options);
963
+ }
964
+ }
965
+
966
+ // Legacy KIR-P path for compatibility with raw KIR-P inputs
967
+ const kir = input;
968
+ const labels = new Map();
969
+ const syntheticFunctions = [];
970
+ const functionNames = relationFunctionNames(kir);
971
+ const runtimeMode = options.runtimeMode || "fast";
972
+ const relationFunctions = sortedRelationEntries(kir).flatMap(([name, rel]) => {
973
+ const ctx = createLoweringContext(functionNames, labels, syntheticFunctions, rel.patternGraph || null, {
974
+ refName: name,
975
+ catchTail: true,
976
+ runtimeMode
977
+ });
978
+ return emitFunctionBody(functionNames.get(name), rel.body, ctx, "internal");
979
+ });
980
+ const entry = entryRelation(kir);
981
+ const mainContext = createLoweringContext(functionNames, labels, syntheticFunctions, entry.patternGraph || null, {
982
+ runtimeMode
983
+ });
984
+ const functionBody = emitFunctionBody("@k_main", entry.body, mainContext);
985
+ const relation = options.relation || kir.main || "main";
986
+ const payload = JSON.stringify({
987
+ format: ARTIFACT_FORMAT,
988
+ version: ARTIFACT_VERSION,
989
+ relation,
990
+ kir
991
+ });
992
+ const payloadBytes = Buffer.byteLength(payload, "utf8") + 1;
993
+ const symbol = llvmIdentifier(options.symbol || relation);
994
+
995
+ return [
996
+ "; k-llvm prototype artifact",
997
+ `; relation: ${relation}`,
998
+ `source_filename = "k-llvm:${symbol}"`,
999
+ "",
1000
+ `@k_llvm_metadata = private unnamed_addr constant [${payloadBytes} x i8] c"${cStringBytes(payload)}", align 1`,
1001
+ ...labelGlobals({ labels }),
1002
+ "",
1003
+ ...runtimeDeclarations(),
1004
+ "",
1005
+ ...syntheticFunctions.flat(),
1006
+ ...relationFunctions,
1007
+ ...functionBody
1008
+ ].join("\n");
1009
+ }
1010
+
1011
+ export function compileObjectToLLVM(object, options = {}) {
1012
+ const inputPattern = options.inputPattern ? readPattern(options.inputPattern) : null;
1013
+ const relation = options.relation || object.main;
1014
+
1015
+ let retypedKir = null;
1016
+ let entryInputPattern = inputPattern;
1017
+ let outputPattern = null;
1018
+
1019
+ if (inputPattern) {
1020
+ const retyped = retypeObjectRelationForBackend(object, relation, inputPattern, {
1021
+ source: options.source || "<k-llvm>"
1022
+ });
1023
+ retypedKir = retyped.kir;
1024
+ entryInputPattern = retyped.inputPattern;
1025
+ outputPattern = retyped.outputPattern;
1026
+ } else {
1027
+ retypedKir = objectToKIRP(object);
1028
+ }
1029
+
1030
+ let kvmArtifact = objectToKVMArtifact(object, relation, null, options);
1031
+ let specializedKvm = kvmArtifact;
1032
+ if (inputPattern) {
1033
+ try {
1034
+ specializedKvm = specializeKVM(kvmArtifact, inputPattern);
1035
+ } catch {
1036
+ specializedKvm = {
1037
+ ...kvmArtifact,
1038
+ functions: lowerKIRToKVM(retypedKir),
1039
+ entry: relation
1040
+ };
1041
+ }
1042
+ }
1043
+
1044
+ const entry = specializedKvm.entry || relation;
1045
+ const functions = specializedKvm.functions;
1046
+
1047
+ const llvm = compileKVMModuleToLLVM(entry, functions, {
1048
+ ...options,
1049
+ relation,
1050
+ inputPattern: entryInputPattern || specializedKvm.inputPattern,
1051
+ outputPattern: outputPattern || specializedKvm.outputPattern
1052
+ });
1053
+
1054
+ return {
1055
+ kir: retypedKir,
1056
+ kvm: specializedKvm,
1057
+ relation,
1058
+ inputPattern: entryInputPattern || specializedKvm.inputPattern,
1059
+ outputPattern: outputPattern || specializedKvm.outputPattern,
1060
+ llvm
1061
+ };
1062
+ }
1063
+
1064
+ export function compileBufferToLLVM(buffer, options = {}) {
1065
+ return compileObjectToLLVM(decodeObject(buffer), options);
1066
+ }
1067
+
1068
+ export function compileLLVMArtifactFromKVM(kvmInput, options = {}) {
1069
+ const entry = options.entry || kvmInput.entry || "__main__";
1070
+ const functions = kvmInput.functions || kvmInput;
1071
+ return compileKVMModuleToLLVM(entry, functions, options);
1072
+ }
1073
+
1074
+ export function compileLLVMArtifactFromObject(object, options = {}) {
1075
+ return compileObjectToLLVM(object, options);
1076
+ }
1077
+
1078
+ export {
1079
+ ARTIFACT_FORMAT,
1080
+ ARTIFACT_VERSION
1081
+ };
1082
+
1083
+ export default {
1084
+ ARTIFACT_FORMAT,
1085
+ ARTIFACT_VERSION,
1086
+ compileBufferToLLVM,
1087
+ compileObjectToLLVM,
1088
+ compileLLVMArtifactFromKVM,
1089
+ compileLLVMArtifactFromObject,
1090
+ emitLLVMModule,
1091
+ llvmIdentifier
1092
+ };