-
Notifications
You must be signed in to change notification settings - Fork 5.3k
CoreCLR support for InlineArrayAttribute. (struct layout part) #82744
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
da7b00b
170a306
bb1c3db
0a4cd78
6eeb887
f0d156b
470aee2
78ab523
a452be7
2801f50
e3284d1
4ff0a25
b406dfe
7f197d6
1852993
295caf1
e7738ea
065467b
62b20c8
01e2e17
f844772
1b5f608
bf676e4
7d42fbc
7fff2be
2d403ee
f6f6acc
d68cfa2
ebc698b
9373c77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,21 +14,36 @@ public static GCPointerMap FromInstanceLayout(DefType type) | |
| { | ||
| Debug.Assert(type.ContainsGCPointers); | ||
|
|
||
| GCPointerMapBuilder builder = new GCPointerMapBuilder(type.InstanceByteCount.AsInt, type.Context.Target.PointerSize); | ||
| FromInstanceLayoutHelper(ref builder, type); | ||
| int pointerSize = type.Context.Target.PointerSize; | ||
| GCPointerMapBuilder builder = new GCPointerMapBuilder(type.InstanceByteCount.AsInt, pointerSize); | ||
| FromInstanceLayoutHelper(ref builder, type, pointerSize); | ||
|
|
||
| return builder.ToGCMap(); | ||
| } | ||
|
|
||
| private static void FromInstanceLayoutHelper(ref GCPointerMapBuilder builder, DefType type) | ||
| private static void FromInstanceLayoutHelper(ref GCPointerMapBuilder builder, DefType type, int pointerSize) | ||
| { | ||
| if (!type.IsValueType && type.HasBaseType) | ||
| { | ||
| DefType baseType = type.BaseType; | ||
| GCPointerMapBuilder baseLayoutBuilder = builder.GetInnerBuilder(0, baseType.InstanceByteCount.AsInt); | ||
| FromInstanceLayoutHelper(ref baseLayoutBuilder, baseType); | ||
| FromInstanceLayoutHelper(ref baseLayoutBuilder, baseType, pointerSize); | ||
| } | ||
|
|
||
| int repeat = 1; | ||
|
|
||
| //TODO: VS compute repeat | ||
| //if (type is InstantiatedType it) | ||
| //{ | ||
| // if (it.Name == "ValueArray`2" && it.Namespace == "System") | ||
| // { | ||
| // if (it.Instantiation[1] is ArrayType arr) | ||
| // { | ||
| // repeat = arr.Rank; | ||
| // } | ||
| // } | ||
| //} | ||
|
|
||
| foreach (FieldDesc field in type.GetFields()) | ||
| { | ||
| if (field.IsStatic) | ||
|
|
@@ -37,16 +52,23 @@ private static void FromInstanceLayoutHelper(ref GCPointerMapBuilder builder, De | |
| TypeDesc fieldType = field.FieldType; | ||
| if (fieldType.IsGCPointer) | ||
| { | ||
| builder.MarkGCPointer(field.Offset.AsInt); | ||
| for (int i = 0; i < repeat; i++) | ||
| { | ||
| builder.MarkGCPointer(field.Offset.AsInt + pointerSize * i); | ||
| } | ||
| } | ||
| else if (fieldType.IsValueType) | ||
| { | ||
| var fieldDefType = (DefType)fieldType; | ||
| if (fieldDefType.ContainsGCPointers) | ||
| { | ||
| GCPointerMapBuilder innerBuilder = | ||
| builder.GetInnerBuilder(field.Offset.AsInt, fieldDefType.InstanceByteCount.AsInt); | ||
| FromInstanceLayoutHelper(ref innerBuilder, fieldDefType); | ||
| for (int i = 0; i < repeat; i++) | ||
| { | ||
| int fieldSize = fieldDefType.InstanceByteCount.AsInt; | ||
| GCPointerMapBuilder innerBuilder = | ||
| builder.GetInnerBuilder(field.Offset.AsInt + fieldSize * i, fieldSize); | ||
| FromInstanceLayoutHelper(ref innerBuilder, fieldDefType, pointerSize); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -76,9 +98,10 @@ public static GCPointerMap FromStaticLayout(DefType type) | |
| var fieldDefType = (DefType)fieldType; | ||
| if (fieldDefType.ContainsGCPointers) | ||
| { | ||
| int pointerSize = type.Context.Target.PointerSize; | ||
| GCPointerMapBuilder innerBuilder = | ||
| builder.GetInnerBuilder(field.Offset.AsInt, fieldDefType.InstanceByteCount.AsInt); | ||
| FromInstanceLayoutHelper(ref innerBuilder, fieldDefType); | ||
| FromInstanceLayoutHelper(ref innerBuilder, fieldDefType, pointerSize); | ||
|
||
| } | ||
| } | ||
| } | ||
|
|
@@ -109,9 +132,10 @@ public static GCPointerMap FromThreadStaticLayout(DefType type) | |
| var fieldDefType = (DefType)fieldType; | ||
| if (fieldDefType.ContainsGCPointers) | ||
| { | ||
| int pointerSize = type.Context.Target.PointerSize; | ||
| GCPointerMapBuilder innerBuilder = | ||
| builder.GetInnerBuilder(field.Offset.AsInt, fieldDefType.InstanceByteCount.AsInt); | ||
| FromInstanceLayoutHelper(ref innerBuilder, fieldDefType); | ||
| FromInstanceLayoutHelper(ref innerBuilder, fieldDefType, pointerSize); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1694,6 +1694,25 @@ MethodTableBuilder::BuildMethodTableThrowing( | |
| &pByValueClassCache, bmtMFDescs, bmtFP, | ||
| &totalDeclaredFieldSize); | ||
|
|
||
| // TODO: VS detect valArr | ||
| //if (!bmtGenericsInfo->fContainsGenericVariables && | ||
| // g_pValueArrayClass != NULL && | ||
| // g_pValueArrayClass->GetCl() == GetCl() && | ||
| // g_pValueArrayClass->GetModule() == bmtInternal->pType->GetModule()) | ||
| //{ | ||
| // TypeHandle lengthMarker = bmtGenericsInfo->GetInstantiation()[1]; | ||
| // if (lengthMarker.IsArray()) | ||
| // { | ||
| // DWORD rank = lengthMarker.GetRank(); | ||
| // if (rank > 1) | ||
| // { | ||
| // bmtFP->NumValueArrayElements = rank; | ||
| // // there is no scenario when tearing a value array into pieces is desirable. | ||
| // GetHalfBakedClass()->SetValueArrayFlag(); | ||
| // } | ||
| // } | ||
| //} | ||
|
|
||
| // Place regular static fields | ||
| PlaceRegularStaticFields(); | ||
|
|
||
|
|
@@ -1713,6 +1732,11 @@ MethodTableBuilder::BuildMethodTableThrowing( | |
|
|
||
| _ASSERTE(HasLayout()); | ||
|
|
||
| if (bmtFP->NumValueArrayElements) | ||
| { | ||
| GetLayoutInfo()->m_cbManagedSize *= bmtFP->NumValueArrayElements; | ||
| } | ||
|
|
||
| bmtFP->NumInstanceFieldBytes = GetLayoutInfo()->m_cbManagedSize; | ||
|
|
||
| // For simple Blittable types we still need to check if they have any overlapping | ||
|
|
@@ -8318,6 +8342,16 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCach | |
| BuildMethodTableThrowException(IDS_CLASSLOAD_FIELDTOOLARGE); | ||
| } | ||
|
|
||
| if (bmtFP->NumValueArrayElements > 0) | ||
| { | ||
| dwNumInstanceFieldBytes *= bmtFP->NumValueArrayElements; | ||
|
|
||
| if (pFieldDescList[0].IsByValue()) | ||
| { | ||
| dwNumGCPointerSeries *= bmtFP->NumValueArrayElements; | ||
| } | ||
| } | ||
|
|
||
| bmtFP->NumInstanceFieldBytes = dwNumInstanceFieldBytes; | ||
|
|
||
| bmtFP->NumGCPointerSeries = dwNumGCPointerSeries; | ||
|
|
@@ -11494,12 +11528,19 @@ VOID MethodTableBuilder::HandleGCForValueClasses(MethodTable ** pByValueClassCac | |
|
|
||
| } | ||
|
|
||
| DWORD repeat = 1; | ||
| if (bmtFP->NumValueArrayElements > 0) | ||
| { | ||
| _ASSERTE(bmtEnumFields->dwNumInstanceFields == 1); | ||
| repeat = bmtFP->NumValueArrayElements; | ||
| } | ||
|
|
||
| // Build the pointer series map for this pointers in this instance | ||
| pSeries = ((CGCDesc*)pMT)->GetLowestSeries(); | ||
| if (bmtFP->NumInstanceGCPointerFields) | ||
| { | ||
| // See gcdesc.h for an explanation of why we adjust by subtracting BaseSize | ||
| pSeries->SetSeriesSize( (size_t) (bmtFP->NumInstanceGCPointerFields * TARGET_POINTER_SIZE) - (size_t) pMT->GetBaseSize()); | ||
| pSeries->SetSeriesSize((size_t)(bmtFP->NumInstanceGCPointerFields * repeat * TARGET_POINTER_SIZE) - (size_t)pMT->GetBaseSize()); | ||
| pSeries->SetSeriesOffset(bmtFP->GCPointerFieldStart + OBJECT_SIZE); | ||
| pSeries++; | ||
| } | ||
|
|
@@ -11509,44 +11550,51 @@ VOID MethodTableBuilder::HandleGCForValueClasses(MethodTable ** pByValueClassCac | |
| { | ||
| if (pFieldDescList[i].IsByValue()) | ||
| { | ||
| MethodTable *pByValueMT = pByValueClassCache[i]; | ||
| MethodTable* pByValueMT = pByValueClassCache[i]; | ||
|
|
||
| if (pByValueMT->ContainsPointers()) | ||
| { | ||
| // Offset of the by value class in the class we are building, does NOT include Object | ||
| DWORD dwCurrentOffset = pFieldDescList[i].GetOffset_NoLogging(); | ||
| DWORD dwElementSize = pByValueMT->GetBaseSize() - OBJECT_BASESIZE; | ||
|
|
||
| // The by value class may have more than one pointer series | ||
| CGCDescSeries * pByValueSeries = CGCDesc::GetCGCDescFromMT(pByValueMT)->GetLowestSeries(); | ||
| SIZE_T dwNumByValueSeries = CGCDesc::GetCGCDescFromMT(pByValueMT)->GetNumSeries(); | ||
|
|
||
| for (SIZE_T j = 0; j < dwNumByValueSeries; j++) | ||
| for (DWORD r = 0; r < repeat; r++) | ||
|
||
| { | ||
| size_t cbSeriesSize; | ||
| size_t cbSeriesOffset; | ||
| // The by value class may have more than one pointer series | ||
| CGCDescSeries* pByValueSeries = CGCDesc::GetCGCDescFromMT(pByValueMT)->GetLowestSeries(); | ||
| SIZE_T dwNumByValueSeries = CGCDesc::GetCGCDescFromMT(pByValueMT)->GetNumSeries(); | ||
|
|
||
| _ASSERTE(pSeries <= CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries()); | ||
| for (SIZE_T j = 0; j < dwNumByValueSeries; j++) | ||
| { | ||
| size_t cbSeriesSize; | ||
| size_t cbSeriesOffset; | ||
|
|
||
| _ASSERTE(pSeries <= CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries()); | ||
|
|
||
| cbSeriesSize = pByValueSeries->GetSeriesSize(); | ||
| cbSeriesSize = pByValueSeries->GetSeriesSize(); | ||
|
|
||
| // Add back the base size of the by value class, since it's being transplanted to this class | ||
| cbSeriesSize += pByValueMT->GetBaseSize(); | ||
| // Add back the base size of the by value class, since it's being transplanted to this class | ||
| cbSeriesSize += pByValueMT->GetBaseSize(); | ||
|
|
||
| // Subtract the base size of the class we're building | ||
| cbSeriesSize -= pMT->GetBaseSize(); | ||
| // Subtract the base size of the class we're building | ||
| cbSeriesSize -= pMT->GetBaseSize(); | ||
|
|
||
| // Set current series we're building | ||
| pSeries->SetSeriesSize(cbSeriesSize); | ||
| // Set current series we're building | ||
| pSeries->SetSeriesSize(cbSeriesSize); | ||
|
|
||
| // Get offset into the value class of the first pointer field (includes a +Object) | ||
| cbSeriesOffset = pByValueSeries->GetSeriesOffset(); | ||
| // Get offset into the value class of the first pointer field (includes a +Object) | ||
| cbSeriesOffset = pByValueSeries->GetSeriesOffset(); | ||
|
|
||
| // Add it to the offset of the by value class in our class | ||
| cbSeriesOffset += dwCurrentOffset; | ||
| // Add element N offset | ||
| cbSeriesOffset += r * dwElementSize; | ||
|
|
||
| pSeries->SetSeriesOffset(cbSeriesOffset); // Offset of field | ||
| pSeries++; | ||
| pByValueSeries++; | ||
| // Add it to the offset of the by value class in our class | ||
| cbSeriesOffset += dwCurrentOffset; | ||
|
|
||
| pSeries->SetSeriesOffset(cbSeriesOffset); // Offset of field | ||
| pSeries++; | ||
| pByValueSeries++; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This flag was called
CORINFO_FLG_DONT_PROMOTEat the time of the prototype. We do not want inline arrays to promote as indexing relies on contiguous layout.I assume the flag still has the effect of suppressing field promotion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should double check with the JIT team - I would think we should be setting similar flags that we set for explicit layout structs and explicit layout structs don't set this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dotnet/jit-contrib can someone from the JIT team take a look at this part?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CORINFO_FLG_DONT_DIG_FIELDSdoesn't always suppress promotion (eg a single gc ref field struct will still get promoted).I would introduce a new flag describing this, something like
CORINFO_FLG_INDEXABLE_FIELDS, and update the jit to block promotion when it sees it. Looks like there is still one free bit?Down the road we might be doing more flexible partial promotion and perhaps even handle cases like these where we know exactly which fields are being touched over some span of code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an easy way to find all the places that need to care about the bit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@VSadov I do think we need a JIT-EE GUID update here -- otherwise it can expose all sorts of latent issues for the JIT team once we get newer collections with this flag that older JITs do not handle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Thanks!! I will change the guid in a follow up change.
In theory the presence of the attribute will imply the new runtime and prevent many versioning issues, but it is better to be sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fairly easy. Roslyn will add a layer of convenience, but otherwise the feature can be used directly.
I’ve added some use cases in corlib and libraries as a part of the change.
The only usual caveat with using a feature this early is possibly having to change things if the feature changes based on feedback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jakobbotsch Sorry, did not notice your yesterdays comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem for the JIT team is that we disambiguate compatibility of SPMI collections via the JIT-EE GUID. We automatically collect every Sunday, so after that we will start seeing contexts with the new flag set and no indication that newer JITs are needed to properly handle this. That might not cause any problems except silent bad codegen for our SPMI runs, but it could also conceivably cause spurious assertion failures.
FWIW, @EgorBo just merged #83430 which includes a JIT-EE GUID bump, so you won't need to do that in a follow-up change after all.
I've looked at some of the examples, that looks fairly straightforward. I guess the main thing is how to get access to
InlineArrayAttributefrom the programs I'm generating.