@fraczak/k 6.4.0 → 6.5.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/CHANGELOG.md +17 -0
- package/Examples/arithmetics.k +86 -63
- package/Value.mjs +8 -2
- package/backends/wasm/TECHNICAL-DEBT.md +11 -13
- package/backends/wasm/runtime.wat +375 -2
- package/backends/wasm/src/kvm2wasm.mjs +36 -0
- package/backends/wasm/src/wasm.mjs +48 -4
- package/backends/wasm/tests/test-wasm.mjs +8 -0
- package/browser/vfs-data.mjs +3 -3
- package/codecs/runtime/show-value.mjs +10 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [6.5.0] — 2026-09-22 — Tail-loop arena compaction & OOM diagnostics
|
|
11
|
+
|
|
12
|
+
### WebAssembly Backend
|
|
13
|
+
|
|
14
|
+
- Added threshold-based arena compaction in self-tail-recursive loops to reclaim dead scratch space, allowing deep computations (e.g. multi-thousand-bit arithmetic) without exhausting linear memory.
|
|
15
|
+
- Added explicit Out-Of-Memory error reporting (`OutOfMemoryError`), tracking allocation failure and 32-bit offset overflow via exported runtime globals.
|
|
16
|
+
|
|
17
|
+
### Runtime & REPL
|
|
18
|
+
|
|
19
|
+
- Replaced recursive traversal in `codecs/runtime/show-value.mjs` and `Value.mjs` with iterative loops, eliminating `Maximum call stack size exceeded` errors when rendering deep variant chains.
|
|
20
|
+
|
|
21
|
+
### Examples
|
|
22
|
+
|
|
23
|
+
- Converted arithmetic helper functions in `Examples/arithmetics.k` to tail-recursive relations.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
10
27
|
## [6.2.3] — 2026-06-03 — Unified k compiler output
|
|
11
28
|
|
|
12
29
|
### Toolchain
|
package/Examples/arithmetics.k
CHANGED
|
@@ -39,22 +39,33 @@ empty? = {empty /yes if, () then} .then;
|
|
|
39
39
|
concat = ${bits x, bits y} {.x inv x, .y y} concat_;
|
|
40
40
|
concat_ = ${bits x, bits y}
|
|
41
41
|
<
|
|
42
|
-
{.x
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
{.x empty? if, .y then} .then,
|
|
43
|
+
<
|
|
44
|
+
{.x /0 x, .y |0 y},
|
|
45
|
+
{.x /1 x, .y |1 y}
|
|
46
|
+
> concat_
|
|
45
47
|
>;
|
|
46
48
|
inv = {()x, 0b r} _inv;
|
|
47
|
-
_inv = ${bits x, bits r}
|
|
49
|
+
_inv = ${bits x, bits r}
|
|
50
|
+
<
|
|
51
|
+
{.x empty? if, .r then} .then,
|
|
52
|
+
<
|
|
53
|
+
{.x /0 x, .r |0 r},
|
|
54
|
+
{.x /1 x, .r |1 r}
|
|
55
|
+
> _inv
|
|
56
|
+
>;
|
|
48
57
|
|
|
49
58
|
inOrder = ${bits x, bits y} {.x inv x, .y inv y, yes inOrder} inOrder_;
|
|
50
59
|
inOrder_ = ${bits x, bits y, yes_no inOrder}
|
|
51
60
|
<
|
|
52
|
-
{.x
|
|
53
|
-
{.x /
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
{.x </0,/1> if1, .y /_ if2, .inOrder swap_yes_no then} .then,
|
|
62
|
+
{.x /_ if, .inOrder then} .then,
|
|
63
|
+
<
|
|
64
|
+
{.x /0 x, .y /0 y, .inOrder inOrder},
|
|
65
|
+
{.x /1 x, .y /1 y, .inOrder inOrder},
|
|
66
|
+
{.x /0 x, .y /1 y, .inOrder inOrder},
|
|
67
|
+
{.x /1 y, .y /0 x, .inOrder swap_yes_no inOrder}
|
|
68
|
+
> inOrder_
|
|
58
69
|
>;
|
|
59
70
|
|
|
60
71
|
GT = {inOrder /no if, () then} . then;
|
|
@@ -63,18 +74,21 @@ EQ = GT {.x y, .y x} GT;
|
|
|
63
74
|
|
|
64
75
|
toBitList = {inv x, nil r} _toBitList;
|
|
65
76
|
_toBitList = <
|
|
66
|
-
{.x empty? if, .r then
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
{.x empty? if, .r then}.then,
|
|
78
|
+
<
|
|
79
|
+
{.x /0 x, {0 car, .r cdr} cons r},
|
|
80
|
+
{.x /1 x, {1 car, .r cdr} cons r}
|
|
81
|
+
> _toBitList
|
|
69
82
|
>;
|
|
70
|
-
fromBitList =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
>
|
|
83
|
+
fromBitList = {() l, 0b r} _fromBitList inv $bits;
|
|
84
|
+
_fromBitList =
|
|
85
|
+
<
|
|
86
|
+
{.l nil? if, .r then} .then,
|
|
87
|
+
<
|
|
88
|
+
{.l car /0 if, {.l cdr l, .r |0 r} then} .then,
|
|
89
|
+
{.l car /1 if, {.l cdr l, .r |1 r} then} .then
|
|
90
|
+
> _fromBitList
|
|
91
|
+
>;
|
|
78
92
|
|
|
79
93
|
--------------------------------------
|
|
80
94
|
$ int = < bits '+', bits '-' >;
|
|
@@ -99,9 +113,9 @@ inc = $int <
|
|
|
99
113
|
> $int;
|
|
100
114
|
|
|
101
115
|
_inc = ${bits x, bits y} <
|
|
116
|
+
{.x empty? if, .y |1 then} .then,
|
|
102
117
|
{.x /0 x, .y |1 y} concat_,
|
|
103
|
-
{.x /1 x, .y |0 y} _inc
|
|
104
|
-
.y |1
|
|
118
|
+
{.x /1 x, .y |0 y} _inc
|
|
105
119
|
>;
|
|
106
120
|
|
|
107
121
|
dec = $int <
|
|
@@ -149,38 +163,44 @@ _plus_ =
|
|
|
149
163
|
{.x empty? if, .y empty? and, {.r x, 1 r} _inv then} .then,
|
|
150
164
|
{.x empty? if, .c/0 and, {.r x, .y r} _inv then} .then,
|
|
151
165
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
166
|
+
<
|
|
167
|
+
{.x y, .y empty? x, .c c, .r r},
|
|
168
|
+
{<.c/0 0, 1> x, .y y, .x empty? c0 c, .r r},
|
|
155
169
|
|
|
156
|
-
|
|
157
|
-
|
|
170
|
+
{.x /0 x, .y /0 y, .c/0 c0 c, .r |0 r},
|
|
171
|
+
{.x /0 x, .y /0 y, .c/1 c0 c, .r |1 r},
|
|
158
172
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
173
|
+
{.x /0 x, .y /1 y, .c/0 c0 c, .r |1 r},
|
|
174
|
+
{.x /0 x, .y /1 y, .c/1 c1 c, .r |0 r},
|
|
175
|
+
{.x /1 x, .y /0 y, .c/0 c0 c, .r |1 r},
|
|
176
|
+
{.x /1 x, .y /0 y, .c/1 c1 c, .r |0 r},
|
|
163
177
|
|
|
164
|
-
|
|
165
|
-
|
|
178
|
+
{.x /1 x, .y /1 y, .c/0 c1 c, .r |0 r},
|
|
179
|
+
{.x /1 x, .y /1 y, .c/1 c1 c, .r |1 r}
|
|
180
|
+
> _plus_
|
|
166
181
|
>
|
|
167
182
|
;
|
|
168
183
|
|
|
169
|
-
_minus =
|
|
170
|
-
|
|
171
|
-
|
|
184
|
+
_minus = {.x x, .y y, .c c, 0b r} _minus_;
|
|
185
|
+
|
|
186
|
+
_minus_ =
|
|
187
|
+
-- least significant bit first substraction with carry
|
|
188
|
+
${bits x, bits y, carry c, bits r}
|
|
172
189
|
<
|
|
173
|
-
{.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
190
|
+
{.y empty? if, .c/0 and, {.r x, .x r} _inv then} .then,
|
|
191
|
+
<
|
|
192
|
+
{.x/0 x, .y empty? y, .c/1 c1 c, .r |1 r},
|
|
193
|
+
{.x/1 x, .y empty? y, .c/1 c0 c, .r |0 r},
|
|
194
|
+
|
|
195
|
+
{.x/0 x, .y/0 y, .c/0 c0 c, .r |0 r},
|
|
196
|
+
{.x/0 x, .y/0 y, .c/1 c1 c, .r |1 r},
|
|
197
|
+
{.x/0 x, .y/1 y, .c/0 c1 c, .r |1 r},
|
|
198
|
+
{.x/0 x, .y/1 y, .c/1 c1 c, .r |0 r},
|
|
199
|
+
{.x/1 x, .y/0 y, .c/0 c0 c, .r |1 r},
|
|
200
|
+
{.x/1 x, .y/0 y, .c/1 c0 c, .r |0 r},
|
|
201
|
+
{.x/1 x, .y/1 y, .c/0 c0 c, .r |0 r},
|
|
202
|
+
{.x/1 x, .y/1 y, .c/1 c1 c, .r |1 r}
|
|
203
|
+
> _minus_
|
|
184
204
|
>
|
|
185
205
|
;
|
|
186
206
|
|
|
@@ -212,16 +232,18 @@ _times =
|
|
|
212
232
|
${bits result, bits _x, bits y}
|
|
213
233
|
<
|
|
214
234
|
{ .y empty? if, .result then } .then,
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
{
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
235
|
+
<
|
|
236
|
+
{
|
|
237
|
+
.result |0 result,
|
|
238
|
+
._x _x,
|
|
239
|
+
.y /0 y
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
{._x x, .result |0 y, c0 c } _plus result,
|
|
243
|
+
._x _x,
|
|
244
|
+
.y /1 y
|
|
245
|
+
}
|
|
246
|
+
> _times
|
|
225
247
|
>
|
|
226
248
|
;
|
|
227
249
|
|
|
@@ -327,17 +349,18 @@ lcm =
|
|
|
327
349
|
{{.x int x, .y int y} times x, gcd int y} div .div abs
|
|
328
350
|
;
|
|
329
351
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
352
|
+
factorial = {()n, 1 int acc} factorial_;
|
|
353
|
+
factorial_ = <
|
|
354
|
+
{.n zero_int? if, .acc then} .then,
|
|
355
|
+
{.n pos? dec n, {.n x, .acc y} times acc} factorial_
|
|
333
356
|
>;
|
|
334
357
|
|
|
335
358
|
fib_ =
|
|
336
359
|
<
|
|
360
|
+
{.n int zero_int? if, ."fib(n),fib(n.1)" then} .then,
|
|
337
361
|
{ .n int dec /"+" n,
|
|
338
362
|
."fib(n),fib(n.1)" {plus x, .x y} "fib(n),fib(n.1)"
|
|
339
|
-
} fib_
|
|
340
|
-
."fib(n),fib(n.1)"
|
|
363
|
+
} fib_
|
|
341
364
|
>
|
|
342
365
|
;
|
|
343
366
|
|
package/Value.mjs
CHANGED
|
@@ -176,8 +176,14 @@ class Value {
|
|
|
176
176
|
|
|
177
177
|
toString() {
|
|
178
178
|
if (isVariant(this)) {
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
const tags = [];
|
|
180
|
+
let curr = this;
|
|
181
|
+
while (isVariant(curr)) {
|
|
182
|
+
tags.push(pLabel(curr.tag));
|
|
183
|
+
curr = curr.value;
|
|
184
|
+
}
|
|
185
|
+
const base = curr ? curr.toString() : "";
|
|
186
|
+
return tags.length === 0 ? base : `${base}|${tags.reverse().join("|")}`;
|
|
181
187
|
}
|
|
182
188
|
if (isProduct(this)) {
|
|
183
189
|
return `{${Object.entries(this.product).map(([k, v]) => `${JSON.stringify(k)}:${v.toString()}`).join(',')}}`;
|
|
@@ -31,12 +31,18 @@ paths do not reclaim temporary values until the whole run ends.
|
|
|
31
31
|
|
|
32
32
|
Tail-call lowering now avoids allocating replacement input products for some
|
|
33
33
|
self-tail-recursive loops by keeping top-level product fields in Wasm locals.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
and
|
|
34
|
+
Furthermore, self-tail-recursive loops now incorporate threshold-based arena
|
|
35
|
+
compaction: when arena growth within a tail loop exceeds 1 MB, live accumulator
|
|
36
|
+
and argument graphs are compacted down to the loop entry mark using an iterative
|
|
37
|
+
staging-and-copy mechanism in `runtime.wat`, freeing dead scratch data while
|
|
38
|
+
preserving performance for hot short-lived loops.
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
Additionally, linear memory exhaustion and 32-bit unsigned offset overflow are
|
|
41
|
+
now tracked explicitly via exported flags (`oom_flag`, `oom_size`) in `runtime.wat`,
|
|
42
|
+
which the JS runtime intercepts to throw a descriptive `OutOfMemoryError` instead
|
|
43
|
+
of an untyped `unreachable` trap.
|
|
44
|
+
|
|
45
|
+
Remaining memory-management targets:
|
|
40
46
|
|
|
41
47
|
- Add allocation profiling that attributes arena growth to generated relations
|
|
42
48
|
and static allocation sites. Large regressions should be visible without
|
|
@@ -47,14 +53,6 @@ Future memory-management targets:
|
|
|
47
53
|
- Introduce safe lifetime boundaries for successful non-tail calls. The runtime
|
|
48
54
|
needs a way to reclaim scratch values after a callee result has been consumed
|
|
49
55
|
without copying large live values on every call.
|
|
50
|
-
- Make arena checkpoints part of code generation decisions, not just union
|
|
51
|
-
failure handling. Tail loops should be able to reset dead scratch space while
|
|
52
|
-
preserving the next live input/result.
|
|
53
56
|
- Keep unsigned pointer handling explicit. Wasm arena addresses are `i32`
|
|
54
57
|
offsets, and JavaScript sees exported `i32` values as signed numbers, so all
|
|
55
58
|
JS-side arena offsets must be normalized before `DataView` access.
|
|
56
|
-
|
|
57
|
-
The preferred direction is performance first: reduce allocation volume in hot
|
|
58
|
-
generated loops before adding a general garbage collector or compaction pass.
|
|
59
|
-
Copying live arena values can control memory, but it risks slowing exactly the
|
|
60
|
-
large recursive arithmetic workloads that motivated tail-call lowering.
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
(memory (export "memory") 1)
|
|
3
3
|
|
|
4
4
|
;; Start allocation at byte offset 1024 to leave a safety margin and avoid returning 0 (nullptr)
|
|
5
|
-
(global $arena_free (mut i32) (i32.const 1024))
|
|
5
|
+
(global $arena_free (export "arena_free") (mut i32) (i32.const 1024))
|
|
6
6
|
;; Maximum address of the currently allocated pages (1 page = 64KB)
|
|
7
|
-
(global $arena_max (mut i32) (i32.const 65536))
|
|
7
|
+
(global $arena_max (export "arena_max") (mut i32) (i32.const 65536))
|
|
8
|
+
;; Out-of-memory status flags
|
|
9
|
+
(global $oom_flag (export "oom_flag") (mut i32) (i32.const 0))
|
|
10
|
+
(global $oom_size (export "oom_size") (mut i32) (i32.const 0))
|
|
8
11
|
|
|
9
12
|
;; Bump allocator function: aligns allocation size to 8 bytes, grows memory if needed, and returns the pointer
|
|
10
13
|
(func $alloc (export "alloc") (param $size i32) (result i32)
|
|
@@ -13,6 +16,18 @@
|
|
|
13
16
|
(local $pages_needed i32)
|
|
14
17
|
(local $grow_res i32)
|
|
15
18
|
|
|
19
|
+
;; Check if size exceeds maximum allocatable size
|
|
20
|
+
local.get $size
|
|
21
|
+
i32.const -8
|
|
22
|
+
i32.gt_u
|
|
23
|
+
if
|
|
24
|
+
i32.const 1
|
|
25
|
+
global.set $oom_flag
|
|
26
|
+
local.get $size
|
|
27
|
+
global.set $oom_size
|
|
28
|
+
unreachable
|
|
29
|
+
end
|
|
30
|
+
|
|
16
31
|
;; 1. Align size to 8 bytes: size = (size + 7) & ~8
|
|
17
32
|
local.get $size
|
|
18
33
|
i32.const 7
|
|
@@ -31,6 +46,18 @@
|
|
|
31
46
|
i32.add
|
|
32
47
|
local.set $new_free
|
|
33
48
|
|
|
49
|
+
;; Check for 32-bit unsigned overflow
|
|
50
|
+
local.get $new_free
|
|
51
|
+
local.get $old_free
|
|
52
|
+
i32.lt_u
|
|
53
|
+
if
|
|
54
|
+
i32.const 1
|
|
55
|
+
global.set $oom_flag
|
|
56
|
+
local.get $size
|
|
57
|
+
global.set $oom_size
|
|
58
|
+
unreachable
|
|
59
|
+
end
|
|
60
|
+
|
|
34
61
|
;; 2. Check if we exceed current memory size
|
|
35
62
|
local.get $new_free
|
|
36
63
|
global.get $arena_max
|
|
@@ -56,6 +83,10 @@
|
|
|
56
83
|
i32.const -1
|
|
57
84
|
i32.eq
|
|
58
85
|
if
|
|
86
|
+
i32.const 1
|
|
87
|
+
global.set $oom_flag
|
|
88
|
+
local.get $size
|
|
89
|
+
global.set $oom_size
|
|
59
90
|
unreachable
|
|
60
91
|
end
|
|
61
92
|
|
|
@@ -99,4 +130,346 @@
|
|
|
99
130
|
local.get $mark
|
|
100
131
|
global.set $arena_free
|
|
101
132
|
)
|
|
133
|
+
|
|
134
|
+
;; Compaction staging globals
|
|
135
|
+
(global $compact_buf_start (mut i32) (i32.const 0))
|
|
136
|
+
(global $compact_buf_free (mut i32) (i32.const 0))
|
|
137
|
+
|
|
138
|
+
;; Ensure memory capacity in staging buffer
|
|
139
|
+
(func $ensure_buf_capacity (param $size i32)
|
|
140
|
+
(local $needed i32)
|
|
141
|
+
(local $pages i32)
|
|
142
|
+
(local $grow_res i32)
|
|
143
|
+
|
|
144
|
+
global.get $compact_buf_free
|
|
145
|
+
local.get $size
|
|
146
|
+
i32.add
|
|
147
|
+
local.set $needed
|
|
148
|
+
|
|
149
|
+
;; Check for 32-bit overflow
|
|
150
|
+
local.get $needed
|
|
151
|
+
global.get $compact_buf_free
|
|
152
|
+
i32.lt_u
|
|
153
|
+
if
|
|
154
|
+
i32.const 1
|
|
155
|
+
global.set $oom_flag
|
|
156
|
+
local.get $size
|
|
157
|
+
global.set $oom_size
|
|
158
|
+
unreachable
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
local.get $needed
|
|
162
|
+
global.get $arena_max
|
|
163
|
+
i32.gt_u
|
|
164
|
+
if
|
|
165
|
+
local.get $needed
|
|
166
|
+
global.get $arena_max
|
|
167
|
+
i32.sub
|
|
168
|
+
i32.const 65535
|
|
169
|
+
i32.add
|
|
170
|
+
i32.const 65536
|
|
171
|
+
i32.div_u
|
|
172
|
+
local.set $pages
|
|
173
|
+
|
|
174
|
+
local.get $pages
|
|
175
|
+
memory.grow
|
|
176
|
+
local.set $grow_res
|
|
177
|
+
|
|
178
|
+
local.get $grow_res
|
|
179
|
+
i32.const -1
|
|
180
|
+
i32.eq
|
|
181
|
+
if
|
|
182
|
+
i32.const 1
|
|
183
|
+
global.set $oom_flag
|
|
184
|
+
local.get $size
|
|
185
|
+
global.set $oom_size
|
|
186
|
+
unreachable
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
global.get $arena_max
|
|
190
|
+
local.get $pages
|
|
191
|
+
i32.const 65536
|
|
192
|
+
i32.mul
|
|
193
|
+
i32.add
|
|
194
|
+
global.set $arena_max
|
|
195
|
+
end
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
;; Initialize compaction staging buffer at current arena top
|
|
199
|
+
(func $compact_start (export "compact_start")
|
|
200
|
+
global.get $arena_free
|
|
201
|
+
global.set $compact_buf_start
|
|
202
|
+
global.get $arena_free
|
|
203
|
+
global.set $compact_buf_free
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
;; Compact an object graph reachable from $root down toward $mark
|
|
207
|
+
(func $compact_obj (export "compact_obj") (param $root i32) (param $mark i32) (result i32)
|
|
208
|
+
(local $curr i32)
|
|
209
|
+
(local $first_final i32)
|
|
210
|
+
(local $prev_dest i32)
|
|
211
|
+
(local $dest i32)
|
|
212
|
+
(local $final_ptr i32)
|
|
213
|
+
(local $tag i32)
|
|
214
|
+
(local $payload i32)
|
|
215
|
+
(local $size i32)
|
|
216
|
+
(local $alignedSize i32)
|
|
217
|
+
(local $N i32)
|
|
218
|
+
(local $i i32)
|
|
219
|
+
(local $offsetVal i32)
|
|
220
|
+
(local $fieldPtr i32)
|
|
221
|
+
(local $child_final i32)
|
|
222
|
+
|
|
223
|
+
local.get $root
|
|
224
|
+
local.set $curr
|
|
225
|
+
i32.const 0
|
|
226
|
+
local.set $prev_dest
|
|
227
|
+
|
|
228
|
+
(block $break_chain
|
|
229
|
+
(loop $chain_loop
|
|
230
|
+
;; 1. Check null / empty
|
|
231
|
+
local.get $curr
|
|
232
|
+
i32.eqz
|
|
233
|
+
if
|
|
234
|
+
local.get $prev_dest
|
|
235
|
+
if
|
|
236
|
+
local.get $prev_dest
|
|
237
|
+
i32.const 0
|
|
238
|
+
i32.store offset=8
|
|
239
|
+
end
|
|
240
|
+
br $break_chain
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
;; 2. Check if < mark
|
|
244
|
+
local.get $curr
|
|
245
|
+
local.get $mark
|
|
246
|
+
i32.lt_u
|
|
247
|
+
if
|
|
248
|
+
local.get $prev_dest
|
|
249
|
+
if
|
|
250
|
+
local.get $prev_dest
|
|
251
|
+
local.get $curr
|
|
252
|
+
i32.store offset=8
|
|
253
|
+
end
|
|
254
|
+
local.get $first_final
|
|
255
|
+
i32.eqz
|
|
256
|
+
if
|
|
257
|
+
local.get $curr
|
|
258
|
+
local.set $first_final
|
|
259
|
+
end
|
|
260
|
+
br $break_chain
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
;; 3. Check if forwarded
|
|
264
|
+
local.get $curr
|
|
265
|
+
i32.load offset=0
|
|
266
|
+
local.set $size
|
|
267
|
+
|
|
268
|
+
local.get $size
|
|
269
|
+
i32.const -1
|
|
270
|
+
i32.eq
|
|
271
|
+
if
|
|
272
|
+
local.get $curr
|
|
273
|
+
i32.load offset=4
|
|
274
|
+
local.set $final_ptr
|
|
275
|
+
local.get $prev_dest
|
|
276
|
+
if
|
|
277
|
+
local.get $prev_dest
|
|
278
|
+
local.get $final_ptr
|
|
279
|
+
i32.store offset=8
|
|
280
|
+
end
|
|
281
|
+
local.get $first_final
|
|
282
|
+
i32.eqz
|
|
283
|
+
if
|
|
284
|
+
local.get $final_ptr
|
|
285
|
+
local.set $first_final
|
|
286
|
+
end
|
|
287
|
+
br $break_chain
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
;; 4. Compute final_ptr = mark + (compact_buf_free - compact_buf_start)
|
|
291
|
+
local.get $mark
|
|
292
|
+
global.get $compact_buf_free
|
|
293
|
+
global.get $compact_buf_start
|
|
294
|
+
i32.sub
|
|
295
|
+
i32.add
|
|
296
|
+
local.set $final_ptr
|
|
297
|
+
|
|
298
|
+
local.get $first_final
|
|
299
|
+
i32.eqz
|
|
300
|
+
if
|
|
301
|
+
local.get $final_ptr
|
|
302
|
+
local.set $first_final
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
;; If prev_dest != 0, patch its payload to final_ptr
|
|
306
|
+
local.get $prev_dest
|
|
307
|
+
if
|
|
308
|
+
local.get $prev_dest
|
|
309
|
+
local.get $final_ptr
|
|
310
|
+
i32.store offset=8
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
;; 5. Check if Variant (size == 12)
|
|
314
|
+
local.get $size
|
|
315
|
+
i32.const 12
|
|
316
|
+
i32.eq
|
|
317
|
+
if
|
|
318
|
+
i32.const 16
|
|
319
|
+
call $ensure_buf_capacity
|
|
320
|
+
|
|
321
|
+
local.get $curr
|
|
322
|
+
i32.load offset=4
|
|
323
|
+
local.set $tag
|
|
324
|
+
local.get $curr
|
|
325
|
+
i32.load offset=8
|
|
326
|
+
local.set $payload
|
|
327
|
+
|
|
328
|
+
local.get $curr
|
|
329
|
+
i32.const -1
|
|
330
|
+
i32.store offset=0
|
|
331
|
+
local.get $curr
|
|
332
|
+
local.get $final_ptr
|
|
333
|
+
i32.store offset=4
|
|
334
|
+
|
|
335
|
+
global.get $compact_buf_free
|
|
336
|
+
local.set $dest
|
|
337
|
+
global.get $compact_buf_free
|
|
338
|
+
i32.const 16
|
|
339
|
+
i32.add
|
|
340
|
+
global.set $compact_buf_free
|
|
341
|
+
|
|
342
|
+
local.get $dest
|
|
343
|
+
i32.const 12
|
|
344
|
+
i32.store offset=0
|
|
345
|
+
local.get $dest
|
|
346
|
+
local.get $tag
|
|
347
|
+
i32.store offset=4
|
|
348
|
+
|
|
349
|
+
local.get $dest
|
|
350
|
+
local.set $prev_dest
|
|
351
|
+
local.get $payload
|
|
352
|
+
local.set $curr
|
|
353
|
+
br $chain_loop
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
;; 6. Product handling (size != 12)
|
|
357
|
+
local.get $size
|
|
358
|
+
i32.const 7
|
|
359
|
+
i32.add
|
|
360
|
+
i32.const -8
|
|
361
|
+
i32.and
|
|
362
|
+
local.set $alignedSize
|
|
363
|
+
|
|
364
|
+
local.get $alignedSize
|
|
365
|
+
call $ensure_buf_capacity
|
|
366
|
+
|
|
367
|
+
local.get $curr
|
|
368
|
+
i32.load offset=4
|
|
369
|
+
local.set $N
|
|
370
|
+
|
|
371
|
+
local.get $curr
|
|
372
|
+
i32.const -1
|
|
373
|
+
i32.store offset=0
|
|
374
|
+
local.get $curr
|
|
375
|
+
local.get $final_ptr
|
|
376
|
+
i32.store offset=4
|
|
377
|
+
|
|
378
|
+
global.get $compact_buf_free
|
|
379
|
+
local.set $dest
|
|
380
|
+
global.get $compact_buf_free
|
|
381
|
+
local.get $alignedSize
|
|
382
|
+
i32.add
|
|
383
|
+
global.set $compact_buf_free
|
|
384
|
+
|
|
385
|
+
local.get $dest
|
|
386
|
+
local.get $size
|
|
387
|
+
i32.store offset=0
|
|
388
|
+
local.get $dest
|
|
389
|
+
local.get $N
|
|
390
|
+
i32.store offset=4
|
|
391
|
+
|
|
392
|
+
i32.const 0
|
|
393
|
+
local.set $i
|
|
394
|
+
(block $break_prod
|
|
395
|
+
(loop $loop_prod
|
|
396
|
+
local.get $i
|
|
397
|
+
local.get $N
|
|
398
|
+
i32.ge_u
|
|
399
|
+
br_if $break_prod
|
|
400
|
+
|
|
401
|
+
local.get $curr
|
|
402
|
+
i32.const 8
|
|
403
|
+
local.get $i
|
|
404
|
+
i32.const 4
|
|
405
|
+
i32.mul
|
|
406
|
+
i32.add
|
|
407
|
+
i32.add
|
|
408
|
+
i32.load
|
|
409
|
+
local.set $offsetVal
|
|
410
|
+
|
|
411
|
+
local.get $dest
|
|
412
|
+
i32.const 8
|
|
413
|
+
local.get $i
|
|
414
|
+
i32.const 4
|
|
415
|
+
i32.mul
|
|
416
|
+
i32.add
|
|
417
|
+
i32.add
|
|
418
|
+
local.get $offsetVal
|
|
419
|
+
i32.store
|
|
420
|
+
|
|
421
|
+
local.get $curr
|
|
422
|
+
local.get $offsetVal
|
|
423
|
+
i32.add
|
|
424
|
+
i32.load
|
|
425
|
+
local.set $fieldPtr
|
|
426
|
+
|
|
427
|
+
local.get $fieldPtr
|
|
428
|
+
local.get $mark
|
|
429
|
+
call $compact_obj
|
|
430
|
+
local.set $child_final
|
|
431
|
+
|
|
432
|
+
local.get $dest
|
|
433
|
+
local.get $offsetVal
|
|
434
|
+
i32.add
|
|
435
|
+
local.get $child_final
|
|
436
|
+
i32.store
|
|
437
|
+
|
|
438
|
+
local.get $i
|
|
439
|
+
i32.const 1
|
|
440
|
+
i32.add
|
|
441
|
+
local.set $i
|
|
442
|
+
br $loop_prod
|
|
443
|
+
)
|
|
444
|
+
)
|
|
445
|
+
br $break_chain
|
|
446
|
+
)
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
local.get $first_final
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
;; Finalize compaction by copying staging buffer into mark and resetting arena_free
|
|
453
|
+
(func $compact_finish (export "compact_finish") (param $mark i32)
|
|
454
|
+
(local $total_size i32)
|
|
455
|
+
global.get $compact_buf_free
|
|
456
|
+
global.get $compact_buf_start
|
|
457
|
+
i32.sub
|
|
458
|
+
local.set $total_size
|
|
459
|
+
|
|
460
|
+
local.get $total_size
|
|
461
|
+
i32.const 0
|
|
462
|
+
i32.gt_u
|
|
463
|
+
if
|
|
464
|
+
local.get $mark
|
|
465
|
+
global.get $compact_buf_start
|
|
466
|
+
local.get $total_size
|
|
467
|
+
memory.copy
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
local.get $mark
|
|
471
|
+
local.get $total_size
|
|
472
|
+
i32.add
|
|
473
|
+
global.set $arena_free
|
|
474
|
+
)
|
|
102
475
|
)
|