diff --git a/modules/Kernel/ProcessStack.st b/modules/Kernel/ProcessStack.st index bae0637e..9f64dca4 100644 --- a/modules/Kernel/ProcessStack.st +++ b/modules/Kernel/ProcessStack.st @@ -8,7 +8,8 @@ Class { #superclass : #Object, #instVars : [ 'process', - 'sp' + 'sp', + 'bp' ], #category : #Kernel } diff --git a/runtime/cpp/Allocator/AllocationZone.cpp b/runtime/cpp/Allocator/AllocationZone.cpp new file mode 100644 index 00000000..fde08e8d --- /dev/null +++ b/runtime/cpp/Allocator/AllocationZone.cpp @@ -0,0 +1,240 @@ + +#include "Egg.h" +#include "Memory.h" +#include "AllocationZone.h" +#include "GCSpace.h" +#include "GCHeap.h" +#include "HeapObject.h" + +using namespace Egg; + +AllocationZone::AllocationZone(GCHeap* heap, uintptr_t base, uintptr_t limit) : + _heap(heap), + _base(base), + _nextFree(base), + _limit(limit) +{ + this->build(); +} + +uintptr_t AllocationZone::allocate_(uintptr_t size) +{ + auto oop = this->allocateIfPossibleBumping_(size); + if (!oop) + error_("Out of memory in old space"); + + return oop; +} + +uintptr_t AllocationZone::allocateIfPossibleCurrent_(uintptr_t size) { + //printf("allocing: %d bytes in current\n", size); + return _current->allocateIfPossible_(size); +} + +uintptr_t AllocationZone::allocateIfPossibleBumping_(uintptr_t size) +{ + //printf("allocing: %zu bytes bumping\n", size); + + auto oop = _current->allocateIfPossible_(size); + if (oop) + return oop; + this->bumpSpace(); + return _current->allocateIfPossible_(size); +} + +void AllocationZone::assureFree_(uintptr_t size) +{ + if (_current->softAvailable() < size) this->bumpSpace(); +} + +uintptr_t AllocationZone::availableBytes() +{ + return this->usableBytes() - this->committedBytes(); +} + +void AllocationZone::build() +{ + auto max = this->indexOfPointer_(_limit - 1); + _occupancy.resize(max, 0); + this->bumpSpace(); + this->bumpSpace(); +} + +GCSpace* AllocationZone::bumpSpace() +{ + _current = _next; + //if (_current) // uninitialized on first bump + // printf("bumped space - new limits: 0x%p - 0x%p\n", (void*)_current->base(), (void*)_current->reservedLimit()); + if (_emptySpaces.empty()) this->createEmptySpace(); + + _next = _emptySpaces.back(); + _emptySpaces.pop_back(); + _next->commitMemory_(_next->reservedSize()); + _next->_softLimit = _next->_committedLimit; + this->markAsFull_(_next); + return _next; +} + +uintptr_t AllocationZone::committedBytes() +{ + auto sum = 0; + for (auto space : _spaces) + sum = sum + space->committedSize(); + return sum; +} + +GCSpace *AllocationZone::createEmptySpace() +{ + if (_nextFree == _limit) { + printf("_nextFree: %" PRIxPTR ". _limit: %" PRIxPTR "\n", _nextFree, _limit); + error_("Out of space in old zone"); + } + auto start = _nextFree; + auto end = _nextFree + SPACE_SIZE; + _nextFree = end + SPACE_SIZE; + auto s = GCSpace::allocatedAt_limit_(start, end); + s->_name ="Old"; + //printf("adding old space nÂș %" PRIdPTR "\n", _spaces.size()); + _spaces.push_back(s); + _emptySpaces.push_back(s); + _heap->addSpace_(s); + + //printf("bumping old zone limit: _nextFree: %" PRIxPTR ". _limit: %" PRIxPTR "\n", _nextFree, _limit); + + return s; +} + +GCSpace *AllocationZone::currentSpace() +{ + return _current; +} + +uintptr_t AllocationZone::indexOfPointer_(uintptr_t address) +{ + return ((address - _base) >> SPACE_SIZE_SHIFT) / 2 + 1; // half of the space are forwarders +} + +uintptr_t AllocationZone::indexOfSpace_(GCSpace *space) +{ + return this->indexOfPointer_(space->base()); +} + +void AllocationZone::markAsFull_(GCSpace *space) +{ + /* + We mark spaces as fully occupied so that + the GC doesn't see them as ready to be freed. + */ + auto index = this->indexOfSpace_(space); + _occupancy[index - 1] = SPACE_SIZE; +} + +uintptr_t AllocationZone::occupiedBytes() +{ + auto sum = 0; + for (size_t i = 0; i < _spaces.size(); i++) + if (!_spaces[i]->isFree()) + sum = sum + _occupancy[i]; + return sum; +} + +void AllocationZone::recycleSpace_(GCSpace *space) +{ + _emptySpaces.push_back(space); +} + +uintptr_t AllocationZone::regionCount() +{ + return this->indexOfPointer_(_nextFree - 1); +} + +intptr_t AllocationZone::regionIndexOf_(HeapObject *object) +{ + auto pointer = (uintptr_t)object; + if (pointer < _base) return -1; + if (pointer >= _limit) return -1; + return this->indexOfPointer_(pointer); +} + +void AllocationZone::releaseEvacuated_(std::vector *evacuated) +{ + for (uint32_t i = 1; i < _spaces.size(); i++) + { + auto space = _spaces[i-1]; + auto used = _occupancy[i-1]; + bool recycle = (*evacuated)[i-1] || (used == 0 && space->committedSize() > 0); + if (recycle) + { + auto base = space->_base; + auto size = space->reservedSize(); + space->_next = base; + space->_committedLimit = base; + space->_softLimit = base; + DecommitMemory(base, size * 2); + this->recycleSpace_(space); + } + } +} + +void AllocationZone::relocate_(intptr_t anInteger) +{ + _base = _base + anInteger; + _nextFree = _nextFree + anInteger; + _limit = _limit + anInteger; +} + +uintptr_t AllocationZone::reservedBytes() +{ + return _limit - _base; +} + +void AllocationZone::resetOccupancy() +{ + std::fill(_occupancy.begin(), _occupancy.end(), 0); + this->markAsFull_(_current); + this->markAsFull_(_next); +} + +HeapObject *AllocationZone::shallowCopyCommiting_(HeapObject *object) +{ + auto copy = _current->shallowCopyCommitting_(object); + if (copy) + return copy; + + auto size = object->bodySizeInBytes(); + if (size > GCHeap::LargeThreshold) + { + auto space = _heap->createLargeSpace_(size); + return space->shallowCopyCommitting_(object); + } + this->bumpSpace(); + return _current->shallowCopyCommitting_(object); +} + +uintptr_t AllocationZone::size() +{ + return _limit - _base; +} + +void AllocationZone::updateRegionOccupancy_(HeapObject *object) +{ + auto index = this->regionIndexOf_(object); + if (index < 0) return; + + auto bytes = _occupancy[index - 1]; + _occupancy[index-1] = bytes + object->bodySizeInBytes(); +} + +uintptr_t AllocationZone::usableBytes() +{ + // Half of each reserved area is held for evacuation purposes only and not directly usable. + return this->reservedBytes() / 2; +} + +uintptr_t AllocationZone::usedBytes() +{ + auto count = 0; + for (auto s : _spaces) + count = count + s->usedSize(); + return count; +} diff --git a/runtime/cpp/Allocator/AllocationZone.h b/runtime/cpp/Allocator/AllocationZone.h new file mode 100644 index 00000000..7afb99c4 --- /dev/null +++ b/runtime/cpp/Allocator/AllocationZone.h @@ -0,0 +1,64 @@ +#ifndef _ALLOCATION_ZONE_H_ +#define _ALLOCATION_ZONE_H_ + +#include +#include "Egg.h" + +namespace Egg { + +class HeapObject; +class GCSpace; +class GCHeap; + +class AllocationZone { + GCHeap *_heap; + uintptr_t _base, _nextFree, _limit; + GCSpace *_current, *_next; + std::vector _spaces; + std::vector _emptySpaces; + std::vector _occupancy; + +public: + AllocationZone(GCHeap* heap, uintptr_t base, uintptr_t limit); + + // 256 kb spaces + const uintptr_t SPACE_SIZE_SHIFT = 18; + const uintptr_t SPACE_SIZE = 1 << SPACE_SIZE_SHIFT; + + uintptr_t base() { return _base; } + uintptr_t limit() { return _limit; } + std::vector* occupancy() { return &_occupancy; } + uintptr_t regionSize() { return SPACE_SIZE; } + std::vector& spaces() { return _spaces; } + + uintptr_t allocate_(uintptr_t size); + uintptr_t allocateIfPossibleCurrent_(uintptr_t size); + uintptr_t allocateIfPossibleBumping_(uintptr_t size); + void assureFree_(uintptr_t size); + uintptr_t availableBytes(); + void build(); + GCSpace* bumpSpace(); + uintptr_t committedBytes(); + GCSpace* createEmptySpace(); + GCSpace* currentSpace(); + uintptr_t indexOfPointer_(uintptr_t address); + uintptr_t indexOfSpace_(GCSpace *space); + void markAsFull_(GCSpace *space); + uintptr_t occupiedBytes(); + void recycleSpace_(GCSpace *space); + uintptr_t regionCount(); + intptr_t regionIndexOf_(HeapObject *object); + void releaseEvacuated_(std::vector *evacuated); + void relocate_(intptr_t anInteger); + uintptr_t reservedBytes(); + void resetOccupancy(); + HeapObject* shallowCopyCommiting_(HeapObject *object); + uintptr_t size(); + void updateRegionOccupancy_(HeapObject *object); + uintptr_t usableBytes(); + uintptr_t usedBytes(); +}; + +} // namespace Egg + +#endif // ~ _ALLOCATION_ZONE_H_ ~ diff --git a/runtime/cpp/Allocator/G1GC.cpp b/runtime/cpp/Allocator/G1GC.cpp new file mode 100644 index 00000000..73977128 --- /dev/null +++ b/runtime/cpp/Allocator/G1GC.cpp @@ -0,0 +1,425 @@ +#include + +#include "G1GC.h" + +#include + +#include "AllocationZone.h" +#include "KnownObjects.h" +#include "KnownConstants.h" + +using namespace Egg; + +G1GC::G1GC(Runtime *runtime, AllocationZone *oldZone, GCHeap *heap) : + GarbageCollector(runtime, oldZone, heap) +{ + _countdown = 1 * MB; + _forwarderOffset = _oldZone->regionSize(); + +} + +HeapObject *G1GC::copyOf_(HeapObject *anObject) +{ + auto forwarder = ((uintptr_t)anObject) + _forwarderOffset; + //printf("fetching forwarder of %#" PRIxPTR " -> %#" PRIxPTR "(orig: %s, copy: %s)\n", anObject, *(HeapObject**)forwarder, anObject->printString().c_str(), (*(HeapObject**)forwarder)->printString().c_str()); + return (*(HeapObject**)forwarder); +} + +void G1GC::determineEvacuatedSpaces() { + auto threshold = uintptr_t(_forwarderOffset * 0.8); // if space uses less than this, it is a candidate for evacuation + auto occupancy = _oldZone->occupancy(); // the occupancy was computed in marking stage of prev GC pass + auto max = _oldZone->regionCount(); + auto headroom = _oldZone->availableBytes() * 9 / 10; // avail space for evacuating + + _evacuated.resize(occupancy->size(), false); // initialize the list with all false elements + + auto current = _oldZone->currentSpace(); + for (uint32_t i = 1; i <= max; i++) + { + auto spaceOccup = (*occupancy)[i-1]; + auto space = _oldZone->spaces()[i-1]; + //if (space) printf("space size: %" PRIdPTR ", used: %" PRIdPTR "\n", space->reservedSize(), spaceOccup); + if (0 < spaceOccup && spaceOccup < threshold) + { + //auto space = _oldZone->spaces()[i-1]; + auto used = space->usedSize(); + if (space != current && headroom > used) + { + headroom = headroom - used; + auto size = space->reservedSize(); + CommitMemory(space->_base + size, size); + _evacuated[i-1] = true; + /* + printf("evacuating space: %u (%.1f%% used) - zone: 0x%" PRIxPTR " - 0x%" PRIxPTR "\n", + i - 1, + 100.0 * (float)spaceOccup / (float)_forwarderOffset, + space->base(), + space->reservedLimit()); + */ + } + } + } + + _heap->oldZone()->resetOccupancy(); // will be recomputed in this GC pass, for next pass +} + +HeapObject *G1GC::evacuate_(HeapObject *anObject) +{ + auto copy = _oldZone->shallowCopyCommiting_(anObject); + if (anObject->isRemembered()) + copy->beRemembered(); + + auto forwarder = ((uintptr_t)anObject) + _forwarderOffset; + (*(HeapObject**)forwarder)= copy; + //printf("object %#" PRIxPTR " was forwarded to %#" PRIxPTR "(orig: %s, copy: %s)\n", anObject, copy, anObject->printString().c_str(), copy->printString().c_str()); + + return copy; +} + +bool G1GC::hasToEvacuate_(HeapObject *anObject) +{ + auto index = _oldZone->regionIndexOf_(anObject); + return index > 0 && _evacuated[index-1]; +} + +void G1GC::initializeLocals() +{ + this->determineEvacuatedSpaces(); + auto current = 0; + auto evac = 0; + + for (auto space : _oldZone->spaces()) + { + current += space->usedSize(); + } + + for (auto i = 0; i < _evacuated.size(); i++) + { + if (_evacuated[i]) + evac+= _oldZone->spaces()[i]->reservedSize(); + } + + //printf("going to do GC - current old heap size: %d kb, going to free aprox %x\n", current / 1024, evac); + + _stack.reserve(64 * KB); + _largeSize = _heap->_largeSpaces.size(); + GarbageCollector::initializeLocals(); +} + +void G1GC::releaseLocals() +{ + _oldZone->releaseEvacuated_(&_evacuated); + _evacuated.clear(); + this->resetCountdown(); + this->sweepLargeSpaces(); + _stack.clear(); + _scanned = nullptr; + GarbageCollector::releaseLocals(); +} + +void G1GC::resetCountdown() +{ + /* + * Try to set an amount that is not too large, not too small. Full GC + * allocation countdown has to be relative to heap size (bigger heaps + * result in bigger countdowns). + * Rules: + * - aims for 25% of used size minus what is known to be garbage + * - if near exhaustion of mem (available is small), then make countdown smaller + */ + + intptr_t garbage = _oldZone->committedBytes() - _oldZone->occupiedBytes(); + intptr_t desired = intptr_t(_oldZone->usedBytes() * 0.25) - garbage; + intptr_t available = _oldZone->availableBytes() * 9 / 10; + + // set countdown to not less that 3*region size, not more than old zone remaining free space + _countdown = std::max((intptr_t)_oldZone->regionSize()*3, std::min(desired, available)); + + /* + GenGC unimplemented + intptr_t garbage = _oldZone->committedBytes() - _oldZone->occupiedBytes(); + intptr_t bytes = intptr_t(_oldZone->usedBytes() * 0.25) - garbage; + auto available = _oldZone->availableBytes() * 9 / 10; + auto young = _heap->eden()->reservedSize() + _heap->from()->reservedSize(); + _countdown = std::min(std::max(25 * MB, bytes), available - young); + */ +} + + +void G1GC::updateWeak_at_(HeapObject *weakContainer, uintptr_t anInteger) +{ + auto object = weakContainer->slotAt_(anInteger); + if (object->isSmallInteger()) + return; + + auto hobject = object->asHeapObject(); + + if (!hobject->hasBeenSeen()) + { + weakContainer->slotAt_(anInteger) = (Object*)_tombstone; + } + if (!this->hasToEvacuate_(hobject)) + return; + + auto updated = this->copyOf_(hobject); + weakContainer->slotAt_(anInteger) = (Object*)updated; +} + +bool G1GC::checkReachablePropertyOf_(HeapObject *ephemeron) +{ + auto key = ephemeron->slotAt_(1); + return key->isSmallInteger() || key->asHeapObject()->hasBeenSeen(); +} + + +void G1GC::doCollect() +{ + GarbageCollector::doCollect(); + // unimplemented GenGC + // this->purgeRememberedSet(); // RE-ENABLE AFTER PLUGGING BACK GENGC +} + +void G1GC::followClosure() +{ + this->followGCedRefs(); + while (!_stack.empty() || !_stacks->empty()) + { + this->followObjects(); + this->scanStacks(); + } +} + +void G1GC::followGCedRefs() +{ + _runtime->gcedRefsDo_([this](GCedRef *ref) { + this->queue_from_to_((HeapObject*)ref->getRaw(), 1, 1); + }); +} + + +template +T pop_and_return(std::vector& vec) { + T value = std::move(vec.back()); + vec.pop_back(); + return value; +} + +void G1GC::followObjects() +{ + while (!_stack.empty()) + { + _limit = (uint32_t)pop_and_return(_stack); + _index = (uint32_t)pop_and_return(_stack); + _scanned = (HeapObject*)pop_and_return(_stack); + while (_index <= _limit) + { + if (_index == 0) + this->scanBehavior(); + else + this->scanSlot(); + } + } +} + +uintptr_t G1GC::initialContainerCapacity() +{ + return this->workSpaceSize() / 1000; +} + +/* +void G1GC::purgeRememberedSet() +{ + auto rs = _memory->rememberedSet(); + auto kept = 0; + for (uint32_t i = 1; i <= rs->size(); i++) + { + auto object = rs[i-1]; + if (object->hasBeenSeen()) + { + if (this->hasToEvacuate_(object)) + object = this->copyOf_(object); + kept = kept + 1; + rs[kept] = object; + } + } + rs->size_(kept); + +} +*/ + +void G1GC::queue_from_to_(HeapObject *anObject, uintptr_t start, uintptr_t end) +{ + _stack.push_back((uintptr_t)anObject); + _stack.push_back(start); + _stack.push_back(end); +} + +void G1GC::queueCurrent() +{ + _stack.push_back((uintptr_t)_scanned); + _stack.push_back(_index + 1); + _stack.push_back(_limit); +} + +void G1GC::scan_from_to_(HeapObject *anObject, uintptr_t start, uintptr_t end) +{ + _stack.push_back((uintptr_t)anObject); + _stack.push_back(start); + _stack.push_back(end); +} + +void G1GC::scanBehavior() +{ + auto slot = _scanned->behavior(); +// printf("scanning behavior of %#" PRIxPTR " (%s)", (uintptr_t)_scanned, _scanned->printString().c_str()); +// fflush(stdout); +// printf(", which is %#" PRIxPTR "( %s)\n", (uintptr_t)slot, slot->printString().c_str()); + if (((Object*)slot)->isSmallInteger()) + { + _index = _index + 1; + return; + } + auto evacuate = this->hasToEvacuate_(slot); + if (slot->hasBeenSeen()) + { + if (evacuate) + { + slot = this->copyOf_(slot); + _scanned->behavior(slot); + } + _index = _index + 1; + return; + } + slot->beSeen(); + if (evacuate) + { + slot = this->evacuate_(slot); + _scanned->behavior(slot); + } + else + { + this->updateRegionOccupancy_(slot); + } + + if (slot->isSpecial()) + this->rememberSpecial_(slot); + + if (_index < _limit) + this->queueCurrent(); + + _index = 0; + _limit = slot->strongPointersSize(); + _scanned = slot; +} + +void G1GC::scanSlot() +{ + // scanned can be a heap object or a chunk of stack frame, we cannot use slotAt_ + auto slot = ((Object**)_scanned)[_index-1]; + auto stack = debugRuntime->_evaluator->context()->stack(); +// if ((uintptr_t)_scanned < (uintptr_t)&stack[0] || (uintptr_t)_scanned > (uintptr_t)&stack[0xFFFF]) +// printf("scanning slot %d of %#" PRIxPTR " (%s)", _index, (uintptr_t)_scanned, _scanned->printString().c_str()); +// else +// printf("scanning stack slot of frame %#" PRIxPTR " at %d" , (uintptr_t)_scanned, _index); + +// fflush(stdout); +// printf(" which is %#" PRIxPTR " (%s)\n", (uintptr_t)slot, slot->printString().c_str()); + + if (slot->isSmallInteger()) + { + _index = _index + 1; + return; + } + + auto hslot = slot->asHeapObject(); + + auto evacuate = this->hasToEvacuate_(hslot); + if (hslot->hasBeenSeen()) + { + if (evacuate) + { + hslot = this->copyOf_(hslot); + ((Object**)_scanned)[_index-1] = (Object*)hslot; + } + _index = _index + 1; + return; + } + + hslot->beSeen(); + + if (evacuate) + { + hslot = this->evacuate_(hslot); + ((Object**)_scanned)[_index-1] = (Object*)hslot; + } + else + { + this->updateRegionOccupancy_(hslot); + } + + if (hslot->isSpecial()) + this->rememberSpecial_(hslot); + + if (_index < _limit) + this->queueCurrent(); + + _index = 0; + _limit = hslot->strongPointersSize(); + _scanned = hslot; +} + +void G1GC::scanThreadLocalStorage_(HeapObject *thread) +{ + // unimplemented yet +} + +void G1GC::scanThreadLocalStorage_at_(HeapObject *thread, uint32_t i) +{ + // uimplemented yet +} + +void G1GC::scanTopSlot_(HeapObject *processStack) +{ + error("G1GC::scanTopSlot_ not implemented"); + //this->queue_from_to_(_stackWrapper->sp->asObject(), 1, 1); +} + +void G1GC::sweepLargeSpaces() +{ + auto used = 0; + auto &large = _heap->_largeSpaces; + auto ®istered = _heap->_spaces; + for (int i = 1; i <= _largeSize; i++) + { + GCSpace* s = large[i-1]; + if (s->firstObject()->hasBeenSeen()) + { + used = used + 1; + large[used] = s; + } + else + { + FreeMemory(s->base(), s->reservedSize()); + registered.erase(std::remove(registered.begin(), registered.end(), s), registered.end()); + } + } + + for (uint32_t i = _largeSize + 1; i <= large.size(); i++) + { + auto s = large[i-1]; + used = used + 1; + large[used-1] = s; + } + + for (uint32_t i = used + 1; i <= large.size(); i++) + { + large[i-1] = nullptr; + } + + large.resize(used); +} + +void G1GC::updateRegionOccupancy_(HeapObject *object) +{ + _heap->oldZone()->updateRegionOccupancy_(object); +} diff --git a/runtime/cpp/Allocator/G1GC.h b/runtime/cpp/Allocator/G1GC.h new file mode 100644 index 00000000..b23d0342 --- /dev/null +++ b/runtime/cpp/Allocator/G1GC.h @@ -0,0 +1,82 @@ +#ifndef _G1GC_H_ +#define _G1GC_H_ + +#include +#include "../HeapObject.h" +#include "GarbageCollector.h" + +/** + * This class implements a garbage-first inspired GC for the old heap. + * The main idea is that the algorithm doesn't compact all old spaces + * in the same pass, but only those which are most fragmented. + * The old heap is managed by a allocation zone object, which splits + * memory fixed size GCSpaces (larger objects that wouldn't fit are + * created in another place, outside the allocation zone). + * + * When the system decides it is time to reclaim space, the GC uses + * the fragmentation stats from last G1 pass to decide which spaces + * it will evacuate, according to an occupation threshold. + * + * This allows to do forwarding during marking phase: when the tracer + * starts, it knows all spaces that have to be evacuated. When each + * object is reached for the first time in an evacuated space, it is + * copyied to a different space using bump allocation and a forwarder + * is left. This way, the GC only does one pass through the heap. + * + * After GC pass finishes, some 'floating garbage' will still be left + * because not all spaces are compacted. However, that space should be + * a small proportion of the used memory. +*/ + +namespace Egg { + +class HeapObject; +class AllocationZone; + +class G1GC : public GarbageCollector { +public: + std::vector _stack; + uint32_t _limit, _index; + HeapObject* _scanned; + uint32_t _largeSize; + std::vector _evacuated; + uintptr_t _forwarderOffset; + intptr_t _countdown; + G1GC(Runtime *runtime, AllocationZone *oldZone, GCHeap *memory); + virtual ~G1GC() = default; + + HeapObject* copyOf_(HeapObject *anObject); + HeapObject* evacuate_(HeapObject *anObject); + bool hasReachedCountdown() { return _countdown <= 0; } + bool hasToEvacuate_(HeapObject *anObject); + virtual void initializeLocals(); + virtual void releaseLocals(); + void resetCountdown(); + void determineEvacuatedSpaces(); + void tenured_(intptr_t anInteger) { _countdown = _countdown - anInteger; } + void updateWeak_at_(HeapObject *weakContainer, uintptr_t anInteger); + + bool checkReachablePropertyOf_(HeapObject *ephemeron); + void doCollect(); + void followClosure(); + void followGCedRefs(); + void followObjects(); + uintptr_t initialContainerCapacity(); + //void purgeRememberedSet(); // RE-ENABLE AFTER PLUGGING BACK GENGC + void queue_from_to_(HeapObject *anObject, uintptr_t start, uintptr_t end); + void queueCurrent(); + void scan_from_to_(HeapObject *anObject, uintptr_t start, uintptr_t end); + void scanBehavior(); + void scanSlot(); + void scanThreadLocalStorage_(HeapObject *thread); + void scanThreadLocalStorage_at_(HeapObject *thread, uint32_t i); + void scanTopSlot_(HeapObject *processStack); + void sweepLargeSpaces(); + void updateRegionOccupancy_(HeapObject *object); + uintptr_t workSpaceSize() { return 20 * MB; } + +}; + +} // namespace Egg + +#endif // ~ _G1GC_H_ ~ diff --git a/runtime/cpp/Allocator/GCHeap.cpp b/runtime/cpp/Allocator/GCHeap.cpp new file mode 100644 index 00000000..d286e518 --- /dev/null +++ b/runtime/cpp/Allocator/GCHeap.cpp @@ -0,0 +1,210 @@ +#include + +#include "GCHeap.h" +#include "GCSpace.h" +#include "KnownObjects.h" +#include "AllocationZone.h" +#include "G1GC.h" + +using namespace Egg; + +GCHeap::GCHeap(Runtime *runtime) : _runtime(runtime), _gcNeeded(false) +{ + _atGCSafepoint = false; // defaults to false, only particular points enable GC + _eden = this->addNewSpaceSized_(16*MB); + _eden->_name = "Eden"; + + auto size = 128*MB; + auto address = ReserveMemory(0x100000000,size); + _oldZone = new AllocationZone(this, address, address + size); + _fullGC = new G1GC(_runtime, _oldZone, this); +} + +GCHeap::~GCHeap() +{ + for (auto &space : _spaces) + delete space; +} + +GCSpace *GCHeap::addSpace_(GCSpace *space) +{ + _spaces.push_back(space); + return space; +} + +GCSpace *GCHeap::addNewSpaceSized_(int size) { + auto space = new GCSpace(size); + return this->addSpace_(space); +} + +GCSpace *Egg::GCHeap::createLargeSpace_(uintptr_t size) +{ + auto address = ReserveMemory(0, size); + if (!address) + error_(std::string("Not enough memory to allocate ") + std::to_string(size) + "bytes"); + + auto limit = address + size; + auto space = GCSpace::allocatedAt_limit_(address, limit); + + space->_name = "Large"; + space->_committedLimit = limit; + space->_softLimit = limit; + this->addSpace_(space); + _largeSpaces.push_back(space); + return space; +} + +/* +uintptr_t GCHeap::allocate_(uint32_t size) { + // GenGC unimplemented yet + auto result = _eden->allocateIfPossible_(size); + if (result) + return result; + + if (size > LargeThreshold) + return this->allocateLarge_(size); + + if (atGCSafepoint()) + this->collectIfTime(); + + return this->allocateCommitting_(size); +} +*/ + +uintptr_t GCHeap::allocate_(uint32_t size) { + auto result = _oldZone->allocateIfPossibleCurrent_(size); + if (result) { + _fullGC->tenured_(size); + return result; + } + if (size > LargeThreshold) + return this->allocateLarge_(size); + + if (this->isAtGCSafepoint()) + this->collectIfTime(); + else + requestGC(); + + _fullGC->tenured_(size); + + return _oldZone->allocateIfPossibleBumping_(size); +} + +uintptr_t GCHeap::allocateLarge_(uint32_t size) { + auto space = this->addNewSpaceSized_(size); + return space->allocateIfPossible_(size); +} + + +uintptr_t GCHeap::allocateCommitting_(uint32_t size) +{ + auto result = _eden->allocateIfPossible_(size); + if (result) return result; + + // should allocate committing in old here + + error("Out of space. Fix me: add an old zone"); + return (uintptr_t)nullptr; +} + +HeapObject* GCHeap::allocateSlots_(uint32_t size) { + bool small = size <= HeapObject::MAX_SMALL_SIZE; + auto headerSize = small ? 8 : 16; + auto totalSize = headerSize + size * sizeof(uintptr_t); + auto buffer = this->allocate_(totalSize); + std::memset((void*)buffer, 0, headerSize); + HeapObject *result; + + if (small) + { + result = ((HeapObject::SmallHeader*)buffer)->object(); + result->beSmall(); + result->smallSize((uint8_t)size); + } + else + { + result = ((HeapObject::LargeHeader*)buffer)->object(); + result->beLarge(); + result->largeSize(size); + } + uintptr_t *end = ((uintptr_t*)result) + size; + + std::fill((uintptr_t*)result, (uintptr_t*)end, (uintptr_t)KnownObjects::nil); + + return result; +} + +HeapObject* GCHeap::allocateBytes_(uint32_t size) +{ + bool small = size <= HeapObject::MAX_SMALL_SIZE; + auto headerSize = small ? 8 : 16; + auto totalSize = headerSize + align(size, sizeof(uintptr_t)); + auto buffer = this->allocate_(totalSize); + std::memset((void*)buffer, 0, totalSize); + HeapObject *result; + + if (small) + { + result = ((HeapObject::SmallHeader*)buffer)->object(); + result->beSmall(); + result->smallSize((uint8_t)size); + } + else + { + result = ((HeapObject::LargeHeader*)buffer)->object(); + result->beLarge(); + result->largeSize(size); + } + + result->beBytes(); + result->beArrayed(); + + return result; +} + +/* GenGC unimplemented yet + void GCHeap::collectIfTime() +{ + _eden->commitMemory_(256*KB); + bool success = _eden->increaseSoftLimit_(256 * KB); + if (success) return; + + this->collectYoung(); + + //if (fullCollector->reachedCountdown()) + // this->collectOld(); +} +*/ + +bool GCHeap::isAtGCSafepoint() +{ + return _atGCSafepoint; +} + +void GCHeap::collectIfTime() +{ + if (_gcNeeded || _fullGC->hasReachedCountdown()) + this->collectOld(); +} + +void GCHeap::collectYoung() +{ + error("Fixme: Collect young hasn't been implemented yet"); +} + +void GCHeap::collectOld() +{ + //warning("GCHeap::collectOld()\n"); + for (auto space : _spaces) { + space->unmarkAll(); + } + + //_runtime->checkCache(); + _fullGC->collect(); + //_runtime->checkCache(); + // this->rescueEphemerons(); + finishedGC(); + + //warning("GCHeap::collectOld() done\n"); + +} \ No newline at end of file diff --git a/runtime/cpp/Allocator/GCHeap.h b/runtime/cpp/Allocator/GCHeap.h new file mode 100644 index 00000000..e15f2810 --- /dev/null +++ b/runtime/cpp/Allocator/GCHeap.h @@ -0,0 +1,75 @@ +#ifndef _GCHEAP_H +#define _GCHEAP_H + +#include +#include + +#include "Memory.h" +#include "Egg.h" + +namespace Egg { + +class GCSpace; +class AllocationZone; +class G1GC; +class Runtime; + +class GCHeap { + GCSpace *_eden, *_from, *_to; + uintptr_t _youngBase, _youngLimit; + + Runtime *_runtime; + AllocationZone *_oldZone; + G1GC *_fullGC; + + bool _atGCSafepoint; // when true, it is allowed to start GC (specially, to move objects) + bool _gcNeeded; // set when fast-path allocation fails, will be done later at GC safepoints + +public: + static const int LargeThreshold = 64 * Egg::KB; + + std::vector _spaces; + std::vector _largeSpaces; + + GCHeap(Runtime *runtime); + ~GCHeap(); + + AllocationZone* oldZone() { return _oldZone; } + + GCSpace* eden() { return _eden; } + GCSpace* from() { return _from; } + GCSpace* to() { return _to; } + + uintptr_t youngBase() { return _youngBase; } + uintptr_t youngLimit() { return _youngLimit; } + + GCSpace* addSpace_(GCSpace *space); + GCSpace* addNewSpaceSized_(int size); + + GCSpace* createLargeSpace_(uintptr_t size); + + uintptr_t allocate_(uint32_t size); + uintptr_t allocateLarge_(uint32_t size); + uintptr_t allocateCommitting_(uint32_t size); + + bool isAtGCSafepoint(); + void beAtGCSafepoint(bool newState) { _atGCSafepoint = newState; } + void requestGC() { _gcNeeded = true; } + void finishedGC() { _gcNeeded = false; } + + void collectIfTime(); + void collectYoung(); + void collectOld(); + + HeapObject* allocateSlots_(uint32_t size); + HeapObject* allocateBytes_(uint32_t size); + +}; + +class GCHeapVector { + +}; + +} // namespace Egg + +#endif // ~ _GCHEAP_H ~ diff --git a/runtime/cpp/Allocator/GCSafepoint.h b/runtime/cpp/Allocator/GCSafepoint.h new file mode 100644 index 00000000..213654cb --- /dev/null +++ b/runtime/cpp/Allocator/GCSafepoint.h @@ -0,0 +1,36 @@ + +#ifndef _GCSAFEPOINT_H_ +#define _GCSAFEPOINT_H_ + +#include "GCHeap.h" + +namespace Egg { + +/** + * My instances, on construction, allow the heap to be collected (i.e. to move objects). + * When destructed, we restore the previous state. + * + * Creating an instance does not automatically cause GC. Users should be aware of potentially + * GCing calls (basically, calls to methods that potentially allocate objects). At that point, + * all on-the-fly references to heap objects (raw pointers not known by GC) must be wrapped by + * GCedRefs or dead. + */ + +class GCSafepoint { + GCHeap* _heap; + bool _prevState; + + public: + GCSafepoint(GCHeap *heap) : _heap(heap) { + _prevState = _heap->isAtGCSafepoint(); + _heap->beAtGCSafepoint(true); + } + + ~GCSafepoint() { + _heap->beAtGCSafepoint(_prevState); + } +}; + +} + +#endif // ~ _GCSAFEPOINT_H_ diff --git a/runtime/cpp/Allocator/GCSpace.cpp b/runtime/cpp/Allocator/GCSpace.cpp new file mode 100644 index 00000000..4171ccca --- /dev/null +++ b/runtime/cpp/Allocator/GCSpace.cpp @@ -0,0 +1,167 @@ + +#include "Egg.h" +#include "Memory.h" +#include "GCSpace.h" +#include "HeapObject.h" + +#include + +using namespace Egg; + +GCSpace::GCSpace(int size) +{ + _base = ReserveMemory(0, size); + if (!_base) + error_("Failed to reserve memory."); + + _next = _softLimit = _committedLimit = _base; + _reservedLimit = _base + size; + +} + +GCSpace* GCSpace::allocatedAt_limit_(uintptr_t base, uintptr_t limit, bool empty) +{ + GCSpace *result = new GCSpace(); + result->_base = base; + auto end = empty ? base : limit; + result->_next = result->_softLimit = result->_committedLimit = end; + result->_reservedLimit = limit; + + return result; +} + +GCSpace* GCSpace::allocatedAt_size_(uintptr_t base, uintptr_t size, bool empty) +{ + return allocatedAt_limit_(base, base + size, empty); +} + +GCSpace::~GCSpace() +{ + if (_base) + FreeMemory(_base, _reservedLimit - _base); +} + +uintptr_t GCSpace::committedSize() +{ + return _committedLimit - _base; +} + +uintptr_t GCSpace::usedSize() +{ + return _next - _base; +} + +uintptr_t GCSpace::softAvailable() +{ + return _softLimit - _next; +} + +uintptr_t GCSpace::reservedSize() +{ + return _reservedLimit - _base; +} + +bool GCSpace::commitMemoryUpTo_(uintptr_t address) +{ + auto newLimit = std::min(pagealign(address), _reservedLimit); + if (newLimit < address) + return false; + + CommitMemory(_committedLimit, newLimit - _base); + _committedLimit = newLimit; + return true; +} + +bool GCSpace::commitMemory_(uint32_t delta) +{ + return this->commitMemoryUpTo_(_committedLimit + delta); +} + +uintptr_t GCSpace::allocateIfPossible_(uint32_t size) { + auto result = _next; + auto end = _next + size; + if (end < _softLimit) + { + _next = end; + return result; + } + + return 0; +} + +uintptr_t GCSpace::allocateCommittingIfNeeded_(uint32_t size) +{ + auto answer = _next; + auto next = answer + size; + if (next <= _softLimit || this->commitMemoryUpTo_(next)) + { + _next = next; + return answer; + } + else return 0; +} + +bool GCSpace::increaseSoftLimit_(uint32_t delta) +{ + int32_t available = _committedLimit - _softLimit; + if (available < 0) return false; + + _softLimit = _softLimit + std::min(delta, (uint32_t)available); + return true; +} + +HeapObject *GCSpace::firstObject() +{ + return HeapObject::ObjectHeader::at((void*)_base)->object(); +} + +bool GCSpace::includes_(HeapObject *object) +{ + return _base < (uintptr_t)object && (uintptr_t)object < _next; +} + +HeapObject *GCSpace::shallowCopy_(HeapObject *object) +{ + auto size = object->bodySizeInBytes(); + auto extra = object->headerSizeInBytes(); + auto allocation = this->allocateIfPossible_(extra + size); + if (!allocation) + return nullptr; + + auto copy = (HeapObject*)(allocation + extra); + copy->copyFrom_headerSize_bodySize_(object, (uintptr_t)extra, (uintptr_t)size); + copy->beNotRemembered(); + return copy; + +} + +HeapObject *GCSpace::shallowCopyCommitting_(HeapObject *object) +{ + auto size = object->bodySizeInBytes(); + auto extra = object->headerSizeInBytes(); + auto allocation = this->allocateCommittingIfNeeded_(extra + size); + if (!allocation) + return nullptr; + + auto copy = (HeapObject*)(allocation + extra); + copy->copyFrom_headerSize_bodySize_(object, extra, size); + copy->beNotRemembered(); + return copy; +} + +void GCSpace::unmarkAll() { + this->objectsDo_( + [](HeapObject *object) { object->beUnseen();} + ); +} + +void GCSpace::objectsDo_(const std::function &aBlock) +{ + auto header = HeapObject::ObjectHeader::at((void*)_base); + while ((uintptr_t)header < _next) + { + auto object = header->object(); + header = object->nextHeader(); + aBlock(object); + } +} diff --git a/runtime/cpp/Allocator/GCSpace.h b/runtime/cpp/Allocator/GCSpace.h new file mode 100644 index 00000000..66305ed5 --- /dev/null +++ b/runtime/cpp/Allocator/GCSpace.h @@ -0,0 +1,62 @@ +#ifndef _GCSPACE_H_ +#define _GCSPACE_H_ + +#include +#include +#include + +namespace Egg { + +class HeapObject; + +class GCSpace { + GCSpace() {}; +public: + uintptr_t _base, _next, _softLimit, _committedLimit, _reservedLimit; + std::string _name; + GCSpace(int size); + GCSpace(GCSpace *copy); + + static GCSpace* allocatedAt_limit_(uintptr_t base, uintptr_t limit, bool empty = true); + static GCSpace* allocatedAt_size_(uintptr_t base, uintptr_t size, bool empty = true); + + ~GCSpace(); + + uintptr_t base() { return _base; } + uintptr_t next() { return _next; } + uintptr_t nextFree() { return _next; } + uintptr_t softLimit() { return _softLimit; } + uintptr_t commitedLimit() { return _committedLimit; } + uintptr_t reservedLimit() { return _reservedLimit; } + std::string& name() { return _name; } + + uintptr_t committedSize(); + uintptr_t reservedSize(); + uintptr_t usedSize(); // the amount actually used in the committed chunk + uintptr_t isFree() { return committedSize() == 0; } + + uintptr_t softAvailable(); + + bool commitMemoryUpTo_(uintptr_t address); + bool commitMemory_(uint32_t delta); + uintptr_t allocateIfPossible_(uint32_t size); + uintptr_t allocateCommittingIfNeeded_(uint32_t size); + bool increaseSoftLimit_(uint32_t delta); + + HeapObject* firstObject(); + + bool includes_(HeapObject *object); + + HeapObject* shallowCopy_(HeapObject *object); + HeapObject* shallowCopyCommitting_(HeapObject *object); + + void unmarkAll(); + void f(); + + void objectsDo_(const std::function& aBlock); + +}; + +} // namespace Egg + +#endif // ~ _GCSPACE_H_ ~ diff --git a/runtime/cpp/Allocator/GarbageCollector.cpp b/runtime/cpp/Allocator/GarbageCollector.cpp new file mode 100644 index 00000000..19834622 --- /dev/null +++ b/runtime/cpp/Allocator/GarbageCollector.cpp @@ -0,0 +1,294 @@ +#include "GarbageCollector.h" +#include "KnownObjects.h" +#include "Evaluator/Runtime.h" + +#include +#include + +using namespace Egg; + +GarbageCollector::GarbageCollector(Runtime *runtime, AllocationZone *oldZone, GCHeap *heap) : + _runtime(runtime), + _oldZone(oldZone), + _heap(heap) +{ +} + +bool GarbageCollector::checkEphemerons() +{ + auto done = true; + while (!_uncheckedEphemerons->empty()) + { + HeapObject *ephemeron = _uncheckedEphemerons->back(); + _uncheckedEphemerons->pop_back(); + if (this->checkReachablePropertyOf_(ephemeron)) + { + this->followEphemeronWeaks_(ephemeron); + done = false; + } + else + _unreachedEphemerons->push_back(ephemeron); + } + return done; +} + +void GarbageCollector::collect() +{ + this->initializeLocals(); + this->doCollect(); + this->releaseLocals(); +} + +void GarbageCollector::doCollect() +{ + this->followRoots(); + this->strengthenAndMigrateEphemerons(); + this->scanWeakContainers(); +} + +void GarbageCollector::followEphemerons() +{ + bool done; + do { + this->followLiveEphemerons(); + done = this->rescueUnreachableEphemerons(); + } while (!done); +} + +void GarbageCollector::followEphemeronWeaks_(HeapObject *ephemeron) +{ + this->scan_from_to_(ephemeron, 1, ephemeron->size()); + this->followClosure(); +} + +void GarbageCollector::followLiveEphemerons() +{ + bool done; + do { + done = this->checkEphemerons(); + if (done) + break; + this->swapUncheckedWithUnreached(); + } while (true); +} + +void GarbageCollector::followRoots() +{ + //this->scanStack_(_activeStack); + this->scanCurrentContext(); + //this->scanThreadLocalStorages(); + this->followClosure(); + this->followEphemerons(); +} + +void GarbageCollector::initializeLocals() +{ + auto size = this->initialContainerCapacity(); + + _weakContainers = new std::vector(); + _uncheckedEphemerons = new std::vector(); + _unreachedEphemerons = new std::vector(); + _rescuedEphemerons = new std::vector(); + _stacks = new std::vector(); + + _weakContainers->reserve(size); + _uncheckedEphemerons->reserve(size); + _unreachedEphemerons->reserve(size); + _rescuedEphemerons->reserve(size); + _stacks->reserve(size); +} + +void GarbageCollector::heap_(GCHeap *aGCHeap) +{ + _heap = aGCHeap; + this->postInitialize(); +} + +void GarbageCollector::postInitialize() +{ + _oldZone = _heap->oldZone(); + +} + +void Egg::GarbageCollector::releaseLocals() +{ + //warning("queueEphemerons must be implemented"); + //_memory->queueEphemerons_(_rescuedEphemerons); + + delete _weakContainers; + delete _uncheckedEphemerons; + delete _unreachedEphemerons; + delete _rescuedEphemerons; + delete _stacks; + + _weakContainers = nullptr; + _uncheckedEphemerons = nullptr; + _unreachedEphemerons = nullptr; + _rescuedEphemerons = nullptr; + _stacks = nullptr; + //ActiveProcess stack unlock +} + +void GarbageCollector::rememberSpecial_(HeapObject *anObject) +{ + auto klass = _runtime->speciesOf_((Object*)anObject); + + std::vector *collection; + if (klass == _runtime->_ephemeronClass) + { + this->checkEphemeron_(anObject); + collection = _uncheckedEphemerons; + } + else + { + collection = klass == _runtime->_processStackClass ? _stacks : _weakContainers; + } + collection->push_back(anObject); +} + +void GarbageCollector::rescueEphemeron_(HeapObject *ephemeron) +{ + this->followEphemeronWeaks_(ephemeron); + _rescuedEphemerons->push_back(ephemeron); +} + +bool GarbageCollector::rescueUnreachableEphemerons() +{ + auto done = _unreachedEphemerons->empty(); + for (auto ephemeron : *_unreachedEphemerons) + { + this->rescueEphemeron_(ephemeron); + } + _unreachedEphemerons->clear(); + return done; +} + +void GarbageCollector::scanNativeStackFrame_sized_(uintptr_t *framePointer, uintptr_t size) +{ + // all code is pinned for now + //self fixReturnAddressIn: framePointer _asObject sized: size. + + this->scanStackFrameObjects_sized_(framePointer, size); +} + +void GarbageCollector::scanStackFrameObjects_sized_(uintptr_t *framePointer, uintptr_t size) { + //for (uintptr_t i = 0; i < size; i++) + // printf("adding %s to queue\n", ((Object*)framePointer[i])->printString().c_str()); + this->scan_from_to_((HeapObject*)framePointer, 1, size); +} + +void GarbageCollector::scanSpecialSlots_(HeapObject *special) +{ + this->scan_from_to_(special, 1, special->size()); +} + +void GarbageCollector::nativeFramesStartingAt_bp_do_(uintptr_t **stack, uintptr_t sp, uintptr_t bp, std::function block) { + auto start = sp; + auto end = bp; + while (end != 0) { + auto size = end - start; + block((uintptr_t*)&stack[start-1], size); + start = end + 2; // next frame stars after bp and retaddr slots + end = (uintptr_t)stack[end-1]; + } +} + +void GarbageCollector::scanFirstStackChunk_(HeapObject *aProcessVMStack) { + /** + * Scanning needs to fetch a chain of stack (frame) pointers. The head + * of the chain is either of two cases: + * - The active process. + * In that case, the top of the stack is a common frame (probably a call + * to a primitive). No special action needs to be done. + * - A sleeping (native) process. + * In that case, the top of the stack is that process' env, followed + * by a retaddr. The GC has to scan that addr and then continue normally. + * (TODO: make env an instvar of the ProcessVMStack) + */ + //if (aProcessVMStack != runtime->_activeProcessStack) + // this->scanTopSlot_(aProcessVMStack); + + auto firstSP = _runtime->processVMStackSP_(aProcessVMStack) + 2; + auto firstBP = _runtime->processVMStackBP_(aProcessVMStack); + auto stack = (uintptr_t**)nullptr; //_runtime->processVMStackContext_(aProcessVMStack)->stack(); + this->nativeFramesStartingAt_bp_do_(stack, firstSP, firstBP, + [this](uintptr_t *frame, uintptr_t size) { + this->scanNativeStackFrame_sized_(frame, size); + }); +} + +/* only for use until we have context switches */ +void GarbageCollector::scanCurrentContext() { + auto firstSP = _runtime->_evaluator->context()->stackPointer(); + auto firstBP = _runtime->_evaluator->context()->framePointer(); + auto stack = (uintptr_t**)_runtime->_evaluator->context()->stack(); + this->nativeFramesStartingAt_bp_do_(stack, firstSP, firstBP, + [this](uintptr_t *frame, uintptr_t size) { + this->scanNativeStackFrame_sized_(frame, size); + }); +} + +void GarbageCollector::scanStack_(HeapObject *aProcessVMStack) +{ + //auto context = _runtime->processVMStackContext_(aProcessVMStack); + + // skip this stack if it corresponds to active process, which has already been scanned + //if (context == _runtime->_evaluator->context()) + return; + + auto process = _runtime->processVMStackProcess_(aProcessVMStack); + if (_runtime->processStackIsValid_(process)) + this->scanFirstStackChunk_(aProcessVMStack); + + /* unimplemented GC in callbacks + this->stackFramesBeneathCallbackIn_Do_(aProcessVMStack, + [this](uintptr_t frame, uintptr_t nativeSize) { + this->scanNativeStackFrame_sized_(frame, nativeSize); + }); + */ +} + +void GarbageCollector::scanStacks() +{ + while (!_stacks->empty()) + { + auto s = _stacks->back(); + _stacks->pop_back(); + this->scanSpecialSlots_(s); + this->scanStack_(s); + } +} + +void GarbageCollector::scanWeakContainers() +{ + for (auto weakContainer : *_weakContainers) + { + this->updateWeakReferencesOf_(weakContainer); + } + _weakContainers->clear(); +} + +void GarbageCollector::strengthenAndMigrateEphemerons() +{ + // No need to move ephemerons anywhere now, they will be added to the + // list of unreachable on GC release. That list is iterated after GC + // to finalize each of them. + + for (auto ephemeron : *_rescuedEphemerons) + { + ephemeron->beNotSpecial(); + } +} + +void GarbageCollector::swapUncheckedWithUnreached() +{ + auto aux = this->_uncheckedEphemerons; + this->_uncheckedEphemerons = this->_unreachedEphemerons; + this->_unreachedEphemerons = aux; +} + +void GarbageCollector::updateWeakReferencesOf_(HeapObject *weakContainer) +{ + for (uint32_t i = 0; i < weakContainer->size(); i++) + this->updateWeak_at_(weakContainer, i); +} diff --git a/runtime/cpp/Allocator/GarbageCollector.h b/runtime/cpp/Allocator/GarbageCollector.h new file mode 100644 index 00000000..747b7d43 --- /dev/null +++ b/runtime/cpp/Allocator/GarbageCollector.h @@ -0,0 +1,74 @@ +#ifndef _GARBAGECOLLECTOR_H_ +#define _GARBAGECOLLECTOR_H_ + +#include "GCSpace.h" +#include "GCHeap.h" +#include "Memory.h" +#include + +namespace Egg { + +class Runtime; +class AllocationZone; + +class GarbageCollector { +protected: + Runtime *_runtime; + AllocationZone *_oldZone; + + std::vector *_weakContainers; + std::vector *_uncheckedEphemerons, *_unreachedEphemerons, *_rescuedEphemerons; + HeapObject *_tombstone; + + GCHeap *_heap; + std::vector *_stacks; + uintptr_t _currentSP; + + const int LargeThreshold = 64 * KB; + +public: + GarbageCollector(Runtime *runtime, AllocationZone *oldZone, GCHeap *heap); + + virtual ~GarbageCollector() = default; + + bool checkEphemerons(); + virtual void checkEphemeron_(HeapObject *ephemeron) {} + virtual bool checkReachablePropertyOf_(HeapObject *ephemeron) = 0; + + void collect(); + virtual void doCollect(); + virtual void followClosure() = 0; + void followEphemerons(); + void followEphemeronWeaks_(HeapObject *ephemeron); + void followLiveEphemerons(); + void followRoots(); + virtual uintptr_t initialContainerCapacity() = 0; + virtual void initializeLocals(); + void heap_(GCHeap *aGCHeap); + virtual void postInitialize(); + virtual void releaseLocals(); + void rememberSpecial_(HeapObject *anObject); + void rescueEphemeron_(HeapObject *ephemeron); + bool rescueUnreachableEphemerons(); + virtual void scan_from_to_(HeapObject *current, uintptr_t start, uintptr_t limit) = 0; + void scanNativeStackFrame_sized_(uintptr_t *framePointer, uintptr_t size); + void scanStackFrameObjects_sized_(uintptr_t *framePointer, uintptr_t size); + void scanSpecialSlots_(HeapObject *special); + + void nativeFramesStartingAt_bp_do_(uintptr_t **stack, uintptr_t sp, uintptr_t bp, std::function block); + void scanFirstStackChunk_(HeapObject * aProcessVMStack); + void scanCurrentContext(); + void scanStack_(HeapObject *aProcessVMStack); + void scanStacks(); + virtual void scanThreadLocalStorage_(HeapObject *tread) = 0; + void scanWeakContainers(); + void strengthenAndMigrateEphemerons(); + void swapUncheckedWithUnreached(); + virtual void updateWeak_at_(HeapObject *weakContainer, uintptr_t index) = 0; + void updateWeakReferencesOf_(HeapObject *weakContainer); + +}; + +} // namespace Egg + +#endif // ~ _GARBAGECOLLECTOR_H_ ~ diff --git a/runtime/cpp/Memory.h b/runtime/cpp/Allocator/Memory.h similarity index 62% rename from runtime/cpp/Memory.h rename to runtime/cpp/Allocator/Memory.h index 52d0cd9d..00bb8cd7 100644 --- a/runtime/cpp/Memory.h +++ b/runtime/cpp/Allocator/Memory.h @@ -8,6 +8,7 @@ #include #include "Util.h" +#include "Egg.h" namespace Egg { @@ -15,12 +16,26 @@ namespace Egg void InitializeMemory(); uintptr_t ReserveMemory(uintptr_t base, uintptr_t size); void CommitMemory(uintptr_t base, uintptr_t size); +void DecommitMemory(uintptr_t base, uintptr_t size); void FreeMemory(uintptr_t base, uintptr_t size); uintptr_t ReserveAligned4GB(); uintptr_t pagealign(uintptr_t addr); +template +T* aligned_alloc() +{ +#ifdef _WIN32 + return static_cast(_aligned_malloc(sizeof(T), sizeof(void*))); +#else + return static_cast(std::aligned_alloc(sizeof(void*), sizeof(T))); +#endif +} + +void aligned_free(void* mem); + + class HeapObject; } // namespace Egg diff --git a/runtime/cpp/Bootstrapper.h b/runtime/cpp/Bootstrapper.h index 252bc2b4..0eebb6c7 100644 --- a/runtime/cpp/Bootstrapper.h +++ b/runtime/cpp/Bootstrapper.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include @@ -22,6 +24,8 @@ class Bootstrapper { Bootstrapper(ImageSegment *kernel) { this->_kernel = kernel; this->_runtime = new Runtime(this, this->_kernel); + + this->_runtime->addSegmentSpace_(kernel); // this->_runtime->bootstrapper_(this); this->_runtime->initializeEvaluator(); } @@ -91,6 +95,7 @@ class Bootstrapper { std::vector imports; this->bindModuleImports(imageSegment, imports); imageSegment->fixPointerSlots(imports); + this->_runtime->addSegmentSpace_(imageSegment); return imageSegment; } diff --git a/runtime/cpp/CMakeLists.txt b/runtime/cpp/CMakeLists.txt index f6264305..0d14afb3 100644 --- a/runtime/cpp/CMakeLists.txt +++ b/runtime/cpp/CMakeLists.txt @@ -34,7 +34,7 @@ find_package(cxxopts REQUIRED) # ************************************************* -file(GLOB ALL_SRC "*.cpp" "Evaluator/*.cpp" "${PLATFORM}/*.cpp") +file(GLOB ALL_SRC "*.cpp" "Evaluator/*.cpp" "Allocator/*.cpp" "${PLATFORM}/*.cpp") add_executable(${EXE} ${ALL_SRC}) target_link_libraries(${EXE} libffi::libffi cxxopts::cxxopts) # link agains libffi diff --git a/runtime/cpp/Egg.cpp b/runtime/cpp/Egg.cpp index 3debb531..548a6229 100644 --- a/runtime/cpp/Egg.cpp +++ b/runtime/cpp/Egg.cpp @@ -1,5 +1,5 @@ #include "Egg.h" -#include "Memory.h" +#include "Allocator/Memory.h" #include "Evaluator/Runtime.h" #include "Evaluator/Evaluator.h" #include "Evaluator/EvaluationContext.h" @@ -7,8 +7,6 @@ #include #include -bool Egg::GC_CRITICAL = false; - void Egg::error(const char *message) { if (debugRuntime) std::cerr << debugRuntime->_evaluator->context()->backtrace() << std::endl << std::endl; @@ -20,6 +18,13 @@ void Egg::error_(const std::string &message) { error(message.c_str()); } +void Egg::warning(const char *message) { + std::cerr << "WARNING: " << message << std::endl; +} + +void Egg::warning_(const std::string &message) { + warning(message.c_str()); +} void Egg::Initialize() { InitializeMemory(); diff --git a/runtime/cpp/Egg.h b/runtime/cpp/Egg.h index 815c05c3..43bfeecd 100644 --- a/runtime/cpp/Egg.h +++ b/runtime/cpp/Egg.h @@ -2,17 +2,21 @@ #define _EGG_H_ #include +#include #include typedef unsigned long ulong; typedef unsigned short ushort; typedef unsigned char uchar; -#define WORD_SIZE 8 -#define WORD_SIZE_SHIFT 3 - namespace Egg { +constexpr uintptr_t WORD_SIZE = sizeof(uintptr_t); +constexpr uintptr_t WORD_SIZE_SHIFT = WORD_SIZE == 4 ? 2 : 3; + +constexpr uintptr_t KB = 1024; +constexpr uintptr_t MB = 1024 * 1024; + struct Object; struct HeapObject; struct SmallInteger; @@ -21,9 +25,8 @@ void Initialize(); void osError(); void error(const char *message); void error_(const std::string &message); -void debug(const char *message); - -extern bool GC_CRITICAL; +void warning(const char *message); +void warning_(const std::string &message); } diff --git a/runtime/cpp/Evaluator/EvaluationContext.cpp b/runtime/cpp/Evaluator/EvaluationContext.cpp index 622a3dc7..ec7cd53c 100644 --- a/runtime/cpp/Evaluator/EvaluationContext.cpp +++ b/runtime/cpp/Evaluator/EvaluationContext.cpp @@ -16,8 +16,6 @@ using namespace Egg; -class Runtime; - HeapObject* EvaluationContext::classBinding() { return _runtime->methodClassBinding_(this->method()); @@ -68,12 +66,12 @@ std::vector EvaluationContext::methodArguments() { return arguments; } -std::vector* EvaluationContext::buildLaunchFrame(HeapObject *symbol, int argCount) +PlatformCode* EvaluationContext::buildLaunchFrame(HeapObject *symbol, int argCount) { auto launcher = _runtime->newCompiledMethod(); - auto bytecodes = new std::vector(); - auto executable = _runtime->newExecutableCodeFor_with_(launcher, reinterpret_cast(bytecodes)); - _runtime->methodExecutableCode_put_(launcher, (Object*)executable); + auto bytecodes = newPlatformCode(); + auto executable = _runtime->newExecutableCodeFor_with_(launcher, bytecodes); + _runtime->methodExecutableCode_put_(launcher, executable); this->buildMethodFrameFor_code_environment_((Object*)_runtime->_nilObj, launcher, _runtime->_nilObj); auto literal = new SLiteral(0, (Object*)_runtime->_nilObj); @@ -415,9 +413,9 @@ std::string EvaluationContext::backtrace() { return s.str(); } -void printStackObject_into_(Object *o, std::ostringstream &s) +void printStackObject_into_(uintptr_t index, Object *o, std::ostringstream &s) { - s << "| " << std::setw(sizeof(void*)*2) << std::setfill(' ') << std::hex << o << " | "; + s << std::setw(sizeof(void*)*2) << std::setfill(' ') << std::hex << index - 1 << " | " << std::setw(sizeof(void*)*2) << std::setfill(' ') << std::hex << o << " | "; if (o == nullptr) s << "bad obj"; else @@ -428,15 +426,15 @@ std::string EvaluationContext::printStackContents() { std::vector frames; uintptr_t current = _regSP; uintptr_t next = _regBP; - s << "|------------------|" << std::endl; + s << " |------------------|" << std::endl; do { for (uintptr_t index = current; index < next; index++) { - printStackObject_into_(_stack[index - 1], s); + printStackObject_into_(index, _stack[index - 1], s); s << std::endl; } - s << "| " << std::setw(sizeof(void*)*2) << std::setfill(' ') << std::hex << _stack[next - 1] << " | fp" << std::endl; - s << "| " << std::setw(sizeof(void*)*2) << std::setfill(' ') << std::hex << _stack[next + 1 - 1] << " | retaddr" << std::endl; + s << std::setw(sizeof(void*)*2) << std::setfill(' ') << std::hex << next - 1 << " | " << std::setw(sizeof(void*)*2) << std::setfill(' ') << std::hex << _stack[next - 1] << " | fp" << std::endl; + s << std::setw(sizeof(void*)*2) << std::setfill(' ') << std::hex << next + 1 - 1 << " | " << std::setw(sizeof(void*)*2) << std::setfill(' ') << std::hex << _stack[next + 1 - 1] << " | retaddr" << std::endl; current = next + 2; next = (uintptr_t)_stack[next - 1]; diff --git a/runtime/cpp/Evaluator/EvaluationContext.h b/runtime/cpp/Evaluator/EvaluationContext.h index b00e10d4..22adbe9c 100644 --- a/runtime/cpp/Evaluator/EvaluationContext.h +++ b/runtime/cpp/Evaluator/EvaluationContext.h @@ -52,6 +52,10 @@ class EvaluationContext { HeapObject* environment() { return _regE; } HeapObject* compiledCode() { return _regM; } + uintptr_t stackPointer() { return _regSP; } + uintptr_t framePointer() { return _regBP; } + Object** stack() { return _stack; } + HeapObject* classBinding(); int tempOffset() { return 4; } @@ -91,7 +95,7 @@ class EvaluationContext { std::vector methodArguments(); void buildFrameFor_code_environment_temps_(Object *receiver, HeapObject *compiledCode, HeapObject *environment, uint32_t temps); - std::vector* buildLaunchFrame(HeapObject *symbol, int argCount); + PlatformCode* buildLaunchFrame(HeapObject *symbol, int argCount); void buildClosureFrameFor_code_environment_(Object *receiver, HeapObject *compiledCode, HeapObject *environment); void buildMethodFrameFor_code_environment_(Object *receiver, HeapObject *compiledCode, HeapObject *environment); void popLaunchFrame(HeapObject *prevRegE); diff --git a/runtime/cpp/Evaluator/Evaluator.cpp b/runtime/cpp/Evaluator/Evaluator.cpp index 1f1377e4..b8e7d47f 100644 --- a/runtime/cpp/Evaluator/Evaluator.cpp +++ b/runtime/cpp/Evaluator/Evaluator.cpp @@ -1,6 +1,7 @@ #include "Evaluator.h" #include "Runtime.h" +#include "Allocator/GCSafepoint.h" #include "SExpressionLinearizer.h" #include "SOpAssign.h" #include "SOpDispatchMessage.h" @@ -22,6 +23,7 @@ #include "FFIGlue.h" +#include #include #include #include @@ -158,7 +160,7 @@ void Evaluator::initializePrimitives() this->addPrimitive("FFICall", &Evaluator::primitiveFFICall); this->addPrimitive("HostInitializeFFI", &Evaluator::primitiveHostInitializeFFI); this->addPrimitive("HostPlatformName", &Evaluator::primitiveHostPlatformName); - + this->addPrimitive("HostCurrentMilliseconds", &Evaluator::primitiveHostCurrentMilliseconds); /*this->addPrimitive("PrepareForExecution", &Evaluator::primitivePrepareForExecution); this->addPrimitive("ProcessVMStackInitialize", &Evaluator::primitiveProcessVMStackInitialize); this->addPrimitive("ProcessVMStackAt", &Evaluator::primitiveProcessVMStackAt); @@ -201,9 +203,10 @@ Evaluator::lookup_startingAt_sendSite_(HeapObject *symbol, HeapObject *behavior, Object *Evaluator::invoke_with_(HeapObject *method, Object *receiver) { int size = _runtime->methodEnvironmentSize_(method); - HeapObject *environment = _runtime->newEnvironmentSized_(size); + + HeapObject *environment = _runtime->methodNeedsEnviornment_(method) ? _runtime->newEnvironmentSized_(size) : _runtime-> _nilObj; HeapObject *executable = this->prepareForExecution_(method); - _work = reinterpret_cast*>(_runtime->executableCodePlatformCode_(executable)); + _work = _runtime->executableCodeWork_(executable); this->_context->buildMethodFrameFor_code_environment_(receiver, method, environment); @@ -224,8 +227,8 @@ HeapObject* Evaluator::prepareForExecution_(HeapObject *method) { auto sexpressions = decoder.decodeMethod(); this->_linearizer->visitMethod(sexpressions, method); - executableCode = this->_runtime->newExecutableCodeFor_with_(method, reinterpret_cast(this->_linearizer->operations())); - this->_runtime->methodExecutableCode_put_(method, (Object*)executableCode); + executableCode = this->_runtime->newExecutableCodeFor_with_(method, this->_linearizer->operations()); + this->_runtime->methodExecutableCode_put_(method, executableCode); return executableCode; } @@ -262,7 +265,7 @@ Object* Evaluator::send_to_with_(HeapObject *symbol, Object *receiver, std::vect this->evaluate(); this->_context->popLaunchFrame(prevRegE); auto executableCode = this->_runtime->methodExecutableCode_(this->_context->method()); - this->_work = reinterpret_cast* >(_runtime->executableCodePlatformCode_(executableCode));; + this->_work = _runtime->executableCodeWork_(executableCode); return this->_regR; } @@ -419,6 +422,8 @@ void Evaluator::popFrameAndPrepare() void Evaluator::visitOpReturn(SOpReturn *anSOpReturn) { this->popFrameAndPrepare(); + + _runtime->_heap->collectIfTime(); } void Evaluator::visitOpNonLocalReturn(SOpNonLocalReturn *anSOpNonLocalReturn) @@ -660,6 +665,12 @@ Object* Evaluator::primitiveHash() { return newIntObject(this->_runtime->hashFor_(this->_context->self())); } +Object * Evaluator::primitiveHostCurrentMilliseconds() { + intptr_t now = std::chrono::duration_cast< std::chrono::milliseconds >( + std::chrono::system_clock::now().time_since_epoch()).count(); + return newIntObject((intptr_t)now); +} + Object* Evaluator::primitiveHostPlatformName() { return (Object*)this->_runtime->newString_(PlatformName()); } @@ -685,15 +696,18 @@ Object* Evaluator::primitiveHostLoadModule() { } Object* Evaluator::primitiveNew() { + GCSafepoint safepoint(this->_runtime->_heap); return (Object*)this->_runtime->newSlotsOf_(this->_context->self()->asHeapObject()); } Object* Evaluator::primitiveNewBytes() { + GCSafepoint safepoint(this->_runtime->_heap); auto size = this->_context->firstArgument()->asSmallInteger()->asNative(); return (Object*)this->_runtime->newBytes_size_(this->_context->self()->asHeapObject(), size); } Object* Evaluator::primitiveNewSized() { + GCSafepoint safepoint(this->_runtime->_heap); auto size = this->_context->firstArgument()->asSmallInteger()->asNative(); return (Object*)this->_runtime->newOf_sized_(this->_context->self()->asHeapObject(), size); } diff --git a/runtime/cpp/Evaluator/Evaluator.h b/runtime/cpp/Evaluator/Evaluator.h index d4f29cc5..ba52bb03 100644 --- a/runtime/cpp/Evaluator/Evaluator.h +++ b/runtime/cpp/Evaluator/Evaluator.h @@ -161,8 +161,8 @@ class Evaluator : public SExpressionVisitor { void visitOpPushR(SOpPushR *anSOpPushR) override; void popFrameAndPrepare(); - virtual void visitOpReturn(SOpReturn *anSOpReturn); - virtual void visitOpNonLocalReturn(SOpNonLocalReturn *anSOpNonLocalReturn); + virtual void visitOpReturn(SOpReturn *anSOpReturn) override; + virtual void visitOpNonLocalReturn(SOpNonLocalReturn *anSOpNonLocalReturn) override; private: void evaluate(); @@ -200,6 +200,7 @@ class Evaluator : public SExpressionVisitor { Object* primitiveHash(); + Object* primitiveHostCurrentMilliseconds(); Object* primitiveHostFixOverrides(); Object* primitiveHostInitializeFFI(); Object* primitiveHostLoadModule(); diff --git a/runtime/cpp/Evaluator/PlatformCode.cpp b/runtime/cpp/Evaluator/PlatformCode.cpp new file mode 100644 index 00000000..1bb4be31 --- /dev/null +++ b/runtime/cpp/Evaluator/PlatformCode.cpp @@ -0,0 +1,23 @@ + +#include "PlatformCode.h" + +#include + +Egg::PlatformCode* Egg::newPlatformCode() +{ + void* rawmem = Egg::aligned_alloc(); + if (!rawmem) { + throw std::bad_alloc(); + } + + // Construct the object in the allocated memory + return new (rawmem) PlatformCode(); +} + +void Egg::deletePlatformCode(Egg::PlatformCode* platformCode) +{ + platformCode->~PlatformCode(); + + // Free the raw memory + aligned_free((void*)platformCode); +} \ No newline at end of file diff --git a/runtime/cpp/Evaluator/PlatformCode.h b/runtime/cpp/Evaluator/PlatformCode.h new file mode 100644 index 00000000..6d0a9e48 --- /dev/null +++ b/runtime/cpp/Evaluator/PlatformCode.h @@ -0,0 +1,21 @@ +#ifndef _PLATFORM_CODE_H_ +#define _PLATFORM_CODE_H_ + +#include +#include + +/** +* A simple typedef for wrapping a buffer that can be used as a heap object, +* by encoding it as a tagged small integer. +*/ + +namespace Egg { + class SExpression; + + typedef std::vector PlatformCode; + PlatformCode *newPlatformCode(); + void deletePlatformCode(PlatformCode *platformCode); + +} // namespace Egg + +#endif // ~ _PLATFORM_CODE ~ diff --git a/runtime/cpp/Evaluator/Runtime.cpp b/runtime/cpp/Evaluator/Runtime.cpp index 65a1105f..2601b899 100644 --- a/runtime/cpp/Evaluator/Runtime.cpp +++ b/runtime/cpp/Evaluator/Runtime.cpp @@ -2,18 +2,33 @@ #include "Runtime.h" #include +#include +#include #include "Evaluator.h" -#include "GCHeap.h" +#include "Allocator/GCHeap.h" #include "SAbstractMessage.h" #include "KnownConstants.h" +#include "GCedRef.h" +#include "StackGCedRef.h" using namespace Egg; -Runtime *Egg::debugRuntime = 0; +Runtime *Egg::debugRuntime = nullptr; + + +Runtime::Runtime(Bootstrapper* bootstrapper, ImageSegment* kernel): + _bootstrapper(bootstrapper), + _kernel(kernel), + _lastHash(0) +{ + this->initializeKernelObjects(); + KnownObjects::initializeFrom(this); + _heap = new GCHeap(this); + +} void Runtime::initializeEvaluator() { - _heap = new GCHeap; _evaluator = new Evaluator(this, _falseObj, _trueObj, _nilObj); } @@ -30,9 +45,10 @@ uintptr_t Runtime::arrayedSizeOf_(Object *anObject) { HeapObject* Runtime::newBytes_size_(HeapObject *species, uint32_t size) { auto behavior = this->speciesInstanceBehavior_(species); + StackGCedRef gcedBehavior(_evaluator->context(), (Object*)behavior); auto result = _heap->allocateBytes_(size); - result->behavior(behavior); + result->behavior(gcedBehavior.asHeapObject()); return result; } @@ -40,8 +56,9 @@ HeapObject *Runtime::newSlots_size_(HeapObject *species, uint32_t size) { auto ivars = this->speciesInstanceSize_(species); HeapObject *behavior = this->speciesInstanceBehavior_(species); auto slotSize = ivars + size; + StackGCedRef gcedBehavior(_evaluator->context(), (Object*)behavior); HeapObject *result = _heap->allocateSlots_(slotSize); - result->behavior(behavior); + result->behavior(gcedBehavior.asHeapObject()); if (size > 0) result->beArrayed(); if (ivars > 0) @@ -100,13 +117,13 @@ HeapObject *Runtime::newClosureFor_(HeapObject *block) HeapObject *Runtime::newEnvironmentSized_(uint32_t size) { return this->newArraySized_(size); - } +} HeapObject *Runtime::newExecutableCodeFor_with_(HeapObject *compiledCode, - HeapObject *platformCode) { + PlatformCode *platformCode) { auto result = this->newSlots_size_(_arrayClass, 2); // fixme: use a proper kind of object for this - this->executableCodePlatformCode_put_(result, (Object *)platformCode); - this->executableCodeCompiledCode_put_(result, (Object *)compiledCode); + this->executableCodePlatformCode_put_(result, platformCode); + this->executableCodeCompiledCode_put_(result, compiledCode); return result; } @@ -125,6 +142,13 @@ HeapObject *Runtime::loadModule_(HeapObject *name) { return _bootstrapper->loadModule_(name->asLocalString()); } +void Runtime::addSegmentSpace_(ImageSegment* segment) +{ + GCSpace *space = GCSpace::allocatedAt_limit_(segment->spaceStart(), segment->spaceEnd(), false); + space->_name = this->moduleName_(segment->header.module)->asLocalString(); + this->_heap->addSpace_(space); +} + uintptr_t Runtime::hashFor_(Object *anObject) { if (anObject->isSmallInteger()) @@ -161,14 +185,53 @@ Object* Runtime::sendLocal_to_with_with_(const std::string &selector, Object *re return this->_evaluator->send_to_with_(symbol, receiver, args); } + +std::string Runtime::printGlobalCache() { + std::ostringstream stream; + for (const auto& entry : _globalCache) { + auto key_first = entry.first.first; + auto key_second = entry.first.second; + auto value = entry.second; + + stream << "Key: <" << key_first->get()->printString(); + stream << ", " << key_second->get()->printString(); + stream << "> -> Value: " << value->get()->printString(); + stream << std::endl; + } + + return stream.str(); +} + +void Runtime::checkCache() { + for (const auto& entry : _globalCache) { + auto symbol = entry.first.first->get(); + auto methodSelector = debugRuntime->methodSelector_(entry.second->get()); + ASSERT( symbol == methodSelector); + if (symbol != methodSelector) { + int a = 0; + } + } +} + HeapObject* Runtime::lookup_startingAt_(HeapObject *symbol, HeapObject *behavior) { + checkCache(); + + if (symbol->printString() == "#sizeInBytes") { + int a = 0; + } auto iter = _globalCache.find(global_cache_key(symbol,behavior)); - if (iter != _globalCache.end()) - return iter->second; + if (iter != _globalCache.end()) { + if (iter->second->get()->slotAt_(5)->printString() != symbol->printString()) + int b = 1; + return iter->second->get(); + } auto method = this->doLookup_startingAt_(symbol, behavior); - _globalCache[global_cache_key(symbol,behavior)] = method; + auto key = gced_global_cache_key(new GCedRef(symbol),new GCedRef(behavior)); + auto value = new GCedRef(method); + _globalCache.insert({key, value}); + checkCache(); return method; } @@ -243,15 +306,15 @@ void Runtime::flushDispatchCache_(HeapObject *aSymbol) { auto iter = _inlineCaches.find(aSymbol); if (iter != _inlineCaches.end()) { - auto messages = _inlineCaches[aSymbol]; + auto messages = iter->second; for (auto& m : *messages) { m->flushCache(); } } - std::vector cached; + std::vector cached; for (const auto& entry : _globalCache) { - if (entry.first.first == aSymbol) { + if (entry.first.first->get() == aSymbol) { cached.push_back(entry.first); } } @@ -268,13 +331,57 @@ void Runtime::flushDispatchCache_in_(HeapObject *aSymbol, HeapObject *klass) { auto iter = _inlineCaches.find(aSymbol); if (iter != _inlineCaches.end()) { - auto messages = _inlineCaches[aSymbol]; + auto messages = iter->second; for (auto& m : *messages) { m->flushCache(); } } - _globalCache.erase(std::make_pair(aSymbol, behavior)); + global_cache_key pair = std::make_pair(aSymbol, behavior); + auto globalIter = _globalCache.find(pair); + if (globalIter != _globalCache.end()) + _globalCache.erase(globalIter); +} + +uintptr_t Runtime::assignGCedRefIndex() { + if (_freeGCedRefs.empty()) + { + auto index = _gcedRefs.size(); + _gcedRefs.push_back(nullptr); + return index; + } + else + { + auto index = _freeGCedRefs.back(); + _freeGCedRefs.pop_back(); + return index; + } +} + +void Runtime::registerGCedRef_(GCedRef *gcedRef) { + _gcedRefs[gcedRef->index()] = gcedRef; +} + +GCedRef * Runtime::createGCedRef_(HeapObject *object) { + + auto index = this->assignGCedRefIndex(); + GCedRef *result = new GCedRef(object, index); + + return result; +} + +void Runtime::releaseGCedRef_(uintptr_t index) { + _freeGCedRefs.push_back(index); + _gcedRefs[index] = nullptr; +} + +void Runtime::gcedRefsDo_(const std::function &aBlock) +{ + for (auto ref : _gcedRefs) + { + if (ref != nullptr) + aBlock(ref); + } } std::string Egg::Runtime::print_(HeapObject *obj) { diff --git a/runtime/cpp/Evaluator/Runtime.h b/runtime/cpp/Evaluator/Runtime.h index 2586a843..5a6211e4 100644 --- a/runtime/cpp/Evaluator/Runtime.h +++ b/runtime/cpp/Evaluator/Runtime.h @@ -1,13 +1,16 @@ -#ifndef _POWERTALKRUNTIME_H_ -#define _POWERTALKRUNTIME_H_ +#ifndef _RUNTIME_H_ +#define _RUNTIME_H_ #include #include #include +#include -#include "../HeapObject.h" -#include "../ImageSegment.h" -#include "../KnownConstants.h" +#include "HeapObject.h" +#include "ImageSegment.h" +#include "KnownConstants.h" +#include "PlatformCode.h" +#include "GCedRef.h" namespace Egg { @@ -21,7 +24,9 @@ class Bootstrapper; extern Runtime *debugRuntime; class Runtime { + std::string printGlobalCache(); public: + void checkCache(); Bootstrapper *_bootstrapper; ImageSegment *_kernel; Evaluator *_evaluator; @@ -30,21 +35,16 @@ class Runtime { std::map _knownSymbols; //typedef std::vector inline_cache; - std::map * > _inlineCaches; + std::map *, GCedRef::Comparator > _inlineCaches; - typedef std::pair global_cache_key; - std::map _globalCache; + typedef std::pair gced_global_cache_key; + typedef std::pair global_cache_key; + + std::map _globalCache; uint16_t _lastHash; public: - Runtime(Bootstrapper *bootstrapper, ImageSegment *kernel) : - _bootstrapper(bootstrapper), - _kernel(kernel), - _lastHash(0) - { - this->initializeKernelObjects(); - KnownObjects::initializeFrom(this); - } + Runtime(Bootstrapper *bootstrapper, ImageSegment *kernel); std::string print_(HeapObject* obj); @@ -83,6 +83,7 @@ class Runtime { uintptr_t arrayedSizeOf_(Object *anObject); + HeapObject* newBytes_size_(HeapObject* species, uint32_t size); HeapObject* newSlots_size_(HeapObject *species, uint32_t size); HeapObject* newSlotsOf_(HeapObject* species); @@ -94,19 +95,25 @@ class Runtime { HeapObject* newClosureFor_(HeapObject *block); HeapObject* newCompiledMethod(); HeapObject* newEnvironmentSized_(uint32_t); - HeapObject* newExecutableCodeFor_with_(HeapObject *compiledCode, HeapObject *platformCode); + + HeapObject* newExecutableCodeFor_with_(HeapObject *compiledCode, PlatformCode *platformCode); HeapObject* newString_(const std::string &str); HeapObject* addSymbol_(const std::string &str); void addKnownSymbol_(const std::string &str, HeapObject *symbol) { _knownSymbols[str] = symbol; } HeapObject* loadModule_(HeapObject *name); + void addSegmentSpace_(ImageSegment *segment); uintptr_t hashFor_(Object *anObject); int16_t nextHash() { + auto prev = this->_lastHash; auto shifted = this->_lastHash >> 1; this->_lastHash = (this->_lastHash & 1) == 0 ? shifted : shifted ^ 0xB9C8; + if (this->_lastHash == 0) { + return prev; + } return this->_lastHash; } @@ -116,7 +123,7 @@ class Runtime { if (it == _inlineCaches.end()) { messages = new std::vector(); - _inlineCaches[symbol] = messages; + _inlineCaches.insert({new GCedRef(symbol), messages}); } else { messages = it->second; @@ -204,7 +211,7 @@ class Runtime { return methodExecutableCode_(block); } - void blockExecutableCode_put_(HeapObject* block, Object *anObject) { + void blockExecutableCode_put_(HeapObject* block, HeapObject *anObject) { return methodExecutableCode_put_(block, anObject); } @@ -242,23 +249,25 @@ class Runtime { } Object* executableCodePlatformCode_(HeapObject *code) { - return code->slot(Offsets::ExecutableCodePlatformCode); + return code->slot(Offsets::ExecutableCodePlatformCode); } - void executableCodePlatformCode_put_(HeapObject *code, Object *platformCode) { - code->slot(Offsets::ExecutableCodePlatformCode) = platformCode; + void executableCodePlatformCode_put_(HeapObject *code, PlatformCode *platformCode) { + auto smi = SmallInteger::smallpointerFrom((void*)platformCode); + code->slot(Offsets::ExecutableCodePlatformCode) = (Object*)smi; } HeapObject* executableCodeCompiledCode_(HeapObject *code) { return code->slot(Offsets::ExecutableCodeCompiledCode)->asHeapObject(); } - std::vector* executableCodeWork_(HeapObject *code) { - return (std::vector *)(code->slot(Offsets::ExecutableCodePlatformCode)); + PlatformCode* executableCodeWork_(HeapObject *code) { + auto slot = this->executableCodePlatformCode_(code); + return (PlatformCode*)slot->asSmallInteger()->asObject(); } - void executableCodeCompiledCode_put_(HeapObject *code, Object *compiledCode) { - code->slot(Offsets::ExecutableCodeCompiledCode) = compiledCode; + void executableCodeCompiledCode_put_(HeapObject *code, HeapObject *compiledCode) { + code->slot(Offsets::ExecutableCodeCompiledCode) = (Object*)compiledCode; } HeapObject* blockMethod_(HeapObject* block) { @@ -315,12 +324,17 @@ class Runtime { return ((this->methodFlags(method) >> 25) & 0x3F); } + int methodNeedsEnviornment_(HeapObject *method) { + return this->methodFlags(method) & MethodFlags::MethodNeedsEnvironment; + } + HeapObject* methodExecutableCode_(HeapObject *method) { + return method->slot(Offsets::CompiledCodeExecutableCode)->asHeapObject(); } - void methodExecutableCode_put_(HeapObject *method, Object *anObject) { - method->slot(Offsets::CompiledCodeExecutableCode) = anObject; + void methodExecutableCode_put_(HeapObject *method, HeapObject *executableCode) { + method->slot(Offsets::CompiledCodeExecutableCode) = (Object*)executableCode; } HeapObject* methodExtensionModule_(HeapObject *method) { @@ -365,6 +379,10 @@ class Runtime { return module->slot(Offsets::ModuleNamespace)->asHeapObject(); } + HeapObject* moduleName_(HeapObject *module) { + return module->slot(Offsets::ModuleName)->asHeapObject(); + } + HeapObject* classModule_(HeapObject *class_) { return class_->slot(Offsets::ClassModule)->asHeapObject(); } @@ -439,6 +457,26 @@ class Runtime { return this->speciesInstanceBehavior_(superclass); } + HeapObject* processStack_(HeapObject *process) { + return process->slot(Offsets::ProcessNativeStack)->asHeapObject(); + } + + bool processStackIsValid_(HeapObject *process) { + return process->slot(Offsets::ProcessTopContext) != (Object*)KnownObjects::nil; + } + + HeapObject* processVMStackProcess_(HeapObject * processVMStack) { + return processVMStack->slot(Offsets::ProcessVMStackProcess)->asHeapObject(); + } + + uintptr_t processVMStackSP_(HeapObject * processVMStack) { + return processVMStack->slot(Offsets::ProcessVMStackSP)->asSmallInteger()->asNative(); + } + + uintptr_t processVMStackBP_(HeapObject * processVMStack) { + return processVMStack->slot(Offsets::ProcessVMStackBP)->asSmallInteger()->asNative(); + } + void initializeKernelObjects() { this->_falseObj = _kernel->_exports["false"]; @@ -458,12 +496,13 @@ class Runtime { this->_closureClass = _kernel->_exports["Closure"]; this->_closureInstSize = this->speciesInstanceSize_(this->_closureClass); this->_behaviorClass = _kernel->_exports["Behavior"]; + this->_ephemeronClass = _kernel->_exports["Ephemeron"]; + this->_processStackClass = _kernel->_exports["ProcessVMStack"]; this->_symbolTable = _kernel->_exports["SymbolTable"]; this->_smallIntegerBehavior = this->speciesInstanceBehavior_(_smallIntegerClass); } - HeapObject *_falseObj; HeapObject *_trueObj; HeapObject *_nilObj; @@ -481,11 +520,26 @@ class Runtime { HeapObject *_closureClass; int _closureInstSize; HeapObject *_behaviorClass; + HeapObject *_ephemeronClass; + HeapObject *_processStackClass; HeapObject *_symbolTable; HeapObject *_smallIntegerBehavior; + + // API and state needed to alloc and free GCedRefs + + uintptr_t assignGCedRefIndex(); + void registerGCedRef_(GCedRef *gcedRef); + GCedRef* createGCedRef_(HeapObject * object); + void releaseGCedRef_(uintptr_t index); + void gcedRefsDo_(const std::function &aBlock); + + + std::vector _gcedRefs; + std::vector _freeGCedRefs; + }; } // namespace Egg -#endif // ~ _POWERTALKRUNTIME_H_ ~ +#endif // ~ _RUNTIME_H_ ~ diff --git a/runtime/cpp/Evaluator/SAbstractMessage.h b/runtime/cpp/Evaluator/SAbstractMessage.h index 5604e1a5..c48d1fa4 100644 --- a/runtime/cpp/Evaluator/SAbstractMessage.h +++ b/runtime/cpp/Evaluator/SAbstractMessage.h @@ -5,13 +5,14 @@ #include "SExpression.h" #include "SExpressionVisitor.h" #include "../HeapObject.h" +#include "GCedRef.h" namespace Egg { class SAbstractMessage : public SExpression { - HeapObject* _selector; + GCedRef _selector; std::vector _arguments; - std::vector _cache; + std::vector _cache; public: using UndermessagePointer = Object* (Evaluator::*)(Object *, std::vector &args); @@ -19,7 +20,8 @@ class SAbstractMessage : public SExpression { // this is a hack to make C++ type system stop crying struct UndermessageWrapper { public: - UndermessageWrapper(UndermessagePointer ptr) : _undermessage(ptr) {} + UndermessageWrapper(UndermessagePointer ptr) : + _undermessage(ptr) {} UndermessagePointer _undermessage; }; @@ -42,12 +44,12 @@ class SAbstractMessage : public SExpression { } void cache_when_(HeapObject* anSCompiledMethod, HeapObject* type) { - _cache.push_back(type); - _cache.push_back(anSCompiledMethod); + _cache.push_back(new GCedRef(type)); + _cache.push_back(new GCedRef(anSCompiledMethod)); } void cacheUndermessage_(UndermessagePointer anUndermessage) { - _cache.push_back(reinterpret_cast(new UndermessageWrapper(anUndermessage))); + _cache.push_back(reinterpret_cast(new UndermessageWrapper(anUndermessage))); } UndermessagePointer cachedUndermessage() const { @@ -66,9 +68,9 @@ class SAbstractMessage : public SExpression { HeapObject* methodFor_(HeapObject *behavior) const { for (size_t i = 0; i < _cache.size(); i += 2) { - HeapObject *cached = _cache[i]; - if (cached == behavior) { - return _cache[i + 1]; + GCedRef *cached = _cache[i]; + if (cached->get() == behavior) { + return _cache[i + 1]->get(); } } @@ -79,16 +81,12 @@ class SAbstractMessage : public SExpression { void registerCacheWith_(Runtime* runtime) { if (_cache.empty()) { - runtime->registerCache_for_(this, _selector); + runtime->registerCache_for_(this, _selector.get()); } } - HeapObject* selector() const { - return _selector; - } - - void selector_(HeapObject* aSymbol) { - _selector = aSymbol; + HeapObject* selector() { + return _selector.get(); } }; diff --git a/runtime/cpp/Evaluator/SArgumentBinding.h b/runtime/cpp/Evaluator/SArgumentBinding.h index d03567bf..6c8ea4b1 100644 --- a/runtime/cpp/Evaluator/SArgumentBinding.h +++ b/runtime/cpp/Evaluator/SArgumentBinding.h @@ -16,7 +16,7 @@ class SArgumentBinding : public SLocalBinding { ASSERT(false); } - bool isArgument() const { + bool isArgument() const override { return true; } diff --git a/runtime/cpp/Evaluator/SCascadeMessage.h b/runtime/cpp/Evaluator/SCascadeMessage.h index bcd59618..7fd5c8f4 100644 --- a/runtime/cpp/Evaluator/SCascadeMessage.h +++ b/runtime/cpp/Evaluator/SCascadeMessage.h @@ -33,7 +33,7 @@ class SCascadeMessage : public SAbstractMessage { return true; } - SExpression* receiver() { + SExpression* receiver() override { return _cascade ? _cascade->receiver() : nullptr; } diff --git a/runtime/cpp/Evaluator/SExpressionLinearizer.cpp b/runtime/cpp/Evaluator/SExpressionLinearizer.cpp index ced4d719..3ce888b6 100644 --- a/runtime/cpp/Evaluator/SExpressionLinearizer.cpp +++ b/runtime/cpp/Evaluator/SExpressionLinearizer.cpp @@ -364,7 +364,7 @@ void SExpressionLinearizer::pushR() { } void SExpressionLinearizer::reset() { - this->_operations = new std::vector; + this->_operations = newPlatformCode(); this->_inBlock = false; } @@ -436,7 +436,7 @@ void SExpressionLinearizer::visitBlock(SBlock *anSBlock) { auto prevStackTop = _stackTop; this->_stackTop = _runtime->blockTempCount_(anSBlock->compiledCode()); this->_inBlock = true; - this->_operations = new std::vector; + this->_operations = newPlatformCode(); auto statements = anSBlock->statements(); for (auto node : statements) { node->acceptVisitor_(this); @@ -451,8 +451,8 @@ void SExpressionLinearizer::visitBlock(SBlock *anSBlock) { if (!anSBlock->isInlined()) { - auto code = _runtime->newExecutableCodeFor_with_(anSBlock->_compiledCode, reinterpret_cast(this->_operations)); - _runtime->blockExecutableCode_put_(anSBlock->compiledCode(), (Object*)code); + auto code = _runtime->newExecutableCodeFor_with_(anSBlock->_compiledCode, this->_operations); + _runtime->blockExecutableCode_put_(anSBlock->compiledCode(), code); } this->_stackTop = prevStackTop; diff --git a/runtime/cpp/Evaluator/SExpressionLinearizer.h b/runtime/cpp/Evaluator/SExpressionLinearizer.h index 52d249e7..7fd4b091 100644 --- a/runtime/cpp/Evaluator/SExpressionLinearizer.h +++ b/runtime/cpp/Evaluator/SExpressionLinearizer.h @@ -5,10 +5,11 @@ #include #include "../HeapObject.h" #include "SExpressionVisitor.h" +#include "Runtime.h" +#include "PlatformCode.h" namespace Egg { -class Runtime; class Evaluator; class SExpressionLinearizer : public SExpressionVisitor { @@ -17,7 +18,7 @@ class SExpressionLinearizer : public SExpressionVisitor { bool _inBlock; bool _dropsArguments; size_t _stackTop; - std::vector *_operations; + PlatformCode *_operations; using PrimitivePointer = Object* (Evaluator::*)(); std::map _primitives; @@ -58,7 +59,7 @@ class SExpressionLinearizer : public SExpressionVisitor { this->_orNot = nullptr; } - std::vector* operations() { + PlatformCode* operations() { return this->_operations; } diff --git a/runtime/cpp/Evaluator/SMessage.h b/runtime/cpp/Evaluator/SMessage.h index bfcf1b8d..3fedf698 100644 --- a/runtime/cpp/Evaluator/SMessage.h +++ b/runtime/cpp/Evaluator/SMessage.h @@ -22,7 +22,7 @@ class SMessage : public SAbstractMessage { } bool isInlined() const { return _inlined; } - SExpression* receiver () { return _receiver; } + SExpression* receiver () override { return _receiver; } }; } // namespace Egg diff --git a/runtime/cpp/GCHeap.cpp b/runtime/cpp/GCHeap.cpp deleted file mode 100644 index 2d692590..00000000 --- a/runtime/cpp/GCHeap.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "GCHeap.h" -#include "KnownObjects.h" - -#include - -using namespace Egg; - -Egg::GCHeap::GCHeap() -{ - eden = this->addNewSpaceSized(16*MB); -} - -Egg::GCHeap::~GCHeap() -{ - for (auto &space : spaces) - delete space; -} - -GCSpace *GCHeap::addSpace(GCSpace *space) -{ - spaces.push_back(space); - return space; -} - -GCSpace *GCHeap::addNewSpaceSized(int size) { - auto space = new GCSpace(size); - return this->addSpace(space); -} - -uintptr_t GCHeap::allocate(uint32_t size) { - auto result = eden->allocate(size); - if (result) - return result; - - if (size > LargeThreshold) - return this->allocateLarge(size); - - if (!GC_CRITICAL) - this->collectIfTime(); - - return this->allocateCommitting(size); -} - -uintptr_t GCHeap::allocateLarge(uint32_t size) { - auto space = this->addNewSpaceSized(size); - return space->allocate(size); -} - -uintptr_t GCHeap::allocateCommitting(uint32_t size) -{ - auto result = eden->allocate(size); - if (result) return result; - - // should allocate committing in old here - - error("Out of space. Fix me: add an old zone"); - return (uintptr_t)nullptr; -} - -HeapObject* GCHeap::allocateSlots_(uint32_t size) { - bool small = size <= HeapObject::MAX_SMALL_SIZE; - auto headerSize = small ? 8 : 16; - auto totalSize = headerSize + size * sizeof(uintptr_t); - auto buffer = this->allocate(totalSize); - std::memset((void*)buffer, 0, headerSize); - HeapObject *result; - - if (small) - { - result = ((HeapObject::SmallHeader*)buffer)->object(); - result->beSmall(); - result->smallSize((uint8_t)size); - } - else - { - result = ((HeapObject::LargeHeader*)buffer)->object(); - result->beLarge(); - result->largeSize(size); - } - uintptr_t *end = ((uintptr_t*)result) + size; - - std::fill((uintptr_t*)result, (uintptr_t*)end, (uintptr_t)KnownObjects::nil); - - return result; -} - -HeapObject* GCHeap::allocateBytes_(uint32_t size) -{ - bool small = size <= HeapObject::MAX_SMALL_SIZE; - auto headerSize = small ? 8 : 16; - auto totalSize = headerSize + align(size, sizeof(uintptr_t)); - auto buffer = this->allocate(totalSize); - std::memset((void*)buffer, 0, totalSize); - HeapObject *result; - - if (small) - { - result = ((HeapObject::SmallHeader*)buffer)->object(); - result->beSmall(); - result->smallSize((uint8_t)size); - } - else - { - result = ((HeapObject::LargeHeader*)buffer)->object(); - result->beLarge(); - result->largeSize(size); - } - - result->beBytes(); - result->beArrayed(); - - return result; -} - -void GCHeap::collectIfTime() -{ - eden->commitMemory(256*KB); - bool success = eden->increaseSoftLimit_(256 * KB); - if (success) return; - - this->collectYoung(); - - //if (fullCollector->reachedCountdown()) - // this->collectOld(); -} - -void GCHeap::collectYoung() -{ - error("Fixme: Collect young hasn't been implemented yet"); -} diff --git a/runtime/cpp/GCHeap.h b/runtime/cpp/GCHeap.h deleted file mode 100644 index ea383b70..00000000 --- a/runtime/cpp/GCHeap.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _GCHEAP_H -#define _GCHEAP_H - -#include "GCSpace.h" -#include "Memory.h" -#include - -namespace Egg { - -class GCHeap { - GCSpace *eden, *from, *to; - const int KB = 1024; - const int MB = 1024 * KB; - const int LargeThreshold = 64 * KB; -public: - std::vector spaces; - GCHeap(); - ~GCHeap(); - - GCSpace* addSpace(GCSpace *space); - GCSpace* addNewSpaceSized(int size); - - uintptr_t allocate(uint32_t size); - uintptr_t allocateLarge(uint32_t size); - uintptr_t allocateCommitting(uint32_t size); - - void collectIfTime(); - void collectYoung(); - - HeapObject* allocateSlots_(uint32_t size); - HeapObject* allocateBytes_(uint32_t size); - -}; - -class GCHeapVector { - -}; - -} // namespace Egg - -#endif // ~ _GCHEAP_H ~ diff --git a/runtime/cpp/GCSpace.cpp b/runtime/cpp/GCSpace.cpp deleted file mode 100644 index dfd09531..00000000 --- a/runtime/cpp/GCSpace.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "GCSpace.h" - -#include - -using namespace Egg; - -Egg::GCSpace::GCSpace(int size) -{ - _base = Egg::ReserveMemory(0, size); - if (!_base) - error("Failed to reserve memory."); - - _next = _softLimit = _committedLimit = _base; - _reservedLimit = _base + size; - -} - -Egg::GCSpace::~GCSpace() -{ - if (_base) - Egg::FreeMemory(_base, _reservedLimit - _base); -} - -uintptr_t GCSpace::reservedSize() -{ - return _reservedLimit - _base; -} - -void GCSpace::commitMemory(uint32_t delta) -{ - auto newLimit = std::min(_committedLimit + delta, _reservedLimit); - Egg::CommitMemory(_committedLimit, newLimit - _base); - _committedLimit = newLimit; -} - -uintptr_t GCSpace::allocate(uint32_t size) { - auto result = _next; - auto end = _next + size; - if (end < _softLimit) - { - _next = end; - return result; - } - - return 0; -} - -bool Egg::GCSpace::increaseSoftLimit_(uint32_t delta) -{ - int32_t available = _committedLimit - _softLimit; - if (available < 0) return false; - - _softLimit = _softLimit + std::min(delta, (uint32_t)available); - return true; -} diff --git a/runtime/cpp/GCSpace.h b/runtime/cpp/GCSpace.h deleted file mode 100644 index 83037032..00000000 --- a/runtime/cpp/GCSpace.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef _GCSPACE_H_ -#define _GCSPACE_H_ - -#include "HeapObject.h" -#include "Memory.h" - -namespace Egg { - -class GCSpace { -public: - uintptr_t _base, _next, _softLimit, _committedLimit, _reservedLimit; - GCSpace(int size = 4096 * 8); - ~GCSpace(); - - uintptr_t reservedSize(); - void commitMemory(uint32_t delta); - uintptr_t allocate(uint32_t size); - bool increaseSoftLimit_(uint32_t delta); - -}; - -class GCSpaceVector { - -}; - -} // namespace Egg - -#endif // ~ _GCSPACE_H_ ~ diff --git a/runtime/cpp/GCedRef.cpp b/runtime/cpp/GCedRef.cpp new file mode 100644 index 00000000..d622c5c5 --- /dev/null +++ b/runtime/cpp/GCedRef.cpp @@ -0,0 +1,43 @@ + +#include "GCedRef.h" +#include "Evaluator/Runtime.h" +#include "HeapObject.h" + +using namespace Egg; + +class Runtime; + +GCedRef::GCedRef(HeapObject* object, uintptr_t index) + : _object(object), _index(index) +{} + +GCedRef::GCedRef(HeapObject* object) + : _object(object), _index(debugRuntime->assignGCedRefIndex()) +{ + debugRuntime->registerGCedRef_(this); +} +//GCedRef::GCedRef(GCedRef &other) +// : _runtime(other._runtime), _object(other.get()), index(_runtime->assignRef(other.get())) +//{} + +GCedRef::~GCedRef() { + debugRuntime->releaseGCedRef_(_index); +} + +uintptr_t GCedRef::Comparator::hash(const GCedRef *obj) const { + return debugRuntime->hashFor_((Object*)obj->get()); +} + +uintptr_t GCedRef::Comparator::hash(const HeapObject *obj) const { + return debugRuntime->hashFor_((Object*)obj); +} + +HeapObject* GCedRef::get() +{ + return _object; +} + +uintptr_t GCedRef::index() { + return _index; +} + diff --git a/runtime/cpp/GCedRef.h b/runtime/cpp/GCedRef.h new file mode 100644 index 00000000..3e0c0278 --- /dev/null +++ b/runtime/cpp/GCedRef.h @@ -0,0 +1,88 @@ + +#ifndef _GCEDREF_H_ +#define _GCEDREF_H_ + +#include "Egg.h" + +namespace Egg { + +class Runtime; + +class GCedRef +{ +public: + /* Create a new NULL reference. */ + GCedRef(HeapObject *object, uintptr_t index); + GCedRef(HeapObject *object); + + /* Create a new reference from another reference */ + GCedRef(GCedRef &other); + + ~GCedRef(); + + HeapObject *get(); + const HeapObject* get() const { return _object; }; + uintptr_t index(); + + HeapObject **getRaw() { return &_object; } + + // Comparator for GCedRef* that allows comparisons with HeapObject* + struct Comparator { + using is_transparent = void; + uintptr_t hash(const GCedRef *obj) const; + uintptr_t hash(const HeapObject *obj) const; + + bool operator()(const GCedRef* lhs, const GCedRef* rhs) const { + return hash(lhs) <= hash(rhs); + } + + bool operator()(const GCedRef* lhs, const HeapObject* rhs) const { + return hash(lhs) <= hash(rhs); + } + + bool operator()(const HeapObject* lhs, const GCedRef* rhs) const { + return hash(lhs) <= hash(rhs); + } + + // comparisons for pairs of GCedRef* + bool operator()(const std::pair& lhs, const std::pair& rhs) const { + auto lhs1 = hash(lhs.first), lhs2 = hash(lhs.second); + auto rhs1 = hash(rhs.first), rhs2 = hash(rhs.second); + return std::tie(lhs1, lhs2) <= std::tie(rhs1, rhs2); + } + + // Compare two std::pair + bool operator()(const std::pair& lhs, const std::pair& rhs) const { + auto lhs1 = hash(lhs.first), lhs2 = hash(lhs.second); + auto rhs1 = hash(rhs.first), rhs2 = hash(rhs.second); + return std::tie(lhs1, lhs2) <= std::tie(rhs1, rhs2); + } + + // Compare pairs of HeapObject with pairs of GCedRef + bool operator()(const std::pair& lhs, + const std::pair& rhs) const { + auto lhs1 = hash(lhs.first), lhs2 = hash(lhs.second); + auto rhs1 = hash(rhs.first), rhs2 = hash(rhs.second); + return std::tie(lhs1, lhs2) <= std::tie(rhs1, rhs2); + } + + bool operator()(const std::pair& lhs, + const std::pair& rhs) const { + auto lhs1 = hash(lhs.first), lhs2 = hash(lhs.second); + auto rhs1 = hash(rhs.first), rhs2 = hash(rhs.second); + return std::tie(lhs1, lhs2) <= std::tie(rhs1, rhs2); + } + }; + +private: + GCedRef(const GCedRef &other) = delete; // not allowed, to prevent aliasing + GCedRef& operator=(const GCedRef &other) = delete; + + HeapObject *_object; + uintptr_t _index; + //static Runtime *_runtime; +}; + +} + +#endif // ~ _GCEDREF_H_ ~ diff --git a/runtime/cpp/HeapObject.cpp b/runtime/cpp/HeapObject.cpp index c2b2d53b..31eec620 100644 --- a/runtime/cpp/HeapObject.cpp +++ b/runtime/cpp/HeapObject.cpp @@ -358,3 +358,12 @@ std::string Egg::HeapObject::printString() { return debugRuntime->print_(this); } + +void HeapObject::copyFrom_headerSize_bodySize_(HeapObject *object, uintptr_t headerSize, uintptr_t bodySize) +{ + auto srcbase = ((uintptr_t)object) - headerSize; + auto srcend = ((uintptr_t)object) + bodySize; + auto dstbase = ((uintptr_t)this) - headerSize; + + std::copy((uintptr_t*)srcbase, (uintptr_t*)srcend, (uintptr_t*)dstbase); +} diff --git a/runtime/cpp/HeapObject.h b/runtime/cpp/HeapObject.h index 60a404f7..e5d2a877 100644 --- a/runtime/cpp/HeapObject.h +++ b/runtime/cpp/HeapObject.h @@ -231,6 +231,9 @@ struct HeapObject uint64_t& uint64offset(const uint32_t subscript); /// Return a 64-bit uint of this object at 0-based `subscript` uint64_t& unsignedLargeAt_(uint32_t index) { return uint64offset((index - 1) * 8); }; // 1-based index for compatibility reasons + /// ~ copying ~ + void copyFrom_headerSize_bodySize_(HeapObject *object, uintptr_t headerSize, uintptr_t bodySize); + /// ~ object bytes ~ void replaceBytesFrom_to_with_startingAt_( const uintptr_t from, diff --git a/runtime/cpp/ImageSegment.cpp b/runtime/cpp/ImageSegment.cpp index 66c937b6..e86c8348 100644 --- a/runtime/cpp/ImageSegment.cpp +++ b/runtime/cpp/ImageSegment.cpp @@ -14,7 +14,8 @@ #include "Util.h" #include "ImageSegment.h" -#include "Memory.h" + +#include "Allocator/Memory.h" namespace Egg { @@ -53,7 +54,10 @@ ImageSegment::load(std::istream *data) void ImageSegment::fixPointerSlots(const std::vector& imports) { intptr_t delta = this->_currentBase - this->header.baseAddress; - uintptr_t oldBehaviorBase = this->header.baseAddress & (-1 << 32); // discards lower 32 bits + + constexpr auto mask = static_cast(-1LL << 32); // discards lower 32 bits + uintptr_t oldBehaviorBase = this->header.baseAddress & mask; + auto spaceStart = this->spaceStart(); auto current = ((HeapObject::ObjectHeader*)spaceStart)->object(); auto end = (HeapObject*)this->spaceEnd(); @@ -83,6 +87,8 @@ void ImageSegment::fixPointerSlots(const std::vector& imports) } current = current->nextObject(); } + + header.module = relocatedAddress_(header.module); } uintptr_t ImageSegment::spaceStart() @@ -151,7 +157,7 @@ void ImageSegment::readImportStrings(std::istream *data) data->read((char*)&stringSize, sizeof(stringSize)); if (stringSize > bufferSize) { - delete buffer; + delete[] buffer; bufferSize = std::max(stringSize, bufferSize * 2); buffer = new char[bufferSize]; } @@ -159,7 +165,7 @@ void ImageSegment::readImportStrings(std::istream *data) _importStrings.push_back(std::string(buffer, stringSize)); } - delete buffer; + delete[] buffer; } void ImageSegment::readImportDescriptors(std::istream *data) @@ -213,4 +219,4 @@ void ImageSegment::readExports(std::istream *data) } -} // namespace Egg \ No newline at end of file +} // namespace Egg diff --git a/runtime/cpp/KnownConstants.h b/runtime/cpp/KnownConstants.h index d56275a5..7cb889a8 100644 --- a/runtime/cpp/KnownConstants.h +++ b/runtime/cpp/KnownConstants.h @@ -16,6 +16,7 @@ extern uintptr_t BEHAVIOR_ADDRESS_SPACE; namespace Egg { + enum BlockFlags { CapturesHome = 0x800000, CapturesSelf = 0x400000 @@ -33,6 +34,7 @@ enum MethodFlags { MethodArgCount = 0x3F, MethodTempCount = 0x1FE000, MethodTempCountShift = 13, + MethodNeedsEnvironment = 0x400000, MethodIsExtension = 0x80000000 }; @@ -86,6 +88,15 @@ enum Offsets { ExecutableCodePlatformCode = 0, ExecutableCodeCompiledCode = 1, + // inst vars of Process + ProcessNativeStack = 1, + ProcessTopContext = 2, + + // inst vars of ProcessVMStack (the VM's internal stack object for processes) + ProcessVMStackProcess = 0, + ProcessVMStackSP = 1, + ProcessVMStackBP = 2, + FFI_uint8 = 0, FFI_sint8 = 1, FFI_uint16 = 2, diff --git a/runtime/cpp/Posix/Memory.cpp b/runtime/cpp/Posix/Memory.cpp index 379d8961..00191d1d 100644 --- a/runtime/cpp/Posix/Memory.cpp +++ b/runtime/cpp/Posix/Memory.cpp @@ -11,9 +11,9 @@ #include -#include "../Memory.h" -#include "../Egg.h" -#include "../KnownConstants.h" +#include "Allocator/Memory.h" +#include "Egg.h" +#include "KnownConstants.h" using namespace Egg; @@ -30,6 +30,7 @@ namespace Egg { } uintptr_t Egg::ReserveAligned4GB() { + uintptr_t size = 4L * 1024 * 1024 * 1024; // 4GB uintptr_t alignment = 4L * 1024 * 1024 * 1024; // 4GB alignment uintptr_t total_size = size + alignment; @@ -70,6 +71,11 @@ void Egg::InitializeMemory() } +void Egg::aligned_free(void* mem) +{ + std::free(mem); +} + uintptr_t Egg::pagealign(uintptr_t addr) { static int pagesize = -1; @@ -118,6 +124,13 @@ void Egg::CommitMemory(uintptr_t base, uintptr_t size) std::memset((char*)base, 0, size); } +void Egg::DecommitMemory(uintptr_t base, uintptr_t size) +{ + if (madvise((void*)base, size, MADV_DONTNEED) != 0) { + error("Failed to decommit memory."); + } +} + void Egg::FreeMemory(uintptr_t base, uintptr_t size) { munmap((void*)base, size); diff --git a/runtime/cpp/SmallInteger.h b/runtime/cpp/SmallInteger.h index 5b249257..1864c22f 100644 --- a/runtime/cpp/SmallInteger.h +++ b/runtime/cpp/SmallInteger.h @@ -35,14 +35,33 @@ struct SmallInteger { return (SmallInteger *)(((uintptr_t)intVal << 1) | 1); } + /** + * Returns a `small pointer` from an aligned address: it is a way to encode + * a pointer as a small integer (marking int bit), so that GC doesn't mess + * with it when tracing. It only requires the pointer is aligned to pointer + * size (rightmost bits are 0). Used for example to encode native code addresses. + */ + static SmallInteger* + smallpointerFrom(void* alignedBuffer) + { + ASSERT( ((uintptr_t)alignedBuffer & 1) == 0 ); + return (SmallInteger *)(((uintptr_t)alignedBuffer) | 1); + } + bool isSmallInteger(); - intptr_t asNative(); /// Assuming `this` encodes a SmallInteger, decode its - /// (signed) integer value + /** + * Assuming `this` encodes a SmallInteger, decode its + * (signed) integer value + **/ + intptr_t asNative(); + /** + * Assuming `this` encodes a pointer stored as a SmallInteger, + * return the address of the original pointer (by clearing last bit) + **/ template - T asObject() /// Assuming `this` encodes a pointer stored as a SmallInteger, - /// return the address of the original pointer + T asObject() { // ASSERT(this->object()->isSmallInteger()); diff --git a/runtime/cpp/StackGCedRef.cpp b/runtime/cpp/StackGCedRef.cpp new file mode 100644 index 00000000..24450ad9 --- /dev/null +++ b/runtime/cpp/StackGCedRef.cpp @@ -0,0 +1,27 @@ + +#include "StackGCedRef.h" +#include "Evaluator/EvaluationContext.h" + +using namespace Egg; +StackGCedRef::StackGCedRef(EvaluationContext* context, Object* object) : + _context(context) +{ + _context->push_(object); + _index = _context->stackPointer(); +} + +StackGCedRef::~StackGCedRef() +{ + ASSERT(_context->stackPointer() == _index); + _context->pop(); +} + +Object* StackGCedRef::asObject() const +{ + return _context->stackAt_(_index); +} + +HeapObject* StackGCedRef::asHeapObject() +{ + return asObject()->asHeapObject(); +} \ No newline at end of file diff --git a/runtime/cpp/StackGCedRef.h b/runtime/cpp/StackGCedRef.h new file mode 100644 index 00000000..d24c3dec --- /dev/null +++ b/runtime/cpp/StackGCedRef.h @@ -0,0 +1,25 @@ + +#ifndef _STACKGCEDREF_H_ +#define _STACKGCEDREF_H_ + +#include "Egg.h" + +namespace Egg { + +class EvaluationContext; + +class StackGCedRef { + EvaluationContext* _context; + uintptr_t _index; + + public: + StackGCedRef(EvaluationContext* context, Object* object); + ~StackGCedRef(); + + Object* asObject() const; + HeapObject* asHeapObject(); +}; + +} + +#endif // ~ _STACKGCEDREF_H_ ~ diff --git a/runtime/cpp/Windows/Memory.cpp b/runtime/cpp/Windows/Memory.cpp index 033c76b4..3215b94a 100644 --- a/runtime/cpp/Windows/Memory.cpp +++ b/runtime/cpp/Windows/Memory.cpp @@ -7,9 +7,9 @@ #include #include -#include "../Memory.h" -#include "../Egg.h" -#include "../KnownConstants.h" +#include "Allocator/Memory.h" +#include "Egg.h" +#include "KnownConstants.h" using namespace Egg; @@ -41,6 +41,11 @@ void Egg::InitializeMemory() } +void Egg::aligned_free(void* mem) +{ + _aligned_free(mem); +} + uintptr_t Egg::pagealign(uintptr_t addr) { return align(addr, Egg::page_alignment); @@ -85,6 +90,13 @@ void Egg::CommitMemory(uintptr_t base, uintptr_t size) std::memset((char*)base, 0, size); } +void Egg::DecommitMemory(uintptr_t base, uintptr_t size) +{ + if (!VirtualFree((void*)base, size, MEM_DECOMMIT)) { + error("Failed to decommit memory."); + } +} + void Egg::FreeMemory(uintptr_t base, uintptr_t size) { VirtualFree((void*)base, 0, MEM_RELEASE);