3 CELL init_zone(F_ZONE *z, CELL size, CELL start)
6 z->start = z->here = start;
11 void init_cards_offset(void)
13 cards_offset = (CELL)data_heap->cards
14 - (data_heap->segment->start >> CARD_BITS);
17 F_DATA_HEAP *alloc_data_heap(CELL gens, CELL young_size, CELL aging_size)
19 young_size = align_page(young_size);
20 aging_size = align_page(aging_size);
22 F_DATA_HEAP *data_heap = safe_malloc(sizeof(F_DATA_HEAP));
23 data_heap->young_size = young_size;
24 data_heap->aging_size = aging_size;
25 data_heap->gen_count = gens;
28 if(data_heap->gen_count == 1)
29 total_size = 2 * aging_size;
30 else if(data_heap->gen_count == 2)
31 total_size = (gens - 1) * young_size + 2 * aging_size;
32 else if(data_heap->gen_count == 3)
33 total_size = gens * young_size + 2 * aging_size;
36 fatal_error("Invalid number of generations",data_heap->gen_count);
37 return NULL; /* can't happen */
40 data_heap->segment = alloc_segment(total_size);
42 data_heap->generations = safe_malloc(sizeof(F_ZONE) * gens);
43 data_heap->semispaces = safe_malloc(sizeof(F_ZONE) * gens);
45 CELL cards_size = total_size / CARD_SIZE;
46 data_heap->cards = safe_malloc(cards_size);
47 data_heap->cards_end = data_heap->cards + cards_size;
49 CELL alloter = data_heap->segment->start;
51 alloter = init_zone(&data_heap->semispaces[NURSERY],0,alloter);
53 alloter = init_zone(&data_heap->generations[TENURED],aging_size,alloter);
54 alloter = init_zone(&data_heap->semispaces[TENURED],aging_size,alloter);
58 if(data_heap->gen_count > 2)
60 alloter = init_zone(&data_heap->generations[AGING],young_size,alloter);
61 alloter = init_zone(&data_heap->semispaces[AGING],young_size,alloter);
63 for(i = gens - 3; i >= 0; i--)
65 alloter = init_zone(&data_heap->generations[i],
71 for(i = gens - 2; i >= 0; i--)
73 alloter = init_zone(&data_heap->generations[i],
78 if(alloter != data_heap->segment->end)
79 critical_error("Bug in alloc_data_heap",alloter);
84 F_DATA_HEAP *grow_data_heap(F_DATA_HEAP *data_heap, CELL requested_bytes)
86 CELL new_young_size = (data_heap->young_size * 2) + requested_bytes;
87 CELL new_aging_size = (data_heap->aging_size * 2) + requested_bytes;
89 return alloc_data_heap(data_heap->gen_count,
94 void dealloc_data_heap(F_DATA_HEAP *data_heap)
96 dealloc_segment(data_heap->segment);
97 free(data_heap->generations);
98 free(data_heap->semispaces);
99 free(data_heap->cards);
103 /* Every card stores the offset of the first object in that card, which must be
104 cleared when a generation has been cleared */
105 void clear_cards(CELL from, CELL to)
107 /* NOTE: reverse order due to heap layout. */
108 F_CARD *last_card = ADDR_TO_CARD(data_heap->generations[from].end);
109 F_CARD *ptr = ADDR_TO_CARD(data_heap->generations[to].start);
110 for(; ptr < last_card; ptr++)
114 void set_data_heap(F_DATA_HEAP *data_heap_)
116 data_heap = data_heap_;
117 nursery = &data_heap->generations[NURSERY];
119 clear_cards(NURSERY,TENURED);
122 void init_data_heap(CELL gens,
127 set_data_heap(alloc_data_heap(gens,young_size,aging_size));
129 extra_roots_region = alloc_segment(getpagesize());
130 extra_roots = extra_roots_region->start - CELLS;
133 minor_collections = 0;
135 secure_gc = secure_gc_;
138 /* Size of the object pointed to by a tagged pointer */
139 CELL object_size(CELL tagged)
141 if(immediate_p(tagged))
144 return untagged_object_size(UNTAG(tagged));
147 /* Size of the object pointed to by an untagged pointer */
148 CELL untagged_object_size(CELL pointer)
150 return align8(unaligned_object_size(pointer));
153 /* Size of the data area of an object pointed to by an untagged pointer */
154 CELL unaligned_object_size(CELL pointer)
156 switch(untag_header(get(pointer)))
161 return array_size(array_capacity((F_ARRAY*)pointer));
162 case BYTE_ARRAY_TYPE:
163 return byte_array_size(
164 byte_array_capacity((F_BYTE_ARRAY*)pointer));
166 return bit_array_size(
167 bit_array_capacity((F_BIT_ARRAY*)pointer));
168 case FLOAT_ARRAY_TYPE:
169 return float_array_size(
170 float_array_capacity((F_FLOAT_ARRAY*)pointer));
172 return string_size(string_capacity((F_STRING*)pointer));
174 return sizeof(F_QUOTATION);
176 return sizeof(F_WORD);
178 return sizeof(F_HASHTABLE);
180 return sizeof(F_VECTOR);
182 return sizeof(F_SBUF);
184 return sizeof(F_RATIO);
186 return sizeof(F_FLOAT);
188 return sizeof(F_COMPLEX);
190 return sizeof(F_DLL);
192 return sizeof(F_ALIEN);
194 return sizeof(F_WRAPPER);
196 return sizeof(F_CURRY);
198 return callstack_size(
199 untag_fixnum_fast(((F_CALLSTACK *)pointer)->length));
201 critical_error("Invalid header",pointer);
202 return -1; /* can't happen */
206 DEFINE_PRIMITIVE(size)
208 box_unsigned_cell(object_size(dpop()));
211 /* Push memory usage statistics in data heap */
212 DEFINE_PRIMITIVE(data_room)
214 F_ARRAY *a = allot_array(ARRAY_TYPE,data_heap->gen_count * 2,F);
217 dpush(tag_fixnum((data_heap->cards_end - data_heap->cards) >> 10));
219 for(gen = 0; gen < data_heap->gen_count; gen++)
221 F_ZONE *z = &data_heap->generations[gen];
222 set_array_nth(a,gen * 2,tag_fixnum((z->end - z->here) >> 10));
223 set_array_nth(a,gen * 2 + 1,tag_fixnum((z->size) >> 10));
226 dpush(tag_object(a));
229 /* Disables GC and activates next-object ( -- obj ) primitive */
230 void begin_scan(void)
232 heap_scan_ptr = data_heap->generations[TENURED].start;
236 DEFINE_PRIMITIVE(begin_scan)
242 CELL next_object(void)
245 general_error(ERROR_HEAP_SCAN,F,F,NULL);
247 CELL value = get(heap_scan_ptr);
248 CELL obj = heap_scan_ptr;
251 if(heap_scan_ptr >= data_heap->generations[TENURED].here)
254 type = untag_header(value);
255 heap_scan_ptr += untagged_object_size(heap_scan_ptr);
257 return RETAG(obj,type <= HEADER_TYPE ? type : OBJECT_TYPE);
260 /* Push object at heap scan cursor and advance; pushes f when done */
261 DEFINE_PRIMITIVE(next_object)
263 dpush(next_object());
267 DEFINE_PRIMITIVE(end_scan)
272 /* Scan all the objects in the card */
273 INLINE void collect_card(F_CARD *ptr, CELL gen, CELL here)
276 CELL offset = (c & CARD_BASE_MASK);
278 if(offset == CARD_BASE_MASK)
281 critical_error("bad card",(CELL)ptr);
286 CELL card_scan = (CELL)CARD_TO_ADDR(ptr) + offset;
287 CELL card_end = (CELL)CARD_TO_ADDR(ptr + 1);
289 while(card_scan < card_end && card_scan < here)
290 card_scan = collect_next(card_scan);
295 /* Copy all newspace objects referenced from marked cards to the destination */
296 INLINE void collect_gen_cards(CELL gen)
298 F_CARD *ptr = ADDR_TO_CARD(data_heap->generations[gen].start);
299 CELL here = data_heap->generations[gen].here;
300 F_CARD *last_card = ADDR_TO_CARD(here - 1);
304 /* if we are collecting the nursery, we care about old->nursery pointers
305 but not old->aging pointers */
306 if(collecting_gen == NURSERY)
308 mask = CARD_POINTS_TO_NURSERY;
310 /* after the collection, no old->nursery pointers remain
311 anywhere, but old->aging pointers might remain in tenured
314 unmask = CARD_POINTS_TO_NURSERY;
315 /* after the collection, all cards in aging space can be
317 else if(HAVE_AGING_P && gen == AGING)
318 unmask = CARD_MARK_MASK;
321 critical_error("bug in collect_gen_cards",gen);
325 /* if we are collecting aging space into tenured space, we care about
326 all old->nursery and old->aging pointers. no old->aging pointers can
328 else if(HAVE_AGING_P && collecting_gen == AGING)
330 if(collecting_aging_again)
332 mask = CARD_POINTS_TO_AGING;
333 unmask = CARD_MARK_MASK;
335 /* after we collect aging space into the aging semispace, no
336 old->nursery pointers remain but tenured space might still have
337 pointers to aging space. */
340 mask = CARD_POINTS_TO_AGING;
341 unmask = CARD_POINTS_TO_NURSERY;
346 critical_error("bug in collect_gen_cards",gen);
350 for(; ptr <= last_card; ptr++)
354 collect_card(ptr,gen,here);
360 /* Scan cards in all generations older than the one being collected, copying
361 old->new references */
362 void collect_cards(void)
365 for(i = collecting_gen + 1; i < data_heap->gen_count; i++)
366 collect_gen_cards(i);
369 /* Copy all tagged pointers in a range of memory */
370 void collect_stack(F_SEGMENT *region, CELL top)
372 CELL bottom = region->start;
375 for(ptr = bottom; ptr <= top; ptr += CELLS)
376 copy_handle((CELL*)ptr);
379 void collect_stack_frame(F_STACK_FRAME *frame)
381 if(frame_type(frame) == QUOTATION_TYPE)
383 CELL scan = frame->scan - frame->array;
384 copy_handle(&frame->array);
385 frame->scan = scan + frame->array;
389 recursive_mark(frame->xt);
392 /* The base parameter allows us to adjust for a heap-allocated
393 callstack snapshot */
394 void collect_callstack(F_CONTEXT *stacks)
396 CELL top = (CELL)stacks->callstack_top;
397 CELL bottom = (CELL)stacks->callstack_bottom;
399 iterate_callstack(top,bottom,base,collect_stack_frame);
402 /* Copy roots over at the start of GC, namely various constants, stacks,
403 the user environment and extra roots registered with REGISTER_ROOT */
404 void collect_roots(void)
410 copy_handle(&bignum_zero);
411 copy_handle(&bignum_pos_one);
412 copy_handle(&bignum_neg_one);
414 collect_stack(extra_roots_region,extra_roots);
417 stacks = stack_chain;
421 collect_stack(stacks->datastack_region,stacks->datastack);
422 collect_stack(stacks->retainstack_region,stacks->retainstack);
424 copy_handle(&stacks->catchstack_save);
425 copy_handle(&stacks->current_callback_save);
427 collect_callstack(stacks);
429 stacks = stacks->next;
432 for(i = 0; i < USER_ENV; i++)
433 copy_handle(&userenv[i]);
436 /* Given a pointer to oldspace, copy it to newspace */
437 INLINE void *copy_untagged_object(void *pointer, CELL size)
440 if(newspace->here + size >= newspace->end)
442 allot_barrier(newspace->here);
443 newpointer = allot_zone(newspace,size);
444 memcpy(newpointer,pointer,size);
448 INLINE void forward_object(CELL pointer, CELL newpointer)
450 put(UNTAG(pointer),RETAG(newpointer,GC_COLLECTED));
453 INLINE CELL copy_object_impl(CELL pointer)
455 CELL newpointer = (CELL)copy_untagged_object(
456 (void*)UNTAG(pointer),
457 object_size(pointer));
458 forward_object(pointer,newpointer);
462 /* Follow a chain of forwarding pointers */
463 CELL resolve_forwarding(CELL untagged, CELL tag)
465 CELL header = get(untagged);
466 /* another forwarding pointer */
467 if(TAG(header) == GC_COLLECTED)
468 return resolve_forwarding(UNTAG(header),tag);
469 /* we've found the destination */
472 CELL pointer = RETAG(untagged,tag);
473 if(should_copy(untagged))
474 pointer = RETAG(copy_object_impl(pointer),tag);
479 /* Given a pointer to a tagged pointer to oldspace, copy it to newspace.
480 If the object has already been copied, return the forwarding
481 pointer address without copying anything; otherwise, install
482 a new forwarding pointer. */
483 INLINE CELL copy_object(CELL pointer)
485 CELL tag = TAG(pointer);
486 CELL header = get(UNTAG(pointer));
488 if(TAG(header) == GC_COLLECTED)
489 return resolve_forwarding(UNTAG(header),tag);
491 return RETAG(copy_object_impl(pointer),tag);
494 void copy_handle(CELL *handle)
496 CELL pointer = *handle;
498 if(!immediate_p(pointer) && should_copy(pointer))
499 *handle = copy_object(pointer);
502 /* The number of cells from the start of the object which should be scanned by
503 the GC. Some types have a binary payload at the end (string, word, DLL) which
505 CELL binary_payload_start(CELL pointer)
507 switch(untag_header(get(pointer)))
509 /* these objects do not refer to other objects at all */
512 case BYTE_ARRAY_TYPE:
514 case FLOAT_ARRAY_TYPE:
518 /* these objects have some binary data at the end */
520 return sizeof(F_WORD) - CELLS;
526 return sizeof(F_QUOTATION) - CELLS;
527 /* everything else consists entirely of pointers */
529 return unaligned_object_size(pointer);
533 void collect_callstack_object(F_CALLSTACK *callstack)
535 iterate_callstack_object(callstack,collect_stack_frame);
538 CELL collect_next(CELL scan)
540 do_slots(scan,copy_handle);
542 /* Special behaviors */
547 switch(object_type(scan))
550 word = (F_WORD *)scan;
551 if(collecting_code && word->compiledp != F)
552 recursive_mark(word->xt);
555 quot = (F_QUOTATION *)scan;
556 if(collecting_code && quot->xt != NULL)
557 recursive_mark(quot->xt);
560 stack = (F_CALLSTACK *)scan;
561 collect_callstack_object(stack);
565 return scan + untagged_object_size(scan);
568 INLINE void reset_generation(CELL i)
570 F_ZONE *z = &data_heap->generations[i];
573 memset((void*)z->start,69,z->size);
576 /* After garbage collection, any generations which are now empty need to have
577 their allocation pointers and cards reset. */
578 void reset_generations(CELL from, CELL to)
581 for(i = from; i <= to; i++) reset_generation(i);
582 clear_cards(from,to);
585 /* Prepare to start copying reachable objects into an unused zone */
586 void begin_gc(CELL requested_bytes)
588 if(growing_data_heap)
590 if(collecting_gen != TENURED)
591 critical_error("Invalid parameters to begin_gc",0);
593 old_data_heap = data_heap;
594 set_data_heap(grow_data_heap(old_data_heap,requested_bytes));
595 newspace = &data_heap->generations[collecting_gen];
597 else if(collecting_accumulation_gen_p())
599 /* when collecting one of these generations, rotate it
600 with the semispace */
601 F_ZONE z = data_heap->generations[collecting_gen];
602 data_heap->generations[collecting_gen] = data_heap->semispaces[collecting_gen];
603 data_heap->semispaces[collecting_gen] = z;
604 reset_generation(collecting_gen);
605 newspace = &data_heap->generations[collecting_gen];
606 clear_cards(collecting_gen,collecting_gen);
610 /* when collecting a younger generation, we copy
611 reachable objects to the next oldest generation,
612 so we set the newspace so the next generation. */
613 newspace = &data_heap->generations[collecting_gen + 1];
617 void major_gc_message(void)
619 fprintf(stderr,"*** %s GC (%ld minor, %ld cards)\n",
620 collecting_code ? "Code and data" : "Data",
621 minor_collections,cards_scanned);
623 minor_collections = 0;
629 if(growing_data_heap)
631 dealloc_data_heap(old_data_heap);
632 old_data_heap = NULL;
633 growing_data_heap = false;
635 fprintf(stderr,"*** Data heap resized to %lu bytes\n",
636 data_heap->segment->size);
639 if(collecting_accumulation_gen_p())
641 /* all younger generations except are now empty.
642 if collecting_gen == NURSERY here, we only have 1 generation;
643 old-school Cheney collector */
644 if(collecting_gen != NURSERY)
645 reset_generations(NURSERY,collecting_gen - 1);
647 if(collecting_gen == TENURED)
649 else if(HAVE_AGING_P && collecting_gen == AGING)
654 /* all generations up to and including the one
655 collected are now empty */
656 reset_generations(NURSERY,collecting_gen);
663 /* now that all reachable code blocks have been marked,
664 deallocate the rest */
665 free_unmarked(&code_heap);
668 collecting_aging_again = false;
671 /* Collect gen and all younger generations.
672 If growing_data_heap_ is true, we must grow the data heap to such a size that
673 an allocation of requested_bytes won't fail */
674 void garbage_collection(CELL gen,
676 bool growing_data_heap_,
677 CELL requested_bytes)
681 critical_error("GC disabled",gen);
685 s64 start = current_millis();
687 performing_gc = true;
688 collecting_code = code_gc;
689 growing_data_heap = growing_data_heap_;
690 collecting_gen = gen;
692 /* we come back here if a generation is full */
695 /* We have no older generations we can try collecting, so we
696 resort to growing the data heap */
697 if(collecting_gen == TENURED)
699 growing_data_heap = true;
701 /* see the comment in unmark_marked() */
703 unmark_marked(&code_heap);
705 /* we try collecting AGING space twice before going on to
708 && collecting_gen == AGING
709 && !collecting_aging_again)
711 collecting_aging_again = true;
713 /* Collect the next oldest generation */
720 begin_gc(requested_bytes);
722 /* initialize chase pointer */
723 CELL scan = newspace->here;
725 /* collect objects referenced from stacks and environment */
728 /* collect objects referenced from older generations */
733 /* don't scan code heap unless it has pointers to this
734 generation or younger */
735 if(collecting_gen >= last_code_heap_scan)
737 /* if we are doing code GC, then we will copy over
738 literals from any code block which gets marked as live.
739 if we are not doing code GC, just consider all literals
742 if(collecting_accumulation_gen_p())
743 last_code_heap_scan = collecting_gen;
745 last_code_heap_scan = collecting_gen + 1;
749 while(scan < newspace->here)
750 scan = collect_next(scan);
754 gc_time += (current_millis() - start);
755 performing_gc = false;
760 garbage_collection(TENURED,false,false,0);
763 DEFINE_PRIMITIVE(data_gc)
768 /* Push total time spent on GC */
769 DEFINE_PRIMITIVE(gc_time)
771 box_unsigned_8(gc_time);
779 DEFINE_PRIMITIVE(become)
781 F_ARRAY *new_objects = untag_array(dpop());
782 F_ARRAY *old_objects = untag_array(dpop());
784 CELL capacity = array_capacity(new_objects);
785 if(capacity != array_capacity(old_objects))
786 critical_error("bad parameters to become",0);
790 for(i = 0; i < capacity; i++)
792 CELL old_obj = array_nth(old_objects,i);
793 CELL new_obj = array_nth(new_objects,i);
795 forward_object(old_obj,new_obj);