From 3efb476e2e22f7dd5ac96968203ef931704d34b8 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 30 Nov 2021 10:44:38 -0800 Subject: [PATCH] RuntimeHelpers.CreateSpan (#61079) Implement `RuntimeHelpers.CreateSpan` #60948 Implementation provides for - Both non-intrinsic and intrinsic implementations in CoreCLR - Non-intrinsic implementation in Mono - Mono implementation also implements untested big endian support Co-authored-by: Aaron Robinson Co-authored-by: Jan Kotas --- .../RuntimeHelpers.CoreCLR.cs | 6 + .../classlibnative/bcltype/arraynative.cpp | 43 ++++++ .../classlibnative/bcltype/arraynative.h | 5 + src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/importer.cpp | 134 ++++++++++++++++++ src/coreclr/jit/namedintrinsiclist.h | 2 + .../tools/Common/JitInterface/CorInfoImpl.cs | 34 ++++- .../Common/TypeSystem/Common/Instantiation.cs | 22 ++- src/coreclr/vm/ecalllist.h | 1 + src/coreclr/vm/runtimehandles.h | 2 +- .../CompilerServices/RuntimeHelpers.cs | 8 ++ .../System.Runtime/ref/System.Runtime.cs | 1 + .../CompilerServices/RuntimeHelpers.Mono.cs | 17 +++ src/mono/mono/metadata/class-accessors.c | 32 ++++- src/mono/mono/metadata/class-internals.h | 9 ++ src/mono/mono/metadata/class.c | 60 ++++++-- src/mono/mono/metadata/icall-def-netcore.h | 1 + src/mono/mono/metadata/icall.c | 32 +++++ src/tests/JIT/Intrinsics/CreateSpan_il.il | 117 +++++++++++++++ src/tests/JIT/Intrinsics/CreateSpan_il.ilproj | 12 ++ 20 files changed, 520 insertions(+), 19 deletions(-) create mode 100644 src/tests/JIT/Intrinsics/CreateSpan_il.il create mode 100644 src/tests/JIT/Intrinsics/CreateSpan_il.ilproj 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 8b55280cf2dbb..836c5934ccffe 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,12 @@ 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, + 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 // 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..738fd98c7f9e7 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -1153,3 +1153,46 @@ FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntim HELPER_METHOD_FRAME_END(); } FCIMPLEND + +FCIMPL3(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetType, INT32* count) +{ + FCALL_CONTRACT; + struct + { + REFLECTFIELDREF refField; + } gc; + gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); + void* data; + HELPER_METHOD_FRAME_BEGIN_RET_1(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); + + if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data) + COMPlusThrow(kArgumentException); + + *count = (INT32)totalSize / targetTypeSize; + +#if BIGENDIAN + COMPlusThrow(kPlatformNotSupportedException); +#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 af926d8e42458..6a1c8979525cd 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 FCDECL3(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetType, INT32* count); + private: // Helper for CreateInstance static void CheckElementType(TypeHandle elementType); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 7bf8c0c2f63ac..9f9b904fa6ea3 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4483,6 +4483,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 b8f52bf71aa8c..86eff6ee92505 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3652,6 +3652,120 @@ 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)); + 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 + 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]; + 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* lengthValue = gtNewIconNode(count, TYP_INT); + GenTree* pointerValue = gtNewIconHandleNode((size_t)data, GTF_ICON_CONST_PTR); + + // Construct ReadOnlySpan to return. + 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, impCurStmtDI); + impAppendTree(pointerFieldAsg, (unsigned)CHECK_SPILL_NONE, impCurStmtDI); + + // And finally create a tree that points at the span. + return impCreateLocalNode(spanTempNum DEBUGARG(0)); +} + //------------------------------------------------------------------------ // impIntrinsic: possibly expand intrinsic call into alternate IR sequence // @@ -3811,6 +3925,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. @@ -4079,6 +4199,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: { @@ -5195,6 +5321,14 @@ 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/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 4bffcc181daab..3d0c990ff6cc0 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -576,6 +576,8 @@ private void CompileMethodCleanup() _actualInstructionSetUnsupported = default(InstructionSetFlags); #endif + _instantiationToJitVisibleInstantiation = null; + _pgoResults.Clear(); } @@ -662,6 +664,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); @@ -684,12 +705,15 @@ 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.methInstCount = (uint)method.Instantiation.Length; + if (method.Instantiation.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.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..d3b0fdd5686e9 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,25 @@ 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() => ComputeGenericInstanceHashCode(1); } } diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index e02bad38f9430..01a9ef6c7332f 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -626,6 +626,7 @@ FCFuncEnd() FCFuncStart(gRuntimeHelpers) FCFuncElement("GetObjectValue", ObjectNative::GetObjectValue) FCIntrinsic("InitializeArray", ArrayNative::InitializeArray, CORINFO_INTRINSIC_InitializeArray) + FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("Equals", ObjectNative::Equals) diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index 184f27a7a311f..85b7fa3d8e7f5 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -113,8 +113,8 @@ typedef RuntimeTypeHandle FCALLRuntimeTypeHandle; #define FCALL_RTH_TO_REFLECTCLASS(x) (x).pRuntimeTypeDONOTUSEDIRECTLY class RuntimeTypeHandle { - ReflectClassBaseObject *pRuntimeTypeDONOTUSEDIRECTLY; public: + ReflectClassBaseObject *pRuntimeTypeDONOTUSEDIRECTLY; // Static method on RuntimeTypeHandle static FCDECL1(Object*, AllocateComObject, void* pClassFactory); 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..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 @@ -109,5 +109,13 @@ 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; + + /// 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 unsafe ReadOnlySpan CreateSpan(RuntimeFieldHandle fldHandle) => new ReadOnlySpan(GetSpanDataFrom(fldHandle, typeof(T).TypeHandle, out int length), length); } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index a03a42d21a6f9..5da725f34f727 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -13203,6 +13203,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..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,6 +19,17 @@ public static void InitializeArray(Array array, RuntimeFieldHandle fldHandle) InitializeArray(array, fldHandle.Value); } + private static unsafe void* GetSpanDataFrom( + RuntimeFieldHandle fldHandle, + RuntimeTypeHandle targetTypeHandle, + out int count) + { + fixed (int *pCount = &count) + { + return (void*)GetSpanDataFrom(fldHandle.Value, targetTypeHandle.Value, new IntPtr(pCount)); + } + } + public static int OffsetToStringData { [Intrinsic] @@ -165,6 +176,12 @@ public static object GetUninitializedObject( [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern void InitializeArray(Array array, IntPtr fldHandle); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern unsafe IntPtr GetSpanDataFrom( + IntPtr fldHandle, + IntPtr targetTypeHandle, + 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..076419d078604 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,59 @@ 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); + } +#undef SWAP + def_values [field_index].data = swizzledRvaData; + } else { + def_values [field_index].data = rvaData; + } } return def_values [field_index].data; @@ -5480,7 +5518,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..03d5403df0a83 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, 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 8309550934fbc..1597b0b866af3 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -942,6 +942,38 @@ ves_icall_System_Runtime_RuntimeImports_ZeroMemory (guint8 *p, size_t byte_lengt memset (p, 0, byte_length); } +gpointer +ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom (MonoClassField *field_handle, MonoType_ptr targetTypeHandle, gpointer countPtr, MonoError *error) +{ + gint32* count = (gint32*)countPtr; + MonoType *field_type = mono_field_get_type_checked (field_handle, error); + 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 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 NULL; + } + + int swizzle = 1; + int align; +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + swizzle = mono_type_size (type, &align); +#endif + + int dummy; + *count = mono_type_size (field_type, &dummy)/mono_type_size (type, &align); + return (gpointer)mono_field_get_rva (field_handle, swizzle); +} + void ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray (MonoArrayHandle array, MonoClassField *field_handle, MonoError *error) { 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 + + + + +