// 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()); } }
// Update the addresses in a group of words. void LoadRelocate::RelocateAddressAt(PolyWord *pt) { PolyWord val = *pt; if (val.IsTagged()) return; // Which segment is this address in? unsigned i; for (i = 0; i < nDescrs; i++) { SavedStateSegmentDescr *descr = &descrs[i]; if (val.AsAddress() > descr->originalAddress && val.AsAddress() <= (char*)descr->originalAddress + descr->segmentSize) { // It's in this segment: relocate it to the current position. MemSpace *space = descr->segmentIndex == 0 ? gMem.IoSpace() : gMem.SpaceForIndex(descr->segmentIndex); // Error if this doesn't match. byte *setAddress = (byte*)space->bottom + ((char*)val.AsAddress() - (char*)descr->originalAddress); *pt = PolyWord::FromCodePtr(setAddress); break; } } if (i == nDescrs) { // Error: Not found. errorMessage = "Unmatched address"; } }
// 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; }
void DoCheck (const PolyWord pt) { if (pt == PolyWord::FromUnsigned(0)) return; if (pt.IsTagged()) return; CheckAddress(pt.AsStackAddr()); }
Handle string_length_c(TaskData *mdTaskData, Handle string) /* Length of a string */ { PolyWord str = string->Word(); if (str.IsTagged()) // Short form return Make_arbitrary_precision(mdTaskData, 1); POLYUNSIGNED length = ((PolyStringObject *)str.AsObjPtr())->length; return Make_arbitrary_precision(mdTaskData, length); }
// 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; }
// We use _OBJ_GC_MARK to detect when we have visited a cell but not yet // computed the depth. We have to be careful that this bit is removed // before we finish in the case that we run out of memory and throw an // exception. PushToStack may throw the exception if the stack needs to // grow. POLYUNSIGNED ProcessAddToVector::AddObjectsToDepthVectors(PolyWord old) { // If this is a tagged integer or an IO pointer that's simply a constant. if (old.IsTagged() || old == PolyWord::FromUnsigned(0)) return 0; MemSpace *space = gMem.SpaceForAddress(old.AsAddress()); if (space == 0 || space->spaceType == ST_IO) return 0; PolyObject *obj = old.AsObjPtr(); POLYUNSIGNED L = obj->LengthWord(); if (OBJ_IS_DEPTH(L)) // tombstone contains genuine depth or 0. return OBJ_GET_DEPTH(L); if (obj->LengthWord() & _OBJ_GC_MARK) return 0; // Marked but not yet scanned. Circular structure. ASSERT (OBJ_IS_LENGTH(L)); if (obj->IsMutable()) { // Mutable data in the local or permanent areas if (! obj->IsByteObject()) { // Add it to the vector so we will update any addresses it contains. m_parent->AddToVector(0, L, old.AsObjPtr()); // and follow any addresses to try to merge those. PushToStack(obj); obj->SetLengthWord(L | _OBJ_GC_MARK); // To prevent rescan } return 0; // Level is zero } if (space->spaceType == ST_PERMANENT && ((PermanentMemSpace*)space)->hierarchy == 0) { // Immutable data in the permanent area can't be merged // because it's read only. We need to follow the addresses // because they may point to mutable areas containing data // that can be. A typical case is the root function pointing // at the global name table containing new declarations. Bitmap *bm = &((PermanentMemSpace*)space)->shareBitmap; if (! bm->TestBit((PolyWord*)obj - space->bottom)) { bm->SetBit((PolyWord*)obj - space->bottom); if (! obj->IsByteObject()) PushToStack(obj); } return 0; } /* There's a problem sharing code objects if they have relative calls/jumps in them to other code. The code of two functions may be identical (e.g. they both call functions 100 bytes ahead) and so they will appear the same but if the functions they jump to are different they are actually different. For that reason we don't share code segments. DCJM 4/1/01 */ if (obj->IsCodeObject()) { // We want to update addresses in the code segment. m_parent->AddToVector(0, L, old.AsObjPtr()); PushToStack(obj); obj->SetLengthWord(L | _OBJ_GC_MARK); // To prevent rescan return 0; } // Byte objects always have depth 1 and can't contain addresses. if (obj->IsByteObject()) { m_parent->AddToVector (1, L, old.AsObjPtr());// add to vector at correct depth obj->SetLengthWord(OBJ_SET_DEPTH(1)); return 1; } ASSERT(OBJ_IS_WORD_OBJECT(L)); // That leaves immutable data objects. PushToStack(obj); obj->SetLengthWord(L | _OBJ_GC_MARK); // To prevent rescan return 0; }
// Copy a stack static void CopyStackFrame(StackObject *old_stack, POLYUNSIGNED old_length, StackObject *new_stack, POLYUNSIGNED new_length) { /* Moves a stack, updating all references within the stack */ PolyWord *old_base = (PolyWord *)old_stack; PolyWord *new_base = (PolyWord*)new_stack; PolyWord *old_top = old_base + old_length; /* Calculate the offset of the new stack from the old. If the frame is being extended objects in the new frame will be further up the stack than in the old one. */ POLYSIGNED offset = new_base - old_base + new_length - old_length; /* Copy the registers, changing any that point into the stack. */ new_stack->p_space = old_stack->p_space; new_stack->p_pc = old_stack->p_pc; new_stack->p_sp = old_stack->p_sp + offset; new_stack->p_hr = old_stack->p_hr + offset; new_stack->p_nreg = old_stack->p_nreg; /* p_nreg contains contains the number of CHECKED registers */ POLYUNSIGNED i; for (i = 0; i < new_stack->p_nreg; i++) { PolyWord R = old_stack->p_reg[i]; /* if the register points into the old stack, make the new copy point at the same relative offset within the new stack, otherwise make the new copy identical to the old version. */ if (R.IsTagged() || R.AsStackAddr() < old_base || R.AsStackAddr() >= old_top) new_stack->p_reg[i] = R; else new_stack->p_reg[i] = PolyWord::FromStackAddr(R.AsStackAddr() + offset); } /* Copy unchecked registers. - The next "register" is the number of unchecked registers to copy. Unchecked registers are used for values that might look like addresses, i.e. don't have tag bits, but are not. */ POLYUNSIGNED n = old_stack->p_reg[i].AsUnsigned(); new_stack->p_reg[i] = old_stack->p_reg[i]; i++; ASSERT (n < 100); while (n--) { new_stack->p_reg[i] = old_stack->p_reg[i]; i++; } /* Skip the unused part of the stack. */ i = (PolyWord*)old_stack->p_sp - old_base; ASSERT (i <= old_length); i = old_length - i; PolyWord *old = old_stack->p_sp; PolyWord *newp= new_stack->p_sp; while (i--) { // ASSERT(old >= old_base && old < old_base+old_length); // ASSERT(newp >= new_base && newp < new_base+new_length); PolyWord old_word = *old++; if (old_word.IsTagged() || old_word.AsStackAddr() < old_base || old_word.AsStackAddr() >= old_top) *newp++ = old_word; else *newp++ = PolyWord::FromStackAddr(old_word.AsStackAddr() + offset); } ASSERT(old == ((PolyWord*)old_stack)+old_length); ASSERT(newp == ((PolyWord*)new_stack)+new_length); }