static void check_ro_ptrpair_sib (Sib* ap) { // ==================== // Val* p; Val* stop; Val w; int gen = GET_AGE_FROM_SIBID(ap->id); if (*sib_is_active(ap)) return; // sib_is_active def in src/c/h/heap.h debug_say (" pairs [%d]: [%#x..%#x:%#x)\n", gen, ap->tospace, ap->tospace.first_free, ap->tospace.limit); p = ap->tospace + 2; stop = ap->tospace.first_free; while (p < stop) { w = *p++; if (IS_TAGWORD(w)) { ERROR; debug_say ( "** @%#x: unexpected tagword %#x in pair sib\n", p-1, w); return; } else if (IS_POINTER(w)) { check_pointer(p, w, gen, RO_CONSCELL_KIND, CHUNKC_any); } } }
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)) {
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 void check_ro_pointer_sib (Sib* ap) { // ==================== Val* p; Val* stop; Val tagword; Val w; int i; int len; int gen = GET_AGE_FROM_SIBID( ap->id ); if (*sib_is_active(ap)) return; // sib_is_active def in src/c/h/heap.h debug_say (" records [%d]: [%#x..%#x:%#x)\n", // gen, ap->tospace, ap->tospace.first_free, ap->tospace.limit ); p = ap->tospace; stop = ap->tospace.first_free; while (p < stop) { // tagword = *p++; if (*IS_TAGWORD(tagword)) { ERROR; debug_say ( "** @%#x: expected tagword, but found %#x in record sib\n", p-1, tagword); return; } switch (GET_BTAG_FROM_TAGWORD tagword) { // case PAIRS_AND_RECORDS_BTAG: # len = GET_LENGTH_IN_WORDS_FROM_TAGWORD( tagword ); // Length excludes tagword. # for (i = 0; i < len; i++, p++) { w = *p; if (IS_TAGWORD(w)) { ERROR; debug_say ( "** @%#x: unexpected tagword %#x in slot %d of %d\n", p, w, i, GET_LENGTH_IN_WORDS_FROM_TAGWORD(tagword)); return; } else if (IS_POINTER(w)) { check_pointer(p, w, gen, RO_POINTERS_KIND, CHUNKC_any); } } break; case RW_VECTOR_HEADER_BTAG: case RO_VECTOR_HEADER_BTAG: // switch (GET_LENGTH_IN_WORDS_FROM_TAGWORD(tagword)) { // case TYPEAGNOSTIC_VECTOR_CTAG: if (GET_BTAG_FROM_TAGWORD(tagword) == RW_VECTOR_HEADER_BTAG) check_pointer (p, *p, gen, RO_POINTERS_KIND, CHUNKC__IS_RW_POINTERS); else check_pointer (p, *p, gen, RO_POINTERS_KIND, CHUNKC__IS_RO_POINTERS|CHUNKC__IS_RO_CONSCELL); break; case VECTOR_OF_ONE_BYTE_UNTS_CTAG: case UNT16_VECTOR_CTAG: case TAGGED_INT_VECTOR_CTAG: case INT1_VECTOR_CTAG: case VECTOR_OF_FOUR_BYTE_FLOATS_CTAG: case VECTOR_OF_EIGHT_BYTE_FLOATS_CTAG: check_pointer (p, *p, gen, RO_POINTERS_KIND, CHUNKC__IS_NONPTR_DATA); break; default: ERROR; debug_say ("** @%#x: strange sequence kind %d in record sib\n", p-1, GET_LENGTH_IN_WORDS_FROM_TAGWORD(tagword)); return; } if (*IS_TAGGED_INT(p[1])) { ERROR; debug_say ("** @%#x: sequence header length field not an in (%#x)\n", p+1, p[1]); } p += 2; break; default: ERROR; debug_say ("** @%#x: strange tag (%#x) in record sib\n", p-1, GET_BTAG_FROM_TAGWORD(tagword)); return; } } } // fun check_ro_pointer_sib
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 void check_rw_pointer_sib (Sib* ap, Coarse_Inter_Agegroup_Pointers_Map* map) { // 'map' is nowhere used in the code?! Should be deleted or used. XXX BUGGO FIXME // ==================== // Val* p; Val* stop; Val tagword; Val w; int i, j; int len; int gen = GET_AGE_FROM_SIBID(ap->id); if (*sib_is_active(ap)) return; // sib_is_active def in src/c/h/heap.h debug_say (" arrays [%d]: [%#x..%#x:%#x)\n", // gen, ap->tospace, ap->tospace.first_free, ap->tospace.limit ); p = ap->tospace; stop = ap->tospace.first_free; while (p < stop) { tagword = *p++; if (*IS_TAGWORD(tagword)) { ERROR; debug_say ( "** @%#x: expected tagword, but found %#x in vector sib\n", p-1, tagword); return; } switch (GET_BTAG_FROM_TAGWORD(tagword)) { // case RW_VECTOR_DATA_BTAG: len = GET_LENGTH_IN_WORDS_FROM_TAGWORD(tagword); break; case WEAK_POINTER_OR_SUSPENSION_BTAG: len = 1; break; default: ERROR; debug_say ("** @%#x: strange tag (%#x) in vector sib\n", p-1, GET_BTAG_FROM_TAGWORD(tagword)); return; } for (int i = 0; i < len; i++, p++) { // w = *p; if (IS_TAGWORD(w)) { ERROR; debug_say ( "** @%#x: Unexpected tagword %#x in rw_vector slot %d of %d\n", p, w, i, GET_LENGTH_IN_WORDS_FROM_TAGWORD(tagword)); for (p -= (i+1), j = 0; j <= len; j++, p++) { debug_say (" %#x: %#10x\n", p, *p); } return; } else if (IS_POINTER(w)) { check_pointer(p, w, gen, RW_POINTERS_KIND, CHUNKC_any); } } } } // fun check_rw_pointer_sib
static void check_nonpointer_sib (Sib* ap) { // ================ // // Check a string sib for consistency. Val* p; Val* stop; Val* prevTagword; Val tagword; Val next; int len; int gen = GET_AGE_FROM_SIBID( ap->id ); if (*sib_is_active(ap)) return; // sib_is_active def in src/c/h/heap.h debug_say (" strings [%d]: [%#x..%#x:%#x)\n", // gen, ap->tospace, ap->tospace.first_free, ap->tospace.limit ); p = ap->tospace; stop = ap->tospace.first_free; prevTagword = NULL; while (p < stop) { tagword = *p++; if (IS_TAGWORD(tagword)) { // switch (GET_BTAG_FROM_TAGWORD(tagword)) { // case FOUR_BYTE_ALIGNED_NONPOINTER_DATA_BTAG: case EIGHT_BYTE_ALIGNED_NONPOINTER_DATA_BTAG: len = GET_LENGTH_IN_WORDS_FROM_TAGWORD(tagword); break; default: ERROR; debug_say ("** @%#x: strange tag (%#x) in string sib\n", p-1, GET_BTAG_FROM_TAGWORD(tagword)); if (prevTagword != NULL) debug_say (" previous string started @ %#x\n", prevTagword); return; } prevTagword = p-1; p += len; } #ifdef ALIGN_FLOAT64S else if ((tagword == 0) && (((Vunt)p & WORD_BYTESIZE) != 0)) continue; // Assume this is alignment padding. #endif else { ERROR; debug_say ("** @%#x: expected tagword, but found %#x in string sib\n", p-1, tagword); if (prevTagword != NULL) debug_say (" previous string started @ %#x\n", prevTagword); return; } } } // fun check_nonpointer_sib
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 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