// Tests if this needs to be scanned. It marks it if it has not been marked // unless it has to be scanned. bool MTGCProcessMarkPointers::TestForScan(PolyWord *pt) { if ((*pt).IsTagged()) return false; // This could contain a forwarding pointer if it points into an // allocation area and has been moved by the minor GC. // We have to be a little careful. Another thread could also // be following any forwarding pointers here. However it's safe // because they will update it with the same value. PolyObject *obj = (*pt).AsObjPtr(); if (obj->ContainsForwardingPtr()) { obj = FollowForwarding(obj); *pt = obj; } if (gMem.LocalSpaceForAddress(obj) == 0) return false; // Ignore it if it points to a permanent area POLYUNSIGNED L = obj->LengthWord(); if (L & _OBJ_GC_MARK) return false; // Already marked if (debugOptions & DEBUG_GC_DETAIL) Log("GC: Mark: %p %" POLYUFMT " %u\n", obj, OBJ_OBJECT_LENGTH(L), GetTypeBits(L)); if (OBJ_IS_BYTE_OBJECT(L)) { obj->SetLengthWord(L | _OBJ_GC_MARK); // Mark it return false; // We've done as much as we need } return true; }
// The initial entry to process the roots. These may be RTS addresses or addresses in // a thread stack. Also called recursively to process the addresses of constants in // code segments. This is used in situations where a scanner may return the // updated address of an object. PolyObject *MTGCProcessMarkPointers::ScanObjectAddress(PolyObject *obj) { PolyWord val = obj; LocalMemSpace *space = gMem.LocalSpaceForAddress(val.AsAddress()); if (space == 0) return obj; // Ignore it if it points to a permanent area // We may have a forwarding pointer if this has been moved by the // minor GC. if (obj->ContainsForwardingPtr()) { obj = FollowForwarding(obj); val = obj; space = gMem.LocalSpaceForAddress(val.AsAddress()); } ASSERT(obj->ContainsNormalLengthWord()); POLYUNSIGNED L = obj->LengthWord(); if (L & _OBJ_GC_MARK) return obj; // Already marked obj->SetLengthWord(L | _OBJ_GC_MARK); // Mark it if (profileMode == kProfileLiveData || (profileMode == kProfileLiveMutables && obj->IsMutable())) AddObjectProfile(obj); POLYUNSIGNED n = OBJ_OBJECT_LENGTH(L); if (debugOptions & DEBUG_GC_DETAIL) Log("GC: Mark: %p %" POLYUFMT " %u\n", obj, n, GetTypeBits(L)); if (OBJ_IS_BYTE_OBJECT(L)) return obj; // If we already have something on the stack we must being called // recursively to process a constant in a code segment. Just push // it on the stack and let the caller deal with it. if (msp != 0) PushToStack(obj); // Can't check this because it may have forwarding ptrs. else { MTGCProcessMarkPointers::ScanAddressesInObject(obj, L); // We can only check after we've processed it because if we // have addresses left over from an incomplete partial GC they // may need to forwarded. CheckObject (obj); } return obj; }
void DoCheckObject (const PolyObject *base, POLYUNSIGNED L) { PolyWord *pt = (PolyWord*)base; CheckAddress(pt); MemSpace *space = gMem.SpaceForAddress(pt-1); if (space == 0) Crash ("Bad pointer 0x%08" PRIxPTR " found", (uintptr_t)pt); ASSERT (OBJ_IS_LENGTH(L)); POLYUNSIGNED n = OBJ_OBJECT_LENGTH(L); if (n == 0) return; ASSERT (n > 0); ASSERT(pt-1 >= space->bottom && pt+n <= space->top); byte flags = GetTypeBits(L); /* discards GC flag and mutable bit */ if (flags == F_BYTE_OBJ) /* possibly signed byte object */ return; /* Nothing more to do */ if (flags == F_CODE_OBJ) /* code object */ { ScanCheckAddress checkAddr; /* We flush the instruction cache here in case we change any of the instructions when we update addresses. */ machineDependent->FlushInstructionCache(pt, (n + 1) * sizeof(PolyWord)); machineDependent->ScanConstantsWithinCode((PolyObject *)base, (PolyObject *)base, n, &checkAddr); /* Skip to the constants. */ base->GetConstSegmentForCode(n, pt, n); } else if (flags == F_CLOSURE_OBJ) { n -= sizeof(PolyObject*) / sizeof(PolyWord); pt += sizeof(PolyObject*) / sizeof(PolyWord); } else ASSERT (flags == 0); /* ordinary word object */ while (n--) DoCheck (*pt++); }
// Copy objects from the source space into an earlier space or up within the // current space. static void copyAllData(GCTaskId *id, void * /*arg1*/, void * /*arg2*/) { LocalMemSpace *mutableDest = 0, *immutableDest = 0; for (std::vector<LocalMemSpace*>::reverse_iterator i = gMem.lSpaces.rbegin(); i != gMem.lSpaces.rend(); i++) { LocalMemSpace *src = *i; if (src->spaceOwner == 0) { PLocker lock(©Lock); if (src->spaceOwner == 0) src->spaceOwner = id; else continue; } else if (src->spaceOwner != id) continue; if (debugOptions & DEBUG_GC_ENHANCED) Log("GC: Copy: copying area %p (thread %p) %s \n", src, id, src->spaceTypeString()); // We start at fullGCLowerLimit which is the lowest marked object in the heap // N.B. It's essential that the first set bit at or above this corresponds // to the length word of a real object. uintptr_t bitno = src->wordNo(src->fullGCLowerLimit); // Set the limit to the top so we won't rescan this. That can // only happen if copying takes a very short time and the same // thread runs multiple tasks. src->fullGCLowerLimit = src->top; // src->highest is the bit position that corresponds to the top of // generation we're copying. uintptr_t highest = src->wordNo(src->top); for (;;) { if (bitno >= highest) break; /* SPF version; Invariant: 0 < highest - bitno */ bitno += src->bitmap.CountZeroBits(bitno, highest - bitno); if (bitno >= highest) break; /* first set bit corresponds to the length word */ PolyWord *old = src->wordAddr(bitno); /* Old object address */ PolyObject *obj = (PolyObject*)(old+1); POLYUNSIGNED L = obj->LengthWord(); ASSERT (OBJ_IS_LENGTH(L)); POLYUNSIGNED n = OBJ_OBJECT_LENGTH(L) + 1 ;/* Length of allocation (including length word) */ bitno += n; // Find a mutable space for the mutable objects and an immutable space for // the immutables. We copy objects into earlier spaces or within its own // space but we don't copy an object to a later space. This avoids the // risk of copying an object multiple times. Previously this copied objects // into later spaces but that doesn't work well if we have converted old // saved state segments into local areas. It's much better to delete them // if possible. bool isMutable = OBJ_IS_MUTABLE_OBJECT(L); LocalMemSpace *destSpace = isMutable || immutableDest == 0 ? mutableDest : immutableDest; PolyWord *newp = FindFreeAndAllocate(destSpace, (src == destSpace) ? bitno : 0, n); if (newp == 0 && src != destSpace) { // See if we can find a different space. // N.B. FindNextSpace side-effects mutableDest/immutableDest to give the next space. if (FindNextSpace(src, isMutable ? &mutableDest : &immutableDest, isMutable, id)) { bitno -= n; // Redo this object continue; } // else just leave it } if (newp == 0) /* no room */ { // We're not going to move this object // Update src->upperAllocPtr, so the old object doesn't get trampled. if (old < src->upperAllocPtr) src->upperAllocPtr = old; // Previously this continued compressing to try to make space available // on the next GC. Normally full GCs are infrequent so the chances are // that at the next GC other data will have been freed. Just stop at // this point. // However if we're compressing a mutable area and there is immutable // data in it we should move those out because the mutable area is scanned // on every partial GC. if (! src->isMutable || src->i_marked == 0) break; } else { PolyObject *destAddress = (PolyObject*)(newp+1); obj->SetForwardingPtr(destAddress); CopyObjectToNewAddress(obj, destAddress, L); if (debugOptions & DEBUG_GC_DETAIL) Log("GC: Copy: %p %lu %u -> %p\n", obj, OBJ_OBJECT_LENGTH(L), GetTypeBits(L), destAddress); } } if (mutableDest == src) mutableDest = 0; if (immutableDest == src) immutableDest = 0; } }