| /* |
| * Copyright (c) 1999 by Silicon Graphics. All rights reserved. |
| * |
| * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED |
| * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. |
| * |
| * Permission is hereby granted to use or copy this program |
| * for any purpose, provided the above notices are retained on all copies. |
| * Permission to modify the code and to distribute modified code is granted, |
| * provided the above notices are retained, and a notice that the code was |
| * modified is included with the above copyright notice. |
| */ |
| |
| #ifdef NURSERY |
| ??? This implementation is incomplete. If you are trying to |
| ??? compile this you are doing something wrong. |
| |
| #include "nursery.h" |
| |
| #define SCAN_STATICS_FOR_NURSERY |
| /* If this is not defined, the collector will not see */ |
| /* references from static data areas to the nursery. */ |
| |
| struct copy_obj { |
| ptr_t forward; /* Forwarding link for copied objects. */ |
| GC_copy_descriptor descr; /* Object descriptor */ |
| word data[1]; |
| } |
| |
| ptr_t GC_nursery_start; /* Start of nursery area. */ |
| /* Must be NURSERY_BLOCK_SIZE */ |
| /* aligned. */ |
| ptr_t GC_nursery_end; /* End of nursery area. */ |
| unsigned char * GC_nursery_map; |
| /* GC_nursery_map[i] != 0 if an object */ |
| /* starts on the ith 64-bit "word" of */ |
| /* nursery. This simple structure has */ |
| /* the advantage that */ |
| /* allocation is cheap. Lookup is */ |
| /* cheap for pointers to the head of */ |
| /* an object, which should be the */ |
| /* usual case. */ |
| # define NURSERY_MAP_NOT_START 0 /* Not start of object. */ |
| # define NURSERY_MAP_START 1 /* Start of object. */ |
| # define NURSERY_MAP_PINNED 2 /* Start of pinned obj. */ |
| |
| # ifdef ALIGN_DOUBLE |
| # define NURSERY_WORD_SIZE (2 * sizeof(word)) |
| # else |
| # define NURSERY_WORD_SIZE sizeof(word) |
| # endif |
| |
| # define NURSERY_BLOCK_SIZE (HBLKSIZE/2) |
| /* HBLKSIZE must be a multiple of NURSERY_BLOCK_SIZE */ |
| # define NURSERY_SIZE (1024 * NURSERY_BLOCK_SIZE) |
| |
| size_t GC_nursery_size = NURSERY_SIZE; |
| /* Must be multiple of NURSERY_BLOCK_SIZE */ |
| |
| size_t GC_nursery_blocks; /* Number of blocks in the nursery. */ |
| |
| unsigned GC_next_nursery_block; /* index of next block we will attempt */ |
| /* allocate from during this cycle. */ |
| /* If it is pinned, we won't actually */ |
| /* use it. */ |
| |
| unsigned short *GC_pinned; /* Number of pinned objects in ith */ |
| /* nursery block. */ |
| /* GC_pinned[i] != 0 if the ith nursery */ |
| /* block is pinned, and thus not used */ |
| /* for allocation. */ |
| |
| GC_copy_alloc_state global_alloc_state = (ptr_t)(-1); /* will overflow. */ |
| |
| /* Array of known rescuing pointers from the heap to the nursery. */ |
| ptr_t ** nursery_rescuers; |
| /* Pointer to one past the last slot in rescuer table */ |
| ptr_t ** nursery_rescuers_end; |
| /* Maximum number of known rescuing pointers. */ |
| # define MAX_NURSERY_RESCUERS 32*1024 |
| /* Add a rescuer to the list */ |
| # define ADD_NURSERY_RESCUER(p) \ |
| if (nursery_rescuers_end >= nursery_rescuers + MAX_NURSERY_RESCUERS) { \ |
| ABORT("Nursery recuers overflow"); /* Fix later !!! */ \ |
| } else { \ |
| *nursery_rescuers_end++ = p; \ |
| } |
| /* Remove rescuer at the given position in the table */ |
| # define REMOVE_RESCUER(p) \ |
| *p = *--nursery_rescuers_end |
| |
| /* Should be called with allocator lock held. */ |
| GC_nursery_init() { |
| GC_nursery_start = GET_MEM(GC_nursery_size); |
| GC_nursery_end = GC_nursery_start + GC_nursery_size; |
| GC_next_nursery_block = 0; |
| if (GC_nursery_start < GC_least_plausible_heap_addr) { |
| GC_least_plausible_heap_addr = GC_nursery_start; |
| } |
| if (GC_nursery_end > GC_greatest_plausible_heap_addr) { |
| GC_greatest_plausible_heap_addr = GC_nursery_end; |
| } |
| if (GC_nursery_start & (NURSERY_BLOCK_SIZE-1)) { |
| GC_err_printf1("Nursery area is misaligned!!"); |
| /* This should be impossible, since GET_MEM returns HBLKSIZE */ |
| /* aligned chunks, and that should be a multiple of */ |
| /* NURSERY_BLOCK_SIZE */ |
| ABORT("misaligned nursery"); |
| } |
| GC_nursery_map = GET_MEM(GC_nursery_size/NURSERY_WORD_SIZE); |
| /* Map is cleared a block at a time when we allocate from the block. */ |
| /* BZERO(GC_nursery_map, GC_nursery_size/NURSERY_WORD_SIZE); */ |
| GC_nursery_blocks = GC_nursery_size/NURSERY_BLOCK_SIZE; |
| GC_pinned = GC_scratch_alloc(GC_nursery_blocks * sizeof(unsigned short)); |
| BZERO(GC_pinned, GC_nursery_blocks); |
| nursery_rescuers = GET_MEM(MAX_NURSERY_RESCUERS * sizeof(ptr_t *)); |
| nursery_rescuers_end = nursery_rescuers; |
| if (0 == GC_nursery_start || 0 == GC_nursery_map || 0 == nursery_rescuers) |
| ABORT("Insufficient memory for nursery"); |
| } |
| |
| #define PIN_OBJ(p) \ |
| if (p >= GC_nursery_start && p < GC_nursery_end) { GC_pin_obj_checked(p); } |
| |
| /* Pin the object at p, if it's in the nursery. */ |
| void GC_pin_obj(ptr_t p) { |
| PIN_OBJ(p); |
| } |
| |
| void (*GC_push_proc)(ptr_t) = 0; |
| |
| /* Pin the object at p, which is known to be in the nursery. */ |
| void GC_pin_obj_checked(ptr_t p) { |
| unsigned offset = p - GC_nursery_start; |
| unsigned word_offset = BYTES_TO_WORDS(offset); |
| unsigned blockno = (current - GC_nursery_start)/NURSERY_BLOCK_SIZE; |
| while (GC_nursery_map[word_offset] == NURSERY_MAP_NOT_START) { |
| --word_offset; |
| } |
| if (GC_nursery_map[word_offset] != NURSERY_MAP_PINNED) { |
| GC_nursery_map[word_offset] = NURSERY_MAP_PINNED; |
| ++GC_pinned[blockno]; |
| ??Push object at GC_nursery_start + WORDS_TO_BYTES(word_offset) |
| ??onto mark stack. |
| } |
| } |
| |
| void GC_scan_region_for_nursery(ptr_t low, ptr_t high) { |
| # if CPP_WORDSZ/8 != ALIGNMENT |
| --> fix this |
| # endif |
| word * l = (word *)((word)low + ALIGNMENT - 1 & ~(ALIGNMENT - 1)); |
| word * h = (word *)((word)high & ~(ALIGNMENT - 1)); |
| word * p; |
| for (p = l; p < h; ++p) { |
| PIN_OBJ(p); |
| } |
| } |
| |
| /* Invoke GC_scan_region_for_nursery on ranges that are not excluded. */ |
| void GC_scan_region_for_nursery_with_exclusions(ptr_t bottom, ptr_t top) |
| { |
| struct exclusion * next; |
| ptr_t excl_start; |
| |
| while (bottom < top) { |
| next = GC_next_exclusion(bottom); |
| if (0 == next || (excl_start = next -> e_start) >= top) { |
| GC_scan_region_for_nursery(bottom, top); |
| return; |
| } |
| if (excl_start > bottom) |
| GC_scan_region_for_nursery(bottom, excl_start); |
| bottom = next -> e_end; |
| } |
| } |
| |
| |
| void GC_scan_stacks_for_nursery(void) { |
| # ifdef THREADS |
| --> fix this |
| # endif |
| # ifdef STACK_GROWS_DOWN |
| ptr_t stack_low = GC_approx_sp(); |
| ptr_t stack_high = GC_stackbottom; |
| # else |
| ptr_t stack_low = GC_stackbottom; |
| ptr_t stack_high = GC_approx_sp(); |
| # endif |
| GC_scan_region_for_nursery(stack_low, stack_high); |
| # ifdef IA64 |
| GC_scan_region_for_nursery(BACKING_STORE_BASE, |
| (ptr_t) GC_save_regs_ret_val); |
| # endif |
| } |
| |
| void GC_scan_roots_for_nursery(void) { |
| /* Scan registers. */ |
| /* Direct GC_push_one to call GC_pin_obj instead of marking */ |
| /* and pushing objects. */ |
| /* This is a bit ugly, but we don't have to touch the */ |
| /* platform-dependent code. */ |
| |
| void (*old_push_proc)(ptr_t) = GC_push_proc; |
| GC_push_proc = GC_pin_obj; |
| GC_push_regs(); |
| GC_push_proc = old_push_proc; |
| GC_scan_stacks_for_nursery(); |
| # ifdef SCAN_STATICS_FOR_NURSERY |
| # if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(PCR)) \ |
| && !defined(SRC_M3) |
| GC_remove_tmp_roots(); |
| GC_register_dynamic_libraries(); |
| # endif |
| /* Mark everything in static data areas */ |
| for (i = 0; i < n_root_sets; i++) { |
| GC_scan_region_for_nursery_with_exclusions ( |
| GC_static_roots[i].r_start, |
| GC_static_roots[i].r_end); |
| } |
| # endif |
| } |
| |
| /* Array of known rescuing pointers from the heap to the nursery. */ |
| ptr_t ** nursery_rescuers; |
| |
| /* Caller holds allocation lock. */ |
| void GC_collect_nursery(void) { |
| int i; |
| ptr_t scan_ptr = 0; |
| STOP_WORLD; |
| for (i = 0; i < GC_nursery_blocks; ++i) GC_pinned[i] = 0; |
| GC_scan_roots_for_nursery(); |
| /* All objects referenced by roots are now pinned. */ |
| /* Their contents are described by */ |
| /* mark stack entries. */ |
| |
| /* Pin blocks corresponding to valid allocation states. */ |
| /* that probably happens automagically if the allocation */ |
| /* states are kept where we can see them. */ |
| /* It will take work if static roots are not scanned. */ |
| /* We want to do this both for correctness and to avoid */ |
| /* promoting very young objects. */ |
| |
| /* Somehow capture dirty bits. Update rescuers array to */ |
| /* reflect newly valid and invalid references from dirty */ |
| /* pages. Other references should remain valid, since the */ |
| /* referents should have been pinned. */ |
| |
| /* Traverse the old object heap. Pin objects in the */ |
| /* nursery that are ambiguously referenced, copy those */ |
| /* that are unambiguously referenced. */ |
| |
| /* Traverse objects in mark stack. */ |
| /* If referenced object is in pinned block, add contents */ |
| /* to mark stack. If referenced object is forwarded, */ |
| /* update pointer. Otherwise reallocate the object in the */ |
| /* old heap, copy its contents, and then enqueue its */ |
| /* contents in the mark stack. */ |
| START_WORLD; |
| } |
| |
| /* Initialize an allocation state so that it can be used for */ |
| /* allocation. This implicitly reserves a small section of the */ |
| /* nursery for use with this allocator. */ |
| /* Also called to replenish an allocator that has been */ |
| /* exhausted. */ |
| void GC_init_copy_alloc_state(GC_copy_alloc_state *) |
| unsigned next_block; |
| ptr_t block_addr; |
| LOCK(); |
| next_block = GC_next_nursery_block; |
| while(is_pinned[next_block] && next_block < GC_nursery_blocks) { |
| ++next_block; |
| } |
| if (next_block < GC_nursery_blocks) { |
| block_addr = GC_nursery_start + NURSERY_BLOCK_SIZE * next_block; |
| GC_next_nursery_block = next_block + 1; |
| BZERO(GC_nursery_map + next_block * |
| (NURSERY_BLOCK_SIZE/NURSERY_WORD_SIZE), |
| NURSERY_BLOCK_SIZE/NURSERY_WORD_SIZE); |
| *GC_copy_alloc_state = block_addr; |
| UNLOCK(); |
| } else { |
| GC_collect_nursery(); |
| GC_next_nursery_block = 0; |
| UNLOCK(); |
| get_new_block(s); |
| } |
| } |
| |
| GC_PTR GC_copying_malloc2(GC_copy_descriptor *d, GC_copy_alloc_state *s) { |
| size_t sz = GC_SIZE_FROM_DESCRIPTOR(d); |
| ptrdiff_t offset; |
| ptr_t result = *s; |
| ptr_t new = result + sz; |
| if (new & COPY_BLOCK_MASK <= result & COPY_BLOCK_MASK> { |
| GC_init_copy_alloc_state(s); |
| result = *s; |
| new = result + sz; |
| GC_ASSERT(new & COPY_BLOCK_MASK > result & COPY_BLOCK_MASK> |
| } |
| (struct copy_obj *)result -> descr = d; |
| (struct copy_obj *)result -> forward = 0; |
| offset = (result - GC_nursery_start)/NURSERY_WORD_SIZE; |
| GC_nursery_map[offset] = NURSERY_MAP_NOT_START; |
| } |
| |
| GC_PTR GC_copying_malloc(GC_copy_descriptor *d) { |
| } |
| |
| #endif /* NURSERY */ |