Skip to content
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

[hackathon] [testonly] ValueArray - a compliment type to the Span, which owns its data without indirections. #60608

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8920a59
ValueArray<T> that has its field repeated 64 times
VSadov Oct 13, 2021
8ff4f56
Update ValueArray API, add range checks.
VSadov Oct 14, 2021
9513c8e
less hacky ValueArray detection
VSadov Oct 14, 2021
d9cf4d5
Use MD array to encode Length
VSadov Oct 15, 2021
08d0cd6
Tweak ValueArray so that range checks could be inlined/omitted by JIT
VSadov Oct 15, 2021
1884e23
comments
VSadov Oct 15, 2021
9b17a5a
Make the Element0 field private
VSadov Oct 15, 2021
1e2ef51
Make API more similar to Span.
VSadov Oct 15, 2021
b10d2e9
add a proper test
VSadov Oct 16, 2021
48461cf
Add a field test
VSadov Oct 16, 2021
e477426
bug fixes
VSadov Oct 17, 2021
3443c22
Value semantics should consider all elements.
VSadov Oct 17, 2021
b698d94
Hide `GetPinnableReference` per convention, since it is for use in `f…
VSadov Oct 17, 2021
5e174f7
Add a test for use with pointers/fixed
VSadov Oct 17, 2021
ff3ef33
A few test fixes to match the changes.
VSadov Oct 20, 2021
cc67b00
Disable ValueArray tests on mono until implemented.
VSadov Oct 20, 2021
d053de0
teach crossgen about ValueArray
VSadov Oct 21, 2021
f3d8569
introducing IValueArray
VSadov Oct 22, 2021
057bd96
Rename R --> Size
VSadov Oct 27, 2021
618ffe6
Use ValueArray in couple trivial cases.(just as an example, there are…
VSadov Oct 22, 2021
000e295
Use ValueArray in TlsOverPerCoreLockedStacksArrayPool to avoid indire…
VSadov Oct 21, 2021
4040fad
Use ValueArray as inline storage in ValueListBuilder.
VSadov Oct 22, 2021
88c2c84
Use ValueArray as inline storage in MethodCallExpression nodes.
VSadov Oct 22, 2021
b85d94a
Use ValueArray in ParamsArray.
VSadov Oct 22, 2021
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
2 changes: 1 addition & 1 deletion src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@ enum CorInfoFlag
CORINFO_FLG_ARRAY = 0x00080000, // class is an array class (initialized differently)
CORINFO_FLG_OVERLAPPING_FIELDS = 0x00100000, // struct or class has fields that overlap (aka union)
CORINFO_FLG_INTERFACE = 0x00200000, // it is an interface
CORINFO_FLG_DONT_PROMOTE = 0x00400000, // don't try to promote fields (used for types outside of AOT compilation version bubble)
CORINFO_FLG_DONT_PROMOTE = 0x00400000, // don't try to promote fields (used for ValueArray and types outside of AOT compilation version bubble)
CORINFO_FLG_CUSTOMLAYOUT = 0x00800000, // does this struct have custom layout?
CORINFO_FLG_CONTAINS_GC_PTR = 0x01000000, // does the class contain a gc ptr ?
CORINFO_FLG_DELEGATE = 0x02000000, // is this a subclass of delegate or multicast delegate ?
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/inc/dacvars.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pMulticastDelegateClass, ::g_p
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pFreeObjectMethodTable, ::g_pFreeObjectMethodTable)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pOverlappedDataClass, ::g_pOverlappedDataClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pValueTypeClass, ::g_pValueTypeClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pValueArrayClass, ::g_pValueArrayClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pEnumClass, ::g_pEnumClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pThreadClass, ::g_pThreadClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pPredefinedArrayTypes, ::g_pPredefinedArrayTypes)
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1928,6 +1928,17 @@ private uint getClassAttribsInternal(TypeDesc type)
result |= CorInfoFlag.CORINFO_FLG_ABSTRACT;
}

if (type is InstantiatedType it)
{
if (it.Name == "ValueArray`2" && it.Namespace == "System")
{
if (it.Instantiation[1] is ArrayType arr && arr.Rank > 1)
{
result |= CorInfoFlag.CORINFO_FLG_DONT_PROMOTE;
}
}
}

#if READYTORUN
if (!_compilation.CompilationModuleGroup.VersionsWithType(type))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,27 @@ protected ComputedInstanceFieldLayout ComputeSequentialFieldLayout(MetadataType
alignUpInstanceByteSize: true,
out instanceByteSizeAndAlignment);

if (type is InstantiatedType it)
{
if (it.Name == "ValueArray`2" && it.Namespace == "System")
{
if (it.Instantiation[1] is ArrayType arr)
{
int repeat = arr.Rank;

if (!instanceSizeAndAlignment.Size.IsIndeterminate)
{
instanceSizeAndAlignment.Size = new LayoutInt(instanceSizeAndAlignment.Size.AsInt * repeat);
}

if (!instanceByteSizeAndAlignment.Size.IsIndeterminate)
{
instanceByteSizeAndAlignment.Size = new LayoutInt(instanceByteSizeAndAlignment.Size.AsInt * repeat);
}
}
}
}

ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout();
computedLayout.FieldAlignment = instanceSizeAndAlignment.Alignment;
computedLayout.FieldSize = instanceSizeAndAlignment.Size;
Expand Down Expand Up @@ -722,6 +743,27 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type,
alignUpInstanceByteSize: true,
out instanceByteSizeAndAlignment);

if (type is InstantiatedType it)
{
if (it.Name == "ValueArray`2" && it.Namespace == "System")
{
if (it.Instantiation[1] is ArrayType arr)
{
int repeat = arr.Rank;

if (!instanceSizeAndAlignment.Size.IsIndeterminate)
{
instanceSizeAndAlignment.Size = new LayoutInt(instanceSizeAndAlignment.Size.AsInt * repeat);
}

if (!instanceByteSizeAndAlignment.Size.IsIndeterminate)
{
instanceByteSizeAndAlignment.Size = new LayoutInt(instanceByteSizeAndAlignment.Size.AsInt * repeat);
}
}
}
}

ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout();
computedLayout.FieldAlignment = instanceSizeAndAlignment.Alignment;
computedLayout.FieldSize = instanceSizeAndAlignment.Size;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,32 @@ 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;
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())
Expand All @@ -37,16 +50,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);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ public static TypeDesc ConvertToCanon(TypeDesc typeToConvert, ref CanonicalFormK
}
else if (typeToConvert.IsArray)
{
TypeDesc elementType = ((ArrayType)typeToConvert).ElementType;
if (elementType.IsObject)
{
return typeToConvert;
}

return context.CanonType;
}
else
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/appdomain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,8 @@ void SystemDomain::LoadBaseSystemClasses()
g_pICastableInterface = CoreLibBinder::GetClass(CLASS__ICASTABLE);
#endif // FEATURE_ICASTABLE

g_pValueArrayClass = CoreLibBinder::GetClass(CLASS__VALUE_ARRAY);

// Make sure that FCall mapping for Monitor.Enter is initialized. We need it in case Monitor.Enter is used only as JIT helper.
// For more details, see comment in code:JITutil_MonEnterWorker around "__me = GetEEFuncEntryPointMacro(JIT_MonEnter)".
ECall::GetFCallImpl(CoreLibBinder::GetMethod(METHOD__MONITOR__ENTER));
Expand Down
22 changes: 16 additions & 6 deletions src/coreclr/vm/class.h
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,18 @@ class EEClass // DO NOT CREATE A NEW EEClass USING NEW!
LIMITED_METHOD_CONTRACT;
m_VMFlags |= (DWORD)VMFLAG_HAS_FIELDS_WHICH_MUST_BE_INITED;
}

BOOL HasNoPromotionFlagSet()
{
LIMITED_METHOD_CONTRACT;
return (m_VMFlags & VMFLAG_DONT_PROMOTE);
}
void SetNoPromotionFlag()
{
LIMITED_METHOD_CONTRACT;
m_VMFlags |= (DWORD)VMFLAG_DONT_PROMOTE;
}

void SetCannotBeBlittedByObjectCloner()
{
/* no op */
Expand Down Expand Up @@ -1669,7 +1681,8 @@ class EEClass // DO NOT CREATE A NEW EEClass USING NEW!
VMFLAG_BESTFITMAPPING = 0x00004000, // BestFitMappingAttribute.Value
VMFLAG_THROWONUNMAPPABLECHAR = 0x00008000, // BestFitMappingAttribute.ThrowOnUnmappableChar

// unused = 0x00010000,
// suppress struct promotion (used by ValueArrays)
VMFLAG_DONT_PROMOTE = 0x00010000,
VMFLAG_NO_GUID = 0x00020000,
VMFLAG_HASNONPUBLICFIELDS = 0x00040000,
// unused = 0x00080000,
Expand Down Expand Up @@ -1975,7 +1988,7 @@ class ArrayClass : public EEClass
private:

DAC_ALIGNAS(EEClass) // Align the first member to the alignment of the base class
unsigned char m_rank;
DWORD m_rank;
CorElementType m_ElementType;// Cache of element type in m_ElementTypeHnd

public:
Expand All @@ -1986,10 +1999,7 @@ class ArrayClass : public EEClass
}
void SetRank (unsigned Rank) {
LIMITED_METHOD_CONTRACT;
// The only code path calling this function is code:ClassLoader::CreateTypeHandleForTypeKey, which has
// checked the rank already. Assert that the rank is less than MAX_RANK and that it fits in one byte.
_ASSERTE((Rank <= MAX_RANK) && (Rank <= (unsigned char)(-1)));
m_rank = (unsigned char)Rank;
m_rank = Rank;
}

CorElementType GetArrayElementType() {
Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/vm/clsload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3046,10 +3046,6 @@ TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracke
}
}

if (rank > MAX_RANK)
{
ThrowTypeLoadException(pKey, IDS_CLASSLOAD_RANK_TOOLARGE);
}

templateMT = pLoaderModule->CreateArrayMethodTable(paramType, kind, rank, pamTracker);
typeHnd = TypeHandle(templateMT);
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,8 @@ DEFINE_CLASS(VALUE_TYPE, System, ValueType)
DEFINE_METHOD(VALUE_TYPE, GET_HASH_CODE, GetHashCode, IM_RetInt)
DEFINE_METHOD(VALUE_TYPE, EQUALS, Equals, IM_Obj_RetBool)

DEFINE_CLASS(VALUE_ARRAY, System, ValueArray`2)

DEFINE_CLASS(GC, System, GC)
DEFINE_METHOD(GC, KEEP_ALIVE, KeepAlive, SM_Obj_RetVoid)
DEFINE_METHOD(GC, COLLECT, Collect, SM_RetVoid)
Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/vm/generics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ TypeHandle ClassLoader::CanonicalizeGenericArg(TypeHandle thGenericArg)
#if defined(FEATURE_SHARE_GENERIC_CODE)
CorElementType et = thGenericArg.GetSignatureCorElementType();

// canonical form of MD object array is an MD object array - do not lose the rank
if (et == ELEMENT_TYPE_ARRAY &&
thGenericArg.GetArrayElementTypeHandle() == TypeHandle(g_pObjectClass))
{
RETURN(thGenericArg);
}

// Note that generic variables do not share

if (CorTypeInfo::IsObjRef_NoThrow(et))
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3701,8 +3701,13 @@ uint32_t CEEInfo::getClassAttribsInternal (CORINFO_CLASS_HANDLE clsHnd)
if (pClass->IsUnsafeValueClass())
ret |= CORINFO_FLG_UNSAFE_VALUECLASS;
}

if (pClass->HasExplicitFieldOffsetLayout() && pClass->HasOverLayedField())
ret |= CORINFO_FLG_OVERLAPPING_FIELDS;

if (pClass->HasNoPromotionFlagSet())
ret |= CORINFO_FLG_DONT_PROMOTE;

if (VMClsHnd.IsCanonicalSubtype())
ret |= CORINFO_FLG_SHAREDINST;

Expand Down
Loading