* Extension to GENCGC which provides for pages of objects
* that are static in placement but subject to reclamation.
*/
* This software is part of the SBCL system. See the README file for
* more information.
*
* This software is derived from the CMU CL system, which was
* written at Carnegie Mellon University and released into the
* public domain. The software is in the public domain and is
* provided with absolutely no warranty. See the COPYING and CREDITS
* files for more information.
*/
* TODO:
* 1. Space accounting (GET-BYTES-CONSED etc)
* 2. Heuristic for auto-trigger. (Can't yet because no space accounting)
* Currently happens with regular GC trigger mechanism.
* 3. Specify space size on startup
*/
#if (defined(__clang__) && (__clang_major__ == 6) && (__clang_minor__ == 0))
#define _FORTIFY_SOURCE 0
#endif
#include "gc.h"
#include "gc-internal.h"
#include "gc-private.h"
#include "genesis/gc-tables.h"
#include "genesis/cons.h"
#include "genesis/vector.h"
#include "genesis/layout.h"
#include "forwarding-ptr.h"
#include "getallocptr.h"
#include "var-io.h"
#include "immobile-space.h"
#include "unaligned.h"
#include "code.h"
#include "lispstring.h"
#include <stdlib.h>
#include <stdio.h>
#define WORDS_PER_PAGE ((int)IMMOBILE_CARD_BYTES/N_WORD_BYTES)
#define DOUBLEWORDS_PER_PAGE (WORDS_PER_PAGE/2)
#define DEFRAGMENT_FIXEDOBJ_SUBSPACE 1
#define WRITABLE_TEXT_SEGMENT 0
#undef DEBUG
#undef VERIFY_PAGE_GENS
#ifdef DEBUG
# define dprintf(arg) fprintf arg
FILE * logfile;
#else
# define dprintf(arg)
#endif
static void defrag_immobile_space(boolean verbose);
uword_t FIXEDOBJ_SPACE_START, VARYOBJ_SPACE_START;
uword_t immobile_space_lower_bound, immobile_space_max_offset;
uword_t immobile_range_1_max_offset, immobile_range_2_min_offset;
unsigned int varyobj_space_size = VARYOBJ_SPACE_SIZE;
struct fixedobj_page *fixedobj_pages;
lispobj* immobile_scav_queue;
int immobile_scav_queue_head;
unsigned int immobile_scav_queue_count;
#define QCAPACITY 1024
#define gens attr.parts.gens_
#define WRITE_PROTECT 0x80
#define WRITE_PROTECT_CLEARED 0x40
#define MAKE_ATTR(spacing) ((spacing)<<8)
#define OBJ_SPACING(attr) ((attr>>8) & 0xFF)
#define ATTRIBUTES_MATCH_P(page_attr,specified_attr) \
((page_attr & 0xFFFF3F) == specified_attr)
#define SET_WP_FLAG(index,flag) \
fixedobj_pages[index].attr.parts.flags = (fixedobj_pages[index].attr.parts.flags & 0x3F) | flag
#define set_page_full(i) fixedobj_pages[i].free_index = IMMOBILE_CARD_BYTES
#define page_full_p(i) (fixedobj_pages[i].free_index >= (int)IMMOBILE_CARD_BYTES)
#define fixedobj_page_wp(i) (fixedobj_pages[i].attr.parts.flags & WRITE_PROTECT)
* starting at 'base' and stepping by 'spacing_bytes' bytes */
static inline lispobj* compute_fixedobj_limit(void* base, int spacing_bytes) {
return (lispobj*)((char*)base + IMMOBILE_CARD_BYTES - spacing_bytes);
}
#define NEXT_FIXEDOBJ(where, spacing_bytes) \
(where = (lispobj*)((char*)where + spacing_bytes))
unsigned int* varyobj_page_touched_bits;
static int n_bitmap_elts;
boolean immobile_card_protected_p(void* addr)
{
low_page_index_t page;
page = find_varyobj_page_index(addr);
if (page >= 0) return !((varyobj_page_touched_bits[page/32] >> (page&31)) & 1);
page = find_fixedobj_page_index(addr);
if (page >= 0) return fixedobj_page_wp(page);
lose("immobile_card_protected_p(%p)", addr);
}
struct varyobj_page *varyobj_pages;
lispobj varyobj_holes;
#define varyobj_page_touched(x) ((varyobj_page_touched_bits[x/32] >> (x&31)) & 1)
#ifdef VERIFY_PAGE_GENS
void check_fixedobj_page(low_page_index_t, generation_index_t, generation_index_t);
void check_varyobj_pages();
#endif
including at most one object that starts before the page but ends on
or after it.
If the scan start is within the page, i.e. less than DOUBLEWORDS_PER_PAGE
(note that the scan start is measured relative to the page end) then
we don't need to OR in the generation byte from an extra object,
as all headers on the page are accounted for in the page generation mask.
Also an empty page (where scan start is zero) avoids looking
at the next page's first object by accident via the same test. */
unsigned char varyobj_page_gens_augmented(low_page_index_t page_index)
{
return (varyobj_pages[page_index].scan_start_offset <= DOUBLEWORDS_PER_PAGE
? 0 : (1<<immobile_obj_generation(varyobj_scan_start(page_index))))
| varyobj_pages[page_index].generations;
}
starting with 'hint_page' and wrapping around.
'attributes' determine an eligible page.
*FIXEDOBJ-SPACE-FREE-POINTER* is updated to point beyond the found page
if it previously did not. */
static int get_freeish_page(int hint_page, int attributes)
{
int page = hint_page;
lispobj *new_free_pointer, *old_free_pointer, *actual_old;
int page_attr_packed;
unsigned char best_genmask = 0xff;
int best_page = -1;
const int npages = FIXEDOBJ_SPACE_SIZE / IMMOBILE_CARD_BYTES;
do {
page_attr_packed = fixedobj_pages[page].attr.packed;
if (page_attr_packed == 0)
if ((page_attr_packed =
__sync_val_compare_and_swap(&fixedobj_pages[page].attr.packed,
0, attributes)) == 0) {
new_free_pointer = fixedobj_page_address(page+1);
old_free_pointer = fixedobj_free_pointer;
while (new_free_pointer > old_free_pointer) {
actual_old =
__sync_val_compare_and_swap(&fixedobj_free_pointer,
old_free_pointer,
new_free_pointer);
if (actual_old == old_free_pointer)
break;
old_free_pointer = actual_old;
}
return page;
}
if (ATTRIBUTES_MATCH_P(page_attr_packed, attributes)
&& !page_full_p(page)) {
if (fixedobj_pages[page].gens < (1<<PSEUDO_STATIC_GENERATION)) {
return page;
} else if (fixedobj_pages[page].gens < best_genmask) {
best_genmask = fixedobj_pages[page].gens;
best_page = page;
}
}
if (++page >= npages) page = 0;
} while (page != hint_page);
if (best_page >= 0)
return best_page;
lose("No more immobile pages available");
}
#define MAX_ALLOCATOR_SIZE_CLASSES 10
long fixedobj_page_hint[MAX_ALLOCATOR_SIZE_CLASSES];
long immobile_alloc_collisions;
for an object on a page with page_attributes. Write its header word
and return a C (native) pointer. The start page MUST have the proper
characteristisc, but might be totally full.
Precondition: Lisp has established a pseudo-atomic section. */
than what is currently implemented:
- hint should be the address of a word that you try to claim
as an object header; it moves from high-to-low instead of low-to-high.
It's easier to compute the page base than the last valid object start
if there are some wasted words at the end due to page size not being
a perfect multiple of object size.
- you do a CAS into that word, and either suceed or fail
- if you succeed, subtract the object spacing and compare
to the page's base address, which can be computed by
masking. if the next address is above or equal to the page start,
store it in the hint, otherwise mark the page full */
lispobj AMD64_SYSV_ABI
alloc_immobile_fixedobj(int size_class, int spacing_words, uword_t header)
{
size_class = fixnum_value(size_class);
spacing_words = fixnum_value(spacing_words);
header = fixnum_value(header);
int page;
lispobj word;
char * page_data, * obj_ptr, * next_obj_ptr, * limit, * next_free;
int page_attributes = MAKE_ATTR(spacing_words);
int spacing_in_bytes = spacing_words << WORD_SHIFT;
const int npages = FIXEDOBJ_SPACE_SIZE / IMMOBILE_CARD_BYTES;
page = fixedobj_page_hint[size_class];
if (!page) page = get_freeish_page(0, page_attributes);
gc_dcheck(fixedobj_page_address(page) < (void*)fixedobj_free_pointer);
do {
page_data = fixedobj_page_address(page);
obj_ptr = page_data + fixedobj_pages[page].free_index;
limit = page_data + IMMOBILE_CARD_BYTES - spacing_in_bytes;
while (obj_ptr <= limit) {
word = *(lispobj*)obj_ptr;
next_obj_ptr = obj_ptr + spacing_in_bytes;
if (fixnump(word)
&& __sync_bool_compare_and_swap((lispobj*)obj_ptr,
word, header)) {
fixedobj_pages[page].free_index = next_obj_ptr + word - page_data;
return compute_lispobj((lispobj*)obj_ptr);
}
next_free = page_data + fixedobj_pages[page].free_index;
obj_ptr = next_free > next_obj_ptr ? next_free : next_obj_ptr;
}
set_page_full(page);
int old_page = page;
page = get_freeish_page(page+1 >= npages ? 0 : page+1,
page_attributes);
__sync_val_compare_and_swap(&fixedobj_page_hint[size_class],
old_page, page);
} while (1);
}
Example: Conside the freelist initially pointing to word index 6
Threads A, and B, and C each want to claim index 6.
- Thread A wins and then is switched out immediately after the CAS.
- Thread B fails to claim cell 6, claims cell 12 instead.
- Thread C fails to claim a cell and is switched out immediately
after the CAS.
- Thread B writes the index of the next hole, cell 18 into the
page's freelist cell.
- Thread A wakes up and writes 12 into the freelist cell.
- Thread C wakes up sees 12 for next_offset. 12 is greater than 6,
so it sets its next probe location to 12.
It fails the fixnump(header) test.
- Thread C sees that next_offset is still 12,
so it skips by the page's object spacing instead, and will continue
to do so until hitting the end of the page.
*/
#define calc_max_used_fixedobj_page() find_fixedobj_page_index(fixedobj_free_pointer-1)
#define calc_max_used_varyobj_page() find_varyobj_page_index(varyobj_free_pointer-1)
void update_immobile_nursery_bits()
{
low_page_index_t max_used_fixedobj_page = calc_max_used_fixedobj_page();
low_page_index_t page;
if (ENABLE_PAGE_PROTECTION) {
os_protect((os_vm_address_t)FIXEDOBJ_SPACE_START,
(lispobj)fixedobj_free_pointer - FIXEDOBJ_SPACE_START,
OS_VM_PROT_ALL);
}
for (page=0; page <= max_used_fixedobj_page ; ++page) {
if (fixedobj_pages[page].free_index >> WORD_SHIFT !=
fixedobj_pages[page].prior_gc_free_word_index)
fixedobj_pages[page].gens |= 1;
#ifdef VERIFY_PAGE_GENS
check_fixedobj_page(page, 0xff, 0xff);
#endif
}
#ifdef VERIFY_PAGE_GENS
check_varyobj_pages();
#endif
}
void
enliven_immobile_obj(lispobj *ptr, int rescan)
{
gc_assert(widetag_of(ptr) != SIMPLE_FUN_WIDETAG);
gc_assert(immobile_obj_gen_bits(ptr) == from_space);
int pointerish = !leaf_obj_widetag_p(widetag_of(ptr));
int bits = (pointerish ? 0 : IMMOBILE_OBJ_VISITED_FLAG);
if (widetag_of(ptr) == CODE_HEADER_WIDETAG)
bits |= OBJ_WRITTEN_FLAG;
assign_generation(ptr, bits | new_space);
low_page_index_t page_index = find_fixedobj_page_index(ptr);
boolean varyobj = 0;
if (page_index < 0) {
page_index = find_varyobj_page_index(ptr);
gc_assert(page_index >= 0);
varyobj_pages[page_index].generations |= 1<<new_space;
varyobj = 1;
} else {
fixedobj_pages[page_index].gens |= 1<<new_space;
}
if (!rescan) {
if (pointerish) {
if (varyobj)
varyobj_page_touched_bits[page_index/32] |= 1U << (page_index & 31);
else
SET_WP_FLAG(page_index, WRITE_PROTECT_CLEARED);
}
return;
}
if (!pointerish || immobile_scav_queue_count > QCAPACITY) return;
if (immobile_scav_queue_count < QCAPACITY) {
immobile_scav_queue[immobile_scav_queue_head] = (lispobj)ptr;
immobile_scav_queue_head = (immobile_scav_queue_head + 1) & (QCAPACITY - 1);
}
++immobile_scav_queue_count;
}
live by promotion. But if the object is not in the generation
being collected, do nothing */
boolean immobile_space_preserve_pointer(void* addr)
{
unsigned char genmask = compacting_p() ? 1<<from_space : 0xff;
lispobj* object_start;
int valid = 0;
low_page_index_t page_index = find_varyobj_page_index(addr);
if (page_index >= 0) {
lispobj* scan_start;
valid = addr < (void*)varyobj_free_pointer
&& (varyobj_page_gens_augmented(page_index) & genmask)
&& (scan_start = varyobj_scan_start(page_index)) <= (lispobj*)addr
&& (object_start = gc_search_space(scan_start, addr)) != 0
* search_immobile_space which can not */
&& !filler_obj_p(object_start)
&& (instruction_ptr_p(addr, object_start)
|| properly_tagged_descriptor_p(addr, object_start));
} else if ((page_index = find_fixedobj_page_index(addr)) >= FIXEDOBJ_RESERVED_PAGES
&& ((fixedobj_pages[page_index].gens & genmask) != 0)) {
int obj_spacing = fixedobj_page_obj_align(page_index);
int obj_index = ((uword_t)addr & (IMMOBILE_CARD_BYTES-1)) / obj_spacing;
dprintf((logfile,"Pointer %p is to immobile page %d, object %d\n",
addr, page_index, obj_index));
char* page_start_addr = PTR_ALIGN_DOWN(addr, IMMOBILE_CARD_BYTES);
object_start = (lispobj*)(page_start_addr + obj_index * obj_spacing);
valid = !fixnump(*object_start)
&& (widetag_of(object_start) == FUNCALLABLE_INSTANCE_WIDETAG ||
widetag_of(object_start) == FDEFN_WIDETAG ||
properly_tagged_descriptor_p(addr, object_start));
} else {
return 0;
}
if (valid && (!compacting_p() ||
immobile_obj_gen_bits(object_start) == from_space)) {
dprintf((logfile,"immobile obj @ %p (<- %p) is conservatively live\n",
object_start, addr));
if (compacting_p())
enliven_immobile_obj(object_start, 0);
else
gc_mark_obj(compute_lispobj(object_start));
return 1;
}
return 0;
}
static void full_scavenge_immobile_newspace()
{
page_index_t page;
unsigned char bit = 1<<new_space;
low_page_index_t max_used_fixedobj_page = calc_max_used_fixedobj_page();
for (page = FIXEDOBJ_RESERVED_PAGES; page <= max_used_fixedobj_page; ++page) {
if (!(fixedobj_pages[page].gens & bit)) continue;
int obj_spacing = fixedobj_page_obj_align(page);
lispobj* obj = fixedobj_page_address(page);
lispobj* limit = compute_fixedobj_limit(obj, obj_spacing);
do {
if (!fixnump(*obj) && immobile_obj_gen_bits(obj) == new_space) {
set_visited(obj);
lispobj header = *obj;
scavtab[header_widetag(header)](obj, header);
}
} while (NEXT_FIXEDOBJ(obj, obj_spacing) <= limit);
}
low_page_index_t max_used_varyobj_page = calc_max_used_varyobj_page();
page = -1;
while (1) {
do {
if (++page > max_used_varyobj_page) return;
} while ((varyobj_pages[page].generations & bit) == 0);
lispobj* obj = varyobj_scan_start(page);
do {
lispobj* limit = (lispobj*)varyobj_page_address(page) + WORDS_PER_PAGE;
int n_words;
for ( ; obj < limit ; obj += n_words ) {
lispobj header = *obj;
if (immobile_obj_gen_bits(obj) == new_space) {
set_visited(obj);
n_words = scavtab[header_widetag(header)](obj, header);
} else {
n_words = sizetab[header_widetag(header)](obj);
}
}
page = find_varyobj_page_index(obj);
if (page < 0) return;
} while (varyobj_pages[page].generations & bit);
}
}
void scavenge_immobile_newspace()
{
while (immobile_scav_queue_count) {
if (immobile_scav_queue_count > QCAPACITY) {
immobile_scav_queue_count = 0;
full_scavenge_immobile_newspace();
} else {
int queue_index_from = (immobile_scav_queue_head - immobile_scav_queue_count)
& (QCAPACITY - 1);
int queue_index_to = immobile_scav_queue_head;
int i = queue_index_from;
do {
lispobj* obj = (lispobj*)(uword_t)immobile_scav_queue[i];
i = (1 + i) & (QCAPACITY-1);
if (immobile_scav_queue_count <= QCAPACITY)
--immobile_scav_queue_count;
if (!(immobile_obj_gen_bits(obj) & IMMOBILE_OBJ_VISITED_FLAG)) {
set_visited(obj);
lispobj header = *obj;
scavtab[header_widetag(header)](obj, header);
}
} while (i != queue_index_to);
}
}
}
static int next_varyobj_root_page(unsigned int page_index,
unsigned int end_bitmap_index,
unsigned char genmask)
{
unsigned int map_index = page_index / 32;
if (map_index >= end_bitmap_index) return -1;
int bit_index = page_index & 31;
unsigned int word = (0xFFFFFFFFU << bit_index) & varyobj_page_touched_bits[map_index];
while (1) {
if (word) {
bit_index = ffs(word) - 1;
page_index = map_index * 32 + bit_index;
if (varyobj_page_gens_augmented(page_index) & genmask)
return page_index;
else {
word ^= (1U<<bit_index);
continue;
}
}
if (++map_index >= end_bitmap_index) return -1;
word = varyobj_page_touched_bits[map_index];
}
}
void
scavenge_immobile_roots(generation_index_t min_gen, generation_index_t max_gen)
{
int genmask = ((1 << (max_gen - min_gen + 1)) - 1) << min_gen;
low_page_index_t max_used_fixedobj_page = calc_max_used_fixedobj_page();
low_page_index_t page;
for (page = FIXEDOBJ_RESERVED_PAGES; page <= max_used_fixedobj_page ; ++page) {
if (fixedobj_page_wp(page) || !(fixedobj_pages[page].gens & genmask))
continue;
int obj_spacing = fixedobj_page_obj_align(page);
lispobj* obj = fixedobj_page_address(page);
lispobj* limit = compute_fixedobj_limit(obj, obj_spacing);
int gen;
do {
if (!fixnump(*obj) && (genmask >> (gen=immobile_obj_gen_bits(obj)) & 1)) {
if (gen == new_space) { set_visited(obj); }
lispobj header = *obj;
scavtab[header_widetag(header)](obj, header);
}
} while (NEXT_FIXEDOBJ(obj, obj_spacing) <= limit);
}
low_page_index_t max_used_varyobj_page = calc_max_used_varyobj_page();
unsigned n_varyobj_pages = 1+max_used_varyobj_page;
unsigned end_bitmap_index = (n_varyobj_pages+31)/32;
page = next_varyobj_root_page(0, end_bitmap_index, genmask);
while (page >= 0) {
lispobj* obj = varyobj_scan_start(page);
do {
lispobj* limit = (lispobj*)varyobj_page_address(page) + WORDS_PER_PAGE;
int n_words, gen;
for ( ; obj < limit ; obj += n_words ) {
lispobj header = *obj;
if (genmask >> (gen=immobile_obj_gen_bits(obj)) & 1) {
if (gen == new_space) { set_visited(obj); }
n_words = scavtab[header_widetag(header)](obj, header);
} else {
n_words = sizetab[header_widetag(header)](obj);
}
}
page = find_varyobj_page_index(obj);
} while (page > 0
&& (varyobj_pages[page].generations & genmask)
&& varyobj_page_touched(page));
if (page < 0) break;
page = next_varyobj_root_page(1+page, end_bitmap_index, genmask);
}
if (sb_sprof_enabled) {
lispobj* where = (lispobj*)VARYOBJ_SPACE_START;
lispobj* limit = varyobj_free_pointer;
while (where < limit) {
if (widetag_of(where) == CODE_HEADER_WIDETAG
&& immobile_obj_gen_bits(where) == from_space
&& code_serialno((struct code*)where) != 0)
enliven_immobile_obj(where, 1);
where += sizetab[widetag_of(where)](where);
}
}
scavenge_immobile_newspace();
}
void write_protect_immobile_space()
{
immobile_scav_queue_head = 0;
if (!ENABLE_PAGE_PROTECTION)
return;
int i, start = -1, end = -1;
low_page_index_t max_used_fixedobj_page = calc_max_used_fixedobj_page();
for (i = max_used_fixedobj_page ; i >= 0 ; --i) {
if (fixedobj_page_wp(i)) {
if (end < 0) end = i;
start = i;
}
if (end >= 0 && (!fixedobj_page_wp(i) || i == 0)) {
os_protect(fixedobj_page_address(start),
IMMOBILE_CARD_BYTES * (1 + end - start),
OS_VM_PROT_READ|OS_VM_PROT_EXECUTE);
start = end = -1;
}
}
}
static inline generation_index_t
pointee_gen(lispobj thing, int keep_gen, int new_gen)
{
int to_page = find_page_index((void*)thing);
int gen = 127;
if (to_page >= 0) {
gen = page_table[to_page].gen;
if (gen == PSEUDO_STATIC_GENERATION+1)
gen = new_gen;
} else if (immobile_space_p(thing)) {
gen = immobile_obj_gen_bits(base_pointer(thing));
if (gen == keep_gen)
gen = new_gen;
}
return gen;
}
static int
younger_p(lispobj thing, int gen, int keep_gen, int new_gen)
{
return is_lisp_pointer(thing) && pointee_gen(thing, keep_gen, new_gen) < gen;
}
static int
range_points_to_younger_p(lispobj* obj, lispobj* end,
int gen, int keep_gen, int new_gen)
{
#ifdef DEBUG
lispobj* __attribute__((unused)) saved_obj = obj, __attribute__((unused)) header = *obj;
#endif
do {
lispobj thing = *obj;
if (is_lisp_pointer(thing) && pointee_gen(thing, keep_gen, new_gen) < gen)
return 1;
} while (++obj < end);
return 0;
}
static boolean
fixedobj_points_to_younger_p(lispobj* obj, int n_words,
int gen, int keep_gen, int new_gen)
{
lispobj layout;
switch (widetag_of(obj)) {
case FDEFN_WIDETAG:
return younger_p(fdefn_callee_lispobj((struct fdefn*)obj),
gen, keep_gen, new_gen)
|| range_points_to_younger_p(obj+1, obj+3, gen, keep_gen, new_gen);
case CODE_HEADER_WIDETAG:
return younger_p(((struct code*)obj)->debug_info, gen, keep_gen, new_gen);
case INSTANCE_WIDETAG:
case FUNCALLABLE_INSTANCE_WIDETAG:
layout = instance_layout(obj);
if (!layout) return 0;
if (younger_p(layout, gen, keep_gen, new_gen))
return 1;
struct bitmap bitmap = get_layout_bitmap(LAYOUT(layout));
gc_assert(bitmap.nwords == 1);
if (bitmap.bits[0] != (sword_t)-1) {
sword_t mask = bitmap.bits[0];
lispobj* where = obj + 1;
lispobj* limit = obj + n_words;
for ( ; where < limit ; ++where, mask >>= 1 )
if ((mask & 1) != 0 && younger_p(*where, gen, keep_gen, new_gen))
return 1;
return 0;
}
}
return range_points_to_younger_p(obj+1, obj+n_words, gen, keep_gen, new_gen);
}
static boolean
varyobj_points_to_younger_p(lispobj* obj, int gen, int keep_gen, int new_gen,
os_vm_address_t page_begin,
os_vm_address_t page_end)
{
lispobj *begin, *end, word = *obj;
unsigned char widetag = header_widetag(word);
if (widetag == CODE_HEADER_WIDETAG) {
return header_rememberedp(word);
} else if (widetag == FDEFN_WIDETAG ||
widetag == FUNCALLABLE_INSTANCE_WIDETAG) {
return fixedobj_points_to_younger_p(obj, sizetab[widetag](obj),
gen, keep_gen, new_gen);
} else if (widetag == SIMPLE_VECTOR_WIDETAG) {
sword_t length = vector_len((struct vector *)obj);
begin = obj + 2;
end = obj + ALIGN_UP(length + 2, 2);
} else if (leaf_obj_widetag_p(widetag)) {
return 0;
} else {
lose("Unexpected widetag %x @ %p", widetag, obj);
}
if (page_begin > (os_vm_address_t)begin) begin = (lispobj*)page_begin;
if (page_end < (os_vm_address_t)end) end = (lispobj*)page_end;
if (end > begin && range_points_to_younger_p(begin, end, gen, keep_gen, new_gen))
return 1;
return 0;
}
static inline boolean can_wp_fixedobj_page(page_index_t page, int keep_gen, int new_gen)
{
int obj_spacing = fixedobj_page_obj_align(page);
lispobj* obj = fixedobj_page_address(page);
lispobj* limit = compute_fixedobj_limit(obj, obj_spacing);
do {
if (!fixnump(*obj) &&
fixedobj_points_to_younger_p(obj,
sizetab[widetag_of(obj)](obj),
immobile_obj_generation(obj),
keep_gen, new_gen))
return 0;
} while (NEXT_FIXEDOBJ(obj, obj_spacing) <= limit);
return 1;
}
static inline boolean can_wp_varyobj_page(page_index_t page, int keep_gen, int new_gen)
{
lispobj *begin = varyobj_page_address(page);
lispobj *end = begin + WORDS_PER_PAGE;
lispobj *obj = varyobj_scan_start(page);
for ( ; obj < end ; obj += sizetab[widetag_of(obj)](obj) ) {
gc_assert(other_immediate_lowtag_p(*obj));
if (!filler_obj_p(obj) &&
varyobj_points_to_younger_p(obj,
immobile_obj_generation(obj),
keep_gen, new_gen,
(os_vm_address_t)begin,
(os_vm_address_t)end))
return 0;
}
return 1;
}
Sweep immobile space by zeroing the memory of trashed objects
and linking them into the freelist.
Possible improvements:
- If an entire page becomes nothing but holes, we could bzero it
instead of object-at-a-time clearing. But it's not known to be
so until after the sweep, so it would entail two passes per page,
one to mark holes and one to zero them.
- And perhaps bzero could be used on ranges of holes, because
in that case each hole's pointer to the next hole is zero as well.
*/
#define SETUP_GENS() \
\
int relevant_genmask = (1 << from_space) | (1 << new_space); \
\
int keep_gen = IMMOBILE_OBJ_VISITED_FLAG | new_space; \
\
int discard_gen = from_space; \
\
generation_index_t new_gen = from_space + (raise!=0)
#define COMPUTE_NEW_MASK(var, old) \
int var = old & ~(1<<from_space); \
if ( raise ) \
var |= 1<<(from_space+1) & any_kept; \
else \
var = (var & ~(1<<new_space)) | (1<<from_space & any_kept)
static void
sweep_fixedobj_pages(int raise)
{
char *page_base;
lispobj *obj, *limit, *hole;
int n_holes = 0;
SETUP_GENS();
low_page_index_t max_used_fixedobj_page = calc_max_used_fixedobj_page();
low_page_index_t page;
for (page = FIXEDOBJ_RESERVED_PAGES; page <= max_used_fixedobj_page; ++page) {
if (!(fixedobj_pages[page].gens & relevant_genmask)) {
if (ENABLE_PAGE_PROTECTION && !fixedobj_page_wp(page)
&& fixedobj_pages[page].gens > 1
&& can_wp_fixedobj_page(page, keep_gen, new_gen)) {
SET_WP_FLAG(page, WRITE_PROTECT);
dprintf((logfile, "set WP(1) on fixedobj page %d (mask=%#x)\n",
page, fixedobj_pages[page].gens));
}
continue;
}
int obj_spacing = fixedobj_page_obj_align(page);
page_base = fixedobj_page_address(page);
limit = compute_fixedobj_limit(page_base, obj_spacing);
obj = (lispobj*)page_base;
hole = NULL;
int any_kept = 0;
n_holes = 0;
int wp_it = ENABLE_PAGE_PROTECTION && !fixedobj_page_wp(page);
int gen;
do {
if (fixnump(*obj)) {
trash_it:
if (hole)
*hole = (lispobj)((char*)obj - ((char*)hole + obj_spacing));
else
fixedobj_pages[page].free_index = (char*)obj - page_base;
hole = obj;
n_holes ++;
} else if ((gen = immobile_obj_gen_bits(obj)) == discard_gen) {
memset(obj, 0, obj_spacing);
goto trash_it;
} else if (gen == keep_gen) {
assign_generation(obj, gen = new_gen);
#ifdef DEBUG
gc_assert(!fixedobj_points_to_younger_p(obj,
sizetab[widetag_of(obj)](obj),
gen, keep_gen, new_gen));
#endif
any_kept = -1;
} else if (wp_it && fixedobj_points_to_younger_p(obj,
sizetab[widetag_of(obj)](obj),
gen, keep_gen, new_gen))
wp_it = 0;
} while (NEXT_FIXEDOBJ(obj, obj_spacing) <= limit);
if ( hole )
*hole = (lispobj)((char*)obj - ((char*)hole + obj_spacing));
fixedobj_pages[page].prior_gc_free_word_index =
fixedobj_pages[page].free_index >> WORD_SHIFT;
COMPUTE_NEW_MASK(mask, fixedobj_pages[page].gens);
if ( mask ) {
fixedobj_pages[page].gens = mask;
if (wp_it) {
SET_WP_FLAG(page, WRITE_PROTECT);
dprintf((logfile, "set WP(2) on fixedobj page %d\n", page));
}
} else {
dprintf((logfile,"page %d is all garbage\n", page));
fixedobj_pages[page].attr.packed = 0;
}
#ifdef DEBUG
check_fixedobj_page(page, keep_gen, new_gen);
#endif
dprintf((logfile,"page %d: %d holes\n", page, n_holes));
}
memset(fixedobj_page_hint, 0, sizeof fixedobj_page_hint);
}
static void make_filler(void* where, int nbytes)
{
if (nbytes < 4*N_WORD_BYTES)
lose("can't place filler @ %p - too small", where);
else {
struct code* code = (struct code*)where;
code->header = ((uword_t)nbytes << (CODE_HEADER_SIZE_SHIFT-WORD_SHIFT))
| CODE_HEADER_WIDETAG;
code->boxed_size = 0;
code->debug_info = varyobj_holes;
varyobj_holes = (lispobj)code;
}
}
static void
sweep_varyobj_pages(int raise)
{
SETUP_GENS();
low_page_index_t max_used_varyobj_page = calc_max_used_varyobj_page();
lispobj* free_pointer = varyobj_free_pointer;
low_page_index_t page;
for (page = 0; page <= max_used_varyobj_page; ++page) {
int genmask = varyobj_pages[page].generations;
if (!(genmask & relevant_genmask)) {
if (ENABLE_PAGE_PROTECTION && varyobj_page_touched(page)
&& varyobj_page_gens_augmented(page) > 1
&& can_wp_varyobj_page(page, keep_gen, new_gen)) {
varyobj_page_touched_bits[page/32] &= ~(1U<<(page & 31));
}
continue;
}
lispobj* page_base = varyobj_page_address(page);
lispobj* limit = page_base + WORDS_PER_PAGE;
if (limit > free_pointer) limit = free_pointer;
int any_kept = 0;
int wp_it = ENABLE_PAGE_PROTECTION && varyobj_page_touched(page);
lispobj* obj = varyobj_scan_start(page);
int size, gen;
if (obj < page_base) {
if (wp_it
&& (gen = immobile_obj_gen_bits(obj)) > new_gen
&& varyobj_points_to_younger_p(obj, gen, keep_gen, new_gen,
(os_vm_address_t)page_base,
(os_vm_address_t)limit)) {
wp_it = 0;
}
obj += sizetab[widetag_of(obj)](obj);
gc_assert(obj < limit);
}
for ( ; obj < limit ; obj += size ) {
lispobj word = *obj;
size = sizetab[header_widetag(word)](obj);
if (filler_obj_p(obj)) {
} else if ((gen = immobile_obj_gen_bits(obj)) == discard_gen) {
if (header_widetag(word) == CODE_HEADER_WIDETAG) {
make_lispobj(obj, OTHER_POINTER_LOWTAG),
code_serialno((struct code*)obj)); */
}
make_filler(obj, size * N_WORD_BYTES);
} else if (gen == keep_gen) {
assign_generation(obj, gen = new_gen);
#ifdef DEBUG
gc_assert(!varyobj_points_to_younger_p(obj, gen, keep_gen, new_gen,
(os_vm_address_t)page_base,
(os_vm_address_t)limit));
#endif
any_kept = -1;
} else if (wp_it &&
varyobj_points_to_younger_p(obj, gen, keep_gen, new_gen,
(os_vm_address_t)page_base,
(os_vm_address_t)limit))
wp_it = 0;
}
COMPUTE_NEW_MASK(mask, varyobj_pages[page].generations);
varyobj_pages[page].generations = mask;
if ( mask && wp_it )
varyobj_page_touched_bits[page/32] &= ~(1U << (page & 31));
}
}
void
sweep_immobile_space(int raise)
{
gc_assert(immobile_scav_queue_count == 0);
sweep_fixedobj_pages(raise);
sweep_varyobj_pages(raise);
}
static void gc_init_immobile()
{
#ifdef DEBUG
logfile = stderr;
#endif
int n_fixedobj_pages = FIXEDOBJ_SPACE_SIZE / IMMOBILE_CARD_BYTES;
int n_varyobj_pages = VARYOBJ_SPACE_SIZE / IMMOBILE_CARD_BYTES;
fixedobj_pages = calloc(n_fixedobj_pages, sizeof(struct fixedobj_page));
gc_assert(fixedobj_pages);
n_bitmap_elts = ALIGN_UP(n_varyobj_pages, 32) / 32;
int request = n_bitmap_elts * sizeof (int) + n_varyobj_pages * sizeof (int);
varyobj_page_touched_bits = (unsigned int*)calloc(1, request);
gc_assert(varyobj_page_touched_bits);
memset(varyobj_page_touched_bits, 0xff, n_bitmap_elts * sizeof (int));
varyobj_pages = (struct varyobj_page*)(varyobj_page_touched_bits + n_bitmap_elts);
immobile_scav_queue = malloc(QCAPACITY * sizeof(lispobj));
}
static int page_attributes_valid;
void immobile_space_coreparse(uword_t fixedobj_len, uword_t varyobj_len)
{
int n_pages, word_idx, page;
generation_index_t gen = CORE_PAGE_GENERATION;
gc_init_immobile();
if (gen != PSEUDO_STATIC_GENERATION) {
lispobj *where, *end;
where = fixedobj_page_address(0);
end = (lispobj*)((char*)where + fixedobj_len);
while (where < end) {
if (!fixnump(*where)) assign_generation(where, gen);
where += OBJECT_SIZE(*where, where);
}
where = (lispobj*)VARYOBJ_SPACE_START;
end = (lispobj*)((char*)where + varyobj_len);
while (where < end) {
if (!filler_obj_p(where)) assign_generation(where, gen);
where += OBJECT_SIZE(*where, where);
}
fprintf(stderr, "WARNING: demoted immobile objects to gen%d\n", gen);
}
n_pages = fixedobj_len / IMMOBILE_CARD_BYTES;
for (page = 0; page <= FIXEDOBJ_RESERVED_PAGES; ++page) {
fixedobj_pages[page].attr.parts.obj_align = 1;
if (gen != 0 && ENABLE_PAGE_PROTECTION)
fixedobj_pages[page].attr.parts.flags = WRITE_PROTECT;
fixedobj_pages[page].gens |= 1 << gen;
}
for (page = FIXEDOBJ_RESERVED_PAGES ; page < n_pages ; ++page) {
lispobj* page_data = fixedobj_page_address(page);
for (word_idx = 0 ; word_idx < WORDS_PER_PAGE ; ++word_idx) {
lispobj* obj = page_data + word_idx;
lispobj header = *obj;
if (!fixnump(header)) {
gc_assert(other_immediate_lowtag_p(*obj));
int size = sizetab[header_widetag(header)](obj);
fixedobj_pages[page].attr.parts.obj_align = size;
fixedobj_pages[page].gens |= 1 << immobile_obj_gen_bits(obj);
if (gen != 0 && ENABLE_PAGE_PROTECTION)
fixedobj_pages[page].attr.parts.flags = WRITE_PROTECT;
break;
}
}
}
if (!VARYOBJ_SPACE_START) {
VARYOBJ_SPACE_START = (uword_t)varyobj_free_pointer;
varyobj_space_size = 0;
page_attributes_valid = 1;
return;
}
uword_t address = VARYOBJ_SPACE_START;
n_pages = varyobj_len / IMMOBILE_CARD_BYTES;
lispobj* obj = (lispobj*)address;
int n_words;
low_page_index_t last_page = 0;
lispobj* limit = varyobj_free_pointer;
gc_assert(limit != 0
&& limit <= (lispobj*)(address + varyobj_len));
for ( ; obj < limit ; obj += n_words ) {
gc_assert(other_immediate_lowtag_p(obj[0]));
n_words = sizetab[widetag_of(obj)](obj);
if (filler_obj_p(obj)) {
varyobj_holes = (lispobj)obj;
continue;
}
low_page_index_t first_page = find_varyobj_page_index(obj);
last_page = find_varyobj_page_index(obj+n_words-1);
varyobj_pages[first_page].generations |= 1<<immobile_obj_gen_bits(obj);
int page;
for (page = first_page ; page <= last_page ; ++page) {
if (!varyobj_pages[page].scan_start_offset) {
long offset = (char*)varyobj_page_address(page+1) - (char*)obj;
varyobj_pages[page].scan_start_offset = offset >> (WORD_SHIFT + 1);
}
}
}
if ((uword_t)limit & (IMMOBILE_CARD_BYTES-1)) {
int remainder = IMMOBILE_CARD_BYTES - ((uword_t)limit & (IMMOBILE_CARD_BYTES-1));
int words = (remainder >> WORD_SHIFT) - 2;
if (limit[0] == SIMPLE_ARRAY_FIXNUM_WIDETAG) {
gc_assert(vector_len((struct vector*)limit) == words);
} else {
#ifdef LISP_FEATURE_UBSAN
limit[0] = ((uword_t)words << (32+N_FIXNUM_TAG_BITS)) | SIMPLE_ARRAY_FIXNUM_WIDETAG;
#else
limit[0] = SIMPLE_ARRAY_FIXNUM_WIDETAG;
limit[1] = make_fixnum(words);
#endif
}
int size = sizetab[SIMPLE_ARRAY_FIXNUM_WIDETAG](limit);
lispobj* __attribute__((unused)) padded_end = limit + size;
gc_assert(!((uword_t)padded_end & (IMMOBILE_CARD_BYTES-1)));
}
if (gen != 0 && ENABLE_PAGE_PROTECTION) {
low_page_index_t page;
for (page = 0 ; page <= last_page ; ++page)
varyobj_page_touched_bits[page/32] &= ~(1U<<(page & 31));
}
page_attributes_valid = 1;
}
void prepare_immobile_space_for_final_gc()
{
int page;
char* page_base;
char* page_end = (char*)fixedobj_free_pointer;
SYMBOL(IMMOBILE_FREELIST)->value = NIL;
for (page = 0, page_base = fixedobj_page_address(page) ;
page_base < page_end ;
page_base += IMMOBILE_CARD_BYTES, ++page) {
unsigned char mask = fixedobj_pages[page].gens;
if (mask & 1<<PSEUDO_STATIC_GENERATION) {
lispobj* obj = (lispobj*)page_base;
lispobj* limit = (lispobj*)((uword_t)obj + IMMOBILE_CARD_BYTES);
for ( ; obj < limit ; obj += sizetab[widetag_of(obj)](obj) )
if (other_immediate_lowtag_p(*obj) &&
immobile_obj_gen_bits(obj) == PSEUDO_STATIC_GENERATION)
assign_generation(obj, HIGHEST_NORMAL_GENERATION);
fixedobj_pages[page].gens = (mask & ~(1<<PSEUDO_STATIC_GENERATION))
| 1<<HIGHEST_NORMAL_GENERATION;
}
}
lispobj* obj = (lispobj*)VARYOBJ_SPACE_START;
lispobj* limit = varyobj_free_pointer;
for ( ; obj < limit ; obj += sizetab[widetag_of(obj)](obj) ) {
if (immobile_obj_gen_bits(obj) == PSEUDO_STATIC_GENERATION)
assign_generation(obj, HIGHEST_NORMAL_GENERATION);
}
int max_page = find_varyobj_page_index(limit-1);
for ( page = 0 ; page <= max_page ; ++page ) {
int mask = varyobj_pages[page].generations;
if (mask & (1<<PSEUDO_STATIC_GENERATION)) {
varyobj_pages[page].generations
= (mask & ~(1<<PSEUDO_STATIC_GENERATION))
| 1<<HIGHEST_NORMAL_GENERATION;
}
}
}
int* code_component_order;
* 'coreparse' causes all pages in dynamic space to be pseudo-static, but
* each immobile object stores its own generation, so this must be done at
* save time, or else it would require touching every object on startup */
void prepare_immobile_space_for_save(boolean verbose)
{
if (verbose) {
printf("[defragmenting immobile space... ");
fflush(stdout);
}
defrag_immobile_space(verbose);
lispobj* obj = (lispobj*)FIXEDOBJ_SPACE_START;
lispobj* limit = fixedobj_free_pointer;
while (obj < limit) {
if (other_immediate_lowtag_p(*obj))
assign_generation(obj, PSEUDO_STATIC_GENERATION);
obj += sizetab[widetag_of(obj)](obj);
}
obj = (lispobj*)VARYOBJ_SPACE_START;
limit = varyobj_free_pointer;
for ( varyobj_holes = 0 ; obj < limit ; obj += sizetab[widetag_of(obj)](obj) ) {
if (filler_obj_p(obj)) {
struct code* code = (struct code*)obj;
code->debug_info = varyobj_holes;
code->fixups = 0;
varyobj_holes = (lispobj)code;
int nwords = sizetab[widetag_of(obj)](obj);
memset(code->constants, 0,
(nwords * N_WORD_BYTES) - offsetof(struct code, constants));
} else
assign_generation(obj, PSEUDO_STATIC_GENERATION);
}
if (verbose) printf("done]\n");
}
int immobile_space_handle_wp_violation(void* fault_addr)
{
low_page_index_t fixedobj_page_index = find_fixedobj_page_index(fault_addr);
if (fixedobj_page_index < 0)
return 0;
os_protect(PTR_ALIGN_DOWN(fault_addr, IMMOBILE_CARD_BYTES),
IMMOBILE_CARD_BYTES, OS_VM_PROT_ALL);
if (!(fixedobj_pages[fixedobj_page_index].attr.parts.flags
& (WRITE_PROTECT|WRITE_PROTECT_CLEARED)))
return 0;
SET_WP_FLAG(fixedobj_page_index, WRITE_PROTECT_CLEARED);
return 1;
}
static struct tempspace {
char* start;
int n_bytes;
} fixedobj_tempspace, varyobj_tempspace;
lispobj *
search_immobile_space(void *pointer)
{
lispobj *start;
if ((void*)VARYOBJ_SPACE_START <= pointer
&& pointer < (void*)varyobj_free_pointer) {
low_page_index_t page_index = find_varyobj_page_index(pointer);
if (page_attributes_valid) {
start = varyobj_scan_start(page_index);
if (start > (lispobj*)pointer) return NULL;
} else {
start = (lispobj*)VARYOBJ_SPACE_START;
}
lispobj* found = gc_search_space(start, pointer);
return (found && filler_obj_p(found)) ? 0 : found;
} else if ((void*)FIXEDOBJ_SPACE_START <= pointer
&& pointer < (void*)fixedobj_free_pointer) {
low_page_index_t page_index = find_fixedobj_page_index(pointer);
char *page_base = PTR_ALIGN_DOWN(pointer, IMMOBILE_CARD_BYTES);
if (page_attributes_valid && page_index >= FIXEDOBJ_RESERVED_PAGES) {
int spacing = fixedobj_page_obj_align(page_index);
if (spacing == 0) return NULL;
int index = ((char*)pointer - page_base) / spacing;
lispobj *obj = (void*)(page_base + spacing * index);
char* end;
* so the sizing functions don't work. Following the forwarding pointer
* isn't right, because it points to where the object _will_ be,
* but it isn't actually there. So we have to conservatively assume
* that the object size is the object alignment.
* It all other situations, it is OK to call the sizing function. */
if (fixedobj_tempspace.start)
end = (char*)obj + spacing;
else
end = (char*)(obj + sizetab[widetag_of(obj)](obj));
if ((char*)pointer < end) return obj;
} else {
return gc_search_space((lispobj*)page_base, pointer);
}
}
return NULL;
}
lispobj* find_preceding_object(lispobj* obj)
{
int page = find_varyobj_page_index(obj);
gc_assert(page >= 0);
while (1) {
int offset = varyobj_pages[page].scan_start_offset;
if (offset) {
lispobj* start = varyobj_scan_start(page);
if (start < obj) {
while (1) {
lispobj* end = start + sizetab[widetag_of(start)](start);
if (end == obj) return start;
gc_assert(end < obj);
start = end;
}
}
}
if (page == 0) {
gc_assert(obj == varyobj_page_address(0));
return 0;
}
--page;
}
}
#define FUN_TRAMP_SIZE 6
#define GF_SIZE 6
static lispobj* tempspace_addr(void* address)
{
gc_assert(immobile_space_p((lispobj)address));
if (find_fixedobj_page_index(address) >= 0) {
if (fixedobj_tempspace.n_bytes == 0) return address;
int byte_index = (char*)address - (char*)FIXEDOBJ_SPACE_START;
gc_assert(byte_index < fixedobj_tempspace.n_bytes);
return (void*)(fixedobj_tempspace.start + byte_index);
} else {
int byte_index = (char*)address - (char*)VARYOBJ_SPACE_START;
gc_assert(byte_index < varyobj_tempspace.n_bytes);
return (void*)(varyobj_tempspace.start + byte_index);
}
}
static void copy_back(uword_t space_start, struct tempspace* tempspace,
lispobj** pfree_ptr)
{
lispobj* old_free_ptr = *pfree_ptr;
int old_usage = (uword_t)old_free_ptr - space_start;
int new_usage = tempspace->n_bytes;
memcpy((char*)space_start, tempspace->start, tempspace->n_bytes);
uword_t end = space_start + new_usage;
if (new_usage < old_usage) bzero((char*)end, old_usage - new_usage);
*pfree_ptr = (lispobj*)end;
}
static inline boolean tempspace_p(char* addr)
{
return (addr >= fixedobj_tempspace.start &&
addr < fixedobj_tempspace.start + fixedobj_tempspace.n_bytes)
|| (addr >= varyobj_tempspace.start &&
addr < varyobj_tempspace.start + varyobj_tempspace.n_bytes);
}
static inline boolean known_space_p(lispobj ptr)
{
return find_page_index((char*)ptr) >= 0
|| tempspace_p((char*)ptr)
|| immobile_space_p(ptr)
|| (STATIC_SPACE_OBJECTS_START <= ptr && ptr < STATIC_SPACE_END);
}
static boolean forwardable_ptr_p(lispobj ptr)
{
return is_lisp_pointer(ptr) &&
known_space_p(ptr) &&
forwarding_pointer_p(native_pointer(ptr));
}
static void adjust_words(lispobj *where, sword_t n_words)
{
int i;
for (i=0;i<n_words;++i) {
lispobj ptr = where[i];
if (forwardable_ptr_p(ptr))
where[i] = forwarding_pointer_value(native_pointer(ptr));
}
}
static lispobj adjust_fun_entrypoint(lispobj raw_addr)
{
if (asm_routines_start <= raw_addr && raw_addr < asm_routines_end)
return raw_addr;
lispobj simple_fun = fun_taggedptr_from_self(raw_addr);
adjust_words(&simple_fun, 1);
return fun_self_from_taggedptr(simple_fun);
}
static void adjust_fdefn_raw_addr(struct fdefn* fdefn)
{
if (!fdefn->raw_addr || points_to_asm_code_p((lispobj)fdefn->raw_addr))
return;
lispobj* raw_addr = (lispobj*)fdefn->raw_addr;
lispobj* obj_base = 0;
lispobj header;
int i;
for (i=1; i<=4; ++i)
if ((header = raw_addr[-i]) == 1 || other_immediate_lowtag_p(header)) {
obj_base = raw_addr-i;
break;
}
gc_assert(obj_base);
int offset = (char*)raw_addr - (char*)obj_base;
if (header == 1) {
char* new = (char*)native_pointer(forwarding_pointer_value(obj_base));
fdefn->raw_addr = new + offset;
}
}
* and return the layout's address in tempspace. */
static struct layout* fix_object_layout(lispobj* obj)
{
#ifdef LISP_FEATURE_COMPACT_INSTANCE_HEADER
gc_assert(widetag_of(obj) == INSTANCE_WIDETAG
|| widetag_of(obj) == FUNCALLABLE_INSTANCE_WIDETAG
|| widetag_of(obj) == CLOSURE_WIDETAG);
#else
gc_assert(widetag_of(obj) == INSTANCE_WIDETAG);
#endif
lispobj layout = layout_of(obj);
if (layout == 0) return 0;
#ifdef LISP_FEATURE_METASPACE
return LAYOUT(layout);
#else
if (forwarding_pointer_p(native_pointer(layout))) {
layout = forwarding_pointer_value(native_pointer(layout));
layout_of(obj) = layout;
}
struct layout* native_layout = (struct layout*)tempspace_addr(LAYOUT(layout));
gc_assert(header_widetag(native_layout->header) == INSTANCE_WIDETAG);
gc_assert(layoutp(make_lispobj(native_layout, INSTANCE_POINTER_LOWTAG)));
return native_layout;
#endif
}
static void apply_absolute_fixups(lispobj, struct code*);
#include "genesis/hash-table.h"
static void fixup_space(lispobj* where, size_t n_words)
{
lispobj* end = where + n_words;
int widetag;
long size;
struct code* code;
while (where < end) {
gc_assert(!forwarding_pointer_p(where));
lispobj header_word = *where;
if (!is_header(header_word)) {
adjust_words(where, 2);
where += 2;
continue;
}
widetag = header_widetag(header_word);
size = sizetab[widetag](where);
switch (widetag) {
default:
if (!leaf_obj_widetag_p(widetag))
lose("Unhandled widetag in fixup_space: %p", (void*)header_word);
break;
case INSTANCE_WIDETAG:
case FUNCALLABLE_INSTANCE_WIDETAG:
{
lispobj* slots = where+1;
int i;
struct bitmap bitmap = get_layout_bitmap(fix_object_layout(where));
for(i=0; i<(size-1); ++i)
if (bitmap_logbitp(i, bitmap)) adjust_words(slots+i, 1);
}
break;
case CODE_HEADER_WIDETAG:
code = (struct code*)where;
adjust_words(where+2, code_header_words(code)-2);
apply_absolute_fixups(code->fixups, code);
break;
case CLOSURE_WIDETAG:
where[1] = adjust_fun_entrypoint(where[1]);
#ifndef LISP_FEATURE_COMPACT_INSTANCE_HEADER
case FUNCALLABLE_INSTANCE_WIDETAG:
#endif
adjust_words(where+2, size-2);
break;
case FDEFN_WIDETAG:
adjust_words(where+1, 2);
adjust_fdefn_raw_addr((struct fdefn*)where);
break;
case SIMPLE_VECTOR_WIDETAG:
if (vector_flagp(header_word, VectorAddrHashing)) {
struct vector* kv_vector = (struct vector*)where;
lispobj* data = kv_vector->data;
gc_assert(vector_len(kv_vector) >= 5);
boolean needs_rehash = 0;
unsigned int hwm = KV_PAIRS_HIGH_WATER_MARK(data);
unsigned int i;
for (i = 1; i <= hwm; ++i) {
lispobj ptr = data[2*i];
if (forwardable_ptr_p(ptr)) {
data[2*i] = forwarding_pointer_value(native_pointer(ptr));
needs_rehash = 1;
}
ptr = data[2*i+1];
if (forwardable_ptr_p(ptr))
data[2*i+1] = forwarding_pointer_value(native_pointer(ptr));
}
if (needs_rehash)
KV_PAIRS_REHASH(data) |= make_fixnum(1);
break;
}
case SIMPLE_ARRAY_WIDETAG:
#ifdef COMPLEX_CHARACTER_STRING_WIDETAG
case COMPLEX_CHARACTER_STRING_WIDETAG:
#endif
case COMPLEX_BASE_STRING_WIDETAG:
case COMPLEX_BIT_VECTOR_WIDETAG:
case COMPLEX_VECTOR_WIDETAG:
case COMPLEX_ARRAY_WIDETAG:
case SYMBOL_WIDETAG:
case VALUE_CELL_WIDETAG:
case WEAK_POINTER_WIDETAG:
case RATIO_WIDETAG:
case COMPLEX_WIDETAG:
adjust_words(where+1, size-1);
break;
}
where += size;
}
}
int* immobile_space_reloc_index;
int* immobile_space_relocs;
static lispobj* get_load_address(lispobj* old)
{
if (forwarding_pointer_p(old))
return native_pointer(forwarding_pointer_value(old));
gc_assert(filler_obj_p(old));
return 0;
}
#if DEFRAGMENT_FIXEDOBJ_SUBSPACE
#include "genesis/package.h"
#define N_SYMBOL_KINDS 5
static int classify_symbol(lispobj* obj)
{
struct symbol* symbol = (struct symbol*)obj;
if (symbol->package == NIL) return 0;
struct vector* package_name = (struct vector*)
native_pointer(((struct package*)native_pointer(symbol->package))->_name);
if (widetag_of(&package_name->header) == SIMPLE_BASE_STRING_WIDETAG
&& !strcmp((char*)package_name->data, "KEYWORD"))
return 1;
if ((HeaderValue(*obj) & 0xFF) > (SYMBOL_SIZE-1))
return 2;
struct vector* symbol_name = VECTOR(symbol->name);
if (vector_len(symbol_name) >= 2 &&
schar(symbol_name, 0) == '*' &&
schar(symbol_name, vector_len(symbol_name)-1) == '*')
return 3;
return 4;
}
static inline char* compute_defrag_start_address()
{
return (char*)FIXEDOBJ_SPACE_START + 2*IMMOBILE_CARD_BYTES;
}
static int calc_n_fixedobj_pages(int n_objects, int words_per_object)
{
words_per_object = ALIGN_UP(words_per_object, 2);
int objects_per_page = WORDS_PER_PAGE / words_per_object;
return (n_objects + objects_per_page - 1) / objects_per_page;
}
#if WRITABLE_TEXT_SEGMENT
static int calc_n_varyobj_pages(int n_objects, int words_per_object)
{
words_per_object = ALIGN_UP(words_per_object, 2);
int objects_per_page = WORDS_PER_PAGE / words_per_object;
if (WORDS_PER_PAGE - (words_per_object * objects_per_page) == 2)
--objects_per_page;
return (n_objects + objects_per_page - 1) / objects_per_page;
}
#endif
static void place_fixedobj(lispobj* obj, int size_in_bytes,
char *alloc_ptrs[64], char *symbol_alloc_ptr[])
{
lispobj word = *obj;
int widetag = header_widetag(word);
char** alloc_ptr;
if (widetag == SYMBOL_WIDETAG)
alloc_ptr = &symbol_alloc_ptr[classify_symbol(obj)];
else if (!(alloc_ptr = &alloc_ptrs[widetag>>2]))
lose("Unexpected widetag %x", widetag);
char* new = *alloc_ptr;
char* page_end =
PTR_ALIGN_DOWN(new, IMMOBILE_CARD_BYTES) + IMMOBILE_CARD_BYTES;
int space_avail = (page_end - new) - size_in_bytes;
#if WRITABLE_TEXT_SEGMENT
if (widetag == FDEFN_WIDETAG || widetag == CODE_HEADER_WIDETAG
|| widetag == FUNCALLABLE_INSTANCE_WIDETAG) {
if (space_avail < 0 || space_avail == 2*N_WORD_BYTES) {
make_filler(tempspace_addr(new), page_end - new);
new = page_end;
}
} else {
if (space_avail < 0)
new = page_end;
}
#else
if (space_avail < 0) new = page_end;
#endif
gc_assert(!*tempspace_addr(new));
memcpy(tempspace_addr(new), obj, size_in_bytes);
set_forwarding_pointer(obj, make_lispobj(new, LOWTAG_FOR_WIDETAG(widetag)));
if (widetag == FUNCALLABLE_INSTANCE_WIDETAG)
tempspace_addr(new)[1] = (lispobj)(new + 4*N_WORD_BYTES);
*alloc_ptr = new + size_in_bytes;
}
struct size_class {
char* alloc_ptr;
* After computing the total number of layout pages needed, this becomes a count
* of how many objects can be allocated starting from 'alloc_ptr' without
* overflowing the page. When zero, we grab the next available page. */
int count;
};
* restarted core can not discern the original (coarser) size class
* in which a layout was allocated. It can only use the actual size.
* 2n+8 words for N from 0..20 gives a range from 8 to 48 words */
#define MAX_LAYOUT_DEFRAG_SIZE_CLASSES 21
static inline int layout_size_class_nwords(int index) {
return 8 + 2*index ;
}
static inline int nwords_to_layout_size_class(unsigned int nwords) {
int index = nwords <= 8 ? 0 : (nwords - 8)/2;
if (index >= MAX_LAYOUT_DEFRAG_SIZE_CLASSES)
lose("Oversized layout: can't defragment");
return index;
}
static void place_layout(lispobj* obj,
struct size_class size_classes[],
char** alloc_ptr, char *alloc_ptr_limit)
{
int nwords = 1 + (instance_length(*obj) | 1);
int size_class_index = nwords_to_layout_size_class(nwords);
if (size_classes[size_class_index].count == 0) {
gc_assert(*alloc_ptr <= alloc_ptr_limit);
size_classes[size_class_index].alloc_ptr = *alloc_ptr;
size_classes[size_class_index].count = (IMMOBILE_CARD_BYTES>>WORD_SHIFT) / nwords;
*alloc_ptr += IMMOBILE_CARD_BYTES;
} else {
gc_assert(size_classes[size_class_index].alloc_ptr != 0);
gc_assert(size_classes[size_class_index].count > 0);
}
size_classes[size_class_index].count--;
char* new = size_classes[size_class_index].alloc_ptr;
gc_assert(!*tempspace_addr(new));
memcpy(tempspace_addr(new), obj, nwords*N_WORD_BYTES);
set_forwarding_pointer(obj, make_lispobj(new, INSTANCE_POINTER_LOWTAG));
size_classes[size_class_index].alloc_ptr +=
layout_size_class_nwords(size_class_index) << WORD_SHIFT;
}
static void __attribute__((unused)) add_filler_if_needed(char* from, char* to)
{
if (to>from)
make_filler(tempspace_addr(from), to-from);
}
static boolean executable_object_p(lispobj* obj)
{
int widetag = widetag_of(obj);
int answer = widetag == FDEFN_WIDETAG ||
(widetag == CODE_HEADER_WIDETAG && code_header_words((struct code*)obj) >= 3
* to the original closure or funcallable instance */
&& lowtag_of(((struct code*)obj)->debug_info) == FUN_POINTER_LOWTAG)
|| widetag == FUNCALLABLE_INSTANCE_WIDETAG;
#if !WRITABLE_TEXT_SEGMENT
gc_assert(!answer);
#endif
return answer;
}
static void defrag_immobile_space(boolean verbose)
{
lispobj* addr;
int i;
int *components = code_component_order;
int obj_type_histo[64];
struct { int size, count; } sym_kind_histo[N_SYMBOL_KINDS];
bzero(obj_type_histo, sizeof obj_type_histo);
bzero(sym_kind_histo, sizeof sym_kind_histo);
struct size_class layout_size_class[MAX_LAYOUT_DEFRAG_SIZE_CLASSES];
bzero(layout_size_class, sizeof layout_size_class);
lispobj* obj = (lispobj*)VARYOBJ_SPACE_START;
while (obj < varyobj_free_pointer) {
int widetag = widetag_of(obj);
if (executable_object_p(obj)) ++obj_type_histo[widetag/4];
obj += sizetab[widetag](obj);
}
#if DEFRAGMENT_FIXEDOBJ_SUBSPACE
char* defrag_base = compute_defrag_start_address();
low_page_index_t page_index = find_fixedobj_page_index(defrag_base);
low_page_index_t max_used_fixedobj_page = calc_max_used_fixedobj_page();
for ( ; page_index <= max_used_fixedobj_page ; ++page_index) {
int obj_spacing = fixedobj_page_obj_align(page_index);
if (obj_spacing) {
lispobj* obj = fixedobj_page_address(page_index);
lispobj* limit = compute_fixedobj_limit(obj, obj_spacing);
do {
lispobj word = *obj;
if (!fixnump(word)) {
int widetag = header_widetag(word);
int size = sizetab[widetag](obj);
++obj_type_histo[widetag/4];
switch (widetag) {
case SYMBOL_WIDETAG:
{
int kind = classify_symbol(obj);
++sym_kind_histo[kind].count;
if (!sym_kind_histo[kind].size)
sym_kind_histo[kind].size = size;
gc_assert(sym_kind_histo[kind].size == size);
}
break;
case INSTANCE_WIDETAG:
gc_assert(layoutp(make_lispobj(obj, INSTANCE_POINTER_LOWTAG)));
int class_index = nwords_to_layout_size_class(size);
++layout_size_class[class_index].count;
break;
}
}
} while (NEXT_FIXEDOBJ(obj, obj_spacing) <= limit);
}
}
#ifndef LISP_FEATURE_METASPACE
gc_assert(obj_type_histo[INSTANCE_WIDETAG/4]);
#endif
int n_layout_pages = 0;
int class_index;
for (class_index = 0; class_index < MAX_LAYOUT_DEFRAG_SIZE_CLASSES; ++class_index) {
int count = layout_size_class[class_index].count;
n_layout_pages += calc_n_fixedobj_pages(count, layout_size_class_nwords(class_index));
layout_size_class[class_index].count = 0;
}
char* layout_alloc_ptr = defrag_base;
char* symbol_alloc_ptrs[N_SYMBOL_KINDS+1];
symbol_alloc_ptrs[0] = layout_alloc_ptr + n_layout_pages * IMMOBILE_CARD_BYTES;
for (i=0; i<N_SYMBOL_KINDS ; ++i)
symbol_alloc_ptrs[i+1] =
symbol_alloc_ptrs[i] + calc_n_fixedobj_pages(
sym_kind_histo[i].count, sym_kind_histo[i].size) * IMMOBILE_CARD_BYTES;
#if WRITABLE_TEXT_SEGMENT
fixedobj_tempspace.n_bytes =
symbol_alloc_ptrs[N_SYMBOL_KINDS] - (char*)FIXEDOBJ_SPACE_START;
#else
int n_fdefn_pages = calc_n_fixedobj_pages(obj_type_histo[FDEFN_WIDETAG/4], FDEFN_SIZE);
int n_tramp_pages = calc_n_fixedobj_pages(obj_type_histo[CODE_HEADER_WIDETAG/4], FUN_TRAMP_SIZE);
int n_gf_pages = calc_n_fixedobj_pages(obj_type_histo[FUNCALLABLE_INSTANCE_WIDETAG/4], GF_SIZE);
char* fdefn_alloc_ptr = symbol_alloc_ptrs[N_SYMBOL_KINDS];
char* tramp_alloc_ptr = fdefn_alloc_ptr + n_fdefn_pages * IMMOBILE_CARD_BYTES;
char* gf_alloc_ptr = tramp_alloc_ptr + n_tramp_pages * IMMOBILE_CARD_BYTES;
fixedobj_tempspace.n_bytes =
gf_alloc_ptr + n_gf_pages * IMMOBILE_CARD_BYTES - (char*)FIXEDOBJ_SPACE_START;
#endif
fixedobj_tempspace.start = calloc(fixedobj_tempspace.n_bytes, 1);
memcpy(fixedobj_tempspace.start, (char*)FIXEDOBJ_SPACE_START,
(lispobj)defrag_base - FIXEDOBJ_SPACE_START);
#endif
int n_code_components = 0;
int n_code_bytes = 0;
if (components) {
#ifdef LISP_FEATURE_METASPACE
SB-FASL::*ASSEMBLER-ROUTINES* which is in read-only space and not
itself relocatable, but does contain absolute fixups */
for (i=1 ; components[i*2] ; ++i) {
#else
for (i=0 ; components[i*2] ; ++i) {
#endif
addr = (lispobj*)(long)components[i*2];
gc_assert(lowtag_of((lispobj)addr) == OTHER_POINTER_LOWTAG);
addr = native_pointer((lispobj)addr);
int widetag = widetag_of(addr);
gc_assert(widetag == CODE_HEADER_WIDETAG);
lispobj new_vaddr = 0;
if (!filler_obj_p(addr)) {
if ((lispobj)addr > VARYOBJ_SPACE_START)
gc_assert(code_n_funs((struct code*)addr));
++n_code_components;
new_vaddr = VARYOBJ_SPACE_START + n_code_bytes;
n_code_bytes += sizetab[widetag](addr) << WORD_SHIFT;
}
components[i*2+1] = new_vaddr;
}
}
int aligned_nbytes = ALIGN_UP(n_code_bytes, IMMOBILE_CARD_BYTES);
if (aligned_nbytes - n_code_bytes == 2 * N_WORD_BYTES)
aligned_nbytes += IMMOBILE_CARD_BYTES;
#if WRITABLE_TEXT_SEGMENT
char* fdefn_alloc_ptr = (char*)VARYOBJ_SPACE_START + aligned_nbytes;
int n_fdefn_pages =
calc_n_varyobj_pages(obj_type_histo[FDEFN_WIDETAG/4],
FDEFN_SIZE);
int n_gf_pages =
calc_n_varyobj_pages(obj_type_histo[FUNCALLABLE_INSTANCE_WIDETAG/4],
GF_SIZE);
int n_tramp_pages =
calc_n_varyobj_pages(obj_type_histo[CODE_HEADER_WIDETAG/4],
FUN_TRAMP_SIZE);
char* tramp_alloc_ptr = fdefn_alloc_ptr + n_fdefn_pages * IMMOBILE_CARD_BYTES;
char* gf_alloc_ptr = tramp_alloc_ptr + n_tramp_pages * IMMOBILE_CARD_BYTES;
varyobj_tempspace.n_bytes =
= gf_alloc_ptr + n_gf_pages * IMMOBILE_CARD_BYTES - (char*)VARYOBJ_SPACE_START;
#else
varyobj_tempspace.n_bytes = aligned_nbytes;
#endif
varyobj_tempspace.start = calloc(varyobj_tempspace.n_bytes, 1);
if (verbose)
printf("(fin,inst,fdefn,code,sym)=%d+%d+%d+%d+%d... ",
obj_type_histo[FUNCALLABLE_INSTANCE_WIDETAG/4],
obj_type_histo[INSTANCE_WIDETAG/4],
obj_type_histo[FDEFN_WIDETAG/4],
obj_type_histo[CODE_HEADER_WIDETAG/4] + n_code_components,
obj_type_histo[SYMBOL_WIDETAG/4]);
if (components) {
lispobj new_vaddr;
for (i=0 ; components[i*2] ; ++i) {
if ((new_vaddr = components[i*2+1]) != 0) {
addr = native_pointer(components[i*2]);
memcpy(tempspace_addr((void*)new_vaddr), addr,
sizetab[widetag_of(addr)](addr) << WORD_SHIFT);
int displacement = new_vaddr - (lispobj)addr;
switch (widetag_of(addr)) {
default:
lose("What is object type %x doing here?", widetag_of(addr));
case CODE_HEADER_WIDETAG:
for_each_simple_fun(index, fun, (struct code*)addr, 1, {
struct simple_fun *new_fun =
(struct simple_fun*)((char*)fun + displacement);
((struct simple_fun*)tempspace_addr(new_fun))->self =
fun_self_from_baseptr(new_fun);
set_forwarding_pointer((lispobj*)fun,
make_lispobj(new_fun, FUN_POINTER_LOWTAG));
});
{
lispobj* jump_table =
code_jumptable_start((struct code*)tempspace_addr((void*)new_vaddr));
int count = jumptable_count(jump_table);
int i;
for (i = 1; i < count; ++i)
if (jump_table[i]) jump_table[i] += displacement;
}
break;
}
set_forwarding_pointer(addr,
make_lispobj((void*)new_vaddr,
OTHER_POINTER_LOWTAG));
}
}
if (aligned_nbytes > n_code_bytes)
make_filler(tempspace_addr((char*)VARYOBJ_SPACE_START + n_code_bytes),
aligned_nbytes - n_code_bytes);
}
#if DEFRAGMENT_FIXEDOBJ_SUBSPACE
char* alloc_ptrs[64];
bzero(alloc_ptrs, sizeof alloc_ptrs);
alloc_ptrs[INSTANCE_WIDETAG/4] = layout_alloc_ptr;
alloc_ptrs[FDEFN_WIDETAG/4] = fdefn_alloc_ptr;
alloc_ptrs[CODE_HEADER_WIDETAG/4] = tramp_alloc_ptr;
alloc_ptrs[FUNCALLABLE_INSTANCE_WIDETAG/4] = gf_alloc_ptr;
#if WRITABLE_TEXT_SEGMENT
obj = (lispobj*)VARYOBJ_SPACE_START;
while (obj < varyobj_free_pointer) {
lispobj* fwdobj =
forwarding_pointer_p(obj) ?
tempspace_addr(native_pointer(forwarding_pointer_value(obj))) : obj;
int size = sizetab[widetag_of(fwdobj)](fwdobj);
if (executable_object_p(fwdobj))
place_fixedobj(obj, size << WORD_SHIFT, alloc_ptrs, 0);
obj += size;
}
#endif
for ( page_index = find_fixedobj_page_index(defrag_base) ;
page_index <= max_used_fixedobj_page ; ++page_index) {
int obj_spacing = fixedobj_page_obj_align(page_index);
if (!obj_spacing) continue;
lispobj* obj = fixedobj_page_address(page_index);
lispobj* limit = compute_fixedobj_limit(obj, obj_spacing);
do {
if (fixnump(*obj)) continue;
if (widetag_of(obj) == INSTANCE_WIDETAG) {
place_layout(obj, layout_size_class,
&layout_alloc_ptr, symbol_alloc_ptrs[0]);
} else {
place_fixedobj(obj, obj_spacing, alloc_ptrs, symbol_alloc_ptrs);
}
} while (NEXT_FIXEDOBJ(obj, obj_spacing) <= limit);
}
#if WRITABLE_TEXT_SEGMENT
add_filler_if_needed(alloc_ptrs[FDEFN_WIDETAG/4], tramp_alloc_ptr);
add_filler_if_needed(alloc_ptrs[CODE_HEADER_WIDETAG/4], gf_alloc_ptr);
#endif
#endif
if (immobile_space_reloc_index) {
#ifdef LISP_FEATURE_X86_64
for (i = 0 ; immobile_space_reloc_index[i*2] ; ++i) {
lispobj code = immobile_space_reloc_index[i*2] - OTHER_POINTER_LOWTAG;
lispobj load_addr;
if (code >= READ_ONLY_SPACE_START && code < READ_ONLY_SPACE_END)
load_addr = code;
else
load_addr = (lispobj)get_load_address((lispobj*)code);
if (!load_addr) continue;
int reloc_index = immobile_space_reloc_index[i*2+1];
int end_reloc_index = immobile_space_reloc_index[i*2+3];
for ( ; reloc_index < end_reloc_index ; reloc_index += 2) {
int offset = immobile_space_relocs[reloc_index];
char* inst_addr = (char*)code + offset;
int target_adjust = 0;
lispobj obj = immobile_space_relocs[reloc_index+1];
if (obj) {
lispobj *npobj = native_pointer(obj);
if (forwarding_pointer_p(npobj))
target_adjust = forwarding_pointer_value(npobj) - obj;
}
char* fixup_loc =
immobile_space_p((lispobj)inst_addr) ?
(char*)tempspace_addr(inst_addr - code + load_addr) : inst_addr;
UNALIGNED_STORE32(fixup_loc,
UNALIGNED_LOAD32(fixup_loc)
+ target_adjust + (code - load_addr));
}
}
#endif
free(immobile_space_relocs);
free(immobile_space_reloc_index);
}
fixup_space((lispobj*)STATIC_SPACE_OBJECTS_START,
static_space_free_pointer - (lispobj*)STATIC_SPACE_OBJECTS_START);
#if DEFRAGMENT_FIXEDOBJ_SUBSPACE
fixup_space((lispobj*)fixedobj_tempspace.start,
fixedobj_tempspace.n_bytes >> WORD_SHIFT);
#else
fixup_space((lispobj*)FIXEDOBJ_SPACE_START,
FIXEDOBJ_SPACE_SIZE >> WORD_SHIFT);
#endif
fixup_space((lispobj*)varyobj_tempspace.start,
varyobj_tempspace.n_bytes >> WORD_SHIFT);
fixup_space(current_dynamic_space,
(lispobj*)get_alloc_pointer() - current_dynamic_space);
#if DEFRAGMENT_FIXEDOBJ_SUBSPACE
copy_back(FIXEDOBJ_SPACE_START, &fixedobj_tempspace, &fixedobj_free_pointer);
#endif
#ifdef LISP_FEATURE_IMMOBILE_CODE
copy_back(VARYOBJ_SPACE_START, &varyobj_tempspace, &varyobj_free_pointer);
free(components);
#endif
page_attributes_valid = 0;
#if 0
printf("verifying defrag\n");
verify_heap(1);
#endif
free(fixedobj_tempspace.start);
free(varyobj_tempspace.start);
}
#endif
#include "forwarding-ptr.h"
#ifdef LISP_FEATURE_X86_64
static void apply_absolute_fixups(lispobj fixups, struct code* code)
{
struct varint_unpacker unpacker;
varint_unpacker_init(&unpacker, fixups);
char* instructions = code_text_start(code);
int prev_loc = 0, loc;
while (varint_unpack(&unpacker, &loc) && loc != 0) {
loc += prev_loc;
prev_loc = loc;
void* fixup_where = instructions + loc;
lispobj ptr = (lispobj)UNALIGNED_LOAD32(fixup_where);
lispobj* header_addr;
long fpval;
if (is_lisp_pointer(ptr)) {
lispobj fixed = follow_fp(ptr);
if (fixed != ptr)
UNALIGNED_STORE32(fixup_where, fixed);
continue;
}
if (asm_routines_start <= ptr && ptr < asm_routines_end) {
continue;
}
#ifdef LISP_FEATURE_METASPACE
if (ptr >= READ_ONLY_SPACE_START && ptr < READ_ONLY_SPACE_END) continue;
#endif
if (find_fixedobj_page_index((void*)ptr) >= 0) {
header_addr = search_immobile_space((void*)ptr);
gc_assert(header_addr);
if (!forwarding_pointer_p(header_addr))
continue;
fpval = forwarding_pointer_value(header_addr);
int widetag = widetag_of(tempspace_addr(native_pointer(fpval)));
if (!(widetag == SYMBOL_WIDETAG || widetag == FDEFN_WIDETAG))
lose("Expected symbol or fdefn @ %p", header_addr);
} else {
* and fdefns using the two-instruction sequence:
* MOV RAX, #x{addr} ; CALL RAX
* where the addr is either word index 0 of an fdefn
* (the jump instruction), or word index 2 of a simple-fun.
* We have to heuristically figure out which it is.
* If we started by assuming that it's a simple-fun then
* we might go astray if it's an fdefn because we can't
* look at negative word indices. */
header_addr = (lispobj*)(ptr - 2);
if (forwarding_pointer_p(header_addr)) {
fpval = forwarding_pointer_value(header_addr);
if (widetag_of(tempspace_addr(native_pointer(fpval))) == FDEFN_WIDETAG)
goto fix;
lose("Expected fdefn @ %p", header_addr);
}
header_addr = (lispobj*)(ptr - offsetof(struct simple_fun, insts));
if (forwarding_pointer_p(header_addr)) {
fpval = forwarding_pointer_value(header_addr);
if (widetag_of(tempspace_addr(native_pointer(fpval))) == SIMPLE_FUN_WIDETAG)
goto fix;
lose("Expected simple-fun @ %p", header_addr);
}
lose("Can't determine referent of absolute fixup");
}
fix: UNALIGNED_STORE32(fixup_where,
ptr - (lispobj)header_addr + (lispobj)native_pointer(fpval));
}
}
#endif
#ifdef VERIFY_PAGE_GENS
void check_fixedobj_page(int page,
generation_index_t keep_gen,
generation_index_t new_gen)
{
int genmask, obj_size, obj_spacing, i, all_ok = 1;
lispobj *obj, header;
int sees_younger = 0;
obj_size = fixedobj_page_obj_size(page);
obj_spacing = fixedobj_page_obj_align(page);
obj = fixedobj_page_address(page);
lispobj *limit = compute_fixedobj_limit(obj, obj_spacing);
genmask = 0;
if (obj_size == 0) {
gc_assert(!fixedobj_pages[page].gens);
for (i=0; i<WORDS_PER_PAGE && obj[i]==0; ++i)
;
if(i<WORDS_PER_PAGE)
lose("page %d @ %p nonempty", page, fixedobj_page_address(page));
return;
}
do {
header = *obj;
if (!fixnump(header)) {
int gen = immobile_obj_gen_bits(obj);
gc_assert(0 <= gen && gen <= PSEUDO_STATIC_GENERATION);
genmask |= 1<<gen;
if (fixedobj_points_to_younger_p(obj, obj_size, gen, keep_gen, new_gen)) {
if (fixedobj_page_wp(page))
lose("sees_younger @ %p + %d", obj, obj_size);
sees_younger = 1;
}
}
} while (NEXT_FIXEDOBJ(obj, obj_spacing) <= limit);
if (fixedobj_pages[page].gens != genmask
&& fixedobj_pages[page].gens != (genmask|1)) {
fprintf(stderr, "Page %d @ %p: stored mask=%x actual=%x\n",
page, fixedobj_page_address(page),
fixedobj_pages[page].gens, genmask);
all_ok = 0;
}
if (fixedobj_page_wp(page) && sees_younger) {
fprintf(stderr, "Page %d @ %p: WP is wrong\n",
page, fixedobj_page_address(page));
all_ok = 0;
}
gc_assert(all_ok);
}
int n_immobile_objects;
int *immobile_objects, *immobile_objects_limit;
int comparator_eq(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
int comparator_le(const void* a, const void* b) {
int diff = *(int*)a - *(int*)b;
if (diff <= 0) return diff;
if (b == (void*)immobile_objects_limit || ((int*)b)[1] > *(int*)a) return 0;
return 1;
}
int comparator_ge(const void* a, const void* b) {
int diff = *(int*)a - *(int*)b;
if (diff >= 0) return diff;
if (b == (void*)immobile_objects || ((int*)b)[-1] < *(int*)a) return 0;
return -1;
}
void check_varyobj_pages()
{
lispobj* obj = (lispobj*)VARYOBJ_SPACE_START;
lispobj* end = varyobj_free_pointer;
low_page_index_t end_page = find_varyobj_page_index((char*)end-1);
n_immobile_objects = 0;
while (obj < end) {
lispobj word = *obj;
gc_assert(other_immediate_lowtag_p(word));
int n_words = sizetab[header_widetag(word)](obj);
obj += n_words;
++n_immobile_objects;
}
gc_assert(obj == end);
immobile_objects = calloc(n_immobile_objects, sizeof (lispobj));
immobile_objects_limit = immobile_objects + n_immobile_objects - 1;
obj = (lispobj*)VARYOBJ_SPACE_START;
int i = 0;
while (obj < end) {
immobile_objects[i++] = (lispobj)obj;
lispobj word = *obj;
int n_words = sizetab[header_widetag(word)](obj);
obj += n_words;
}
low_page_index_t page;
for (page = 0; page <= end_page; ++page) {
lispobj page_addr = (lispobj)varyobj_page_address(page);
int* found_below = bsearch(&page_addr, immobile_objects, n_immobile_objects,
sizeof (int), comparator_le);
int* found_above = bsearch(&page_addr, immobile_objects, n_immobile_objects,
sizeof (int), comparator_ge);
int stored_scan_start = (int)(long)varyobj_scan_start(page);
lispobj* scan_start_obj = (lispobj*)(long)*found_below;
if (scan_start_obj != (lispobj*)(long)stored_scan_start) {
while (filler_obj_p(scan_start_obj)) {
int nwords = sizetab[widetag_of(scan_start_obj)](scan_start_obj);
scan_start_obj += nwords;
if ((int)(long)scan_start_obj == stored_scan_start)
break;
if (scan_start_obj == varyobj_free_pointer
|| scan_start_obj >= (lispobj*)varyobj_page_address(page+1)) {
scan_start_obj = varyobj_page_address(page+1);
break;
}
}
}
if (scan_start_obj != (lispobj*)(long)stored_scan_start)
lose("page %d: stored_scan_start=%p does not match found %p",
page, stored_scan_start, *found_below);
if (found_below != found_above) {
lispobj* below = (lispobj*)(long)*found_below;
int n_words = sizetab[header_widetag(*below)](below);
lispobj* end = below + n_words;
gc_assert(end > (lispobj*)page_addr);
}
}
free(immobile_objects);
for (page = 0; page <= end_page; ++page) {
if (!varyobj_page_scan_start_offset[page])
continue;
obj = varyobj_scan_start(page);
lispobj word = *obj;
int n_words = sizetab[header_widetag(word)](obj);
if (obj < (lispobj*)varyobj_page_address(page)) obj += n_words;
lispobj* limit = (lispobj*)varyobj_page_address(page) + WORDS_PER_PAGE;
lispobj* freeptr = varyobj_free_pointer;
if (limit > freeptr) limit = freeptr;
int mask = 0;
for ( ; obj < limit ; obj += sizetab[widetag_of(obj)](obj) ) {
int gen = immobile_obj_gen_bits(obj);
if (filler_obj_p(obj)) {
gc_assert(gen == 0);
} else {
gc_assert(0 <= gen && gen <= PSEUDO_STATIC_GENERATION);
mask |= 1 << gen;
}
}
* the genmasks, so that they remain as overestimates */
int actual = varyobj_page_gens[page];
* actual=0, expected=0 -> // ok
* actual=1, expected=1 -> // ok
* actual=1, expected=0 -> // ok
* actual=0, expected=1 -> // NOT ok
*/
if (~actual & mask)
lose("genmask wrong: actual=%x expect=%x", actual, mask);
}
}
#endif