44 * @module std/assembly/collector/itcm
55 */ /***/
66
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 ;
810
911import {
1012 AL_MASK ,
1113 MAX_SIZE_32
1214} from "../internal/allocator" ;
1315
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+
1438// ╒═══════════════ Managed object layout (32-bit) ════════════════╕
1539// 3 2 1
1640// 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 {
2347// └───────────────────────────────────────────────────────────────┘
2448// F: flags
2549
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-
3850/** Represents a managed object in memory, consisting of a header followed by the object's data. */
3951@unmanaged
4052class ManagedObject {
4153
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 ;
4456
4557 /** Pointer to the previous object. */
4658 prev : ManagedObject ;
4759
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 ;
5062
5163 /** Size of a managed object after alignment. */
5264 static readonly SIZE : usize = ( offsetof < ManagedObject > ( ) + AL_MASK ) & ~ AL_MASK ;
5365
5466 /** Gets the pointer to the next object in the list. */
5567 get next ( ) : ManagedObject {
56- return changetype < ManagedObject > ( this . nextWithFlags & ~ Flags . MASK ) ;
68+ return changetype < ManagedObject > ( this . nextWithColor & ~ 3 ) ;
5769 }
5870
5971 /** Sets the pointer to the next object in the list. */
6072 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 ;
6284 }
6385
6486 /** Inserts an object to this list. */
65- insert ( obj : ManagedObject ) : void {
87+ push ( obj : ManagedObject ) : void {
6688 var prev = this . prev ;
89+ trace ( " push" , 3 , objToRef ( prev ) , objToRef ( obj ) , objToRef ( this ) ) ;
6790 obj . next = this ;
6891 obj . prev = prev ;
6992 prev . next = obj ;
7093 this . prev = obj ;
7194 }
7295
73- /** Removes this object from its list. */
74- remove ( ) : void {
96+ /** Unlinks this object from its list. */
97+ unlink ( ) : void {
7598 var next = this . next ;
7699 var prev = this . prev ;
100+ if ( TRACE ) trace ( " unlink" , 3 , objToRef ( prev ) , objToRef ( this ) , objToRef ( next ) ) ;
77101 next . prev = prev ;
78102 prev . next = next ;
79103 }
80104
81105 clear ( ) : void {
82- this . nextWithFlags = changetype < usize > ( this ) ;
106+ if ( TRACE ) trace ( " clear" , 1 , objToRef ( this ) ) ;
107+ this . nextWithColor = changetype < usize > ( this ) ;
83108 this . prev = this ;
84109 }
85110
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-
111111 /** Marks this object as gray, that is reachable with unscanned children. */
112112 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 ;
120119 }
121120}
122121
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+ } ) ;
133128}
134129
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-
143130/** Performs a single step according to the current state. */
144131function step ( ) : void {
145132 var obj : ManagedObject ;
146133 switch ( state ) {
147134 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" ) ;
153145 // fall-through
154146 }
155147 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 ( ) ;
164150 state = State . MARK ;
151+ if ( TRACE ) trace ( "gc~state = MARK" ) ;
165152 break ;
166153 }
167154 case State . MARK : {
168155 obj = iter . next ;
169- if ( obj != set2 ) {
156+ if ( obj !== to ) {
157+ if ( TRACE ) trace ( "gc~step/MARK iterate" , 1 , objToRef ( obj ) ) ;
170158 iter = obj ;
171- obj . makeBlack ( ) ;
172- obj . visitFn ( changetype < usize > ( obj ) + ManagedObject . SIZE ) ;
159+ obj . color = < i32 > ! white ;
160+ obj . visitFn ( objToRef ( obj ) ) ;
173161 } else {
162+ if ( TRACE ) trace ( "gc~step/MARK finish" ) ;
163+ markRoots ( ) ;
174164 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 ;
182171 state = State . SWEEP ;
172+ if ( TRACE ) trace ( "gc~state = SWEEP" ) ;
183173 }
184174 }
185175 break ;
186176 }
187177 case State . SWEEP : {
188178 obj = iter ;
189- if ( obj !== set2 ) {
179+ if ( obj !== to ) {
180+ if ( TRACE ) trace ( "gc~step/SWEEP free" , 1 , objToRef ( obj ) ) ;
190181 iter = obj . next ;
191182 memory . free ( changetype < usize > ( obj ) ) ;
192183 } else {
193- set2 . clear ( ) ;
184+ if ( TRACE ) trace ( "gc~step/SWEEP finish" ) ;
185+ to . clear ( ) ;
194186 state = State . IDLE ;
187+ if ( TRACE ) trace ( "gc~state = IDLE" ) ;
195188 }
196189 break ;
197190 }
@@ -213,30 +206,33 @@ function step(): void {
213206 size : usize ,
214207 visitFn : ( ref : usize ) => void
215208) : 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
217212 var obj = changetype < ManagedObject > ( memory . allocate ( ManagedObject . SIZE + size ) ) ;
218- obj . makeWhite ( ) ;
219213 obj . visitFn = visitFn ;
220- set1 . insert ( obj ) ;
214+ obj . color = white ;
215+ from . push ( obj ) ;
221216 return objToRef ( obj ) ;
222217}
223218
224219/** Marks a reachable object. Called from the visitFn functions. */
225220@global export function __gc_mark ( ref : usize ) : void {
221+ if ( TRACE ) trace ( "gc.mark" , 1 , ref ) ;
226222 var obj = refToObj ( ref ) ;
227- if ( state == State . SWEEP ) return ;
228- if ( obj . isWhite ) obj . makeGray ( ) ;
223+ if ( obj . color == white ) obj . makeGray ( ) ;
229224}
230225
231226/** Links a managed child object to its parent object. */
232227@global export function __gc_link ( parentRef : usize , childRef : usize ) : void {
228+ if ( TRACE ) trace ( "gc.link" , 2 , parentRef , childRef ) ;
233229 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 ( ) ;
236231}
237232
238233/** Performs a full garbage collection cycle. */
239234@global export function __gc_collect ( ) : void {
235+ if ( TRACE ) trace ( "gc.collect" ) ;
240236 // begin collecting if not yet collecting
241237 switch ( state ) {
242238 case State . INIT :
0 commit comments