1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-09 09:34:49 +09:00

added gen num and ForEachObjectRef with range,

ForEachPage, barrier, card size and initialization fixes
This commit is contained in:
vsadov 2020-09-25 12:15:35 -07:00
parent 01748e2d65
commit c61cff6826
21 changed files with 515 additions and 132 deletions

View file

@ -311,7 +311,7 @@ FORCEINLINE void InlinedMemmoveGCRefsHelper(void *dest, const void *src, size_t
if (len >= sizeof(size_t))
{
CheckEscapeSatoriRange(dest, (void*)src, len);
CheckEscapeSatoriRange(dest, (size_t)src, len);
}
// To be able to copy forwards, the destination buffer cannot start inside the source buffer

View file

@ -96,6 +96,30 @@ __forceinline T Interlocked::CompareExchange(T volatile *destination, T exchange
#endif
}
template <>
__forceinline size_t Interlocked::CompareExchange<size_t>(size_t volatile * destination, size_t exchange, size_t comparand)
{
#ifdef _MSC_VER
return _InterlockedCompareExchange64((volatile long long*)destination, exchange, comparand);
#else
T result = __sync_val_compare_and_swap(destination, comparand, exchange);
ArmInterlockedOperationBarrier();
return result;
#endif
}
template <>
__forceinline uint8_t Interlocked::CompareExchange<uint8_t>(uint8_t volatile* destination, uint8_t exchange, uint8_t comparand)
{
#ifdef _MSC_VER
return _InterlockedCompareExchange8((char*)destination, exchange, comparand);
#else
T result = __sync_val_compare_and_swap(destination, comparand, exchange);
ArmInterlockedOperationBarrier();
return result;
#endif
}
// Perform an atomic addition of two 32-bit values and return the original value of the addend.
// Parameters:
// addend - variable to be added to

View file

@ -161,13 +161,13 @@ SatoriObject* SatoriAllocator::AllocRegular(SatoriAllocationContext* context, si
size_t allocRemaining = region->AllocRemaining();
if (moreSpace <= allocRemaining)
{
bool isZeroing = true;
if (isZeroing && moreSpace < Satori::MIN_REGULAR_ALLOC)
bool zeroInitialize = !(flags & GC_ALLOC_ZEROING_OPTIONAL);
if (zeroInitialize && moreSpace < Satori::MIN_REGULAR_ALLOC)
{
moreSpace = min(allocRemaining, Satori::MIN_REGULAR_ALLOC);
}
if (region->Allocate(moreSpace, isZeroing))
if (region->Allocate(moreSpace, zeroInitialize))
{
context->alloc_bytes += moreSpace;
context->alloc_limit += moreSpace;
@ -247,7 +247,8 @@ SatoriObject* SatoriAllocator::AllocLarge(SatoriAllocationContext* context, size
size_t allocRemaining = region->AllocRemaining();
if (allocRemaining >= size)
{
SatoriObject* result = SatoriObject::At(region->Allocate(size, true));
bool zeroInitialize = !(flags & GC_ALLOC_ZEROING_OPTIONAL);
SatoriObject* result = SatoriObject::At(region->Allocate(size, zeroInitialize));
if (result)
{
result->CleanSyncBlock();
@ -286,7 +287,6 @@ SatoriObject* SatoriAllocator::AllocLarge(SatoriAllocationContext* context, size
}
_ASSERTE(region->NothingMarked());
region->m_ownerThreadTag = SatoriUtil::GetCurrentThreadTag();
context->LargeRegion() = region;
}
}
@ -302,7 +302,8 @@ SatoriObject* SatoriAllocator::AllocHuge(SatoriAllocationContext* context, size_
return nullptr;
}
SatoriObject* result = SatoriObject::At(region->AllocateHuge(size, true));
bool zeroInitialize = !(flags & GC_ALLOC_ZEROING_OPTIONAL);
SatoriObject* result = SatoriObject::At(region->AllocateHuge(size, zeroInitialize));
if (result)
{
result->CleanSyncBlock();
@ -318,8 +319,9 @@ SatoriObject* SatoriAllocator::AllocHuge(SatoriAllocationContext* context, size_
// we do not want to keep huge region in allocator for simplicity,
// but can't drop it to recycler yet since the object has no MethodTable.
// we will leave the region unowned and send it to recycler later in PublishObject.
// we will make the region gen1 and send it to recycler later in PublishObject.
region->StopAllocating(/* allocPtr */ 0);
region->SetGeneration(1);
return result;
}
@ -345,7 +347,7 @@ bool SatoriAllocator::AddMoreMarkChunks()
while (true)
{
size_t mem = region->Allocate(Satori::MARK_CHUNK_SIZE, /*ensureZeroInited*/ false);
size_t mem = region->Allocate(Satori::MARK_CHUNK_SIZE, /*zeroInitialize*/ false);
if (!mem)
{
break;

View file

@ -127,9 +127,8 @@ int SatoriGC::WaitForFullGCComplete(int millisecondsTimeout)
unsigned SatoriGC::WhichGeneration(Object* obj)
{
//TODO: Satori update when have Gen1
SatoriObject* so = (SatoriObject*)obj;
return so->ContainingRegion()->IsThreadLocal() ? 0 : 2;
return (unsigned)so->ContainingRegion()->Generation();
}
int SatoriGC::CollectionCount(int generation, int get_bgc_fgc_coutn)
@ -379,7 +378,7 @@ void SatoriGC::PublishObject(uint8_t* obj)
// we do not retain huge regions in allocator,
// but can't drop them in recycler until object has a MethodTable.
// do that here.
if (!region->IsThreadLocal())
if (region->Generation() != 0)
{
_ASSERTE(region->Size() > Satori::REGION_SIZE_GRANULARITY);
m_heap->Recycler()->AddRegion(region);

View file

@ -16,6 +16,7 @@
#include "SatoriRegion.h"
#include "SatoriObject.h"
#include "SatoriPage.h"
#include "SatoriPage.inl"
void InitWriteBarrier(uint8_t* segmentTable, size_t highest_address)
{
@ -183,8 +184,9 @@ SatoriPage* SatoriHeap::AddLargePage(size_t minSize)
m_pageMap[i] = 1;
for (int j = 1; j < mapMarkCount; j++)
{
// TODO: VS skip-marks
m_pageMap[i + j] = 2;
DWORD log2;
BitScanReverse(&log2, j);
m_pageMap[i + j] = (uint8_t)(log2 + 2);
}
// we also need to ensure that the other thread doing the barrier,
@ -210,30 +212,6 @@ SatoriPage* SatoriHeap::AddLargePage(size_t minSize)
return nullptr;
}
//TODO: VS move into inl
inline SatoriPage* SatoriHeap::PageForAddress(size_t address)
{
size_t mapIndex = address >> Satori::PAGE_BITS;
while (m_pageMap[mapIndex] > 1)
{
mapIndex -= ((size_t)1 << (m_pageMap[mapIndex] - 2));
}
return (SatoriPage *)(mapIndex << Satori::PAGE_BITS);
}
//TODO: VS unused?
SatoriRegion* SatoriHeap::RegionForAddress(size_t address)
{
if (IsHeapAddress(address))
{
return PageForAddress(address)->RegionForAddress(address);
}
return nullptr;
}
//TODO: VS optimize this, move to inl
SatoriObject* SatoriHeap::ObjectForAddress(size_t address)
{
if (IsHeapAddress(address))
@ -243,9 +221,3 @@ SatoriObject* SatoriHeap::ObjectForAddress(size_t address)
return nullptr;
}
//TODO: VS move into inl
void SatoriHeap::SetCardForAddress(size_t address)
{
PageForAddress(address)->SetCardForAddress(address);
}

View file

@ -47,18 +47,45 @@ public:
return &m_finalizationQueue;
}
SatoriPage* PageForAddress(size_t address);
SatoriRegion* RegionForAddress(size_t address);
SatoriObject* ObjectForAddress(size_t address);
void SetCardForAddress(size_t address);
bool IsHeapAddress(size_t address)
{
size_t mapIndex = address >> Satori::PAGE_BITS;
return (unsigned int)mapIndex < (unsigned int)m_committedMapSize &&
return (unsigned int)mapIndex < (unsigned int)m_committedMapSize&&
m_pageMap[mapIndex] != 0;
}
SatoriPage* PageForAddress(size_t address)
{
size_t mapIndex = address >> Satori::PAGE_BITS;
while (m_pageMap[mapIndex] > 1)
{
mapIndex -= ((size_t)1 << (m_pageMap[mapIndex] - 2));
}
return (SatoriPage*)(mapIndex << Satori::PAGE_BITS);
}
SatoriObject* ObjectForAddress(size_t address);
template<typename F>
void ForEachPage(F& lambda)
{
size_t mapIndex = m_nextPageIndex - 1;
while (mapIndex > 0)
{
switch (m_pageMap[mapIndex])
{
case 1:
lambda((SatoriPage*)(mapIndex << Satori::PAGE_BITS));
case 0:
mapIndex--;
continue;
default:
mapIndex -= ((size_t)1 << (m_pageMap[mapIndex] - 2));
}
}
}
private:
SatoriAllocator m_allocator;
SatoriRecycler m_recycler;

View file

@ -13,6 +13,8 @@
#include "SatoriObject.inl"
#include "SatoriRegion.h"
#include "SatoriRegion.inl"
#include "SatoriPage.h"
#include "SatoriPage.inl"
MethodTable* SatoriObject::s_emptyObjectMt;
@ -80,6 +82,32 @@ SatoriObject* SatoriObject::FormatAsFreeAfterHuge(size_t location, size_t size)
return obj;
}
void SatoriObject::SetCardsForContent()
{
_ASSERTE(IsMarked());
MethodTable* mt = RawGetMethodTable();
if (mt->ContainsPointers())
{
SatoriPage* page = ContainingRegion()->m_containingPage;
// TODO: VS SetCardsForRange
for (size_t i = Start(); i < End(); i++)
{
page->SetCardForAddress(i);
}
}
if (mt->Collectible())
{
SatoriObject* o = (SatoriObject*)GCToEEInterface::GetLoaderAllocatorObjectForGC(this);
if (!o->IsMarked())
{
o->SetMarked();
o->SetCardsForContent();
}
}
}
void SatoriObject::Validate()
{
#ifdef _DEBUG

View file

@ -44,6 +44,8 @@ public:
bool IsEscapedOrPinned();
int MarkBitOffset(size_t* bitmapIndex);
void SetCardsForContent();
void EscapeCheck();
bool IsFinalizationSuppressed();
@ -62,6 +64,9 @@ public:
template<typename F>
void ForEachObjectRef(F& lambda, bool includeCollectibleAllocator = false);
template<typename F>
void ForEachObjectRef(F& lambda, size_t start, size_t end);
private:
static MethodTable* s_emptyObjectMt;
static void Initialize();

View file

@ -276,4 +276,105 @@ inline void SatoriObject::ForEachObjectRef(F& lambda, bool includeCollectibleAll
}
}
}
template<typename F>
inline void SatoriObject::ForEachObjectRef(F& lambda, size_t start, size_t end)
{
MethodTable* mt = RawGetMethodTable();
if (!mt->ContainsPointers())
{
return;
}
CGCDesc* map = CGCDesc::GetCGCDescFromMT(mt);
CGCDescSeries* cur = map->GetHighestSeries();
// GetNumSeries is actually signed.
// Negative value means the pattern repeats -cnt times such as in a case of arrays
ptrdiff_t cnt = (ptrdiff_t)map->GetNumSeries();
if (cnt >= 0)
{
CGCDescSeries* last = map->GetLowestSeries();
// series size is offset by the object size
size_t size = mt->GetBaseSize();
// object arrays are handled here too, so need to compensate for that.
if (mt->HasComponentSize())
{
size += (size_t)((ArrayBase*)this)->GetNumComponents() * sizeof(size_t);
}
do
{
size_t refPtr = (size_t)this + cur->GetSeriesOffset();
size_t refPtrStop = refPtr + size + cur->GetSeriesSize();
refPtr = max(refPtr, start);
// TODO: VS make ordinary while
if (refPtr < refPtrStop)
{
do
{
if (refPtr >= end)
{
return;
}
lambda((SatoriObject**)refPtr);
refPtr += sizeof(size_t);
} while (refPtr < refPtrStop);
}
cur--;
} while (cur >= last);
}
else
{
// repeating patern - an array of structs
ptrdiff_t componentNum = ((ArrayBase*)this)->GetNumComponents();
size_t elementSize = mt->RawGetComponentSize();
size_t refPtr = (size_t)this + cur->GetSeriesOffset();
if (refPtr < start)
{
size_t skip = (start - refPtr) / elementSize;
componentNum -= skip;
refPtr += skip * elementSize;
}
while (componentNum-- > 0)
{
for (ptrdiff_t i = 0; i > cnt; i--)
{
val_serie_item item = cur->val_serie[i];
size_t refPtrStop = refPtr + item.nptrs * sizeof(size_t);
if (refPtrStop <= start)
{
// serie does not intersect with the range
refPtr = refPtrStop;
}
else
{
refPtr = max(refPtr, start);
do
{
if (refPtr >= end)
{
return;
}
lambda((SatoriObject**)refPtr);
refPtr += sizeof(size_t);
} while (refPtr < refPtrStop);
}
refPtr += item.skip;
}
}
}
}
#endif

View file

@ -11,6 +11,7 @@
#include "../env/gcenv.os.h"
#include "SatoriPage.h"
#include "SatoriPage.inl"
#include "SatoriRegion.h"
#include "SatoriRegion.inl"
@ -24,10 +25,12 @@ SatoriPage* SatoriPage::InitializeAt(size_t address, size_t pageSize, SatoriHeap
return result;
}
// 128 bit/byte
size_t cardTableBytes = pageSize / (128 * 8);
size_t cardTableBytes = pageSize / Satori::BYTES_PER_CARD_BYTE;
// TODO: VS we do not need to commit the whole card table, we can wait until regions are used
// and track commit via card groups.
// commit size is the same as header size. We could commit more in the future.
// or commit on demand as regions are committed.
size_t commitSize = ALIGN_UP(cardTableBytes, Satori::CommitGranularity());
if (!GCToOSInterface::VirtualCommit((void*)address, commitSize))
{
@ -41,15 +44,19 @@ SatoriPage* SatoriPage::InitializeAt(size_t address, size_t pageSize, SatoriHeap
result->m_cardTableSize = (int)cardTableBytes / sizeof(size_t);
// conservatively assume the first useful card word to cover the start of the first region.
size_t cardTableStart = (result->m_firstRegion - address) / (128 * 8 * sizeof(size_t));
size_t cardTableStart = (result->m_firstRegion - address) / Satori::BYTES_PER_CARD_WORD;
// make sure the first useful card word is beyond the header.
size_t regionMapSize = pageSize >> Satori::REGION_BITS;
_ASSERTE(cardTableStart * sizeof(size_t) > offsetof(SatoriPage, m_regionMap) + regionMapSize);
result->m_cardTableStart = (int)cardTableStart;
result->m_heap = heap;
// make sure offset of m_cardsStatus is 128.
_ASSERTE(offsetof(SatoriPage, m_cardGroups) == 128);
result->m_regionMap = (uint8_t*)(address + 128 + (pageSize >> Satori::REGION_BITS));
// make sure the first useful card word is beyond the header.
_ASSERTE(result->Start() + cardTableStart * sizeof(size_t) > (size_t)(result->m_regionMap) + regionMapSize);
return result;
}
@ -68,8 +75,9 @@ void SatoriPage::RegionInitialized(SatoriRegion* region)
RegionMap()[startIndex] = 1;
for (int i = 1; i < mapCount; i++)
{
// TODO: VS skip-marks
RegionMap()[startIndex + i] = 2;
DWORD log2;
BitScanReverse(&log2, i);
RegionMap()[startIndex + i] = (uint8_t)(log2 + 2);
}
}
@ -80,8 +88,9 @@ void SatoriPage::RegionDestroyed(SatoriRegion* region)
size_t mapCount = region->Size() >> Satori::REGION_BITS;
for (int i = 0; i < mapCount; i++)
{
// TODO: VS skip-marks
RegionMap()[startIndex + i] = 0;
DWORD log2;
BitScanReverse(&log2, i);
RegionMap()[startIndex + i] = (uint8_t)(log2 + 2);
}
}

View file

@ -10,6 +10,7 @@
#include "common.h"
#include "../gc.h"
#include "SatoriUtil.h"
class SatoriHeap;
class SatoriRegion;
@ -28,59 +29,76 @@ public:
SatoriRegion* RegionForAddress(size_t address);
size_t Start()
size_t Start();
size_t End();
size_t RegionsStart();
uint8_t* RegionMap();
SatoriHeap* Heap();
void SetCardForAddress(size_t address);
bool IsClean()
{
return (size_t)this;
return VolatileLoadWithoutBarrier(&m_cardState) == 0;
}
size_t End()
void SetProcessing()
{
return m_end;
VolatileStoreWithoutBarrier(&m_cardState, 2);
}
size_t RegionsStart()
bool TrySetClean()
{
return m_firstRegion;
_ASSERTE((m_cardState >= 0) && (m_cardState <= 2));
return Interlocked::CompareExchange(&m_cardState, 0, 2) != 1;
}
uint8_t* RegionMap()
size_t CardGroupCount()
{
return m_regionMap;
return (End() - Start()) >> Satori::REGION_BITS;
}
SatoriHeap* Heap()
Satori::CardGroupState CardGroupState(size_t i)
{
return m_heap;
return (Satori::CardGroupState)m_cardGroups[i];
}
void SetCardForAddress(size_t address)
void CardGroupSetProcessing(size_t i)
{
size_t offset = address - Start();
size_t cardByteOffset = offset / (128 * 8);
m_cardGroups[i] = Satori::CardGroupState::processing;
}
_ASSERTE(cardByteOffset / 8 > m_cardTableStart);
_ASSERTE(cardByteOffset / 8 < m_cardTableSize);
void CardGroupTrySetClean(size_t i)
{
Interlocked::CompareExchange<uint8_t>(&m_cardGroups[i], Satori::CardGroupState::clean, Satori::CardGroupState::processing);
}
((uint8_t*)this)[cardByteOffset] = 0xFF;
size_t* CardsForGroup(size_t i)
{
return &m_cardTable[i * Satori::CARD_WORDS_IN_CARD_GROUP];
}
// TODO: VS dirty the region
size_t LocationForCard(void* cardPtr)
{
return Start() + ((size_t)cardPtr - Start()) * Satori::BYTES_PER_CARD_BYTE;
}
private:
union
{
// 1bit - 128 bytes
// 1byte - 1k
// 2K - 2Mb (region granularity)
// 1Mb - 1Gb
// 1bit - 64 bytes
// 1byte - 512 bytes i.e cards add ~ 0.002 overhead
// 8byte - 4k
// 4K - 2Mb (region granularity)
// 2Mb - 1Gb (region granule can store cards for 1Gb page)
// We can start card table at the beginning of the page for simplicity
// The first 2K+ cover the header, which includes the region map and the table itself
// so that space will be unused.
// The first 4K cover the card itself, so that space will be unused and we can use it for other metadata.
size_t m_cardTable[1];
// header (can be up to 2Kb for 1Gb page)
struct
{
int32_t m_cardState;
size_t m_end;
size_t m_initialCommit;
size_t m_firstRegion;
@ -92,9 +110,19 @@ private:
int m_cardTableSize;
int m_cardTableStart;
// ----- we can have a few more fields above as long as m_cardsStatus starts at offset 128.
// that can be adjusted if needed
// computed size,
// 1byte per region
// 512 bytes per 1Gb
uint8_t m_regionMap[1];
uint8_t* m_regionMap;
// computed size,
// 1byte per region
// 512 bytes per 1Gb
DECLSPEC_ALIGN(128)
uint8_t m_cardGroups[1];
};
};
};

View file

@ -0,0 +1,55 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// SatoriPage.inl
//
#ifndef __SATORI_PAGE_INL__
#define __SATORI_PAGE_INL__
#include "SatoriUtil.h"
#include "SatoriPage.h"
inline size_t SatoriPage::Start()
{
return (size_t)this;
}
inline size_t SatoriPage::End()
{
return m_end;
}
inline size_t SatoriPage::RegionsStart()
{
return m_firstRegion;
}
inline uint8_t* SatoriPage::RegionMap()
{
return m_regionMap;
}
inline SatoriHeap* SatoriPage::Heap()
{
return m_heap;
}
inline void SatoriPage::SetCardForAddress(size_t address)
{
size_t offset = address - Start();
size_t cardByteOffset = offset / Satori::BYTES_PER_CARD_BYTE;
_ASSERTE(cardByteOffset / 8 > m_cardTableStart);
_ASSERTE(cardByteOffset / 8 < m_cardTableSize);
((uint8_t*)this)[cardByteOffset] = 0xFF;
size_t cardGroupOffset = offset / Satori::REGION_SIZE_GRANULARITY;
this->m_cardGroups[cardGroupOffset] = Satori::CardGroupState::dirty;
this->m_cardState = 1;
}
#endif

View file

@ -12,6 +12,7 @@
#include "SatoriHeap.h"
#include "SatoriPage.h"
#include "SatoriPage.inl"
#include "SatoriRecycler.h"
#include "SatoriObject.h"
#include "SatoriObject.inl"
@ -116,19 +117,19 @@ void SatoriRecycler::Collect(bool force)
while (m_workList->Count() > 0)
{
DrainMarkQueues();
//TODO: VS clean cards (could be due to overflow)
}
// all strongly reachable objects are marked here
// all strongly reachable objects are marked here
AssertNoWork();
DependentHandlesInitialScan();
while (m_workList->Count() > 0)
{
DrainMarkQueues();
//TODO: VS clean cards (could be due to overflow)
DependentHandlesRescan();
}
AssertNoWork();
// sync
WeakPtrScan(/*isShort*/ true);
// sync
@ -138,10 +139,10 @@ void SatoriRecycler::Collect(bool force)
while (m_workList->Count() > 0)
{
DrainMarkQueues();
//TODO: VS clean cards (could be due to overflow)
DependentHandlesRescan();
}
AssertNoWork();
// sync
WeakPtrScan(/*isShort*/ false);
WeakPtrScanBySingleThread();
@ -202,6 +203,18 @@ void SatoriRecycler::Collect(bool force)
m_gcInProgress = false;
}
void SatoriRecycler::AssertNoWork()
{
_ASSERTE(m_workList->Count() == 0);
m_heap->ForEachPage(
[&](SatoriPage* page)
{
_ASSERTE(page->IsClean());
}
);
}
void SatoriRecycler::DeactivateFn(gc_alloc_context* gcContext, void* param)
{
SatoriAllocationContext* context = (SatoriAllocationContext*)gcContext;
@ -247,15 +260,20 @@ void SatoriRecycler::PushToMarkQueuesSlow(SatoriMarkChunk* &currentMarkChunk, Sa
m_workList->Push(currentMarkChunk);
}
currentMarkChunk = m_heap->Allocator()->TryGetMarkChunk();
//TODO: VS HACK HACK HACK, this is just to force overflows.(do this always in debug?)
currentMarkChunk = nullptr;
if (m_workList->Count() == 0)
{
currentMarkChunk = m_heap->Allocator()->TryGetMarkChunk();
}
if (currentMarkChunk)
{
currentMarkChunk->Push(o);
}
else
{
// TODO: VS handle mark overflow.
_ASSERTE(!"overflow");
o->SetCardsForContent();
}
}
@ -288,7 +306,7 @@ void SatoriRecycler::MarkFn(PTR_PTR_Object ppObject, ScanContext* sc, uint32_t f
}
}
if (o->ContainingRegion()->IsThreadLocal())
if (o->ContainingRegion()->Generation() == 0)
{
// do not mark thread local regions.
_ASSERTE(!"thread local region is unexpected");
@ -303,9 +321,6 @@ void SatoriRecycler::MarkFn(PTR_PTR_Object ppObject, ScanContext* sc, uint32_t f
MarkContext* context = (MarkContext*)sc->_unused1;
// TODO: VS we do not need to push if card is marked, we will have to revisit anyways.
// TODO: VS test card setting. for now this is unused.
// context->m_recycler->m_heap->SetCardForAddress(location);
context->PushToMarkQueues(o);
}
@ -432,6 +447,91 @@ void SatoriRecycler::DrainMarkQueues()
_ASSERTE(dstChunk->Count() == 0);
m_heap->Allocator()->ReturnMarkChunk(dstChunk);
}
CleanCards();
}
void SatoriRecycler::CleanCards()
{
SatoriMarkChunk* dstChunk = nullptr;
bool revisit = false;
do
{
m_heap->ForEachPage(
[&](SatoriPage* page)
{
if (!page->IsClean())
{
page->SetProcessing();
size_t groupCount = page->CardGroupCount();
for (size_t i = 0; i < groupCount; i++)
{
// TODO: VS when stealing is implemented we should start from a random location
if (page->CardGroupState(i) == Satori::CardGroupState::dirty)
{
page->CardGroupSetProcessing(i);
size_t* cards = page->CardsForGroup(i);
// TODO: VS page->RegionForCardGroup
SatoriRegion* region = page->RegionForAddress(page->LocationForCard(cards));
for (int j = 0; j < Satori::CARD_WORDS_IN_CARD_GROUP; j++)
{
// TODO: VS do one word at a time for now, scan for contiguous ranges later.
size_t card = cards[j];
if (!card)
{
continue;
}
cards[j] = 0;
size_t start = page->LocationForCard(&cards[j]);
size_t end = start + Satori::BYTES_PER_CARD_WORD;
SatoriObject* o = region->FindObject(start);
do
{
if (o->IsMarked())
{
o->ForEachObjectRef(
[&](SatoriObject** ref)
{
// TODO: VS check that the ref is dirty in the card.
SatoriObject* child = *ref;
if (child && !child->IsMarked())
{
child->SetMarked();
child->Validate();
if (!dstChunk || !dstChunk->TryPush(child))
{
this->PushToMarkQueuesSlow(dstChunk, child);
}
}
},
start,
end
);
}
o = o->Next();
} while (o->Start() < end);
}
page->CardGroupTrySetClean(i);
}
}
// record missed clean to revisit the whole deal.
revisit = !page->TrySetClean();
}
}
);
} while (revisit);
if (dstChunk)
{
m_workList->Push(dstChunk);
}
}
void SatoriRecycler::MarkHandles()

View file

@ -30,6 +30,8 @@ public:
void Collect(bool force);
void AssertNoWork();
private:
SatoriHeap* m_heap;
@ -65,6 +67,7 @@ private:
void MarkOtherStacks();
void IncrementScanCount();
void DrainMarkQueues();
void CleanCards();
void MarkHandles();
void WeakPtrScan(bool isShort);
void WeakPtrScanBySingleThread();

View file

@ -19,8 +19,9 @@
#include "SatoriObject.inl"
#include "SatoriRegion.h"
#include "SatoriRegion.inl"
#include "SatoriQueue.h"
#include "SatoriPage.h"
#include "SatoriPage.inl"
#include "SatoriQueue.h"
#include "SatoriMarkChunk.h"
#ifdef memcpy
@ -83,6 +84,7 @@ void SatoriRegion::MakeBlank()
{
m_ownerThreadTag = 0;
m_escapeFunc = EscapeFn;
m_generation = 0;
m_allocStart = (size_t)&m_firstObject;
m_allocEnd = End();
m_occupancy = 0;
@ -99,7 +101,7 @@ void SatoriRegion::MakeBlank()
}
#ifdef _DEBUG
memset(&m_syncBlock, 0xFE, m_used - (size_t)&m_syncBlock);
// memset(&m_syncBlock, 0xFE, m_used - (size_t)&m_syncBlock);
#endif
}
@ -239,7 +241,7 @@ void SatoriRegion::Coalesce(SatoriRegion* next)
m_containingPage->RegionInitialized(this);
}
size_t SatoriRegion::Allocate(size_t size, bool ensureZeroInited)
size_t SatoriRegion::Allocate(size_t size, bool zeroInitialize)
{
_ASSERTE(m_containingQueue == nullptr);
@ -260,13 +262,13 @@ size_t SatoriRegion::Allocate(size_t size, bool ensureZeroInited)
m_committed = newComitted;
}
if (ensureZeroInited)
if (zeroInitialize)
{
size_t zeroUpTo = min(m_used, chunkEnd);
ptrdiff_t zeroInitCount = zeroUpTo - chunkStart;
if (zeroInitCount > 0)
{
ZeroMemory((void*)chunkStart, zeroInitCount);
memset((void*)chunkStart, 0, zeroInitCount);
}
}
@ -278,7 +280,7 @@ size_t SatoriRegion::Allocate(size_t size, bool ensureZeroInited)
return 0;
}
size_t SatoriRegion::AllocateHuge(size_t size, bool ensureZeroInited)
size_t SatoriRegion::AllocateHuge(size_t size, bool zeroInitialize)
{
_ASSERTE(ValidateBlank());
_ASSERTE(AllocRemaining() >= size);
@ -300,6 +302,13 @@ size_t SatoriRegion::AllocateHuge(size_t size, bool ensureZeroInited)
chunkStart = m_allocStart;
}
// in rare cases the object does not cross into the last tile. (when free obj padding is on the edge).
// in such case force the object to cross.
if (End() - (chunkStart + size) >= Satori::REGION_SIZE_GRANULARITY)
{
chunkStart += Satori::LARGE_OBJECT_THRESHOLD + Satori::MIN_FREE_SIZE;
}
chunkEnd = chunkStart + size;
// huge allocation should cross the end of the first tile, but have enough space between
@ -319,14 +328,14 @@ size_t SatoriRegion::AllocateHuge(size_t size, bool ensureZeroInited)
m_committed = newComitted;
}
if (ensureZeroInited)
if (zeroInitialize)
{
size_t zeroInitStart = chunkStart;
size_t zeroUpTo = min(m_used, chunkEnd);
ptrdiff_t zeroInitCount = zeroUpTo - zeroInitStart;
if (zeroInitCount > 0)
{
ZeroMemory((void*)zeroInitStart, zeroInitCount);
memset((void*)zeroInitStart, 0, zeroInitCount);
}
}
@ -347,9 +356,10 @@ inline int LocationToIndex(size_t location)
return (location >> Satori::INDEX_GRANULARITY_BITS) % Satori::INDEX_LENGTH;
}
//TODO: VS need FindObjectChecked when a real object must be found.
SatoriObject* SatoriRegion::FindObject(size_t location)
{
_ASSERTE(location >= (size_t)FirstObject() && location <= End());
_ASSERTE(location >= Start() && location <= End());
// start search from the first object or after unparseable alloc gap
SatoriObject* obj = (IsAllocating() && (location >= m_allocEnd)) ?
@ -389,7 +399,6 @@ SatoriObject* SatoriRegion::FindObject(size_t location)
next = next->Next();
}
_ASSERTE(!obj->IsFree());
return obj;
}
@ -485,6 +494,7 @@ void SatoriRegion::SetExposed(SatoriObject** location)
void SatoriRegion::EscapeRecursively(SatoriObject* o)
{
_ASSERTE(this->OwnedByCurrentThread());
_ASSERTE(o->ContainingRegion() == this);
if (o->IsEscaped())
{
@ -1163,7 +1173,7 @@ void SatoriRegion::CompactFinalizables()
void SatoriRegion::CleanMarks()
{
ZeroMemory(&m_bitmap[BITMAP_START], (BITMAP_LENGTH - BITMAP_START) * sizeof(size_t));
memset(&m_bitmap[BITMAP_START], 0, (BITMAP_LENGTH - BITMAP_START) * sizeof(size_t));
}
void SatoriRegion::Verify(bool allowMarked)

View file

@ -44,8 +44,8 @@ public:
size_t AllocStart();
size_t AllocRemaining();
size_t Allocate(size_t size, bool ensureZeroInited);
size_t AllocateHuge(size_t size, bool ensureZeroInited);
size_t Allocate(size_t size, bool zeroInitialize);
size_t AllocateHuge(size_t size, bool zeroInitialize);
void StopAllocating(size_t allocPtr);
bool IsAllocating();
@ -53,6 +53,8 @@ public:
bool IsThreadLocal();
bool OwnedByCurrentThread();
size_t Generation();
void SetGeneration(size_t generation);
size_t Start();
size_t End();
@ -106,6 +108,7 @@ private:
// TEB address could be used on Windows, for example
size_t m_ownerThreadTag;
void (*m_escapeFunc)(SatoriObject**, SatoriObject*, SatoriRegion*);
size_t m_generation;
size_t m_end;
size_t m_committed;

View file

@ -15,6 +15,7 @@
#include "../env/gcenv.ee.h"
#include "SatoriRegion.h"
//TODO: VS unused?
inline bool SatoriRegion::IsThreadLocal()
{
return m_ownerThreadTag;
@ -25,6 +26,16 @@ inline bool SatoriRegion::OwnedByCurrentThread()
return m_ownerThreadTag == SatoriUtil::GetCurrentThreadTag();
}
inline size_t SatoriRegion::Generation()
{
return m_generation;
}
inline void SatoriRegion::SetGeneration(size_t generation)
{
m_generation = generation;
}
inline bool SatoriRegion::IsAllocating()
{
return m_allocEnd != 0;
@ -71,6 +82,7 @@ inline size_t SatoriRegion::Occupancy()
inline void SatoriRegion::Publish()
{
m_ownerThreadTag = 0;
m_generation = 1;
}
template<typename F>

View file

@ -61,6 +61,18 @@ namespace Satori
// TODO: VS this can be configured or computed at start up, but should not change dynamically.
return 4096;
}
static const int BYTES_PER_CARD_BYTE = 512;
static const int BYTES_PER_CARD_WORD = BYTES_PER_CARD_BYTE * sizeof(size_t);
static const int CARD_WORDS_IN_CARD_GROUP = Satori::REGION_SIZE_GRANULARITY / BYTES_PER_CARD_WORD;
enum CardGroupState : uint8_t
{
uncommitted = 0xFF,
clean = 0,
dirty = 1,
processing = 2
};
}
class SatoriUtil

View file

@ -470,16 +470,16 @@ LEAF_ENTRY JIT_PatchedCodeLast, _TEXT
ret
LEAF_END JIT_PatchedCodeLast, _TEXT
; Framed helper for a rare path to invoke recursive escape before doing assignment.
; A helper for a rare path to invoke recursive escape before doing assignment.
; rcx - dest (assumed to be in the heap)
; rdx - src
; r8 - source region
;
NESTED_ENTRY JIT_WriteBarrierHelper_SATORI, _TEXT
push_vol_reg rcx
push_vol_reg rdx
alloc_stack 20h
END_PROLOGUE
LEAF_ENTRY JIT_WriteBarrierHelper_SATORI, _TEXT
; save rcx and rdx and have enough stack for the callee
push rcx
push rdx
sub rsp, 20h
; void SatoriRegion::EscapeFn(SatoriObject** dst, SatoriObject* src, SatoriRegion* region)
call qword ptr [r8 + 8]
@ -487,10 +487,11 @@ NESTED_ENTRY JIT_WriteBarrierHelper_SATORI, _TEXT
add rsp, 20h
pop rdx
pop rcx
; the actual assignment. (AV here will be attributed to the caller)
; the actual assignment. (AV here will be attributed to the caller, unwinder knows this method)
mov [rcx], rdx
ret
NESTED_END_MARKED JIT_WriteBarrierHelper_SATORI, _TEXT
LEAF_END_MARKED JIT_WriteBarrierHelper_SATORI, _TEXT
; JIT_ByRefWriteBarrier has weird symantics, see usage in StubLinkerX86.cpp
;

View file

@ -1311,20 +1311,22 @@ void CheckEscapeSatori(Object** dst, Object* ref)
#endif
}
void CheckEscapeSatoriRange(void* dst, void* src, size_t len)
void CheckEscapeSatoriRange(void* dst, size_t src, size_t len)
{
#if FEATURE_SATORI_GC
if (!IsInHeapSatori(src))
if (!IsInHeapSatori((void*)src))
{
return;
}
SatoriRegion* srcRegion = PageForAddressSatori(src)->RegionForAddress((size_t)src);
SatoriRegion* srcRegion = PageForAddressSatori((void*)src)->RegionForAddress((size_t)src);
if (!srcRegion->OwnedByCurrentThread())
{
return;
}
// TODO: VS the following IsEscaped checks could be done faster by scanning bitmaps
// if move is within a region, check if the dest is escaped.
if ((((size_t)dst ^ (size_t)src) >> 21) == 0)
{
@ -1344,24 +1346,14 @@ void CheckEscapeSatoriRange(void* dst, void* src, size_t len)
containingSrcObj->ForEachObjectRef(
[&](SatoriObject** ref)
{
// TODO: VS fold bounds checks into iterator
if ((size_t)ref < (size_t)src)
{
return;
}
size_t offset = (size_t)ref - (size_t)src;
if (offset >= len)
{
return;
}
SatoriObject* child = *ref;
if (child->ContainingRegion() == srcRegion)
{
srcRegion->EscapeRecursively(child);
}
}
},
src,
src + len
);
#endif
}

View file

@ -68,7 +68,7 @@ extern void ThrowOutOfMemoryDimensionsExceeded();
void ErectWriteBarrier(OBJECTREF* dst, OBJECTREF ref);
bool IsInHeapSatori(void* ptr);
void CheckEscapeSatori(Object** dst, Object* ref);
void CheckEscapeSatoriRange(void* dst, void* src, size_t len);
void CheckEscapeSatoriRange(void* dst, size_t src, size_t len);
//void SetCardsAfterBulkCopy(Object **start, size_t len);
#endif // _GCHELPERS_H_