From f5a5d9cafd7747521451e3a0b78baffd8889e49f Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 15 Oct 2021 10:59:31 -0700 Subject: [PATCH 01/18] Add non-intrinsic implementation for CreateSpan. (#60451) * Add non-intrinsic implementation for CreateSpan. * Validate RVA field for Span Co-authored-by: David Wrighton --- .../RuntimeHelpers.CoreCLR.cs | 7 ++ .../classlibnative/bcltype/arraynative.cpp | 37 ++++++++++ .../classlibnative/bcltype/arraynative.h | 5 ++ src/coreclr/vm/ecalllist.h | 1 + src/coreclr/vm/runtimehandles.h | 2 +- .../CompilerServices/RuntimeHelpers.cs | 11 +++ .../System.Runtime/ref/System.Runtime.cs | 1 + .../CompilerServices/RuntimeHelpers.Mono.cs | 16 +++++ src/mono/mono/metadata/class-accessors.c | 32 ++++++++- src/mono/mono/metadata/class-internals.h | 9 +++ src/mono/mono/metadata/class.c | 71 ++++++++++++++++--- src/mono/mono/metadata/icall-def-netcore.h | 1 + src/mono/mono/metadata/icall.c | 31 ++++++++ src/tests/JIT/Intrinsics/CreateSpan.cs | 19 +++++ src/tests/JIT/Intrinsics/CreateSpan_r.csproj | 13 ++++ src/tests/JIT/Intrinsics/CreateSpan_ro.csproj | 13 ++++ 16 files changed, 256 insertions(+), 13 deletions(-) create mode 100644 src/tests/JIT/Intrinsics/CreateSpan.cs create mode 100644 src/tests/JIT/Intrinsics/CreateSpan_r.csproj create mode 100644 src/tests/JIT/Intrinsics/CreateSpan_ro.csproj diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index fd5c7f4bab8b7..3819ff1345f23 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -17,6 +17,13 @@ public static partial class RuntimeHelpers [MethodImpl(MethodImplOptions.InternalCall)] public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe void GetSpanDataFrom( + RuntimeFieldHandle fldHandle, + RuntimeTypeHandle targetTypeHandle, + void** data, + int* count); + // GetObjectValue is intended to allow value classes to be manipulated as 'Object' // but have aliasing behavior of a value class. The intent is that you would use // this function just before an assignment to a variable of type 'Object'. If the diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index b0236e918704b..8356ea08c1423 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -1153,3 +1153,40 @@ FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntim HELPER_METHOD_FRAME_END(); } FCIMPLEND + +FCIMPL4(void, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetType, void** data, INT32* count) +{ + FCALL_CONTRACT; + struct + { + REFLECTFIELDREF refField; + } gc; + gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); + HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); + + FieldDesc* pField = (FieldDesc*)gc.refField->GetField(); + + if (!pField->IsRVA()) + COMPlusThrow(kArgumentException); + + TypeHandle targetTypeHandle = FCALL_RTH_TO_REFLECTCLASS(targetType)->GetType(); + if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum()) + COMPlusThrow(kArgumentException); + + DWORD totalSize = pField->LoadSize(); + DWORD targetTypeSize = targetTypeHandle.GetSize(); + + // Report the RVA field to the logger. + g_IBCLogger.LogRVADataAccess(pField); + + _ASSERTE(data != NULL && count != NULL); + *data = pField->GetStaticAddressHandle(NULL); + *count = (INT32)totalSize / targetTypeSize; + +#if BIGENDIAN + COMPlusThrow(kPlatformNotSupportedException); +#endif + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index af926d8e42458..902f49a7b91c5 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -14,6 +14,7 @@ #define _ARRAYNATIVE_H_ #include "fcall.h" +#include "runtimehandles.h" struct FCALLRuntimeFieldHandle { @@ -45,6 +46,10 @@ class ArrayNative // to a field. static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); + // This method will acquire data to create a span from a TypeHandle + // to a field. + static FCDECL4(void, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetType, void** data, INT32* count); + private: // Helper for CreateInstance static void CheckElementType(TypeHandle elementType); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index a8c054d5b23c4..a4c9825735eec 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -854,6 +854,7 @@ FCFuncEnd() FCFuncStart(gRuntimeHelpers) FCFuncElement("GetObjectValue", ObjectNative::GetObjectValue) FCIntrinsic("InitializeArray", ArrayNative::InitializeArray, CORINFO_INTRINSIC_InitializeArray) + FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) QCFuncElement("RunClassConstructor", ReflectionInvocation::RunClassConstructor) QCFuncElement("RunModuleConstructor", ReflectionInvocation::RunModuleConstructor) QCFuncElement("CompileMethod", ReflectionInvocation::CompileMethod) diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index 284c8032bd316..2eb702e04422c 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -118,8 +118,8 @@ typedef RuntimeTypeHandle FCALLRuntimeTypeHandle; #define FCALL_RTH_TO_REFLECTCLASS(x) (x).pRuntimeTypeDONOTUSEDIRECTLY class RuntimeTypeHandle { - ReflectClassBaseObject *pRuntimeTypeDONOTUSEDIRECTLY; public: + ReflectClassBaseObject *pRuntimeTypeDONOTUSEDIRECTLY; // Static method on RuntimeTypeHandle diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 5b91776722c59..1e47192f9e78f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -109,5 +109,16 @@ public static void PrepareConstrainedRegionsNoOP() internal static bool IsPrimitiveType(this CorElementType et) // COR_ELEMENT_TYPE_I1,I2,I4,I8,U1,U2,U4,U8,R4,R8,I,U,CHAR,BOOLEAN => ((1 << (int)et) & 0b_0011_0000_0000_0011_1111_1111_1100) != 0; + + public static ReadOnlySpan CreateSpan(RuntimeFieldHandle fldHandle) + { + unsafe + { + void* data = default; + int count = default; + GetSpanDataFrom(fldHandle, typeof(T).TypeHandle, &data, &count); + return new ReadOnlySpan(data, count); + } + } } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index bd2e0d81869b2..50fe6ca0670ed 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -13120,6 +13120,7 @@ public static void ExecuteCodeWithGuaranteedCleanup(System.Runtime.CompilerServi public static T[] GetSubArray(T[] array, System.Range range) { throw null; } public static object GetUninitializedObject([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type) { throw null; } public static void InitializeArray(System.Array array, System.RuntimeFieldHandle fldHandle) { } + public static ReadOnlySpan CreateSpan(System.RuntimeFieldHandle fldHandle) { throw null; } public static bool IsReferenceOrContainsReferences() { throw null; } [System.ObsoleteAttribute("The Constrained Execution Region (CER) feature is not supported.", DiagnosticId = "SYSLIB0004", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public static void PrepareConstrainedRegions() { } diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index 01446a2ad9e4a..c8093437dfad7 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -19,6 +19,15 @@ public static void InitializeArray(Array array, RuntimeFieldHandle fldHandle) InitializeArray(array, fldHandle.Value); } + private static unsafe void GetSpanDataFrom( + RuntimeFieldHandle fldHandle, + RuntimeTypeHandle targetTypeHandle, + void** data, + int* count) + { + GetSpanDataFrom(fldHandle.Value, targetTypeHandle.Value, new IntPtr(data), new IntPtr(count)); + } + public static int OffsetToStringData { [Intrinsic] @@ -165,6 +174,13 @@ public static object GetUninitializedObject( [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern void InitializeArray(Array array, IntPtr fldHandle); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern unsafe void GetSpanDataFrom( + IntPtr fldHandle, + IntPtr targetTypeHandle, + IntPtr data, + IntPtr count); + [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern void RunClassConstructor(IntPtr type); diff --git a/src/mono/mono/metadata/class-accessors.c b/src/mono/mono/metadata/class-accessors.c index fbbbc8cda4be4..76065cb45c456 100644 --- a/src/mono/mono/metadata/class-accessors.c +++ b/src/mono/mono/metadata/class-accessors.c @@ -25,7 +25,10 @@ typedef enum { PROP_FIELD_DEF_VALUES = 7, /* MonoFieldDefaultValue* */ PROP_DECLSEC_FLAGS = 8, /* guint32 */ PROP_WEAK_BITMAP = 9, - PROP_DIM_CONFLICTS = 10 /* GSList of MonoMethod* */ + PROP_DIM_CONFLICTS = 10, /* GSList of MonoMethod* */ + PROP_FIELD_DEF_VALUES_2BYTESWIZZLE = 11, /* MonoFieldDefaultValue* with default values swizzled at 2 byte boundaries*/ + PROP_FIELD_DEF_VALUES_4BYTESWIZZLE = 12, /* MonoFieldDefaultValue* with default values swizzled at 4 byte boundaries*/ + PROP_FIELD_DEF_VALUES_8BYTESWIZZLE = 13 /* MonoFieldDefaultValue* with default values swizzled at 8 byte boundaries*/ } InfrequentDataKind; /* Accessors based on class kind*/ @@ -382,12 +385,39 @@ mono_class_get_field_def_values (MonoClass *klass) return (MonoFieldDefaultValue*)get_pointer_property (klass, PROP_FIELD_DEF_VALUES); } +MonoFieldDefaultValue* +mono_class_get_field_def_values_with_swizzle (MonoClass *klass, int swizzle) +{ + InfrequentDataKind dataKind = PROP_FIELD_DEF_VALUES; + if (swizzle == 2) + dataKind = PROP_FIELD_DEF_VALUES_2BYTESWIZZLE; + else if (swizzle == 4) + dataKind = PROP_FIELD_DEF_VALUES_4BYTESWIZZLE; + else + dataKind = PROP_FIELD_DEF_VALUES_8BYTESWIZZLE; + return (MonoFieldDefaultValue*)get_pointer_property (klass, dataKind); +} + + void mono_class_set_field_def_values (MonoClass *klass, MonoFieldDefaultValue *values) { set_pointer_property (klass, PROP_FIELD_DEF_VALUES, values); } +void +mono_class_set_field_def_values_with_swizzle (MonoClass *klass, MonoFieldDefaultValue *values, int swizzle) +{ + InfrequentDataKind dataKind = PROP_FIELD_DEF_VALUES; + if (swizzle == 2) + dataKind = PROP_FIELD_DEF_VALUES_2BYTESWIZZLE; + else if (swizzle == 4) + dataKind = PROP_FIELD_DEF_VALUES_4BYTESWIZZLE; + else + dataKind = PROP_FIELD_DEF_VALUES_8BYTESWIZZLE; + set_pointer_property (klass, dataKind, values); +} + guint32 mono_class_get_declsec_flags (MonoClass *klass) { diff --git a/src/mono/mono/metadata/class-internals.h b/src/mono/mono/metadata/class-internals.h index 3759bb4de274f..72234b97d4542 100644 --- a/src/mono/mono/metadata/class-internals.h +++ b/src/mono/mono/metadata/class-internals.h @@ -1406,9 +1406,15 @@ mono_class_set_event_info (MonoClass *klass, MonoClassEventInfo *info); MonoFieldDefaultValue* mono_class_get_field_def_values (MonoClass *klass); +MonoFieldDefaultValue* +mono_class_get_field_def_values_with_swizzle (MonoClass *klass, int swizzle); + void mono_class_set_field_def_values (MonoClass *klass, MonoFieldDefaultValue *values); +void +mono_class_set_field_def_values_with_swizzle (MonoClass *klass, MonoFieldDefaultValue *values, int swizzle); + guint32 mono_class_get_declsec_flags (MonoClass *klass); @@ -1467,6 +1473,9 @@ mono_class_get_object_finalize_slot (void); MonoMethod * mono_class_get_default_finalize_method (void); +const char * +mono_field_get_rva (MonoClassField *field, int swizzle); + void mono_field_resolve_type (MonoClassField *field, MonoError *error); diff --git a/src/mono/mono/metadata/class.c b/src/mono/mono/metadata/class.c index 2a1fd8f1e4d72..e73295a936e32 100644 --- a/src/mono/mono/metadata/class.c +++ b/src/mono/mono/metadata/class.c @@ -5435,8 +5435,8 @@ mono_field_get_offset (MonoClassField *field) return field->offset; } -static const char * -mono_field_get_rva (MonoClassField *field) +const char * +mono_field_get_rva (MonoClassField *field, int swizzle) { guint32 rva; int field_index; @@ -5445,21 +5445,70 @@ mono_field_get_rva (MonoClassField *field) g_assert (field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA); - def_values = mono_class_get_field_def_values (klass); + def_values = mono_class_get_field_def_values_with_swizzle (klass, swizzle); if (!def_values) { def_values = (MonoFieldDefaultValue *)mono_class_alloc0 (klass, sizeof (MonoFieldDefaultValue) * mono_class_get_field_count (klass)); - mono_class_set_field_def_values (klass, def_values); + mono_class_set_field_def_values_with_swizzle (klass, def_values, swizzle); } field_index = mono_field_get_index (field); - if (!def_values [field_index].data && !image_is_dynamic (m_class_get_image (klass))) { - int first_field_idx = mono_class_get_first_field_idx (klass); - mono_metadata_field_info (m_class_get_image (field->parent), first_field_idx + field_index, NULL, &rva, NULL); - if (!rva) - g_warning ("field %s in %s should have RVA data, but hasn't", mono_field_get_name (field), m_class_get_name (field->parent)); - def_values [field_index].data = mono_image_rva_map (m_class_get_image (field->parent), rva); + if (!def_values [field_index].data) { + const char *rvaData; + + if (!image_is_dynamic (m_class_get_image (klass))) + { + int first_field_idx = mono_class_get_first_field_idx (klass); + mono_metadata_field_info (m_class_get_image (field->parent), first_field_idx + field_index, NULL, &rva, NULL); + if (!rva) + g_warning ("field %s in %s should have RVA data, but hasn't", mono_field_get_name (field), m_class_get_name (field->parent)); + + rvaData = mono_image_rva_map (m_class_get_image (field->parent), rva); + } + else + { + rvaData = mono_field_get_data (field); + } + + if (rvaData == NULL) + return NULL; + + if (swizzle != 1) + { + int dummy; + int dataSizeInBytes = mono_type_size (field->type, &dummy); + char *swizzledRvaData = mono_class_alloc0 (klass, dataSizeInBytes); + +#define SWAP(n) { \ + guint ## n *data = (guint ## n *) swizzledRvaData; \ + guint ## n *src = (guint ## n *) rvaData; \ + int i, \ + nEnt = (dataSizeInBytes / sizeof(guint ## n)); \ + \ + for (i = 0; i < nEnt; i++) { \ + data[i] = read ## n (&src[i]); \ + } \ +} + if (swizzle == 2) + { + SWAP (16); + } + else if (swizzle == 4) + { + SWAP (32); + } + else + { + SWAP (64); + } + def_values [field_index].data = swizzledRvaData; + } + else +#undef SWAP + { + def_values [field_index].data = rvaData; + } } return def_values [field_index].data; @@ -5480,7 +5529,7 @@ mono_field_get_data (MonoClassField *field) return mono_class_get_field_default_value (field, &def_type); } else if (field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA) { - return mono_field_get_rva (field); + return mono_field_get_rva (field, 1); } else { return NULL; } diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index 1f0a400008e5e..71b389aef0711 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -315,6 +315,7 @@ HANDLES(MPROP_5, "internal_from_handle_type", ves_icall_System_Reflection_Runtim ICALL_TYPE(RUNH, "System.Runtime.CompilerServices.RuntimeHelpers", RUNH_1) HANDLES(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetObjectValue, MonoObject, 1, (MonoObject)) +HANDLES(RUNH_6, "GetSpanDataFrom", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom, void, 4, (MonoClassField_ptr, MonoType_ptr, gpointer, gpointer)) HANDLES(RUNH_2, "GetUninitializedObjectInternal", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetUninitializedObjectInternal, MonoObject, 1, (MonoType_ptr)) HANDLES(RUNH_3, "InitializeArray", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray, void, 2, (MonoArray, MonoClassField_ptr)) HANDLES(RUNH_7, "InternalGetHashCode", mono_object_hash_icall, int, 1, (MonoObject)) diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index da35682da1321..edd2e771865db 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -942,6 +942,37 @@ ves_icall_System_Runtime_RuntimeImports_ZeroMemory (guint8 *p, size_t byte_lengt memset (p, 0, byte_length); } +void +ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom (MonoClassField *field_handle, MonoType_ptr targetTypeHandle, gpointer dataPtr, gpointer countPtr, MonoError *error) +{ + gint32* count = (gint32*)countPtr; + const char **data = (const char **)dataPtr; + MonoType *field_type = mono_field_get_type_checked (field_handle, error); + if (!field_type) + return; + + if (!(field_type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA)) { + mono_error_set_argument_format (error, "field_handle", "Field '%s' doesn't have an RVA", mono_field_get_name (field_handle)); + return; + } + + MonoType *type = targetTypeHandle; + if (MONO_TYPE_IS_REFERENCE (type) || type->type == MONO_TYPE_VALUETYPE) { + mono_error_set_argument (error, "array", "Cannot initialize array of non-primitive type"); + return; + } + + int swizzle = 1; + int align; +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + swizzle = mono_type_size (type, &align); +#endif + + *data = mono_field_get_rva (field_handle, swizzle); + int dummy; + *count = mono_type_size (field_type, &dummy)/mono_type_size (type, &align); +} + void ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray (MonoArrayHandle array, MonoClassField *field_handle, MonoError *error) { diff --git a/src/tests/JIT/Intrinsics/CreateSpan.cs b/src/tests/JIT/Intrinsics/CreateSpan.cs new file mode 100644 index 0000000000000..09381fda48ab1 --- /dev/null +++ b/src/tests/JIT/Intrinsics/CreateSpan.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using System; + +class CreateSpanTest +{ + static int Main() + { + ReadOnlySpan intSpan = (ReadOnlySpan)new int[]{25,15,35,25}; + int result = 0; + foreach (int i in intSpan) + { + result += i; + } + return result; + } +} \ No newline at end of file diff --git a/src/tests/JIT/Intrinsics/CreateSpan_r.csproj b/src/tests/JIT/Intrinsics/CreateSpan_r.csproj new file mode 100644 index 0000000000000..c6d2a855474d5 --- /dev/null +++ b/src/tests/JIT/Intrinsics/CreateSpan_r.csproj @@ -0,0 +1,13 @@ + + + Exe + + + true + None + + + + + + diff --git a/src/tests/JIT/Intrinsics/CreateSpan_ro.csproj b/src/tests/JIT/Intrinsics/CreateSpan_ro.csproj new file mode 100644 index 0000000000000..2bc8b85d2e073 --- /dev/null +++ b/src/tests/JIT/Intrinsics/CreateSpan_ro.csproj @@ -0,0 +1,13 @@ + + + Exe + + + true + None + True + + + + + From 52a7dd305de7dc2a73526e10005f1b31ce14cd7f Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 15 Oct 2021 17:35:26 -0700 Subject: [PATCH 02/18] [hackathon] Implement CreateSpan intrinsic. (#60498) * Implement CreateSpan intrinsic. --- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/importer.cpp | 109 ++++++++++++++++++ src/coreclr/jit/namedintrinsiclist.h | 2 + .../CompilerServices/RuntimeHelpers.cs | 1 + 4 files changed, 113 insertions(+) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f8dfa34e65046..5e0be6e117d01 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4434,6 +4434,7 @@ class Compiler bool readonlyCall, CorInfoIntrinsics intrinsicID); GenTree* impInitializeArrayIntrinsic(CORINFO_SIG_INFO* sig); + GenTree* impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig); GenTree* impKeepAliveIntrinsic(GenTree* objToKeepAlive); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index d1c955fddb321..d43a57594d8e7 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3647,6 +3647,102 @@ const char* Compiler::impGetIntrinsicName(CorInfoIntrinsics intrinsicID) #endif // DEBUG +GenTree* Compiler::impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig) +{ + assert(sig->numArgs == 1); + assert(sig->sigInst.methInstCount == 1); + + GenTree* fieldTokenNode = impStackTop(0).val; + + // + // Verify that the field token is known and valid. Note that it's also + // possible for the token to come from reflection, in which case we cannot do + // the optimization and must therefore revert to calling the helper. You can + // see an example of this in bvt\DynIL\initarray2.exe (in Main). + // + + // Check to see if the ldtoken helper call is what we see here. + if (fieldTokenNode->gtOper != GT_CALL || (fieldTokenNode->AsCall()->gtCallType != CT_HELPER) || + (fieldTokenNode->AsCall()->gtCallMethHnd != eeFindHelper(CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD))) + { + return nullptr; + } + + // Strip helper call away + fieldTokenNode = fieldTokenNode->AsCall()->gtCallArgs->GetNode(); + if (fieldTokenNode->gtOper == GT_IND) + { + fieldTokenNode = fieldTokenNode->AsOp()->gtOp1; + } + + // Check for constant + if (fieldTokenNode->gtOper != GT_CNS_INT) + { + return nullptr; + } + + CORINFO_FIELD_HANDLE fieldToken = (CORINFO_FIELD_HANDLE)fieldTokenNode->AsIntCon()->gtCompileTimeHandle; + if (!fieldTokenNode->IsIconHandle(GTF_ICON_FIELD_HDL) || (fieldToken == nullptr)) + { + return nullptr; + } + + CORINFO_CLASS_HANDLE fieldOwnerHnd = info.compCompHnd->getFieldClass(fieldToken); + + CORINFO_CLASS_HANDLE fieldClsHnd; + var_types fieldElementType = JITtype2varType(info.compCompHnd->getFieldType(fieldToken, &fieldClsHnd, fieldOwnerHnd)); + assert(fieldElementType == var_types::TYP_STRUCT); + + const unsigned totalFieldSize = info.compCompHnd->getClassSize(fieldClsHnd); + + // Limit to primitive or enum type - see ArrayNative::GetSpanDataFrom() + CORINFO_CLASS_HANDLE targetElemHnd = sig->sigInst.methInst[0]; + if (info.compCompHnd->getTypeForPrimitiveValueClass(targetElemHnd) == CORINFO_TYPE_UNDEF) + { + return nullptr; + } + + const unsigned targetElemSize = info.compCompHnd->getClassSize(targetElemHnd); + assert(targetElemSize != 0); + + const unsigned count = totalFieldSize / targetElemSize; + if (count == 0) + { + return nullptr; + } + + void* data = info.compCompHnd->getArrayInitializationData(fieldToken, totalFieldSize); + if (!data) + { + return nullptr; + } + + // + // Ready to commit to the work + // + + impPopStack(); + + // Turn count and pointer value into constants. + GenTree* spanElemCount = gtNewIconNode(count, TYP_INT); + GenTree* spanPointerField = gtNewIconNode((ssize_t)data, TYP_I_IMPL); + + // Construct ReadOnlySpan to return. + unsigned spanTTemp = lvaGrabTemp(true DEBUGARG("ReadOnlySpan for CreateSpan")); + lvaSetStruct(spanTTemp, sig->retTypeClass, false); + GenTree* addrOfSpanTTempPointer = gtNewOperNode(GT_ADDR, TYP_BYREF, impCreateLocalNode(spanTTemp DEBUGARG(0))); // HACKATHON OFFSET not set usefully here + GenTree* dereffedSpanTTempPointer = gtNewOperNode(GT_IND, TYP_BYREF, addrOfSpanTTempPointer); + GenTree* gtAssignedSpanTTempPointer = gtNewAssignNode(dereffedSpanTTempPointer, spanPointerField); + + GenTree* addrOfSpanTTempPointer2 = gtNewOperNode(GT_ADDR, TYP_BYREF, impCreateLocalNode(spanTTemp DEBUGARG(0))); // HACKATHON OFFSET not set usefully here + GenTree* addrOfSpanTTempSize = gtNewOperNode(GT_ADD, TYP_BYREF, addrOfSpanTTempPointer2, gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL)); // HACKATHON OFFSET not set usefully here + GenTree* dereffedSpanTTempSize = gtNewOperNode(GT_IND, TYP_INT, addrOfSpanTTempSize); + GenTree* gtAssignedSpanTTempSize = gtNewAssignNode(dereffedSpanTTempSize, spanElemCount); + + GenTree* gtCommaAssignPointerAndGetResult = gtNewOperNode(GT_COMMA, TYP_STRUCT, gtAssignedSpanTTempPointer, impCreateLocalNode(spanTTemp DEBUGARG(0))); + return gtNewOperNode(GT_COMMA, TYP_STRUCT, gtAssignedSpanTTempSize, gtCommaAssignPointerAndGetResult); +} + //------------------------------------------------------------------------ // impIntrinsic: possibly expand intrinsic call into alternate IR sequence // @@ -4074,6 +4170,12 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, break; } + case NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan: + { + retNode = impCreateSpanIntrinsic(sig); + break; + } + case NI_System_Span_get_Item: case NI_System_ReadOnlySpan_get_Item: { @@ -5190,6 +5292,13 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) result = SimdAsHWIntrinsicInfo::lookupId(&sig, className, methodName, enclosingClassName, sizeOfVectorT); } #endif // FEATURE_HW_INTRINSICS + else if ((strcmp(namespaceName, "System.Runtime.CompilerServices") == 0) && (strcmp(className, "RuntimeHelpers") == 0)) + { + if (strcmp(methodName, "CreateSpan") == 0) + { + result = NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan; + } + } else if (strncmp(namespaceName, "System.Runtime.Intrinsics", 25) == 0) { // We go down this path even when FEATURE_HW_INTRINSICS isn't enabled diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index 8a0af00d3ec47..75740aaa4dca7 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -61,6 +61,8 @@ enum NamedIntrinsic : unsigned short NI_System_Array_GetUpperBound, NI_System_Object_MemberwiseClone, + NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan, + NI_System_String_get_Chars, NI_System_String_get_Length, NI_System_Span_get_Item, diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 1e47192f9e78f..85ea8d218bc04 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -110,6 +110,7 @@ internal static bool IsPrimitiveType(this CorElementType et) // COR_ELEMENT_TYPE_I1,I2,I4,I8,U1,U2,U4,U8,R4,R8,I,U,CHAR,BOOLEAN => ((1 << (int)et) & 0b_0011_0000_0000_0011_1111_1111_1100) != 0; + [Intrinsic] public static ReadOnlySpan CreateSpan(RuntimeFieldHandle fldHandle) { unsafe From 5917c2c9a1d2e0e74668f34f10214baf392d3153 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Sat, 16 Oct 2021 18:30:36 -0700 Subject: [PATCH 03/18] Fix CreateSpan build failure (#60510) --- .../tools/Common/JitInterface/CorInfoImpl.cs | 31 ++++++++++++++++--- .../Common/TypeSystem/Common/Instantiation.cs | 30 +++++++++++++++++- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index bbc0c99d4f597..c812a99c216a0 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -558,6 +558,8 @@ private void CompileMethodCleanup() _actualInstructionSetUnsupported = default(InstructionSetFlags); #endif + _instantiationToJitVisibleInstantiation = null; + _pgoResults.Clear(); } @@ -644,6 +646,25 @@ private bool Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, CORIN return true; } + private Dictionary _instantiationToJitVisibleInstantiation = null; + private CORINFO_CLASS_STRUCT_** GetJitInstantiation(Instantiation inst) + { + IntPtr [] jitVisibleInstantiation; + if (_instantiationToJitVisibleInstantiation == null) + { + _instantiationToJitVisibleInstantiation = new Dictionary(); + } + + if (!_instantiationToJitVisibleInstantiation.TryGetValue(inst, out jitVisibleInstantiation)) + { + jitVisibleInstantiation = new IntPtr[inst.Length]; + for (int i = 0; i < inst.Length; i++) + jitVisibleInstantiation[i] = (IntPtr)ObjectToHandle(inst[i]); + _instantiationToJitVisibleInstantiation.Add(inst, jitVisibleInstantiation); + } + return (CORINFO_CLASS_STRUCT_**)GetPin(jitVisibleInstantiation); + } + private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, bool suppressHiddenArgument = false) { Get_CORINFO_SIG_INFO(method.Signature, sig); @@ -668,10 +689,12 @@ private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, bool sig->sigInst.classInstCount = (uint)owningTypeInst.Length; if (owningTypeInst.Length > 0) { - var classInst = new IntPtr[owningTypeInst.Length]; - for (int i = 0; i < owningTypeInst.Length; i++) - classInst[i] = (IntPtr)ObjectToHandle(owningTypeInst[i]); - sig->sigInst.classInst = (CORINFO_CLASS_STRUCT_**)GetPin(classInst); + sig->sigInst.classInst = GetJitInstantiation(owningTypeInst); + } + + if (method.Instantiation.Length != 0) + { + sig->sigInst.methInst = GetJitInstantiation(method.Instantiation); } } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs b/src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs index 136f7f4e04b10..313a1b11c7a8a 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs @@ -11,7 +11,7 @@ namespace Internal.TypeSystem /// Represents a generic instantiation - a collection of generic parameters /// or arguments of a generic type or a generic method. /// - public struct Instantiation + public struct Instantiation : IEquatable { private TypeDesc[] _genericParameters; @@ -113,5 +113,33 @@ public bool MoveNext() return true; } } + + public bool Equals(Instantiation other) + { + if (_genericParameters.Length != other._genericParameters.Length) + return false; + + for (int i = 0; i < _genericParameters.Length; i++) + { + if (_genericParameters[i] != other._genericParameters[i]) + return false; + } + return true; + } + public override bool Equals(object o) + { + if (o is Instantiation inst) + return Equals(inst); + return false; + } + public override int GetHashCode() + { + int hashcode = 1; + foreach (var t in _genericParameters) + { + hashcode = (hashcode << 3) ^ t.GetHashCode(); + } + return hashcode; + } } } From fc747796be8d6bfd452ddd29f0653206e09b7454 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Sun, 17 Oct 2021 14:28:51 -0700 Subject: [PATCH 04/18] Most static initialization data fields are of a structure type, but it is legal for them to be of a primitive type. Fix the CreateSpan intrinsic to handle that case (#60525) --- src/coreclr/jit/importer.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index d43a57594d8e7..e910488d27fe6 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3691,9 +3691,17 @@ GenTree* Compiler::impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig) CORINFO_CLASS_HANDLE fieldClsHnd; var_types fieldElementType = JITtype2varType(info.compCompHnd->getFieldType(fieldToken, &fieldClsHnd, fieldOwnerHnd)); - assert(fieldElementType == var_types::TYP_STRUCT); + unsigned totalFieldSize; - const unsigned totalFieldSize = info.compCompHnd->getClassSize(fieldClsHnd); + // Most static initialization data fields are of some structure, but it is possible for them to be of various primitive types as well + if (fieldElementType == var_types::TYP_STRUCT) + { + totalFieldSize = info.compCompHnd->getClassSize(fieldClsHnd); + } + else + { + totalFieldSize = genTypeSize(fieldElementType); + } // Limit to primitive or enum type - see ArrayNative::GetSpanDataFrom() CORINFO_CLASS_HANDLE targetElemHnd = sig->sigInst.methInst[0]; From b18bb64febc11f7df67fc4795949c32c5bb27f2e Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 1 Nov 2021 16:45:15 -0700 Subject: [PATCH 05/18] Fix CreateSpan in R2R code generation --- src/coreclr/jit/importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index e910488d27fe6..7dd44ec3623e6 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3733,7 +3733,7 @@ GenTree* Compiler::impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig) // Turn count and pointer value into constants. GenTree* spanElemCount = gtNewIconNode(count, TYP_INT); - GenTree* spanPointerField = gtNewIconNode((ssize_t)data, TYP_I_IMPL); + GenTree* spanPointerField = gtNewIconHandleNode((size_t)data, GTF_ICON_CONST_PTR); // Construct ReadOnlySpan to return. unsigned spanTTemp = lvaGrabTemp(true DEBUGARG("ReadOnlySpan for CreateSpan")); From 1950ef1a74f769ba83f3ab9f6a7b2dbd07e2fcdf Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 1 Nov 2021 16:51:56 -0700 Subject: [PATCH 06/18] Convert CreateSpan test to IL --- src/tests/JIT/Intrinsics/CreateSpan_il.il | 117 ++++++++++++++++++ src/tests/JIT/Intrinsics/CreateSpan_il.ilproj | 12 ++ 2 files changed, 129 insertions(+) create mode 100644 src/tests/JIT/Intrinsics/CreateSpan_il.il create mode 100644 src/tests/JIT/Intrinsics/CreateSpan_il.ilproj diff --git a/src/tests/JIT/Intrinsics/CreateSpan_il.il b/src/tests/JIT/Intrinsics/CreateSpan_il.il new file mode 100644 index 0000000000000..61421a584392c --- /dev/null +++ b/src/tests/JIT/Intrinsics/CreateSpan_il.il @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +// This test covers creation and use of a ReadOnlySpan which points at constant data +// using the CreateSpan api. + +// Metadata version: v4.0.30319 +.assembly extern System.Runtime +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: + .ver 7:0:0:0 +} +.assembly CreateSpan_ro +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) + + .permissionset reqmin + = {[System.Runtime]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module CreateSpan_ro.dll +// MVID: {E6E30CA2-CC29-4014-B26A-79C94A13BF58} +.custom instance void [System.Runtime]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x000001ADF9680000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class private auto ansi beforefieldinit CreateSpanTest + extends [System.Runtime]System.Object +{ + .method private hidebysig static int32 + Main() cil managed + { + .entrypoint + // Code size 47 (0x2f) + .maxstack 2 + .locals init (int32 V_0, + valuetype [System.Runtime]System.ReadOnlySpan`1 V_1, + int32 V_2, + int32 V_3) + IL_0000: ldtoken field valuetype ''/'__StaticArrayInitTypeSize=16' ''::'8A4C0D07C79596FFAE679AE1790CCA491B4E0D51EE259857EC635CA222073650' + IL_0005: call valuetype [System.Runtime]System.ReadOnlySpan`1 [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::CreateSpan(valuetype [System.Runtime]System.RuntimeFieldHandle) + IL_000a: ldc.i4.0 + IL_000b: stloc.0 + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: stloc.2 + IL_000f: br.s IL_0023 + + IL_0011: ldloca.s V_1 + IL_0013: ldloc.2 + IL_0014: call instance !0& modreq([System.Runtime]System.Runtime.InteropServices.InAttribute) valuetype [System.Runtime]System.ReadOnlySpan`1::get_Item(int32) + IL_0019: ldind.i4 + IL_001a: stloc.3 + IL_001b: ldloc.0 + IL_001c: ldloc.3 + IL_001d: add + IL_001e: stloc.0 + IL_001f: ldloc.2 + IL_0020: ldc.i4.1 + IL_0021: add + IL_0022: stloc.2 + IL_0023: ldloc.2 + IL_0024: ldloca.s V_1 + IL_0026: call instance int32 valuetype [System.Runtime]System.ReadOnlySpan`1::get_Length() + IL_002b: blt.s IL_0011 + + IL_002d: ldloc.0 + IL_002e: ret + } // end of method CreateSpanTest::Main + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [System.Runtime]System.Object::.ctor() + IL_0006: ret + } // end of method CreateSpanTest::.ctor + +} // end of class CreateSpanTest + +.class private auto ansi sealed '' + extends [System.Runtime]System.Object +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=16' + extends [System.Runtime]System.ValueType + { + .pack 1 + .size 16 + } // end of class '__StaticArrayInitTypeSize=16' + + .field static assembly initonly valuetype ''/'__StaticArrayInitTypeSize=16' '8A4C0D07C79596FFAE679AE1790CCA491B4E0D51EE259857EC635CA222073650' at I_000026F8 +} // end of class '' + + +// ============================================================= + +.data cil I_000026F8 = bytearray ( + 19 00 00 00 0F 00 00 00 23 00 00 00 19 00 00 00) // ........#....... +// *********** DISASSEMBLY COMPLETE *********************** +// WARNING: Created Win32 resource file CreateSpan_il.res diff --git a/src/tests/JIT/Intrinsics/CreateSpan_il.ilproj b/src/tests/JIT/Intrinsics/CreateSpan_il.ilproj new file mode 100644 index 0000000000000..403e74dc75156 --- /dev/null +++ b/src/tests/JIT/Intrinsics/CreateSpan_il.ilproj @@ -0,0 +1,12 @@ + + + Exe + + + PdbOnly + True + + + + + From 320452cb9e8b004a88b4eca2f49759e3b87c58a3 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 1 Nov 2021 17:06:05 -0700 Subject: [PATCH 07/18] Remove CreateSpan C# based test as the Roslyn used in the main branch does not support CreateSpan --- src/tests/JIT/Intrinsics/CreateSpan.cs | 19 ------------------- src/tests/JIT/Intrinsics/CreateSpan_r.csproj | 13 ------------- src/tests/JIT/Intrinsics/CreateSpan_ro.csproj | 13 ------------- 3 files changed, 45 deletions(-) delete mode 100644 src/tests/JIT/Intrinsics/CreateSpan.cs delete mode 100644 src/tests/JIT/Intrinsics/CreateSpan_r.csproj delete mode 100644 src/tests/JIT/Intrinsics/CreateSpan_ro.csproj diff --git a/src/tests/JIT/Intrinsics/CreateSpan.cs b/src/tests/JIT/Intrinsics/CreateSpan.cs deleted file mode 100644 index 09381fda48ab1..0000000000000 --- a/src/tests/JIT/Intrinsics/CreateSpan.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// - -using System; - -class CreateSpanTest -{ - static int Main() - { - ReadOnlySpan intSpan = (ReadOnlySpan)new int[]{25,15,35,25}; - int result = 0; - foreach (int i in intSpan) - { - result += i; - } - return result; - } -} \ No newline at end of file diff --git a/src/tests/JIT/Intrinsics/CreateSpan_r.csproj b/src/tests/JIT/Intrinsics/CreateSpan_r.csproj deleted file mode 100644 index c6d2a855474d5..0000000000000 --- a/src/tests/JIT/Intrinsics/CreateSpan_r.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - Exe - - - true - None - - - - - - diff --git a/src/tests/JIT/Intrinsics/CreateSpan_ro.csproj b/src/tests/JIT/Intrinsics/CreateSpan_ro.csproj deleted file mode 100644 index 2bc8b85d2e073..0000000000000 --- a/src/tests/JIT/Intrinsics/CreateSpan_ro.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - Exe - - - true - None - True - - - - - From 415ea45c2f0893711617614bd227c4516d607003 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 1 Nov 2021 17:27:47 -0700 Subject: [PATCH 08/18] Add xml doc comment for CreateSpan api --- .../src/System/Runtime/CompilerServices/RuntimeHelpers.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 85ea8d218bc04..390e36a29f07a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -110,6 +110,11 @@ internal static bool IsPrimitiveType(this CorElementType et) // COR_ELEMENT_TYPE_I1,I2,I4,I8,U1,U2,U4,U8,R4,R8,I,U,CHAR,BOOLEAN => ((1 << (int)et) & 0b_0011_0000_0000_0011_1111_1111_1100) != 0; + /// Provide a fast way to access constant data stored in a module as a ReadOnlySpan{T} + /// A field handle that specifies the location of the data to be referred to by the ReadOnlySpan{T}. The Rva of the field must be aligned on a natural boundary of type T + /// A ReadOnlySpan{T} of the data stored in the field + /// does not refer to a field which is an Rva, is misaligned, or T is of an invalid type. + /// This method is intended for compiler user rather than use directly in code. T must be one of byte, sbyte, char, short, ushort, int, long, ulong, float, or double. [Intrinsic] public static ReadOnlySpan CreateSpan(RuntimeFieldHandle fldHandle) { From 0636c7927fd462a353065572b6176ae4afb91d3c Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 1 Nov 2021 17:36:42 -0700 Subject: [PATCH 09/18] Fix xml comment --- .../src/System/Runtime/CompilerServices/RuntimeHelpers.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 390e36a29f07a..c7a1c1c0481ed 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -111,9 +111,9 @@ internal static bool IsPrimitiveType(this CorElementType et) => ((1 << (int)et) & 0b_0011_0000_0000_0011_1111_1111_1100) != 0; /// Provide a fast way to access constant data stored in a module as a ReadOnlySpan{T} - /// A field handle that specifies the location of the data to be referred to by the ReadOnlySpan{T}. The Rva of the field must be aligned on a natural boundary of type T + /// A field handle that specifies the location of the data to be referred to by the ReadOnlySpan{T}. The Rva of the field must be aligned on a natural boundary of type T /// A ReadOnlySpan{T} of the data stored in the field - /// does not refer to a field which is an Rva, is misaligned, or T is of an invalid type. + /// does not refer to a field which is an Rva, is misaligned, or T is of an invalid type. /// This method is intended for compiler user rather than use directly in code. T must be one of byte, sbyte, char, short, ushort, int, long, ulong, float, or double. [Intrinsic] public static ReadOnlySpan CreateSpan(RuntimeFieldHandle fldHandle) From cd636f03bb2ad03755505cb9945405d41730eb7e Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 1 Nov 2021 18:04:42 -0700 Subject: [PATCH 10/18] Update src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Co-authored-by: Jan Kotas --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index c812a99c216a0..1cee25ff40028 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -692,6 +692,7 @@ private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, bool sig->sigInst.classInst = GetJitInstantiation(owningTypeInst); } + sig->sigInst.methodnstCount = (uint)method.Instantiation.Length; if (method.Instantiation.Length != 0) { sig->sigInst.methInst = GetJitInstantiation(method.Instantiation); From 510c0d2fe37866608ebc7056119e3949a0913e47 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 1 Nov 2021 18:04:50 -0700 Subject: [PATCH 11/18] Update src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs Co-authored-by: Jan Kotas --- src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs b/src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs index 313a1b11c7a8a..3a1241b1c515a 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs @@ -137,7 +137,7 @@ public override int GetHashCode() int hashcode = 1; foreach (var t in _genericParameters) { - hashcode = (hashcode << 3) ^ t.GetHashCode(); + hashcode = BitOperations.RotateLeft(hashcode, 3) ^ t.GetHashCode(); } return hashcode; } From 0dac4b1be6a8923ad3c7787e36b836901c16fd33 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 1 Nov 2021 18:07:30 -0700 Subject: [PATCH 12/18] Apply suggestions from code review --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 1cee25ff40028..62101f7802d31 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -687,12 +687,12 @@ private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, bool // JIT doesn't care what the instantiation is and this is expensive. Instantiation owningTypeInst = method.OwningType.Instantiation; sig->sigInst.classInstCount = (uint)owningTypeInst.Length; - if (owningTypeInst.Length > 0) + if (owningTypeInst.Length != 0) { sig->sigInst.classInst = GetJitInstantiation(owningTypeInst); } - sig->sigInst.methodnstCount = (uint)method.Instantiation.Length; + sig->sigInst.methodInstCount = (uint)method.Instantiation.Length; if (method.Instantiation.Length != 0) { sig->sigInst.methInst = GetJitInstantiation(method.Instantiation); From 4987e01d933eee4de531c738bce8d7dcc2ca8d45 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 2 Nov 2021 11:54:46 -0700 Subject: [PATCH 13/18] Update based on code review feedback to improve generated code --- src/coreclr/jit/importer.cpp | 38 +++++++++++-------- .../tools/Common/JitInterface/CorInfoImpl.cs | 2 +- .../Common/TypeSystem/Common/Instantiation.cs | 10 +---- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 7dd44ec3623e6..d1a6c6b4d3371 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3732,23 +3732,31 @@ GenTree* Compiler::impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig) impPopStack(); // Turn count and pointer value into constants. - GenTree* spanElemCount = gtNewIconNode(count, TYP_INT); - GenTree* spanPointerField = gtNewIconHandleNode((size_t)data, GTF_ICON_CONST_PTR); + GenTree* lengthValue = gtNewIconNode(count, TYP_INT); + GenTree* pointerValue = gtNewIconHandleNode((size_t)data, GTF_ICON_CONST_PTR); // Construct ReadOnlySpan to return. - unsigned spanTTemp = lvaGrabTemp(true DEBUGARG("ReadOnlySpan for CreateSpan")); - lvaSetStruct(spanTTemp, sig->retTypeClass, false); - GenTree* addrOfSpanTTempPointer = gtNewOperNode(GT_ADDR, TYP_BYREF, impCreateLocalNode(spanTTemp DEBUGARG(0))); // HACKATHON OFFSET not set usefully here - GenTree* dereffedSpanTTempPointer = gtNewOperNode(GT_IND, TYP_BYREF, addrOfSpanTTempPointer); - GenTree* gtAssignedSpanTTempPointer = gtNewAssignNode(dereffedSpanTTempPointer, spanPointerField); - - GenTree* addrOfSpanTTempPointer2 = gtNewOperNode(GT_ADDR, TYP_BYREF, impCreateLocalNode(spanTTemp DEBUGARG(0))); // HACKATHON OFFSET not set usefully here - GenTree* addrOfSpanTTempSize = gtNewOperNode(GT_ADD, TYP_BYREF, addrOfSpanTTempPointer2, gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL)); // HACKATHON OFFSET not set usefully here - GenTree* dereffedSpanTTempSize = gtNewOperNode(GT_IND, TYP_INT, addrOfSpanTTempSize); - GenTree* gtAssignedSpanTTempSize = gtNewAssignNode(dereffedSpanTTempSize, spanElemCount); - - GenTree* gtCommaAssignPointerAndGetResult = gtNewOperNode(GT_COMMA, TYP_STRUCT, gtAssignedSpanTTempPointer, impCreateLocalNode(spanTTemp DEBUGARG(0))); - return gtNewOperNode(GT_COMMA, TYP_STRUCT, gtAssignedSpanTTempSize, gtCommaAssignPointerAndGetResult); + CORINFO_CLASS_HANDLE spanHnd = sig->retTypeClass; + unsigned spanTempNum = lvaGrabTemp(true DEBUGARG("ReadOnlySpan for CreateSpan")); + lvaSetStruct(spanTempNum, spanHnd, false); + + CORINFO_FIELD_HANDLE pointerFieldHnd = info.compCompHnd->getFieldInClass(spanHnd, 0); + CORINFO_FIELD_HANDLE lengthFieldHnd = info.compCompHnd->getFieldInClass(spanHnd, 1); + + GenTreeLclFld* pointerField = gtNewLclFldNode(spanTempNum, TYP_BYREF, 0); + pointerField->SetFieldSeq(GetFieldSeqStore()->CreateSingleton(pointerFieldHnd)); + GenTree* pointerFieldAsg = gtNewAssignNode(pointerField, pointerValue); + + GenTreeLclFld* lengthField = gtNewLclFldNode(spanTempNum, TYP_INT, TARGET_POINTER_SIZE); + lengthField->SetFieldSeq(GetFieldSeqStore()->CreateSingleton(lengthFieldHnd)); + GenTree* lengthFieldAsg = gtNewAssignNode(lengthField, lengthValue); + + // Now append a few statements the initialize the span + impAppendTree(lengthFieldAsg, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(pointerFieldAsg, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + + // And finally create a tree that points at the span. + return impCreateLocalNode(spanTempNum DEBUGARG(0)); } //------------------------------------------------------------------------ diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 1cee25ff40028..d687480c52c03 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -692,7 +692,7 @@ private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, bool sig->sigInst.classInst = GetJitInstantiation(owningTypeInst); } - sig->sigInst.methodnstCount = (uint)method.Instantiation.Length; + sig->sigInst.methInstCount = (uint)method.Instantiation.Length; if (method.Instantiation.Length != 0) { sig->sigInst.methInst = GetJitInstantiation(method.Instantiation); diff --git a/src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs b/src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs index 3a1241b1c515a..d3b0fdd5686e9 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/Instantiation.cs @@ -132,14 +132,6 @@ public override bool Equals(object o) return Equals(inst); return false; } - public override int GetHashCode() - { - int hashcode = 1; - foreach (var t in _genericParameters) - { - hashcode = BitOperations.RotateLeft(hashcode, 3) ^ t.GetHashCode(); - } - return hashcode; - } + public override int GetHashCode() => ComputeGenericInstanceHashCode(1); } } From 2356d02174459c1a9e1fc8eee75e067d2be3f918 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 2 Nov 2021 14:21:33 -0700 Subject: [PATCH 14/18] Fix JIT formatting issues --- src/coreclr/jit/importer.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index d1a6c6b4d3371..27e052be946af 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3690,10 +3690,12 @@ GenTree* Compiler::impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig) CORINFO_CLASS_HANDLE fieldOwnerHnd = info.compCompHnd->getFieldClass(fieldToken); CORINFO_CLASS_HANDLE fieldClsHnd; - var_types fieldElementType = JITtype2varType(info.compCompHnd->getFieldType(fieldToken, &fieldClsHnd, fieldOwnerHnd)); + var_types fieldElementType = + JITtype2varType(info.compCompHnd->getFieldType(fieldToken, &fieldClsHnd, fieldOwnerHnd)); unsigned totalFieldSize; - // Most static initialization data fields are of some structure, but it is possible for them to be of various primitive types as well + // Most static initialization data fields are of some structure, but it is possible for them to be of various + // primitive types as well if (fieldElementType == var_types::TYP_STRUCT) { totalFieldSize = info.compCompHnd->getClassSize(fieldClsHnd); @@ -3704,7 +3706,7 @@ GenTree* Compiler::impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig) } // Limit to primitive or enum type - see ArrayNative::GetSpanDataFrom() - CORINFO_CLASS_HANDLE targetElemHnd = sig->sigInst.methInst[0]; + CORINFO_CLASS_HANDLE targetElemHnd = sig->sigInst.methInst[0]; if (info.compCompHnd->getTypeForPrimitiveValueClass(targetElemHnd) == CORINFO_TYPE_UNDEF) { return nullptr; @@ -3732,7 +3734,7 @@ GenTree* Compiler::impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig) impPopStack(); // Turn count and pointer value into constants. - GenTree* lengthValue = gtNewIconNode(count, TYP_INT); + GenTree* lengthValue = gtNewIconNode(count, TYP_INT); GenTree* pointerValue = gtNewIconHandleNode((size_t)data, GTF_ICON_CONST_PTR); // Construct ReadOnlySpan to return. @@ -5308,7 +5310,8 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) result = SimdAsHWIntrinsicInfo::lookupId(&sig, className, methodName, enclosingClassName, sizeOfVectorT); } #endif // FEATURE_HW_INTRINSICS - else if ((strcmp(namespaceName, "System.Runtime.CompilerServices") == 0) && (strcmp(className, "RuntimeHelpers") == 0)) + else if ((strcmp(namespaceName, "System.Runtime.CompilerServices") == 0) && + (strcmp(className, "RuntimeHelpers") == 0)) { if (strcmp(methodName, "CreateSpan") == 0) { From fbf632be65d9319bde2df64440e5d0368ac8e91d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 2 Nov 2021 16:19:49 -0700 Subject: [PATCH 15/18] MustExpand intrinsic for NativeAOT --- src/coreclr/jit/importer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 27e052be946af..3e87cdd2308ce 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3920,6 +3920,12 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, return new (this, GT_LABEL) GenTree(GT_LABEL, TYP_I_IMPL); } + if ((ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan) && IsTargetAbi(CORINFO_CORERT_ABI)) + { + // CreateSpan must be expanded for NativeAOT + mustExpand = true; + } + GenTree* retNode = nullptr; // Under debug and minopts, only expand what is required. From 6aec0ab99ec8a4ae61a2266add6b0721ccf0fa5a Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 17 Nov 2021 18:01:14 -0800 Subject: [PATCH 16/18] Respond to code review --- .../RuntimeHelpers.CoreCLR.cs | 5 ++-- .../classlibnative/bcltype/arraynative.cpp | 10 +++++-- .../classlibnative/bcltype/arraynative.h | 2 +- .../CompilerServices/RuntimeHelpers.cs | 11 +------- .../CompilerServices/RuntimeHelpers.Mono.cs | 13 ++++----- src/mono/mono/metadata/class.c | 27 ++++++------------- src/mono/mono/metadata/icall-def-netcore.h | 2 +- src/mono/mono/metadata/icall.c | 17 ++++++------ 8 files changed, 37 insertions(+), 50 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 3819ff1345f23..fc31d9d4ff1b8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -18,11 +18,10 @@ public static partial class RuntimeHelpers public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void GetSpanDataFrom( + private static extern unsafe void* GetSpanDataFrom( RuntimeFieldHandle fldHandle, RuntimeTypeHandle targetTypeHandle, - void** data, - int* count); + out int count); // GetObjectValue is intended to allow value classes to be manipulated as 'Object' // but have aliasing behavior of a value class. The intent is that you would use diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 8356ea08c1423..cbf6120c0126f 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -1154,7 +1154,7 @@ FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntim } FCIMPLEND -FCIMPL4(void, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetType, void** data, INT32* count) +FCIMPL3(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetType, INT32* count) { FCALL_CONTRACT; struct @@ -1162,6 +1162,7 @@ FCIMPL4(void, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, REFLECTFIELDREF refField; } gc; gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); + void* data; HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); FieldDesc* pField = (FieldDesc*)gc.refField->GetField(); @@ -1180,7 +1181,11 @@ FCIMPL4(void, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, g_IBCLogger.LogRVADataAccess(pField); _ASSERTE(data != NULL && count != NULL); - *data = pField->GetStaticAddressHandle(NULL); + data = pField->GetStaticAddressHandle(NULL); + + if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data) + COMPlusThrow(kArgumentException); + *count = (INT32)totalSize / targetTypeSize; #if BIGENDIAN @@ -1188,5 +1193,6 @@ FCIMPL4(void, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, #endif HELPER_METHOD_FRAME_END(); + return data; } FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 902f49a7b91c5..6a1c8979525cd 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -48,7 +48,7 @@ class ArrayNative // This method will acquire data to create a span from a TypeHandle // to a field. - static FCDECL4(void, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetType, void** data, INT32* count); + static FCDECL3(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetType, INT32* count); private: // Helper for CreateInstance diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index c7a1c1c0481ed..ddaf26982c575 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -116,15 +116,6 @@ internal static bool IsPrimitiveType(this CorElementType et) /// does not refer to a field which is an Rva, is misaligned, or T is of an invalid type. /// This method is intended for compiler user rather than use directly in code. T must be one of byte, sbyte, char, short, ushort, int, long, ulong, float, or double. [Intrinsic] - public static ReadOnlySpan CreateSpan(RuntimeFieldHandle fldHandle) - { - unsafe - { - void* data = default; - int count = default; - GetSpanDataFrom(fldHandle, typeof(T).TypeHandle, &data, &count); - return new ReadOnlySpan(data, count); - } - } + public static unsafe ReadOnlySpan CreateSpan(RuntimeFieldHandle fldHandle) => new ReadOnlySpan(GetSpanDataFrom(fldHandle, typeof(T).TypeHandle, out int length), length); } } diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index c8093437dfad7..5cf723dc60aed 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -19,13 +19,15 @@ public static void InitializeArray(Array array, RuntimeFieldHandle fldHandle) InitializeArray(array, fldHandle.Value); } - private static unsafe void GetSpanDataFrom( + private static unsafe void* GetSpanDataFrom( RuntimeFieldHandle fldHandle, RuntimeTypeHandle targetTypeHandle, - void** data, - int* count) + out int count) { - GetSpanDataFrom(fldHandle.Value, targetTypeHandle.Value, new IntPtr(data), new IntPtr(count)); + fixed (int *pCount = &count) + { + return (void*)GetSpanDataFrom(fldHandle.Value, targetTypeHandle.Value, new IntPtr(pCount)); + } } public static int OffsetToStringData @@ -175,10 +177,9 @@ public static object GetUninitializedObject( private static extern void InitializeArray(Array array, IntPtr fldHandle); [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern unsafe void GetSpanDataFrom( + private static extern unsafe IntPtr GetSpanDataFrom( IntPtr fldHandle, IntPtr targetTypeHandle, - IntPtr data, IntPtr count); [MethodImplAttribute(MethodImplOptions.InternalCall)] diff --git a/src/mono/mono/metadata/class.c b/src/mono/mono/metadata/class.c index e73295a936e32..076419d078604 100644 --- a/src/mono/mono/metadata/class.c +++ b/src/mono/mono/metadata/class.c @@ -5457,25 +5457,21 @@ mono_field_get_rva (MonoClassField *field, int swizzle) if (!def_values [field_index].data) { const char *rvaData; - if (!image_is_dynamic (m_class_get_image (klass))) - { + if (!image_is_dynamic (m_class_get_image (klass))) { int first_field_idx = mono_class_get_first_field_idx (klass); mono_metadata_field_info (m_class_get_image (field->parent), first_field_idx + field_index, NULL, &rva, NULL); if (!rva) g_warning ("field %s in %s should have RVA data, but hasn't", mono_field_get_name (field), m_class_get_name (field->parent)); rvaData = mono_image_rva_map (m_class_get_image (field->parent), rva); - } - else - { + } else { rvaData = mono_field_get_data (field); } if (rvaData == NULL) return NULL; - if (swizzle != 1) - { + if (swizzle != 1) { int dummy; int dataSizeInBytes = mono_type_size (field->type, &dummy); char *swizzledRvaData = mono_class_alloc0 (klass, dataSizeInBytes); @@ -5490,23 +5486,16 @@ mono_field_get_rva (MonoClassField *field, int swizzle) data[i] = read ## n (&src[i]); \ } \ } - if (swizzle == 2) - { + if (swizzle == 2) { SWAP (16); - } - else if (swizzle == 4) - { + } else if (swizzle == 4) { SWAP (32); - } - else - { + } else { SWAP (64); } - def_values [field_index].data = swizzledRvaData; - } - else #undef SWAP - { + def_values [field_index].data = swizzledRvaData; + } else { def_values [field_index].data = rvaData; } } diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index 71b389aef0711..03d5403df0a83 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -315,7 +315,7 @@ HANDLES(MPROP_5, "internal_from_handle_type", ves_icall_System_Reflection_Runtim ICALL_TYPE(RUNH, "System.Runtime.CompilerServices.RuntimeHelpers", RUNH_1) HANDLES(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetObjectValue, MonoObject, 1, (MonoObject)) -HANDLES(RUNH_6, "GetSpanDataFrom", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom, void, 4, (MonoClassField_ptr, MonoType_ptr, gpointer, gpointer)) +HANDLES(RUNH_6, "GetSpanDataFrom", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom, gpointer, 3, (MonoClassField_ptr, MonoType_ptr, gpointer)) HANDLES(RUNH_2, "GetUninitializedObjectInternal", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetUninitializedObjectInternal, MonoObject, 1, (MonoType_ptr)) HANDLES(RUNH_3, "InitializeArray", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray, void, 2, (MonoArray, MonoClassField_ptr)) HANDLES(RUNH_7, "InternalGetHashCode", mono_object_hash_icall, int, 1, (MonoObject)) diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index edd2e771865db..f38a12e604da4 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -942,24 +942,25 @@ ves_icall_System_Runtime_RuntimeImports_ZeroMemory (guint8 *p, size_t byte_lengt memset (p, 0, byte_length); } -void -ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom (MonoClassField *field_handle, MonoType_ptr targetTypeHandle, gpointer dataPtr, gpointer countPtr, MonoError *error) +gpointer +ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom (MonoClassField *field_handle, MonoType_ptr targetTypeHandle, gpointer countPtr, MonoError *error) { gint32* count = (gint32*)countPtr; - const char **data = (const char **)dataPtr; MonoType *field_type = mono_field_get_type_checked (field_handle, error); - if (!field_type) - return; + if (!field_type) { + mono_error_set_argument (error, "fldHandle", "fldHandle invalid"); + return NULL; + } if (!(field_type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA)) { mono_error_set_argument_format (error, "field_handle", "Field '%s' doesn't have an RVA", mono_field_get_name (field_handle)); - return; + return NULL; } MonoType *type = targetTypeHandle; if (MONO_TYPE_IS_REFERENCE (type) || type->type == MONO_TYPE_VALUETYPE) { mono_error_set_argument (error, "array", "Cannot initialize array of non-primitive type"); - return; + return NULL; } int swizzle = 1; @@ -968,9 +969,9 @@ ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom (MonoCl swizzle = mono_type_size (type, &align); #endif - *data = mono_field_get_rva (field_handle, swizzle); int dummy; *count = mono_type_size (field_type, &dummy)/mono_type_size (type, &align); + return (gpointer)mono_field_get_rva (field_handle, swizzle); } void From 38f080c6e4ba0d4d6c01c00ce786b06d27a7c55f Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 17 Nov 2021 18:19:27 -0800 Subject: [PATCH 17/18] Adjust to changes in JIT --- src/coreclr/jit/importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 5400bb95933e6..f67604a557362 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3759,8 +3759,8 @@ GenTree* Compiler::impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig) GenTree* lengthFieldAsg = gtNewAssignNode(lengthField, lengthValue); // Now append a few statements the initialize the span - impAppendTree(lengthFieldAsg, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); - impAppendTree(pointerFieldAsg, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs); + impAppendTree(lengthFieldAsg, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); + impAppendTree(pointerFieldAsg, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); // And finally create a tree that points at the span. return impCreateLocalNode(spanTempNum DEBUGARG(0)); From 34303ee5c7816f057dafe05984d6af07d0f76ba6 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 18 Nov 2021 11:49:50 -0800 Subject: [PATCH 18/18] Fix HELPER_METHOD_FRAME define usage --- src/coreclr/classlibnative/bcltype/arraynative.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index cbf6120c0126f..738fd98c7f9e7 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -1163,7 +1163,7 @@ FCIMPL3(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField } gc; gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); void* data; - HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); + HELPER_METHOD_FRAME_BEGIN_RET_1(gc); FieldDesc* pField = (FieldDesc*)gc.refField->GetField();