4
4
* @module std/assembly/collector/itcm
5
5
*/ /***/
6
6
7
- // Based on the concepts of Bach Le's μgc, see: https://github.com/bullno1/ugc
7
+ // Largely based on the Bach Le's μgc, see: https://github.com/bullno1/ugc
8
+
9
+ const TRACE = true ;
8
10
9
11
import {
10
12
AL_MASK ,
11
13
MAX_SIZE_32
12
14
} from "../internal/allocator" ;
13
15
16
+ /** Collector states. */
17
+ const enum State {
18
+ /** Not yet initialized. */
19
+ INIT = 0 ,
20
+ /** Currently transitioning from SWEEP to MARK state. */
21
+ IDLE = 1 ,
22
+ /** Currently marking reachable objects. */
23
+ MARK = 2 ,
24
+ /** Currently sweeping unreachable objects. */
25
+ SWEEP = 3
26
+ }
27
+
28
+ /** Current collector state. */
29
+ var state = State . INIT ;
30
+ /** Current white color value. */
31
+ var white = 0 ;
32
+
33
+ // From and to spaces
34
+ var from : ManagedObject ;
35
+ var to : ManagedObject ;
36
+ var iter : ManagedObject ;
37
+
14
38
// ╒═══════════════ Managed object layout (32-bit) ════════════════╕
15
39
// 3 2 1
16
40
// 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
@@ -23,175 +47,144 @@ import {
23
47
// └───────────────────────────────────────────────────────────────┘
24
48
// F: flags
25
49
26
- /** Managed object flags. */
27
- namespace Flags {
28
- /** Object is unreachable (so far). */
29
- export var WHITE = 0 ;
30
- /** Object is reachable. */
31
- export var BLACK = 1 ;
32
- /** Object is reachable but its children have not yet been scanned. */
33
- export const GRAY = 2 ;
34
- /** Mask to obtain just the flag bits. */
35
- export const MASK = AL_MASK ;
36
- }
37
-
38
50
/** Represents a managed object in memory, consisting of a header followed by the object's data. */
39
51
@unmanaged
40
52
class ManagedObject {
41
53
42
- /** Pointer to the next object with additional flags stored in the alignment bits. */
43
- nextWithFlags : usize ;
54
+ /** Pointer to the next object with color flags stored in the alignment bits. */
55
+ nextWithColor : usize ;
44
56
45
57
/** Pointer to the previous object. */
46
58
prev : ManagedObject ;
47
59
48
- /** Visitor function called with the data pointer (excl. header) . */
49
- visitFn : ( obj : usize ) => void ;
60
+ /** Visitor function called with the payload reference . */
61
+ visitFn : ( ref : usize ) => void ;
50
62
51
63
/** Size of a managed object after alignment. */
52
64
static readonly SIZE : usize = ( offsetof < ManagedObject > ( ) + AL_MASK ) & ~ AL_MASK ;
53
65
54
66
/** Gets the pointer to the next object in the list. */
55
67
get next ( ) : ManagedObject {
56
- return changetype < ManagedObject > ( this . nextWithFlags & ~ Flags . MASK ) ;
68
+ return changetype < ManagedObject > ( this . nextWithColor & ~ 3 ) ;
57
69
}
58
70
59
71
/** Sets the pointer to the next object in the list. */
60
72
set next ( obj : ManagedObject ) {
61
- this . nextWithFlags = changetype < usize > ( obj ) | ( this . nextWithFlags & Flags . MASK ) ;
73
+ this . nextWithColor = changetype < usize > ( obj ) | ( this . nextWithColor & 3 ) ;
74
+ }
75
+
76
+ /** Gets this object's color. */
77
+ get color ( ) : i32 {
78
+ return this . nextWithColor & 3 ;
79
+ }
80
+
81
+ /** Sets this object's color. */
82
+ set color ( color : i32 ) {
83
+ this . nextWithColor = ( this . nextWithColor & ~ 3 ) | color ;
62
84
}
63
85
64
86
/** Inserts an object to this list. */
65
- insert ( obj : ManagedObject ) : void {
87
+ push ( obj : ManagedObject ) : void {
66
88
var prev = this . prev ;
89
+ trace ( " push" , 3 , objToRef ( prev ) , objToRef ( obj ) , objToRef ( this ) ) ;
67
90
obj . next = this ;
68
91
obj . prev = prev ;
69
92
prev . next = obj ;
70
93
this . prev = obj ;
71
94
}
72
95
73
- /** Removes this object from its list. */
74
- remove ( ) : void {
96
+ /** Unlinks this object from its list. */
97
+ unlink ( ) : void {
75
98
var next = this . next ;
76
99
var prev = this . prev ;
100
+ if ( TRACE ) trace ( " unlink" , 3 , objToRef ( prev ) , objToRef ( this ) , objToRef ( next ) ) ;
77
101
next . prev = prev ;
78
102
prev . next = next ;
79
103
}
80
104
81
105
clear ( ) : void {
82
- this . nextWithFlags = changetype < usize > ( this ) ;
106
+ if ( TRACE ) trace ( " clear" , 1 , objToRef ( this ) ) ;
107
+ this . nextWithColor = changetype < usize > ( this ) ;
83
108
this . prev = this ;
84
109
}
85
110
86
- /** Tests if this object is white, that is unreachable (so far). */
87
- get isWhite ( ) : bool {
88
- return ( this . nextWithFlags & Flags . MASK ) == Flags . WHITE ;
89
- }
90
-
91
- /** Marks this object as white, that is unreachable (so far). */
92
- makeWhite ( ) : void {
93
- this . nextWithFlags = ( this . nextWithFlags & ~ Flags . MASK ) | Flags . WHITE ;
94
- }
95
-
96
- /** Tests if this object is black, that is reachable. Root objects are always reachable. */
97
- get isBlack ( ) : bool {
98
- return ( this . nextWithFlags & Flags . MASK ) == Flags . BLACK ;
99
- }
100
-
101
- /** Marks this object as black, that is reachable. */
102
- makeBlack ( ) : void {
103
- this . nextWithFlags = ( this . nextWithFlags & ~ Flags . MASK ) | Flags . BLACK ;
104
- }
105
-
106
- /** Tests if this object is gray, that is reachable with unscanned children. */
107
- get isGray ( ) : bool {
108
- return ( this . nextWithFlags & Flags . MASK ) == Flags . GRAY ;
109
- }
110
-
111
111
/** Marks this object as gray, that is reachable with unscanned children. */
112
112
makeGray ( ) : void {
113
- if ( this != iter ) {
114
- this . remove ( ) ;
115
- set2 . insert ( this ) ;
116
- } else {
117
- iter = iter . prev ;
118
- }
119
- this . nextWithFlags = ( this . nextWithFlags & ~ Flags . MASK ) | Flags . GRAY ;
113
+ if ( TRACE ) trace ( " makeGray" , 1 , objToRef ( this ) ) ;
114
+ const gray = 2 ;
115
+ if ( this == iter ) iter = this . prev ;
116
+ this . unlink ( ) ;
117
+ to . push ( this ) ;
118
+ this . nextWithColor = ( this . nextWithColor & ~ 3 ) | gray ;
120
119
}
121
120
}
122
121
123
- /** Collector states. */
124
- const enum State {
125
- /** Not yet initialized. */
126
- INIT = 0 ,
127
- /** Currently transitioning from SWEEP to MARK state. */
128
- IDLE = 1 ,
129
- /** Currently marking reachable objects. */
130
- MARK = 2 ,
131
- /** Currently sweeping unreachable objects. */
132
- SWEEP = 3
122
+ function markRoots ( ) : void {
123
+ if ( TRACE ) trace ( " markRoots" ) ;
124
+ gc . iterateRoots ( function markRoot ( ref : usize ) : void {
125
+ if ( TRACE ) trace ( " markRoot" , 1 , ref ) ;
126
+ if ( ref ) __gc_mark ( ref ) ;
127
+ } ) ;
133
128
}
134
129
135
- /** Current collector state. */
136
- var state = State . INIT ;
137
-
138
- // From and to spaces
139
- var set1 : ManagedObject ;
140
- var set2 : ManagedObject ;
141
- var iter : ManagedObject ;
142
-
143
130
/** Performs a single step according to the current state. */
144
131
function step ( ) : void {
145
132
var obj : ManagedObject ;
146
133
switch ( state ) {
147
134
case State . INIT : {
148
- set1 = changetype < ManagedObject > ( memory . allocate ( ManagedObject . SIZE ) ) ;
149
- set1 . clear ( ) ;
150
- set2 = changetype < ManagedObject > ( memory . allocate ( ManagedObject . SIZE ) ) ;
151
- set2 . clear ( ) ;
152
- iter = set2 ;
135
+ if ( TRACE ) trace ( "gc~step/INIT" ) ;
136
+ from = changetype < ManagedObject > ( memory . allocate ( ManagedObject . SIZE ) ) ;
137
+ from . visitFn = changetype < ( ref : usize ) => void > ( < u32 > - 1 ) ; // would error
138
+ from . clear ( ) ;
139
+ to = changetype < ManagedObject > ( memory . allocate ( ManagedObject . SIZE ) ) ;
140
+ to . visitFn = changetype < ( ref : usize ) => void > ( < u32 > - 1 ) ; // would error
141
+ to . clear ( ) ;
142
+ iter = to ;
143
+ state = State . IDLE ;
144
+ if ( TRACE ) trace ( "gc~state = IDLE" ) ;
153
145
// fall-through
154
146
}
155
147
case State . IDLE : {
156
- // start by marking roots
157
- gc . iterateRoots ( function mark_root ( ref : usize ) : void {
158
- if ( ref ) {
159
- let obj = changetype < ManagedObject > ( ref - ManagedObject . SIZE ) ;
160
- obj . makeBlack ( ) ;
161
- obj . visitFn ( ref ) ;
162
- }
163
- } ) ;
148
+ if ( TRACE ) trace ( "gc~step/IDLE" ) ;
149
+ markRoots ( ) ;
164
150
state = State . MARK ;
151
+ if ( TRACE ) trace ( "gc~state = MARK" ) ;
165
152
break ;
166
153
}
167
154
case State . MARK : {
168
155
obj = iter . next ;
169
- if ( obj != set2 ) {
156
+ if ( obj !== to ) {
157
+ if ( TRACE ) trace ( "gc~step/MARK iterate" , 1 , objToRef ( obj ) ) ;
170
158
iter = obj ;
171
- obj . makeBlack ( ) ;
172
- obj . visitFn ( changetype < usize > ( obj ) + ManagedObject . SIZE ) ;
159
+ obj . color = < i32 > ! white ;
160
+ obj . visitFn ( objToRef ( obj ) ) ;
173
161
} else {
162
+ if ( TRACE ) trace ( "gc~step/MARK finish" ) ;
163
+ markRoots ( ) ;
174
164
obj = iter . next ;
175
- if ( obj == set2 ) {
176
- let set1_ = set1 ;
177
- set1 = set2 ;
178
- set2 = set1_ ;
179
- Flags . WHITE ^= 1 ;
180
- Flags . BLACK ^= 1 ;
181
- iter = set1 . next ;
165
+ if ( obj === to ) {
166
+ let prevFrom = from ;
167
+ from = to ;
168
+ to = prevFrom ;
169
+ white = < i32 > ! white ;
170
+ iter = prevFrom . next ;
182
171
state = State . SWEEP ;
172
+ if ( TRACE ) trace ( "gc~state = SWEEP" ) ;
183
173
}
184
174
}
185
175
break ;
186
176
}
187
177
case State . SWEEP : {
188
178
obj = iter ;
189
- if ( obj !== set2 ) {
179
+ if ( obj !== to ) {
180
+ if ( TRACE ) trace ( "gc~step/SWEEP free" , 1 , objToRef ( obj ) ) ;
190
181
iter = obj . next ;
191
182
memory . free ( changetype < usize > ( obj ) ) ;
192
183
} else {
193
- set2 . clear ( ) ;
184
+ if ( TRACE ) trace ( "gc~step/SWEEP finish" ) ;
185
+ to . clear ( ) ;
194
186
state = State . IDLE ;
187
+ if ( TRACE ) trace ( "gc~state = IDLE" ) ;
195
188
}
196
189
break ;
197
190
}
@@ -213,30 +206,33 @@ function step(): void {
213
206
size : usize ,
214
207
visitFn : ( ref : usize ) => void
215
208
) : usize {
216
- assert ( size <= MAX_SIZE_32 - ManagedObject . SIZE ) ;
209
+ if ( TRACE ) trace ( "gc.allocate" , 1 , size ) ;
210
+ if ( size > MAX_SIZE_32 - ManagedObject . SIZE ) unreachable ( ) ;
211
+ step ( ) ; // also makes sure it's initialized
217
212
var obj = changetype < ManagedObject > ( memory . allocate ( ManagedObject . SIZE + size ) ) ;
218
- obj . makeWhite ( ) ;
219
213
obj . visitFn = visitFn ;
220
- set1 . insert ( obj ) ;
214
+ obj . color = white ;
215
+ from . push ( obj ) ;
221
216
return objToRef ( obj ) ;
222
217
}
223
218
224
219
/** Marks a reachable object. Called from the visitFn functions. */
225
220
@global export function __gc_mark ( ref : usize ) : void {
221
+ if ( TRACE ) trace ( "gc.mark" , 1 , ref ) ;
226
222
var obj = refToObj ( ref ) ;
227
- if ( state == State . SWEEP ) return ;
228
- if ( obj . isWhite ) obj . makeGray ( ) ;
223
+ if ( obj . color == white ) obj . makeGray ( ) ;
229
224
}
230
225
231
226
/** Links a managed child object to its parent object. */
232
227
@global export function __gc_link ( parentRef : usize , childRef : usize ) : void {
228
+ if ( TRACE ) trace ( "gc.link" , 2 , parentRef , childRef ) ;
233
229
var parent = refToObj ( parentRef ) ;
234
- var child = refToObj ( childRef ) ;
235
- if ( parent . isBlack && child . isWhite ) parent . makeGray ( ) ;
230
+ if ( parent . color == < i32 > ! white && refToObj ( childRef ) . color == white ) parent . makeGray ( ) ;
236
231
}
237
232
238
233
/** Performs a full garbage collection cycle. */
239
234
@global export function __gc_collect ( ) : void {
235
+ if ( TRACE ) trace ( "gc.collect" ) ;
240
236
// begin collecting if not yet collecting
241
237
switch ( state ) {
242
238
case State . INIT :
0 commit comments