toilscript 0.0.1 → 0.1.1
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/LICENSE +201 -201
- package/NOTICE +94 -94
- package/README.md +101 -114
- package/bin/asc.js +0 -0
- package/bin/asinit.js +6 -6
- package/dist/asc.generated.d.ts +10027 -0
- package/dist/asc.js +24474 -0
- package/dist/asc.js.map +7 -0
- package/dist/importmap.json +9 -0
- package/dist/toilscript.generated.d.ts +11242 -0
- package/dist/toilscript.js +337 -0
- package/dist/toilscript.js.map +7 -0
- package/dist/web.js +22 -0
- package/lib/binaryen.d.ts +2 -2
- package/lib/binaryen.js +2 -2
- package/package.json +115 -114
- package/std/README.md +6 -6
- package/std/assembly/array.ts +550 -550
- package/std/assembly/arraybuffer.ts +77 -77
- package/std/assembly/atomics.ts +127 -127
- package/std/assembly/bindings/asyncify.ts +16 -16
- package/std/assembly/bindings/dom.ts +291 -291
- package/std/assembly/bindings/node.ts +6 -6
- package/std/assembly/bitflags.ts +53 -53
- package/std/assembly/builtins.ts +2650 -2650
- package/std/assembly/byteslice.ts +177 -177
- package/std/assembly/compat.ts +2 -2
- package/std/assembly/console.ts +42 -42
- package/std/assembly/crypto.ts +9 -9
- package/std/assembly/dataview.ts +181 -181
- package/std/assembly/date.ts +375 -375
- package/std/assembly/diagnostics.ts +11 -11
- package/std/assembly/encoding.ts +151 -151
- package/std/assembly/endian.ts +45 -45
- package/std/assembly/error.ts +44 -44
- package/std/assembly/fixedarray.ts +173 -173
- package/std/assembly/fixedmap.ts +326 -326
- package/std/assembly/fixedset.ts +275 -275
- package/std/assembly/function.ts +42 -42
- package/std/assembly/index.d.ts +2892 -2891
- package/std/assembly/iterator.ts +35 -35
- package/std/assembly/map.ts +269 -269
- package/std/assembly/math.ts +3289 -3289
- package/std/assembly/memory.ts +123 -123
- package/std/assembly/number.ts +388 -388
- package/std/assembly/object.ts +36 -36
- package/std/assembly/performance.ts +9 -9
- package/std/assembly/pointer.ts +80 -80
- package/std/assembly/polyfills.ts +27 -27
- package/std/assembly/process.ts +50 -50
- package/std/assembly/reference.ts +48 -48
- package/std/assembly/regexp.ts +12 -12
- package/std/assembly/rt/README.md +83 -83
- package/std/assembly/rt/common.ts +81 -81
- package/std/assembly/rt/index-incremental.ts +2 -2
- package/std/assembly/rt/index-memory.ts +1 -1
- package/std/assembly/rt/index-minimal.ts +2 -2
- package/std/assembly/rt/index-stub.ts +1 -1
- package/std/assembly/rt/index.d.ts +37 -37
- package/std/assembly/rt/itcms.ts +419 -419
- package/std/assembly/rt/memory-runtime.ts +94 -94
- package/std/assembly/rt/rtrace.ts +15 -15
- package/std/assembly/rt/stub.ts +133 -133
- package/std/assembly/rt/tcms.ts +254 -254
- package/std/assembly/rt/tlsf.ts +592 -592
- package/std/assembly/rt.ts +90 -90
- package/std/assembly/set.ts +225 -225
- package/std/assembly/shared/feature.ts +68 -68
- package/std/assembly/shared/runtime.ts +13 -13
- package/std/assembly/shared/target.ts +11 -11
- package/std/assembly/shared/tsconfig.json +11 -11
- package/std/assembly/shared/typeinfo.ts +72 -72
- package/std/assembly/staticarray.ts +423 -423
- package/std/assembly/string.ts +850 -850
- package/std/assembly/symbol.ts +114 -114
- package/std/assembly/table.ts +16 -16
- package/std/assembly/toilscript.ts +16 -0
- package/std/assembly/tsconfig.json +6 -6
- package/std/assembly/typedarray.ts +1954 -1954
- package/std/assembly/uri.ts +17 -17
- package/std/assembly/util/bytes.ts +107 -107
- package/std/assembly/util/casemap.ts +497 -497
- package/std/assembly/util/error.ts +58 -58
- package/std/assembly/util/hash.ts +117 -117
- package/std/assembly/util/math.ts +1922 -1922
- package/std/assembly/util/memory.ts +290 -290
- package/std/assembly/util/number.ts +873 -873
- package/std/assembly/util/sort.ts +313 -313
- package/std/assembly/util/string.ts +1202 -1202
- package/std/assembly/util/uri.ts +275 -275
- package/std/assembly/vector.ts +4 -4
- package/std/assembly.json +16 -16
- package/std/portable/index.d.ts +461 -461
- package/std/portable/index.js +416 -416
- package/std/portable.json +11 -11
- package/std/types/assembly/index.d.ts +1 -1
- package/std/types/assembly/package.json +2 -2
- package/std/types/portable/index.d.ts +1 -1
- package/std/types/portable/package.json +2 -2
- package/tsconfig-base.json +13 -13
- package/util/README.md +23 -23
- package/util/browser/fs.js +1 -1
- package/util/browser/module.js +5 -5
- package/util/browser/path.js +520 -520
- package/util/browser/process.js +59 -59
- package/util/browser/url.js +23 -23
- package/util/cpu.d.ts +9 -9
- package/util/cpu.js +42 -42
- package/util/find.d.ts +6 -6
- package/util/find.js +20 -20
- package/util/node.d.ts +21 -21
- package/util/node.js +34 -34
- package/util/options.d.ts +70 -70
- package/util/options.js +262 -262
- package/util/terminal.d.ts +52 -52
- package/util/terminal.js +35 -35
- package/util/text.d.ts +26 -26
- package/util/text.js +114 -114
- package/util/tsconfig.json +9 -9
- package/util/web.d.ts +11 -11
- package/util/web.js +33 -33
package/std/assembly/rt/itcms.ts
CHANGED
|
@@ -1,419 +1,419 @@
|
|
|
1
|
-
import { BLOCK, BLOCK_OVERHEAD, OBJECT_OVERHEAD, OBJECT_MAXSIZE, TOTAL_OVERHEAD, DEBUG, TRACE, RTRACE, PROFILE } from "./common";
|
|
2
|
-
import { onvisit, oncollect, oninterrupt, onyield } from "./rtrace";
|
|
3
|
-
import { TypeinfoFlags } from "../shared/typeinfo";
|
|
4
|
-
import { E_ALLOCATION_TOO_LARGE, E_ALREADY_PINNED, E_NOT_PINNED } from "../util/error";
|
|
5
|
-
|
|
6
|
-
// === ITCMS: An incremental Tri-Color Mark & Sweep garbage collector ===
|
|
7
|
-
// Adapted from Bach Le's μgc, see: https://github.com/bullno1/ugc
|
|
8
|
-
|
|
9
|
-
// ╒═════════════╤══════════════ Colors ═══════════════════════════╕
|
|
10
|
-
// │ Color │ Meaning │
|
|
11
|
-
// ├─────────────┼─────────────────────────────────────────────────┤
|
|
12
|
-
// │ WHITE* │ Unprocessed │
|
|
13
|
-
// │ BLACK* │ Processed │
|
|
14
|
-
// │ GRAY │ Processed with unprocessed children │
|
|
15
|
-
// │ TRANSPARENT │ Manually pinned (always reachable) │
|
|
16
|
-
// └─────────────┴─────────────────────────────────────────────────┘
|
|
17
|
-
// * flipped between cycles
|
|
18
|
-
|
|
19
|
-
// @ts-ignore: decorator
|
|
20
|
-
@lazy let white = 0;
|
|
21
|
-
// @ts-ignore: decorator
|
|
22
|
-
@inline const gray = 2;
|
|
23
|
-
// @ts-ignore: decorator
|
|
24
|
-
@inline const transparent = 3;
|
|
25
|
-
// @ts-ignore: decorator
|
|
26
|
-
@inline const COLOR_MASK = 3;
|
|
27
|
-
|
|
28
|
-
/** Size in memory of all objects currently managed by the GC. */
|
|
29
|
-
// @ts-ignore: decorator
|
|
30
|
-
@lazy let total: usize = 0;
|
|
31
|
-
|
|
32
|
-
/** Currently transitioning from SWEEP to MARK state. */
|
|
33
|
-
// @ts-ignore: decorator
|
|
34
|
-
@inline const STATE_IDLE = 0;
|
|
35
|
-
/** Currently marking reachable objects. */
|
|
36
|
-
// @ts-ignore: decorator
|
|
37
|
-
@inline const STATE_MARK = 1;
|
|
38
|
-
/** Currently sweeping unreachable objects. */
|
|
39
|
-
// @ts-ignore: decorator
|
|
40
|
-
@inline const STATE_SWEEP = 2;
|
|
41
|
-
/** Current collector state. */
|
|
42
|
-
// @ts-ignore: decorator
|
|
43
|
-
@lazy let state = STATE_IDLE;
|
|
44
|
-
|
|
45
|
-
// @ts-ignore: decorator
|
|
46
|
-
@lazy let fromSpace = initLazy(changetype<Object>(memory.data(offsetof<Object>())));
|
|
47
|
-
// @ts-ignore: decorator
|
|
48
|
-
@lazy let toSpace = initLazy(changetype<Object>(memory.data(offsetof<Object>())));
|
|
49
|
-
// @ts-ignore: decorator
|
|
50
|
-
@lazy let pinSpace = initLazy(changetype<Object>(memory.data(offsetof<Object>())));
|
|
51
|
-
// @ts-ignore: decorator
|
|
52
|
-
@lazy let iter: Object = changetype<Object>(0); // unsafe initializion below
|
|
53
|
-
|
|
54
|
-
function initLazy(space: Object): Object {
|
|
55
|
-
space.nextWithColor = changetype<usize>(space);
|
|
56
|
-
space.prev = space;
|
|
57
|
-
return space;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** Visit cookie indicating scanning of an object. */
|
|
61
|
-
// @ts-ignore: decorator
|
|
62
|
-
@inline const VISIT_SCAN = 0;
|
|
63
|
-
|
|
64
|
-
// ╒═══════════════ Managed object layout (32-bit) ════════════════╕
|
|
65
|
-
// 3 2 1
|
|
66
|
-
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
|
|
67
|
-
// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤
|
|
68
|
-
// │ Memory manager block │
|
|
69
|
-
// ╞═══════════════════════════════════════════════════════════╤═══╡
|
|
70
|
-
// │ next │ C │ = nextWithColor
|
|
71
|
-
// ├───────────────────────────────────────────────────────────┴───┤
|
|
72
|
-
// │ prev │
|
|
73
|
-
// ├───────────────────────────────────────────────────────────────┤
|
|
74
|
-
// │ rtId │
|
|
75
|
-
// ├───────────────────────────────────────────────────────────────┤
|
|
76
|
-
// │ rtSize │
|
|
77
|
-
// ╞>ptr═══════════════════════════════════════════════════════════╡
|
|
78
|
-
// │ ... │
|
|
79
|
-
// C: color
|
|
80
|
-
|
|
81
|
-
/** Represents a managed object in memory, consisting of a header followed by the object's data. */
|
|
82
|
-
@unmanaged class Object extends BLOCK {
|
|
83
|
-
/** Pointer to the next object with color flags stored in the alignment bits. */
|
|
84
|
-
nextWithColor: usize; // *u32
|
|
85
|
-
/** Pointer to the previous object. */
|
|
86
|
-
prev: Object; // *u32
|
|
87
|
-
/** Runtime id. */
|
|
88
|
-
rtId: u32;
|
|
89
|
-
/** Runtime size. */
|
|
90
|
-
rtSize: u32;
|
|
91
|
-
|
|
92
|
-
/** Gets the pointer to the next object. */
|
|
93
|
-
get next(): Object {
|
|
94
|
-
return changetype<Object>(this.nextWithColor & ~COLOR_MASK);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** Sets the pointer to the next object. */
|
|
98
|
-
set next(obj: Object) {
|
|
99
|
-
this.nextWithColor = changetype<usize>(obj) | (this.nextWithColor & COLOR_MASK);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Gets this object's color. */
|
|
103
|
-
get color(): i32 {
|
|
104
|
-
return i32(this.nextWithColor & COLOR_MASK);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** Sets this object's color. */
|
|
108
|
-
set color(color: i32) {
|
|
109
|
-
this.nextWithColor = (this.nextWithColor & ~COLOR_MASK) | color;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** Gets the size of this object in memory. */
|
|
113
|
-
get size(): usize {
|
|
114
|
-
return BLOCK_OVERHEAD + (this.mmInfo & ~3);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Tests if this object is pointerfree. */
|
|
118
|
-
get isPointerfree(): bool {
|
|
119
|
-
let rtId = this.rtId;
|
|
120
|
-
// 0: Object, 1: ArrayBuffer, 2: String
|
|
121
|
-
return rtId <= idof<string>() || (__typeinfo(rtId) & TypeinfoFlags.POINTERFREE) != 0;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** Unlinks this object from its list. */
|
|
125
|
-
unlink(): void {
|
|
126
|
-
let next = this.next;
|
|
127
|
-
if (next == null) {
|
|
128
|
-
if (DEBUG) assert(this.prev == null && changetype<usize>(this) < __heap_base);
|
|
129
|
-
return; // static data not yet linked
|
|
130
|
-
}
|
|
131
|
-
let prev = this.prev;
|
|
132
|
-
if (DEBUG) assert(prev);
|
|
133
|
-
next.prev = prev;
|
|
134
|
-
prev.next = next;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** Links this object to the specified list, with the given color. */
|
|
138
|
-
linkTo(list: Object, withColor: i32): void {
|
|
139
|
-
let prev = list.prev;
|
|
140
|
-
this.nextWithColor = changetype<usize>(list) | withColor;
|
|
141
|
-
this.prev = prev;
|
|
142
|
-
prev.next = this;
|
|
143
|
-
list.prev = this;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** Marks this object as gray, that is reachable with unscanned children. */
|
|
147
|
-
makeGray(): void {
|
|
148
|
-
if (this == iter) iter = assert(this.prev);
|
|
149
|
-
this.unlink();
|
|
150
|
-
this.linkTo(toSpace, this.isPointerfree ? i32(!white) : gray);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/** Visits all objects considered to be program roots. */
|
|
155
|
-
function visitRoots(cookie: u32): void {
|
|
156
|
-
__visit_globals(cookie);
|
|
157
|
-
let pn = pinSpace;
|
|
158
|
-
let iter = pn.next;
|
|
159
|
-
while (iter != pn) {
|
|
160
|
-
if (DEBUG) assert(iter.color == transparent);
|
|
161
|
-
__visit_members(changetype<usize>(iter) + TOTAL_OVERHEAD, cookie);
|
|
162
|
-
iter = iter.next;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/** Visits all objects on the stack. */
|
|
167
|
-
function visitStack(cookie: u32): void {
|
|
168
|
-
let ptr = __stack_pointer;
|
|
169
|
-
while (ptr < __heap_base) {
|
|
170
|
-
__visit(load<usize>(ptr), cookie);
|
|
171
|
-
ptr += sizeof<usize>();
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/** Performs a single step according to the current state. */
|
|
176
|
-
function step(): usize {
|
|
177
|
-
// Magic constants responsible for pause times. Obtained experimentally
|
|
178
|
-
// using the compiler compiling itself. 2048 budget pro run by default.
|
|
179
|
-
const MARKCOST = isDefined(ASC_GC_MARKCOST) ? ASC_GC_MARKCOST : 1;
|
|
180
|
-
const SWEEPCOST = isDefined(ASC_GC_SWEEPCOST) ? ASC_GC_SWEEPCOST : 10;
|
|
181
|
-
let obj: Object;
|
|
182
|
-
switch (state) {
|
|
183
|
-
case STATE_IDLE: {
|
|
184
|
-
state = STATE_MARK;
|
|
185
|
-
visitCount = 0;
|
|
186
|
-
visitRoots(VISIT_SCAN);
|
|
187
|
-
iter = toSpace;
|
|
188
|
-
return visitCount * MARKCOST;
|
|
189
|
-
}
|
|
190
|
-
case STATE_MARK: {
|
|
191
|
-
let black = i32(!white);
|
|
192
|
-
obj = iter.next;
|
|
193
|
-
while (obj != toSpace) {
|
|
194
|
-
iter = obj;
|
|
195
|
-
if (obj.color != black) { // skip already-blacks (pointerfree)
|
|
196
|
-
obj.color = black;
|
|
197
|
-
visitCount = 0;
|
|
198
|
-
__visit_members(changetype<usize>(obj) + TOTAL_OVERHEAD, VISIT_SCAN);
|
|
199
|
-
return visitCount * MARKCOST;
|
|
200
|
-
}
|
|
201
|
-
obj = obj.next;
|
|
202
|
-
}
|
|
203
|
-
visitCount = 0;
|
|
204
|
-
visitRoots(VISIT_SCAN);
|
|
205
|
-
obj = iter.next;
|
|
206
|
-
if (obj == toSpace) {
|
|
207
|
-
visitStack(VISIT_SCAN);
|
|
208
|
-
obj = iter.next;
|
|
209
|
-
while (obj != toSpace) {
|
|
210
|
-
if (obj.color != black) {
|
|
211
|
-
obj.color = black;
|
|
212
|
-
__visit_members(changetype<usize>(obj) + TOTAL_OVERHEAD, VISIT_SCAN);
|
|
213
|
-
}
|
|
214
|
-
obj = obj.next;
|
|
215
|
-
}
|
|
216
|
-
let from = fromSpace;
|
|
217
|
-
fromSpace = toSpace;
|
|
218
|
-
toSpace = from;
|
|
219
|
-
white = black;
|
|
220
|
-
iter = from.next;
|
|
221
|
-
state = STATE_SWEEP;
|
|
222
|
-
}
|
|
223
|
-
return visitCount * MARKCOST;
|
|
224
|
-
}
|
|
225
|
-
case STATE_SWEEP: {
|
|
226
|
-
obj = iter;
|
|
227
|
-
if (obj != toSpace) {
|
|
228
|
-
iter = obj.next;
|
|
229
|
-
if (DEBUG) assert(obj.color == i32(!white)); // old white
|
|
230
|
-
free(obj);
|
|
231
|
-
return SWEEPCOST;
|
|
232
|
-
}
|
|
233
|
-
toSpace.nextWithColor = changetype<usize>(toSpace);
|
|
234
|
-
toSpace.prev = toSpace;
|
|
235
|
-
state = STATE_IDLE;
|
|
236
|
-
break;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return 0;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/** Frees an object. */
|
|
243
|
-
function free(obj: Object): void {
|
|
244
|
-
if (changetype<usize>(obj) < __heap_base) {
|
|
245
|
-
obj.nextWithColor = 0; // may become linked again
|
|
246
|
-
obj.prev = changetype<Object>(0);
|
|
247
|
-
} else {
|
|
248
|
-
total -= obj.size;
|
|
249
|
-
if (isDefined(__finalize)) {
|
|
250
|
-
__finalize(changetype<usize>(obj) + TOTAL_OVERHEAD);
|
|
251
|
-
}
|
|
252
|
-
__free(changetype<usize>(obj) + BLOCK_OVERHEAD);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Garbage collector interface
|
|
257
|
-
|
|
258
|
-
// @ts-ignore: decorator
|
|
259
|
-
@global @unsafe
|
|
260
|
-
export function __new(size: usize, id: i32): usize {
|
|
261
|
-
if (size >= OBJECT_MAXSIZE) throw new Error(E_ALLOCATION_TOO_LARGE);
|
|
262
|
-
if (total >= threshold) interrupt();
|
|
263
|
-
let obj = changetype<Object>(__alloc(OBJECT_OVERHEAD + size) - BLOCK_OVERHEAD);
|
|
264
|
-
obj.rtId = id;
|
|
265
|
-
obj.rtSize = <u32>size;
|
|
266
|
-
obj.linkTo(fromSpace, white); // inits next/prev
|
|
267
|
-
total += obj.size;
|
|
268
|
-
let ptr = changetype<usize>(obj) + TOTAL_OVERHEAD;
|
|
269
|
-
// may be visited before being fully initialized, so must fill
|
|
270
|
-
memory.fill(ptr, 0, size);
|
|
271
|
-
return ptr;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// @ts-ignore: decorator
|
|
275
|
-
@global @unsafe
|
|
276
|
-
export function __renew(oldPtr: usize, size: usize): usize {
|
|
277
|
-
let oldObj = changetype<Object>(oldPtr - TOTAL_OVERHEAD);
|
|
278
|
-
// Update object size if its block is large enough
|
|
279
|
-
if (size <= (oldObj.mmInfo & ~3) - OBJECT_OVERHEAD) {
|
|
280
|
-
oldObj.rtSize = <u32>size;
|
|
281
|
-
return oldPtr;
|
|
282
|
-
}
|
|
283
|
-
// If not the same object anymore, we have to move it move it due to the
|
|
284
|
-
// shadow stack potentially still referencing the old object
|
|
285
|
-
let newPtr = __new(size, oldObj.rtId);
|
|
286
|
-
memory.copy(newPtr, oldPtr, min(size, oldObj.rtSize));
|
|
287
|
-
return newPtr;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// @ts-ignore: decorator
|
|
291
|
-
@global @unsafe
|
|
292
|
-
export function __link(parentPtr: usize, childPtr: usize, expectMultiple: bool): void {
|
|
293
|
-
// Write barrier is unnecessary if non-incremental
|
|
294
|
-
if (!childPtr) return;
|
|
295
|
-
if (DEBUG) assert(parentPtr);
|
|
296
|
-
let child = changetype<Object>(childPtr - TOTAL_OVERHEAD);
|
|
297
|
-
if (child.color == white) {
|
|
298
|
-
let parent = changetype<Object>(parentPtr - TOTAL_OVERHEAD);
|
|
299
|
-
let parentColor = parent.color;
|
|
300
|
-
if (parentColor == i32(!white)) {
|
|
301
|
-
// Maintain the invariant that no black object may point to a white object.
|
|
302
|
-
if (expectMultiple) {
|
|
303
|
-
// Move the barrier "backward". Suitable for containers receiving multiple stores.
|
|
304
|
-
// Avoids a barrier for subsequent objects stored into the same container.
|
|
305
|
-
parent.makeGray();
|
|
306
|
-
} else {
|
|
307
|
-
// Move the barrier "forward". Suitable for objects receiving isolated stores.
|
|
308
|
-
child.makeGray();
|
|
309
|
-
}
|
|
310
|
-
} else if (parentColor == transparent && state == STATE_MARK) {
|
|
311
|
-
// Pinned objects are considered 'black' during the mark phase.
|
|
312
|
-
child.makeGray();
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// @ts-ignore: decorator
|
|
318
|
-
@lazy let visitCount = 0;
|
|
319
|
-
|
|
320
|
-
// @ts-ignore: decorator
|
|
321
|
-
@global @unsafe
|
|
322
|
-
export function __visit(ptr: usize, cookie: i32): void {
|
|
323
|
-
if (!ptr) return;
|
|
324
|
-
let obj = changetype<Object>(ptr - TOTAL_OVERHEAD);
|
|
325
|
-
if (RTRACE) if (!onvisit(obj)) return;
|
|
326
|
-
if (obj.color == white) {
|
|
327
|
-
obj.makeGray();
|
|
328
|
-
++visitCount;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// @ts-ignore: decorator
|
|
333
|
-
@global @unsafe
|
|
334
|
-
export function __pin(ptr: usize): usize {
|
|
335
|
-
if (ptr) {
|
|
336
|
-
let obj = changetype<Object>(ptr - TOTAL_OVERHEAD);
|
|
337
|
-
if (obj.color == transparent) {
|
|
338
|
-
throw new Error(E_ALREADY_PINNED);
|
|
339
|
-
}
|
|
340
|
-
obj.unlink(); // from fromSpace
|
|
341
|
-
obj.linkTo(pinSpace, transparent);
|
|
342
|
-
}
|
|
343
|
-
return ptr;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// @ts-ignore: decorator
|
|
347
|
-
@global @unsafe
|
|
348
|
-
export function __unpin(ptr: usize): void {
|
|
349
|
-
if (!ptr) return;
|
|
350
|
-
let obj = changetype<Object>(ptr - TOTAL_OVERHEAD);
|
|
351
|
-
if (obj.color != transparent) {
|
|
352
|
-
throw new Error(E_NOT_PINNED);
|
|
353
|
-
}
|
|
354
|
-
if (state == STATE_MARK) {
|
|
355
|
-
// We may be right at the point after marking roots for the second time and
|
|
356
|
-
// entering the sweep phase, in which case the object would be missed if it
|
|
357
|
-
// is not only pinned but also a root. Make sure it isn't missed.
|
|
358
|
-
obj.makeGray();
|
|
359
|
-
} else {
|
|
360
|
-
obj.unlink();
|
|
361
|
-
obj.linkTo(fromSpace, white);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// @ts-ignore: decorator
|
|
366
|
-
@global @unsafe
|
|
367
|
-
export function __collect(): void {
|
|
368
|
-
if (TRACE) trace("GC (full) at", 1, total);
|
|
369
|
-
if (state > STATE_IDLE) {
|
|
370
|
-
// finish current cycle
|
|
371
|
-
while (state != STATE_IDLE) step();
|
|
372
|
-
}
|
|
373
|
-
// perform a full cycle
|
|
374
|
-
step();
|
|
375
|
-
while (state != STATE_IDLE) step();
|
|
376
|
-
threshold = <usize>(<u64>total * IDLEFACTOR / 100) + GRANULARITY;
|
|
377
|
-
if (TRACE) trace("GC (full) done at cur/max", 2, total, memory.size() << 16);
|
|
378
|
-
if (RTRACE || PROFILE) oncollect(total);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Garbage collector automation
|
|
382
|
-
|
|
383
|
-
/** How often to interrupt. The default of 1024 means "interrupt each 1024 bytes allocated". */
|
|
384
|
-
// @ts-ignore: decorator
|
|
385
|
-
@inline const GRANULARITY: usize = isDefined(ASC_GC_GRANULARITY) ? ASC_GC_GRANULARITY : 1024;
|
|
386
|
-
/** How long to interrupt. The default of 200% means "run at double the speed of allocations". */
|
|
387
|
-
// @ts-ignore: decorator
|
|
388
|
-
@inline const STEPFACTOR: usize = isDefined(ASC_GC_SWEEPFACTOR) ? ASC_GC_SWEEPFACTOR : 200;
|
|
389
|
-
/** How long to idle. The default of 200% means "wait for memory to double before kicking in again". */
|
|
390
|
-
// @ts-ignore: decorator
|
|
391
|
-
@inline const IDLEFACTOR: usize = isDefined(ASC_GC_IDLEFACTOR) ? ASC_GC_IDLEFACTOR : 200;
|
|
392
|
-
|
|
393
|
-
/** Threshold of memory used by objects to exceed before interrupting again. */
|
|
394
|
-
// @ts-ignore: decorator
|
|
395
|
-
@lazy let threshold: usize = ((<usize>memory.size() << 16) - __heap_base) >> 1;
|
|
396
|
-
|
|
397
|
-
/** Performs a reasonable amount of incremental GC steps. */
|
|
398
|
-
function interrupt(): void {
|
|
399
|
-
if (PROFILE) oninterrupt(total);
|
|
400
|
-
if (TRACE) trace("GC (auto) at", 1, total);
|
|
401
|
-
let budget: isize = GRANULARITY * STEPFACTOR / 100;
|
|
402
|
-
do {
|
|
403
|
-
budget -= step();
|
|
404
|
-
if (state == STATE_IDLE) {
|
|
405
|
-
if (TRACE) trace("└ GC (auto) done at cur/max", 2, total, memory.size() << 16);
|
|
406
|
-
if (IDLEFACTOR % 100 == 0) {
|
|
407
|
-
// optimization for the default GC parameters which idle factor is 200%
|
|
408
|
-
threshold = total * (IDLEFACTOR / 100) + GRANULARITY;
|
|
409
|
-
} else {
|
|
410
|
-
threshold = <usize>((<u64>total * IDLEFACTOR) / 100) + GRANULARITY;
|
|
411
|
-
}
|
|
412
|
-
if (PROFILE) onyield(total);
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
} while (budget > 0);
|
|
416
|
-
if (TRACE) trace("└ GC (auto) ongoing at", 1, total);
|
|
417
|
-
threshold = total + GRANULARITY * usize(total - threshold < GRANULARITY);
|
|
418
|
-
if (PROFILE) onyield(total);
|
|
419
|
-
}
|
|
1
|
+
import { BLOCK, BLOCK_OVERHEAD, OBJECT_OVERHEAD, OBJECT_MAXSIZE, TOTAL_OVERHEAD, DEBUG, TRACE, RTRACE, PROFILE } from "./common";
|
|
2
|
+
import { onvisit, oncollect, oninterrupt, onyield } from "./rtrace";
|
|
3
|
+
import { TypeinfoFlags } from "../shared/typeinfo";
|
|
4
|
+
import { E_ALLOCATION_TOO_LARGE, E_ALREADY_PINNED, E_NOT_PINNED } from "../util/error";
|
|
5
|
+
|
|
6
|
+
// === ITCMS: An incremental Tri-Color Mark & Sweep garbage collector ===
|
|
7
|
+
// Adapted from Bach Le's μgc, see: https://github.com/bullno1/ugc
|
|
8
|
+
|
|
9
|
+
// ╒═════════════╤══════════════ Colors ═══════════════════════════╕
|
|
10
|
+
// │ Color │ Meaning │
|
|
11
|
+
// ├─────────────┼─────────────────────────────────────────────────┤
|
|
12
|
+
// │ WHITE* │ Unprocessed │
|
|
13
|
+
// │ BLACK* │ Processed │
|
|
14
|
+
// │ GRAY │ Processed with unprocessed children │
|
|
15
|
+
// │ TRANSPARENT │ Manually pinned (always reachable) │
|
|
16
|
+
// └─────────────┴─────────────────────────────────────────────────┘
|
|
17
|
+
// * flipped between cycles
|
|
18
|
+
|
|
19
|
+
// @ts-ignore: decorator
|
|
20
|
+
@lazy let white = 0;
|
|
21
|
+
// @ts-ignore: decorator
|
|
22
|
+
@inline const gray = 2;
|
|
23
|
+
// @ts-ignore: decorator
|
|
24
|
+
@inline const transparent = 3;
|
|
25
|
+
// @ts-ignore: decorator
|
|
26
|
+
@inline const COLOR_MASK = 3;
|
|
27
|
+
|
|
28
|
+
/** Size in memory of all objects currently managed by the GC. */
|
|
29
|
+
// @ts-ignore: decorator
|
|
30
|
+
@lazy let total: usize = 0;
|
|
31
|
+
|
|
32
|
+
/** Currently transitioning from SWEEP to MARK state. */
|
|
33
|
+
// @ts-ignore: decorator
|
|
34
|
+
@inline const STATE_IDLE = 0;
|
|
35
|
+
/** Currently marking reachable objects. */
|
|
36
|
+
// @ts-ignore: decorator
|
|
37
|
+
@inline const STATE_MARK = 1;
|
|
38
|
+
/** Currently sweeping unreachable objects. */
|
|
39
|
+
// @ts-ignore: decorator
|
|
40
|
+
@inline const STATE_SWEEP = 2;
|
|
41
|
+
/** Current collector state. */
|
|
42
|
+
// @ts-ignore: decorator
|
|
43
|
+
@lazy let state = STATE_IDLE;
|
|
44
|
+
|
|
45
|
+
// @ts-ignore: decorator
|
|
46
|
+
@lazy let fromSpace = initLazy(changetype<Object>(memory.data(offsetof<Object>())));
|
|
47
|
+
// @ts-ignore: decorator
|
|
48
|
+
@lazy let toSpace = initLazy(changetype<Object>(memory.data(offsetof<Object>())));
|
|
49
|
+
// @ts-ignore: decorator
|
|
50
|
+
@lazy let pinSpace = initLazy(changetype<Object>(memory.data(offsetof<Object>())));
|
|
51
|
+
// @ts-ignore: decorator
|
|
52
|
+
@lazy let iter: Object = changetype<Object>(0); // unsafe initializion below
|
|
53
|
+
|
|
54
|
+
function initLazy(space: Object): Object {
|
|
55
|
+
space.nextWithColor = changetype<usize>(space);
|
|
56
|
+
space.prev = space;
|
|
57
|
+
return space;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Visit cookie indicating scanning of an object. */
|
|
61
|
+
// @ts-ignore: decorator
|
|
62
|
+
@inline const VISIT_SCAN = 0;
|
|
63
|
+
|
|
64
|
+
// ╒═══════════════ Managed object layout (32-bit) ════════════════╕
|
|
65
|
+
// 3 2 1
|
|
66
|
+
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
|
|
67
|
+
// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤
|
|
68
|
+
// │ Memory manager block │
|
|
69
|
+
// ╞═══════════════════════════════════════════════════════════╤═══╡
|
|
70
|
+
// │ next │ C │ = nextWithColor
|
|
71
|
+
// ├───────────────────────────────────────────────────────────┴───┤
|
|
72
|
+
// │ prev │
|
|
73
|
+
// ├───────────────────────────────────────────────────────────────┤
|
|
74
|
+
// │ rtId │
|
|
75
|
+
// ├───────────────────────────────────────────────────────────────┤
|
|
76
|
+
// │ rtSize │
|
|
77
|
+
// ╞>ptr═══════════════════════════════════════════════════════════╡
|
|
78
|
+
// │ ... │
|
|
79
|
+
// C: color
|
|
80
|
+
|
|
81
|
+
/** Represents a managed object in memory, consisting of a header followed by the object's data. */
|
|
82
|
+
@unmanaged class Object extends BLOCK {
|
|
83
|
+
/** Pointer to the next object with color flags stored in the alignment bits. */
|
|
84
|
+
nextWithColor: usize; // *u32
|
|
85
|
+
/** Pointer to the previous object. */
|
|
86
|
+
prev: Object; // *u32
|
|
87
|
+
/** Runtime id. */
|
|
88
|
+
rtId: u32;
|
|
89
|
+
/** Runtime size. */
|
|
90
|
+
rtSize: u32;
|
|
91
|
+
|
|
92
|
+
/** Gets the pointer to the next object. */
|
|
93
|
+
get next(): Object {
|
|
94
|
+
return changetype<Object>(this.nextWithColor & ~COLOR_MASK);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Sets the pointer to the next object. */
|
|
98
|
+
set next(obj: Object) {
|
|
99
|
+
this.nextWithColor = changetype<usize>(obj) | (this.nextWithColor & COLOR_MASK);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Gets this object's color. */
|
|
103
|
+
get color(): i32 {
|
|
104
|
+
return i32(this.nextWithColor & COLOR_MASK);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Sets this object's color. */
|
|
108
|
+
set color(color: i32) {
|
|
109
|
+
this.nextWithColor = (this.nextWithColor & ~COLOR_MASK) | color;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Gets the size of this object in memory. */
|
|
113
|
+
get size(): usize {
|
|
114
|
+
return BLOCK_OVERHEAD + (this.mmInfo & ~3);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Tests if this object is pointerfree. */
|
|
118
|
+
get isPointerfree(): bool {
|
|
119
|
+
let rtId = this.rtId;
|
|
120
|
+
// 0: Object, 1: ArrayBuffer, 2: String
|
|
121
|
+
return rtId <= idof<string>() || (__typeinfo(rtId) & TypeinfoFlags.POINTERFREE) != 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Unlinks this object from its list. */
|
|
125
|
+
unlink(): void {
|
|
126
|
+
let next = this.next;
|
|
127
|
+
if (next == null) {
|
|
128
|
+
if (DEBUG) assert(this.prev == null && changetype<usize>(this) < __heap_base);
|
|
129
|
+
return; // static data not yet linked
|
|
130
|
+
}
|
|
131
|
+
let prev = this.prev;
|
|
132
|
+
if (DEBUG) assert(prev);
|
|
133
|
+
next.prev = prev;
|
|
134
|
+
prev.next = next;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Links this object to the specified list, with the given color. */
|
|
138
|
+
linkTo(list: Object, withColor: i32): void {
|
|
139
|
+
let prev = list.prev;
|
|
140
|
+
this.nextWithColor = changetype<usize>(list) | withColor;
|
|
141
|
+
this.prev = prev;
|
|
142
|
+
prev.next = this;
|
|
143
|
+
list.prev = this;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Marks this object as gray, that is reachable with unscanned children. */
|
|
147
|
+
makeGray(): void {
|
|
148
|
+
if (this == iter) iter = assert(this.prev);
|
|
149
|
+
this.unlink();
|
|
150
|
+
this.linkTo(toSpace, this.isPointerfree ? i32(!white) : gray);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Visits all objects considered to be program roots. */
|
|
155
|
+
function visitRoots(cookie: u32): void {
|
|
156
|
+
__visit_globals(cookie);
|
|
157
|
+
let pn = pinSpace;
|
|
158
|
+
let iter = pn.next;
|
|
159
|
+
while (iter != pn) {
|
|
160
|
+
if (DEBUG) assert(iter.color == transparent);
|
|
161
|
+
__visit_members(changetype<usize>(iter) + TOTAL_OVERHEAD, cookie);
|
|
162
|
+
iter = iter.next;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Visits all objects on the stack. */
|
|
167
|
+
function visitStack(cookie: u32): void {
|
|
168
|
+
let ptr = __stack_pointer;
|
|
169
|
+
while (ptr < __heap_base) {
|
|
170
|
+
__visit(load<usize>(ptr), cookie);
|
|
171
|
+
ptr += sizeof<usize>();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Performs a single step according to the current state. */
|
|
176
|
+
function step(): usize {
|
|
177
|
+
// Magic constants responsible for pause times. Obtained experimentally
|
|
178
|
+
// using the compiler compiling itself. 2048 budget pro run by default.
|
|
179
|
+
const MARKCOST = isDefined(ASC_GC_MARKCOST) ? ASC_GC_MARKCOST : 1;
|
|
180
|
+
const SWEEPCOST = isDefined(ASC_GC_SWEEPCOST) ? ASC_GC_SWEEPCOST : 10;
|
|
181
|
+
let obj: Object;
|
|
182
|
+
switch (state) {
|
|
183
|
+
case STATE_IDLE: {
|
|
184
|
+
state = STATE_MARK;
|
|
185
|
+
visitCount = 0;
|
|
186
|
+
visitRoots(VISIT_SCAN);
|
|
187
|
+
iter = toSpace;
|
|
188
|
+
return visitCount * MARKCOST;
|
|
189
|
+
}
|
|
190
|
+
case STATE_MARK: {
|
|
191
|
+
let black = i32(!white);
|
|
192
|
+
obj = iter.next;
|
|
193
|
+
while (obj != toSpace) {
|
|
194
|
+
iter = obj;
|
|
195
|
+
if (obj.color != black) { // skip already-blacks (pointerfree)
|
|
196
|
+
obj.color = black;
|
|
197
|
+
visitCount = 0;
|
|
198
|
+
__visit_members(changetype<usize>(obj) + TOTAL_OVERHEAD, VISIT_SCAN);
|
|
199
|
+
return visitCount * MARKCOST;
|
|
200
|
+
}
|
|
201
|
+
obj = obj.next;
|
|
202
|
+
}
|
|
203
|
+
visitCount = 0;
|
|
204
|
+
visitRoots(VISIT_SCAN);
|
|
205
|
+
obj = iter.next;
|
|
206
|
+
if (obj == toSpace) {
|
|
207
|
+
visitStack(VISIT_SCAN);
|
|
208
|
+
obj = iter.next;
|
|
209
|
+
while (obj != toSpace) {
|
|
210
|
+
if (obj.color != black) {
|
|
211
|
+
obj.color = black;
|
|
212
|
+
__visit_members(changetype<usize>(obj) + TOTAL_OVERHEAD, VISIT_SCAN);
|
|
213
|
+
}
|
|
214
|
+
obj = obj.next;
|
|
215
|
+
}
|
|
216
|
+
let from = fromSpace;
|
|
217
|
+
fromSpace = toSpace;
|
|
218
|
+
toSpace = from;
|
|
219
|
+
white = black;
|
|
220
|
+
iter = from.next;
|
|
221
|
+
state = STATE_SWEEP;
|
|
222
|
+
}
|
|
223
|
+
return visitCount * MARKCOST;
|
|
224
|
+
}
|
|
225
|
+
case STATE_SWEEP: {
|
|
226
|
+
obj = iter;
|
|
227
|
+
if (obj != toSpace) {
|
|
228
|
+
iter = obj.next;
|
|
229
|
+
if (DEBUG) assert(obj.color == i32(!white)); // old white
|
|
230
|
+
free(obj);
|
|
231
|
+
return SWEEPCOST;
|
|
232
|
+
}
|
|
233
|
+
toSpace.nextWithColor = changetype<usize>(toSpace);
|
|
234
|
+
toSpace.prev = toSpace;
|
|
235
|
+
state = STATE_IDLE;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return 0;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Frees an object. */
|
|
243
|
+
function free(obj: Object): void {
|
|
244
|
+
if (changetype<usize>(obj) < __heap_base) {
|
|
245
|
+
obj.nextWithColor = 0; // may become linked again
|
|
246
|
+
obj.prev = changetype<Object>(0);
|
|
247
|
+
} else {
|
|
248
|
+
total -= obj.size;
|
|
249
|
+
if (isDefined(__finalize)) {
|
|
250
|
+
__finalize(changetype<usize>(obj) + TOTAL_OVERHEAD);
|
|
251
|
+
}
|
|
252
|
+
__free(changetype<usize>(obj) + BLOCK_OVERHEAD);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Garbage collector interface
|
|
257
|
+
|
|
258
|
+
// @ts-ignore: decorator
|
|
259
|
+
@global @unsafe
|
|
260
|
+
export function __new(size: usize, id: i32): usize {
|
|
261
|
+
if (size >= OBJECT_MAXSIZE) throw new Error(E_ALLOCATION_TOO_LARGE);
|
|
262
|
+
if (total >= threshold) interrupt();
|
|
263
|
+
let obj = changetype<Object>(__alloc(OBJECT_OVERHEAD + size) - BLOCK_OVERHEAD);
|
|
264
|
+
obj.rtId = id;
|
|
265
|
+
obj.rtSize = <u32>size;
|
|
266
|
+
obj.linkTo(fromSpace, white); // inits next/prev
|
|
267
|
+
total += obj.size;
|
|
268
|
+
let ptr = changetype<usize>(obj) + TOTAL_OVERHEAD;
|
|
269
|
+
// may be visited before being fully initialized, so must fill
|
|
270
|
+
memory.fill(ptr, 0, size);
|
|
271
|
+
return ptr;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// @ts-ignore: decorator
|
|
275
|
+
@global @unsafe
|
|
276
|
+
export function __renew(oldPtr: usize, size: usize): usize {
|
|
277
|
+
let oldObj = changetype<Object>(oldPtr - TOTAL_OVERHEAD);
|
|
278
|
+
// Update object size if its block is large enough
|
|
279
|
+
if (size <= (oldObj.mmInfo & ~3) - OBJECT_OVERHEAD) {
|
|
280
|
+
oldObj.rtSize = <u32>size;
|
|
281
|
+
return oldPtr;
|
|
282
|
+
}
|
|
283
|
+
// If not the same object anymore, we have to move it move it due to the
|
|
284
|
+
// shadow stack potentially still referencing the old object
|
|
285
|
+
let newPtr = __new(size, oldObj.rtId);
|
|
286
|
+
memory.copy(newPtr, oldPtr, min(size, oldObj.rtSize));
|
|
287
|
+
return newPtr;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// @ts-ignore: decorator
|
|
291
|
+
@global @unsafe
|
|
292
|
+
export function __link(parentPtr: usize, childPtr: usize, expectMultiple: bool): void {
|
|
293
|
+
// Write barrier is unnecessary if non-incremental
|
|
294
|
+
if (!childPtr) return;
|
|
295
|
+
if (DEBUG) assert(parentPtr);
|
|
296
|
+
let child = changetype<Object>(childPtr - TOTAL_OVERHEAD);
|
|
297
|
+
if (child.color == white) {
|
|
298
|
+
let parent = changetype<Object>(parentPtr - TOTAL_OVERHEAD);
|
|
299
|
+
let parentColor = parent.color;
|
|
300
|
+
if (parentColor == i32(!white)) {
|
|
301
|
+
// Maintain the invariant that no black object may point to a white object.
|
|
302
|
+
if (expectMultiple) {
|
|
303
|
+
// Move the barrier "backward". Suitable for containers receiving multiple stores.
|
|
304
|
+
// Avoids a barrier for subsequent objects stored into the same container.
|
|
305
|
+
parent.makeGray();
|
|
306
|
+
} else {
|
|
307
|
+
// Move the barrier "forward". Suitable for objects receiving isolated stores.
|
|
308
|
+
child.makeGray();
|
|
309
|
+
}
|
|
310
|
+
} else if (parentColor == transparent && state == STATE_MARK) {
|
|
311
|
+
// Pinned objects are considered 'black' during the mark phase.
|
|
312
|
+
child.makeGray();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// @ts-ignore: decorator
|
|
318
|
+
@lazy let visitCount = 0;
|
|
319
|
+
|
|
320
|
+
// @ts-ignore: decorator
|
|
321
|
+
@global @unsafe
|
|
322
|
+
export function __visit(ptr: usize, cookie: i32): void {
|
|
323
|
+
if (!ptr) return;
|
|
324
|
+
let obj = changetype<Object>(ptr - TOTAL_OVERHEAD);
|
|
325
|
+
if (RTRACE) if (!onvisit(obj)) return;
|
|
326
|
+
if (obj.color == white) {
|
|
327
|
+
obj.makeGray();
|
|
328
|
+
++visitCount;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// @ts-ignore: decorator
|
|
333
|
+
@global @unsafe
|
|
334
|
+
export function __pin(ptr: usize): usize {
|
|
335
|
+
if (ptr) {
|
|
336
|
+
let obj = changetype<Object>(ptr - TOTAL_OVERHEAD);
|
|
337
|
+
if (obj.color == transparent) {
|
|
338
|
+
throw new Error(E_ALREADY_PINNED);
|
|
339
|
+
}
|
|
340
|
+
obj.unlink(); // from fromSpace
|
|
341
|
+
obj.linkTo(pinSpace, transparent);
|
|
342
|
+
}
|
|
343
|
+
return ptr;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// @ts-ignore: decorator
|
|
347
|
+
@global @unsafe
|
|
348
|
+
export function __unpin(ptr: usize): void {
|
|
349
|
+
if (!ptr) return;
|
|
350
|
+
let obj = changetype<Object>(ptr - TOTAL_OVERHEAD);
|
|
351
|
+
if (obj.color != transparent) {
|
|
352
|
+
throw new Error(E_NOT_PINNED);
|
|
353
|
+
}
|
|
354
|
+
if (state == STATE_MARK) {
|
|
355
|
+
// We may be right at the point after marking roots for the second time and
|
|
356
|
+
// entering the sweep phase, in which case the object would be missed if it
|
|
357
|
+
// is not only pinned but also a root. Make sure it isn't missed.
|
|
358
|
+
obj.makeGray();
|
|
359
|
+
} else {
|
|
360
|
+
obj.unlink();
|
|
361
|
+
obj.linkTo(fromSpace, white);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// @ts-ignore: decorator
|
|
366
|
+
@global @unsafe
|
|
367
|
+
export function __collect(): void {
|
|
368
|
+
if (TRACE) trace("GC (full) at", 1, total);
|
|
369
|
+
if (state > STATE_IDLE) {
|
|
370
|
+
// finish current cycle
|
|
371
|
+
while (state != STATE_IDLE) step();
|
|
372
|
+
}
|
|
373
|
+
// perform a full cycle
|
|
374
|
+
step();
|
|
375
|
+
while (state != STATE_IDLE) step();
|
|
376
|
+
threshold = <usize>(<u64>total * IDLEFACTOR / 100) + GRANULARITY;
|
|
377
|
+
if (TRACE) trace("GC (full) done at cur/max", 2, total, memory.size() << 16);
|
|
378
|
+
if (RTRACE || PROFILE) oncollect(total);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Garbage collector automation
|
|
382
|
+
|
|
383
|
+
/** How often to interrupt. The default of 1024 means "interrupt each 1024 bytes allocated". */
|
|
384
|
+
// @ts-ignore: decorator
|
|
385
|
+
@inline const GRANULARITY: usize = isDefined(ASC_GC_GRANULARITY) ? ASC_GC_GRANULARITY : 1024;
|
|
386
|
+
/** How long to interrupt. The default of 200% means "run at double the speed of allocations". */
|
|
387
|
+
// @ts-ignore: decorator
|
|
388
|
+
@inline const STEPFACTOR: usize = isDefined(ASC_GC_SWEEPFACTOR) ? ASC_GC_SWEEPFACTOR : 200;
|
|
389
|
+
/** How long to idle. The default of 200% means "wait for memory to double before kicking in again". */
|
|
390
|
+
// @ts-ignore: decorator
|
|
391
|
+
@inline const IDLEFACTOR: usize = isDefined(ASC_GC_IDLEFACTOR) ? ASC_GC_IDLEFACTOR : 200;
|
|
392
|
+
|
|
393
|
+
/** Threshold of memory used by objects to exceed before interrupting again. */
|
|
394
|
+
// @ts-ignore: decorator
|
|
395
|
+
@lazy let threshold: usize = ((<usize>memory.size() << 16) - __heap_base) >> 1;
|
|
396
|
+
|
|
397
|
+
/** Performs a reasonable amount of incremental GC steps. */
|
|
398
|
+
function interrupt(): void {
|
|
399
|
+
if (PROFILE) oninterrupt(total);
|
|
400
|
+
if (TRACE) trace("GC (auto) at", 1, total);
|
|
401
|
+
let budget: isize = GRANULARITY * STEPFACTOR / 100;
|
|
402
|
+
do {
|
|
403
|
+
budget -= step();
|
|
404
|
+
if (state == STATE_IDLE) {
|
|
405
|
+
if (TRACE) trace("└ GC (auto) done at cur/max", 2, total, memory.size() << 16);
|
|
406
|
+
if (IDLEFACTOR % 100 == 0) {
|
|
407
|
+
// optimization for the default GC parameters which idle factor is 200%
|
|
408
|
+
threshold = total * (IDLEFACTOR / 100) + GRANULARITY;
|
|
409
|
+
} else {
|
|
410
|
+
threshold = <usize>((<u64>total * IDLEFACTOR) / 100) + GRANULARITY;
|
|
411
|
+
}
|
|
412
|
+
if (PROFILE) onyield(total);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
} while (budget > 0);
|
|
416
|
+
if (TRACE) trace("└ GC (auto) ongoing at", 1, total);
|
|
417
|
+
threshold = total + GRANULARITY * usize(total - threshold < GRANULARITY);
|
|
418
|
+
if (PROFILE) onyield(total);
|
|
419
|
+
}
|