@@ -10,7 +10,7 @@ void CC_workList_init(
10
10
HM_chunkList c = & (w -> storage );
11
11
HM_initChunkList (c );
12
12
// arbitrary, just need an initial chunk
13
- w -> currentChunk = HM_allocateChunk (c , sizeof (objptr ));
13
+ w -> currentChunk = HM_allocateChunk (c , sizeof (struct CC_workList_elem ));
14
14
}
15
15
16
16
@@ -34,41 +34,240 @@ bool CC_workList_isEmpty(
34
34
}
35
35
36
36
37
+ pointer findStackNonEmptyFrameTop (GC_state s , pointer bottom , pointer top ) {
38
+ assert (bottom <= top );
39
+
40
+ while (top > bottom ) {
41
+ /* Invariant: top points just past a "return address". */
42
+ GC_returnAddress returnAddress =
43
+ * ((GC_returnAddress * )(top - GC_RETURNADDRESS_SIZE ));
44
+ GC_frameInfo frameInfo = getFrameInfoFromReturnAddress (s , returnAddress );
45
+ // index zero of this array is size
46
+ GC_frameOffsets frameOffsets = frameInfo -> offsets ;
47
+
48
+ if (frameOffsets [0 ] > 0 ) {
49
+ return top ;
50
+ }
51
+
52
+ top -= frameInfo -> size ;
53
+ }
54
+
55
+ return NULL ;
56
+ }
57
+
58
+
59
+ // returns FALSE if object has no objptrs and doesn't need to be traced
60
+ bool makeInitialElem (GC_state s , objptr op , CC_workList_elem result ) {
61
+ GC_header header ;
62
+ uint16_t numObjptrs ;
63
+ GC_objectTypeTag tag ;
64
+ header = getHeader (objptrToPointer (op , NULL ));
65
+ splitHeader (s , header , & tag , NULL , NULL , & numObjptrs );
66
+
67
+ if (NORMAL_TAG == tag ) {
68
+ if (0 == numObjptrs ) return FALSE;
69
+
70
+ result -> op = op ;
71
+ result -> data .normal .objptrIdx = 0 ;
72
+ return TRUE;
73
+ }
74
+
75
+ if (SEQUENCE_TAG == tag ) {
76
+ if (0 == numObjptrs ) return FALSE;
77
+ if (0 == getSequenceLength (objptrToPointer (op , NULL ))) return FALSE;
78
+
79
+ result -> op = op ;
80
+ result -> data .sequence .cellIdx = 0 ;
81
+ result -> data .sequence .objptrIdx = 0 ;
82
+ return TRUE;
83
+ }
84
+
85
+ if (STACK_TAG == tag ) {
86
+ // printf("makeInitialElem stack\n");
87
+
88
+ GC_stack stack = (GC_stack )objptrToPointer (op , NULL );
89
+ assert (stack -> used <= stack -> reserved );
90
+ pointer bottom = getStackBottom (s , stack );
91
+ pointer top = getStackTop (s , stack );
92
+ pointer firstTop = findStackNonEmptyFrameTop (s , bottom , top );
93
+
94
+ if (NULL == firstTop ) {
95
+ // printf("makeInitialElem: skipping stack "FMTOBJPTR"\n", op);
96
+ return FALSE;
97
+ }
98
+
99
+ assert (firstTop > bottom );
100
+
101
+ result -> op = op ;
102
+ result -> data .stack .topCursor = firstTop ;
103
+ result -> data .stack .frameOffsetsIdx = 0 ;
104
+
105
+ // printf("makeInitialElem: push stack "FMTOBJPTR"\n", op);
106
+ return TRUE;
107
+ }
108
+
109
+ DIE ("makeInitialElem: cannot handle tag %u" , tag );
110
+ return FALSE;
111
+ }
112
+
113
+
37
114
void CC_workList_push (
38
- __attribute__(( unused )) GC_state s ,
115
+ GC_state s ,
39
116
CC_workList w ,
40
117
objptr op )
41
118
{
119
+ struct CC_workList_elem elem ;
120
+ if (!makeInitialElem (s , op , & elem ))
121
+ return ;
122
+
42
123
HM_chunkList list = & (w -> storage );
43
124
HM_chunk chunk = w -> currentChunk ;
44
- size_t opsz = sizeof (objptr );
125
+ size_t elemSize = sizeof (struct CC_workList_elem );
45
126
46
- if (HM_getChunkSizePastFrontier (chunk ) < opsz ) {
127
+ if (HM_getChunkSizePastFrontier (chunk ) < elemSize ) {
47
128
if (chunk -> nextChunk != NULL ) {
48
129
chunk = chunk -> nextChunk ; // this will be an empty chunk
49
130
} else {
50
- chunk = HM_allocateChunk (list , opsz );
131
+ chunk = HM_allocateChunk (list , elemSize );
51
132
}
52
133
w -> currentChunk = chunk ;
53
134
}
54
135
55
136
assert (NULL != chunk );
56
- assert (HM_getChunkSizePastFrontier (chunk ) >= opsz );
137
+ assert (HM_getChunkSizePastFrontier (chunk ) >= elemSize );
57
138
assert (chunk == w -> currentChunk );
58
139
59
140
pointer frontier = HM_getChunkFrontier (chunk );
60
141
HM_updateChunkFrontierInList (
61
142
list ,
62
143
chunk ,
63
- frontier + opsz );
144
+ frontier + elemSize );
145
+
146
+ * (CC_workList_elem )frontier = elem ;
147
+ return ;
148
+ }
64
149
65
- * ((objptr * )frontier ) = op ;
66
150
151
+ struct advanceOneFieldResult {
152
+ objptr * field ;
153
+ bool objectDone ;
154
+ };
155
+
156
+ void advanceOneField (
157
+ GC_state s ,
158
+ CC_workList_elem elem ,
159
+ struct advanceOneFieldResult * result )
160
+ {
161
+ pointer p = objptrToPointer (elem -> op , NULL );
162
+
163
+ // inspect the object
164
+ GC_header header ;
165
+ uint16_t bytesNonObjptrs ;
166
+ uint16_t numObjptrs ;
167
+ GC_objectTypeTag tag ;
168
+ header = getHeader (p );
169
+ splitHeader (s , header , & tag , NULL , & bytesNonObjptrs , & numObjptrs );
170
+
171
+ // ======================== NORMAL OBJECTS ========================
172
+
173
+ if (NORMAL_TAG == tag ) {
174
+ uint16_t objptrIdx = elem -> data .normal .objptrIdx ;
175
+ assert (objptrIdx < numObjptrs );
176
+ result -> field = (objptr * )(p + bytesNonObjptrs + (objptrIdx * OBJPTR_SIZE ));
177
+ result -> objectDone = (objptrIdx + 1 == numObjptrs );
178
+ elem -> data .normal .objptrIdx ++ ;
179
+ return ;
180
+ }
181
+
182
+ // ======================== SEQUENCE OBJECTS ========================
183
+
184
+ if (SEQUENCE_TAG == tag ) {
185
+ GC_sequenceLength numCells = getSequenceLength (p );
186
+ size_t bytesPerCell = bytesNonObjptrs + (numObjptrs * OBJPTR_SIZE );
187
+
188
+ size_t cellIdx = elem -> data .sequence .cellIdx ;
189
+ uint16_t objptrIdx = elem -> data .sequence .objptrIdx ;
190
+ assert (cellIdx < numCells );
191
+ assert (objptrIdx < numObjptrs );
192
+
193
+ result -> field =
194
+ (objptr * )(
195
+ p // object start
196
+ + (cellIdx * bytesPerCell ) // cell offset
197
+ + bytesNonObjptrs // objptrs offset
198
+ + (objptrIdx * OBJPTR_SIZE ) // current objptr offset
199
+ );
200
+
201
+ result -> objectDone = FALSE;
202
+
203
+ elem -> data .sequence .objptrIdx ++ ;
204
+ if (objptrIdx + 1 == numObjptrs ) {
205
+ elem -> data .sequence .cellIdx ++ ;
206
+ elem -> data .sequence .objptrIdx = 0 ;
207
+ if (cellIdx + 1 == numCells )
208
+ result -> objectDone = TRUE;
209
+ }
210
+ return ;
211
+ }
212
+
213
+ // ======================== STACK OBJECTS ========================
214
+
215
+ if (STACK_TAG == tag ) {
216
+ GC_stack stack = (GC_stack )p ;
217
+ pointer bottom = getStackBottom (s , stack );
218
+ pointer top = elem -> data .stack .topCursor ;
219
+ unsigned int i = elem -> data .stack .frameOffsetsIdx ;
220
+
221
+ // printf(
222
+ // "advanceOneField: stack "FMTOBJPTR" top="FMTPTR" bottom="FMTPTR" i=%u\n",
223
+ // (uintptr_t)elem->op,
224
+ // (uintptr_t)top,
225
+ // (uintptr_t)bottom,
226
+ // i
227
+ // );
228
+
229
+ /* Invariant: top points just past a "return address". */
230
+ GC_returnAddress returnAddress =
231
+ * ((GC_returnAddress * )(top - GC_RETURNADDRESS_SIZE ));
232
+ GC_frameInfo frameInfo = getFrameInfoFromReturnAddress (s , returnAddress );
233
+ // index zero of this array is size
234
+ GC_frameOffsets frameOffsets = frameInfo -> offsets ;
235
+ pointer frameStart = top - frameInfo -> size ;
236
+
237
+ assert (frameOffsets [0 ] > 0 );
238
+ assert (i < frameOffsets [0 ]);
239
+ assert (frameStart >= bottom );
240
+
241
+ result -> field = (objptr * )(frameStart + frameOffsets [i + 1 ]);
242
+ elem -> data .stack .frameOffsetsIdx ++ ;
243
+
244
+ result -> objectDone = FALSE;
245
+
246
+ if (i + 1 == frameOffsets [0 ]) {
247
+ /** walk backwards until we find a frame that has at least one objptr
248
+ * or, until we find the end of the stack.
249
+ */
250
+ pointer newTop = findStackNonEmptyFrameTop (s , bottom , frameStart );
251
+
252
+ if (NULL == newTop ) {
253
+ result -> objectDone = TRUE;
254
+ } else {
255
+ elem -> data .stack .topCursor = newTop ;
256
+ elem -> data .stack .frameOffsetsIdx = 0 ;
257
+ }
258
+ }
259
+
260
+ return ;
261
+ }
262
+
263
+ // ======================== OTHERWISE... ========================
264
+
265
+ DIE ("advanceOneField: cannot handle tag %u" , tag );
67
266
return ;
68
267
}
69
268
70
269
71
- objptr CC_workList_pop (
270
+ objptr * CC_workList_pop (
72
271
GC_state s ,
73
272
CC_workList w )
74
273
{
@@ -81,7 +280,7 @@ objptr CC_workList_pop(
81
280
HM_chunk prevChunk = chunk -> prevChunk ;
82
281
if (prevChunk == NULL ) {
83
282
// whole worklist is empty
84
- return BOGUS_OBJPTR ;
283
+ return NULL ;
85
284
}
86
285
87
286
/** Otherwise, there is a chunk before us. It's now safe (for cost
@@ -101,19 +300,25 @@ objptr CC_workList_pop(
101
300
}
102
301
103
302
assert (w -> currentChunk == chunk );
104
- assert (HM_getChunkFrontier (chunk ) >= HM_getChunkStart (chunk ) + sizeof (objptr ));
303
+ assert (HM_getChunkFrontier (chunk ) >= HM_getChunkStart (chunk ) + sizeof (struct CC_workList_elem ));
105
304
106
305
pointer frontier = HM_getChunkFrontier (chunk );
107
- pointer newFrontier = frontier - sizeof (objptr );
306
+ pointer elemPtr = frontier - sizeof (struct CC_workList_elem );
307
+ CC_workList_elem elem = (CC_workList_elem )elemPtr ;
108
308
109
- HM_updateChunkFrontierInList (
110
- list ,
111
- chunk ,
112
- newFrontier );
309
+ struct advanceOneFieldResult r ;
310
+ advanceOneField (s , elem , & r );
113
311
114
- objptr result = * ((objptr * )newFrontier );
312
+ if (r .objectDone ) {
313
+ pointer newFrontier = elemPtr ;
314
+ HM_updateChunkFrontierInList (
315
+ list ,
316
+ chunk ,
317
+ newFrontier );
318
+ }
115
319
116
- return result ;
320
+ assert (NULL != r .field );
321
+ return r .field ;
117
322
}
118
323
119
324
#endif /* MLTON_GC_INTERNAL_FUNCS */
0 commit comments