// Store a constant value. Also used with a patch table when importing a saved heap which has // been exported using the C exporter. void ScanAddress::SetConstantValue(byte *addressOfConstant, PolyWord p, ScanRelocationKind code) { switch (code) { case PROCESS_RELOC_DIRECT: // 32 or 64 bit address of target { POLYUNSIGNED valu = p.AsUnsigned(); for (unsigned i = 0; i < sizeof(PolyWord); i++) { addressOfConstant[i] = (byte)(valu & 255); valu >>= 8; } } break; case PROCESS_RELOC_I386RELATIVE: // 32 bit relative address { POLYSIGNED newDisp = p.AsCodePtr() - addressOfConstant - 4; #if (SIZEOF_VOIDP != 4) ASSERT(newDisp < 0x80000000 && newDisp >= -(POLYSIGNED)0x80000000); #endif for (unsigned i = 0; i < 4; i++) { addressOfConstant[i] = (byte)(newDisp & 0xff); newDisp >>= 8; } } break; } }
// 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"; } }
// Process the value at a given location and update it as necessary. POLYUNSIGNED ScanAddress::ScanAddressAt(PolyWord *pt) { PolyWord val = *pt; PolyWord newVal = val; if (IS_INT(val) || val == PolyWord::FromUnsigned(0)) { // We can get zeros in the constant area if we garbage collect // while compiling some code. */ } else if (val.IsCodePtr()) { // We can get code pointers either in the stack as return addresses or // handler pointers or in constants in code segments as the addresses of // exception handlers. // 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; // Mustn't use ScanAddressAt here. That's only valid if the value points // into the area being updated. PolyObject *newObject = ScanObjectAddress(oldObject); newVal = PolyWord::FromCodePtr((byte*)newObject + offset); } else { ASSERT(OBJ_IS_DATAPTR(val)); // Database pointer, local pointer or IO pointer. // We need to include IO area pointers when we produce an object module. newVal = ScanObjectAddress(val.AsObjPtr()); } if (newVal != val) // Only update if we need to. *pt = newVal; return 0; }
// adds w to the set // returns true iff the set was changed bool MalcevSet::addWord( const PolyWord& pw ) { bool wasChanged = false; PolyWord remainder = collect(pw); while( ! remainder.isEmpty() ) { Generator key = leader(remainder); // if there is no word with the same leader, include the remainder to the set if( ! theSet.bound(key) ) { theSet.bind( key, remainder ); isBasis = false; isNormal = dontknow; return true; } // reduce the remainder PolyWord currentWord = theSet.valueOf(key); if( reduceWords( currentWord, remainder) ) { theSet.bind( key, currentWord ); isBasis = false; isNormal = dontknow; wasChanged = true; } } return wasChanged; }
bool MalcevSet::isNormalClosure() const { if( isNormal != dontknow ) return isNormal; if( ! isBasis ) error("Attempt to use MalcevSet::isNormalClosure before the set is full."); const BasicCommutators& BC = theCollector.commutators(); MalcevSet* This = (MalcevSet *)this; // to break physical constness //the subgroup is normal iff any w^x is in the set for(int i = 1; i < BC.theFirstOfWeight(BC.nilpotencyClass() ); i++) { if( ! theSet.bound( Generator(i) ) ) continue; PolyWord W = theSet.valueOf( Generator(i) ); for(int j = 1; j <= BC.numberOfGenerators(); j++) { PolyWord conj = collect( Letter(j, -1) * W * Letter(j, 1) ); checkMembership(conj); if(! conj.isEmpty() ) { This->isNormal = no; return false; } } } This->isNormal = yes; return true; }
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; }
static Handle setRegistryKey(TaskData *taskData, Handle args, HKEY hkey) { TCHAR valName[MAX_PATH]; LONG lRes; PolyWord str = args->WordP()->Get(3); POLYUNSIGNED length = Poly_string_to_C(args->WordP()->Get(1), valName, MAX_PATH); DWORD dwType = get_C_unsigned(taskData, DEREFWORDHANDLE(args)->Get(2)); if (length > MAX_PATH) raise_syscall(taskData, "Value name too long", ENAMETOOLONG); // The value is binary. Strings will already have had a null added. if (IS_INT(str)) { byte b = (byte)UNTAGGED(str); // Single byte value. lRes = RegSetValueEx(hkey, valName, 0, dwType, &b, 1); } else { PolyStringObject *ps = (PolyStringObject*)str.AsObjPtr(); lRes = RegSetValueEx(hkey, valName, 0, dwType, (CONST BYTE *)ps->chars, (DWORD)ps->length); } if (lRes != ERROR_SUCCESS) raise_syscall(taskData, "RegSetValue failed", -lRes); return Make_arbitrary_precision(taskData, 0); }
PolyWord MalcevSet::makeCommutator( PolyWord& pw1, PolyWord& pw2 ) { // commute words PolyWord comm = commutatorOfInverses(pw1, pw2); comm.freelyReduce(); return comm; }
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); }
void PExport::printValue(PolyWord q) { if (IS_INT(q) || q == PolyWord::FromUnsigned(0)) fprintf(exportFile, "%" POLYSFMT, UNTAGGED(q)); else if (OBJ_IS_CODEPTR(q)) printCodeAddr(q.AsCodePtr()); else printAddress(q.AsAddress()); }
bool PImport::GetValue(PolyWord *result) { int ch = getc(f); if (ch == '@') { /* Address of an object. */ POLYUNSIGNED obj; fscanf(f, "%" POLYUFMT, &obj); ASSERT(obj < nObjects); *result = objMap[obj]; } else if (ch == '$') { /* Code address. */ POLYUNSIGNED obj, offset; fscanf(f, "%" POLYUFMT "+%" POLYUFMT, &obj, &offset); ASSERT(obj < nObjects); PolyObject *q = objMap[obj]; ASSERT(q->IsCodeObject()); *result = PolyWord::FromCodePtr((PolyWord(q)).AsCodePtr() + offset); /* The offset is in bytes. */ } else if ((ch >= '0' && ch <= '9') || ch == '-') { /* Tagged integer. */ POLYSIGNED j; ungetc(ch, f); fscanf(f, "%" POLYSFMT, &j); /* The assertion may be false if we are porting to a machine with a shorter tagged representation. */ ASSERT(j >= -MAXTAGGED-1 && j <= MAXTAGGED); *result = TAGGED(j); } else if (ch == 'I') { /* IO entry number. */ POLYUNSIGNED j; fscanf(f, "%" POLYUFMT, &j); ASSERT(j < POLY_SYS_vecsize); *result = (PolyObject*)&gMem.ioSpace->bottom[j * IO_SPACING]; } else if (ch == 'J') { /* IO entry number with offset. */ POLYUNSIGNED j, offset; fscanf(f, "%" POLYUFMT "+%" POLYUFMT, &j, &offset); ASSERT(j < POLY_SYS_vecsize); PolyWord base = (PolyObject*)&gMem.ioSpace->bottom[j * IO_SPACING]; *result = PolyWord::FromCodePtr(base.AsCodePtr() + offset); } else { fprintf(stderr, "Unexpected character in stream"); return false; } 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; } }
// 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 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()); } }
// 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()); } }
// 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; }
Chars SGOfFreeNilpotentGroupRep::asDecomposition( const PolyWord& w ) const { if( ! basisIsInitialized() ) error("SGOfFreeNilpotentGroupRep::asDecomposition: the basis must be built"); return w.toChars( theBasisNames ); }
/* Get the index corresponding to an address. */ PolyWord MachoExport::createRelocation(PolyWord p, void *relocAddr) { void *addr = p.AsAddress(); unsigned addrArea = findArea(addr); POLYUNSIGNED offset = (char*)addr - (char*)memTable[addrArea].mtAddr; adjustOffset(addrArea, offset); // It looks as though struct relocation_info entries are only used // with GENERIC_RELOC_VANILLA types. struct relocation_info relInfo; setRelocationAddress(relocAddr, &relInfo.r_address); relInfo.r_symbolnum = addrArea+1; // Section numbers start at 1 relInfo.r_pcrel = 0; #if (SIZEOF_VOIDP == 8) relInfo.r_length = 3; // 8 bytes relInfo.r_type = X86_64_RELOC_UNSIGNED; #else relInfo.r_length = 2; // 4 bytes relInfo.r_type = GENERIC_RELOC_VANILLA; #endif relInfo.r_extern = 0; // r_symbolnum is a section number. It should be 1 if we make the IO area a common. fwrite(&relInfo, sizeof(relInfo), 1, exportFile); relocationCount++; return PolyWord::FromUnsigned(offset); }
AbelianGroup MalcevSet::mapToQuotient(int k) const { if( ! isBasis ) error("MalcevSet::mapToQuotient: the set must be full"); // The generators of the quotient are basic commutators of weight k. const BasicCommutators& bc = theCollector.commutators(); int numGen = bc.numberOfWeight(k); int firstGen = bc.theFirstOfWeight(k) - 1; // The relators are Malcev basis words of weight k SetOf<Word> relsForAbelian; QuickAssociationsIterator< Generator, PolyWord > iter(theSet); for( ; ! iter.done(); iter.next() ) { // take a word from Malcev basis PolyWord pw = iter.value(); Letter first = pw.firstLetter(); if( ord(first.gen) != firstGen ) continue; //Ok, this is a word from the quotient. Abelianize it. ConstPolyWordIterator iter( pw ); Word w; for(iter.startFromLeft(); ! iter.done(); iter.stepRight() ) { Letter s = iter.thisLetter(); int newgen = ord(s.gen) - firstGen; if( newgen > numGen ) break; s.gen = Generator( newgen ); w *= Word(s); } relsForAbelian.adjoinElement(w); } // make the abelian quotient AbelianGroup abel( FPGroup(numGen, relsForAbelian) ); abel.computeCyclicDecomposition(); return abel; }
void print_string(PolyWord s) { if (IS_INT(s)) putc((char)UNTAGGED(s), stdout); else { PolyStringObject * str = (PolyStringObject *)s.AsObjPtr(); fwrite(str->chars, 1, str->length, stdout); } }
void MalcevSet::makeNormalClosure() { if(isNormal == yes) return; const BasicCommutators& BC = theCollector.commutators(); int nilClass = BC.nilpotencyClass(); int upper_i = BC.theFirstOfWeight(nilClass); for(int i = 1; i <= upper_i; i++) { if( ! theSet.bound( Generator(i) ) ) continue; PolyWord Wi = theSet.valueOf( Generator(i) ); PolyWord WiInv = Wi.inverse(); // trying generators of the group for(int j = 1; j <= BC.numberOfGenerators(); j++) { Generator g(j); PolyWord comm = collect( Wi * Letter(g,-1) * WiInv * Letter(g,1) ); addWord(comm); } // trying basis elements int upper_j = BC.theFirstOfWeight(nilClass - BC.weightOf(i) + 1); if(upper_j > i) upper_j = i; for(int j = 1; j < upper_j; j++) { if( ! theSet.bound( Generator(j) ) ) continue; PolyWord Wj = theSet.valueOf( Generator(j) ); PolyWord comm = collect( Wi * Wj * WiInv * Wj.inverse() ); addWord(comm); } } isBasis = true; isNormal = yes; }
// This deals with weak references within the run-time system. void MTGCCheckWeakRef::ScanRuntimeAddress(PolyObject **pt, RtsStrength weak) { /* If the object has not been marked and this is only a weak reference */ /* then the pointer is set to zero. This allows streams or windows */ /* to be closed if there is no other reference to them. */ PolyObject *val = *pt; PolyWord w = val; if (weak == STRENGTH_STRONG) return; LocalMemSpace *space = gMem.LocalSpaceForAddress(w.AsStackAddr()); if (space == 0) return; // Not in local area // If it hasn't been marked set it to zero. if (! space->bitmap.TestBit(space->wordNo(w.AsStackAddr()))) *pt = 0; }
void MalcevSet::checkMembership(PolyWord& remainder) const { while( ! remainder.isEmpty() && theSet.bound( leader(remainder) ) ) { PolyWord curWord = theSet.valueOf( leader(remainder) ); if( absPower(remainder) % absPower(curWord) != 0 ) break; // The rest can be reduced. Do it. reduceWords( curWord, remainder ); } }
static PHANDLETAB get_handle(PolyWord token, HANDENTRYTYPE heType) { StreamToken *handle_token = (StreamToken*)token.AsObjPtr(); POLYUNSIGNED handle_no = handle_token->streamNo; if (handle_no >= maxHandleTab || handleTable[handle_no].token != handle_token || handleTable[handle_no].entryType != heType) return 0; return &handleTable[handle_no]; }
// These functions are used in the interpreter. They are generally replaced by // hand-coded versions in the assembly code section. static int string_test(PolyWord x, PolyWord y) /* Returns -1, 0, +1 if the first string is less, equal to or greater than the second. These are addresses of the strings because calling fix_persistent_address could result in a garbage-collection which could move the other string. */ { POLYUNSIGNED i; PolyStringObject *xs, *ys; /* Deal with single characters. */ if (IS_INT(x)) { s_test_x.length = 1; s_test_x.chars[0] = (char)UNTAGGED(x); xs = &s_test_x; } else xs = (PolyStringObject*)x.AsObjPtr(); if (IS_INT(y)) { s_test_y.length = 1; s_test_y.chars[0] = (char)UNTAGGED(y); ys = &s_test_y; } else ys = (PolyStringObject*)y.AsObjPtr(); /* Now do the comparison. */ for(i = 0; i < xs->length && i < ys->length; i++) { if (xs->chars[i] != ys->chars[i]) return xs->chars[i] < ys->chars[i] ? -1 : 1; } /* They must be equal or one must be a leading substring of the other. */ if (i < xs->length) return 1; /* y must be the substring. */ else if (i < ys->length) return -1; /* x must be the substring */ else return 0; /* They must be equal. */ }
// Create a relocation entry for an address at a given location. PolyWord SaveStateExport::createRelocation(PolyWord p, void *relocAddr) { RelocationEntry reloc; // Set the offset within the section we're scanning. setRelocationAddress(relocAddr, &reloc.relocAddress); void *addr = p.AsAddress(); unsigned addrArea = findArea(addr); reloc.targetAddress = (char*)addr - (char*)memTable[addrArea].mtAddr; reloc.targetSegment = (unsigned)memTable[addrArea].mtIndex; reloc.relKind = PROCESS_RELOC_DIRECT; fwrite(&reloc, sizeof(reloc), 1, exportFile); relocationCount++; return p; // Don't change the contents }
bool MalcevSet::reduceWords(PolyWord& pw1, PolyWord& pw2) const { if(pw1.isEmpty() || pw2.isEmpty()) error("MalcevSet::reduceWords: empty argument"); bool firstChanged = false; int power1 = absPower(pw1); int power2 = absPower(pw2); // make both PolyWords to be of distinct signs if( sign(pw1) ^ sign(pw2) == 0) // if they have the same sign pw2 = pw2.inverse(); // in fact, this is Euclid algorithm for finding GCD do { if( power1 > power2 ) { // swapping two words PolyWord tmp = pw1; pw1 = pw2; pw2 = tmp; int t = power1; power1 = power2; power2 = t; firstChanged = true; } power2 -= power1; pw2 = theCollector.multiply(pw1, pw2); } while(power2 != 0); return firstChanged; }
// This gets called in two circumstances. It may be called for the roots // in which case the stack will be empty and we want to process it completely // or it is called for a constant address in which case it will have been // called from RecursiveScan::ScanAddressesInObject and that can process // any addresses. PolyObject *RecursiveScan::ScanObjectAddress(PolyObject *obj) { PolyWord pWord = obj; // Test to see if this needs to be scanned. // It may update the word. bool test = TestForScan(&pWord); obj = pWord.AsObjPtr(); if (test) { MarkAsScanning(obj); if (obj->IsByteObject()) Completed(obj); // Don't need to put it on the stack // 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. else if (StackIsEmpty()) RecursiveScan::ScanAddressesInObject(obj, obj->LengthWord()); else PushToStack(obj); } return obj; }
POLYUNSIGNED Poly_string_to_C(PolyWord ps, WCHAR *buff, POLYUNSIGNED bufflen) { if (IS_INT(ps)) { buff[0] = (WCHAR)(UNTAGGED(ps)); buff[1] = 0; return(1); } PolyStringObject *str = (PolyStringObject *)ps.AsObjPtr(); POLYUNSIGNED chars = str->length >= bufflen ? bufflen-1 : str->length; for (POLYUNSIGNED i = 0; i < chars; i++) buff[i] = str->chars[i]; buff[chars] = 0; return chars; } /* Poly_string_to_C */