void GetSharing::Completed(PolyObject *obj) { // We mustn't include cells in the permanent area. // We scan the permanent mutable areas for local addresses // but we mustn't add the cells themselves. Normally they // will be mutable so would be ignored but cells that have been // locked will now be immutable. The test in TestForScan is bypassed // by ScanAddressesInRegion. PolyWord *lengthWord = ((PolyWord*)obj) - 1; if (gMem.LocalSpaceForAddress(lengthWord) == 0) return; POLYUNSIGNED L = obj->LengthWord(); // We have tables for word objects and byte objects // We chain entries together using the length word so it // is important that we only do this for objects that // have no other bits in the header, such as the sign bit. if ((L & _OBJ_PRIVATE_FLAGS_MASK) == 0) { POLYUNSIGNED length = obj->Length(); if (length < NUM_WORD_VECTORS) wordVectors[length].AddToVector(obj, length); else largeWordCount++; wordAdded++; } else if ((L & _OBJ_PRIVATE_FLAGS_MASK) == _OBJ_BYTE_OBJ) { POLYUNSIGNED length = obj->Length(); if (length < NUM_BYTE_VECTORS) byteVectors[length].AddToVector(obj, length); else largeByteCount++; byteAdded++; } else if (! OBJ_IS_CODE_OBJECT(L) && ! OBJ_IS_MUTABLE_OBJECT(L)) excludedCount++; // Code and mutables can't be shared - see what could be // TODO: We don't attempt to share closure cells in 32-in-64. }
// 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(); } }
// General purpose object processor, Processes all the addresses in an object. // Handles the various kinds of object that may contain addresses. void ScanAddress::ScanAddressesInObject(PolyObject *obj, POLYUNSIGNED lengthWord) { do { ASSERT (OBJ_IS_LENGTH(lengthWord)); if (OBJ_IS_BYTE_OBJECT(lengthWord)) return; /* Nothing more to do */ POLYUNSIGNED length = OBJ_OBJECT_LENGTH(lengthWord); PolyWord *baseAddr = (PolyWord*)obj; if (OBJ_IS_CODE_OBJECT(lengthWord)) { // Scan constants within the code. machineDependent->ScanConstantsWithinCode(obj, obj, length, this); // Skip to the constants and get ready to scan them. obj->GetConstSegmentForCode(length, baseAddr, length); } // else it's a normal object, PolyWord *endWord = baseAddr + length; // We want to minimise the actual recursion we perform so we try to // use tail recursion if we can. We first scan from the end and // remove any words that don't need recursion. POLYUNSIGNED lastLengthWord = 0; while (endWord != baseAddr) { PolyWord wordAt = endWord[-1]; if (IS_INT(wordAt) || wordAt == PolyWord::FromUnsigned(0)) endWord--; // Don't need to look at this. else if ((lastLengthWord = ScanAddressAt(endWord-1)) != 0) // We need to process this one break; else endWord--; // We're not interested in this. } if (endWord == baseAddr) return; // We've done everything. // There is at least one word that needs to be processed, the // one at endWord-1. // Now process from the beginning forward to see if there are // any words before this that need to be handled. This way we are more // likely to handle the head of a list by recursion and the // tail by looping (tail recursion). while (baseAddr < endWord-1) { PolyWord wordAt = *baseAddr; if (IS_INT(wordAt) || wordAt == PolyWord::FromUnsigned(0)) baseAddr++; // Don't need to look at this. else { POLYUNSIGNED lengthWord = ScanAddressAt(baseAddr); if (lengthWord != 0) { wordAt = *baseAddr; // Reload because it may have been side-effected // We really have to process this recursively. if (wordAt.IsCodePtr()) ScanAddressesInObject(ObjCodePtrToPtr(wordAt.AsCodePtr()), lengthWord); else ScanAddressesInObject(wordAt.AsObjPtr(), lengthWord); baseAddr++; } else baseAddr++; } } // Finally process the last word we found that has to be processed. // Do this by looping rather than recursion. PolyWord wordAt = *baseAddr; // Last word to do. // This must be an address if (wordAt.IsCodePtr()) obj = ObjCodePtrToPtr(wordAt.AsCodePtr()); else obj = wordAt.AsObjPtr(); lengthWord = lastLengthWord; } while(1); }
// 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(); } }