3 /* Scan all the objects in the card */
4 void copy_card(F_CARD
*ptr
, CELL gen
, CELL here
)
6 CELL card_scan
= (CELL
)CARD_TO_ADDR(ptr
) + CARD_OFFSET(ptr
);
7 CELL card_end
= (CELL
)CARD_TO_ADDR(ptr
+ 1);
12 copy_reachable_objects(card_scan
,&card_end
);
17 void copy_card_deck(F_DECK
*deck
, CELL gen
, F_CARD mask
, F_CARD unmask
)
19 F_CARD
*first_card
= DECK_TO_CARD(deck
);
20 F_CARD
*last_card
= DECK_TO_CARD(deck
+ 1);
22 CELL here
= data_heap
->generations
[gen
].here
;
25 u32 quad_mask
= mask
| (mask
<< 8) | (mask
<< 16) | (mask
<< 24);
27 for(quad_ptr
= (u32
*)first_card
; quad_ptr
< (u32
*)last_card
; quad_ptr
++)
29 if(*quad_ptr
& quad_mask
)
31 F_CARD
*ptr
= (F_CARD
*)quad_ptr
;
34 for(card
= 0; card
< 4; card
++)
38 copy_card(&ptr
[card
],gen
,here
);
48 /* Copy all newspace objects referenced from marked cards to the destination */
49 void copy_gen_cards(CELL gen
)
51 F_DECK
*first_deck
= ADDR_TO_DECK(data_heap
->generations
[gen
].start
);
52 F_DECK
*last_deck
= ADDR_TO_DECK(data_heap
->generations
[gen
].end
);
56 /* if we are collecting the nursery, we care about old->nursery pointers
57 but not old->aging pointers */
58 if(collecting_gen
== NURSERY
)
60 mask
= CARD_POINTS_TO_NURSERY
;
62 /* after the collection, no old->nursery pointers remain
63 anywhere, but old->aging pointers might remain in tenured
66 unmask
= CARD_POINTS_TO_NURSERY
;
67 /* after the collection, all cards in aging space can be
69 else if(HAVE_AGING_P
&& gen
== AGING
)
70 unmask
= CARD_MARK_MASK
;
73 critical_error("bug in copy_gen_cards",gen
);
77 /* if we are collecting aging space into tenured space, we care about
78 all old->nursery and old->aging pointers. no old->aging pointers can
80 else if(HAVE_AGING_P
&& collecting_gen
== AGING
)
82 if(collecting_aging_again
)
84 mask
= CARD_POINTS_TO_AGING
;
85 unmask
= CARD_MARK_MASK
;
87 /* after we collect aging space into the aging semispace, no
88 old->nursery pointers remain but tenured space might still have
89 pointers to aging space. */
92 mask
= CARD_POINTS_TO_AGING
;
93 unmask
= CARD_POINTS_TO_NURSERY
;
98 critical_error("bug in copy_gen_cards",gen
);
104 for(ptr
= first_deck
; ptr
< last_deck
; ptr
++)
108 copy_card_deck(ptr
,gen
,mask
,unmask
);
114 /* Scan cards in all generations older than the one being collected, copying
115 old->new references */
116 void copy_cards(void)
119 for(i
= collecting_gen
+ 1; i
< data_heap
->gen_count
; i
++)
123 /* Copy all tagged pointers in a range of memory */
124 void copy_stack_elements(F_SEGMENT
*region
, CELL top
)
126 CELL ptr
= region
->start
;
128 for(; ptr
<= top
; ptr
+= CELLS
)
129 copy_handle((CELL
*)ptr
);
132 void copy_registered_locals(void)
134 CELL ptr
= gc_locals_region
->start
;
136 for(; ptr
<= gc_locals
; ptr
+= CELLS
)
137 copy_handle(*(CELL
**)ptr
);
140 /* Copy roots over at the start of GC, namely various constants, stacks,
141 the user environment and extra roots registered with REGISTER_ROOT */
142 void copy_roots(void)
145 copy_handle(&bignum_zero
);
146 copy_handle(&bignum_pos_one
);
147 copy_handle(&bignum_neg_one
);
149 copy_registered_locals();
150 copy_stack_elements(extra_roots_region
,extra_roots
);
153 F_CONTEXT
*stacks
= stack_chain
;
157 copy_stack_elements(stacks
->datastack_region
,stacks
->datastack
);
158 copy_stack_elements(stacks
->retainstack_region
,stacks
->retainstack
);
160 copy_handle(&stacks
->catchstack_save
);
161 copy_handle(&stacks
->current_callback_save
);
163 mark_active_blocks(stacks
);
165 stacks
= stacks
->next
;
169 for(i
= 0; i
< USER_ENV
; i
++)
170 copy_handle(&userenv
[i
]);
173 /* Given a pointer to oldspace, copy it to newspace */
174 INLINE
void *copy_untagged_object(void *pointer
, CELL size
)
176 if(newspace
->here
+ size
>= newspace
->end
)
178 allot_barrier(newspace
->here
);
179 void *newpointer
= allot_zone(newspace
,size
);
181 F_GC_STATS
*s
= &gc_stats
[collecting_gen
];
183 s
->bytes_copied
+= size
;
185 memcpy(newpointer
,pointer
,size
);
189 INLINE
void forward_object(CELL pointer
, CELL newpointer
)
191 if(pointer
!= newpointer
)
192 put(UNTAG(pointer
),RETAG(newpointer
,GC_COLLECTED
));
195 INLINE CELL
copy_object_impl(CELL pointer
)
197 CELL newpointer
= (CELL
)copy_untagged_object(
198 (void*)UNTAG(pointer
),
199 object_size(pointer
));
200 forward_object(pointer
,newpointer
);
204 /* Follow a chain of forwarding pointers */
205 CELL
resolve_forwarding(CELL untagged
, CELL tag
)
207 CELL header
= get(untagged
);
208 /* another forwarding pointer */
209 if(TAG(header
) == GC_COLLECTED
)
210 return resolve_forwarding(UNTAG(header
),tag
);
211 /* we've found the destination */
214 CELL pointer
= RETAG(untagged
,tag
);
215 if(should_copy(untagged
))
216 pointer
= RETAG(copy_object_impl(pointer
),tag
);
221 /* Given a pointer to a tagged pointer to oldspace, copy it to newspace.
222 If the object has already been copied, return the forwarding
223 pointer address without copying anything; otherwise, install
224 a new forwarding pointer. */
225 INLINE CELL
copy_object(CELL pointer
)
227 CELL tag
= TAG(pointer
);
228 CELL header
= get(UNTAG(pointer
));
230 if(TAG(header
) == GC_COLLECTED
)
231 return resolve_forwarding(UNTAG(header
),tag
);
233 return RETAG(copy_object_impl(pointer
),tag
);
236 void copy_handle(CELL
*handle
)
238 CELL pointer
= *handle
;
240 if(!immediate_p(pointer
) && should_copy(pointer
))
241 *handle
= copy_object(pointer
);
244 CELL
copy_next_from_nursery(CELL scan
)
246 CELL
*obj
= (CELL
*)scan
;
247 CELL
*end
= (CELL
*)(scan
+ binary_payload_start(scan
));
253 CELL nursery_start
= nursery
.start
;
254 CELL nursery_end
= nursery
.end
;
256 for(; obj
< end
; obj
++)
260 if(!immediate_p(pointer
)
261 && (pointer
>= nursery_start
&& pointer
< nursery_end
))
262 *obj
= copy_object(pointer
);
266 return scan
+ untagged_object_size(scan
);
269 CELL
copy_next_from_aging(CELL scan
)
271 CELL
*obj
= (CELL
*)scan
;
272 CELL
*end
= (CELL
*)(scan
+ binary_payload_start(scan
));
278 CELL tenured_start
= data_heap
->generations
[TENURED
].start
;
279 CELL tenured_end
= data_heap
->generations
[TENURED
].end
;
281 CELL newspace_start
= newspace
->start
;
282 CELL newspace_end
= newspace
->end
;
284 for(; obj
< end
; obj
++)
288 if(!immediate_p(pointer
)
289 && !(pointer
>= newspace_start
&& pointer
< newspace_end
)
290 && !(pointer
>= tenured_start
&& pointer
< tenured_end
))
291 *obj
= copy_object(pointer
);
295 return scan
+ untagged_object_size(scan
);
298 CELL
copy_next_from_tenured(CELL scan
)
300 CELL
*obj
= (CELL
*)scan
;
301 CELL
*end
= (CELL
*)(scan
+ binary_payload_start(scan
));
307 CELL newspace_start
= newspace
->start
;
308 CELL newspace_end
= newspace
->end
;
310 for(; obj
< end
; obj
++)
314 if(!immediate_p(pointer
) && !(pointer
>= newspace_start
&& pointer
< newspace_end
))
315 *obj
= copy_object(pointer
);
319 mark_object_code_block(scan
);
321 return scan
+ untagged_object_size(scan
);
324 void copy_reachable_objects(CELL scan
, CELL
*end
)
326 if(HAVE_NURSERY_P
&& collecting_gen
== NURSERY
)
329 scan
= copy_next_from_nursery(scan
);
331 else if(HAVE_AGING_P
&& collecting_gen
== AGING
)
334 scan
= copy_next_from_aging(scan
);
336 else if(collecting_gen
== TENURED
)
339 scan
= copy_next_from_tenured(scan
);
343 /* Prepare to start copying reachable objects into an unused zone */
344 void begin_gc(CELL requested_bytes
)
346 if(growing_data_heap
)
348 if(collecting_gen
!= TENURED
)
349 critical_error("Invalid parameters to begin_gc",0);
351 old_data_heap
= data_heap
;
352 set_data_heap(grow_data_heap(old_data_heap
,requested_bytes
));
353 newspace
= &data_heap
->generations
[TENURED
];
355 else if(collecting_accumulation_gen_p())
357 /* when collecting one of these generations, rotate it
358 with the semispace */
359 F_ZONE z
= data_heap
->generations
[collecting_gen
];
360 data_heap
->generations
[collecting_gen
] = data_heap
->semispaces
[collecting_gen
];
361 data_heap
->semispaces
[collecting_gen
] = z
;
362 reset_generation(collecting_gen
);
363 newspace
= &data_heap
->generations
[collecting_gen
];
364 clear_cards(collecting_gen
,collecting_gen
);
365 clear_decks(collecting_gen
,collecting_gen
);
366 clear_allot_markers(collecting_gen
,collecting_gen
);
370 /* when collecting a younger generation, we copy
371 reachable objects to the next oldest generation,
372 so we set the newspace so the next generation. */
373 newspace
= &data_heap
->generations
[collecting_gen
+ 1];
377 void end_gc(CELL gc_elapsed
)
379 F_GC_STATS
*s
= &gc_stats
[collecting_gen
];
382 s
->gc_time
+= gc_elapsed
;
383 if(s
->max_gc_time
< gc_elapsed
)
384 s
->max_gc_time
= gc_elapsed
;
386 if(growing_data_heap
)
388 dealloc_data_heap(old_data_heap
);
389 old_data_heap
= NULL
;
390 growing_data_heap
= false;
393 if(collecting_accumulation_gen_p())
395 /* all younger generations except are now empty.
396 if collecting_gen == NURSERY here, we only have 1 generation;
397 old-school Cheney collector */
398 if(collecting_gen
!= NURSERY
)
399 reset_generations(NURSERY
,collecting_gen
- 1);
401 else if(HAVE_NURSERY_P
&& collecting_gen
== NURSERY
)
403 nursery
.here
= nursery
.start
;
407 /* all generations up to and including the one
408 collected are now empty */
409 reset_generations(NURSERY
,collecting_gen
);
412 if(collecting_gen
== TENURED
)
414 /* now that all reachable code blocks have been marked,
415 deallocate the rest */
416 free_unmarked(&code_heap
);
419 collecting_aging_again
= false;
422 /* Collect gen and all younger generations.
423 If growing_data_heap_ is true, we must grow the data heap to such a size that
424 an allocation of requested_bytes won't fail */
425 void garbage_collection(CELL gen
,
426 bool growing_data_heap_
,
427 CELL requested_bytes
)
431 critical_error("GC disabled",gen
);
435 s64 start
= current_micros();
437 performing_gc
= true;
438 growing_data_heap
= growing_data_heap_
;
439 collecting_gen
= gen
;
441 /* we come back here if a generation is full */
444 /* We have no older generations we can try collecting, so we
445 resort to growing the data heap */
446 if(collecting_gen
== TENURED
)
448 growing_data_heap
= true;
450 /* see the comment in unmark_marked() */
451 unmark_marked(&code_heap
);
453 /* we try collecting AGING space twice before going on to
456 && collecting_gen
== AGING
457 && !collecting_aging_again
)
459 collecting_aging_again
= true;
461 /* Collect the next oldest generation */
468 begin_gc(requested_bytes
);
470 /* initialize chase pointer */
471 CELL scan
= newspace
->here
;
473 /* collect objects referenced from stacks and environment */
475 /* collect objects referenced from older generations */
477 /* do some tracing */
478 copy_reachable_objects(scan
,&newspace
->here
);
480 /* don't scan code heap unless it has pointers to this
481 generation or younger */
482 if(collecting_gen
>= last_code_heap_scan
)
486 if(collecting_gen
== TENURED
)
487 update_code_heap_roots();
489 copy_code_heap_roots();
491 if(collecting_accumulation_gen_p())
492 last_code_heap_scan
= collecting_gen
;
494 last_code_heap_scan
= collecting_gen
+ 1;
497 CELL gc_elapsed
= (current_micros() - start
);
501 performing_gc
= false;
506 garbage_collection(TENURED
,false,0);
511 garbage_collection(NURSERY
,false,0);
514 void primitive_gc(void)
519 void primitive_gc_stats(void)
521 GROWABLE_ARRAY(stats
);
524 u64 total_gc_time
= 0;
526 for(i
= 0; i
< MAX_GEN_COUNT
; i
++)
528 F_GC_STATS
*s
= &gc_stats
[i
];
529 GROWABLE_ARRAY_ADD(stats
,allot_cell(s
->collections
));
530 GROWABLE_ARRAY_ADD(stats
,tag_bignum(long_long_to_bignum(s
->gc_time
)));
531 GROWABLE_ARRAY_ADD(stats
,tag_bignum(long_long_to_bignum(s
->max_gc_time
)));
532 GROWABLE_ARRAY_ADD(stats
,allot_cell(s
->collections
== 0 ? 0 : s
->gc_time
/ s
->collections
));
533 GROWABLE_ARRAY_ADD(stats
,allot_cell(s
->object_count
));
534 GROWABLE_ARRAY_ADD(stats
,tag_bignum(long_long_to_bignum(s
->bytes_copied
)));
536 total_gc_time
+= s
->gc_time
;
539 GROWABLE_ARRAY_ADD(stats
,tag_bignum(long_long_to_bignum(total_gc_time
)));
540 GROWABLE_ARRAY_ADD(stats
,tag_bignum(long_long_to_bignum(cards_scanned
)));
541 GROWABLE_ARRAY_ADD(stats
,tag_bignum(long_long_to_bignum(decks_scanned
)));
542 GROWABLE_ARRAY_ADD(stats
,allot_cell(code_heap_scans
));
544 GROWABLE_ARRAY_TRIM(stats
);
548 void clear_gc_stats(void)
551 for(i
= 0; i
< MAX_GEN_COUNT
; i
++)
552 memset(&gc_stats
[i
],0,sizeof(F_GC_STATS
));
559 void primitive_clear_gc_stats(void)
564 void primitive_become(void)
566 F_ARRAY
*new_objects
= untag_array(dpop());
567 F_ARRAY
*old_objects
= untag_array(dpop());
569 CELL capacity
= array_capacity(new_objects
);
570 if(capacity
!= array_capacity(old_objects
))
571 critical_error("bad parameters to become",0);
575 for(i
= 0; i
< capacity
; i
++)
577 CELL old_obj
= array_nth(old_objects
,i
);
578 CELL new_obj
= array_nth(new_objects
,i
);
580 forward_object(old_obj
,new_obj
);