Skip to content

Commit f5a5d9c

Browse files
Add non-intrinsic implementation for CreateSpan<T>. (dotnet#60451)
* Add non-intrinsic implementation for CreateSpan<T>. * Validate RVA field for Span<T> Co-authored-by: David Wrighton <davidwr@microsoft.com>
1 parent 1d08e15 commit f5a5d9c

File tree

16 files changed

+256
-13
lines changed

16 files changed

+256
-13
lines changed

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ public static partial class RuntimeHelpers
1717
[MethodImpl(MethodImplOptions.InternalCall)]
1818
public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle);
1919

20+
[MethodImpl(MethodImplOptions.InternalCall)]
21+
private static extern unsafe void GetSpanDataFrom(
22+
RuntimeFieldHandle fldHandle,
23+
RuntimeTypeHandle targetTypeHandle,
24+
void** data,
25+
int* count);
26+
2027
// GetObjectValue is intended to allow value classes to be manipulated as 'Object'
2128
// but have aliasing behavior of a value class. The intent is that you would use
2229
// this function just before an assignment to a variable of type 'Object'. If the

src/coreclr/classlibnative/bcltype/arraynative.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,3 +1153,40 @@ FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntim
11531153
HELPER_METHOD_FRAME_END();
11541154
}
11551155
FCIMPLEND
1156+
1157+
FCIMPL4(void, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetType, void** data, INT32* count)
1158+
{
1159+
FCALL_CONTRACT;
1160+
struct
1161+
{
1162+
REFLECTFIELDREF refField;
1163+
} gc;
1164+
gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField));
1165+
HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
1166+
1167+
FieldDesc* pField = (FieldDesc*)gc.refField->GetField();
1168+
1169+
if (!pField->IsRVA())
1170+
COMPlusThrow(kArgumentException);
1171+
1172+
TypeHandle targetTypeHandle = FCALL_RTH_TO_REFLECTCLASS(targetType)->GetType();
1173+
if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum())
1174+
COMPlusThrow(kArgumentException);
1175+
1176+
DWORD totalSize = pField->LoadSize();
1177+
DWORD targetTypeSize = targetTypeHandle.GetSize();
1178+
1179+
// Report the RVA field to the logger.
1180+
g_IBCLogger.LogRVADataAccess(pField);
1181+
1182+
_ASSERTE(data != NULL && count != NULL);
1183+
*data = pField->GetStaticAddressHandle(NULL);
1184+
*count = (INT32)totalSize / targetTypeSize;
1185+
1186+
#if BIGENDIAN
1187+
COMPlusThrow(kPlatformNotSupportedException);
1188+
#endif
1189+
1190+
HELPER_METHOD_FRAME_END();
1191+
}
1192+
FCIMPLEND

src/coreclr/classlibnative/bcltype/arraynative.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define _ARRAYNATIVE_H_
1515

1616
#include "fcall.h"
17+
#include "runtimehandles.h"
1718

1819
struct FCALLRuntimeFieldHandle
1920
{
@@ -45,6 +46,10 @@ class ArrayNative
4546
// to a field.
4647
static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField);
4748

49+
// This method will acquire data to create a span from a TypeHandle
50+
// to a field.
51+
static FCDECL4(void, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetType, void** data, INT32* count);
52+
4853
private:
4954
// Helper for CreateInstance
5055
static void CheckElementType(TypeHandle elementType);

src/coreclr/vm/ecalllist.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,7 @@ FCFuncEnd()
854854
FCFuncStart(gRuntimeHelpers)
855855
FCFuncElement("GetObjectValue", ObjectNative::GetObjectValue)
856856
FCIntrinsic("InitializeArray", ArrayNative::InitializeArray, CORINFO_INTRINSIC_InitializeArray)
857+
FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom)
857858
QCFuncElement("RunClassConstructor", ReflectionInvocation::RunClassConstructor)
858859
QCFuncElement("RunModuleConstructor", ReflectionInvocation::RunModuleConstructor)
859860
QCFuncElement("CompileMethod", ReflectionInvocation::CompileMethod)

src/coreclr/vm/runtimehandles.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ typedef RuntimeTypeHandle FCALLRuntimeTypeHandle;
118118
#define FCALL_RTH_TO_REFLECTCLASS(x) (x).pRuntimeTypeDONOTUSEDIRECTLY
119119

120120
class RuntimeTypeHandle {
121-
ReflectClassBaseObject *pRuntimeTypeDONOTUSEDIRECTLY;
122121
public:
122+
ReflectClassBaseObject *pRuntimeTypeDONOTUSEDIRECTLY;
123123

124124
// Static method on RuntimeTypeHandle
125125

src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,5 +109,16 @@ public static void PrepareConstrainedRegionsNoOP()
109109
internal static bool IsPrimitiveType(this CorElementType et)
110110
// COR_ELEMENT_TYPE_I1,I2,I4,I8,U1,U2,U4,U8,R4,R8,I,U,CHAR,BOOLEAN
111111
=> ((1 << (int)et) & 0b_0011_0000_0000_0011_1111_1111_1100) != 0;
112+
113+
public static ReadOnlySpan<T> CreateSpan<T>(RuntimeFieldHandle fldHandle)
114+
{
115+
unsafe
116+
{
117+
void* data = default;
118+
int count = default;
119+
GetSpanDataFrom(fldHandle, typeof(T).TypeHandle, &data, &count);
120+
return new ReadOnlySpan<T>(data, count);
121+
}
122+
}
112123
}
113124
}

src/libraries/System.Runtime/ref/System.Runtime.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13120,6 +13120,7 @@ public static void ExecuteCodeWithGuaranteedCleanup(System.Runtime.CompilerServi
1312013120
public static T[] GetSubArray<T>(T[] array, System.Range range) { throw null; }
1312113121
public static object GetUninitializedObject([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type) { throw null; }
1312213122
public static void InitializeArray(System.Array array, System.RuntimeFieldHandle fldHandle) { }
13123+
public static ReadOnlySpan<T> CreateSpan<T>(System.RuntimeFieldHandle fldHandle) { throw null; }
1312313124
public static bool IsReferenceOrContainsReferences<T>() { throw null; }
1312413125
[System.ObsoleteAttribute("The Constrained Execution Region (CER) feature is not supported.", DiagnosticId = "SYSLIB0004", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
1312513126
public static void PrepareConstrainedRegions() { }

src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ public static void InitializeArray(Array array, RuntimeFieldHandle fldHandle)
1919
InitializeArray(array, fldHandle.Value);
2020
}
2121

22+
private static unsafe void GetSpanDataFrom(
23+
RuntimeFieldHandle fldHandle,
24+
RuntimeTypeHandle targetTypeHandle,
25+
void** data,
26+
int* count)
27+
{
28+
GetSpanDataFrom(fldHandle.Value, targetTypeHandle.Value, new IntPtr(data), new IntPtr(count));
29+
}
30+
2231
public static int OffsetToStringData
2332
{
2433
[Intrinsic]
@@ -165,6 +174,13 @@ public static object GetUninitializedObject(
165174
[MethodImplAttribute(MethodImplOptions.InternalCall)]
166175
private static extern void InitializeArray(Array array, IntPtr fldHandle);
167176

177+
[MethodImplAttribute(MethodImplOptions.InternalCall)]
178+
private static extern unsafe void GetSpanDataFrom(
179+
IntPtr fldHandle,
180+
IntPtr targetTypeHandle,
181+
IntPtr data,
182+
IntPtr count);
183+
168184
[MethodImplAttribute(MethodImplOptions.InternalCall)]
169185
private static extern void RunClassConstructor(IntPtr type);
170186

src/mono/mono/metadata/class-accessors.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ typedef enum {
2525
PROP_FIELD_DEF_VALUES = 7, /* MonoFieldDefaultValue* */
2626
PROP_DECLSEC_FLAGS = 8, /* guint32 */
2727
PROP_WEAK_BITMAP = 9,
28-
PROP_DIM_CONFLICTS = 10 /* GSList of MonoMethod* */
28+
PROP_DIM_CONFLICTS = 10, /* GSList of MonoMethod* */
29+
PROP_FIELD_DEF_VALUES_2BYTESWIZZLE = 11, /* MonoFieldDefaultValue* with default values swizzled at 2 byte boundaries*/
30+
PROP_FIELD_DEF_VALUES_4BYTESWIZZLE = 12, /* MonoFieldDefaultValue* with default values swizzled at 4 byte boundaries*/
31+
PROP_FIELD_DEF_VALUES_8BYTESWIZZLE = 13 /* MonoFieldDefaultValue* with default values swizzled at 8 byte boundaries*/
2932
} InfrequentDataKind;
3033

3134
/* Accessors based on class kind*/
@@ -382,12 +385,39 @@ mono_class_get_field_def_values (MonoClass *klass)
382385
return (MonoFieldDefaultValue*)get_pointer_property (klass, PROP_FIELD_DEF_VALUES);
383386
}
384387

388+
MonoFieldDefaultValue*
389+
mono_class_get_field_def_values_with_swizzle (MonoClass *klass, int swizzle)
390+
{
391+
InfrequentDataKind dataKind = PROP_FIELD_DEF_VALUES;
392+
if (swizzle == 2)
393+
dataKind = PROP_FIELD_DEF_VALUES_2BYTESWIZZLE;
394+
else if (swizzle == 4)
395+
dataKind = PROP_FIELD_DEF_VALUES_4BYTESWIZZLE;
396+
else
397+
dataKind = PROP_FIELD_DEF_VALUES_8BYTESWIZZLE;
398+
return (MonoFieldDefaultValue*)get_pointer_property (klass, dataKind);
399+
}
400+
401+
385402
void
386403
mono_class_set_field_def_values (MonoClass *klass, MonoFieldDefaultValue *values)
387404
{
388405
set_pointer_property (klass, PROP_FIELD_DEF_VALUES, values);
389406
}
390407

408+
void
409+
mono_class_set_field_def_values_with_swizzle (MonoClass *klass, MonoFieldDefaultValue *values, int swizzle)
410+
{
411+
InfrequentDataKind dataKind = PROP_FIELD_DEF_VALUES;
412+
if (swizzle == 2)
413+
dataKind = PROP_FIELD_DEF_VALUES_2BYTESWIZZLE;
414+
else if (swizzle == 4)
415+
dataKind = PROP_FIELD_DEF_VALUES_4BYTESWIZZLE;
416+
else
417+
dataKind = PROP_FIELD_DEF_VALUES_8BYTESWIZZLE;
418+
set_pointer_property (klass, dataKind, values);
419+
}
420+
391421
guint32
392422
mono_class_get_declsec_flags (MonoClass *klass)
393423
{

src/mono/mono/metadata/class-internals.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,9 +1406,15 @@ mono_class_set_event_info (MonoClass *klass, MonoClassEventInfo *info);
14061406
MonoFieldDefaultValue*
14071407
mono_class_get_field_def_values (MonoClass *klass);
14081408

1409+
MonoFieldDefaultValue*
1410+
mono_class_get_field_def_values_with_swizzle (MonoClass *klass, int swizzle);
1411+
14091412
void
14101413
mono_class_set_field_def_values (MonoClass *klass, MonoFieldDefaultValue *values);
14111414

1415+
void
1416+
mono_class_set_field_def_values_with_swizzle (MonoClass *klass, MonoFieldDefaultValue *values, int swizzle);
1417+
14121418
guint32
14131419
mono_class_get_declsec_flags (MonoClass *klass);
14141420

@@ -1467,6 +1473,9 @@ mono_class_get_object_finalize_slot (void);
14671473
MonoMethod *
14681474
mono_class_get_default_finalize_method (void);
14691475

1476+
const char *
1477+
mono_field_get_rva (MonoClassField *field, int swizzle);
1478+
14701479
void
14711480
mono_field_resolve_type (MonoClassField *field, MonoError *error);
14721481

src/mono/mono/metadata/class.c

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5435,8 +5435,8 @@ mono_field_get_offset (MonoClassField *field)
54355435
return field->offset;
54365436
}
54375437

5438-
static const char *
5439-
mono_field_get_rva (MonoClassField *field)
5438+
const char *
5439+
mono_field_get_rva (MonoClassField *field, int swizzle)
54405440
{
54415441
guint32 rva;
54425442
int field_index;
@@ -5445,21 +5445,70 @@ mono_field_get_rva (MonoClassField *field)
54455445

54465446
g_assert (field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA);
54475447

5448-
def_values = mono_class_get_field_def_values (klass);
5448+
def_values = mono_class_get_field_def_values_with_swizzle (klass, swizzle);
54495449
if (!def_values) {
54505450
def_values = (MonoFieldDefaultValue *)mono_class_alloc0 (klass, sizeof (MonoFieldDefaultValue) * mono_class_get_field_count (klass));
54515451

5452-
mono_class_set_field_def_values (klass, def_values);
5452+
mono_class_set_field_def_values_with_swizzle (klass, def_values, swizzle);
54535453
}
54545454

54555455
field_index = mono_field_get_index (field);
54565456

5457-
if (!def_values [field_index].data && !image_is_dynamic (m_class_get_image (klass))) {
5458-
int first_field_idx = mono_class_get_first_field_idx (klass);
5459-
mono_metadata_field_info (m_class_get_image (field->parent), first_field_idx + field_index, NULL, &rva, NULL);
5460-
if (!rva)
5461-
g_warning ("field %s in %s should have RVA data, but hasn't", mono_field_get_name (field), m_class_get_name (field->parent));
5462-
def_values [field_index].data = mono_image_rva_map (m_class_get_image (field->parent), rva);
5457+
if (!def_values [field_index].data) {
5458+
const char *rvaData;
5459+
5460+
if (!image_is_dynamic (m_class_get_image (klass)))
5461+
{
5462+
int first_field_idx = mono_class_get_first_field_idx (klass);
5463+
mono_metadata_field_info (m_class_get_image (field->parent), first_field_idx + field_index, NULL, &rva, NULL);
5464+
if (!rva)
5465+
g_warning ("field %s in %s should have RVA data, but hasn't", mono_field_get_name (field), m_class_get_name (field->parent));
5466+
5467+
rvaData = mono_image_rva_map (m_class_get_image (field->parent), rva);
5468+
}
5469+
else
5470+
{
5471+
rvaData = mono_field_get_data (field);
5472+
}
5473+
5474+
if (rvaData == NULL)
5475+
return NULL;
5476+
5477+
if (swizzle != 1)
5478+
{
5479+
int dummy;
5480+
int dataSizeInBytes = mono_type_size (field->type, &dummy);
5481+
char *swizzledRvaData = mono_class_alloc0 (klass, dataSizeInBytes);
5482+
5483+
#define SWAP(n) { \
5484+
guint ## n *data = (guint ## n *) swizzledRvaData; \
5485+
guint ## n *src = (guint ## n *) rvaData; \
5486+
int i, \
5487+
nEnt = (dataSizeInBytes / sizeof(guint ## n)); \
5488+
\
5489+
for (i = 0; i < nEnt; i++) { \
5490+
data[i] = read ## n (&src[i]); \
5491+
} \
5492+
}
5493+
if (swizzle == 2)
5494+
{
5495+
SWAP (16);
5496+
}
5497+
else if (swizzle == 4)
5498+
{
5499+
SWAP (32);
5500+
}
5501+
else
5502+
{
5503+
SWAP (64);
5504+
}
5505+
def_values [field_index].data = swizzledRvaData;
5506+
}
5507+
else
5508+
#undef SWAP
5509+
{
5510+
def_values [field_index].data = rvaData;
5511+
}
54635512
}
54645513

54655514
return def_values [field_index].data;
@@ -5480,7 +5529,7 @@ mono_field_get_data (MonoClassField *field)
54805529

54815530
return mono_class_get_field_default_value (field, &def_type);
54825531
} else if (field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA) {
5483-
return mono_field_get_rva (field);
5532+
return mono_field_get_rva (field, 1);
54845533
} else {
54855534
return NULL;
54865535
}

src/mono/mono/metadata/icall-def-netcore.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ HANDLES(MPROP_5, "internal_from_handle_type", ves_icall_System_Reflection_Runtim
315315

316316
ICALL_TYPE(RUNH, "System.Runtime.CompilerServices.RuntimeHelpers", RUNH_1)
317317
HANDLES(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetObjectValue, MonoObject, 1, (MonoObject))
318+
HANDLES(RUNH_6, "GetSpanDataFrom", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom, void, 4, (MonoClassField_ptr, MonoType_ptr, gpointer, gpointer))
318319
HANDLES(RUNH_2, "GetUninitializedObjectInternal", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetUninitializedObjectInternal, MonoObject, 1, (MonoType_ptr))
319320
HANDLES(RUNH_3, "InitializeArray", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray, void, 2, (MonoArray, MonoClassField_ptr))
320321
HANDLES(RUNH_7, "InternalGetHashCode", mono_object_hash_icall, int, 1, (MonoObject))

src/mono/mono/metadata/icall.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,37 @@ ves_icall_System_Runtime_RuntimeImports_ZeroMemory (guint8 *p, size_t byte_lengt
942942
memset (p, 0, byte_length);
943943
}
944944

945+
void
946+
ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom (MonoClassField *field_handle, MonoType_ptr targetTypeHandle, gpointer dataPtr, gpointer countPtr, MonoError *error)
947+
{
948+
gint32* count = (gint32*)countPtr;
949+
const char **data = (const char **)dataPtr;
950+
MonoType *field_type = mono_field_get_type_checked (field_handle, error);
951+
if (!field_type)
952+
return;
953+
954+
if (!(field_type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA)) {
955+
mono_error_set_argument_format (error, "field_handle", "Field '%s' doesn't have an RVA", mono_field_get_name (field_handle));
956+
return;
957+
}
958+
959+
MonoType *type = targetTypeHandle;
960+
if (MONO_TYPE_IS_REFERENCE (type) || type->type == MONO_TYPE_VALUETYPE) {
961+
mono_error_set_argument (error, "array", "Cannot initialize array of non-primitive type");
962+
return;
963+
}
964+
965+
int swizzle = 1;
966+
int align;
967+
#if G_BYTE_ORDER != G_LITTLE_ENDIAN
968+
swizzle = mono_type_size (type, &align);
969+
#endif
970+
971+
*data = mono_field_get_rva (field_handle, swizzle);
972+
int dummy;
973+
*count = mono_type_size (field_type, &dummy)/mono_type_size (type, &align);
974+
}
975+
945976
void
946977
ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray (MonoArrayHandle array, MonoClassField *field_handle, MonoError *error)
947978
{
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
//
4+
5+
using System;
6+
7+
class CreateSpanTest
8+
{
9+
static int Main()
10+
{
11+
ReadOnlySpan<int> intSpan = (ReadOnlySpan<int>)new int[]{25,15,35,25};
12+
int result = 0;
13+
foreach (int i in intSpan)
14+
{
15+
result += i;
16+
}
17+
return result;
18+
}
19+
}

0 commit comments

Comments
 (0)