Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
optimize more cases of all-pointer GC series
  • Loading branch information
VSadov committed Mar 22, 2023
commit 6c020686a520aca9febae6fc28cfc2eec1614f0c
8 changes: 5 additions & 3 deletions src/coreclr/vm/array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,8 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy
}

// Set up GC information
if (CorTypeInfo::IsObjRef(elemType) || pMT->IsAllGCPointers())
if (CorTypeInfo::IsObjRef(elemType) ||
(elemType == ELEMENT_TYPE_VALUETYPE) && pElemMT->IsAllGCPointers())
{
pMT->SetContainsPointers();

Expand All @@ -593,10 +594,11 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy

CGCDescSeries* pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();

pSeries->SetSeriesOffset(ArrayBase::GetDataPtrOffset(pMT));
int offsetToData = ArrayBase::GetDataPtrOffset(pMT);
// For arrays, the size is the negative of the BaseSize (the GC always adds the total
// size of the object, so what you end up with is the size of the data portion of the array)
pSeries->SetSeriesSize(-(SSIZE_T)(pMT->GetBaseSize()));
pSeries->SetSeriesSize(-(SSIZE_T)(offsetToData + sizeof(size_t)));
pSeries->SetSeriesOffset(offsetToData);
}
else if (elemType == ELEMENT_TYPE_VALUETYPE)
{
Expand Down
12 changes: 8 additions & 4 deletions src/coreclr/vm/methodtable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1766,10 +1766,14 @@ BOOL MethodTable::IsAllGCPointers()
if (this->ContainsPointers())
{
// check for canonical GC encoding for all-pointer types
CGCDescSeries* pSeries = CGCDesc::GetCGCDescFromMT(this)->GetHighestSeries();
return pSeries->GetSeriesCount() == 1 &&
pSeries->GetSeriesOffset() == this->GetBaseSize() - sizeof(size_t) &&
pSeries->GetSeriesSize() - this->GetBaseSize() == 0;
CGCDesc* pDesc = CGCDesc::GetCGCDescFromMT(this);
if (pDesc->GetNumSeries() != 1)
return false;

int offsetToData = IsArray() ? ArrayBase::GetDataPtrOffset(this) : sizeof(size_t);
CGCDescSeries* pSeries = pDesc->GetHighestSeries();
return pSeries->GetSeriesOffset() == offsetToData &&
(SSIZE_T)pSeries->GetSeriesSize() == -(SSIZE_T)(offsetToData + sizeof(size_t));
}

return false;
Expand Down
69 changes: 59 additions & 10 deletions src/coreclr/vm/methodtablebuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1863,8 +1863,8 @@ MethodTableBuilder::BuildMethodTableThrowing(
// Perform relevant GC calculations for value classes
HandleGCForValueClasses(pByValueClassCache);

// GC reqires the series to be sorted.
// TODO: fix it so that we emit them in the correct order in the first place.
// GC reqires the series to be sorted.
// TODO: fix it so that we emit them in the correct order in the first place.
if (pMT->ContainsPointers())
{
CGCDesc* gcDesc = CGCDesc::GetCGCDescFromMT(pMT);
Expand Down Expand Up @@ -8042,6 +8042,8 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCach
FieldDesc *pFieldDescList = GetHalfBakedClass()->GetFieldDescList();
DWORD dwCumulativeInstanceFieldPos;

bool isAllGCPointers = true;

// Instance fields start right after the parent
if (HasParent())
{
Expand All @@ -8058,6 +8060,11 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCach
{
dwCumulativeInstanceFieldPos = pParentMT->GetNumInstanceFieldBytes();
}

if (pParentMT->GetNumInstanceFields() != 0 && !pParentMT->IsAllGCPointers())
{
isAllGCPointers = false;
}
}
else
{
Expand Down Expand Up @@ -8300,13 +8307,23 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCach
// Add pointer series for by-value classes
dwNumGCPointerSeries += (DWORD)CGCDesc::GetCGCDescFromMT(pByValueMT)->GetNumSeries();
}

if (!pByValueMT->ContainsPointers() || !pByValueMT->IsAllGCPointers())
{
isAllGCPointers = false;
}
}
else
{
// non-value-type fields always require pointer alignment
// This does not account for types that are marked IsAlign8Candidate due to 8-byte fields
// but that is explicitly handled when we calculate the final alignment for the type.
largestAlignmentRequirement = max(largestAlignmentRequirement, TARGET_POINTER_SIZE);

if (!pFieldDescList[i].IsObjRef())
{
isAllGCPointers = false;
}
}
}

Expand All @@ -8315,14 +8332,17 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCach

if (IsValueClass())
{
// Like C++ we enforce that there can be no 0 length structures.
// Thus for a value class with no fields, we 'pad' the length to be 1
// Like C++ we enforce that there can be no 0 length structures.
// Thus for a value class with no fields, we 'pad' the length to be 1
if (dwNumInstanceFieldBytes == 0)
{
dwNumInstanceFieldBytes = 1;
isAllGCPointers = false;
}

// The JITs like to copy full machine words,
// so if the size is bigger than a void* round it up to minAlign
// and if the size is smaller than void* round it up to next power of two
// The JITs like to copy full machine words,
// so if the size is bigger than a void* round it up to minAlign
// and if the size is smaller than void* round it up to next power of two
unsigned minAlign;

#ifdef FEATURE_64BIT_ALIGNMENT
Expand Down Expand Up @@ -8372,6 +8392,13 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCach
}
}

bmtFP->fIsAllGCPointers = isAllGCPointers && dwNumGCPointerSeries;
if (bmtFP->fIsAllGCPointers)
{
// we can use optimized form of GCDesc taking one serie
dwNumGCPointerSeries = 1;
}

bmtFP->NumInstanceFieldBytes = dwNumInstanceFieldBytes;

bmtFP->NumGCPointerSeries = dwNumGCPointerSeries;
Expand Down Expand Up @@ -9023,6 +9050,10 @@ void MethodTableBuilder::FindPointerSeriesExplicit(UINT instanceSliceSize,

bmtFP->NumGCPointerSeries = bmtParent->NumParentPointerSeries + bmtGCSeries->numSeries;

// since the GC series are computed from a ref map,
// in most cases where optimized GCDesc could be used, that is what we will compute anyways,
// so we will not try optimizing this case.
bmtFP->fIsAllGCPointers = false;
}

//*******************************************************************************
Expand Down Expand Up @@ -11536,8 +11567,27 @@ VOID MethodTableBuilder::HandleGCForValueClasses(MethodTable ** pByValueClassCac

pMT->SetContainsPointers();

// Copy the pointer series map from the parent
CGCDesc::Init( (PVOID) pMT, bmtFP->NumGCPointerSeries );

// special case when all instance fields are objects - we can encode that as one serie.
if (bmtFP->fIsAllGCPointers)
{
_ASSERTE(bmtFP->NumGCPointerSeries == 1);

CGCDescSeries* pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();

// the data is right after the method table ptr
int offsetToData = TARGET_POINTER_SIZE;

// Set the size as the negative of the BaseSize (the GC always adds the total
// size of the object, so what you end up with is the size of the data portion of the instance)
pSeries->SetSeriesSize(-(SSIZE_T)(offsetToData + TARGET_POINTER_SIZE));
pSeries->SetSeriesOffset(offsetToData);

return;
}

// Copy the pointer series map from the parent
if (bmtParent->NumParentPointerSeries != 0)
{
size_t ParentGCSize = CGCDesc::ComputeSize(bmtParent->NumParentPointerSeries);
Expand All @@ -11554,7 +11604,7 @@ VOID MethodTableBuilder::HandleGCForValueClasses(MethodTable ** pByValueClassCac
repeat = bmtFP->NumInlineArrayElements;
}

// Build the pointer series map for this pointers in this instance
// Build the pointer series map for pointers in this instance
pSeries = ((CGCDesc*)pMT)->GetLowestSeries();
if (bmtFP->NumInstanceGCPointerFields)
{
Expand All @@ -11579,7 +11629,6 @@ VOID MethodTableBuilder::HandleGCForValueClasses(MethodTable ** pByValueClassCac

// if we have an inline array, we will have only one formal instance field,
// but will have to replicate the layout "repeat" times.
// otherwise every field will be matched with 1 serie.
for (DWORD r = 0; r < repeat; r++)
{
// The by value class may have more than one pointer series
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/methodtablebuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -2012,6 +2012,7 @@ class MethodTableBuilder
DWORD NumInstanceFieldBytes;
DWORD NumInlineArrayElements;

bool fIsAllGCPointers;
bool fIsByRefLikeType;
bool fHasFixedAddressValueTypes;
bool fHasSelfReferencingStaticValueTypeField_WithRVA;
Expand Down