@fraczak/k 6.5.0 → 6.6.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.
- package/backends/arm64/src/runtime.c +20 -4
- package/backends/arm64/tests/integration.sh +1 -1
- package/backends/llvm/runtime/krt.c +387 -2
- package/backends/llvm/runtime/krt.h +1 -0
- package/backends/llvm/src/kvm2llvm.mjs +98 -29
- package/backends/llvm/tests/perf-support.mjs +1 -1
- package/browser/repl-browser.mjs +24 -7
- package/browser/tree-view.mjs +599 -0
- package/package.json +1 -1
- package/repl.mjs +4 -0
- package/scripts/build-repl-html.mjs +301 -0
- package/scripts/perf-ieee.mjs +5 -2
- package/scripts/perf-int.mjs +331 -0
- package/scripts/run-tests.mjs +1 -0
- package/tests/test-repl-html.mjs +141 -0
- package/tests/test-tree-view.mjs +185 -0
|
@@ -455,6 +455,307 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
455
455
|
margin: 4px 0 4px 16px;
|
|
456
456
|
}
|
|
457
457
|
|
|
458
|
+
/* Tree View & Toolbar */
|
|
459
|
+
.k-entry-container {
|
|
460
|
+
display: flex;
|
|
461
|
+
flex-direction: column;
|
|
462
|
+
gap: 6px;
|
|
463
|
+
margin-top: 4px;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.k-output-toolbar {
|
|
467
|
+
display: flex;
|
|
468
|
+
align-items: center;
|
|
469
|
+
justify-content: space-between;
|
|
470
|
+
gap: 8px;
|
|
471
|
+
margin-bottom: 2px;
|
|
472
|
+
flex-wrap: wrap;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.k-mode-group, .k-action-group {
|
|
476
|
+
display: flex;
|
|
477
|
+
align-items: center;
|
|
478
|
+
gap: 4px;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.k-btn-mode, .k-btn-action {
|
|
482
|
+
background: var(--bg-surface);
|
|
483
|
+
border: 1px solid var(--border-color);
|
|
484
|
+
color: var(--text-muted);
|
|
485
|
+
border-radius: 4px;
|
|
486
|
+
padding: 2px 8px;
|
|
487
|
+
font-size: 11px;
|
|
488
|
+
font-family: inherit;
|
|
489
|
+
cursor: pointer;
|
|
490
|
+
display: inline-flex;
|
|
491
|
+
align-items: center;
|
|
492
|
+
gap: 4px;
|
|
493
|
+
transition: all 0.15s ease;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.k-btn-mode:hover, .k-btn-action:hover {
|
|
497
|
+
background: var(--bg-subtle);
|
|
498
|
+
color: var(--text-main);
|
|
499
|
+
border-color: #8b949e;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.k-btn-mode.active {
|
|
503
|
+
background: rgba(88, 166, 255, 0.2);
|
|
504
|
+
border-color: var(--color-prompt);
|
|
505
|
+
color: var(--color-prompt);
|
|
506
|
+
font-weight: 600;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.k-tree-view {
|
|
510
|
+
display: flex;
|
|
511
|
+
flex-direction: column;
|
|
512
|
+
gap: 6px;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.k-tree-section {
|
|
516
|
+
background: rgba(22, 27, 34, 0.6);
|
|
517
|
+
border: 1px solid var(--border-color);
|
|
518
|
+
border-radius: 6px;
|
|
519
|
+
overflow: hidden;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.k-tree-section-summary {
|
|
523
|
+
background: rgba(33, 38, 45, 0.5);
|
|
524
|
+
padding: 6px 10px;
|
|
525
|
+
cursor: pointer;
|
|
526
|
+
list-style: none;
|
|
527
|
+
display: flex;
|
|
528
|
+
align-items: center;
|
|
529
|
+
gap: 8px;
|
|
530
|
+
font-size: 12px;
|
|
531
|
+
user-select: none;
|
|
532
|
+
border-bottom: 1px solid transparent;
|
|
533
|
+
transition: background 0.15s;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.k-tree-section[open] > .k-tree-section-summary {
|
|
537
|
+
border-bottom-color: rgba(48, 54, 61, 0.5);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.k-tree-section-summary::-webkit-details-marker {
|
|
541
|
+
display: none;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
.k-tree-section-summary:hover {
|
|
545
|
+
background: rgba(33, 38, 45, 0.8);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.k-section-title {
|
|
549
|
+
font-weight: 700;
|
|
550
|
+
color: var(--color-prompt);
|
|
551
|
+
white-space: nowrap;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.k-section-preview {
|
|
555
|
+
color: var(--text-muted);
|
|
556
|
+
font-size: 11px;
|
|
557
|
+
overflow: hidden;
|
|
558
|
+
text-overflow: ellipsis;
|
|
559
|
+
white-space: nowrap;
|
|
560
|
+
flex: 1;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.k-tree-section-body {
|
|
564
|
+
padding: 8px 10px;
|
|
565
|
+
font-size: 13px;
|
|
566
|
+
line-height: 1.6;
|
|
567
|
+
overflow-x: auto;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/* Tree Node & Summary */
|
|
571
|
+
.k-tree-node {
|
|
572
|
+
margin-left: 12px;
|
|
573
|
+
border-left: 1px dashed rgba(48, 54, 61, 0.6);
|
|
574
|
+
padding-left: 8px;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.k-tree-summary {
|
|
578
|
+
cursor: pointer;
|
|
579
|
+
list-style: none;
|
|
580
|
+
display: inline-flex;
|
|
581
|
+
align-items: baseline;
|
|
582
|
+
gap: 6px;
|
|
583
|
+
outline: none;
|
|
584
|
+
user-select: none;
|
|
585
|
+
padding: 1px 4px;
|
|
586
|
+
border-radius: 4px;
|
|
587
|
+
transition: background 0.15s;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.k-tree-summary::-webkit-details-marker {
|
|
591
|
+
display: none;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
.k-tree-summary:hover {
|
|
595
|
+
background: rgba(88, 166, 255, 0.1);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.k-tree-arrow {
|
|
599
|
+
display: inline-block;
|
|
600
|
+
font-size: 8px;
|
|
601
|
+
width: 10px;
|
|
602
|
+
height: 10px;
|
|
603
|
+
line-height: 10px;
|
|
604
|
+
text-align: center;
|
|
605
|
+
transition: transform 0.15s ease;
|
|
606
|
+
color: var(--text-muted);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
details[open] > summary > .k-tree-arrow {
|
|
610
|
+
transform: rotate(90deg);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.k-tree-children {
|
|
614
|
+
display: flex;
|
|
615
|
+
flex-direction: column;
|
|
616
|
+
gap: 2px;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.k-tree-row {
|
|
620
|
+
display: flex;
|
|
621
|
+
align-items: baseline;
|
|
622
|
+
gap: 6px;
|
|
623
|
+
padding-left: 8px;
|
|
624
|
+
margin: 1px 0;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.k-tree-child-val, .k-tree-child-pat {
|
|
628
|
+
flex: 1;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.k-tree-leaf {
|
|
632
|
+
display: inline-flex;
|
|
633
|
+
align-items: baseline;
|
|
634
|
+
gap: 4px;
|
|
635
|
+
padding: 1px 4px;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/* Syntax Highlighting */
|
|
639
|
+
.k-val-brace, .k-pat-brace {
|
|
640
|
+
color: var(--color-yellow);
|
|
641
|
+
font-weight: 600;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
.k-pat-union {
|
|
645
|
+
color: var(--color-cyan);
|
|
646
|
+
font-weight: 600;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
.k-val-pipe {
|
|
650
|
+
color: var(--text-muted);
|
|
651
|
+
font-weight: 700;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
.k-val-tag {
|
|
655
|
+
color: var(--color-purple);
|
|
656
|
+
font-weight: 600;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.k-pat-tag {
|
|
660
|
+
color: var(--color-cyan);
|
|
661
|
+
font-weight: 600;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.k-tree-field-key, .k-pat-field-key {
|
|
665
|
+
color: var(--color-blue);
|
|
666
|
+
font-weight: 600;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
.k-val-empty, .k-pat-empty {
|
|
670
|
+
color: var(--text-muted);
|
|
671
|
+
font-style: italic;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.k-pat-var {
|
|
675
|
+
color: var(--color-green);
|
|
676
|
+
font-weight: 700;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.k-pat-vardef {
|
|
680
|
+
color: var(--color-green);
|
|
681
|
+
font-size: 11px;
|
|
682
|
+
font-weight: 600;
|
|
683
|
+
background: rgba(63, 185, 80, 0.15);
|
|
684
|
+
border-radius: 3px;
|
|
685
|
+
padding: 0 4px;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
.k-pat-any {
|
|
689
|
+
color: var(--color-yellow);
|
|
690
|
+
font-style: italic;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.k-pat-ellipsis {
|
|
694
|
+
color: var(--text-muted);
|
|
695
|
+
letter-spacing: 2px;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.k-val-preview {
|
|
699
|
+
color: var(--text-muted);
|
|
700
|
+
font-size: 11px;
|
|
701
|
+
max-width: 320px;
|
|
702
|
+
overflow: hidden;
|
|
703
|
+
text-overflow: ellipsis;
|
|
704
|
+
white-space: nowrap;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
.k-tree-badge {
|
|
708
|
+
font-size: 10px;
|
|
709
|
+
background: rgba(139, 148, 158, 0.15);
|
|
710
|
+
color: var(--text-muted);
|
|
711
|
+
padding: 1px 5px;
|
|
712
|
+
border-radius: 10px;
|
|
713
|
+
font-weight: 500;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
.k-badge-cycle {
|
|
717
|
+
background: rgba(248, 81, 73, 0.2);
|
|
718
|
+
color: var(--color-red);
|
|
719
|
+
font-weight: 600;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
.k-codecs-box {
|
|
723
|
+
background: rgba(22, 27, 34, 0.8);
|
|
724
|
+
border: 1px solid var(--border-color);
|
|
725
|
+
border-left: 3px solid var(--color-green);
|
|
726
|
+
border-radius: 0 6px 6px 0;
|
|
727
|
+
padding: 6px 12px;
|
|
728
|
+
display: flex;
|
|
729
|
+
flex-direction: column;
|
|
730
|
+
gap: 3px;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.k-codec-row {
|
|
734
|
+
color: var(--color-green);
|
|
735
|
+
font-weight: 500;
|
|
736
|
+
font-size: 13px;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
.k-timing-row {
|
|
740
|
+
color: var(--text-muted);
|
|
741
|
+
font-size: 11px;
|
|
742
|
+
font-style: italic;
|
|
743
|
+
padding: 2px 4px;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.k-raw-pre {
|
|
747
|
+
background: rgba(13, 17, 23, 0.8);
|
|
748
|
+
border: 1px solid var(--border-color);
|
|
749
|
+
border-radius: 6px;
|
|
750
|
+
padding: 8px 12px;
|
|
751
|
+
font-family: inherit;
|
|
752
|
+
font-size: 13px;
|
|
753
|
+
color: var(--color-green);
|
|
754
|
+
white-space: pre-wrap;
|
|
755
|
+
word-break: break-word;
|
|
756
|
+
user-select: text;
|
|
757
|
+
}
|
|
758
|
+
|
|
458
759
|
/* Welcome banner */
|
|
459
760
|
.welcome-banner {
|
|
460
761
|
padding: 16px;
|
package/scripts/perf-ieee.mjs
CHANGED
|
@@ -74,7 +74,7 @@ const arm64OptLevel = process.env.ARM64_OPT || "-O2";
|
|
|
74
74
|
|
|
75
75
|
// LLVM options
|
|
76
76
|
const llvmWarmupIterations = parseNonNegativeIntEnv("LLVM_WARMUP_ITERATIONS", 1);
|
|
77
|
-
const llvmRuntimeMode = process.env.K_LLVM_IEEE_RUNTIME_MODE || "
|
|
77
|
+
const llvmRuntimeMode = process.env.K_LLVM_IEEE_RUNTIME_MODE || "fast";
|
|
78
78
|
|
|
79
79
|
function getClangOptLevels() {
|
|
80
80
|
for (const arg of process.argv.slice(2)) {
|
|
@@ -156,7 +156,8 @@ await evaluateInput(perfKSource, state);
|
|
|
156
156
|
|
|
157
157
|
const ieeeRaw = fs.readFileSync(ieeePath, "utf8");
|
|
158
158
|
// Exclude trailing unassigned expression and comment block to ensure clean parse
|
|
159
|
-
const
|
|
159
|
+
const endIdx = ieeeRaw.indexOf("\n()\n");
|
|
160
|
+
const ieeeBase = endIdx !== -1 ? ieeeRaw.slice(0, endIdx) : ieeeRaw;
|
|
160
161
|
const fullSource = `${ieeeBase}\n${perfKSource}\nperf_ieee`;
|
|
161
162
|
|
|
162
163
|
console.log("==> Preparing relation perf_ieee");
|
|
@@ -375,6 +376,8 @@ if (runLLVM) {
|
|
|
375
376
|
console.log(`==> Running LLVM (${opt}) (${iterations} iterations)...`);
|
|
376
377
|
const result = await llvmRunner.run(iterations);
|
|
377
378
|
llvmResults.set(opt, result);
|
|
379
|
+
} catch (err) {
|
|
380
|
+
console.log(`==> LLVM (${opt}) execution failed: ${err.message?.split("\n")[0]}`);
|
|
378
381
|
} finally {
|
|
379
382
|
llvmRunner.close();
|
|
380
383
|
}
|
package/scripts/perf-int.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
createState,
|
|
10
10
|
decodeWire,
|
|
11
11
|
evaluateInput,
|
|
12
|
+
executeKVM,
|
|
12
13
|
exportPatternGraph,
|
|
13
14
|
patternToPropertyList,
|
|
14
15
|
propertyListToPattern,
|
|
@@ -38,6 +39,7 @@ import {
|
|
|
38
39
|
runTimedIterations,
|
|
39
40
|
runTimedIterationsAsync,
|
|
40
41
|
shouldStrictFail,
|
|
42
|
+
safeDeepEqual,
|
|
41
43
|
toPlainObject,
|
|
42
44
|
tryCompileCase,
|
|
43
45
|
wireInput,
|
|
@@ -77,6 +79,9 @@ const iterations = parsePositiveIntEnv("ITERATIONS", 3);
|
|
|
77
79
|
// ARM64 options
|
|
78
80
|
const arm64WarmupIterations = parseNonNegativeIntEnv("ARM64_WARMUP_ITERATIONS", 1);
|
|
79
81
|
const arm64OptLevel = process.env.ARM64_OPT || "-O2";
|
|
82
|
+
if (!process.env.K_ARENA_CAPACITY_MB && !process.env.K_ARENA_MB) {
|
|
83
|
+
process.env.K_ARENA_CAPACITY_MB = "4096";
|
|
84
|
+
}
|
|
80
85
|
|
|
81
86
|
// LLVM options
|
|
82
87
|
const llvmWarmupIterations = parseNonNegativeIntEnv("LLVM_WARMUP_ITERATIONS", 1);
|
|
@@ -529,3 +534,329 @@ console.log(`Conformance validation: ${validationSummary.join(", ")} cases match
|
|
|
529
534
|
if (runLLVM && shouldStrictFail(testSuite)) process.exitCode = 1;
|
|
530
535
|
if (runWasm && testSuite.some(tc => tc.wasmConformance !== "ok")) process.exitCode = 1;
|
|
531
536
|
if (runARM64 && testSuite.some(tc => tc.arm64Conformance !== "ok")) process.exitCode = 1;
|
|
537
|
+
|
|
538
|
+
// =========================================================================
|
|
539
|
+
// Squaring Scale & Memory Profiling Benchmark
|
|
540
|
+
// Expression: s = {()x,()y}times; 987654321 s s s s s s s s s s
|
|
541
|
+
// =========================================================================
|
|
542
|
+
|
|
543
|
+
function countBitsValue(val) {
|
|
544
|
+
let count = 0;
|
|
545
|
+
let cur = val?.tag === "+" || val?.tag === "-" ? val.value : val;
|
|
546
|
+
while (cur && (cur.tag === "0" || cur.tag === "1")) {
|
|
547
|
+
count++;
|
|
548
|
+
cur = cur.value;
|
|
549
|
+
}
|
|
550
|
+
return count;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function getProcessMemory(pid) {
|
|
554
|
+
try {
|
|
555
|
+
const status = fs.readFileSync(`/proc/${pid}/status`, "utf8");
|
|
556
|
+
const vmrss = status.match(/VmRSS:\s+(\d+)\s+kB/);
|
|
557
|
+
const vmhwm = status.match(/VmHWM:\s+(\d+)\s+kB/);
|
|
558
|
+
return {
|
|
559
|
+
rssMb: vmrss ? Number((parseInt(vmrss[1], 10) / 1024).toFixed(1)) : null,
|
|
560
|
+
peakRssMb: vmhwm ? Number((parseInt(vmhwm[1], 10) / 1024).toFixed(1)) : null
|
|
561
|
+
};
|
|
562
|
+
} catch {
|
|
563
|
+
return { rssMb: null, peakRssMb: null };
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function getNodeMemory() {
|
|
568
|
+
const mem = process.memoryUsage();
|
|
569
|
+
return {
|
|
570
|
+
heapMb: Number((mem.heapUsed / (1024 * 1024)).toFixed(1)),
|
|
571
|
+
rssMb: Number((mem.rss / (1024 * 1024)).toFixed(1))
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const squaringSteps = parsePositiveIntEnv("SQUARING_STEPS", 10);
|
|
576
|
+
const squaringInputText = process.env.SQUARING_INPUT || "987654321";
|
|
577
|
+
const squaringStepTimeoutMs = parsePositiveIntEnv("SQUARING_STEP_TIMEOUT_MS", 60000);
|
|
578
|
+
|
|
579
|
+
console.log("\n=================== SQUARING SCALE & MEMORY BENCHMARK ===================");
|
|
580
|
+
console.log(`Expression: s = {()x,()y}times; ${squaringInputText} ${"s ".repeat(squaringSteps).trim()}`);
|
|
581
|
+
console.log(`Evaluation: ${squaringSteps} successive squarings doubling bit length each step`);
|
|
582
|
+
|
|
583
|
+
await evaluateInput("s = {()x,()y}times;", state);
|
|
584
|
+
codes.load(state.codes);
|
|
585
|
+
const sRel = prepareRelation(state, "s", {
|
|
586
|
+
source: `${arithmeticsLibrarySource}\ns = {()x,()y}times;\ns`,
|
|
587
|
+
sourceLabel: "@fraczak/k/Examples/arithmetics.k#s"
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
const sInitialVal = intValue(squaringInputText);
|
|
591
|
+
const { inputWire: sInitWire, inputPattern: sInputPattern } = wireInput(sInitialVal);
|
|
592
|
+
|
|
593
|
+
// Build executables for persistent servers
|
|
594
|
+
let sLlvmServer = null;
|
|
595
|
+
if (runLLVM) {
|
|
596
|
+
const sLlvmCompiled = tryCompileCase({
|
|
597
|
+
object: sRel.object,
|
|
598
|
+
relationName: sRel.relationName,
|
|
599
|
+
relHash: sRel.relHash,
|
|
600
|
+
inputPattern: sInputPattern,
|
|
601
|
+
cacheDir,
|
|
602
|
+
sourceLabel: "@fraczak/k/Examples/arithmetics.k#s"
|
|
603
|
+
});
|
|
604
|
+
if (sLlvmCompiled.status === "ok") {
|
|
605
|
+
sLlvmServer = new PersistentExecutable(sLlvmCompiled.exePath);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
let sWasmServer = null;
|
|
610
|
+
if (runWasm) {
|
|
611
|
+
try {
|
|
612
|
+
const sWasmPath = path.join(cacheDir, "perf_int_wasm_s.wasm");
|
|
613
|
+
const sWasmExe = path.join(cacheDir, "perf_int_wasm_s.exe");
|
|
614
|
+
const wasmBuf = await compileWasmArtifactFromObject(sRel.object, { entry: "__main__" });
|
|
615
|
+
fs.writeFileSync(sWasmPath, wasmBuf);
|
|
616
|
+
const runBin = fileURLToPath(import.meta.resolve("../backends/wasm/bin/k-wasm-run.mjs"));
|
|
617
|
+
fs.writeFileSync(sWasmExe, `#!/bin/sh\nexec node "${runBin}" "${sWasmPath}" "$@"\n`, { mode: 0o755 });
|
|
618
|
+
sWasmServer = new PersistentExecutable(sWasmExe);
|
|
619
|
+
} catch (e) {
|
|
620
|
+
console.log("Failed to build Wasm executable for 's':", e.message);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
let sArm64Server = null;
|
|
625
|
+
if (runARM64) {
|
|
626
|
+
try {
|
|
627
|
+
const sArm64Exe = path.join(cacheDir, "perf_int_arm64_s");
|
|
628
|
+
compileARM64ArtifactFromObject(sRel.object, {
|
|
629
|
+
entry: "__main__",
|
|
630
|
+
outputPath: sArm64Exe,
|
|
631
|
+
optLevel: arm64OptLevel
|
|
632
|
+
});
|
|
633
|
+
sArm64Server = new PersistentExecutable(sArm64Exe);
|
|
634
|
+
} catch (e) {
|
|
635
|
+
console.log("Failed to build ARM64 executable for 's':", e.message);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const squaringLanes = [];
|
|
640
|
+
if (runBaselines) {
|
|
641
|
+
squaringLanes.push({ id: "jsAware", name: "Native JS (Aware)", type: "jsAware", memLabel: "heap" });
|
|
642
|
+
squaringLanes.push({ id: "jsFree", name: "Native JS (Free)", type: "jsFree", memLabel: "heap" });
|
|
643
|
+
squaringLanes.push({ id: "kvm", name: "kVM (Env-Free)", type: "kvm", memLabel: "heap" });
|
|
644
|
+
}
|
|
645
|
+
if (runLLVM && sLlvmServer) {
|
|
646
|
+
squaringLanes.push({ id: "llvm", name: llvmLaneName(), type: "persistent", server: sLlvmServer, memLabel: "peak RSS" });
|
|
647
|
+
}
|
|
648
|
+
if (runWasm && sWasmServer) {
|
|
649
|
+
squaringLanes.push({ id: "wasm", name: wasmLaneName(), type: "persistent", server: sWasmServer, memLabel: "peak RSS" });
|
|
650
|
+
}
|
|
651
|
+
if (runARM64 && sArm64Server) {
|
|
652
|
+
squaringLanes.push({ id: "arm64", name: arm64LaneName(arm64OptLevel), type: "persistent", server: sArm64Server, memLabel: "peak RSS" });
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const squaringResults = {};
|
|
656
|
+
for (const lane of squaringLanes) squaringResults[lane.id] = [];
|
|
657
|
+
const stepMetadata = [];
|
|
658
|
+
|
|
659
|
+
try {
|
|
660
|
+
// 1. Persistent child processes (ARM64, Wasm, LLVM)
|
|
661
|
+
for (const lane of squaringLanes.filter(l => l.type === "persistent")) {
|
|
662
|
+
console.log(`==> Running ${lane.name} squaring lane...`);
|
|
663
|
+
let curWire = sInitWire;
|
|
664
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
665
|
+
try {
|
|
666
|
+
const t0 = performance.now();
|
|
667
|
+
curWire = await lane.server.request(curWire);
|
|
668
|
+
const t1 = performance.now();
|
|
669
|
+
const timeMs = t1 - t0;
|
|
670
|
+
const mem = getProcessMemory(lane.server.child.pid);
|
|
671
|
+
squaringResults[lane.id].push({ status: "ok", timeMs, memMb: mem.peakRssMb, wire: curWire });
|
|
672
|
+
if (!stepMetadata[step - 1]) {
|
|
673
|
+
stepMetadata[step - 1] = {
|
|
674
|
+
wireLen: curWire.length,
|
|
675
|
+
bits: null,
|
|
676
|
+
canonicalWire: curWire
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
if (timeMs > squaringStepTimeoutMs) break;
|
|
680
|
+
} catch (e) {
|
|
681
|
+
squaringResults[lane.id].push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// 2. In-process baselines (JS Aware, JS Free, kVM)
|
|
688
|
+
if (runBaselines) {
|
|
689
|
+
// JS Aware
|
|
690
|
+
console.log("==> Running Native JS (Envelope-Aware) squaring lane...");
|
|
691
|
+
let curValAware = sInitialVal;
|
|
692
|
+
run.defs = state;
|
|
693
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
694
|
+
try {
|
|
695
|
+
const t0 = performance.now();
|
|
696
|
+
curValAware = run(codes.find, sRel.relDef.def, curValAware, sRel.relDef.typePatternGraph);
|
|
697
|
+
const t1 = performance.now();
|
|
698
|
+
const timeMs = t1 - t0;
|
|
699
|
+
const mem = getNodeMemory();
|
|
700
|
+
squaringResults.jsAware.push({ status: "ok", timeMs, memMb: mem.heapMb, value: curValAware });
|
|
701
|
+
if (stepMetadata[step - 1] && stepMetadata[step - 1].bits == null) {
|
|
702
|
+
stepMetadata[step - 1].bits = countBitsValue(curValAware);
|
|
703
|
+
}
|
|
704
|
+
if (timeMs > squaringStepTimeoutMs) break;
|
|
705
|
+
} catch (e) {
|
|
706
|
+
squaringResults.jsAware.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// JS Free
|
|
712
|
+
console.log("==> Running Native JS (Envelope-Free) squaring lane...");
|
|
713
|
+
let curValFree = sInitialVal;
|
|
714
|
+
run_converged.defs = state;
|
|
715
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
716
|
+
try {
|
|
717
|
+
const t0 = performance.now();
|
|
718
|
+
curValFree = run_converged(codes.find, sRel.relDef.def, curValFree, sRel.relDef.typePatternGraph);
|
|
719
|
+
const t1 = performance.now();
|
|
720
|
+
const timeMs = t1 - t0;
|
|
721
|
+
const mem = getNodeMemory();
|
|
722
|
+
squaringResults.jsFree.push({ status: "ok", timeMs, memMb: mem.heapMb, value: curValFree });
|
|
723
|
+
if (stepMetadata[step - 1] && stepMetadata[step - 1].bits == null) {
|
|
724
|
+
stepMetadata[step - 1].bits = countBitsValue(curValFree);
|
|
725
|
+
}
|
|
726
|
+
if (timeMs > squaringStepTimeoutMs) break;
|
|
727
|
+
} catch (e) {
|
|
728
|
+
squaringResults.jsFree.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
729
|
+
break;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// kVM
|
|
734
|
+
console.log("==> Running kVM Interpreter (Env-Free) squaring lane...");
|
|
735
|
+
let curValKvm = sInitialVal;
|
|
736
|
+
const kvmContext = { rels: state.rels, findCode: codes.find, options: { envelopeFree: true } };
|
|
737
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
738
|
+
try {
|
|
739
|
+
const t0 = performance.now();
|
|
740
|
+
curValKvm = executeKVM(sRel.kvmFunc, curValKvm, kvmContext);
|
|
741
|
+
const t1 = performance.now();
|
|
742
|
+
const timeMs = t1 - t0;
|
|
743
|
+
const mem = getNodeMemory();
|
|
744
|
+
squaringResults.kvm.push({ status: "ok", timeMs, memMb: mem.heapMb, value: curValKvm });
|
|
745
|
+
if (stepMetadata[step - 1] && stepMetadata[step - 1].bits == null) {
|
|
746
|
+
stepMetadata[step - 1].bits = countBitsValue(curValKvm);
|
|
747
|
+
}
|
|
748
|
+
if (timeMs > squaringStepTimeoutMs) break;
|
|
749
|
+
} catch (e) {
|
|
750
|
+
squaringResults.kvm.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
} finally {
|
|
757
|
+
if (sArm64Server) sArm64Server.close();
|
|
758
|
+
if (sWasmServer) sWasmServer.close();
|
|
759
|
+
if (sLlvmServer) sLlvmServer.close();
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// For any remaining steps without bit counts, decode canonicalWire safely to get bits:
|
|
763
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
764
|
+
const meta = stepMetadata[step - 1];
|
|
765
|
+
if (meta && meta.bits == null && meta.canonicalWire) {
|
|
766
|
+
try {
|
|
767
|
+
const decoded = decodeWire(meta.canonicalWire).value;
|
|
768
|
+
meta.bits = countBitsValue(decoded);
|
|
769
|
+
} catch {}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// Display results table
|
|
774
|
+
console.log("\n======================== SQUARING SCALING & MEMORY BENCHMARK ========================");
|
|
775
|
+
console.log(`Expression: s = {()x,()y}times; ${squaringInputText} ${"s ".repeat(squaringSteps).trim()}`);
|
|
776
|
+
console.log(`Initial: ${squaringInputText} (${countBitsValue(sInitialVal)} bits)`);
|
|
777
|
+
console.log("----------------------------------------------------------------------------------------------------------------------------------");
|
|
778
|
+
const headerCols = ["Step", "Bits", "Wire(B)", ...squaringLanes.map(l => `${l.name} (${l.memLabel})`)];
|
|
779
|
+
const colWidths = [4, 6, 7, ...squaringLanes.map(l => Math.max(22, l.name.length + l.memLabel.length + 5))];
|
|
780
|
+
console.log(headerCols.map((h, i) => i < 3 ? h.padStart(colWidths[i]) : h.padEnd(colWidths[i])).join(" | "));
|
|
781
|
+
console.log(colWidths.map(w => "-".repeat(w)).join("-|-"));
|
|
782
|
+
|
|
783
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
784
|
+
const meta = stepMetadata[step - 1] || { bits: "?", wireLen: "?" };
|
|
785
|
+
const cols = [
|
|
786
|
+
String(step).padStart(colWidths[0]),
|
|
787
|
+
String(meta.bits ?? "?").padStart(colWidths[1]),
|
|
788
|
+
String(meta.wireLen ?? "?").padStart(colWidths[2])
|
|
789
|
+
];
|
|
790
|
+
for (let li = 0; li < squaringLanes.length; li++) {
|
|
791
|
+
const lane = squaringLanes[li];
|
|
792
|
+
const res = squaringResults[lane.id][step - 1];
|
|
793
|
+
const w = colWidths[3 + li];
|
|
794
|
+
if (!res) {
|
|
795
|
+
cols.push("-".padEnd(w));
|
|
796
|
+
} else if (res.status === "ok") {
|
|
797
|
+
const t = res.timeMs < 10 ? `${res.timeMs.toFixed(2)}ms` : `${res.timeMs.toFixed(1)}ms`;
|
|
798
|
+
const m = res.memMb != null ? `${res.memMb}MB` : "";
|
|
799
|
+
cols.push(`${t} / ${m}`.padEnd(w));
|
|
800
|
+
} else {
|
|
801
|
+
let errText = res.error;
|
|
802
|
+
if (errText.includes("Maximum call stack")) {
|
|
803
|
+
errText = "Stack overflow (V8)";
|
|
804
|
+
} else if (errText.includes("status null") || errText.includes("failed with status")) {
|
|
805
|
+
errText = "Arena limit reached";
|
|
806
|
+
}
|
|
807
|
+
cols.push(errText.slice(0, w - 1).padEnd(w));
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
console.log(cols.join(" | "));
|
|
811
|
+
}
|
|
812
|
+
console.log("==================================================================================================================================");
|
|
813
|
+
|
|
814
|
+
// Conformance check across all computed steps
|
|
815
|
+
let squaringConformanceOk = true;
|
|
816
|
+
for (let step = 1; step <= squaringSteps; step++) {
|
|
817
|
+
const meta = stepMetadata[step - 1];
|
|
818
|
+
if (!meta?.canonicalWire) continue;
|
|
819
|
+
for (const lane of squaringLanes) {
|
|
820
|
+
const res = squaringResults[lane.id][step - 1];
|
|
821
|
+
if (res?.status === "ok") {
|
|
822
|
+
if (res.wire) {
|
|
823
|
+
if (Buffer.compare(res.wire, meta.canonicalWire) !== 0) {
|
|
824
|
+
squaringConformanceOk = false;
|
|
825
|
+
console.log(`Squaring conformance mismatch on step ${step} (${lane.name})`);
|
|
826
|
+
}
|
|
827
|
+
} else if (res.value) {
|
|
828
|
+
try {
|
|
829
|
+
const wireVal = decodeWire(meta.canonicalWire).value;
|
|
830
|
+
if (!safeDeepEqual(res.value, wireVal)) {
|
|
831
|
+
squaringConformanceOk = false;
|
|
832
|
+
console.log(`Squaring conformance mismatch on step ${step} (${lane.name})`);
|
|
833
|
+
}
|
|
834
|
+
} catch {}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
console.log("\nSquaring Performance & Scaling Summary:");
|
|
841
|
+
for (const lane of squaringLanes) {
|
|
842
|
+
const okSteps = squaringResults[lane.id].filter(r => r.status === "ok");
|
|
843
|
+
const maxStep = okSteps.length;
|
|
844
|
+
const lastOk = okSteps[okSteps.length - 1];
|
|
845
|
+
const peakMem = okSteps.reduce((max, r) => Math.max(max, r.memMb || 0), 0);
|
|
846
|
+
const failureReason = squaringResults[lane.id][maxStep]?.error;
|
|
847
|
+
let statusDetail = "";
|
|
848
|
+
if (failureReason) {
|
|
849
|
+
if (failureReason.includes("Maximum call stack")) statusDetail = " (V8 call stack overflow)";
|
|
850
|
+
else if (failureReason.includes("status null") || failureReason.includes("failed with status")) statusDetail = " (arena capacity limit reached)";
|
|
851
|
+
else statusDetail = ` (${failureReason.slice(0, 30)})`;
|
|
852
|
+
}
|
|
853
|
+
const timeStr = lastOk ? ` (step ${maxStep} in ${lastOk.timeMs < 10 ? lastOk.timeMs.toFixed(2) : lastOk.timeMs.toFixed(1)}ms)` : "";
|
|
854
|
+
console.log(` - ${lane.name.padEnd(32)}: reached Step ${String(maxStep).padStart(2)}/${squaringSteps}${timeStr}, peak ${lane.memLabel}: ${peakMem}MB${statusDetail}`);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
if (squaringConformanceOk) {
|
|
858
|
+
console.log("\nSquaring Conformance: All completed squaring steps match expected values across all backends!\n");
|
|
859
|
+
} else {
|
|
860
|
+
console.log("\nSquaring Conformance: Warning, some step outputs differed between backends!\n");
|
|
861
|
+
process.exitCode = 1;
|
|
862
|
+
}
|
package/scripts/run-tests.mjs
CHANGED
|
@@ -26,6 +26,7 @@ const tests = [
|
|
|
26
26
|
["conformance/run.mjs", node, ["conformance/run.mjs"]],
|
|
27
27
|
["tests/test-repl.mjs", node, ["tests/test-repl.mjs"]],
|
|
28
28
|
["tests/test-repl-html.mjs", node, ["tests/test-repl-html.mjs"]],
|
|
29
|
+
["tests/test-tree-view.mjs", node, ["tests/test-tree-view.mjs"]],
|
|
29
30
|
["tests/test-kvm.mjs", node, ["tests/test-kvm.mjs"]],
|
|
30
31
|
["tests/test-kvm-polymorphic.mjs", node, ["tests/test-kvm-polymorphic.mjs"]],
|
|
31
32
|
["tests/test-arithmetics.mjs", node, ["tests/test-arithmetics.mjs"]],
|