static Val repair_word ( // =========== // Val w, Sibid* oldBOOK2SIBID, // Punt addrOffset [ MAX_AGEGROUPS ][ MAX_PLAIN_SIBS ], // Addresstable* hugechunk_region_table, Val* externs ) { // if (IS_POINTER(w)) { // Punt chunk = HEAP_POINTER_AS_UNT(w); Sibid aid = SIBID_FOR_POINTER(oldBOOK2SIBID, chunk); if (SIBID_KIND_IS_CODE(aid)) { Hugechunk_Relocation_Info* dp = address_to_relocation_info (oldBOOK2SIBID, hugechunk_region_table, aid, chunk); return PTR_CAST( Val, (chunk - dp->old_address) + dp->new_chunk->chunk); } else { int g = GET_AGE_FROM_SIBID(aid)-1; int chunkc = GET_KIND_FROM_SIBID(aid)-1; return PTR_CAST( Val, PTR_CAST(char*, w) + addrOffset[g][chunkc]); } } else if (IS_EXTERNAL_TAG(w)) {
static inline void forward_to_agegroup1_if_in_agegroup0 (Sibid* book2sibid, Agegroup* g1, Val *p, Task* task) { // 'task' arg is only for debugging, can be dropped in production code. // ==================================== // // Forward *p if it is in agegroup0: Val w = *p; // if (IS_POINTER(w)) { // Sibid sibid = SIBID_FOR_POINTER( book2sibid, w ); // if (sibid == AGEGROUP0_SIBID) *p = forward_agegroup0_chunk_to_agegroup1( g1, w, task, 0 ); } }
int get_chunk_age (Val chunk) { //============= // // Get the agegroup of a chunk. // Return -1 for external/unboxed chunks. // // We are called (only) from // src/c/heapcleaner/datastructure-pickler.c if (! IS_POINTER( chunk )) { return -1; } else { // Sibid aid = SIBID_FOR_POINTER( book_to_sibid__global, chunk ); // if (SIBID_KIND_IS_CODE( aid )) { // int i; for (i = GET_BOOK_CONTAINING_POINTEE(chunk); !SIBID_ID_IS_BIGCHUNK_RECORD(aid); aid = book_to_sibid__global[--i]) { continue; } Hugechunk_Quire* // hq = (Hugechunk_Quire*) ADDRESS_OF_BOOK( i ); Hugechunk* // dp = get_hugechunk_holding_pointee( hq, chunk ); return dp->age; } else if (aid == AGEGROUP0_SIBID) { return 0; } else if (BOOK_IS_UNMAPPED(aid)) { return -1; } else { return GET_AGE_FROM_SIBID( aid ); } } }
static int get_age_of_codechunk (Val codechunk) { // ==================== // Sibid* b2s = book_to_sibid__global; // Cache global locally for speed. book_to_sibid__global def in src/c/heapcleaner/heapcleaner-initialization.c Sibid dst_sibid = SIBID_FOR_POINTER(b2s, codechunk ); // Get the Sibid tag for the ram-book containing the codechunk. int book; for (book = GET_BOOK_CONTAINING_POINTEE( codechunk ); // !SIBID_ID_IS_BIGCHUNK_RECORD( dst_sibid ); // dst_sibid = b2s[ --book ] ); Hugechunk_Quire* // q = (Hugechunk_Quire*) ADDRESS_OF_BOOK( book ); Hugechunk* hc = get_hugechunk_holding_pointee( q, codechunk ); // get_hugechunk_holding_pointee def in src/c/h/heap.h return hc->age; }
static int check_pointer (Val* p, Val w, int src_age, int srcKind, int dstKind) { // ============= // Sibid sibid = SIBID_FOR_POINTER( book_to_sibid__global, w); int dstGen = GET_AGE_FROM_SIBID(sibid); int chunkc = GET_KIND_FROM_SIBID(sibid); switch (chunkc) { // case RO_POINTERS_KIND: case RO_CONSCELL_KIND: case NONPTR_DATA_KIND: case RW_POINTERS_KIND: if (!(dstKind & (1 << chunkc))) { ERROR; debug_say ( "** @%#x: sequence data kind mismatch (expected %d, found %d)\n", p, dstKind, chunkc); } if (dstGen < src_age) { if (srcKind != RW_POINTERS_KIND) { ERROR; debug_say ( "** @%#x: reference to younger chunk @%#x (gen = %d)\n", p, w, dstGen); } } if ((chunkc != RO_CONSCELL_KIND) && (*IS_TAGWORD(((Val *)w)[-1]))) { ERROR; debug_say ("** @%#x: reference into chunk middle @#x\n", p, w); } break; case CODE_KIND: break; case NEW_KIND: ERROR; debug_say ("** @%#x: unexpected new-space reference\n", p); dstGen = MAX_AGEGROUPS; break; default: if (sibid != UNMAPPED_BOOK_SIBID) { die("bogus chunk ilk in book_to_sibid__global\n"); } else { if (name_of_cfun(w) == NULL) { // name_of_cfun def in src/c/heapcleaner/mythryl-callable-cfun-hashtable.c ERROR; debug_say ( "** @%#x: reference to unregistered external address %#x\n", p, w); } dstGen = MAX_AGEGROUPS; } break; } return dstGen; } // fun check_pointer
static Val pickle_heap_datastructure (Task *task, Val root_chunk, Pickler_Result* result) { // ========================= // Heap* heap = task->heap; int max_age = result->oldest_agegroup_included_in_pickle; Vunt total_sib_buffer_bytesize[ MAX_PLAIN_SIBS ]; Vunt total_bytesize; struct { Vunt base; // Base address of the sib buffer in the heap. Vunt offset; // Relative position in the merged sib buffer. // } adjust[ MAX_AGEGROUPS ][ MAX_PLAIN_SIBS ]; Sib_Header* p; // Sib_Header def in src/c/heapcleaner/runtime-heap-image.h Sib_Header* sib_headers[ TOTAL_SIBS ]; Sib_Header* sib_header_buffer; int sib_header_bytesize; int smallchunk_sibs_count; Val pickle; Writer* wr; // Compute the sib offsets in the heap image: // for (int ilk = 0; ilk < MAX_PLAIN_SIBS; ilk++) { // total_sib_buffer_bytesize[ ilk ] = 0; } // The embedded literals go first: // total_sib_buffer_bytesize[ NONPTR_DATA_SIB ] // pickler__relocate_embedded_literals def in src/c/heapcleaner/datastructure-pickler-cleaner.c = pickler__relocate_embedded_literals( result, NONPTR_DATA_SIB, 0 ); // DEBUG debug_say("%d bytes of string literals\n", total_sib_buffer_bytesize[NONPTR_DATA_SIB]); for (int age = 0; age < max_age; age++) { for (int ilk = 0; ilk < MAX_PLAIN_SIBS; ilk++) { // Sib* sib = heap->agegroup[ age ]->sib[ ilk ]; adjust[ age ][ ilk ].offset = total_sib_buffer_bytesize[ ilk ]; if (!sib_is_active(sib)) { // sib_is_active def in src/c/h/heap.h // adjust[ age ][ ilk ].base = 0; // } else { // total_sib_buffer_bytesize[ ilk ] += (Vunt) sib->tospace.first_free - (Vunt) sib->tospace.start; adjust[ age ][ ilk ].base = (Vunt) sib->tospace.start; } } } // DEBUG for (ilk = 0; ilk < MAX_PLAIN_SIBS; ilk++) debug_say ("sib %d: %d bytes\n", ilk+1, total_sib_buffer_bytesize[ilk]); // WHAT ABOUT THE BIG CHUNKS??? XXX BUGGO FIXME // Compute the total size of the pickled datastructure: // smallchunk_sibs_count = 0; total_bytesize = 0; // for (int ilk = 0; ilk < MAX_PLAIN_SIBS; ilk++) { // if (total_sib_buffer_bytesize[ilk] > 0) { smallchunk_sibs_count++; total_bytesize += total_sib_buffer_bytesize[ilk]; } } total_bytesize += sizeof( Heapfile_Header ) + sizeof( Pickle_Header ) + (smallchunk_sibs_count * sizeof( Sib_Header )); // COUNT SPACE FOR BIG CHUNKS total_bytesize += sizeof(Externs_Header) + heapfile_cfun_table_bytesize( result->cfun_table ); // Include the space for the external symbols (i.e., runtime C functions referenced within the heapgraph). // Allocate the heap bytevector for the pickled // datastructure representation and initialize // the bytevector-writer. // pickle = allocate_heap_ram_for_pickle( task, total_bytesize ); // wr = WR_OpenMem( PTR_CAST(Unt8*, pickle), total_bytesize ); // WR_OpenMem def in src/c/heapcleaner/mem-writer.c // Initialize the sib headers: // sib_header_bytesize = smallchunk_sibs_count * sizeof(Sib_Header); // sib_header_buffer = (Sib_Header*) MALLOC (sib_header_bytesize); // p = sib_header_buffer; // for (int ilk = 0; ilk < MAX_PLAIN_SIBS; ilk++) { // if (total_sib_buffer_bytesize[ ilk ] <= 0) { // sib_headers[ilk] = NULL; // } else { // p->age = 0; p->chunk_ilk = ilk; // p->info.o.base_address = 0; // Not used. p->info.o.bytesize = total_sib_buffer_bytesize[ ilk ]; p->info.o.rounded_bytesize = -1; // Not used. // p->offset = -1; // Not used. sib_headers[ ilk ] = p; p++; } } // What about big chunks? XXX BUGGO FIXME // Write the pickle image header: // if (heapio__write_image_header (wr, NORMAL_DATASTRUCTURE_PICKLE) == FALSE) { // heapio__write_image_header def in src/c/heapcleaner/export-heap-stuff.c // FREE( sib_header_buffer ); return PICKLER_ERROR; } // Write the pickle header: // { Pickle_Header header; header.smallchunk_sibs_count = smallchunk_sibs_count; header.hugechunk_sibs_count = 0; // FIX THIS XXX BUGGO FIXME header.hugechunk_quire_count = 0; // FIX THIS XXX BUGGO FIXME if (!IS_EXTERNAL_TAG( root_chunk )) { Sibid sibid = SIBID_FOR_POINTER( book_to_sibid__global, root_chunk ); if (!SIBID_KIND_IS_CODE(sibid)) { // This is the normal case -- // we're saving a vanilla heap value. Vunt addr = HEAP_POINTER_AS_UNT( root_chunk ); int age = GET_AGE_FROM_SIBID( sibid) - 1; int kind = GET_KIND_FROM_SIBID(sibid) - 1; // GET_KIND_FROM_SIBID def in src/c/h/sibid.h addr -= adjust[ age ][ kind ].base; addr += adjust[ age ][ kind ].offset; header.root_chunk = HIO_TAG_PTR(kind, addr); // HIO_TAG_PTR def in src/c/heapcleaner/runtime-heap-image.h } else { // Embedded_Chunk_Info* p = FIND_EMBEDDED_CHUNK( result->embedded_chunk_table, root_chunk ); if ((p == NULL) || (p->kind == USED_CODE)) { // say_error( "Pickling compiled Mythryl code not implemented\n" ); FREE (sib_header_buffer); return PICKLER_ERROR; } else { header.root_chunk = p->relocated_address; } } } else { // IS_EXTERNAL_TAG( root_chunk ) // ASSERT( smallchunk_sibs_count == 0 ); header.root_chunk = root_chunk; } WR_WRITE(wr, &header, sizeof(header)); // WR_WRITE def in src/c/heapcleaner/writer.h // if (WR_ERROR(wr)) { FREE (sib_header_buffer); return PICKLER_ERROR; } } // Record in the pickle the table of heap-referenced // runtime C functions. May also include // a handful of assembly fns, exceptions // and refcells: // { int bytes_written = heapio__write_cfun_table( wr, result->cfun_table ); // heapio__write_cfun_table def in src/c/heapcleaner/export-heap-stuff.c if (bytes_written == -1) { FREE( sib_header_buffer ); return PICKLER_ERROR; } } // Write the pickle sib headers: // WR_WRITE (wr, sib_header_buffer, sib_header_bytesize); // if (WR_ERROR(wr)) { FREE (sib_header_buffer); return PICKLER_ERROR; } // Write the pickled datastructure proper: // for (int ilk = 0; ilk < MAX_PLAIN_SIBS; ilk++) { // if (ilk == NONPTR_DATA_SIB) { // Write into the pickle the required embedded literals: // pickler__pickle_embedded_literals( wr ); // pickler__pickle_embedded_literals def in src/c/heapcleaner/datastructure-pickler-cleaner.c // Write into the pickle remaining required strings: // for (int age = 0; age < max_age; age++) { // Sib* sib = heap->agegroup[ age ]->sib[ ilk ]; if (sib_is_active(sib)) { // sib_is_active def in src/c/h/heap.h // WR_WRITE( wr, sib->tospace.start, (Vunt) sib->tospace.first_free -(Vunt) sib->tospace.start ); } } } else { for (int age = 0; age < max_age; age++) { // Sib* sib = heap->agegroup[ age ]->sib[ ilk ]; if (sib_is_active( sib )) { // Val* top = sib->tospace.first_free; // for (Val* p = sib->tospace.start; p < top; p++ ){ Val w = *p; if (IS_POINTER(w)) { // Sibid sibid = SIBID_FOR_POINTER( book_to_sibid__global, w ); if (BOOK_IS_UNMAPPED(sibid)) { // w = add_cfun_to_heapfile_cfun_table( result->cfun_table, w); ASSERT (w != HEAP_VOID); } else if (SIBID_KIND_IS_CODE(sibid)) { Embedded_Chunk_Info* chunk_info = FIND_EMBEDDED_CHUNK( result->embedded_chunk_table, w ); if (chunk_info == NULL || chunk_info->kind == USED_CODE ){ die("Pickling of Mythryl compiled code not implemented"); } else { w = chunk_info->relocated_address; } } else { // Adjust the pointer: // int age = GET_AGE_FROM_SIBID( sibid)-1; int kind = GET_KIND_FROM_SIBID(sibid)-1; Vunt addr = HEAP_POINTER_AS_UNT(w); addr -= adjust[ age ][ kind ].base; addr += adjust[ age ][ kind ].offset; w = HIO_TAG_PTR( kind, addr ); } } // if (IS_POINTER(w)) WR_PUT(wr, (Vunt)w); } // for } } } } FREE( sib_header_buffer ); if (WR_ERROR(wr)) return PICKLER_ERROR; return make_vector_header(task, STRING_TAGWORD, pickle, total_bytesize); } // fun pickle_heap_datastructure
static Val forward_special_chunk (Agegroup* ag1, Val* chunk, Val tagword) { // ===================== // // Forward a special chunk (suspension or weak pointer). Sib* sib = ag1->sib[ RW_POINTERS_SIB ]; // Special chunks can be updated (modified) // so they have to go in RW_POINTERS_SIB. Val* new_chunk = sib->tospace.first_free; sib->tospace.first_free += SPECIAL_CHUNK_SIZE_IN_WORDS; // All specials are two words. switch (GET_LENGTH_IN_WORDS_FROM_TAGWORD( tagword )) { // case EVALUATED_LAZY_SUSPENSION_CTAG: case UNEVALUATED_LAZY_SUSPENSION_CTAG: // *new_chunk++ = tagword; *new_chunk = *chunk; break; case WEAK_POINTER_CTAG: { // Val v = *chunk; #ifdef DEBUG_WEAKREFS debug_say ("MinorGC: weak [%#x ==> %#x] --> %#x", chunk, new_chunk+1, v); #endif if (! IS_POINTER( v )) { #ifdef DEBUG_WEAKREFS debug_say (" unboxed\n"); #endif // Weak references to unboxed chunks (i.e., immediate Int31) // can never be nullified, since Int31 values, being stored // in-pointer, take no actual heapspace and thus cannot actually // ever get garbage-collected. Consequently, we can just copy // such weakrefs over and skip the rest of our usual processing: // new_chunk[0] = WEAKREF_TAGWORD; new_chunk[1] = v; ++new_chunk; } else { Sibid sibid = SIBID_FOR_POINTER( book_to_sibid__global, v ); Val* vp = PTR_CAST( Val*, v ); if (sibid != AGEGROUP0_SIBID) { // Weakref points to a value in an older heap agegroup. // Since we are only heapcleaning agegroup0 in // this file, the referenced value cannot get // garbage-collected this pass, so we can skip // the usual work to check for that and if necessary // null out the weakref: // #ifdef DEBUG_WEAKREFS debug_say (" old chunk\n"); #endif new_chunk[0] = WEAKREF_TAGWORD; new_chunk[1] = v; ++new_chunk; } else { // if (vp[-1] == FORWARDED_CHUNK_TAGWORD) { // // Reference to a chunk that has already been forwarded. // Note that we have to put the pointer to the non-forwarded // copy of the chunk (i.e, v) into the to-space copy // of the weak pointer, since the heapcleaner has the invariant // that it never sees to-space pointers during sweeping. #ifdef DEBUG_WEAKREFS debug_say (" already forwarded to %#x\n", PTR_CAST( Val, FOLLOW_FORWARDING_POINTER(vp))); #endif new_chunk[0] = WEAKREF_TAGWORD; new_chunk[1] = v; ++new_chunk; } else { // This is the important case: We are copying a weakref // of an agegroup0 value. That agegroup0 value might get // get garbage-collected this pass; if it does, we must null // out the weakref. // // To do this efficiently, as we copy such weakrefs from // agegroup0 into agegroup1 we chain them togther via // their tagword fields with the root pointer kept // in ag1->heap->weakrefs_forwarded_during_heapcleaning. // // At the end of heapcleaning we will consume this chain of // weakrefs in null_out_newly_dead_weakrefs() where // null_out_newly_dead_weakrefs is from src/c/heapcleaner/heapcleaner-stuff.c // we will null out any newly dead weakrefs and then // replace the chainlinks with valid tagwords -- either // WEAKREF_TAGWORD or NULLED_WEAKREF_TAGWORD, // as appropriate, thus erasing our weakref chain and // restoring sanity. // // We mark the chunk reference field in the forwarded copy // to make it look like an Tagged_Int so that the to-space // sweeper does not follow the weak reference. #ifdef DEBUG_WEAKREFS debug_say (" forward\n"); #endif new_chunk[0] = MARK_POINTER(PTR_CAST( Val, ag1->heap->weakrefs_forwarded_during_heapcleaning )); // MARK_POINTER just sets the low bit to 1, making it look like an Int31 value new_chunk[1] = MARK_POINTER( vp ); // MARK_POINTER is from src/c/h/heap-tags.h ag1->heap->weakrefs_forwarded_during_heapcleaning = new_chunk; ++new_chunk; } } } } break; case NULLED_WEAK_POINTER_CTAG: // Shouldn't happen in agegroup0. default: die ( "strange/unexpected special chunk @ %#x; tagword = %#x\n", chunk, tagword ); } // switch (GET_LENGTH_IN_WORDS_FROM_TAGWORD(tagword)) chunk[-1] = FORWARDED_CHUNK_TAGWORD; chunk[ 0] = (Val) (Vunt) new_chunk; return PTR_CAST( Val, new_chunk ); } // fun forward_special_chunk
static void process_task_heap_changelog (Task* task, Heap* heap) { // =========================== // // As tasks run, they note all stores into pointer-valued // Tagged-Int-valued refcells cannot contain cross-generation pointers so we don't track them in changelog. // refcells and rw_vectors in the 'heap_changelog', // a lisp-style list of "CONS cells" -- (val,next) pointer-pairs. // // We need this done because such stores into the heap // can introduce pointers from one agegroup into a // younger agegroup, which we need to take into account // when doing partial heapcleanings ("garbage collections"). // // Our job here is to promote to agegroup 1 all agegroup0 // values referenced by a refcell/vectorslot in the heap_changelog. Val this_heap_changelog_cell = task->heap_changelog; if (this_heap_changelog_cell == HEAP_CHANGELOG_NIL) return; // Abort quickly if no work to do. int updates = 0; // Heapcleaner statistics. Agegroup* age1 = heap->agegroup[ 0 ]; // Cache heap entry for speed. Sibid* b2s = book_to_sibid__global; // Cache global locally for speed. book_to_sibid__global def in src/c/heapcleaner/heapcleaner-initialization.c while (this_heap_changelog_cell != HEAP_CHANGELOG_NIL) { // Over all entries in the heap_changelog. // ++updates; // Heapcleaner statistics. Val* pointer = HEAP_CHANGELOG_HEAD( this_heap_changelog_cell ); // Get pointer to next updated refcell/vector slot to process. this_heap_changelog_cell = HEAP_CHANGELOG_TAIL( this_heap_changelog_cell ); // Step to next cell in heap_changelog list. Val pointee = *pointer; // Get contents of updated refcell/vectorslot. if (!IS_POINTER( pointee )) continue; // Ignore refcells and vectorslots containing Tagged_Int values. Sibid src_sibid = SIBID_FOR_POINTER(b2s, pointer ); // Get the Sibid tag for the ram-book containing the refcell/vectorslot. Sibid def in src/c/h/sibid.h if (src_sibid == AGEGROUP0_SIBID) continue; // Ignore updates to agegroup0 refcells and vectorslots. if (BOOK_IS_UNMAPPED( src_sibid )) continue; // Ignore updates to runtime-global refcells and vectorslots, which are handled elsewhere. Sibid dst_sibid = SIBID_FOR_POINTER(b2s, pointee ); // Get the Sibid tag for the ram-book containing the value referenced by the refcell/vectorslot. // int src_age = GET_AGE_FROM_SIBID( src_sibid ); // agegroup of the updated refcell/vectorslot. int dst_age = GET_AGE_FROM_SIBID( dst_sibid ); // agegroup of the chunk that the refcell/vectorslot points to. if (!SIBID_KIND_IS_CODE( dst_sibid )) { // if (dst_age == AGEGROUP0) { // *pointer = forward_agegroup0_chunk_to_agegroup1( age1, pointee,task, 1); // Promote pointee to agegroup 1. dst_age = 1; // Remember pointee now has age 1, not 0. // } } else { // Refcell/vector slot is pointing to code. if (dst_age >= src_age) continue; dst_age = get_age_of_codechunk( pointee ); } // Maybe update min_age value for // the card containing 'pointer': // if (src_age > dst_age) { // MAYBE_UPDATE_CARD_MIN_AGE_PER_POINTER( // MAYBE_UPDATE_CARD_MIN_AGE_PER_POINTER def in src/c/h/coarse-inter-agegroup-pointers-map.h // heap->agegroup[ src_age-1 ]->coarse_inter_agegroup_pointers_map, pointer, dst_age ); } } update_count__global += updates; // Cleaner statistics. Apparently never used. task->heap_changelog = HEAP_CHANGELOG_NIL; // We're done with heap_changelog so clear it. } // fun process_task_heap_changelog