// Process a value within the stack. PolyWord ScanAddress::ScanStackAddress(PolyWord val, StackSpace *stack, bool isCode) { PolyWord *base = stack->bottom; PolyWord *end = stack->top; // If isCode is set we definitely have a code address. It may have the // bottom bit set or it may be word aligned. if (isCode || val.IsCodePtr()) { /* Find the start of the code segment */ PolyObject *oldObject = ObjCodePtrToPtr(val.AsCodePtr()); // Calculate the byte offset of this value within the code object. POLYUNSIGNED offset = val.AsCodePtr() - (byte*)oldObject; PolyObject *newObject = ScanObjectAddress(oldObject); return PolyWord::FromCodePtr((byte*)newObject + offset); } else if (val.IsTagged() || val == PolyWord::FromUnsigned(0) || (val.AsAddress() > base && val.AsAddress() <= end)) /* We don't need to process tagged integers (now we've checked it isn't a code address) and we don't need to process addresses within the current stack. */ /* N.B. We have "<= end" rather than "< end" because it is possible for the stack to be completely empty on a terminated thread. */ return val; else { ASSERT(val.IsDataPtr()); return ScanObjectAddress(val.AsObjPtr()); } }
// Returns the new address if the argument is the address of an object that // has moved, otherwise returns the original. PolyWord SaveFixupAddress::GetNewAddress(PolyWord old) { if (old.IsTagged() || old == PolyWord::FromUnsigned(0) || gMem.IsIOPointer(old.AsAddress())) return old; // Nothing to do. // When we are updating addresses in the stack or in code segments we may have // code pointers. if (old.IsCodePtr()) { // Find the start of the code segment PolyObject *oldObject = ObjCodePtrToPtr(old.AsCodePtr()); // Calculate the byte offset of this value within the code object. POLYUNSIGNED offset = old.AsCodePtr() - (byte*)oldObject; PolyWord newObject = GetNewAddress(oldObject); return PolyWord::FromCodePtr(newObject.AsCodePtr() + offset); } ASSERT(old.IsDataPtr()); PolyObject *obj = old.AsObjPtr(); if (obj->ContainsForwardingPtr()) // tombstone is a pointer to a moved object { PolyObject *newp = obj->GetForwardingPtr(); ASSERT (newp->ContainsNormalLengthWord()); return newp; } ASSERT (obj->ContainsNormalLengthWord()); // object is not moved return old; }
bool GetSharing::TestForScan(PolyWord *pt) { PolyObject *obj; // This may be a forwarding pointer left over from a minor GC that did // not complete or it may be a sharing chain pointer that we've set up. while (1) { PolyWord p = *pt; ASSERT(p.IsDataPtr()); obj = p.AsObjPtr(); PolyWord *lengthWord = ((PolyWord*)obj) - 1; LocalMemSpace *space = gMem.LocalSpaceForAddress(lengthWord); if (space == 0) return false; // Ignore it if it points to a permanent area if (space->bitmap.TestBit(space->wordNo(lengthWord))) return false; // Wasn't marked - must be a forwarding pointer. if (obj->ContainsForwardingPtr()) { obj = obj->GetForwardingPtr(); *pt = obj; } else break; } ASSERT(obj->ContainsNormalLengthWord()); totalVisited += 1; totalSize += obj->Length() + 1; return true; }
void ProcessEnvModule::GarbageCollect(ScanAddress *process) /* Ensures that all the objects are retained and their addresses updated. */ { if (at_exit_list.IsDataPtr()) { PolyObject *obj = at_exit_list.AsObjPtr(); process->ScanRuntimeAddress(&obj, ScanAddress::STRENGTH_STRONG); at_exit_list = obj; } }
void DoCheckPointer (const PolyWord pt) { if (pt == PolyWord::FromUnsigned(0)) return; if (OBJ_IS_AN_INTEGER(pt)) return; DoCheck (pt); if (pt.IsDataPtr()) { PolyObject *obj = pt.AsObjPtr(); DoCheckObject (obj, obj->LengthWord()); } }
// Returns the new address if the argument is the address of an object that // has moved, otherwise returns the original. PolyWord ProcessFixupAddress::GetNewAddress(PolyWord old) { if (old.IsTagged() || old == PolyWord::FromUnsigned(0) || gMem.IsIOPointer(old.AsAddress())) return old; // Nothing to do. // When we are updating addresses in the stack or in code segments we may have // code pointers. if (old.IsCodePtr()) { // Find the start of the code segment PolyObject *oldObject = ObjCodePtrToPtr(old.AsCodePtr()); // Calculate the byte offset of this value within the code object. POLYUNSIGNED offset = old.AsCodePtr() - (byte*)oldObject; PolyWord newObject = GetNewAddress(oldObject); return PolyWord::FromCodePtr(newObject.AsCodePtr() + offset); } ASSERT(old.IsDataPtr()); PolyObject *obj = old.AsObjPtr(); POLYUNSIGNED L = obj->LengthWord(); // Generally each address will point to an object processed at a lower depth. // The exception is if we have a cycle and have assigned the rest of the // structure to a higher depth. // N.B. We return the original address here but this could actually share // with something else and not be retained. if (OBJ_IS_DEPTH(L)) return old; if (obj->ContainsForwardingPtr()) // tombstone is a pointer to a shared object { PolyObject *newp = obj->GetForwardingPtr(); // ASSERT (newp->ContainsNormalLengthWord()); return newp; } ASSERT (obj->ContainsNormalLengthWord()); // object is not shared return old; }
// Deal with weak objects void MTGCCheckWeakRef::ScanAddressesInObject(PolyObject *obj, POLYUNSIGNED L) { if (! OBJ_IS_WEAKREF_OBJECT(L)) return; ASSERT(OBJ_IS_MUTABLE_OBJECT(L)); // Should be a mutable. ASSERT(OBJ_IS_WORD_OBJECT(L)); // Should be a plain object. // See if any of the SOME objects contain unreferenced refs. POLYUNSIGNED length = OBJ_OBJECT_LENGTH(L); PolyWord *baseAddr = (PolyWord*)obj; for (POLYUNSIGNED i = 0; i < length; i++) { PolyWord someAddr = baseAddr[i]; if (someAddr.IsDataPtr()) { LocalMemSpace *someSpace = gMem.LocalSpaceForAddress(someAddr.AsAddress()); if (someSpace != 0) { PolyObject *someObj = someAddr.AsObjPtr(); // If this is a weak object the SOME value may refer to an unreferenced // ref. If so we have to set this entry to NONE. For safety we also // set the contents of the SOME to TAGGED(0). ASSERT(someObj->Length() == 1 && someObj->IsWordObject()); // Should be a SOME node. PolyWord refAddress = someObj->Get(0); LocalMemSpace *space = gMem.LocalSpaceForAddress(refAddress.AsAddress()); if (space != 0) // If the ref is permanent it's always there. { POLYUNSIGNED new_bitno = space->wordNo(refAddress.AsStackAddr()); if (! space->bitmap.TestBit(new_bitno)) { // It wasn't marked so it's otherwise unreferenced. baseAddr[i] = TAGGED(0); // Set it to NONE. someObj->Set(0, TAGGED(0)); // For safety. convertedWeak = true; } } } } } }
// This is called via ScanAddressesInRegion to process the permanent mutables. It is // also called from ScanObjectAddress to process root addresses. // It processes all the addresses reachable from the object. void RecursiveScan::ScanAddressesInObject(PolyObject *obj, POLYUNSIGNED lengthWord) { if (OBJ_IS_BYTE_OBJECT(lengthWord)) { Completed(obj); return; } while (true) { ASSERT (OBJ_IS_LENGTH(lengthWord)); // Get the length and base address. N.B. If this is a code segment // these will be side-effected by GetConstSegmentForCode. POLYUNSIGNED length = OBJ_OBJECT_LENGTH(lengthWord); PolyWord *baseAddr = (PolyWord*)obj; if (OBJ_IS_CODE_OBJECT(lengthWord)) { // It's better to process the whole code object in one go. ScanAddress::ScanAddressesInObject(obj, lengthWord); length = 0; // Finished } ASSERT(! OBJ_IS_BYTE_OBJECT(lengthWord)); // Check - remove this later // else it's a normal object, // If there are only two addresses in this cell that need to be // followed we follow them immediately and treat this cell as done. // If there are more than two we push the address of this cell on // the stack, follow the first address and then rescan it. That way // list cells are processed once only but we don't overflow the // stack by pushing all the addresses in a very large vector. PolyWord *endWord = baseAddr + length; PolyObject *firstWord = 0; PolyObject *secondWord = 0; while (baseAddr != endWord) { PolyWord wordAt = *baseAddr; if (wordAt.IsDataPtr() && wordAt != PolyWord::FromUnsigned(0)) { // Normal address. We can have words of all zeros at least in the // situation where we have a partially constructed code segment where // the constants at the end of the code have not yet been filled in. if (TestForScan(baseAddr)) // Test value at baseAddr (may side-effect it) { PolyObject *wObj = (*baseAddr).AsObjPtr(); if (wObj->IsByteObject()) { // Can do this now - don't need to push it MarkAsScanning(wObj); Completed(wObj); } else if (firstWord == 0) { firstWord = wObj; // We mark the word immediately. We can have // two words in an object that are the same // and we don't want to process it again. MarkAsScanning(firstWord); } else if (secondWord == 0) secondWord = wObj; else break; // More than two words. } } else if (wordAt.IsCodePtr()) { // If we're processing the constant area of a code segment this could // be a code address. PolyObject *oldObject = ObjCodePtrToPtr(wordAt.AsCodePtr()); // Calculate the byte offset of this value within the code object. POLYUNSIGNED offset = wordAt.AsCodePtr() - (byte*)oldObject; wordAt = oldObject; bool test = TestForScan(&wordAt); // TestForScan may side-effect the word. PolyObject *newObject = wordAt.AsObjPtr(); wordAt = PolyWord::FromCodePtr((byte*)newObject + offset); if (wordAt != *baseAddr) *baseAddr = wordAt; if (test) { if (firstWord == 0) { firstWord = newObject; MarkAsScanning(firstWord); } else if (secondWord == 0) secondWord = newObject; else break; } } baseAddr++; } if (baseAddr == endWord) { // We have done everything except possibly firstWord and secondWord. Completed(obj); if (secondWord != 0) { MarkAsScanning(secondWord); // Put this on the stack. If this is a list node we will be // pushing the tail. PushToStack(secondWord); } } else // Put this back on the stack while we process the first word PushToStack(obj); if (firstWord != 0) // Process it immediately. obj = firstWord; else if (StackIsEmpty()) return; else obj = PopFromStack(); lengthWord = obj->LengthWord(); } }
// This is called via ScanAddressesInRegion to process the permanent mutables. It is // also called from ScanObjectAddress to process root addresses. // It processes all the addresses reachable from the object. void MTGCProcessMarkPointers::ScanAddressesInObject(PolyObject *obj, POLYUNSIGNED lengthWord) { if (OBJ_IS_BYTE_OBJECT(lengthWord)) return; while (true) { ASSERT (OBJ_IS_LENGTH(lengthWord)); // Get the length and base address. N.B. If this is a code segment // these will be side-effected by GetConstSegmentForCode. POLYUNSIGNED length = OBJ_OBJECT_LENGTH(lengthWord); if (OBJ_IS_WEAKREF_OBJECT(lengthWord)) { // Special case. ASSERT(OBJ_IS_MUTABLE_OBJECT(lengthWord)); // Should be a mutable. ASSERT(OBJ_IS_WORD_OBJECT(lengthWord)); // Should be a plain object. // We need to mark the "SOME" values in this object but we don't mark // the references contained within the "SOME". PolyWord *baseAddr = (PolyWord*)obj; // Mark every word but ignore the result. for (POLYUNSIGNED i = 0; i < length; i++) (void)MarkAndTestForScan(baseAddr+i); // We've finished with this. length = 0; } else if (OBJ_IS_CODE_OBJECT(lengthWord)) { // It's better to process the whole code object in one go. ScanAddress::ScanAddressesInObject(obj, lengthWord); length = 0; // Finished } // else it's a normal object, // If there are only two addresses in this cell that need to be // followed we follow them immediately and treat this cell as done. // If there are more than two we push the address of this cell on // the stack, follow the first address and then rescan it. That way // list cells are processed once only but we don't overflow the // stack by pushing all the addresses in a very large vector. PolyWord *baseAddr = (PolyWord*)obj; PolyWord *endWord = baseAddr + length; PolyObject *firstWord = 0; PolyObject *secondWord = 0; PolyWord *restartAddr = 0; if (obj == largeObjectCache[locPtr].base) { baseAddr = largeObjectCache[locPtr].current; ASSERT(baseAddr > (PolyWord*)obj && baseAddr < ((PolyWord*)obj)+length); if (locPtr == 0) locPtr = LARGECACHE_SIZE-1; else locPtr--; } while (baseAddr != endWord) { PolyWord wordAt = *baseAddr; if (wordAt.IsDataPtr() && wordAt != PolyWord::FromUnsigned(0)) { // Normal address. We can have words of all zeros at least in the // situation where we have a partially constructed code segment where // the constants at the end of the code have not yet been filled in. if (TestForScan(baseAddr)) { if (firstWord == 0) firstWord = baseAddr->AsObjPtr(); else if (secondWord == 0) { // If we need to rescan because there are three or more words to do // this is the place we need to restart (or the start of the cell if it's // small). restartAddr = baseAddr; secondWord = baseAddr->AsObjPtr(); } else break; // More than two words. } } else if (wordAt.IsCodePtr()) { // If we're processing the constant area of a code segment this could // be a code address. // Check that this is actually an address. If we have had a bad pointer // earlier we may treat some length fields as values. ASSERT(gMem.SpaceForAddress(wordAt.AsCodePtr()) != 0); PolyObject *oldObject = ObjCodePtrToPtr(wordAt.AsCodePtr()); // Calculate the byte offset of this value within the code object. POLYUNSIGNED offset = wordAt.AsCodePtr() - (byte*)oldObject; wordAt = oldObject; bool test = TestForScan(&wordAt); // If we've changed it because we had a left-over forwarding pointer // we need to update the original. PolyObject *newObject = wordAt.AsObjPtr(); wordAt = PolyWord::FromCodePtr((byte*)newObject + offset); if (wordAt != *baseAddr) *baseAddr = wordAt; if (test) { if (firstWord == 0) firstWord = newObject; else if (secondWord == 0) { restartAddr = baseAddr; secondWord = newObject; } else break; } } baseAddr++; } if (baseAddr != endWord) // Put this back on the stack while we process the first word PushToStack(obj, length < largeObjectSize ? 0 : restartAddr, length); else if (secondWord != 0) { // Mark it now because we will process it. secondWord->SetLengthWord(secondWord->LengthWord() | _OBJ_GC_MARK); // Put this on the stack. If this is a list node we will be // pushing the tail. PushToStack(secondWord); } if (firstWord != 0) { // Mark it and process it immediately. firstWord->SetLengthWord(firstWord->LengthWord() | _OBJ_GC_MARK); obj = firstWord; } else if (msp == 0) { markStack[msp] = 0; // Really finished return; } else { // Clear the item above the top. This really is finished. if (msp < MARK_STACK_SIZE) markStack[msp] = 0; // Pop the item from the stack but don't overwrite it yet. // This allows another thread to steal it if there really // is nothing else to do. This is only really important // for large objects. obj = markStack[--msp]; // Pop something. } lengthWord = obj->LengthWord(); } }
// Process one level of the word data. // N.B. The length words are updated without any locking. This is safe // because all length words are initially chain entries and a chain entry // can be replaced by another chain entry, a forwarding pointer or a normal // length word. Forwarding pointers and normal length words are only ever // set once. There is a small chance that we could lose some sharing as a // result of a race condition if a thread defers an object because it // contains a pointer with a chain entry and later sees an otherwise // equal object where another thread has replaced the chain with a // normal address, adds it to the list for immediate processing and // so never compares the two. void SortVector::wordDataTask(GCTaskId*, void *a, void *) { SortVector *s = (SortVector*)a; // Partition the objects between those that have pointers to objects that are // still to be processed and those that have been processed. if (s->baseObject.objList == ENDOFLIST) return; PolyObject *h = s->baseObject.objList; s->baseObject.objList = ENDOFLIST; s->baseObject.objCount = 0; POLYUNSIGNED words = OBJ_OBJECT_LENGTH(s->lengthWord); s->carryOver = 0; for (unsigned i = 0; i < 256; i++) { // Clear the entries in the hash table but not the sharing count. s->processObjects[i].objList = ENDOFLIST; s->processObjects[i].objCount = 0; } while (h != ENDOFLIST) { PolyObject *next = h->GetForwardingPtr(); bool deferred = false; for (POLYUNSIGNED i = 0; i < words; i++) { PolyWord w = h->Get(i); if (w.IsDataPtr()) { PolyObject *p = w.AsObjPtr(); objectState state = getObjectState(p); if (state == FORWARDED) { // Update the addresses of objects that have been merged h->Set(i, p->GetForwardingPtr()); s->carryOver++; break; } else if (state == CHAINED) { // If it is still to be shared leave it deferred = true; break; // from the loop } } } if (deferred) { // We can't do it yet: add it back to the list h->SetForwardingPtr(s->baseObject.objList); s->baseObject.objList = h; s->baseObject.objCount++; } else { // Add it to the hash table. unsigned char hash = 0; for (POLYUNSIGNED i = 0; i < words*sizeof(PolyWord); i++) hash += h->AsBytePtr()[i]; h->SetForwardingPtr(s->processObjects[hash].objList); s->processObjects[hash].objList = h; s->processObjects[hash].objCount++; } h = next; } s->SortData(); }