Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -398,11 +398,14 @@ private static void CreateInstanceGCDesc(TypeBuilderState state, MethodTable* pT
if (cbGCDesc != 0)
{
pEEType->ContainsGCPointers = true;
if (state.IsArrayOfReferenceTypes)
if (state.IsArrayOfReferenceTypes || IsAllGCPointers(gcBitfield))
{
IntPtr* gcDescStart = (IntPtr*)((byte*)pEEType - cbGCDesc);
// Series size
gcDescStart[0] = new IntPtr(-baseSize);
// Series offset
gcDescStart[1] = new IntPtr(baseSize - sizeof(IntPtr));
// NumSeries
gcDescStart[2] = new IntPtr(1);
}
else
Expand Down Expand Up @@ -443,9 +446,10 @@ private static unsafe int GetInstanceGCDescSize(TypeBuilderState state, MethodTa
var gcBitfield = state.InstanceGCLayout;
if (isArray)
{
if (state.IsArrayOfReferenceTypes)
if (state.IsArrayOfReferenceTypes ||
(gcBitfield != null && IsAllGCPointers(gcBitfield)))
{
// Reference type arrays have a GC desc the size of 3 pointers
// For efficiency this is special cased and encoded as one serie
return 3 * sizeof(IntPtr);
}
else
Expand All @@ -472,6 +476,20 @@ private static unsafe int GetInstanceGCDescSize(TypeBuilderState state, MethodTa
}
}

private static bool IsAllGCPointers(LowLevelList<bool> bitfield)
{
int count = bitfield.Count;
Debug.Assert(count > 0);

for (int i = 0; i < count; i++)
{
if (!bitfield[i])
return false;
}

return true;
}

private static unsafe int CreateArrayGCDesc(LowLevelList<bool> bitfield, int rank, bool isSzArray, void* gcdesc)
{
if (bitfield == null)
Expand Down
30 changes: 15 additions & 15 deletions src/coreclr/tools/Common/Internal/Runtime/GCDescEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static int GetGCDescSize(TypeDesc type)
TypeDesc elementType = ((ArrayType)type).ElementType;
if (elementType.IsGCPointer)
{
// For efficiency this is special cased and encoded as one serie.
// For efficiency this is special cased and encoded as one serie
return 3 * type.Context.Target.PointerSize;
}
else if (elementType.IsDefType)
Expand All @@ -34,7 +34,7 @@ public static int GetGCDescSize(TypeDesc type)
GCPointerMap pointerMap = GCPointerMap.FromInstanceLayout(defType);
if (pointerMap.IsAllGCPointers)
{
// For efficiency this is special cased and encoded as one serie.
// For efficiency this is special cased and encoded as one serie
return 3 * type.Context.Target.PointerSize;
}
else
Expand Down Expand Up @@ -69,18 +69,18 @@ public static void EncodeGCDesc<T>(ref T builder, TypeDesc type)
{
TypeDesc elementType = ((ArrayType)type).ElementType;

// 2 means m_pEEType and _numComponents. Syncblock is sort of appended at the end of the object layout in this case.
int baseSize = 2 * builder.TargetPointerSize;
// 2 means m_pEEType and _numComponents.
int offsetToData = 2 * builder.TargetPointerSize;

if (type.IsMdArray)
{
// Multi-dim arrays include upper and lower bounds for each rank
baseSize += 2 * sizeof(int) * ((ArrayType)type).Rank;
offsetToData += 2 * sizeof(int) * ((ArrayType)type).Rank;
}

if (elementType.IsGCPointer)
{
EncodeAllGCPointersArrayGCDesc(ref builder, baseSize);
EncodeAllGCPointersGCDesc(ref builder, offsetToData);
}
else if (elementType.IsDefType)
{
Expand All @@ -90,11 +90,11 @@ public static void EncodeGCDesc<T>(ref T builder, TypeDesc type)
GCPointerMap pointerMap = GCPointerMap.FromInstanceLayout(elementDefType);
if (pointerMap.IsAllGCPointers)
{
EncodeAllGCPointersArrayGCDesc(ref builder, baseSize);
EncodeAllGCPointersGCDesc(ref builder, offsetToData);
}
else
{
EncodeArrayGCDesc(ref builder, pointerMap, baseSize);
EncodeArrayGCDesc(ref builder, pointerMap, offsetToData);
}
}
}
Expand Down Expand Up @@ -150,22 +150,22 @@ public static void EncodeStandardGCDesc<T>(ref T builder, GCPointerMap map, int
}

// Arrays of all GC references are encoded as special kind of GC desc for efficiency
private static void EncodeAllGCPointersArrayGCDesc<T>(ref T builder, int baseSize)
private static void EncodeAllGCPointersGCDesc<T>(ref T builder, int offsetToData)
where T : struct, ITargetBinaryWriter
{
// Construct the gc info as if this array contains exactly one pointer
// Construct the gc info as if this instance contains exactly one pointer
// - the encoding trick where the size of the series is measured as a difference from
// total object size will make this work for arbitrary array lengths
// total object size will make this work for arbitrary instance lengths

// Series size
builder.EmitNaturalInt(-(baseSize + builder.TargetPointerSize));
builder.EmitNaturalInt(-(offsetToData + builder.TargetPointerSize));
// Series offset
builder.EmitNaturalInt(baseSize);
builder.EmitNaturalInt(offsetToData);
// NumSeries
builder.EmitNaturalInt(1);
}

private static void EncodeArrayGCDesc<T>(ref T builder, GCPointerMap map, int baseSize)
private static void EncodeArrayGCDesc<T>(ref T builder, GCPointerMap map, int ofsetToData)
where T : struct, ITargetBinaryWriter
{
// NOTE: This format cannot properly represent element types with sizes >= 64k bytes.
Expand Down Expand Up @@ -212,7 +212,7 @@ private static void EncodeArrayGCDesc<T>(ref T builder, GCPointerMap map, int ba
}

Debug.Assert(numSeries > 0);
builder.EmitNaturalInt(baseSize + leadingNonPointerCount * pointerSize);
builder.EmitNaturalInt(ofsetToData + leadingNonPointerCount * pointerSize);
builder.EmitNaturalInt(-numSeries);
}
}
Expand Down
86 changes: 38 additions & 48 deletions src/coreclr/vm/array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -584,67 +584,73 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy
}

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

// This array is all GC Pointers
CGCDesc::GetCGCDescFromMT(pMT)->Init( pMT, 1 );

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

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)(offsetToData + sizeof(size_t)));
pSeries->SetSeriesOffset(offsetToData);
}
else if (elemType == ELEMENT_TYPE_VALUETYPE)
{
// If it's an array of value classes, there is a different format for the GCDesc if it contains pointers
if (pElemMT->ContainsPointers())
{
CGCDescSeries *pSeries;

// There must be only one series for value classes
CGCDescSeries *pByValueSeries = CGCDesc::GetCGCDescFromMT(pElemMT)->GetHighestSeries();

pMT->SetContainsPointers();

CGCDescSeries* pElemSeries = CGCDesc::GetCGCDescFromMT(pElemMT)->GetHighestSeries();

// negative series has a special meaning, indicating a different form of GCDesc
SSIZE_T nSeries = (SSIZE_T) CGCDesc::GetCGCDescFromMT(pElemMT)->GetNumSeries();
CGCDesc::GetCGCDescFromMT(pMT)->InitValueClassSeries(pMT, nSeries);

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

// sort by offset
SSIZE_T AllocSizeSeries;
if (!ClrSafeInt<SSIZE_T>::multiply(sizeof(CGCDescSeries*), nSeries, AllocSizeSeries))
COMPlusThrowOM();
CGCDescSeries** sortedSeries = (CGCDescSeries**) _alloca(AllocSizeSeries);
int index;
for (index = 0; index < nSeries; index++)
sortedSeries[index] = &pByValueSeries[-index];

// section sort
for (int i = 0; i < nSeries; i++) {
for (int j = i+1; j < nSeries; j++)
if (sortedSeries[j]->GetSeriesOffset() < sortedSeries[i]->GetSeriesOffset())
{
CGCDescSeries* temp = sortedSeries[i];
sortedSeries[i] = sortedSeries[j];
sortedSeries[j] = temp;
}
CGCDescSeries* pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();

#if _DEBUG
// GC series must be sorted by the offset
// we will validate that here just in case.
size_t prevOffset = pElemSeries[0].GetSeriesOffset();
for (int index = 1; index < nSeries; index++)
{
size_t offset = pElemSeries[-index].GetSeriesOffset();
_ASSERTE((offset - prevOffset) > 0);
prevOffset = offset;
}
#endif // _DEBUG

// Offset of the first pointer in the array
// This equals the offset of the first pointer if this were an array of entirely pointers, plus the offset of the
// first pointer in the value class
pSeries->SetSeriesOffset(ArrayBase::GetDataPtrOffset(pMT)
+ (sortedSeries[0]->GetSeriesOffset()) - OBJECT_SIZE);
for (index = 0; index < nSeries; index ++)
+ (pElemSeries[0].GetSeriesOffset()) - OBJECT_SIZE);

for (int index = 0; index < nSeries; index ++)
{
size_t numPtrsInBytes = sortedSeries[index]->GetSeriesSize()
size_t numPtrsInBytes = pElemSeries[-index].GetSeriesSize()
+ pElemMT->GetBaseSize();
size_t currentOffset;
size_t skip;
currentOffset = sortedSeries[index]->GetSeriesOffset()+numPtrsInBytes;
currentOffset = pElemSeries[-index].GetSeriesOffset()+numPtrsInBytes;
if (index != nSeries-1)
{
skip = sortedSeries[index+1]->GetSeriesOffset()-currentOffset;
skip = pElemSeries[-(index+1)].GetSeriesOffset()-currentOffset;
}
else if (index == 0)
{
skip = pElemMT->GetNumInstanceFieldBytes() - numPtrsInBytes;
}
else
{
skip = sortedSeries[0]->GetSeriesOffset() + pElemMT->GetBaseSize()
skip = pElemSeries[0].GetSeriesOffset() + pElemMT->GetBaseSize()
- OBJECT_BASESIZE - currentOffset;
}

Expand All @@ -665,22 +671,6 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy
}
}
}
else if (CorTypeInfo::IsObjRef(elemType))
{
CGCDescSeries *pSeries;

pMT->SetContainsPointers();

// This array is all GC Pointers
CGCDesc::GetCGCDescFromMT(pMT)->Init( pMT, 1 );

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

pSeries->SetSeriesOffset(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()));
}

// If we get here we are assuming that there was no truncation. If this is not the case then
// an array whose base type is not a value class was created and was larger then 0xffff (a word)
Expand Down
19 changes: 19 additions & 0 deletions src/coreclr/vm/methodtable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1761,6 +1761,25 @@ BOOL MethodTable::CanShareVtableChunksFrom(MethodTable *pTargetMT, Module *pCurr
return pTargetMT->GetLoaderModule() == pCurrentLoaderModule;
}

BOOL MethodTable::IsAllGCPointers()
{
if (this->ContainsPointers())
{
// check for canonical GC encoding for all-pointer types
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 ((int)pSeries->GetSeriesOffset() == offsetToData) &&
((SSIZE_T)pSeries->GetSeriesSize() == -(SSIZE_T)(offsetToData + sizeof(size_t)));
}

return false;
}


#ifdef _DEBUG

void
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/vm/methodtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,7 @@ class MethodTable
LIMITED_METHOD_CONTRACT;
return GetFlag(enum_flag_ContainsPointers);
}

BOOL Collectible()
{
LIMITED_METHOD_CONTRACT;
Expand All @@ -1591,6 +1592,7 @@ class MethodTable
return FALSE;
#endif
}

BOOL ContainsPointersOrCollectible()
{
LIMITED_METHOD_CONTRACT;
Expand All @@ -1602,6 +1604,8 @@ class MethodTable

BOOL IsNotTightlyPacked();

BOOL IsAllGCPointers();

void SetContainsPointers()
{
LIMITED_METHOD_CONTRACT;
Expand Down
Loading