@fraczak/k 7.0.0 → 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.
- package/CHANGELOG.md +50 -0
- package/DOCS/BIG_PICTURE.md +5 -5
- package/DOCS/FILE_FORMATS.md +5 -5
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +9 -9
- package/DOCS/PATTERNS.md +3 -3
- package/DOCS/PROFILING_AND_TRACING.md +60 -72
- package/DOCS/REPL.md +3 -3
- package/DOCS/book/03-types-as-finite-automata.md +4 -4
- package/DOCS/book/06-values-in-memory.md +9 -9
- package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
- package/DOCS/book/09-compiler-architecture.md +2 -2
- package/DOCS/book/10-from-ast-to-intermediate-representation.md +2 -2
- package/DOCS/book/14-canonical-serialization.md +1 -1
- package/Examples/arithmetics.k +2 -2
- package/Examples/bnat.k +1 -1
- package/Examples/karatsuba-mult.k +2 -2
- package/README.md +3 -8
- package/backend-api.mjs +9 -1
- package/backends/llvm/runtime/krt.c +33 -0
- package/backends/llvm/runtime/krt.h +1 -0
- package/backends/llvm/scripts/run-tests.mjs +5 -0
- package/backends/llvm/src/executable.mjs +4 -4
- package/backends/llvm/src/kvm2llvm.mjs +42 -19
- package/backends/llvm/src/llvm.mjs +30 -36
- package/backends/llvm/tests/integration.sh +2 -2
- package/backends/llvm/tests/perf-support.mjs +1 -55
- package/backends/llvm/tests/test-llvm.mjs +3 -3
- package/backends/llvm/tests/test-open-product-misalignment.mjs +140 -0
- package/backends/wasm/demo_ieee.mjs +2 -3
- package/backends/wasm/runtime.wat +157 -5
- package/backends/wasm/src/kvm2wasm.mjs +57 -34
- package/backends/wasm/src/wasm.mjs +40 -18
- package/backends/wasm/tests/perf-support.mjs +4 -4
- package/backends/wasm/tests/test-wasm.mjs +13 -14
- package/browser/vfs-data.mjs +6 -6
- package/core.k +8 -8
- package/harness/format.mjs +2 -2
- package/harness/registry.mjs +0 -2
- package/kir.mjs +299 -17
- package/package.json +1 -5
- package/scripts/benchmark-idempotency.mjs +12 -8
- package/scripts/benchmark.mjs +5 -9
- package/scripts/build-repl-html.mjs +2 -2
- package/scripts/perf-ieee.mjs +17 -118
- package/scripts/perf-int.mjs +313 -318
- package/scripts/perf-multiplication.mjs +10 -20
- package/scripts/perf-poly.mjs +29 -183
- package/tests/code-derivation/pattern-envelope.mjs +4 -4
- package/tests/test-kvm-polymorphic.mjs +2 -2
- package/tests/test-repl-html.mjs +15 -0
- package/tests/test-repl.mjs +10 -0
- package/backends/arm64/README.md +0 -103
- package/backends/arm64/bin/k-arm64-compile.mjs +0 -93
- package/backends/arm64/bin/k-arm64-run.mjs +0 -58
- package/backends/arm64/bin/k-arm64.mjs +0 -88
- package/backends/arm64/scripts/run-tests.mjs +0 -57
- package/backends/arm64/src/arm64.mjs +0 -229
- package/backends/arm64/src/cli.mjs +0 -155
- package/backends/arm64/src/kvm2arm64.mjs +0 -906
- package/backends/arm64/src/runtime.c +0 -744
- package/backends/arm64/tests/integration.sh +0 -112
- package/backends/arm64/tests/test-arm64.mjs +0 -240
- package/harness/adapters/arm64.mjs +0 -105
|
@@ -398,6 +398,14 @@
|
|
|
398
398
|
i32.ge_u
|
|
399
399
|
br_if $break_prod
|
|
400
400
|
|
|
401
|
+
;; Copy field tagId at offset 8 + 4*i
|
|
402
|
+
local.get $dest
|
|
403
|
+
i32.const 8
|
|
404
|
+
local.get $i
|
|
405
|
+
i32.const 4
|
|
406
|
+
i32.mul
|
|
407
|
+
i32.add
|
|
408
|
+
i32.add
|
|
401
409
|
local.get $curr
|
|
402
410
|
i32.const 8
|
|
403
411
|
local.get $i
|
|
@@ -406,17 +414,19 @@
|
|
|
406
414
|
i32.add
|
|
407
415
|
i32.add
|
|
408
416
|
i32.load
|
|
409
|
-
|
|
417
|
+
i32.store
|
|
410
418
|
|
|
411
|
-
|
|
419
|
+
;; Value slot offset is 8 + 4*N + 4*i
|
|
412
420
|
i32.const 8
|
|
413
|
-
local.get $
|
|
421
|
+
local.get $N
|
|
414
422
|
i32.const 4
|
|
415
423
|
i32.mul
|
|
416
424
|
i32.add
|
|
425
|
+
local.get $i
|
|
426
|
+
i32.const 4
|
|
427
|
+
i32.mul
|
|
417
428
|
i32.add
|
|
418
|
-
local.
|
|
419
|
-
i32.store
|
|
429
|
+
local.set $offsetVal
|
|
420
430
|
|
|
421
431
|
local.get $curr
|
|
422
432
|
local.get $offsetVal
|
|
@@ -472,4 +482,146 @@
|
|
|
472
482
|
i32.add
|
|
473
483
|
global.set $arena_free
|
|
474
484
|
)
|
|
485
|
+
|
|
486
|
+
;; Lookup product field by tagId with predictedIndex hint
|
|
487
|
+
(func $product_get (export "product_get") (param $prod i32) (param $targetTagId i32) (param $predictedIndex i32) (result i32)
|
|
488
|
+
(local $size i32)
|
|
489
|
+
(local $N i32)
|
|
490
|
+
(local $i i32)
|
|
491
|
+
|
|
492
|
+
;; 1. Null check
|
|
493
|
+
local.get $prod
|
|
494
|
+
i32.eqz
|
|
495
|
+
if
|
|
496
|
+
i32.const 0
|
|
497
|
+
return
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
;; 2. Read totalSize at prod + 0
|
|
501
|
+
local.get $prod
|
|
502
|
+
i32.load offset=0
|
|
503
|
+
local.set $size
|
|
504
|
+
|
|
505
|
+
;; 3. Validate product header (not a variant and size >= 8)
|
|
506
|
+
local.get $size
|
|
507
|
+
i32.const 12
|
|
508
|
+
i32.eq
|
|
509
|
+
if
|
|
510
|
+
i32.const 0
|
|
511
|
+
return
|
|
512
|
+
end
|
|
513
|
+
local.get $size
|
|
514
|
+
i32.const 8
|
|
515
|
+
i32.lt_u
|
|
516
|
+
if
|
|
517
|
+
i32.const 0
|
|
518
|
+
return
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
;; 4. Read field count N at prod + 4
|
|
522
|
+
local.get $prod
|
|
523
|
+
i32.load offset=4
|
|
524
|
+
local.set $N
|
|
525
|
+
|
|
526
|
+
;; 5. Validate size == 8 + 8 * N
|
|
527
|
+
local.get $size
|
|
528
|
+
i32.const 8
|
|
529
|
+
local.get $N
|
|
530
|
+
i32.const 8
|
|
531
|
+
i32.mul
|
|
532
|
+
i32.add
|
|
533
|
+
i32.ne
|
|
534
|
+
if
|
|
535
|
+
i32.const 0
|
|
536
|
+
return
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
;; 6. Fast path: check predictedIndex if in range [0, N)
|
|
540
|
+
local.get $predictedIndex
|
|
541
|
+
i32.const 0
|
|
542
|
+
i32.ge_s
|
|
543
|
+
if
|
|
544
|
+
local.get $predictedIndex
|
|
545
|
+
local.get $N
|
|
546
|
+
i32.lt_u
|
|
547
|
+
if
|
|
548
|
+
;; Load tagId at prod + 8 + 4 * predictedIndex
|
|
549
|
+
local.get $prod
|
|
550
|
+
i32.const 8
|
|
551
|
+
local.get $predictedIndex
|
|
552
|
+
i32.const 4
|
|
553
|
+
i32.mul
|
|
554
|
+
i32.add
|
|
555
|
+
i32.add
|
|
556
|
+
i32.load
|
|
557
|
+
local.get $targetTagId
|
|
558
|
+
i32.eq
|
|
559
|
+
if
|
|
560
|
+
;; Match! Return value at prod + 8 + 4*N + 4*predictedIndex
|
|
561
|
+
local.get $prod
|
|
562
|
+
i32.const 8
|
|
563
|
+
local.get $N
|
|
564
|
+
i32.const 4
|
|
565
|
+
i32.mul
|
|
566
|
+
i32.add
|
|
567
|
+
local.get $predictedIndex
|
|
568
|
+
i32.const 4
|
|
569
|
+
i32.mul
|
|
570
|
+
i32.add
|
|
571
|
+
i32.add
|
|
572
|
+
i32.load
|
|
573
|
+
return
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
;; 7. Scan path: check i = 0 .. N-1
|
|
579
|
+
i32.const 0
|
|
580
|
+
local.set $i
|
|
581
|
+
(block $break_scan
|
|
582
|
+
(loop $loop_scan
|
|
583
|
+
local.get $i
|
|
584
|
+
local.get $N
|
|
585
|
+
i32.ge_u
|
|
586
|
+
br_if $break_scan
|
|
587
|
+
|
|
588
|
+
;; Load tagId at prod + 8 + 4 * i
|
|
589
|
+
local.get $prod
|
|
590
|
+
i32.const 8
|
|
591
|
+
local.get $i
|
|
592
|
+
i32.const 4
|
|
593
|
+
i32.mul
|
|
594
|
+
i32.add
|
|
595
|
+
i32.add
|
|
596
|
+
i32.load
|
|
597
|
+
local.get $targetTagId
|
|
598
|
+
i32.eq
|
|
599
|
+
if
|
|
600
|
+
;; Match! Return value at prod + 8 + 4*N + 4*i
|
|
601
|
+
local.get $prod
|
|
602
|
+
i32.const 8
|
|
603
|
+
local.get $N
|
|
604
|
+
i32.const 4
|
|
605
|
+
i32.mul
|
|
606
|
+
i32.add
|
|
607
|
+
local.get $i
|
|
608
|
+
i32.const 4
|
|
609
|
+
i32.mul
|
|
610
|
+
i32.add
|
|
611
|
+
i32.add
|
|
612
|
+
i32.load
|
|
613
|
+
return
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
local.get $i
|
|
617
|
+
i32.const 1
|
|
618
|
+
i32.add
|
|
619
|
+
local.set $i
|
|
620
|
+
br $loop_scan
|
|
621
|
+
)
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
;; 8. Not found: return 0
|
|
625
|
+
i32.const 0
|
|
626
|
+
)
|
|
475
627
|
)
|
|
@@ -108,22 +108,28 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
108
108
|
const getPattern = (rName, inputMap = {}) => registerPatterns.get(mappedReg(rName, inputMap));
|
|
109
109
|
|
|
110
110
|
const getProductEdges = (inst, inputMap) => {
|
|
111
|
-
if (typePatternGraph) {
|
|
112
|
-
const inputPatternId = inst.exp.patterns[0];
|
|
113
|
-
const inputPatternNodeId = typePatternGraph.find(inputPatternId);
|
|
114
|
-
const inputPropertyList = patternToPropertyList(exportPatternGraph(typePatternGraph, inputPatternNodeId));
|
|
115
|
-
return inputPropertyList[0][1];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
111
|
const inputPropertyList = getPattern(inst.src, inputMap);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
112
|
+
if (Array.isArray(inputPropertyList) && inputPropertyList.length > 0) {
|
|
113
|
+
const inputRoot = inputPropertyList[0];
|
|
114
|
+
if ((inputRoot[0] === "open-product" || inputRoot[0] === "closed-product") && Array.isArray(inputRoot[1])) {
|
|
115
|
+
return inputRoot[1];
|
|
116
|
+
}
|
|
122
117
|
}
|
|
123
|
-
|
|
124
|
-
|
|
118
|
+
|
|
119
|
+
if (typePatternGraph && inst.exp?.patterns?.[0] != null) {
|
|
120
|
+
try {
|
|
121
|
+
const inputPatternId = inst.exp.patterns[0];
|
|
122
|
+
const inputPatternNodeId = typePatternGraph.find(inputPatternId);
|
|
123
|
+
const inputPropertyList = patternToPropertyList(exportPatternGraph(typePatternGraph, inputPatternNodeId));
|
|
124
|
+
if (Array.isArray(inputPropertyList?.[0]?.[1])) {
|
|
125
|
+
return inputPropertyList[0][1];
|
|
126
|
+
}
|
|
127
|
+
} catch {
|
|
128
|
+
// Ignore fallback errors
|
|
129
|
+
}
|
|
125
130
|
}
|
|
126
|
-
|
|
131
|
+
|
|
132
|
+
return null;
|
|
127
133
|
};
|
|
128
134
|
|
|
129
135
|
const registers = new Set();
|
|
@@ -205,14 +211,12 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
205
211
|
const lines = [];
|
|
206
212
|
for (const field of inputProductFields) {
|
|
207
213
|
const fieldIndex = inputProductEdges.findIndex(([label]) => label === field.label);
|
|
214
|
+
const tagId = getTagId(field.label);
|
|
208
215
|
lines.push(` ;; cache input field ${field.label} in $${field.local}`);
|
|
209
216
|
lines.push(` local.get $in`);
|
|
210
|
-
lines.push(`
|
|
211
|
-
lines.push(` i32.const ${
|
|
212
|
-
lines.push(`
|
|
213
|
-
lines.push(` i32.load`);
|
|
214
|
-
lines.push(` i32.add`);
|
|
215
|
-
lines.push(` i32.load`);
|
|
217
|
+
lines.push(` i32.const ${tagId}`);
|
|
218
|
+
lines.push(` i32.const ${fieldIndex}`);
|
|
219
|
+
lines.push(` call $product_get`);
|
|
216
220
|
lines.push(` local.set $${field.local}`);
|
|
217
221
|
}
|
|
218
222
|
return lines.join("\n");
|
|
@@ -231,9 +235,10 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
231
235
|
lines.push(` i32.const ${inputProductFields.length}`);
|
|
232
236
|
lines.push(` i32.store offset=4`);
|
|
233
237
|
for (const field of inputProductFields) {
|
|
238
|
+
const tagId = getTagId(field.label);
|
|
234
239
|
const offsetVal = 8 + 4 * inputProductFields.length + 4 * field.index;
|
|
235
240
|
lines.push(` local.get $${dest}`);
|
|
236
|
-
lines.push(` i32.const ${
|
|
241
|
+
lines.push(` i32.const ${tagId}`);
|
|
237
242
|
lines.push(` i32.store offset=${8 + 4 * field.index}`);
|
|
238
243
|
lines.push(` local.get $${dest}`);
|
|
239
244
|
lines.push(` local.get $${field.local}`);
|
|
@@ -314,14 +319,24 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
314
319
|
break;
|
|
315
320
|
}
|
|
316
321
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
322
|
+
let predictedIndex = -1;
|
|
323
|
+
try {
|
|
324
|
+
const edges = getProductEdges(inst, inputMap);
|
|
325
|
+
if (Array.isArray(edges)) {
|
|
326
|
+
predictedIndex = edges.findIndex(([label]) => label === inst.label);
|
|
327
|
+
}
|
|
328
|
+
} catch {
|
|
329
|
+
predictedIndex = -1;
|
|
321
330
|
}
|
|
331
|
+
const tagId = getTagId(inst.label);
|
|
322
332
|
|
|
323
|
-
lines.push(` ;; project_field ${inst.label} (
|
|
333
|
+
lines.push(` ;; project_field ${inst.label} (tagId ${tagId}, hint ${predictedIndex}) from $${src} to $${dest}`);
|
|
324
334
|
lines.push(` local.get $${src}`);
|
|
335
|
+
lines.push(` i32.const ${tagId}`);
|
|
336
|
+
lines.push(` i32.const ${predictedIndex}`);
|
|
337
|
+
lines.push(` call $product_get`);
|
|
338
|
+
lines.push(` local.set $${dest}`);
|
|
339
|
+
lines.push(` local.get $${dest}`);
|
|
325
340
|
lines.push(` i32.eqz`);
|
|
326
341
|
lines.push(` if`);
|
|
327
342
|
if (failTarget) {
|
|
@@ -332,14 +347,6 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
332
347
|
lines.push(` return`);
|
|
333
348
|
}
|
|
334
349
|
lines.push(` end`);
|
|
335
|
-
lines.push(` local.get $${src}`);
|
|
336
|
-
lines.push(` local.get $${src}`);
|
|
337
|
-
lines.push(` i32.const ${8 + 4 * fieldIndex}`);
|
|
338
|
-
lines.push(` i32.add`);
|
|
339
|
-
lines.push(` i32.load`);
|
|
340
|
-
lines.push(` i32.add`);
|
|
341
|
-
lines.push(` i32.load`);
|
|
342
|
-
lines.push(` local.set $${dest}`);
|
|
343
350
|
setPattern(dest, inst.pattern);
|
|
344
351
|
break;
|
|
345
352
|
}
|
|
@@ -443,11 +450,13 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
443
450
|
lines.push(` i32.store offset=4`);
|
|
444
451
|
|
|
445
452
|
for (let i = 0; i < N; i++) {
|
|
453
|
+
const branch = sortedBranches[i];
|
|
454
|
+
const tagId = getTagId(branch.label);
|
|
446
455
|
const offsetVal = 8 + 4 * N + 4 * i;
|
|
447
456
|
const fieldTmp = `${dest}_f${i}`;
|
|
448
457
|
|
|
449
458
|
lines.push(` local.get $${dest}`);
|
|
450
|
-
lines.push(` i32.const ${
|
|
459
|
+
lines.push(` i32.const ${tagId}`);
|
|
451
460
|
lines.push(` i32.store offset=${8 + 4 * i}`);
|
|
452
461
|
|
|
453
462
|
lines.push(` local.get $${dest}`);
|
|
@@ -501,6 +510,20 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
501
510
|
}
|
|
502
511
|
lines.push(` end`);
|
|
503
512
|
|
|
513
|
+
lines.push(` local.get $${src}`);
|
|
514
|
+
lines.push(` i32.load offset=0`);
|
|
515
|
+
lines.push(` i32.const 12`);
|
|
516
|
+
lines.push(` i32.ne`);
|
|
517
|
+
lines.push(` if`);
|
|
518
|
+
if (failTarget) {
|
|
519
|
+
lines.push(` br ${failTarget}`);
|
|
520
|
+
} else {
|
|
521
|
+
lines.push(` i32.const 0`);
|
|
522
|
+
lines.push(` i32.const 0`);
|
|
523
|
+
lines.push(` return`);
|
|
524
|
+
}
|
|
525
|
+
lines.push(` end`);
|
|
526
|
+
|
|
504
527
|
lines.push(` local.get $${src}`);
|
|
505
528
|
lines.push(` i32.load offset=4`);
|
|
506
529
|
lines.push(` i32.const ${tagId}`);
|
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
patternToPropertyList,
|
|
14
14
|
propertyListToPattern,
|
|
15
15
|
Value,
|
|
16
|
-
specializeKVM
|
|
16
|
+
specializeKVM,
|
|
17
|
+
specializeObjectRelations
|
|
17
18
|
} from "@fraczak/k/backend-api.mjs";
|
|
18
19
|
import { intersectPropertyListPatterns } from "@fraczak/k/codecs/runtime/codec.mjs";
|
|
19
20
|
import { propertyListToFilter } from "@fraczak/k/codecs/runtime/show-value.mjs";
|
|
@@ -304,7 +305,8 @@ function compileModule(mainRelName, defs) {
|
|
|
304
305
|
|
|
305
306
|
kvmFunc.name = nameMap.get(name) || cleanName(name);
|
|
306
307
|
cleanCallNames(kvmFunc.body, nameMap);
|
|
307
|
-
|
|
308
|
+
const wasmText = lowerToWasm(kvmFunc, kvmFunc.name);
|
|
309
|
+
wats.push(wasmText);
|
|
308
310
|
}
|
|
309
311
|
|
|
310
312
|
return {
|
|
@@ -437,7 +439,8 @@ function relTypeStatus(defs, name) {
|
|
|
437
439
|
const rel = defs.rels?.[name];
|
|
438
440
|
const explicit = rel?.typeDerivation?.status;
|
|
439
441
|
if (explicit) return explicit;
|
|
440
|
-
const
|
|
442
|
+
const stats = compileStatsByRelName(defs.compileStats);
|
|
443
|
+
const scc = stats.get(name) || (name.includes("$") ? stats.get(name.split("$")[0]) : null);
|
|
441
444
|
if (scc) return scc.converged ? "converged" : "not-converged";
|
|
442
445
|
return "unknown";
|
|
443
446
|
}
|
|
@@ -611,8 +614,10 @@ async function compileWasmArtifactFromObject(object, { entry = null, inputEnvelo
|
|
|
611
614
|
if (!mainRel) throw new Error(`No main relation (${mainRelName}) defined in object`);
|
|
612
615
|
const inputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[0]);
|
|
613
616
|
const outputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[1]);
|
|
614
|
-
|
|
615
|
-
|
|
617
|
+
const typing = classifyTyping(inputPattern, outputPattern);
|
|
618
|
+
if (typing.program === "polymorphic") {
|
|
619
|
+
const specialized = specializeObjectRelations(object, mainRelName, inputEnvelopePattern);
|
|
620
|
+
defs = { rels: specialized.rels, relAlias: specialized.relAlias, compileStats: specialized.compileStats };
|
|
616
621
|
typingMode = "specialized";
|
|
617
622
|
}
|
|
618
623
|
}
|
|
@@ -635,7 +640,9 @@ async function compileWasmArtifact(
|
|
|
635
640
|
const inputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[0]);
|
|
636
641
|
const outputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[1]);
|
|
637
642
|
if (classifyTyping(inputPattern, outputPattern).program === "polymorphic") {
|
|
638
|
-
|
|
643
|
+
const obj = { format: "k-object", rels: defs.rels, relAlias: defs.relAlias, compileStats: defs.compileStats, main: "__main__" };
|
|
644
|
+
const specialized = specializeObjectRelations(obj, "__main__", inputEnvelopePattern);
|
|
645
|
+
defs = { rels: specialized.rels, relAlias: specialized.relAlias, compileStats: specialized.compileStats };
|
|
639
646
|
typingMode = "specialized";
|
|
640
647
|
}
|
|
641
648
|
}
|
|
@@ -723,20 +730,18 @@ function readArenaValue(exports, ptr, pattern, patternNodeId, patternPropertyLis
|
|
|
723
730
|
|
|
724
731
|
if (patternNode.kind === NODE_KIND.OPEN_PRODUCT || patternNode.kind === NODE_KIND.CLOSED_PRODUCT) {
|
|
725
732
|
const N = view.getUint32(frame.ptr + 4, true);
|
|
726
|
-
if (N !== patternNode.edges.length) {
|
|
727
|
-
throw new Error(`Cannot decode product pointer ${frame.ptr}: arena field count ${N} does not match output pattern`);
|
|
728
|
-
}
|
|
729
733
|
const product = {};
|
|
730
734
|
frame.assign(Value.product(product, patternForNode(patternPropertyList, frame.patternNodeId)));
|
|
731
735
|
for (let i = N - 1; i >= 0; i--) {
|
|
732
|
-
const
|
|
733
|
-
const
|
|
734
|
-
const
|
|
736
|
+
const tagId = view.getUint32(frame.ptr + 8 + 4 * i, true);
|
|
737
|
+
const label = tags.getTag(tagId) || patternNode.edges[i]?.label;
|
|
738
|
+
const edge = patternNode.edges.find((candidate) => candidate.label === label);
|
|
739
|
+
const childPtr = view.getUint32(frame.ptr + 8 + 4 * N + 4 * i, true);
|
|
735
740
|
stack.push({
|
|
736
741
|
ptr: wasmPtr(childPtr),
|
|
737
|
-
patternNodeId: edge
|
|
742
|
+
patternNodeId: edge?.target ?? null,
|
|
738
743
|
assign(value) {
|
|
739
|
-
product[
|
|
744
|
+
product[label] = value;
|
|
740
745
|
}
|
|
741
746
|
});
|
|
742
747
|
}
|
|
@@ -787,8 +792,9 @@ function writeValueToArena(exports, value, pattern, patternNodeId, arenaValues,
|
|
|
787
792
|
view.setUint32(frame.ptr, 8 + 8 * N, true);
|
|
788
793
|
view.setUint32(frame.ptr + 4, N, true);
|
|
789
794
|
for (let i = 0; i < N; i++) {
|
|
795
|
+
const tagId = tags.getId(frame.fields[i].label);
|
|
790
796
|
const offset = 8 + 4 * N + 4 * i;
|
|
791
|
-
view.setUint32(frame.ptr + 8 + 4 * i,
|
|
797
|
+
view.setUint32(frame.ptr + 8 + 4 * i, tagId, true);
|
|
792
798
|
view.setUint32(frame.ptr + offset, frame.childPtrs[i], true);
|
|
793
799
|
}
|
|
794
800
|
arenaValues.set(frame.ptr, valueWithPatternNode(frame.value, patternPropertyList, frame.patternNodeId));
|
|
@@ -820,6 +826,13 @@ function writeValueToArena(exports, value, pattern, patternNodeId, arenaValues,
|
|
|
820
826
|
? Object.keys(frame.value.product)
|
|
821
827
|
.sort()
|
|
822
828
|
.map((label) => ({ label, patternNodeId: frame.patternNodeId }))
|
|
829
|
+
: isOpenProduct
|
|
830
|
+
? Object.keys(frame.value.product)
|
|
831
|
+
.sort()
|
|
832
|
+
.map((label) => {
|
|
833
|
+
const edge = patternNode.edges.find((e) => e.label === label);
|
|
834
|
+
return { label, patternNodeId: edge ? edge.target : null };
|
|
835
|
+
})
|
|
823
836
|
: patternNode.edges.map((edge) => ({ label: edge.label, patternNodeId: edge.target }));
|
|
824
837
|
const fieldLabels = new Set(fields.map(({ label }) => label));
|
|
825
838
|
if (isClosedProduct) {
|
|
@@ -829,9 +842,17 @@ function writeValueToArena(exports, value, pattern, patternNodeId, arenaValues,
|
|
|
829
842
|
}
|
|
830
843
|
}
|
|
831
844
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
845
|
+
if (isOpenProduct) {
|
|
846
|
+
for (const edge of patternNode.edges) {
|
|
847
|
+
if (!Object.hasOwn(frame.value.product, edge.label)) {
|
|
848
|
+
throw new Error(`Product field '${edge.label}' is required by input pattern node ${frame.patternNodeId}`);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
} else {
|
|
852
|
+
for (const { label } of fields) {
|
|
853
|
+
if (!Object.hasOwn(frame.value.product, label)) {
|
|
854
|
+
throw new Error(`Product field '${label}' is required by input pattern node ${frame.patternNodeId}`);
|
|
855
|
+
}
|
|
835
856
|
}
|
|
836
857
|
}
|
|
837
858
|
|
|
@@ -841,6 +862,7 @@ function writeValueToArena(exports, value, pattern, patternNodeId, arenaValues,
|
|
|
841
862
|
finishProduct: true,
|
|
842
863
|
value: frame.value,
|
|
843
864
|
patternNodeId: frame.patternNodeId,
|
|
865
|
+
fields,
|
|
844
866
|
ptr,
|
|
845
867
|
childPtrs,
|
|
846
868
|
assign: frame.assign
|
|
@@ -200,8 +200,7 @@ export function readArenaValue(exports, rootPtr, pattern, rootPatternNodeId, pat
|
|
|
200
200
|
|
|
201
201
|
for (let i = N - 1; i >= 0; i--) {
|
|
202
202
|
const edge = patternNode.edges[i];
|
|
203
|
-
const
|
|
204
|
-
const childPtr = view.getUint32(ptr + offsetVal, true);
|
|
203
|
+
const childPtr = view.getUint32(ptr + 8 + 4 * N + 4 * i, true);
|
|
205
204
|
const label = edge.label;
|
|
206
205
|
stack.push({
|
|
207
206
|
ptr: childPtr,
|
|
@@ -253,14 +252,14 @@ export function writeValueToArena(exports, rootValue, pattern, rootPatternNodeId
|
|
|
253
252
|
const frame = stack.pop();
|
|
254
253
|
|
|
255
254
|
if (frame.finishProduct) {
|
|
256
|
-
const { ptr, totalSize, N, childPtrs, assign } = frame;
|
|
255
|
+
const { ptr, totalSize, N, keys, childPtrs, assign } = frame;
|
|
257
256
|
const view = new DataView(exports.memory.buffer);
|
|
258
257
|
view.setUint32(ptr, totalSize, true);
|
|
259
258
|
view.setUint32(ptr + 4, N, true);
|
|
260
259
|
|
|
261
260
|
for (let i = 0; i < N; i++) {
|
|
262
261
|
const offsetVal = 8 + 4 * N + 4 * i;
|
|
263
|
-
view.setUint32(ptr + 8 + 4 * i,
|
|
262
|
+
view.setUint32(ptr + 8 + 4 * i, getTagId(keys[i]), true);
|
|
264
263
|
view.setUint32(ptr + offsetVal, childPtrs[i], true);
|
|
265
264
|
}
|
|
266
265
|
assign(ptr);
|
|
@@ -294,6 +293,7 @@ export function writeValueToArena(exports, rootValue, pattern, rootPatternNodeId
|
|
|
294
293
|
ptr,
|
|
295
294
|
totalSize,
|
|
296
295
|
N,
|
|
296
|
+
keys,
|
|
297
297
|
childPtrs,
|
|
298
298
|
assign
|
|
299
299
|
});
|
|
@@ -194,7 +194,7 @@ console.log("==> Running Product Operations Integration Tests");
|
|
|
194
194
|
for (let i = 0; i < N; i++) {
|
|
195
195
|
const offsetVal = 8 + 4 * N + 4 * i;
|
|
196
196
|
const val = fieldsMap[keys[i]];
|
|
197
|
-
view.setUint32(ptr + 8 + 4 * i,
|
|
197
|
+
view.setUint32(ptr + 8 + 4 * i, getTagId(keys[i]), true);
|
|
198
198
|
view.setUint32(ptr + offsetVal, val, true);
|
|
199
199
|
}
|
|
200
200
|
return ptr;
|
|
@@ -215,8 +215,8 @@ console.log("==> Running Product Operations Integration Tests");
|
|
|
215
215
|
const view = new DataView(exports.memory.buffer);
|
|
216
216
|
assert.equal(view.getUint32(ptrOut, true), 24, "Product total size should be 24");
|
|
217
217
|
assert.equal(view.getUint32(ptrOut + 4, true), 2, "Product field count should be 2");
|
|
218
|
-
assert.equal(view.getUint32(ptrOut + 8, true),
|
|
219
|
-
assert.equal(view.getUint32(ptrOut + 12, true),
|
|
218
|
+
assert.equal(view.getUint32(ptrOut + 8, true), getTagId("x"), "TagId of first field (x)");
|
|
219
|
+
assert.equal(view.getUint32(ptrOut + 12, true), getTagId("y"), "TagId of second field (y)");
|
|
220
220
|
|
|
221
221
|
// The first field value should be the projection of x from ptrIn (101)
|
|
222
222
|
const valXPtr = view.getUint32(ptrOut + 16, true);
|
|
@@ -443,12 +443,12 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
|
|
|
443
443
|
const runtimeWat = fs.readFileSync("runtime.wat", "utf8");
|
|
444
444
|
|
|
445
445
|
const script = `
|
|
446
|
-
|
|
447
|
-
0 = {} | 0
|
|
448
|
-
inc = | +1
|
|
449
|
-
dec =
|
|
450
|
-
add =
|
|
451
|
-
{ . x dec x, . y inc y } add,
|
|
446
|
+
nat? = ?< {} 0, nat? +1 >;
|
|
447
|
+
0 = {} | 0 nat?;
|
|
448
|
+
inc = | +1 nat?;
|
|
449
|
+
dec = nat? / +1;
|
|
450
|
+
add = ?{ nat? x, nat? y } = X <
|
|
451
|
+
{ . x dec x, . y inc y } ?X add,
|
|
452
452
|
. y
|
|
453
453
|
>;
|
|
454
454
|
()
|
|
@@ -483,8 +483,7 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
|
|
|
483
483
|
|
|
484
484
|
for (let i = 0; i < N; i++) {
|
|
485
485
|
const edge = patternNode.edges[i];
|
|
486
|
-
const
|
|
487
|
-
const childPtr = view.getUint32(ptr + offsetVal, true);
|
|
486
|
+
const childPtr = view.getUint32(ptr + 8 + 4 * N + 4 * i, true);
|
|
488
487
|
productObj[edge.label] = readArenaValue(childPtr, pattern, edge.target);
|
|
489
488
|
}
|
|
490
489
|
return Value.product(productObj, pattern);
|
|
@@ -530,7 +529,7 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
|
|
|
530
529
|
|
|
531
530
|
for (let i = 0; i < N; i++) {
|
|
532
531
|
const offsetVal = 8 + 4 * N + 4 * i;
|
|
533
|
-
view.setUint32(ptr + 8 + 4 * i,
|
|
532
|
+
view.setUint32(ptr + 8 + 4 * i, getTagId(keys[i]), true);
|
|
534
533
|
view.setUint32(ptr + offsetVal, childPtrs[i], true);
|
|
535
534
|
}
|
|
536
535
|
return ptr;
|
|
@@ -580,8 +579,8 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
|
|
|
580
579
|
const view = new DataView(exports.memory.buffer);
|
|
581
580
|
view.setUint32(ptr, 24, true);
|
|
582
581
|
view.setUint32(ptr + 4, 2, true);
|
|
583
|
-
view.setUint32(ptr + 8,
|
|
584
|
-
view.setUint32(ptr + 12,
|
|
582
|
+
view.setUint32(ptr + 8, getTagId("x"), true);
|
|
583
|
+
view.setUint32(ptr + 12, getTagId("y"), true);
|
|
585
584
|
view.setUint32(ptr + 16, xPtr, true);
|
|
586
585
|
view.setUint32(ptr + 20, yPtr, true);
|
|
587
586
|
return ptr;
|