@fraczak/k 6.5.0 → 6.7.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.
@@ -53,6 +53,7 @@ export function runtimeDeclarations() {
53
53
  "",
54
54
  "declare %k_rt_mark @k_rt_mark(ptr)",
55
55
  "declare void @k_rt_rewind(ptr, %k_rt_mark)",
56
+ "declare ptr @k_rt_compact(ptr, ptr, ptr)",
56
57
  "declare ptr @k_rt_alloc(ptr, i64)",
57
58
  "declare ptr @k_unit(ptr)",
58
59
  "declare ptr @k_bit0(ptr)",
@@ -146,10 +147,11 @@ function lowerRawVariant(ctx, tag, payload) {
146
147
  return variant;
147
148
  }
148
149
 
149
- function lowerProductGetAt(ctx, input, edgeIndex) {
150
+ function lowerProductGetAt(ctx, input, edgeIndex, label = null) {
150
151
  const nonNullBlock = ctx.blockName("product_non_null");
151
152
  const productBlock = ctx.blockName("product_kind");
152
153
  const fieldBlock = ctx.blockName("product_field");
154
+ const fallbackBlock = ctx.blockName("product_fallback");
153
155
  const missingBlock = ctx.blockName("product_missing");
154
156
  const doneBlock = ctx.blockName("product_done");
155
157
  const isMissing = ctx.tempName("product_is_missing");
@@ -172,16 +174,38 @@ function lowerProductGetAt(ctx, input, edgeIndex) {
172
174
  const fields = loadPtrAt(ctx, input, K_VALUE_PRODUCT_FIELDS_OFFSET, "product_fields_ptr");
173
175
  const slot = bytePtr(ctx, fields, edgeIndex * K_FIELD_SIZE, "field_slot");
174
176
  const value = loadPtrAt(ctx, slot, K_FIELD_VALUE_OFFSET, "field");
175
- ctx.lines.push(` br label %${doneBlock}`);
176
177
 
177
- ctx.lines.push(`${missingBlock}:`);
178
- ctx.lines.push(` br label %${doneBlock}`);
178
+ if (label) {
179
+ const fLabel = loadPtrAt(ctx, slot, K_FIELD_LABEL_OFFSET, "field_label");
180
+ const labelMatch = ctx.tempName("label_match");
181
+ ctx.lines.push(` ${labelMatch} = icmp eq ptr ${fLabel}, ${label.pointer}`);
182
+ ctx.lines.push(` br i1 ${labelMatch}, label %${doneBlock}, label %${fallbackBlock}`);
183
+
184
+ ctx.lines.push(`${fallbackBlock}:`);
185
+ const fallbackVal = ctx.tempName("fallback_field");
186
+ ctx.lines.push(` ${fallbackVal} = call ptr @k_product_get_n(ptr ${input}, ptr ${label.pointer}, i64 ${label.length})`);
187
+ ctx.lines.push(` br label %${doneBlock}`);
188
+
189
+ ctx.lines.push(`${missingBlock}:`);
190
+ ctx.lines.push(` br label %${doneBlock}`);
191
+
192
+ ctx.lines.push(`${doneBlock}:`);
193
+ const resultValue = ctx.tempName("field_value");
194
+ ctx.lines.push(` ${resultValue} = phi ptr [${value}, %${fieldBlock}], [${fallbackVal}, %${fallbackBlock}], [null, %${missingBlock}]`);
195
+ ctx.currentBlock = doneBlock;
196
+ return resultValue;
197
+ } else {
198
+ ctx.lines.push(` br label %${doneBlock}`);
179
199
 
180
- ctx.lines.push(`${doneBlock}:`);
181
- const resultValue = ctx.tempName("field_value");
182
- ctx.lines.push(` ${resultValue} = phi ptr [${value}, %${fieldBlock}], [null, %${missingBlock}]`);
183
- ctx.currentBlock = doneBlock;
184
- return resultValue;
200
+ ctx.lines.push(`${missingBlock}:`);
201
+ ctx.lines.push(` br label %${doneBlock}`);
202
+
203
+ ctx.lines.push(`${doneBlock}:`);
204
+ const resultValue = ctx.tempName("field_value");
205
+ ctx.lines.push(` ${resultValue} = phi ptr [${value}, %${fieldBlock}], [null, %${missingBlock}]`);
206
+ ctx.currentBlock = doneBlock;
207
+ return resultValue;
208
+ }
185
209
  }
186
210
 
187
211
  function lowerVariantMatchBranch(ctx, input, label, matchBlock, mismatchBlock) {
@@ -287,6 +311,56 @@ function lowerRtRewind(ctx, mark) {
287
311
  ctx.currentBlock = doneBlock;
288
312
  }
289
313
 
314
+ function lowerRtCompact(ctx, val, tailMarkSlot = "%tail_mark_slot") {
315
+ const curBlock = loadPtrAt(ctx, "%rt", K_RT_BLOCKS_OFFSET, "cur_block");
316
+ const hasBlock = ctx.tempName("has_block");
317
+ const markBlockPtr = ctx.tempName("mark_block_ptr");
318
+ ctx.lines.push(` ${hasBlock} = icmp ne ptr ${curBlock}, null`);
319
+ ctx.lines.push(` ${markBlockPtr} = getelementptr inbounds %k_rt_mark, ptr ${tailMarkSlot}, i32 0, i32 0`);
320
+ const markBlock = ctx.tempName("mark_block");
321
+ ctx.lines.push(` ${markBlock} = load ptr, ptr ${markBlockPtr}`);
322
+
323
+ const diffBlock = ctx.tempName("diff_block");
324
+ ctx.lines.push(` ${diffBlock} = icmp ne ptr ${curBlock}, ${markBlock}`);
325
+ const needCheck = ctx.tempName("need_growth_check");
326
+ ctx.lines.push(` ${needCheck} = and i1 ${hasBlock}, ${diffBlock}`);
327
+
328
+ const checkGrowthBlock = ctx.blockName("check_growth");
329
+ const doCompactBlock = ctx.blockName("do_compact");
330
+ const doneBlock = ctx.blockName("compact_done");
331
+
332
+ ctx.lines.push(` br i1 ${needCheck}, label %${doCompactBlock}, label %${checkGrowthBlock}`);
333
+
334
+ ctx.lines.push(`${checkGrowthBlock}:`);
335
+ const checkBlockHasBlock = ctx.tempName("check_has_block");
336
+ ctx.lines.push(` ${checkBlockHasBlock} = icmp ne ptr ${curBlock}, null`);
337
+ const loadGrowthBlock = ctx.blockName("load_growth");
338
+ ctx.lines.push(` br i1 ${checkBlockHasBlock}, label %${loadGrowthBlock}, label %${doneBlock}`);
339
+
340
+ ctx.lines.push(`${loadGrowthBlock}:`);
341
+ const curUsed = loadI64At(ctx, curBlock, K_ARENA_BLOCK_USED_OFFSET, "cur_used");
342
+ const markUsedPtr = ctx.tempName("mark_used_ptr");
343
+ ctx.lines.push(` ${markUsedPtr} = getelementptr inbounds %k_rt_mark, ptr ${tailMarkSlot}, i32 0, i32 1`);
344
+ const markUsed = ctx.tempName("mark_used");
345
+ ctx.lines.push(` ${markUsed} = load i64, ptr ${markUsedPtr}`);
346
+ const growth = ctx.tempName("arena_growth");
347
+ ctx.lines.push(` ${growth} = sub i64 ${curUsed}, ${markUsed}`);
348
+ const threshExceeded = ctx.tempName("thresh_exceeded");
349
+ ctx.lines.push(` ${threshExceeded} = icmp ugt i64 ${growth}, 4194304`);
350
+ ctx.lines.push(` br i1 ${threshExceeded}, label %${doCompactBlock}, label %${doneBlock}`);
351
+
352
+ ctx.lines.push(`${doCompactBlock}:`);
353
+ const compacted = ctx.tempName("compacted");
354
+ ctx.lines.push(` ${compacted} = call ptr @k_rt_compact(ptr %rt, ptr ${val}, ptr ${tailMarkSlot})`);
355
+ ctx.lines.push(` br label %${doneBlock}`);
356
+
357
+ ctx.lines.push(`${doneBlock}:`);
358
+ const finalVal = ctx.tempName("compact_res");
359
+ ctx.lines.push(` ${finalVal} = phi ptr [${val}, %${checkGrowthBlock}], [${val}, %${loadGrowthBlock}], [${compacted}, %${doCompactBlock}]`);
360
+ ctx.currentBlock = doneBlock;
361
+ return finalVal;
362
+ }
363
+
290
364
  function nullCheck(ctx, val, failTarget) {
291
365
  const isMissing = ctx.tempName("missing");
292
366
  const okBlock = ctx.blockName("nonnull");
@@ -322,7 +396,8 @@ function statusCheck(ctx, callResult, failTarget = "func_fail") {
322
396
  if (ctx.catchTail) {
323
397
  const tailInput = ctx.tempName("tail_arg");
324
398
  ctx.lines.push(` ${tailInput} = extractvalue %k_result ${callResult}, 1`);
325
- ctx.lines.push(` store ptr ${tailInput}, ptr %tail_input_slot`);
399
+ const compacted = lowerRtCompact(ctx, tailInput, "%tail_mark_slot");
400
+ ctx.lines.push(` store ptr ${compacted}, ptr %tail_input_slot`);
326
401
  ctx.lines.push(" br label %tail_loop");
327
402
  } else {
328
403
  ctx.lines.push(` ret %k_result ${callResult}`);
@@ -366,8 +441,7 @@ function hasSelfTailCall(insts, funcName, isTail = true) {
366
441
  if (inst.branches) {
367
442
  const unionIsTail = isTail && tailValueAfter(insts, i + 1, inst.dest);
368
443
  for (let bIdx = 0; bIdx < inst.branches.length; bIdx++) {
369
- const isLast = (bIdx === inst.branches.length - 1);
370
- if (hasSelfTailCall(inst.branches[bIdx].body, funcName, unionIsTail && isLast)) return true;
444
+ if (hasSelfTailCall(inst.branches[bIdx].body, funcName, unionIsTail)) return true;
371
445
  }
372
446
  }
373
447
  }
@@ -384,13 +458,6 @@ function getFieldEdgeIndex(inst, regPatterns, kvmFunc) {
384
458
  if (idx >= 0) return idx;
385
459
  }
386
460
  }
387
- if (Array.isArray(inst.pattern) && inst.pattern.length > 0) {
388
- const root = inst.pattern[0];
389
- if ((root[0] === "open-product" || root[0] === "closed-product") && Array.isArray(root[1])) {
390
- const idx = root[1].findIndex(([l]) => l === inst.label);
391
- if (idx >= 0) return idx;
392
- }
393
- }
394
461
  return -1;
395
462
  }
396
463
 
@@ -538,7 +605,11 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
538
605
 
539
606
  if (isCatchTail) {
540
607
  ctx.allocas.push(" %tail_input_slot = alloca ptr");
608
+ ctx.allocas.push(" %tail_mark_slot = alloca %k_rt_mark");
541
609
  ctx.lines.push(" store ptr %input, ptr %tail_input_slot");
610
+ const initMark = ctx.tempName("init_mark");
611
+ ctx.lines.push(` ${initMark} = call %k_rt_mark @k_rt_mark(ptr %rt)`);
612
+ ctx.lines.push(` store %k_rt_mark ${initMark}, ptr %tail_mark_slot`);
542
613
  ctx.lines.push(" br label %tail_loop");
543
614
  ctx.lines.push("tail_loop:");
544
615
  ctx.lines.push(" %tail_input = load ptr, ptr %tail_input_slot");
@@ -598,17 +669,13 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
598
669
  const input = getReg(inst.src);
599
670
  nullCheck(ctx, input, failTarget);
600
671
  const edgeIndex = getFieldEdgeIndex(inst, regPatterns, kvmFunc);
672
+ const label = ctx.labelRef(inst.label);
601
673
  let val;
602
674
  if (edgeIndex >= 0 && ctx.runtimeMode !== "compact") {
603
- val = lowerProductGetAt(ctx, input, edgeIndex);
675
+ val = lowerProductGetAt(ctx, input, edgeIndex, label);
604
676
  } else {
605
- const label = ctx.labelRef(inst.label);
606
677
  val = ctx.tempName("field");
607
- if (edgeIndex >= 0) {
608
- ctx.lines.push(` ${val} = call ptr @k_product_get_at(ptr ${input}, i64 ${edgeIndex})`);
609
- } else {
610
- ctx.lines.push(` ${val} = call ptr @k_product_get_n(ptr ${input}, ptr ${label.pointer}, i64 ${label.length})`);
611
- }
678
+ ctx.lines.push(` ${val} = call ptr @k_product_get_n(ptr ${input}, ptr ${label.pointer}, i64 ${label.length})`);
612
679
  }
613
680
  nullCheck(ctx, val, failTarget);
614
681
  regValues.set(cleanReg(inst.dest), val);
@@ -656,7 +723,8 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
656
723
  const v = getReg(inst.src);
657
724
  if (isSelfTailCall(insts, i, tailRef)) {
658
725
  if (ctx.catchTail) {
659
- ctx.lines.push(` store ptr ${v}, ptr %tail_input_slot`);
726
+ const compacted = lowerRtCompact(ctx, v, "%tail_mark_slot");
727
+ ctx.lines.push(` store ptr ${compacted}, ptr %tail_input_slot`);
660
728
  ctx.lines.push(" br label %tail_loop");
661
729
  return;
662
730
  } else {
@@ -689,6 +757,7 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
689
757
  const fieldValues = [];
690
758
  const pSrc = getReg(inst.src);
691
759
  const pPattern = regPatterns.get(cleanReg(inst.src)) || (cleanReg(inst.src) === "in" ? regPatterns.get("in") : null);
760
+ let productFailed = false;
692
761
 
693
762
  for (let bIdx = 0; bIdx < N; bIdx++) {
694
763
  const branch = inst.branches[bIdx];
@@ -715,12 +784,21 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
715
784
  nextBlock: branchDone
716
785
  });
717
786
 
787
+ if (!branchRegValues.has(cleanReg(branchRes))) {
788
+ productFailed = true;
789
+ break;
790
+ }
791
+
718
792
  ctx.lines.push(`${branchDone}:`);
719
793
  ctx.currentBlock = branchDone;
720
- const fVal = branchRegValues.get(cleanReg(branchRes)) || branchRes;
794
+ const fVal = branchRegValues.get(cleanReg(branchRes));
721
795
  fieldValues.push({ label: branch.label, val: fVal });
722
796
  }
723
797
 
798
+ if (productFailed) {
799
+ break;
800
+ }
801
+
724
802
  const requestedSize = K_PRODUCT_HEADER_SIZE + (K_FIELD_SIZE * N);
725
803
  const product = lowerRawAlloc(ctx, requestedSize, alignArenaSize(requestedSize));
726
804
  nullCheck(ctx, product, failTarget);
@@ -796,7 +874,7 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
796
874
  outputPattern: kvmFunc.outputPattern,
797
875
  body: branch.body
798
876
  };
799
- const armTailRef = (isLast && tailValueAfter(insts, i + 1, inst.dest)) ? tailRef : null;
877
+ const armTailRef = tailValueAfter(insts, i + 1, inst.dest) ? tailRef : null;
800
878
  const armBody = lowerKVMFunction(armFunc, armName, armFunc.name, moduleCtx, "internal", { ...options, tailRef: armTailRef });
801
879
  moduleCtx.syntheticFunctions.push(armBody);
802
880
 
@@ -818,7 +896,8 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
818
896
  if (ctx.catchTail) {
819
897
  const tailArg = ctx.tempName("arm_tail_arg");
820
898
  ctx.lines.push(` ${tailArg} = extractvalue %k_result ${armCall}, 1`);
821
- ctx.lines.push(` store ptr ${tailArg}, ptr %tail_input_slot`);
899
+ const compacted = lowerRtCompact(ctx, tailArg, "%tail_mark_slot");
900
+ ctx.lines.push(` store ptr ${compacted}, ptr %tail_input_slot`);
822
901
  ctx.lines.push(" br label %tail_loop");
823
902
  } else {
824
903
  ctx.lines.push(` ret %k_result ${armCall}`);
@@ -10,8 +10,7 @@ command -v clang >/dev/null || {
10
10
  }
11
11
 
12
12
  node ../../objects/compile.mjs '()' "$TMP_DIR/id.ko"
13
- printf '[["open-product",[]]]' > "$TMP_DIR/input.pattern.json"
14
- node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/input.pattern.json" "$TMP_DIR/id.ko" "$TMP_DIR/id.ll"
13
+ node ./bin/k-llvm-compile.mjs "$TMP_DIR/id.ko" "$TMP_DIR/id.ll"
15
14
  grep -q '@k_llvm_metadata' "$TMP_DIR/id.ll"
16
15
  grep -q 'define %k_result @k_main' "$TMP_DIR/id.ll"
17
16
 
@@ -19,32 +18,28 @@ clang -Wno-override-module -Iruntime runtime/krt.c tests/identity-driver.c "$TMP
19
18
  "$TMP_DIR/id"
20
19
 
21
20
  node ../../objects/compile.mjs '.x' "$TMP_DIR/projection.ko"
22
- printf '[["closed-product",[["x",1]]],["closed-product",[]]]' > "$TMP_DIR/projection.pattern.json"
23
- node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/projection.pattern.json" "$TMP_DIR/projection.ko" "$TMP_DIR/projection.ll"
21
+ node ./bin/k-llvm-compile.mjs "$TMP_DIR/projection.ko" "$TMP_DIR/projection.ll"
24
22
  grep -q 'product_fields_ptr' "$TMP_DIR/projection.ll"
25
23
  ! grep -q 'call ptr @k_product_get_at' "$TMP_DIR/projection.ll"
26
24
  clang -Wno-override-module -Iruntime runtime/krt.c tests/projection-driver.c "$TMP_DIR/projection.ll" -o "$TMP_DIR/projection"
27
25
  "$TMP_DIR/projection"
28
26
 
29
27
  node ../../objects/compile.mjs '{ .x fieldA, .y fieldB }' "$TMP_DIR/product.ko"
30
- printf '[["closed-product",[["x",1],["y",2]]],["closed-product",[["valA",1]]],["closed-product",[["valB",2]]]]' > "$TMP_DIR/product.pattern.json"
31
- node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/product.pattern.json" "$TMP_DIR/product.ko" "$TMP_DIR/product.ll"
28
+ node ./bin/k-llvm-compile.mjs "$TMP_DIR/product.ko" "$TMP_DIR/product.ll"
32
29
  grep -q 'store i32 1, ptr %i32_slot' "$TMP_DIR/product.ll"
33
30
  grep -q 'store ptr %product_fields' "$TMP_DIR/product.ll"
34
31
  clang -Wno-override-module -Iruntime runtime/krt.c tests/product-driver.c "$TMP_DIR/product.ll" -o "$TMP_DIR/product"
35
32
  "$TMP_DIR/product"
36
33
 
37
34
  node ../../objects/compile.mjs '|tag' "$TMP_DIR/variant.ko"
38
- printf '[["closed-product",[]]]' > "$TMP_DIR/variant.pattern.json"
39
- node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/variant.pattern.json" "$TMP_DIR/variant.ko" "$TMP_DIR/variant.ll"
35
+ node ./bin/k-llvm-compile.mjs "$TMP_DIR/variant.ko" "$TMP_DIR/variant.ll"
40
36
  grep -q 'call ptr @k_rt_alloc' "$TMP_DIR/variant.ll"
41
37
  grep -q 'store i32 2, ptr %i32_slot' "$TMP_DIR/variant.ll"
42
38
  clang -Wno-override-module -Iruntime runtime/krt.c tests/variant-driver.c "$TMP_DIR/variant.ll" -o "$TMP_DIR/variant"
43
39
  "$TMP_DIR/variant"
44
40
 
45
41
  node ../../objects/compile.mjs '/tag' "$TMP_DIR/variant-projection.ko"
46
- printf '[["closed-union",[["tag",1]]],["closed-product",[]]]' > "$TMP_DIR/variant-projection.pattern.json"
47
- node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/variant-projection.pattern.json" "$TMP_DIR/variant-projection.ko" "$TMP_DIR/variant-projection.ll"
42
+ node ./bin/k-llvm-compile.mjs "$TMP_DIR/variant-projection.ko" "$TMP_DIR/variant-projection.ll"
48
43
  grep -q 'tag_byte_ptr' "$TMP_DIR/variant-projection.ll"
49
44
  grep -q 'payload_slot' "$TMP_DIR/variant-projection.ll"
50
45
  ! grep -q 'call i32 @k_variant_tag_matches' "$TMP_DIR/variant-projection.ll"
@@ -53,24 +48,21 @@ clang -Wno-override-module -Iruntime runtime/krt.c tests/variant-projection-driv
53
48
  "$TMP_DIR/variant-projection"
54
49
 
55
50
  node ../../objects/compile.mjs '(.x .y)' "$TMP_DIR/composition.ko"
56
- printf '[["closed-product",[["x",1]]],["closed-product",[["y",2]]],["closed-product",[]]]' > "$TMP_DIR/composition.pattern.json"
57
- node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/composition.pattern.json" "$TMP_DIR/composition.ko" "$TMP_DIR/composition.ll"
51
+ node ./bin/k-llvm-compile.mjs "$TMP_DIR/composition.ko" "$TMP_DIR/composition.ll"
58
52
  grep -q 'product_fields_ptr' "$TMP_DIR/composition.ll"
59
53
  ! grep -q 'call ptr @k_product_get_at' "$TMP_DIR/composition.ll"
60
54
  clang -Wno-override-module -Iruntime runtime/krt.c tests/composition-driver.c "$TMP_DIR/composition.ll" -o "$TMP_DIR/composition"
61
55
  "$TMP_DIR/composition"
62
56
 
63
57
  node ../../objects/compile.mjs 'pick = .x; {.a pick left, .b pick right}' "$TMP_DIR/relation.ko"
64
- printf '[["open-product",[["a",1],["b",2]]],["open-product",[["x",3]]],["open-product",[["x",4]]],["closed-product",[]],["closed-product",[]]]' > "$TMP_DIR/relation.pattern.json"
65
- node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/relation.pattern.json" "$TMP_DIR/relation.ko" "$TMP_DIR/relation.ll"
58
+ node ./bin/k-llvm-compile.mjs "$TMP_DIR/relation.ko" "$TMP_DIR/relation.ll"
66
59
  grep -q 'define internal %k_result @k_rel_pick' "$TMP_DIR/relation.ll"
67
60
  grep -q 'call %k_result @k_rel_pick' "$TMP_DIR/relation.ll"
68
61
  clang -Wno-override-module -Iruntime runtime/krt.c tests/relation-driver.c "$TMP_DIR/relation.ll" -o "$TMP_DIR/relation"
69
62
  "$TMP_DIR/relation"
70
63
 
71
64
  node ../../objects/compile.mjs '< /x |left, /y |right >' "$TMP_DIR/union.ko"
72
- printf '[["closed-union",[["x",1],["y",2]]],["closed-product",[]],["closed-product",[]]]' > "$TMP_DIR/union.pattern.json"
73
- node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/union.pattern.json" "$TMP_DIR/union.ko" "$TMP_DIR/union.ll"
65
+ node ./bin/k-llvm-compile.mjs "$TMP_DIR/union.ko" "$TMP_DIR/union.ll"
74
66
  grep -q 'define internal %k_result @k_union_arm_0' "$TMP_DIR/union.ll"
75
67
  grep -q 'call %k_result @k_union_arm_1' "$TMP_DIR/union.ll"
76
68
  clang -Wno-override-module -Iruntime runtime/krt.c tests/union-driver.c "$TMP_DIR/union.ll" -o "$TMP_DIR/union"
@@ -94,8 +86,7 @@ node ../../objects/compile.mjs '{ .x fieldA, .y fieldB }' "$TMP_DIR/run-product.
94
86
  printf '{"x":"left","y":"right"}' > "$TMP_DIR/run-product-input.kv"
95
87
  node ./bin/k-llvm-run.mjs "$TMP_DIR/run-product.ko" "$TMP_DIR/run-product-input.kv" | grep -qx '{"fieldA":"left","fieldB":"right"}'
96
88
 
97
- printf '[["closed-product",[["x",1],["y",3]]],["open-union",[["left",2]]],["closed-product",[]],["open-union",[["right",2]]]]' > "$TMP_DIR/run-envelope.pattern.json"
98
- node ./bin/k-llvm-build.mjs --input-pattern "$TMP_DIR/run-envelope.pattern.json" "$TMP_DIR/run.ko" "$TMP_DIR/run-exe"
89
+ node ./bin/k-llvm-build.mjs '?{<{}=X0 left, ...> x, <X0 right, ...> y} .x' -o "$TMP_DIR/run-exe"
99
90
  printf '{"x":"left","y":"right"}' \
100
91
  | node ../../codecs/k-parse.mjs \
101
92
  | "$TMP_DIR/run-exe" \
@@ -104,15 +95,13 @@ printf '{"x":"left","y":"right"}' \
104
95
 
105
96
  printf '{"x":"left"}' | node ../../codecs/k-parse.mjs | "$TMP_DIR/run-exe" && exit 1 || test "$?" -eq 5
106
97
 
107
- printf '[["open-union",[["left",1],["right",1]]],["closed-product",[]]]' > "$TMP_DIR/identity-variant.pattern.json"
108
- node ./bin/k-llvm-build.mjs --input-pattern "$TMP_DIR/identity-variant.pattern.json" "$TMP_DIR/id.ko" "$TMP_DIR/id-exe"
98
+ node ./bin/k-llvm-build.mjs '?<{} left, {} right, ...>' -o "$TMP_DIR/id-exe"
109
99
  node --input-type=module -e "import { stdout } from 'node:process'; import { encodeToWire } from '../../codecs/runtime/prefix-codec.mjs'; import { Value } from '../../Value.mjs'; const pattern = [[\"open-union\",[[\"left\",1],[\"right\",1]]],[\"closed-product\",[]]]; stdout.write(encodeToWire(Value.variant(\"left\", Value.product({}), pattern), pattern));" \
110
100
  | "$TMP_DIR/id-exe" \
111
101
  | node --input-type=module -e "import { stdin } from 'node:process'; import { decodeWire } from '../../codecs/runtime/prefix-codec.mjs'; const chunks = []; stdin.on('data', (chunk) => chunks.push(chunk)); stdin.on('end', () => console.log(JSON.stringify(decodeWire(Buffer.concat(chunks)).pattern)));" \
112
102
  | grep -Fqx '[["open-union",[["left",1],["right",1]]],["closed-product",[]]]'
113
103
 
114
- printf '[["closed-union",[["+",1],["-",1]]],["closed-union",[["0",1],["1",1],["_",2]]],["closed-product",[]]]' > "$TMP_DIR/identity-int.pattern.json"
115
- node ./bin/k-llvm-build.mjs --input-pattern "$TMP_DIR/identity-int.pattern.json" "$TMP_DIR/id.ko" "$TMP_DIR/id-int-exe"
104
+ node ./bin/k-llvm-build.mjs '$bits = < {} _, bits 0, bits 1 >; $int = < bits "+", bits "-" >; $int' -o "$TMP_DIR/id-int-exe"
116
105
  printf '2' \
117
106
  | node ../../codecs/int.mjs --parse \
118
107
  | "$TMP_DIR/id-int-exe" \
@@ -121,6 +110,13 @@ printf '2' \
121
110
 
122
111
  node --input-type=module -e "import assert from 'node:assert/strict'; import { spawn } from 'node:child_process'; import { encodeToWire, decodeWire } from '../../codecs/runtime/prefix-codec.mjs'; import { parse, INT_PATTERN } from '../../codecs/int.mjs'; const input = encodeToWire(parse('2'), INT_PATTERN); const header = Buffer.alloc(4); header.writeUInt32BE(input.length); const child = spawn(process.argv[1], ['--server']); const chunks = []; const stderr = []; child.stdout.on('data', (chunk) => chunks.push(chunk)); child.stderr.on('data', (chunk) => stderr.push(chunk)); child.stdin.end(Buffer.concat([header, input])); const status = await new Promise((resolve, reject) => { child.on('error', reject); child.on('close', resolve); }); assert.equal(status, 0, Buffer.concat(stderr).toString('utf8')); const output = Buffer.concat(chunks); const length = output.readUInt32BE(0); assert.equal(output.length, 4 + length); assert.deepEqual(decodeWire(output.subarray(4)).value.toJSON(), parse('2').toJSON());" "$TMP_DIR/id-int-exe"
123
112
 
113
+ node ./bin/k-llvm-build.mjs --lib ../../Examples/arithmetics.k --export times 's = {()x, ()y} times; s' -o "$TMP_DIR/square-exe"
114
+ printf '987654321' \
115
+ | node ../../codecs/int.mjs --parse \
116
+ | "$TMP_DIR/square-exe" \
117
+ | node ../../codecs/int.mjs --print \
118
+ | grep -qx '975461057789971041'
119
+
124
120
  printf '{"x":"left","y":"right"}' \
125
121
  | node ../../codecs/k-parse.mjs \
126
122
  | node ./bin/k-llvm-jit.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/run.ko" \
@@ -33,7 +33,7 @@ function sha256(text) {
33
33
  function backendFingerprint() {
34
34
  if (cachedBackendFingerprint != null) return cachedBackendFingerprint;
35
35
  const hash = crypto.createHash("sha256");
36
- for (const relPath of ["src/llvm.mjs", "src/executable.mjs", "runtime/krt.c", "runtime/krt.h"]) {
36
+ for (const relPath of ["src/llvm.mjs", "src/kvm2llvm.mjs", "src/executable.mjs", "runtime/krt.c", "runtime/krt.h"]) {
37
37
  hash.update(relPath);
38
38
  hash.update("\0");
39
39
  hash.update(fs.readFileSync(path.join(root, relPath)));
@@ -20,6 +20,7 @@ function usage(stream = console.error) {
20
20
  stream(" wasm-file Output .wasm path. Writes the binary artifact to stdout when omitted.");
21
21
  stream("");
22
22
  stream("Options:");
23
+ stream(" -o, --output Specify output file path explicitly.");
23
24
  stream(" --lib file Load one .klib dependency before compiling.");
24
25
  stream(" --export spec Export a library alias into source scope. May be repeated.");
25
26
  stream(" spec is 'name' or 'libname:localname'.");
@@ -33,10 +34,24 @@ try {
33
34
  exit(0);
34
35
  }
35
36
 
36
- const { libraries, exportSpecs } = parseCompileOptions(args);
37
- const input = resolveProgramInput(args, { allowStdinSource: true });
38
- const outputPath = args.shift();
39
- if (args.length > 0) throw new Error("Too many arguments");
37
+ let explicitOutput = null;
38
+ const remainingArgs = [];
39
+ while (args.length > 0) {
40
+ const arg = args[0];
41
+ if (arg === "-o" || arg === "--output") {
42
+ args.shift();
43
+ explicitOutput = args.shift();
44
+ if (!explicitOutput) throw new Error("-o/--output requires a file argument");
45
+ } else {
46
+ remainingArgs.push(args.shift());
47
+ }
48
+ }
49
+
50
+ const { libraries, exportSpecs } = parseCompileOptions(remainingArgs);
51
+ const input = resolveProgramInput(remainingArgs, { allowStdinSource: true });
52
+ const positionalOutput = remainingArgs.shift();
53
+ if (remainingArgs.length > 0) throw new Error("Too many arguments");
54
+ const outputPath = explicitOutput || positionalOutput || null;
40
55
  const artifact = await compileProgramInput(input, { libraries, exportSpecs, stdin });
41
56
  if (outputPath == null) {
42
57
  stdout.write(artifact);
@@ -20,6 +20,7 @@ import {
20
20
  import { Value, isProduct, isVariant } from "../Value.mjs";
21
21
  import { encodeLibrary } from "../object.mjs";
22
22
  import { setVfsFile, getVfsFile, getAllVfsFiles } from "./shims/fs.mjs";
23
+ import { createOutputEntryElement } from "./tree-view.mjs";
23
24
 
24
25
  // Make Value and helpers globally available for custom codecs
25
26
  if (typeof window !== "undefined") {
@@ -155,7 +156,7 @@ function setStatus(busy, text = "Ready") {
155
156
  }
156
157
  }
157
158
 
158
- function appendEntry({ prompt, command, outputs = [], errors = [], warnings = [], duration = null, timing = null }) {
159
+ function appendEntry({ prompt, command, outputs = [], errors = [], warnings = [], duration = null, timing = null, value = null }) {
159
160
  if (!outputEl) return;
160
161
 
161
162
  const entry = document.createElement("div");
@@ -220,11 +221,26 @@ function appendEntry({ prompt, command, outputs = [], errors = [], warnings = []
220
221
  }
221
222
 
222
223
  // Outputs
223
- for (const out of outputs) {
224
- const outDiv = document.createElement("div");
225
- outDiv.className = "entry-line line-output";
226
- outDiv.innerHTML = ansiToHtml(out);
227
- entry.appendChild(outDiv);
224
+ if (value && outputs.length > 0) {
225
+ const rawText = outputs.join("\n");
226
+ const allLines = outputs[0].split("\n");
227
+ const codecOutputs = allLines.slice(1);
228
+ const timingText = outputs.length > 1 ? outputs[1] : null;
229
+
230
+ const outElement = createOutputEntryElement({
231
+ value,
232
+ rawText,
233
+ codecOutputs,
234
+ timingText
235
+ });
236
+ entry.appendChild(outElement);
237
+ } else {
238
+ for (const out of outputs) {
239
+ const outDiv = document.createElement("div");
240
+ outDiv.className = "entry-line line-output";
241
+ outDiv.innerHTML = ansiToHtml(out);
242
+ entry.appendChild(outDiv);
243
+ }
228
244
  }
229
245
 
230
246
  outputEl.appendChild(entry);
@@ -298,7 +314,8 @@ export async function executeCommand(input) {
298
314
  errors,
299
315
  warnings,
300
316
  duration,
301
- timing: state.lastTiming
317
+ timing: state.lastTiming,
318
+ value: state.lastResult
302
319
  });
303
320
 
304
321
  // Re-create completer with updated state